WorkManagerImplTest.java revision 21e16f32d901aee5e399832703fb935b9be63ae7
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.List;
71import java.util.concurrent.Executors;
72import java.util.concurrent.TimeUnit;
73
74import androidx.work.Arguments;
75import androidx.work.BackoffPolicy;
76import androidx.work.BaseWork;
77import androidx.work.Constraints;
78import androidx.work.ContentUriTriggers;
79import androidx.work.PeriodicWork;
80import androidx.work.TestLifecycleOwner;
81import androidx.work.Work;
82import androidx.work.WorkContinuation;
83import androidx.work.WorkManagerTest;
84import androidx.work.WorkStatus;
85import androidx.work.impl.model.Dependency;
86import androidx.work.impl.model.DependencyDao;
87import androidx.work.impl.model.WorkSpec;
88import androidx.work.impl.model.WorkSpecDao;
89import androidx.work.impl.model.WorkTag;
90import androidx.work.impl.model.WorkTagDao;
91import androidx.work.impl.utils.taskexecutor.InstantTaskExecutorRule;
92import androidx.work.worker.InfiniteTestWorker;
93import androidx.work.worker.TestWorker;
94
95@RunWith(AndroidJUnit4.class)
96public class WorkManagerImplTest extends WorkManagerTest {
97    private WorkDatabase mDatabase;
98    private WorkManagerImpl mWorkManagerImpl;
99
100    @Rule
101    public InstantTaskExecutorRule mRule = new InstantTaskExecutorRule();
102
103    @Before
104    public void setUp() {
105        ArchTaskExecutor.getInstance().setDelegate(new TaskExecutor() {
106            @Override
107            public void executeOnDiskIO(@NonNull Runnable runnable) {
108                runnable.run();
109            }
110
111            @Override
112            public void postToMainThread(@NonNull Runnable runnable) {
113                runnable.run();
114            }
115
116            @Override
117            public boolean isMainThread() {
118                return true;
119            }
120        });
121
122        Context context = InstrumentationRegistry.getTargetContext();
123        WorkManagerConfiguration configuration = new WorkManagerConfiguration(
124                context,
125                true,
126                Executors.newSingleThreadExecutor());
127        mWorkManagerImpl = new WorkManagerImpl(context, configuration);
128        mDatabase = mWorkManagerImpl.getWorkDatabase();
129    }
130
131    @After
132    public void tearDown() {
133        List<String> ids = mDatabase.workSpecDao().getAllWorkSpecIds();
134        for (String id : ids) {
135            mWorkManagerImpl.cancelWorkById(id);
136        }
137        mDatabase.close();
138        ArchTaskExecutor.getInstance().setDelegate(null);
139    }
140
141    @Test
142    @SmallTest
143    public void testEnqueue_insertWork() throws InterruptedException {
144        final int workCount = 3;
145        final Work[] workArray = new Work[workCount];
146        for (int i = 0; i < workCount; ++i) {
147            workArray[i] = new Work.Builder(TestWorker.class).build();
148        }
149        mWorkManagerImpl.beginWith(workArray[0]).then(workArray[1]).then(workArray[2]).enqueue();
150
151        for (int i = 0; i < workCount; ++i) {
152            String id = workArray[i].getId();
153            assertThat(mDatabase.workSpecDao().getWorkSpec(id), is(notNullValue()));
154            assertThat(
155                    "index " + i + " does not have expected number of dependencies!",
156                    mDatabase.dependencyDao().getPrerequisites(id).size() > 0,
157                    is(i > 0));
158        }
159    }
160
161    @Test
162    @SmallTest
163    public void testEnqueue_insertMultipleWork() {
164        Work work1 = new Work.Builder(TestWorker.class).build();
165        Work work2 = new Work.Builder(TestWorker.class).build();
166        Work work3 = new Work.Builder(TestWorker.class).build();
167
168        mWorkManagerImpl.enqueue(work1, work2, work3);
169
170        WorkSpecDao workSpecDao = mDatabase.workSpecDao();
171        assertThat(workSpecDao.getWorkSpec(work1.getId()), is(notNullValue()));
172        assertThat(workSpecDao.getWorkSpec(work2.getId()), is(notNullValue()));
173        assertThat(workSpecDao.getWorkSpec(work3.getId()), is(notNullValue()));
174    }
175
176    @Test
177    @SmallTest
178    public void testEnqueue_insertWithDependencies() {
179        Work work1a = new Work.Builder(TestWorker.class).build();
180        Work work1b = new Work.Builder(TestWorker.class).build();
181        Work work2 = new Work.Builder(TestWorker.class).build();
182        Work work3a = new Work.Builder(TestWorker.class).build();
183        Work work3b = new Work.Builder(TestWorker.class).build();
184
185        mWorkManagerImpl.beginWith(work1a, work1b).then(work2).then(work3a, work3b).enqueue();
186
187        WorkSpecDao workSpecDao = mDatabase.workSpecDao();
188        assertThat(workSpecDao.getWorkSpec(work1a.getId()), is(notNullValue()));
189        assertThat(workSpecDao.getWorkSpec(work1b.getId()), is(notNullValue()));
190        assertThat(workSpecDao.getWorkSpec(work2.getId()), is(notNullValue()));
191        assertThat(workSpecDao.getWorkSpec(work3a.getId()), is(notNullValue()));
192        assertThat(workSpecDao.getWorkSpec(work3b.getId()), is(notNullValue()));
193
194        DependencyDao dependencyDao = mDatabase.dependencyDao();
195        assertThat(dependencyDao.getPrerequisites(work1a.getId()),
196                is(emptyCollectionOf(String.class)));
197        assertThat(dependencyDao.getPrerequisites(work1b.getId()),
198                is(emptyCollectionOf(String.class)));
199
200        List<String> prerequisites = dependencyDao.getPrerequisites(work2.getId());
201        assertThat(prerequisites, containsInAnyOrder(work1a.getId(), work1b.getId()));
202
203        prerequisites = dependencyDao.getPrerequisites(work3a.getId());
204        assertThat(prerequisites, containsInAnyOrder(work2.getId()));
205
206        prerequisites = dependencyDao.getPrerequisites(work3b.getId());
207        assertThat(prerequisites, containsInAnyOrder(work2.getId()));
208    }
209
210    @Test
211    @SmallTest
212    public void testEnqueue_insertWithCompletedDependencies_isNotStatusBlocked() {
213        Work work1 = new Work.Builder(TestWorker.class).withInitialState(SUCCEEDED).build();
214
215        WorkContinuation workContinuation = mWorkManagerImpl.beginWith(work1);
216        workContinuation.enqueue();
217        WorkSpecDao workSpecDao = mDatabase.workSpecDao();
218        assertThat(workSpecDao.getState(work1.getId()), is(SUCCEEDED));
219
220        Work work2 = new Work.Builder(InfiniteTestWorker.class).build();
221        workContinuation.then(work2).enqueue();
222        assertThat(workSpecDao.getState(work2.getId()), isOneOf(ENQUEUED, RUNNING));
223    }
224
225    @Test
226    @SmallTest
227    public void testEnqueue_insertWithFailedDependencies_isStatusFailed() {
228        Work work1 = new Work.Builder(TestWorker.class).withInitialState(FAILED).build();
229
230        WorkContinuation workContinuation = mWorkManagerImpl.beginWith(work1);
231        workContinuation.enqueue();
232        WorkSpecDao workSpecDao = mDatabase.workSpecDao();
233        assertThat(workSpecDao.getState(work1.getId()), is(FAILED));
234
235        Work work2 = new Work.Builder(TestWorker.class).build();
236        workContinuation.then(work2).enqueue();
237        assertThat(workSpecDao.getState(work2.getId()), is(FAILED));
238    }
239
240    @Test
241    @SmallTest
242    public void testEnqueue_insertWithCancelledDependencies_isStatusCancelled() {
243        Work work1 = new Work.Builder(TestWorker.class).withInitialState(CANCELLED).build();
244
245        WorkContinuation workContinuation = mWorkManagerImpl.beginWith(work1);
246        workContinuation.enqueue();
247        WorkSpecDao workSpecDao = mDatabase.workSpecDao();
248        assertThat(workSpecDao.getState(work1.getId()), is(CANCELLED));
249
250        Work work2 = new Work.Builder(TestWorker.class).build();
251        workContinuation.then(work2).enqueue();
252        assertThat(workSpecDao.getState(work2.getId()), is(CANCELLED));
253    }
254
255    @Test
256    @SmallTest
257    @SdkSuppress(minSdkVersion = 23)
258    public void testEnqueue_insertWorkConstraints() {
259        Uri testUri1 = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
260        Uri testUri2 = MediaStore.Images.Media.INTERNAL_CONTENT_URI;
261
262        Work work0 = new Work.Builder(TestWorker.class)
263                .withConstraints(
264                        new Constraints.Builder()
265                                .setRequiresCharging(true)
266                                .setRequiresDeviceIdle(true)
267                                .setRequiredNetworkType(METERED)
268                                .setRequiresBatteryNotLow(true)
269                                .setRequiresStorageNotLow(true)
270                                .addContentUriTrigger(testUri1, true)
271                                .addContentUriTrigger(testUri2, false)
272                                .build())
273                .build();
274        Work work1 = new Work.Builder(TestWorker.class).build();
275        mWorkManagerImpl.beginWith(work0).then(work1).enqueue();
276
277        WorkSpec workSpec0 = mDatabase.workSpecDao().getWorkSpec(work0.getId());
278        WorkSpec workSpec1 = mDatabase.workSpecDao().getWorkSpec(work1.getId());
279
280        ContentUriTriggers expectedTriggers = new ContentUriTriggers();
281        expectedTriggers.add(testUri1, true);
282        expectedTriggers.add(testUri2, false);
283
284        Constraints constraints = workSpec0.getConstraints();
285        assertThat(constraints, is(notNullValue()));
286        assertThat(constraints.requiresCharging(), is(true));
287        assertThat(constraints.requiresDeviceIdle(), is(true));
288        assertThat(constraints.requiresBatteryNotLow(), is(true));
289        assertThat(constraints.requiresStorageNotLow(), is(true));
290        assertThat(constraints.getRequiredNetworkType(), is(METERED));
291        if (Build.VERSION.SDK_INT >= 24) {
292            assertThat(constraints.getContentUriTriggers(), is(expectedTriggers));
293        } else {
294            assertThat(constraints.getContentUriTriggers(), is(new ContentUriTriggers()));
295        }
296
297        constraints = workSpec1.getConstraints();
298        assertThat(constraints, is(notNullValue()));
299        assertThat(constraints.requiresCharging(), is(false));
300        assertThat(constraints.requiresDeviceIdle(), is(false));
301        assertThat(constraints.requiresBatteryNotLow(), is(false));
302        assertThat(constraints.requiresStorageNotLow(), is(false));
303        assertThat(constraints.getRequiredNetworkType(), is(NOT_REQUIRED));
304        assertThat(constraints.getContentUriTriggers().size(), is(0));
305    }
306
307    @Test
308    @SmallTest
309    public void testEnqueue_insertWorkInitialDelay() {
310        final long expectedInitialDelay = 5000L;
311        Work work0 = new Work.Builder(TestWorker.class)
312                .withInitialDelay(expectedInitialDelay, TimeUnit.MILLISECONDS)
313                .build();
314        Work work1 = new Work.Builder(TestWorker.class).build();
315        mWorkManagerImpl.beginWith(work0).then(work1).enqueue();
316
317        WorkSpec workSpec0 = mDatabase.workSpecDao().getWorkSpec(work0.getId());
318        WorkSpec workSpec1 = mDatabase.workSpecDao().getWorkSpec(work1.getId());
319
320        assertThat(workSpec0.getInitialDelay(), is(expectedInitialDelay));
321        assertThat(workSpec1.getInitialDelay(), is(0L));
322    }
323
324    @Test
325    @SmallTest
326    public void testEnqueue_insertWorkBackoffPolicy() {
327        Work work0 = new Work.Builder(TestWorker.class)
328                .withBackoffCriteria(BackoffPolicy.LINEAR, 50000, TimeUnit.MILLISECONDS)
329                .build();
330        Work work1 = new Work.Builder(TestWorker.class).build();
331        mWorkManagerImpl.beginWith(work0).then(work1).enqueue();
332
333        WorkSpec workSpec0 = mDatabase.workSpecDao().getWorkSpec(work0.getId());
334        WorkSpec workSpec1 = mDatabase.workSpecDao().getWorkSpec(work1.getId());
335
336        assertThat(workSpec0.getBackoffPolicy(), is(BackoffPolicy.LINEAR));
337        assertThat(workSpec0.getBackoffDelayDuration(), is(50000L));
338
339        assertThat(workSpec1.getBackoffPolicy(), is(BackoffPolicy.EXPONENTIAL));
340        assertThat(workSpec1.getBackoffDelayDuration(), is(BaseWork.DEFAULT_BACKOFF_DELAY_MILLIS));
341    }
342
343    @Test
344    @SmallTest
345    public void testEnqueue_insertWorkTags() {
346        final String firstTag = "first_tag";
347        final String secondTag = "second_tag";
348        final String thirdTag = "third_tag";
349
350        Work work0 = new Work.Builder(TestWorker.class).addTag(firstTag).addTag(secondTag).build();
351        Work work1 = new Work.Builder(TestWorker.class).addTag(firstTag).build();
352        Work work2 = new Work.Builder(TestWorker.class).build();
353        mWorkManagerImpl.beginWith(work0).then(work1).then(work2).enqueue();
354
355        WorkTagDao workTagDao = mDatabase.workTagDao();
356        assertThat(workTagDao.getWorkSpecsWithTag(firstTag),
357                containsInAnyOrder(work0.getId(), work1.getId()));
358        assertThat(workTagDao.getWorkSpecsWithTag(secondTag), containsInAnyOrder(work0.getId()));
359        assertThat(workTagDao.getWorkSpecsWithTag(thirdTag), emptyCollectionOf(String.class));
360    }
361
362    @Test
363    @SmallTest
364    public void testEnqueue_insertPeriodicWork() {
365        PeriodicWork periodicWork = new PeriodicWork.Builder(
366                TestWorker.class,
367                PeriodicWork.MIN_PERIODIC_INTERVAL_MILLIS,
368                TimeUnit.MILLISECONDS)
369                .build();
370        mWorkManagerImpl.enqueue(periodicWork);
371
372        WorkSpec workSpec = mDatabase.workSpecDao().getWorkSpec(periodicWork.getId());
373        assertThat(workSpec.isPeriodic(), is(true));
374        assertThat(workSpec.getIntervalDuration(), is(PeriodicWork.MIN_PERIODIC_INTERVAL_MILLIS));
375        assertThat(workSpec.getFlexDuration(), is(PeriodicWork.MIN_PERIODIC_INTERVAL_MILLIS));
376    }
377
378    @Test
379    @SmallTest
380    public void testEnqueued_work_setsPeriodStartTime() {
381        Work work = new Work.Builder(TestWorker.class).build();
382        assertThat(getWorkSpec(work).getPeriodStartTime(), is(0L));
383
384        long beforeEnqueueTime = System.currentTimeMillis();
385
386        mWorkManagerImpl.enqueue(work);
387
388        WorkSpec workSpec = mDatabase.workSpecDao().getWorkSpec(work.getId());
389        assertThat(workSpec.getPeriodStartTime(), is(greaterThanOrEqualTo(beforeEnqueueTime)));
390    }
391
392    @Test
393    @SmallTest
394    public void testEnqueued_periodicWork_setsPeriodStartTime() {
395        PeriodicWork periodicWork = new PeriodicWork.Builder(
396                TestWorker.class,
397                PeriodicWork.MIN_PERIODIC_INTERVAL_MILLIS,
398                TimeUnit.MILLISECONDS)
399                .build();
400        assertThat(getWorkSpec(periodicWork).getPeriodStartTime(), is(0L));
401
402        long beforeEnqueueTime = System.currentTimeMillis();
403
404        mWorkManagerImpl.enqueue(periodicWork);
405
406        WorkSpec workSpec = mDatabase.workSpecDao().getWorkSpec(periodicWork.getId());
407        assertThat(workSpec.getPeriodStartTime(), is(greaterThanOrEqualTo(beforeEnqueueTime)));
408    }
409
410    @Test
411    @SmallTest
412    public void testUniqueTagSequence_setsUniqueTag() {
413        final String testTag = "mytag";
414
415        Work work = new Work.Builder(TestWorker.class).build();
416        mWorkManagerImpl.beginWithUniqueTag(testTag, REPLACE)
417                .then(work)
418                .enqueue();
419
420        List<String> workSpecIds = mDatabase.workTagDao().getWorkSpecsWithTag(testTag);
421        assertThat(work.getId(), isIn(workSpecIds));
422    }
423
424    @Test
425    @SmallTest
426    public void testUniqueTagSequence_deletesOldTagsOnReplace() {
427        final String testTag = "mytag";
428
429        Work originalWork = new Work.Builder(TestWorker.class).addTag(testTag).build();
430        insertWorkSpecAndTags(originalWork);
431
432        Work replacementWork1 = new Work.Builder(TestWorker.class).build();
433        Work replacementWork2 = new Work.Builder(TestWorker.class).build();
434        mWorkManagerImpl
435                .beginWithUniqueTag(testTag, REPLACE, replacementWork1)
436                .then(replacementWork2)
437                .enqueue();
438
439        List<String> workSpecIds = mDatabase.workTagDao().getWorkSpecsWithTag(testTag);
440        assertThat(
441                workSpecIds,
442                containsInAnyOrder(replacementWork1.getId(), replacementWork2.getId()));
443
444        WorkSpecDao workSpecDao = mDatabase.workSpecDao();
445        assertThat(workSpecDao.getWorkSpec(originalWork.getId()), is(nullValue()));
446        assertThat(workSpecDao.getWorkSpec(replacementWork1.getId()), is(not(nullValue())));
447        assertThat(workSpecDao.getWorkSpec(replacementWork2.getId()), is(not(nullValue())));
448    }
449
450    @Test
451    @SmallTest
452    public void testUniqueTagSequence_keepsExistingWorkOnKeep() {
453        final String testTag = "mytag";
454
455        Work originalWork = new Work.Builder(TestWorker.class).addTag(testTag).build();
456        insertWorkSpecAndTags(originalWork);
457
458        Work replacementWork1 = new Work.Builder(TestWorker.class).build();
459        Work replacementWork2 = new Work.Builder(TestWorker.class).build();
460        mWorkManagerImpl
461                .beginWithUniqueTag(testTag, KEEP, replacementWork1)
462                .then(replacementWork2)
463                .enqueue();
464
465        List<String> workSpecIds = mDatabase.workTagDao().getWorkSpecsWithTag(testTag);
466        assertThat(workSpecIds, containsInAnyOrder(originalWork.getId()));
467
468        WorkSpecDao workSpecDao = mDatabase.workSpecDao();
469        assertThat(workSpecDao.getWorkSpec(originalWork.getId()), is(not(nullValue())));
470        assertThat(workSpecDao.getWorkSpec(replacementWork1.getId()), is(nullValue()));
471        assertThat(workSpecDao.getWorkSpec(replacementWork2.getId()), is(nullValue()));
472    }
473
474    @Test
475    @SmallTest
476    public void testUniqueTagSequence_replacesExistingWorkOnKeepWhenExistingWorkIsFinished() {
477        final String testTag = "mytag";
478
479        Work originalWork = new Work.Builder(TestWorker.class)
480                .withInitialState(SUCCEEDED)
481                .addTag(testTag)
482                .build();
483        insertWorkSpecAndTags(originalWork);
484
485        Work replacementWork1 = new Work.Builder(TestWorker.class).build();
486        Work replacementWork2 = new Work.Builder(TestWorker.class).build();
487        mWorkManagerImpl
488                .beginWithUniqueTag(testTag, KEEP, replacementWork1)
489                .then(replacementWork2)
490                .enqueue();
491
492        List<String> workSpecIds = mDatabase.workTagDao().getWorkSpecsWithTag(testTag);
493        assertThat(workSpecIds,
494                containsInAnyOrder(replacementWork1.getId(), replacementWork2.getId()));
495
496        WorkSpecDao workSpecDao = mDatabase.workSpecDao();
497        assertThat(workSpecDao.getWorkSpec(originalWork.getId()), is(nullValue()));
498        assertThat(workSpecDao.getWorkSpec(replacementWork1.getId()), is(not(nullValue())));
499        assertThat(workSpecDao.getWorkSpec(replacementWork2.getId()), is(not(nullValue())));
500    }
501
502    @Test
503    @SmallTest
504    public void testUniqueTagSequence_appendsExistingWorkOnAppend() {
505        final String testTag = "mytag";
506
507        Work originalWork = new Work.Builder(TestWorker.class).addTag(testTag).build();
508        insertWorkSpecAndTags(originalWork);
509
510        Work appendWork1 = new Work.Builder(TestWorker.class).build();
511        Work appendWork2 = new Work.Builder(TestWorker.class).build();
512        mWorkManagerImpl
513                .beginWithUniqueTag(testTag, APPEND, appendWork1)
514                .then(appendWork2)
515                .enqueue();
516
517        List<String> workSpecIds = mDatabase.workTagDao().getWorkSpecsWithTag(testTag);
518        assertThat(workSpecIds,
519                containsInAnyOrder(originalWork.getId(), appendWork1.getId(), appendWork2.getId()));
520
521        WorkSpecDao workSpecDao = mDatabase.workSpecDao();
522        assertThat(workSpecDao.getWorkSpec(originalWork.getId()), is(not(nullValue())));
523        assertThat(workSpecDao.getState(appendWork1.getId()), is(BLOCKED));
524        assertThat(workSpecDao.getState(appendWork2.getId()), is(BLOCKED));
525
526        assertThat(mDatabase.dependencyDao().getDependentWorkIds(originalWork.getId()),
527                containsInAnyOrder(appendWork1.getId()));
528    }
529
530    @Test
531    @SmallTest
532    public void testUniqueTagSequence_appendsExistingWorkToOnlyLeavesOnAppend() {
533        final String testTag = "mytag";
534
535        Work originalWork1 = new Work.Builder(TestWorker.class).addTag(testTag).build();
536        Work originalWork2 = new Work.Builder(TestWorker.class).addTag(testTag).build();
537        Work originalWork3 = new Work.Builder(TestWorker.class).addTag(testTag).build();
538        Work originalWork4 = new Work.Builder(TestWorker.class).addTag(testTag).build();
539        insertWorkSpecAndTags(originalWork1);
540        insertWorkSpecAndTags(originalWork2);
541        insertWorkSpecAndTags(originalWork3);
542        insertWorkSpecAndTags(originalWork4);
543
544        Dependency dependency21 = new Dependency(originalWork2.getId(), originalWork1.getId());
545        Dependency dependency32 = new Dependency(originalWork3.getId(), originalWork2.getId());
546        Dependency dependency42 = new Dependency(originalWork4.getId(), originalWork2.getId());
547
548        DependencyDao dependencyDao = mDatabase.dependencyDao();
549        dependencyDao.insertDependency(dependency21);
550        dependencyDao.insertDependency(dependency32);
551        dependencyDao.insertDependency(dependency42);
552
553        Work appendWork1 = new Work.Builder(TestWorker.class).build();
554        Work appendWork2 = new Work.Builder(TestWorker.class).build();
555        mWorkManagerImpl
556                .beginWithUniqueTag(testTag, APPEND, appendWork1)
557                .then(appendWork2)
558                .enqueue();
559
560        List<String> workSpecIds = mDatabase.workTagDao().getWorkSpecsWithTag(testTag);
561        assertThat(workSpecIds,
562                containsInAnyOrder(
563                        originalWork1.getId(),
564                        originalWork2.getId(),
565                        originalWork3.getId(),
566                        originalWork4.getId(),
567                        appendWork1.getId(),
568                        appendWork2.getId()));
569
570        WorkSpecDao workSpecDao = mDatabase.workSpecDao();
571        assertThat(workSpecDao.getWorkSpec(originalWork1.getId()), is(not(nullValue())));
572        assertThat(workSpecDao.getWorkSpec(originalWork2.getId()), is(not(nullValue())));
573        assertThat(workSpecDao.getWorkSpec(originalWork3.getId()), is(not(nullValue())));
574        assertThat(workSpecDao.getWorkSpec(originalWork4.getId()), is(not(nullValue())));
575        assertThat(workSpecDao.getState(appendWork1.getId()), is(BLOCKED));
576        assertThat(workSpecDao.getState(appendWork2.getId()), is(BLOCKED));
577
578        assertThat(dependencyDao.getPrerequisites(appendWork1.getId()),
579                containsInAnyOrder(originalWork3.getId(), originalWork4.getId()));
580        assertThat(dependencyDao.getPrerequisites(appendWork2.getId()),
581                containsInAnyOrder(appendWork1.getId()));
582    }
583
584    @Test
585    @SmallTest
586    public void testUniqueTagSequence_insertsExistingWorkWhenNothingToAppendTo() {
587        final String testTag = "mytag";
588
589        Work appendWork1 = new Work.Builder(TestWorker.class).build();
590        Work appendWork2 = new Work.Builder(TestWorker.class).build();
591        mWorkManagerImpl
592                .beginWithUniqueTag(testTag, APPEND, appendWork1)
593                .then(appendWork2)
594                .enqueue();
595
596        List<String> workSpecIds = mDatabase.workTagDao().getWorkSpecsWithTag(testTag);
597        assertThat(workSpecIds,
598                containsInAnyOrder(appendWork1.getId(), appendWork2.getId()));
599    }
600
601    @Test
602    @SmallTest
603    public void testGetStatusSync() {
604        Work work = new Work.Builder(TestWorker.class).withInitialState(SUCCEEDED).build();
605        insertWorkSpecAndTags(work);
606
607        WorkStatus workStatus = mWorkManagerImpl.getStatusSync(work.getId());
608        assertThat(workStatus.getId(), is(work.getId()));
609        assertThat(workStatus.getState(), is(SUCCEEDED));
610    }
611
612    @Test
613    @SmallTest
614    public void testGetStatusSync_ReturnsNullIfNotInDatabase() {
615        WorkStatus workStatus = mWorkManagerImpl.getStatusSync("dummy");
616        assertThat(workStatus, is(nullValue()));
617    }
618
619    @Test
620    @SmallTest
621    @SuppressWarnings("unchecked")
622    public void testGetStatuses() {
623        Work work0 = new Work.Builder(TestWorker.class).build();
624        Work work1 = new Work.Builder(TestWorker.class).build();
625        insertWorkSpecAndTags(work0);
626        insertWorkSpecAndTags(work1);
627
628        Observer<List<WorkStatus>> mockObserver = mock(Observer.class);
629
630        TestLifecycleOwner testLifecycleOwner = new TestLifecycleOwner();
631        LiveData<List<WorkStatus>> liveData =
632                mWorkManagerImpl.getStatuses(Arrays.asList(work0.getId(), work1.getId()));
633        liveData.observe(testLifecycleOwner, mockObserver);
634
635        ArgumentCaptor<List<WorkStatus>> captor =
636                ArgumentCaptor.forClass(List.class);
637        verify(mockObserver).onChanged(captor.capture());
638        assertThat(captor.getValue(), is(not(nullValue())));
639        assertThat(captor.getValue().size(), is(2));
640
641        WorkStatus workStatus0 = new WorkStatus(work0.getId(), ENQUEUED, Arguments.EMPTY);
642        WorkStatus workStatus1 = new WorkStatus(work1.getId(), ENQUEUED, Arguments.EMPTY);
643        assertThat(captor.getValue(), containsInAnyOrder(workStatus0, workStatus1));
644
645        WorkSpecDao workSpecDao = mDatabase.workSpecDao();
646        workSpecDao.setState(RUNNING, work0.getId());
647
648        verify(mockObserver, times(2)).onChanged(captor.capture());
649        assertThat(captor.getValue(), is(not(nullValue())));
650        assertThat(captor.getValue().size(), is(2));
651
652        workStatus0 = new WorkStatus(work0.getId(), RUNNING, Arguments.EMPTY);
653        assertThat(captor.getValue(), containsInAnyOrder(workStatus0, workStatus1));
654
655        clearInvocations(mockObserver);
656        workSpecDao.setState(RUNNING, work1.getId());
657
658        verify(mockObserver).onChanged(captor.capture());
659        assertThat(captor.getValue(), is(not(nullValue())));
660        assertThat(captor.getValue().size(), is(2));
661
662        workStatus1 = new WorkStatus(work1.getId(), RUNNING, Arguments.EMPTY);
663        assertThat(captor.getValue(), containsInAnyOrder(workStatus0, workStatus1));
664
665        liveData.removeObservers(testLifecycleOwner);
666    }
667
668    @Test
669    @SmallTest
670    public void testCancelWorkForId() {
671        WorkSpecDao workSpecDao = mDatabase.workSpecDao();
672
673        Work work0 = new Work.Builder(TestWorker.class).build();
674        Work work1 = new Work.Builder(TestWorker.class).build();
675        insertWorkSpecAndTags(work0);
676        insertWorkSpecAndTags(work1);
677
678        mWorkManagerImpl.cancelWorkById(work0.getId());
679
680        assertThat(workSpecDao.getState(work0.getId()), is(CANCELLED));
681        assertThat(workSpecDao.getState(work1.getId()), is(not(CANCELLED)));
682    }
683
684    @Test
685    @SmallTest
686    public void testCancelWorkForId_cancelsDependentWork() {
687        WorkSpecDao workSpecDao = mDatabase.workSpecDao();
688
689        Work work0 = new Work.Builder(TestWorker.class).build();
690        Work work1 = new Work.Builder(TestWorker.class).withInitialState(BLOCKED).build();
691        insertWorkSpecAndTags(work0);
692        insertWorkSpecAndTags(work1);
693
694        Dependency dependency10 = new Dependency(work1.getId(), work0.getId());
695
696        DependencyDao dependencyDao = mDatabase.dependencyDao();
697        dependencyDao.insertDependency(dependency10);
698
699        mWorkManagerImpl.cancelWorkById(work0.getId());
700
701        assertThat(workSpecDao.getState(work0.getId()), is(CANCELLED));
702        assertThat(workSpecDao.getState(work1.getId()), is(CANCELLED));
703    }
704
705    @Test
706    @SmallTest
707    public void testCancelWorkForId_cancelsUnfinishedWorkOnly() {
708        WorkSpecDao workSpecDao = mDatabase.workSpecDao();
709
710        Work work0 = new Work.Builder(TestWorker.class).withInitialState(SUCCEEDED).build();
711        Work work1 = new Work.Builder(TestWorker.class).withInitialState(ENQUEUED).build();
712        insertWorkSpecAndTags(work0);
713        insertWorkSpecAndTags(work1);
714
715        Dependency dependency10 = new Dependency(work1.getId(), work0.getId());
716
717        DependencyDao dependencyDao = mDatabase.dependencyDao();
718        dependencyDao.insertDependency(dependency10);
719
720        mWorkManagerImpl.cancelWorkById(work0.getId());
721
722        assertThat(workSpecDao.getState(work0.getId()), is(SUCCEEDED));
723        assertThat(workSpecDao.getState(work1.getId()), is(CANCELLED));
724    }
725
726    @Test
727    @SmallTest
728    public void testCancelAllWorkWithTag() {
729        WorkSpecDao workSpecDao = mDatabase.workSpecDao();
730
731        final String tagToClear = "tag_to_clear";
732        final String tagNotToClear = "tag_not_to_clear";
733
734        Work work0 = new Work.Builder(TestWorker.class).addTag(tagToClear).build();
735        Work work1 = new Work.Builder(TestWorker.class).addTag(tagToClear).build();
736        Work work2 = new Work.Builder(TestWorker.class).addTag(tagNotToClear).build();
737        Work work3 = new Work.Builder(TestWorker.class).addTag(tagNotToClear).build();
738        insertWorkSpecAndTags(work0);
739        insertWorkSpecAndTags(work1);
740        insertWorkSpecAndTags(work2);
741        insertWorkSpecAndTags(work3);
742
743        mWorkManagerImpl.cancelAllWorkWithTag(tagToClear);
744
745        assertThat(workSpecDao.getState(work0.getId()), is(CANCELLED));
746        assertThat(workSpecDao.getState(work1.getId()), is(CANCELLED));
747        assertThat(workSpecDao.getState(work2.getId()), is(not(CANCELLED)));
748        assertThat(workSpecDao.getState(work3.getId()), is(not(CANCELLED)));
749    }
750
751    @Test
752    @SmallTest
753    public void testCancelAllWorkWithTag_cancelsDependentWork() {
754        String tag = "tag";
755
756        Work work0 = new Work.Builder(TestWorker.class).addTag(tag).build();
757        Work work1 = new Work.Builder(TestWorker.class).build();
758        Work work2 = new Work.Builder(TestWorker.class).build();
759        Work work3 = new Work.Builder(TestWorker.class).build();
760        Work work4 = new Work.Builder(TestWorker.class).build();
761
762        insertWorkSpecAndTags(work0);
763        insertWorkSpecAndTags(work1);
764        insertWorkSpecAndTags(work2);
765        insertWorkSpecAndTags(work3);
766        insertWorkSpecAndTags(work4);
767
768        // Dependency graph:
769        //                             0
770        //                             |
771        //                       |------------|
772        //            3          1            4
773        //            |          |
774        //            ------------
775        //                 |
776        //                 2
777
778        Dependency dependency21 = new Dependency(work2.getId(), work1.getId());
779        Dependency dependency23 = new Dependency(work2.getId(), work3.getId());
780        Dependency dependency10 = new Dependency(work1.getId(), work0.getId());
781        Dependency dependency40 = new Dependency(work4.getId(), work0.getId());
782
783        DependencyDao dependencyDao = mDatabase.dependencyDao();
784        dependencyDao.insertDependency(dependency21);
785        dependencyDao.insertDependency(dependency23);
786        dependencyDao.insertDependency(dependency10);
787        dependencyDao.insertDependency(dependency40);
788
789        mWorkManagerImpl.cancelAllWorkWithTag(tag);
790
791        WorkSpecDao workSpecDao = mDatabase.workSpecDao();
792        assertThat(workSpecDao.getState(work0.getId()), is(CANCELLED));
793        assertThat(workSpecDao.getState(work1.getId()), is(CANCELLED));
794        assertThat(workSpecDao.getState(work2.getId()), is(CANCELLED));
795        assertThat(workSpecDao.getState(work3.getId()), is(not(CANCELLED)));
796        assertThat(workSpecDao.getState(work4.getId()), is(CANCELLED));
797    }
798
799    @Test
800    @SmallTest
801    public void testSynchronousCancelAndGetStatus() {
802        Work work = new Work.Builder(TestWorker.class).build();
803        insertWorkSpecAndTags(work);
804
805        WorkSpecDao workSpecDao = mDatabase.workSpecDao();
806        assertThat(workSpecDao.getState(work.getId()), is(ENQUEUED));
807
808        mWorkManagerImpl.cancelWorkByIdSync(work.getId());
809        assertThat(mWorkManagerImpl.getStatusSync(work.getId()).getState(), is(CANCELLED));
810    }
811
812    @Test
813    @SmallTest
814    public void testGenerateCleanupCallback_resetsRunningWorkStatuses() {
815        WorkSpecDao workSpecDao = mDatabase.workSpecDao();
816
817        Work work = new Work.Builder(TestWorker.class).withInitialState(RUNNING).build();
818        workSpecDao.insertWorkSpec(getWorkSpec(work));
819
820        assertThat(workSpecDao.getState(work.getId()), is(RUNNING));
821
822        SupportSQLiteOpenHelper openHelper = mDatabase.getOpenHelper();
823        SupportSQLiteDatabase db = openHelper.getWritableDatabase();
824        WorkDatabase.generateCleanupCallback().onOpen(db);
825
826        assertThat(workSpecDao.getState(work.getId()), is(ENQUEUED));
827    }
828
829    @Test
830    @SmallTest
831    public void testGenerateCleanupCallback_deletesOldFinishedWork() {
832        Work work1 = new Work.Builder(TestWorker.class)
833                .withInitialState(SUCCEEDED)
834                .withPeriodStartTime(WorkDatabase.getPruneDate() - 1L, TimeUnit.MILLISECONDS)
835                .build();
836        Work work2 = new Work.Builder(TestWorker.class)
837                .withPeriodStartTime(Long.MAX_VALUE, TimeUnit.MILLISECONDS)
838                .build();
839
840        insertWorkSpecAndTags(work1);
841        insertWorkSpecAndTags(work2);
842
843        SupportSQLiteOpenHelper openHelper = mDatabase.getOpenHelper();
844        SupportSQLiteDatabase db = openHelper.getWritableDatabase();
845        WorkDatabase.generateCleanupCallback().onOpen(db);
846
847        WorkSpecDao workSpecDao = mDatabase.workSpecDao();
848        assertThat(workSpecDao.getWorkSpec(work1.getId()), is(nullValue()));
849        assertThat(workSpecDao.getWorkSpec(work2.getId()), is(not(nullValue())));
850    }
851
852    @Test
853    @SmallTest
854    public void testGenerateCleanupCallback_doesNotDeleteOldFinishedWorkWithActiveDependents() {
855        Work work0 = new Work.Builder(TestWorker.class)
856                .withInitialState(SUCCEEDED)
857                .withPeriodStartTime(WorkDatabase.getPruneDate() - 1L, TimeUnit.MILLISECONDS)
858                .build();
859        Work work1 = new Work.Builder(TestWorker.class)
860                .withInitialState(SUCCEEDED)
861                .withPeriodStartTime(WorkDatabase.getPruneDate() - 1L, TimeUnit.MILLISECONDS)
862                .build();
863        Work work2 = new Work.Builder(TestWorker.class)
864                .withInitialState(ENQUEUED)
865                .withPeriodStartTime(WorkDatabase.getPruneDate() - 1L, TimeUnit.MILLISECONDS)
866                .build();
867
868        insertWorkSpecAndTags(work0);
869        insertWorkSpecAndTags(work1);
870        insertWorkSpecAndTags(work2);
871
872        // Dependency graph: 0 -> 1 -> 2
873
874        Dependency dependency10 = new Dependency(work1.getId(), work0.getId());
875        Dependency dependency21 = new Dependency(work2.getId(), work1.getId());
876        DependencyDao dependencyDao = mDatabase.dependencyDao();
877        dependencyDao.insertDependency(dependency10);
878        dependencyDao.insertDependency(dependency21);
879
880        SupportSQLiteOpenHelper openHelper = mDatabase.getOpenHelper();
881        SupportSQLiteDatabase db = openHelper.getWritableDatabase();
882        WorkDatabase.generateCleanupCallback().onOpen(db);
883
884        WorkSpecDao workSpecDao = mDatabase.workSpecDao();
885        assertThat(workSpecDao.getWorkSpec(work0.getId()), is(nullValue()));
886        assertThat(workSpecDao.getWorkSpec(work1.getId()), is(not(nullValue())));
887        assertThat(workSpecDao.getWorkSpec(work2.getId()), is(not(nullValue())));
888    }
889
890    private void insertWorkSpecAndTags(Work work) {
891        mDatabase.workSpecDao().insertWorkSpec(getWorkSpec(work));
892        for (String tag : getTags(work)) {
893            mDatabase.workTagDao().insert(new WorkTag(tag, work.getId()));
894        }
895    }
896}
897