WorkManagerImplTest.java revision 3665b829af9706f7f85c53e4ef5a0825228f4d7a
1/*
2 * Copyright 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package androidx.work.impl;
18
19import static org.hamcrest.CoreMatchers.is;
20import static org.hamcrest.CoreMatchers.not;
21import static org.hamcrest.CoreMatchers.notNullValue;
22import static org.hamcrest.CoreMatchers.nullValue;
23import static org.hamcrest.MatcherAssert.assertThat;
24import static org.hamcrest.Matchers.containsInAnyOrder;
25import static org.hamcrest.Matchers.emptyCollectionOf;
26import static org.hamcrest.Matchers.greaterThanOrEqualTo;
27import static org.hamcrest.Matchers.isIn;
28import static org.hamcrest.Matchers.isOneOf;
29import static org.mockito.Mockito.clearInvocations;
30import static org.mockito.Mockito.mock;
31import static org.mockito.Mockito.times;
32import static org.mockito.Mockito.verify;
33
34import static androidx.work.ExistingWorkPolicy.APPEND;
35import static androidx.work.ExistingWorkPolicy.KEEP;
36import static androidx.work.ExistingWorkPolicy.REPLACE;
37import static androidx.work.NetworkType.METERED;
38import static androidx.work.NetworkType.NOT_REQUIRED;
39import static androidx.work.State.BLOCKED;
40import static androidx.work.State.CANCELLED;
41import static androidx.work.State.ENQUEUED;
42import static androidx.work.State.FAILED;
43import static androidx.work.State.RUNNING;
44import static androidx.work.State.SUCCEEDED;
45
46import android.arch.core.executor.ArchTaskExecutor;
47import android.arch.core.executor.TaskExecutor;
48import android.arch.lifecycle.LiveData;
49import android.arch.lifecycle.Observer;
50import android.arch.persistence.db.SupportSQLiteDatabase;
51import android.arch.persistence.db.SupportSQLiteOpenHelper;
52import android.content.Context;
53import android.net.Uri;
54import android.os.Build;
55import android.provider.MediaStore;
56import android.support.annotation.NonNull;
57import android.support.test.InstrumentationRegistry;
58import android.support.test.filters.SdkSuppress;
59import android.support.test.filters.SmallTest;
60import android.support.test.runner.AndroidJUnit4;
61
62import org.junit.After;
63import org.junit.Before;
64import org.junit.Rule;
65import org.junit.Test;
66import org.junit.runner.RunWith;
67import org.mockito.ArgumentCaptor;
68
69import java.util.Arrays;
70import java.util.Collections;
71import java.util.List;
72import java.util.concurrent.Executors;
73import java.util.concurrent.TimeUnit;
74
75import androidx.work.Arguments;
76import androidx.work.BackoffPolicy;
77import androidx.work.BaseWork;
78import androidx.work.Constraints;
79import androidx.work.ContentUriTriggers;
80import androidx.work.PeriodicWork;
81import androidx.work.TestLifecycleOwner;
82import androidx.work.Work;
83import androidx.work.WorkContinuation;
84import androidx.work.WorkManagerTest;
85import androidx.work.WorkStatus;
86import androidx.work.impl.model.Dependency;
87import androidx.work.impl.model.DependencyDao;
88import androidx.work.impl.model.WorkSpec;
89import androidx.work.impl.model.WorkSpecDao;
90import androidx.work.impl.model.WorkTag;
91import androidx.work.impl.model.WorkTagDao;
92import androidx.work.impl.utils.taskexecutor.InstantTaskExecutorRule;
93import androidx.work.worker.InfiniteTestWorker;
94import androidx.work.worker.TestWorker;
95
96@RunWith(AndroidJUnit4.class)
97public class WorkManagerImplTest extends WorkManagerTest {
98    private WorkDatabase mDatabase;
99    private WorkManagerImpl mWorkManagerImpl;
100
101    @Rule
102    public InstantTaskExecutorRule mRule = new InstantTaskExecutorRule();
103
104    @Before
105    public void setUp() {
106        ArchTaskExecutor.getInstance().setDelegate(new TaskExecutor() {
107            @Override
108            public void executeOnDiskIO(@NonNull Runnable runnable) {
109                runnable.run();
110            }
111
112            @Override
113            public void postToMainThread(@NonNull Runnable runnable) {
114                runnable.run();
115            }
116
117            @Override
118            public boolean isMainThread() {
119                return true;
120            }
121        });
122
123        Context context = InstrumentationRegistry.getTargetContext();
124        WorkManagerConfiguration configuration = new WorkManagerConfiguration(
125                context,
126                true,
127                Executors.newSingleThreadExecutor());
128        mWorkManagerImpl = new WorkManagerImpl(context, configuration);
129        mDatabase = mWorkManagerImpl.getWorkDatabase();
130    }
131
132    @After
133    public void tearDown() {
134        List<String> ids = mDatabase.workSpecDao().getAllWorkSpecIds();
135        for (String id : ids) {
136            mWorkManagerImpl.cancelWorkById(id);
137        }
138        mDatabase.close();
139        ArchTaskExecutor.getInstance().setDelegate(null);
140    }
141
142    @Test
143    @SmallTest
144    public void testEnqueue_insertWork() throws InterruptedException {
145        final int workCount = 3;
146        final Work[] workArray = new Work[workCount];
147        for (int i = 0; i < workCount; ++i) {
148            workArray[i] = new Work.Builder(TestWorker.class).build();
149        }
150        mWorkManagerImpl.beginWith(workArray[0]).then(workArray[1]).then(workArray[2]).enqueue();
151
152        for (int i = 0; i < workCount; ++i) {
153            String id = workArray[i].getId();
154            assertThat(mDatabase.workSpecDao().getWorkSpec(id), is(notNullValue()));
155            assertThat(
156                    "index " + i + " does not have expected number of dependencies!",
157                    mDatabase.dependencyDao().getPrerequisites(id).size() > 0,
158                    is(i > 0));
159        }
160    }
161
162    @Test
163    @SmallTest
164    public void testEnqueue_insertMultipleWork() {
165        Work work1 = new Work.Builder(TestWorker.class).build();
166        Work work2 = new Work.Builder(TestWorker.class).build();
167        Work work3 = new Work.Builder(TestWorker.class).build();
168
169        mWorkManagerImpl.enqueue(work1, work2, work3);
170
171        WorkSpecDao workSpecDao = mDatabase.workSpecDao();
172        assertThat(workSpecDao.getWorkSpec(work1.getId()), is(notNullValue()));
173        assertThat(workSpecDao.getWorkSpec(work2.getId()), is(notNullValue()));
174        assertThat(workSpecDao.getWorkSpec(work3.getId()), is(notNullValue()));
175    }
176
177    @Test
178    @SmallTest
179    public void testEnqueue_insertWithDependencies() {
180        Work work1a = new Work.Builder(TestWorker.class).build();
181        Work work1b = new Work.Builder(TestWorker.class).build();
182        Work work2 = new Work.Builder(TestWorker.class).build();
183        Work work3a = new Work.Builder(TestWorker.class).build();
184        Work work3b = new Work.Builder(TestWorker.class).build();
185
186        mWorkManagerImpl.beginWith(work1a, work1b).then(work2).then(work3a, work3b).enqueue();
187
188        WorkSpecDao workSpecDao = mDatabase.workSpecDao();
189        assertThat(workSpecDao.getWorkSpec(work1a.getId()), is(notNullValue()));
190        assertThat(workSpecDao.getWorkSpec(work1b.getId()), is(notNullValue()));
191        assertThat(workSpecDao.getWorkSpec(work2.getId()), is(notNullValue()));
192        assertThat(workSpecDao.getWorkSpec(work3a.getId()), is(notNullValue()));
193        assertThat(workSpecDao.getWorkSpec(work3b.getId()), is(notNullValue()));
194
195        DependencyDao dependencyDao = mDatabase.dependencyDao();
196        assertThat(dependencyDao.getPrerequisites(work1a.getId()),
197                is(emptyCollectionOf(String.class)));
198        assertThat(dependencyDao.getPrerequisites(work1b.getId()),
199                is(emptyCollectionOf(String.class)));
200
201        List<String> prerequisites = dependencyDao.getPrerequisites(work2.getId());
202        assertThat(prerequisites, containsInAnyOrder(work1a.getId(), work1b.getId()));
203
204        prerequisites = dependencyDao.getPrerequisites(work3a.getId());
205        assertThat(prerequisites, containsInAnyOrder(work2.getId()));
206
207        prerequisites = dependencyDao.getPrerequisites(work3b.getId());
208        assertThat(prerequisites, containsInAnyOrder(work2.getId()));
209    }
210
211    @Test
212    @SmallTest
213    public void testEnqueue_insertWithCompletedDependencies_isNotStatusBlocked() {
214        Work work1 = new Work.Builder(TestWorker.class).withInitialState(SUCCEEDED).build();
215
216        WorkContinuation workContinuation = mWorkManagerImpl.beginWith(work1);
217        workContinuation.enqueue();
218        WorkSpecDao workSpecDao = mDatabase.workSpecDao();
219        assertThat(workSpecDao.getState(work1.getId()), is(SUCCEEDED));
220
221        Work work2 = new Work.Builder(InfiniteTestWorker.class).build();
222        workContinuation.then(work2).enqueue();
223        assertThat(workSpecDao.getState(work2.getId()), isOneOf(ENQUEUED, RUNNING));
224    }
225
226    @Test
227    @SmallTest
228    public void testEnqueue_insertWithFailedDependencies_isStatusFailed() {
229        Work work1 = new Work.Builder(TestWorker.class).withInitialState(FAILED).build();
230
231        WorkContinuation workContinuation = mWorkManagerImpl.beginWith(work1);
232        workContinuation.enqueue();
233        WorkSpecDao workSpecDao = mDatabase.workSpecDao();
234        assertThat(workSpecDao.getState(work1.getId()), is(FAILED));
235
236        Work work2 = new Work.Builder(TestWorker.class).build();
237        workContinuation.then(work2).enqueue();
238        assertThat(workSpecDao.getState(work2.getId()), is(FAILED));
239    }
240
241    @Test
242    @SmallTest
243    public void testEnqueue_insertWithCancelledDependencies_isStatusCancelled() {
244        Work work1 = new Work.Builder(TestWorker.class).withInitialState(CANCELLED).build();
245
246        WorkContinuation workContinuation = mWorkManagerImpl.beginWith(work1);
247        workContinuation.enqueue();
248        WorkSpecDao workSpecDao = mDatabase.workSpecDao();
249        assertThat(workSpecDao.getState(work1.getId()), is(CANCELLED));
250
251        Work work2 = new Work.Builder(TestWorker.class).build();
252        workContinuation.then(work2).enqueue();
253        assertThat(workSpecDao.getState(work2.getId()), is(CANCELLED));
254    }
255
256    @Test
257    @SmallTest
258    @SdkSuppress(minSdkVersion = 23)
259    public void testEnqueue_insertWorkConstraints() {
260        Uri testUri1 = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
261        Uri testUri2 = MediaStore.Images.Media.INTERNAL_CONTENT_URI;
262
263        Work work0 = new Work.Builder(TestWorker.class)
264                .withConstraints(
265                        new Constraints.Builder()
266                                .setRequiresCharging(true)
267                                .setRequiresDeviceIdle(true)
268                                .setRequiredNetworkType(METERED)
269                                .setRequiresBatteryNotLow(true)
270                                .setRequiresStorageNotLow(true)
271                                .addContentUriTrigger(testUri1, true)
272                                .addContentUriTrigger(testUri2, false)
273                                .build())
274                .build();
275        Work work1 = new Work.Builder(TestWorker.class).build();
276        mWorkManagerImpl.beginWith(work0).then(work1).enqueue();
277
278        WorkSpec workSpec0 = mDatabase.workSpecDao().getWorkSpec(work0.getId());
279        WorkSpec workSpec1 = mDatabase.workSpecDao().getWorkSpec(work1.getId());
280
281        ContentUriTriggers expectedTriggers = new ContentUriTriggers();
282        expectedTriggers.add(testUri1, true);
283        expectedTriggers.add(testUri2, false);
284
285        Constraints constraints = workSpec0.getConstraints();
286        assertThat(constraints, is(notNullValue()));
287        assertThat(constraints.requiresCharging(), is(true));
288        assertThat(constraints.requiresDeviceIdle(), is(true));
289        assertThat(constraints.requiresBatteryNotLow(), is(true));
290        assertThat(constraints.requiresStorageNotLow(), is(true));
291        assertThat(constraints.getRequiredNetworkType(), is(METERED));
292        if (Build.VERSION.SDK_INT >= 24) {
293            assertThat(constraints.getContentUriTriggers(), is(expectedTriggers));
294        } else {
295            assertThat(constraints.getContentUriTriggers(), is(new ContentUriTriggers()));
296        }
297
298        constraints = workSpec1.getConstraints();
299        assertThat(constraints, is(notNullValue()));
300        assertThat(constraints.requiresCharging(), is(false));
301        assertThat(constraints.requiresDeviceIdle(), is(false));
302        assertThat(constraints.requiresBatteryNotLow(), is(false));
303        assertThat(constraints.requiresStorageNotLow(), is(false));
304        assertThat(constraints.getRequiredNetworkType(), is(NOT_REQUIRED));
305        assertThat(constraints.getContentUriTriggers().size(), is(0));
306    }
307
308    @Test
309    @SmallTest
310    public void testEnqueue_insertWorkInitialDelay() {
311        final long expectedInitialDelay = 5000L;
312        Work work0 = new Work.Builder(TestWorker.class)
313                .withInitialDelay(expectedInitialDelay, TimeUnit.MILLISECONDS)
314                .build();
315        Work work1 = new Work.Builder(TestWorker.class).build();
316        mWorkManagerImpl.beginWith(work0).then(work1).enqueue();
317
318        WorkSpec workSpec0 = mDatabase.workSpecDao().getWorkSpec(work0.getId());
319        WorkSpec workSpec1 = mDatabase.workSpecDao().getWorkSpec(work1.getId());
320
321        assertThat(workSpec0.getInitialDelay(), is(expectedInitialDelay));
322        assertThat(workSpec1.getInitialDelay(), is(0L));
323    }
324
325    @Test
326    @SmallTest
327    public void testEnqueue_insertWorkBackoffPolicy() {
328        Work work0 = new Work.Builder(TestWorker.class)
329                .withBackoffCriteria(BackoffPolicy.LINEAR, 50000, TimeUnit.MILLISECONDS)
330                .build();
331        Work work1 = new Work.Builder(TestWorker.class).build();
332        mWorkManagerImpl.beginWith(work0).then(work1).enqueue();
333
334        WorkSpec workSpec0 = mDatabase.workSpecDao().getWorkSpec(work0.getId());
335        WorkSpec workSpec1 = mDatabase.workSpecDao().getWorkSpec(work1.getId());
336
337        assertThat(workSpec0.getBackoffPolicy(), is(BackoffPolicy.LINEAR));
338        assertThat(workSpec0.getBackoffDelayDuration(), is(50000L));
339
340        assertThat(workSpec1.getBackoffPolicy(), is(BackoffPolicy.EXPONENTIAL));
341        assertThat(workSpec1.getBackoffDelayDuration(), is(BaseWork.DEFAULT_BACKOFF_DELAY_MILLIS));
342    }
343
344    @Test
345    @SmallTest
346    public void testEnqueue_insertWorkTags() {
347        final String firstTag = "first_tag";
348        final String secondTag = "second_tag";
349        final String thirdTag = "third_tag";
350
351        Work work0 = new Work.Builder(TestWorker.class).addTag(firstTag).addTag(secondTag).build();
352        Work work1 = new Work.Builder(TestWorker.class).addTag(firstTag).build();
353        Work work2 = new Work.Builder(TestWorker.class).build();
354        mWorkManagerImpl.beginWith(work0).then(work1).then(work2).enqueue();
355
356        WorkTagDao workTagDao = mDatabase.workTagDao();
357        assertThat(workTagDao.getWorkSpecIdsWithTag(firstTag),
358                containsInAnyOrder(work0.getId(), work1.getId()));
359        assertThat(workTagDao.getWorkSpecIdsWithTag(secondTag), containsInAnyOrder(work0.getId()));
360        assertThat(workTagDao.getWorkSpecIdsWithTag(thirdTag), emptyCollectionOf(String.class));
361    }
362
363    @Test
364    @SmallTest
365    public void testEnqueue_insertPeriodicWork() {
366        PeriodicWork periodicWork = new PeriodicWork.Builder(
367                TestWorker.class,
368                PeriodicWork.MIN_PERIODIC_INTERVAL_MILLIS,
369                TimeUnit.MILLISECONDS)
370                .build();
371        mWorkManagerImpl.enqueue(periodicWork);
372
373        WorkSpec workSpec = mDatabase.workSpecDao().getWorkSpec(periodicWork.getId());
374        assertThat(workSpec.isPeriodic(), is(true));
375        assertThat(workSpec.getIntervalDuration(), is(PeriodicWork.MIN_PERIODIC_INTERVAL_MILLIS));
376        assertThat(workSpec.getFlexDuration(), is(PeriodicWork.MIN_PERIODIC_INTERVAL_MILLIS));
377    }
378
379    @Test
380    @SmallTest
381    public void testEnqueued_work_setsPeriodStartTime() {
382        Work work = new Work.Builder(TestWorker.class).build();
383        assertThat(getWorkSpec(work).getPeriodStartTime(), is(0L));
384
385        long beforeEnqueueTime = System.currentTimeMillis();
386
387        mWorkManagerImpl.enqueue(work);
388
389        WorkSpec workSpec = mDatabase.workSpecDao().getWorkSpec(work.getId());
390        assertThat(workSpec.getPeriodStartTime(), is(greaterThanOrEqualTo(beforeEnqueueTime)));
391    }
392
393    @Test
394    @SmallTest
395    public void testEnqueued_periodicWork_setsPeriodStartTime() {
396        PeriodicWork periodicWork = new PeriodicWork.Builder(
397                TestWorker.class,
398                PeriodicWork.MIN_PERIODIC_INTERVAL_MILLIS,
399                TimeUnit.MILLISECONDS)
400                .build();
401        assertThat(getWorkSpec(periodicWork).getPeriodStartTime(), is(0L));
402
403        long beforeEnqueueTime = System.currentTimeMillis();
404
405        mWorkManagerImpl.enqueue(periodicWork);
406
407        WorkSpec workSpec = mDatabase.workSpecDao().getWorkSpec(periodicWork.getId());
408        assertThat(workSpec.getPeriodStartTime(), is(greaterThanOrEqualTo(beforeEnqueueTime)));
409    }
410
411    @Test
412    @SmallTest
413    public void testBeginWithName_setsUniqueName() {
414        final String testName = "myname";
415
416        Work work = new Work.Builder(TestWorker.class).build();
417        mWorkManagerImpl.beginWithName(testName, REPLACE)
418                .then(work)
419                .enqueue();
420
421        List<String> workSpecIds = mDatabase.workNameDao().getWorkSpecIdsWithName(testName);
422        assertThat(work.getId(), isIn(workSpecIds));
423    }
424
425    @Test
426    @SmallTest
427    public void testBeginWithName_deletesOldWorkOnReplace() {
428        final String testName = "myname";
429
430        Work originalWork = new Work.Builder(InfiniteTestWorker.class).build();
431        mWorkManagerImpl.beginWithName(testName, KEEP, originalWork).enqueue();
432
433        List<String> workSpecIds = mDatabase.workNameDao().getWorkSpecIdsWithName(testName);
434        assertThat(workSpecIds, containsInAnyOrder(originalWork.getId()));
435
436        Work replacementWork1 = new Work.Builder(TestWorker.class).build();
437        Work replacementWork2 = new Work.Builder(TestWorker.class).build();
438        mWorkManagerImpl
439                .beginWithName(testName, REPLACE, replacementWork1)
440                .then(replacementWork2)
441                .enqueue();
442
443        workSpecIds = mDatabase.workNameDao().getWorkSpecIdsWithName(testName);
444        assertThat(
445                workSpecIds,
446                containsInAnyOrder(replacementWork1.getId(), replacementWork2.getId()));
447
448        WorkSpecDao workSpecDao = mDatabase.workSpecDao();
449        assertThat(workSpecDao.getWorkSpec(originalWork.getId()), is(nullValue()));
450        assertThat(workSpecDao.getWorkSpec(replacementWork1.getId()), is(not(nullValue())));
451        assertThat(workSpecDao.getWorkSpec(replacementWork2.getId()), is(not(nullValue())));
452    }
453
454    @Test
455    @SmallTest
456    public void testBeginWithName_keepsExistingWorkOnKeep() {
457        final String testName = "myname";
458
459        Work originalWork = new Work.Builder(InfiniteTestWorker.class).build();
460        mWorkManagerImpl.beginWithName(testName, KEEP, originalWork).enqueue();
461
462        List<String> workSpecIds = mDatabase.workNameDao().getWorkSpecIdsWithName(testName);
463        assertThat(workSpecIds, containsInAnyOrder(originalWork.getId()));
464
465        Work replacementWork1 = new Work.Builder(TestWorker.class).build();
466        Work replacementWork2 = new Work.Builder(TestWorker.class).build();
467        mWorkManagerImpl
468                .beginWithName(testName, KEEP, replacementWork1)
469                .then(replacementWork2)
470                .enqueue();
471
472        workSpecIds = mDatabase.workNameDao().getWorkSpecIdsWithName(testName);
473        assertThat(workSpecIds, containsInAnyOrder(originalWork.getId()));
474
475        WorkSpecDao workSpecDao = mDatabase.workSpecDao();
476        assertThat(workSpecDao.getWorkSpec(originalWork.getId()), is(not(nullValue())));
477        assertThat(workSpecDao.getWorkSpec(replacementWork1.getId()), is(nullValue()));
478        assertThat(workSpecDao.getWorkSpec(replacementWork2.getId()), is(nullValue()));
479    }
480
481    @Test
482    @SmallTest
483    public void testBeginWithName_replacesExistingWorkOnKeepWhenExistingWorkIsFinished() {
484        final String testName = "myname";
485
486        Work originalWork = new Work.Builder(TestWorker.class).withInitialState(SUCCEEDED).build();
487        mWorkManagerImpl.beginWithName(testName, KEEP, originalWork).enqueue();
488
489        List<String> workSpecIds = mDatabase.workNameDao().getWorkSpecIdsWithName(testName);
490        assertThat(workSpecIds, containsInAnyOrder(originalWork.getId()));
491
492        Work replacementWork1 = new Work.Builder(TestWorker.class).build();
493        Work replacementWork2 = new Work.Builder(TestWorker.class).build();
494        mWorkManagerImpl
495                .beginWithName(testName, KEEP, replacementWork1)
496                .then(replacementWork2)
497                .enqueue();
498
499        workSpecIds = mDatabase.workNameDao().getWorkSpecIdsWithName(testName);
500        assertThat(workSpecIds,
501                containsInAnyOrder(replacementWork1.getId(), replacementWork2.getId()));
502
503        WorkSpecDao workSpecDao = mDatabase.workSpecDao();
504        assertThat(workSpecDao.getWorkSpec(originalWork.getId()), is(nullValue()));
505        assertThat(workSpecDao.getWorkSpec(replacementWork1.getId()), is(not(nullValue())));
506        assertThat(workSpecDao.getWorkSpec(replacementWork2.getId()), is(not(nullValue())));
507    }
508
509    @Test
510    @SmallTest
511    public void testBeginWithName_appendsExistingWorkOnAppend() {
512        final String testName = "myname";
513
514        Work originalWork = new Work.Builder(InfiniteTestWorker.class).build();
515        mWorkManagerImpl.beginWithName(testName, KEEP, originalWork).enqueue();
516
517        List<String> workSpecIds = mDatabase.workNameDao().getWorkSpecIdsWithName(testName);
518        assertThat(workSpecIds, containsInAnyOrder(originalWork.getId()));
519
520        Work appendWork1 = new Work.Builder(TestWorker.class).build();
521        Work appendWork2 = new Work.Builder(TestWorker.class).build();
522        mWorkManagerImpl
523                .beginWithName(testName, APPEND, appendWork1)
524                .then(appendWork2)
525                .enqueue();
526
527        workSpecIds = mDatabase.workNameDao().getWorkSpecIdsWithName(testName);
528        assertThat(workSpecIds,
529                containsInAnyOrder(originalWork.getId(), appendWork1.getId(), appendWork2.getId()));
530
531        WorkSpecDao workSpecDao = mDatabase.workSpecDao();
532        assertThat(workSpecDao.getWorkSpec(originalWork.getId()), is(not(nullValue())));
533        assertThat(workSpecDao.getState(appendWork1.getId()), is(BLOCKED));
534        assertThat(workSpecDao.getState(appendWork2.getId()), is(BLOCKED));
535
536        assertThat(mDatabase.dependencyDao().getDependentWorkIds(originalWork.getId()),
537                containsInAnyOrder(appendWork1.getId()));
538    }
539
540    @Test
541    @SmallTest
542    public void testBeginWithName_appendsExistingWorkToOnlyLeavesOnAppend() {
543        final String testName = "myname";
544
545        Work originalWork1 = new Work.Builder(InfiniteTestWorker.class).build();
546        Work originalWork2 = new Work.Builder(InfiniteTestWorker.class).build();
547        Work originalWork3 = new Work.Builder(InfiniteTestWorker.class).build();
548        Work originalWork4 = new Work.Builder(InfiniteTestWorker.class).build();
549        mWorkManagerImpl.beginWithName(testName, KEEP, originalWork1)
550                .then(originalWork2)
551                .then(originalWork3, originalWork4)
552                .enqueue();
553
554        List<String> workSpecIds = mDatabase.workNameDao().getWorkSpecIdsWithName(testName);
555        assertThat(workSpecIds,
556                containsInAnyOrder(
557                        originalWork1.getId(),
558                        originalWork2.getId(),
559                        originalWork3.getId(),
560                        originalWork4.getId()));
561
562        Work appendWork1 = new Work.Builder(TestWorker.class).build();
563        Work appendWork2 = new Work.Builder(TestWorker.class).build();
564        mWorkManagerImpl
565                .beginWithName(testName, APPEND, appendWork1)
566                .then(appendWork2)
567                .enqueue();
568
569        workSpecIds = mDatabase.workNameDao().getWorkSpecIdsWithName(testName);
570        assertThat(workSpecIds,
571                containsInAnyOrder(
572                        originalWork1.getId(),
573                        originalWork2.getId(),
574                        originalWork3.getId(),
575                        originalWork4.getId(),
576                        appendWork1.getId(),
577                        appendWork2.getId()));
578
579        WorkSpecDao workSpecDao = mDatabase.workSpecDao();
580        assertThat(workSpecDao.getWorkSpec(originalWork1.getId()), is(not(nullValue())));
581        assertThat(workSpecDao.getWorkSpec(originalWork2.getId()), is(not(nullValue())));
582        assertThat(workSpecDao.getWorkSpec(originalWork3.getId()), is(not(nullValue())));
583        assertThat(workSpecDao.getWorkSpec(originalWork4.getId()), is(not(nullValue())));
584        assertThat(workSpecDao.getState(appendWork1.getId()), is(BLOCKED));
585        assertThat(workSpecDao.getState(appendWork2.getId()), is(BLOCKED));
586
587        DependencyDao dependencyDao = mDatabase.dependencyDao();
588        assertThat(dependencyDao.getPrerequisites(appendWork1.getId()),
589                containsInAnyOrder(originalWork3.getId(), originalWork4.getId()));
590        assertThat(dependencyDao.getPrerequisites(appendWork2.getId()),
591                containsInAnyOrder(appendWork1.getId()));
592    }
593
594    @Test
595    @SmallTest
596    public void testBeginWithName_insertsExistingWorkWhenNothingToAppendTo() {
597        final String testName = "myname";
598
599        Work appendWork1 = new Work.Builder(TestWorker.class).build();
600        Work appendWork2 = new Work.Builder(TestWorker.class).build();
601        mWorkManagerImpl
602                .beginWithName(testName, APPEND, appendWork1)
603                .then(appendWork2)
604                .enqueue();
605
606        List<String> workSpecIds = mDatabase.workNameDao().getWorkSpecIdsWithName(testName);
607        assertThat(workSpecIds,
608                containsInAnyOrder(appendWork1.getId(), appendWork2.getId()));
609    }
610
611    @Test
612    @SmallTest
613    public void testGetStatusByIdSync() {
614        Work work = new Work.Builder(TestWorker.class).withInitialState(SUCCEEDED).build();
615        insertWorkSpecAndTags(work);
616
617        WorkStatus workStatus = mWorkManagerImpl.getStatusByIdBlocking(work.getId());
618        assertThat(workStatus.getId(), is(work.getId()));
619        assertThat(workStatus.getState(), is(SUCCEEDED));
620    }
621
622    @Test
623    @SmallTest
624    public void testGetStatusByIdSync_returnsNullIfNotInDatabase() {
625        WorkStatus workStatus = mWorkManagerImpl.getStatusByIdBlocking("dummy");
626        assertThat(workStatus, is(nullValue()));
627    }
628
629    @Test
630    @SmallTest
631    @SuppressWarnings("unchecked")
632    public void testGetStatusesById() {
633        Work work0 = new Work.Builder(TestWorker.class).build();
634        Work work1 = new Work.Builder(TestWorker.class).build();
635        insertWorkSpecAndTags(work0);
636        insertWorkSpecAndTags(work1);
637
638        Observer<List<WorkStatus>> mockObserver = mock(Observer.class);
639
640        TestLifecycleOwner testLifecycleOwner = new TestLifecycleOwner();
641        LiveData<List<WorkStatus>> liveData =
642                mWorkManagerImpl.getStatusesById(Arrays.asList(work0.getId(), work1.getId()));
643        liveData.observe(testLifecycleOwner, mockObserver);
644
645        ArgumentCaptor<List<WorkStatus>> captor =
646                ArgumentCaptor.forClass(List.class);
647        verify(mockObserver).onChanged(captor.capture());
648        assertThat(captor.getValue(), is(not(nullValue())));
649        assertThat(captor.getValue().size(), is(2));
650
651        WorkStatus workStatus0 = new WorkStatus(
652                work0.getId(),
653                ENQUEUED,
654                Arguments.EMPTY,
655                Collections.<String>emptyList());
656        WorkStatus workStatus1 = new WorkStatus(
657                work1.getId(),
658                ENQUEUED,
659                Arguments.EMPTY,
660                Collections.<String>emptyList());
661        assertThat(captor.getValue(), containsInAnyOrder(workStatus0, workStatus1));
662
663        WorkSpecDao workSpecDao = mDatabase.workSpecDao();
664        workSpecDao.setState(RUNNING, work0.getId());
665
666        verify(mockObserver, times(2)).onChanged(captor.capture());
667        assertThat(captor.getValue(), is(not(nullValue())));
668        assertThat(captor.getValue().size(), is(2));
669
670        workStatus0 = new WorkStatus(
671                work0.getId(),
672                RUNNING,
673                Arguments.EMPTY,
674                Collections.<String>emptyList());
675        assertThat(captor.getValue(), containsInAnyOrder(workStatus0, workStatus1));
676
677        clearInvocations(mockObserver);
678        workSpecDao.setState(RUNNING, work1.getId());
679
680        verify(mockObserver).onChanged(captor.capture());
681        assertThat(captor.getValue(), is(not(nullValue())));
682        assertThat(captor.getValue().size(), is(2));
683
684        workStatus1 = new WorkStatus(
685                work1.getId(),
686                RUNNING,
687                Arguments.EMPTY,
688                Collections.<String>emptyList());
689        assertThat(captor.getValue(), containsInAnyOrder(workStatus0, workStatus1));
690
691        liveData.removeObservers(testLifecycleOwner);
692    }
693
694    @Test
695    @SmallTest
696    public void testGetStatusesByTagSync() {
697        final String firstTag = "first_tag";
698        final String secondTag = "second_tag";
699
700        Work work0 = new Work.Builder(TestWorker.class)
701                .addTag(firstTag)
702                .addTag(secondTag)
703                .withInitialState(RUNNING)
704                .build();
705        Work work1 = new Work.Builder(TestWorker.class)
706                .addTag(firstTag)
707                .withInitialState(BLOCKED)
708                .build();
709        Work work2 = new Work.Builder(TestWorker.class)
710                .addTag(secondTag)
711                .withInitialState(SUCCEEDED)
712                .build();
713        insertWorkSpecAndTags(work0);
714        insertWorkSpecAndTags(work1);
715        insertWorkSpecAndTags(work2);
716
717        WorkStatus workStatus0 = new WorkStatus(
718                work0.getId(),
719                RUNNING,
720                Arguments.EMPTY,
721                Arrays.asList(firstTag, secondTag));
722        WorkStatus workStatus1 = new WorkStatus(
723                work1.getId(),
724                BLOCKED,
725                Arguments.EMPTY,
726                Collections.singletonList(firstTag));
727        WorkStatus workStatus2 = new WorkStatus(
728                work2.getId(),
729                SUCCEEDED,
730                Arguments.EMPTY,
731                Collections.singletonList(secondTag));
732
733        List<WorkStatus> workStatuses = mWorkManagerImpl.getStatusesByTagBlocking(firstTag);
734        assertThat(workStatuses, containsInAnyOrder(workStatus0, workStatus1));
735
736        workStatuses = mWorkManagerImpl.getStatusesByTagBlocking(secondTag);
737        assertThat(workStatuses, containsInAnyOrder(workStatus0, workStatus2));
738
739        workStatuses = mWorkManagerImpl.getStatusesByTagBlocking("dummy");
740        assertThat(workStatuses.size(), is(0));
741    }
742
743    @Test
744    @SmallTest
745    @SuppressWarnings("unchecked")
746    public void testGetStatusesByTag() {
747        final String firstTag = "first_tag";
748        final String secondTag = "second_tag";
749        WorkSpecDao workSpecDao = mDatabase.workSpecDao();
750
751        Work work0 = new Work.Builder(TestWorker.class)
752                .addTag(firstTag)
753                .addTag(secondTag)
754                .withInitialState(RUNNING)
755                .build();
756        Work work1 = new Work.Builder(TestWorker.class)
757                .addTag(firstTag)
758                .withInitialState(BLOCKED)
759                .build();
760        Work work2 = new Work.Builder(TestWorker.class)
761                .addTag(secondTag)
762                .withInitialState(SUCCEEDED)
763                .build();
764        insertWorkSpecAndTags(work0);
765        insertWorkSpecAndTags(work1);
766        insertWorkSpecAndTags(work2);
767
768        Observer<List<WorkStatus>> mockObserver = mock(Observer.class);
769
770        TestLifecycleOwner testLifecycleOwner = new TestLifecycleOwner();
771        LiveData<List<WorkStatus>> liveData =
772                mWorkManagerImpl.getStatusesByTag(firstTag);
773        liveData.observe(testLifecycleOwner, mockObserver);
774
775        ArgumentCaptor<List<WorkStatus>> captor =
776                ArgumentCaptor.forClass(List.class);
777        verify(mockObserver).onChanged(captor.capture());
778        assertThat(captor.getValue(), is(not(nullValue())));
779        assertThat(captor.getValue().size(), is(2));
780
781        WorkStatus workStatus0 = new WorkStatus(
782                work0.getId(),
783                RUNNING,
784                Arguments.EMPTY,
785                Arrays.asList(firstTag, secondTag));
786        WorkStatus workStatus1 = new WorkStatus(
787                work1.getId(),
788                BLOCKED,
789                Arguments.EMPTY,
790                Collections.singletonList(firstTag));
791        assertThat(captor.getValue(), containsInAnyOrder(workStatus0, workStatus1));
792
793        workSpecDao.setState(ENQUEUED, work0.getId());
794
795        verify(mockObserver, times(2)).onChanged(captor.capture());
796        assertThat(captor.getValue(), is(not(nullValue())));
797        assertThat(captor.getValue().size(), is(2));
798
799        workStatus0 = new WorkStatus(
800                work0.getId(),
801                ENQUEUED,
802                Arguments.EMPTY,
803                Arrays.asList(firstTag, secondTag));
804        assertThat(captor.getValue(), containsInAnyOrder(workStatus0, workStatus1));
805
806        liveData.removeObservers(testLifecycleOwner);
807    }
808
809    @Test
810    @SmallTest
811    public void testCancelWorkForId() {
812        WorkSpecDao workSpecDao = mDatabase.workSpecDao();
813
814        Work work0 = new Work.Builder(TestWorker.class).build();
815        Work work1 = new Work.Builder(TestWorker.class).build();
816        insertWorkSpecAndTags(work0);
817        insertWorkSpecAndTags(work1);
818
819        mWorkManagerImpl.cancelWorkById(work0.getId());
820
821        assertThat(workSpecDao.getState(work0.getId()), is(CANCELLED));
822        assertThat(workSpecDao.getState(work1.getId()), is(not(CANCELLED)));
823    }
824
825    @Test
826    @SmallTest
827    public void testCancelWorkForId_cancelsDependentWork() {
828        WorkSpecDao workSpecDao = mDatabase.workSpecDao();
829
830        Work work0 = new Work.Builder(TestWorker.class).build();
831        Work work1 = new Work.Builder(TestWorker.class).withInitialState(BLOCKED).build();
832        insertWorkSpecAndTags(work0);
833        insertWorkSpecAndTags(work1);
834
835        Dependency dependency10 = new Dependency(work1.getId(), work0.getId());
836
837        DependencyDao dependencyDao = mDatabase.dependencyDao();
838        dependencyDao.insertDependency(dependency10);
839
840        mWorkManagerImpl.cancelWorkById(work0.getId());
841
842        assertThat(workSpecDao.getState(work0.getId()), is(CANCELLED));
843        assertThat(workSpecDao.getState(work1.getId()), is(CANCELLED));
844    }
845
846    @Test
847    @SmallTest
848    public void testCancelWorkForId_cancelsUnfinishedWorkOnly() {
849        WorkSpecDao workSpecDao = mDatabase.workSpecDao();
850
851        Work work0 = new Work.Builder(TestWorker.class).withInitialState(SUCCEEDED).build();
852        Work work1 = new Work.Builder(TestWorker.class).withInitialState(ENQUEUED).build();
853        insertWorkSpecAndTags(work0);
854        insertWorkSpecAndTags(work1);
855
856        Dependency dependency10 = new Dependency(work1.getId(), work0.getId());
857
858        DependencyDao dependencyDao = mDatabase.dependencyDao();
859        dependencyDao.insertDependency(dependency10);
860
861        mWorkManagerImpl.cancelWorkById(work0.getId());
862
863        assertThat(workSpecDao.getState(work0.getId()), is(SUCCEEDED));
864        assertThat(workSpecDao.getState(work1.getId()), is(CANCELLED));
865    }
866
867    @Test
868    @SmallTest
869    public void testCancelAllWorkWithTag() {
870        WorkSpecDao workSpecDao = mDatabase.workSpecDao();
871
872        final String tagToClear = "tag_to_clear";
873        final String tagNotToClear = "tag_not_to_clear";
874
875        Work work0 = new Work.Builder(TestWorker.class).addTag(tagToClear).build();
876        Work work1 = new Work.Builder(TestWorker.class).addTag(tagToClear).build();
877        Work work2 = new Work.Builder(TestWorker.class).addTag(tagNotToClear).build();
878        Work work3 = new Work.Builder(TestWorker.class).addTag(tagNotToClear).build();
879        insertWorkSpecAndTags(work0);
880        insertWorkSpecAndTags(work1);
881        insertWorkSpecAndTags(work2);
882        insertWorkSpecAndTags(work3);
883
884        mWorkManagerImpl.cancelAllWorkWithTag(tagToClear);
885
886        assertThat(workSpecDao.getState(work0.getId()), is(CANCELLED));
887        assertThat(workSpecDao.getState(work1.getId()), is(CANCELLED));
888        assertThat(workSpecDao.getState(work2.getId()), is(not(CANCELLED)));
889        assertThat(workSpecDao.getState(work3.getId()), is(not(CANCELLED)));
890    }
891
892    @Test
893    @SmallTest
894    public void testCancelAllWorkWithTag_cancelsDependentWork() {
895        String tag = "tag";
896
897        Work work0 = new Work.Builder(TestWorker.class).addTag(tag).build();
898        Work work1 = new Work.Builder(TestWorker.class).build();
899        Work work2 = new Work.Builder(TestWorker.class).build();
900        Work work3 = new Work.Builder(TestWorker.class).build();
901        Work work4 = new Work.Builder(TestWorker.class).build();
902
903        insertWorkSpecAndTags(work0);
904        insertWorkSpecAndTags(work1);
905        insertWorkSpecAndTags(work2);
906        insertWorkSpecAndTags(work3);
907        insertWorkSpecAndTags(work4);
908
909        // Dependency graph:
910        //                             0
911        //                             |
912        //                       |------------|
913        //            3          1            4
914        //            |          |
915        //            ------------
916        //                 |
917        //                 2
918
919        Dependency dependency21 = new Dependency(work2.getId(), work1.getId());
920        Dependency dependency23 = new Dependency(work2.getId(), work3.getId());
921        Dependency dependency10 = new Dependency(work1.getId(), work0.getId());
922        Dependency dependency40 = new Dependency(work4.getId(), work0.getId());
923
924        DependencyDao dependencyDao = mDatabase.dependencyDao();
925        dependencyDao.insertDependency(dependency21);
926        dependencyDao.insertDependency(dependency23);
927        dependencyDao.insertDependency(dependency10);
928        dependencyDao.insertDependency(dependency40);
929
930        mWorkManagerImpl.cancelAllWorkWithTag(tag);
931
932        WorkSpecDao workSpecDao = mDatabase.workSpecDao();
933        assertThat(workSpecDao.getState(work0.getId()), is(CANCELLED));
934        assertThat(workSpecDao.getState(work1.getId()), is(CANCELLED));
935        assertThat(workSpecDao.getState(work2.getId()), is(CANCELLED));
936        assertThat(workSpecDao.getState(work3.getId()), is(not(CANCELLED)));
937        assertThat(workSpecDao.getState(work4.getId()), is(CANCELLED));
938    }
939
940    @Test
941    @SmallTest
942    public void testSynchronousCancelAndGetStatus() {
943        Work work = new Work.Builder(TestWorker.class).build();
944        insertWorkSpecAndTags(work);
945
946        WorkSpecDao workSpecDao = mDatabase.workSpecDao();
947        assertThat(workSpecDao.getState(work.getId()), is(ENQUEUED));
948
949        mWorkManagerImpl.cancelWorkByIdBlocking(work.getId());
950        assertThat(mWorkManagerImpl.getStatusByIdBlocking(work.getId()).getState(), is(CANCELLED));
951    }
952
953    @Test
954    @SmallTest
955    public void testGenerateCleanupCallback_resetsRunningWorkStatuses() {
956        WorkSpecDao workSpecDao = mDatabase.workSpecDao();
957
958        Work work = new Work.Builder(TestWorker.class).withInitialState(RUNNING).build();
959        workSpecDao.insertWorkSpec(getWorkSpec(work));
960
961        assertThat(workSpecDao.getState(work.getId()), is(RUNNING));
962
963        SupportSQLiteOpenHelper openHelper = mDatabase.getOpenHelper();
964        SupportSQLiteDatabase db = openHelper.getWritableDatabase();
965        WorkDatabase.generateCleanupCallback().onOpen(db);
966
967        assertThat(workSpecDao.getState(work.getId()), is(ENQUEUED));
968    }
969
970    @Test
971    @SmallTest
972    public void testGenerateCleanupCallback_deletesOldFinishedWork() {
973        Work work1 = new Work.Builder(TestWorker.class)
974                .withInitialState(SUCCEEDED)
975                .withPeriodStartTime(WorkDatabase.getPruneDate() - 1L, TimeUnit.MILLISECONDS)
976                .build();
977        Work work2 = new Work.Builder(TestWorker.class)
978                .withPeriodStartTime(Long.MAX_VALUE, TimeUnit.MILLISECONDS)
979                .build();
980
981        insertWorkSpecAndTags(work1);
982        insertWorkSpecAndTags(work2);
983
984        SupportSQLiteOpenHelper openHelper = mDatabase.getOpenHelper();
985        SupportSQLiteDatabase db = openHelper.getWritableDatabase();
986        WorkDatabase.generateCleanupCallback().onOpen(db);
987
988        WorkSpecDao workSpecDao = mDatabase.workSpecDao();
989        assertThat(workSpecDao.getWorkSpec(work1.getId()), is(nullValue()));
990        assertThat(workSpecDao.getWorkSpec(work2.getId()), is(not(nullValue())));
991    }
992
993    @Test
994    @SmallTest
995    public void testGenerateCleanupCallback_doesNotDeleteOldFinishedWorkWithActiveDependents() {
996        Work work0 = new Work.Builder(TestWorker.class)
997                .withInitialState(SUCCEEDED)
998                .withPeriodStartTime(WorkDatabase.getPruneDate() - 1L, TimeUnit.MILLISECONDS)
999                .build();
1000        Work work1 = new Work.Builder(TestWorker.class)
1001                .withInitialState(SUCCEEDED)
1002                .withPeriodStartTime(WorkDatabase.getPruneDate() - 1L, TimeUnit.MILLISECONDS)
1003                .build();
1004        Work work2 = new Work.Builder(TestWorker.class)
1005                .withInitialState(ENQUEUED)
1006                .withPeriodStartTime(WorkDatabase.getPruneDate() - 1L, TimeUnit.MILLISECONDS)
1007                .build();
1008
1009        insertWorkSpecAndTags(work0);
1010        insertWorkSpecAndTags(work1);
1011        insertWorkSpecAndTags(work2);
1012
1013        // Dependency graph: 0 -> 1 -> 2
1014
1015        Dependency dependency10 = new Dependency(work1.getId(), work0.getId());
1016        Dependency dependency21 = new Dependency(work2.getId(), work1.getId());
1017        DependencyDao dependencyDao = mDatabase.dependencyDao();
1018        dependencyDao.insertDependency(dependency10);
1019        dependencyDao.insertDependency(dependency21);
1020
1021        SupportSQLiteOpenHelper openHelper = mDatabase.getOpenHelper();
1022        SupportSQLiteDatabase db = openHelper.getWritableDatabase();
1023        WorkDatabase.generateCleanupCallback().onOpen(db);
1024
1025        WorkSpecDao workSpecDao = mDatabase.workSpecDao();
1026        assertThat(workSpecDao.getWorkSpec(work0.getId()), is(nullValue()));
1027        assertThat(workSpecDao.getWorkSpec(work1.getId()), is(not(nullValue())));
1028        assertThat(workSpecDao.getWorkSpec(work2.getId()), is(not(nullValue())));
1029    }
1030
1031    private void insertWorkSpecAndTags(Work work) {
1032        mDatabase.workSpecDao().insertWorkSpec(getWorkSpec(work));
1033        for (String tag : getTags(work)) {
1034            mDatabase.workTagDao().insert(new WorkTag(tag, work.getId()));
1035        }
1036    }
1037}
1038