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