WorkManagerImplTest.java revision 22499da21adf6c36e4f20938ba5296933e0c099a
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 androidx.work.ExistingWorkPolicy.APPEND;
20import static androidx.work.ExistingWorkPolicy.KEEP;
21import static androidx.work.ExistingWorkPolicy.REPLACE;
22import static androidx.work.NetworkType.METERED;
23import static androidx.work.NetworkType.NOT_REQUIRED;
24import static androidx.work.State.BLOCKED;
25import static androidx.work.State.CANCELLED;
26import static androidx.work.State.ENQUEUED;
27import static androidx.work.State.FAILED;
28import static androidx.work.State.RUNNING;
29import static androidx.work.State.SUCCEEDED;
30
31import static org.hamcrest.CoreMatchers.is;
32import static org.hamcrest.CoreMatchers.not;
33import static org.hamcrest.CoreMatchers.notNullValue;
34import static org.hamcrest.CoreMatchers.nullValue;
35import static org.hamcrest.MatcherAssert.assertThat;
36import static org.hamcrest.Matchers.contains;
37import static org.hamcrest.Matchers.containsInAnyOrder;
38import static org.hamcrest.Matchers.emptyCollectionOf;
39import static org.hamcrest.Matchers.greaterThanOrEqualTo;
40import static org.hamcrest.Matchers.isIn;
41import static org.hamcrest.Matchers.isOneOf;
42import static org.hamcrest.Matchers.lessThanOrEqualTo;
43import static org.mockito.Mockito.clearInvocations;
44import static org.mockito.Mockito.mock;
45import static org.mockito.Mockito.times;
46import static org.mockito.Mockito.verify;
47
48import android.arch.core.executor.ArchTaskExecutor;
49import android.arch.core.executor.TaskExecutor;
50import android.arch.lifecycle.LiveData;
51import android.arch.lifecycle.Observer;
52import android.arch.persistence.db.SupportSQLiteDatabase;
53import android.arch.persistence.db.SupportSQLiteOpenHelper;
54import android.content.Context;
55import android.net.Uri;
56import android.os.Build;
57import android.provider.MediaStore;
58import android.support.annotation.NonNull;
59import android.support.test.InstrumentationRegistry;
60import android.support.test.filters.LargeTest;
61import android.support.test.filters.SdkSuppress;
62import android.support.test.filters.SmallTest;
63import android.support.test.runner.AndroidJUnit4;
64
65import androidx.work.BackoffPolicy;
66import androidx.work.Configuration;
67import androidx.work.Constraints;
68import androidx.work.ContentUriTriggers;
69import androidx.work.Data;
70import androidx.work.ExistingPeriodicWorkPolicy;
71import androidx.work.OneTimeWorkRequest;
72import androidx.work.PeriodicWorkRequest;
73import androidx.work.TestLifecycleOwner;
74import androidx.work.WorkContinuation;
75import androidx.work.WorkRequest;
76import androidx.work.WorkStatus;
77import androidx.work.impl.model.Dependency;
78import androidx.work.impl.model.DependencyDao;
79import androidx.work.impl.model.WorkName;
80import androidx.work.impl.model.WorkSpec;
81import androidx.work.impl.model.WorkSpecDao;
82import androidx.work.impl.model.WorkTag;
83import androidx.work.impl.model.WorkTagDao;
84import androidx.work.impl.utils.taskexecutor.InstantTaskExecutorRule;
85import androidx.work.impl.workers.ConstraintTrackingWorker;
86import androidx.work.worker.InfiniteTestWorker;
87import androidx.work.worker.TestWorker;
88
89import org.junit.After;
90import org.junit.Before;
91import org.junit.Rule;
92import org.junit.Test;
93import org.junit.runner.RunWith;
94import org.mockito.ArgumentCaptor;
95
96import java.util.Arrays;
97import java.util.Collections;
98import java.util.List;
99import java.util.UUID;
100import java.util.concurrent.Executors;
101import java.util.concurrent.TimeUnit;
102
103@RunWith(AndroidJUnit4.class)
104public class WorkManagerImplTest {
105
106    private static final int NUM_WORKERS = 500;
107
108    private WorkDatabase mDatabase;
109    private WorkManagerImpl mWorkManagerImpl;
110
111    @Rule
112    public InstantTaskExecutorRule mRule = new InstantTaskExecutorRule();
113
114    @Before
115    public void setUp() {
116        ArchTaskExecutor.getInstance().setDelegate(new TaskExecutor() {
117            @Override
118            public void executeOnDiskIO(@NonNull Runnable runnable) {
119                runnable.run();
120            }
121
122            @Override
123            public void postToMainThread(@NonNull Runnable runnable) {
124                runnable.run();
125            }
126
127            @Override
128            public boolean isMainThread() {
129                return true;
130            }
131        });
132
133        Context context = InstrumentationRegistry.getTargetContext();
134        Configuration configuration = new Configuration.Builder()
135                .setExecutor(Executors.newSingleThreadExecutor())
136                .build();
137        mWorkManagerImpl = new WorkManagerImpl(context, configuration);
138        WorkManagerImpl.setDelegate(mWorkManagerImpl);
139        mDatabase = mWorkManagerImpl.getWorkDatabase();
140    }
141
142    @After
143    public void tearDown() {
144        WorkManagerImpl.setDelegate(null);
145        ArchTaskExecutor.getInstance().setDelegate(null);
146    }
147
148    @Test
149    @SmallTest
150    public void testEnqueue_insertWork() throws InterruptedException {
151        final int workCount = 3;
152        final OneTimeWorkRequest[] workArray = new OneTimeWorkRequest[workCount];
153        for (int i = 0; i < workCount; ++i) {
154            workArray[i] = new OneTimeWorkRequest.Builder(TestWorker.class).build();
155        }
156
157        mWorkManagerImpl.beginWith(workArray[0]).then(workArray[1])
158                .then(workArray[2])
159                .synchronous().enqueueSync();
160
161        for (int i = 0; i < workCount; ++i) {
162            String id = workArray[i].getStringId();
163            assertThat(mDatabase.workSpecDao().getWorkSpec(id), is(notNullValue()));
164            assertThat(
165                    "index " + i + " does not have expected number of dependencies!",
166                    mDatabase.dependencyDao().getPrerequisites(id).size() > 0,
167                    is(i > 0));
168        }
169    }
170
171    @Test
172    @SmallTest
173    public void testEnqueue_insertMultipleWork() {
174        OneTimeWorkRequest work1 = new OneTimeWorkRequest.Builder(TestWorker.class).build();
175        OneTimeWorkRequest work2 = new OneTimeWorkRequest.Builder(TestWorker.class).build();
176        OneTimeWorkRequest work3 = new OneTimeWorkRequest.Builder(TestWorker.class).build();
177
178        mWorkManagerImpl.synchronous().enqueueSync(work1, work2, work3);
179
180        WorkSpecDao workSpecDao = mDatabase.workSpecDao();
181        assertThat(workSpecDao.getWorkSpec(work1.getStringId()), is(notNullValue()));
182        assertThat(workSpecDao.getWorkSpec(work2.getStringId()), is(notNullValue()));
183        assertThat(workSpecDao.getWorkSpec(work3.getStringId()), is(notNullValue()));
184    }
185
186    @Test
187    @SmallTest
188    public void testEnqueue_insertMultipleWork_continuationBlocking() {
189        OneTimeWorkRequest work1 = new OneTimeWorkRequest.Builder(TestWorker.class).build();
190        OneTimeWorkRequest work2 = new OneTimeWorkRequest.Builder(TestWorker.class).build();
191        OneTimeWorkRequest work3 = new OneTimeWorkRequest.Builder(TestWorker.class).build();
192
193        mWorkManagerImpl.beginWith(work1, work2, work3)
194                .synchronous()
195                .enqueueSync();
196
197        WorkSpecDao workSpecDao = mDatabase.workSpecDao();
198        assertThat(workSpecDao.getWorkSpec(work1.getStringId()), is(notNullValue()));
199        assertThat(workSpecDao.getWorkSpec(work2.getStringId()), is(notNullValue()));
200        assertThat(workSpecDao.getWorkSpec(work3.getStringId()), is(notNullValue()));
201    }
202
203    @Test
204    @SmallTest
205    public void testEnqueue_insertWithDependencies() {
206        OneTimeWorkRequest work1a = new OneTimeWorkRequest.Builder(TestWorker.class).build();
207        OneTimeWorkRequest work1b = new OneTimeWorkRequest.Builder(TestWorker.class).build();
208        OneTimeWorkRequest work2 = new OneTimeWorkRequest.Builder(TestWorker.class).build();
209        OneTimeWorkRequest work3a = new OneTimeWorkRequest.Builder(TestWorker.class).build();
210        OneTimeWorkRequest work3b = new OneTimeWorkRequest.Builder(TestWorker.class).build();
211
212        mWorkManagerImpl.beginWith(work1a, work1b).then(work2)
213                .then(work3a, work3b)
214                .synchronous()
215                .enqueueSync();
216
217        WorkSpecDao workSpecDao = mDatabase.workSpecDao();
218        assertThat(workSpecDao.getWorkSpec(work1a.getStringId()), is(notNullValue()));
219        assertThat(workSpecDao.getWorkSpec(work1b.getStringId()), is(notNullValue()));
220        assertThat(workSpecDao.getWorkSpec(work2.getStringId()), is(notNullValue()));
221        assertThat(workSpecDao.getWorkSpec(work3a.getStringId()), is(notNullValue()));
222        assertThat(workSpecDao.getWorkSpec(work3b.getStringId()), is(notNullValue()));
223
224        DependencyDao dependencyDao = mDatabase.dependencyDao();
225        assertThat(dependencyDao.getPrerequisites(work1a.getStringId()),
226                is(emptyCollectionOf(String.class)));
227        assertThat(dependencyDao.getPrerequisites(work1b.getStringId()),
228                is(emptyCollectionOf(String.class)));
229
230        List<String> prerequisites = dependencyDao.getPrerequisites(work2.getStringId());
231        assertThat(prerequisites, containsInAnyOrder(work1a.getStringId(), work1b.getStringId()));
232
233        prerequisites = dependencyDao.getPrerequisites(work3a.getStringId());
234        assertThat(prerequisites, containsInAnyOrder(work2.getStringId()));
235
236        prerequisites = dependencyDao.getPrerequisites(work3b.getStringId());
237        assertThat(prerequisites, containsInAnyOrder(work2.getStringId()));
238    }
239
240    @Test
241    @SmallTest
242    public void testEnqueue_insertWithCompletedDependencies_isNotStatusBlocked() {
243        OneTimeWorkRequest work1 = new OneTimeWorkRequest.Builder(TestWorker.class)
244                .setInitialState(SUCCEEDED)
245                .build();
246
247        WorkContinuation workContinuation = mWorkManagerImpl.beginWith(work1);
248        workContinuation.synchronous().enqueueSync();
249        WorkSpecDao workSpecDao = mDatabase.workSpecDao();
250        assertThat(workSpecDao.getState(work1.getStringId()), is(SUCCEEDED));
251
252        OneTimeWorkRequest work2 = new OneTimeWorkRequest.Builder(InfiniteTestWorker.class).build();
253        workContinuation.then(work2).synchronous().enqueueSync();
254        assertThat(workSpecDao.getState(work2.getStringId()), isOneOf(ENQUEUED, RUNNING));
255    }
256
257    @Test
258    @SmallTest
259    public void testEnqueue_insertWithFailedDependencies_isStatusFailed() {
260        OneTimeWorkRequest work1 = new OneTimeWorkRequest.Builder(TestWorker.class)
261                .setInitialState(FAILED)
262                .build();
263
264        WorkContinuation workContinuation = mWorkManagerImpl.beginWith(work1);
265        workContinuation.synchronous().enqueueSync();
266        WorkSpecDao workSpecDao = mDatabase.workSpecDao();
267        assertThat(workSpecDao.getState(work1.getStringId()), is(FAILED));
268
269        OneTimeWorkRequest work2 = new OneTimeWorkRequest.Builder(TestWorker.class).build();
270        workContinuation.then(work2).synchronous().enqueueSync();
271        assertThat(workSpecDao.getState(work2.getStringId()), is(FAILED));
272    }
273
274    @Test
275    @SmallTest
276    public void testEnqueue_insertWithCancelledDependencies_isStatusCancelled() {
277        OneTimeWorkRequest work1 = new OneTimeWorkRequest.Builder(TestWorker.class)
278                .setInitialState(CANCELLED)
279                .build();
280
281        WorkContinuation workContinuation = mWorkManagerImpl.beginWith(work1);
282        workContinuation.synchronous().enqueueSync();
283        WorkSpecDao workSpecDao = mDatabase.workSpecDao();
284        assertThat(workSpecDao.getState(work1.getStringId()), is(CANCELLED));
285
286        OneTimeWorkRequest work2 = new OneTimeWorkRequest.Builder(TestWorker.class).build();
287        workContinuation.then(work2).synchronous().enqueueSync();
288        assertThat(workSpecDao.getState(work2.getStringId()), is(CANCELLED));
289    }
290
291    @Test
292    @SmallTest
293    @SdkSuppress(minSdkVersion = 23)
294    public void testEnqueue_insertWorkConstraints() {
295        Uri testUri1 = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
296        Uri testUri2 = MediaStore.Images.Media.INTERNAL_CONTENT_URI;
297
298        OneTimeWorkRequest work0 = new OneTimeWorkRequest.Builder(TestWorker.class)
299                .setConstraints(
300                        new Constraints.Builder()
301                                .setRequiresCharging(true)
302                                .setRequiresDeviceIdle(true)
303                                .setRequiredNetworkType(METERED)
304                                .setRequiresBatteryNotLow(true)
305                                .setRequiresStorageNotLow(true)
306                                .addContentUriTrigger(testUri1, true)
307                                .addContentUriTrigger(testUri2, false)
308                                .build())
309                .build();
310        OneTimeWorkRequest work1 = new OneTimeWorkRequest.Builder(TestWorker.class).build();
311        mWorkManagerImpl.beginWith(work0).then(work1).synchronous().enqueueSync();
312
313        WorkSpec workSpec0 = mDatabase.workSpecDao().getWorkSpec(work0.getStringId());
314        WorkSpec workSpec1 = mDatabase.workSpecDao().getWorkSpec(work1.getStringId());
315
316        ContentUriTriggers expectedTriggers = new ContentUriTriggers();
317        expectedTriggers.add(testUri1, true);
318        expectedTriggers.add(testUri2, false);
319
320        Constraints constraints = workSpec0.constraints;
321        assertThat(constraints, is(notNullValue()));
322        assertThat(constraints.requiresCharging(), is(true));
323        assertThat(constraints.requiresDeviceIdle(), is(true));
324        assertThat(constraints.requiresBatteryNotLow(), is(true));
325        assertThat(constraints.requiresStorageNotLow(), is(true));
326        assertThat(constraints.getRequiredNetworkType(), is(METERED));
327        if (Build.VERSION.SDK_INT >= 24) {
328            assertThat(constraints.getContentUriTriggers(), is(expectedTriggers));
329        } else {
330            assertThat(constraints.getContentUriTriggers(), is(new ContentUriTriggers()));
331        }
332
333        constraints = workSpec1.constraints;
334        assertThat(constraints, is(notNullValue()));
335        assertThat(constraints.requiresCharging(), is(false));
336        assertThat(constraints.requiresDeviceIdle(), is(false));
337        assertThat(constraints.requiresBatteryNotLow(), is(false));
338        assertThat(constraints.requiresStorageNotLow(), is(false));
339        assertThat(constraints.getRequiredNetworkType(), is(NOT_REQUIRED));
340        assertThat(constraints.getContentUriTriggers().size(), is(0));
341    }
342
343    @Test
344    @SmallTest
345    public void testEnqueue_insertWorkInitialDelay() {
346        final long expectedInitialDelay = 5000L;
347        OneTimeWorkRequest work0 = new OneTimeWorkRequest.Builder(TestWorker.class)
348                .setInitialDelay(expectedInitialDelay, TimeUnit.MILLISECONDS)
349                .build();
350        OneTimeWorkRequest work1 = new OneTimeWorkRequest.Builder(TestWorker.class).build();
351        mWorkManagerImpl.beginWith(work0).then(work1).synchronous().enqueueSync();
352
353        WorkSpec workSpec0 = mDatabase.workSpecDao().getWorkSpec(work0.getStringId());
354        WorkSpec workSpec1 = mDatabase.workSpecDao().getWorkSpec(work1.getStringId());
355
356        assertThat(workSpec0.initialDelay, is(expectedInitialDelay));
357        assertThat(workSpec1.initialDelay, is(0L));
358    }
359
360    @Test
361    @SmallTest
362    public void testEnqueue_insertWorkBackoffPolicy() {
363        OneTimeWorkRequest work0 = new OneTimeWorkRequest.Builder(TestWorker.class)
364                .setBackoffCriteria(BackoffPolicy.LINEAR, 50000, TimeUnit.MILLISECONDS)
365                .build();
366        OneTimeWorkRequest work1 = new OneTimeWorkRequest.Builder(TestWorker.class).build();
367        mWorkManagerImpl.beginWith(work0).then(work1).synchronous().enqueueSync();
368
369        WorkSpec workSpec0 = mDatabase.workSpecDao().getWorkSpec(work0.getStringId());
370        WorkSpec workSpec1 = mDatabase.workSpecDao().getWorkSpec(work1.getStringId());
371
372        assertThat(workSpec0.backoffPolicy, is(BackoffPolicy.LINEAR));
373        assertThat(workSpec0.backoffDelayDuration, is(50000L));
374
375        assertThat(workSpec1.backoffPolicy, is(BackoffPolicy.EXPONENTIAL));
376        assertThat(workSpec1.backoffDelayDuration,
377                is(WorkRequest.DEFAULT_BACKOFF_DELAY_MILLIS));
378    }
379
380    @Test
381    @SmallTest
382    public void testEnqueue_insertWorkTags() {
383        final String firstTag = "first_tag";
384        final String secondTag = "second_tag";
385        final String thirdTag = "third_tag";
386
387        OneTimeWorkRequest work0 = new OneTimeWorkRequest.Builder(TestWorker.class)
388                .addTag(firstTag)
389                .addTag(secondTag)
390                .build();
391        OneTimeWorkRequest work1 = new OneTimeWorkRequest.Builder(TestWorker.class)
392                .addTag(firstTag)
393                .build();
394        OneTimeWorkRequest work2 = new OneTimeWorkRequest.Builder(TestWorker.class).build();
395        mWorkManagerImpl.beginWith(work0).then(work1).then(work2).synchronous().enqueueSync();
396
397        WorkTagDao workTagDao = mDatabase.workTagDao();
398        assertThat(workTagDao.getWorkSpecIdsWithTag(firstTag),
399                containsInAnyOrder(work0.getStringId(), work1.getStringId()));
400        assertThat(workTagDao.getWorkSpecIdsWithTag(secondTag),
401                containsInAnyOrder(work0.getStringId()));
402        assertThat(workTagDao.getWorkSpecIdsWithTag(thirdTag), emptyCollectionOf(String.class));
403    }
404
405    @Test
406    @SmallTest
407    public void testEnqueue_insertPeriodicWork() {
408        PeriodicWorkRequest periodicWork = new PeriodicWorkRequest.Builder(
409                TestWorker.class,
410                PeriodicWorkRequest.MIN_PERIODIC_INTERVAL_MILLIS,
411                TimeUnit.MILLISECONDS)
412                .build();
413
414        mWorkManagerImpl.synchronous().enqueueSync(periodicWork);
415
416        WorkSpec workSpec = mDatabase.workSpecDao().getWorkSpec(periodicWork.getStringId());
417        assertThat(workSpec.isPeriodic(), is(true));
418        assertThat(workSpec.intervalDuration, is(PeriodicWorkRequest.MIN_PERIODIC_INTERVAL_MILLIS));
419        assertThat(workSpec.flexDuration, is(PeriodicWorkRequest.MIN_PERIODIC_INTERVAL_MILLIS));
420    }
421
422    @Test
423    @SmallTest
424    public void testEnqueued_work_setsPeriodStartTime() {
425        OneTimeWorkRequest work = new OneTimeWorkRequest.Builder(TestWorker.class).build();
426        assertThat(work.getWorkSpec().periodStartTime, is(0L));
427
428        long beforeEnqueueTime = System.currentTimeMillis();
429
430        mWorkManagerImpl.beginWith(work).synchronous().enqueueSync();
431        WorkSpec workSpec = mDatabase.workSpecDao().getWorkSpec(work.getStringId());
432        assertThat(workSpec.periodStartTime, is(greaterThanOrEqualTo(beforeEnqueueTime)));
433    }
434
435    @Test
436    @SmallTest
437    public void testEnqueued_periodicWork_setsPeriodStartTime() {
438        PeriodicWorkRequest periodicWork = new PeriodicWorkRequest.Builder(
439                TestWorker.class,
440                PeriodicWorkRequest.MIN_PERIODIC_INTERVAL_MILLIS,
441                TimeUnit.MILLISECONDS)
442                .build();
443        assertThat(periodicWork.getWorkSpec().periodStartTime, is(0L));
444
445        long beforeEnqueueTime = System.currentTimeMillis();
446
447        mWorkManagerImpl.synchronous().enqueueSync(periodicWork);
448
449        WorkSpec workSpec = mDatabase.workSpecDao().getWorkSpec(periodicWork.getStringId());
450        assertThat(workSpec.periodStartTime, is(greaterThanOrEqualTo(beforeEnqueueTime)));
451    }
452
453    @Test
454    @SmallTest
455    public void testBeginUniqueWork_setsUniqueName() {
456        final String uniqueName = "myname";
457
458        OneTimeWorkRequest work = new OneTimeWorkRequest.Builder(TestWorker.class).build();
459        mWorkManagerImpl.beginUniqueWork(uniqueName, REPLACE)
460                .then(work)
461                .synchronous()
462                .enqueueSync();
463
464        List<String> workSpecIds = mDatabase.workNameDao().getWorkSpecIdsWithName(uniqueName);
465        assertThat(work.getStringId(), isIn(workSpecIds));
466    }
467
468    @Test
469    @SmallTest
470    public void testEnqueueUniquePeriodicWork_setsUniqueName() {
471        final String uniqueName = "myname";
472
473        PeriodicWorkRequest periodicWork = new PeriodicWorkRequest.Builder(
474                TestWorker.class,
475                15L,
476                TimeUnit.MINUTES)
477                .build();
478        mWorkManagerImpl.enqueueUniquePeriodicWorkSync(
479                uniqueName,
480                ExistingPeriodicWorkPolicy.REPLACE,
481                periodicWork);
482
483        List<String> workSpecIds = mDatabase.workNameDao().getWorkSpecIdsWithName(uniqueName);
484        assertThat(periodicWork.getStringId(), isIn(workSpecIds));
485    }
486
487    @Test
488    @SmallTest
489    public void testBeginUniqueWork_deletesOldWorkOnReplace() {
490        final String uniqueName = "myname";
491
492        OneTimeWorkRequest originalWork =
493                new OneTimeWorkRequest.Builder(InfiniteTestWorker.class).build();
494        insertNamedWorks(uniqueName, originalWork);
495
496        List<String> workSpecIds = mDatabase.workNameDao().getWorkSpecIdsWithName(uniqueName);
497        assertThat(workSpecIds, containsInAnyOrder(originalWork.getStringId()));
498
499        OneTimeWorkRequest replacementWork1 =
500                new OneTimeWorkRequest.Builder(TestWorker.class).build();
501        OneTimeWorkRequest replacementWork2 =
502                new OneTimeWorkRequest.Builder(TestWorker.class).build();
503        mWorkManagerImpl
504                .beginUniqueWork(uniqueName, REPLACE, replacementWork1)
505                .then(replacementWork2)
506                .synchronous()
507                .enqueueSync();
508
509        workSpecIds = mDatabase.workNameDao().getWorkSpecIdsWithName(uniqueName);
510        assertThat(
511                workSpecIds,
512                containsInAnyOrder(replacementWork1.getStringId(), replacementWork2.getStringId()));
513
514        WorkSpecDao workSpecDao = mDatabase.workSpecDao();
515        assertThat(workSpecDao.getWorkSpec(originalWork.getStringId()), is(nullValue()));
516        assertThat(workSpecDao.getWorkSpec(replacementWork1.getStringId()), is(not(nullValue())));
517        assertThat(workSpecDao.getWorkSpec(replacementWork2.getStringId()), is(not(nullValue())));
518    }
519
520    @Test
521    @SmallTest
522    public void testEnqueueUniquePeriodicWork_deletesOldWorkOnReplace() {
523        final String uniqueName = "myname";
524
525        PeriodicWorkRequest originalWork = new PeriodicWorkRequest.Builder(
526                InfiniteTestWorker.class,
527                15L,
528                TimeUnit.MINUTES)
529                .build();
530        insertNamedWorks(uniqueName, originalWork);
531
532        List<String> workSpecIds = mDatabase.workNameDao().getWorkSpecIdsWithName(uniqueName);
533        assertThat(workSpecIds, containsInAnyOrder(originalWork.getStringId()));
534
535        PeriodicWorkRequest replacementWork = new PeriodicWorkRequest.Builder(
536                TestWorker.class,
537                30L,
538                TimeUnit.MINUTES)
539                .build();
540        mWorkManagerImpl.enqueueUniquePeriodicWorkSync(
541                uniqueName,
542                ExistingPeriodicWorkPolicy.REPLACE,
543                replacementWork);
544
545        workSpecIds = mDatabase.workNameDao().getWorkSpecIdsWithName(uniqueName);
546        assertThat(workSpecIds, contains(replacementWork.getStringId()));
547
548        WorkSpecDao workSpecDao = mDatabase.workSpecDao();
549        assertThat(workSpecDao.getWorkSpec(originalWork.getStringId()), is(nullValue()));
550        assertThat(workSpecDao.getWorkSpec(replacementWork.getStringId()), is(not(nullValue())));
551    }
552
553    @Test
554    @SmallTest
555    public void testBeginUniqueWork_keepsExistingWorkOnKeep() {
556        final String uniqueName = "myname";
557
558        OneTimeWorkRequest originalWork =
559                new OneTimeWorkRequest.Builder(InfiniteTestWorker.class).build();
560        insertNamedWorks(uniqueName, originalWork);
561
562        List<String> workSpecIds = mDatabase.workNameDao().getWorkSpecIdsWithName(uniqueName);
563        assertThat(workSpecIds, containsInAnyOrder(originalWork.getStringId()));
564
565        OneTimeWorkRequest replacementWork1 =
566                new OneTimeWorkRequest.Builder(TestWorker.class).build();
567        OneTimeWorkRequest replacementWork2 =
568                new OneTimeWorkRequest.Builder(TestWorker.class).build();
569        mWorkManagerImpl
570                .beginUniqueWork(uniqueName, KEEP, replacementWork1)
571                .then(replacementWork2)
572                .synchronous()
573                .enqueueSync();
574
575        workSpecIds = mDatabase.workNameDao().getWorkSpecIdsWithName(uniqueName);
576        assertThat(workSpecIds, contains(originalWork.getStringId()));
577
578        WorkSpecDao workSpecDao = mDatabase.workSpecDao();
579        assertThat(workSpecDao.getWorkSpec(originalWork.getStringId()), is(not(nullValue())));
580        assertThat(workSpecDao.getWorkSpec(replacementWork1.getStringId()), is(nullValue()));
581        assertThat(workSpecDao.getWorkSpec(replacementWork2.getStringId()), is(nullValue()));
582    }
583
584    @Test
585    @SmallTest
586    public void testEnqueueUniquePeriodicWork_keepsExistingWorkOnKeep() {
587        final String uniqueName = "myname";
588
589        PeriodicWorkRequest originalWork = new PeriodicWorkRequest.Builder(
590                InfiniteTestWorker.class,
591                15L,
592                TimeUnit.MINUTES)
593                .build();
594        insertNamedWorks(uniqueName, originalWork);
595
596        List<String> workSpecIds = mDatabase.workNameDao().getWorkSpecIdsWithName(uniqueName);
597        assertThat(workSpecIds, containsInAnyOrder(originalWork.getStringId()));
598
599        PeriodicWorkRequest replacementWork = new PeriodicWorkRequest.Builder(
600                TestWorker.class,
601                30L,
602                TimeUnit.MINUTES)
603                .build();
604        mWorkManagerImpl.enqueueUniquePeriodicWorkSync(
605                uniqueName,
606                ExistingPeriodicWorkPolicy.KEEP,
607                replacementWork);
608
609        workSpecIds = mDatabase.workNameDao().getWorkSpecIdsWithName(uniqueName);
610        assertThat(workSpecIds, contains(originalWork.getStringId()));
611
612        WorkSpecDao workSpecDao = mDatabase.workSpecDao();
613        assertThat(workSpecDao.getWorkSpec(originalWork.getStringId()), is(not(nullValue())));
614        assertThat(workSpecDao.getWorkSpec(replacementWork.getStringId()), is(nullValue()));
615    }
616
617    @Test
618    @SmallTest
619    public void testBeginUniqueWork_replacesExistingWorkOnKeepWhenExistingWorkIsDone() {
620        final String uniqueName = "myname";
621
622        OneTimeWorkRequest originalWork = new OneTimeWorkRequest.Builder(TestWorker.class)
623                .setInitialState(SUCCEEDED)
624                .build();
625        insertNamedWorks(uniqueName, originalWork);
626
627        List<String> workSpecIds = mDatabase.workNameDao().getWorkSpecIdsWithName(uniqueName);
628        assertThat(workSpecIds, containsInAnyOrder(originalWork.getStringId()));
629
630        OneTimeWorkRequest replacementWork1 =
631                new OneTimeWorkRequest.Builder(TestWorker.class).build();
632        OneTimeWorkRequest replacementWork2 =
633                new OneTimeWorkRequest.Builder(TestWorker.class).build();
634        mWorkManagerImpl
635                .beginUniqueWork(uniqueName, KEEP, replacementWork1)
636                .then(replacementWork2)
637                .synchronous()
638                .enqueueSync();
639
640        workSpecIds = mDatabase.workNameDao().getWorkSpecIdsWithName(uniqueName);
641        assertThat(workSpecIds,
642                containsInAnyOrder(replacementWork1.getStringId(), replacementWork2.getStringId()));
643
644        WorkSpecDao workSpecDao = mDatabase.workSpecDao();
645        assertThat(workSpecDao.getWorkSpec(originalWork.getStringId()), is(nullValue()));
646        assertThat(workSpecDao.getWorkSpec(replacementWork1.getStringId()), is(not(nullValue())));
647        assertThat(workSpecDao.getWorkSpec(replacementWork2.getStringId()), is(not(nullValue())));
648    }
649
650    @Test
651    @SmallTest
652    public void testEnqueueUniquePeriodicWork_replacesExistingWorkOnKeepWhenExistingWorkIsDone() {
653        final String uniqueName = "myname";
654
655        PeriodicWorkRequest originalWork = new PeriodicWorkRequest.Builder(
656                InfiniteTestWorker.class,
657                15L,
658                TimeUnit.MINUTES)
659                .setInitialState(SUCCEEDED)
660                .build();
661        insertNamedWorks(uniqueName, originalWork);
662
663        List<String> workSpecIds = mDatabase.workNameDao().getWorkSpecIdsWithName(uniqueName);
664        assertThat(workSpecIds, containsInAnyOrder(originalWork.getStringId()));
665
666        PeriodicWorkRequest replacementWork = new PeriodicWorkRequest.Builder(
667                TestWorker.class,
668                30L,
669                TimeUnit.MINUTES)
670                .build();
671        mWorkManagerImpl.enqueueUniquePeriodicWorkSync(
672                uniqueName,
673                ExistingPeriodicWorkPolicy.KEEP,
674                replacementWork);
675
676        workSpecIds = mDatabase.workNameDao().getWorkSpecIdsWithName(uniqueName);
677        assertThat(workSpecIds, contains(replacementWork.getStringId()));
678
679        WorkSpecDao workSpecDao = mDatabase.workSpecDao();
680        assertThat(workSpecDao.getWorkSpec(originalWork.getStringId()), is(nullValue()));
681        assertThat(workSpecDao.getWorkSpec(replacementWork.getStringId()), is(not(nullValue())));
682    }
683
684    @Test
685    @SmallTest
686    public void testBeginUniqueWork_appendsExistingWorkOnAppend() {
687        final String uniqueName = "myname";
688
689        OneTimeWorkRequest originalWork =
690                new OneTimeWorkRequest.Builder(InfiniteTestWorker.class).build();
691        insertNamedWorks(uniqueName, originalWork);
692
693        List<String> workSpecIds = mDatabase.workNameDao().getWorkSpecIdsWithName(uniqueName);
694        assertThat(workSpecIds, containsInAnyOrder(originalWork.getStringId()));
695
696        OneTimeWorkRequest appendWork1 = new OneTimeWorkRequest.Builder(TestWorker.class).build();
697        OneTimeWorkRequest appendWork2 = new OneTimeWorkRequest.Builder(TestWorker.class).build();
698        mWorkManagerImpl
699                .beginUniqueWork(uniqueName, APPEND, appendWork1)
700                .then(appendWork2)
701                .synchronous()
702                .enqueueSync();
703
704        workSpecIds = mDatabase.workNameDao().getWorkSpecIdsWithName(uniqueName);
705        assertThat(workSpecIds,
706                containsInAnyOrder(
707                        originalWork.getStringId(),
708                        appendWork1.getStringId(),
709                        appendWork2.getStringId()));
710
711        WorkSpecDao workSpecDao = mDatabase.workSpecDao();
712        assertThat(workSpecDao.getWorkSpec(originalWork.getStringId()), is(not(nullValue())));
713        assertThat(workSpecDao.getState(appendWork1.getStringId()), is(BLOCKED));
714        assertThat(workSpecDao.getState(appendWork2.getStringId()), is(BLOCKED));
715
716        assertThat(mDatabase.dependencyDao().getDependentWorkIds(originalWork.getStringId()),
717                containsInAnyOrder(appendWork1.getStringId()));
718    }
719
720    @Test
721    @SmallTest
722    public void testBeginUniqueWork_appendsExistingWorkToOnlyLeavesOnAppend() {
723        final String uniqueName = "myname";
724
725        OneTimeWorkRequest originalWork1 =
726                new OneTimeWorkRequest.Builder(InfiniteTestWorker.class).build();
727        OneTimeWorkRequest originalWork2 =
728                new OneTimeWorkRequest.Builder(InfiniteTestWorker.class).build();
729        OneTimeWorkRequest originalWork3 =
730                new OneTimeWorkRequest.Builder(InfiniteTestWorker.class).build();
731        OneTimeWorkRequest originalWork4 =
732                new OneTimeWorkRequest.Builder(InfiniteTestWorker.class).build();
733
734        insertNamedWorks(uniqueName, originalWork1, originalWork2, originalWork3, originalWork4);
735        insertDependency(originalWork4, originalWork2);
736        insertDependency(originalWork3, originalWork2);
737        insertDependency(originalWork2, originalWork1);
738
739        List<String> workSpecIds = mDatabase.workNameDao().getWorkSpecIdsWithName(uniqueName);
740        assertThat(workSpecIds,
741                containsInAnyOrder(
742                        originalWork1.getStringId(),
743                        originalWork2.getStringId(),
744                        originalWork3.getStringId(),
745                        originalWork4.getStringId()));
746
747        OneTimeWorkRequest appendWork1 = new OneTimeWorkRequest.Builder(TestWorker.class).build();
748        OneTimeWorkRequest appendWork2 = new OneTimeWorkRequest.Builder(TestWorker.class).build();
749        mWorkManagerImpl
750                .beginUniqueWork(uniqueName, APPEND, appendWork1)
751                .then(appendWork2)
752                .synchronous()
753                .enqueueSync();
754
755        workSpecIds = mDatabase.workNameDao().getWorkSpecIdsWithName(uniqueName);
756        assertThat(workSpecIds,
757                containsInAnyOrder(
758                        originalWork1.getStringId(),
759                        originalWork2.getStringId(),
760                        originalWork3.getStringId(),
761                        originalWork4.getStringId(),
762                        appendWork1.getStringId(),
763                        appendWork2.getStringId()));
764
765        WorkSpecDao workSpecDao = mDatabase.workSpecDao();
766        assertThat(workSpecDao.getWorkSpec(originalWork1.getStringId()), is(not(nullValue())));
767        assertThat(workSpecDao.getWorkSpec(originalWork2.getStringId()), is(not(nullValue())));
768        assertThat(workSpecDao.getWorkSpec(originalWork3.getStringId()), is(not(nullValue())));
769        assertThat(workSpecDao.getWorkSpec(originalWork4.getStringId()), is(not(nullValue())));
770        assertThat(workSpecDao.getState(appendWork1.getStringId()), is(BLOCKED));
771        assertThat(workSpecDao.getState(appendWork2.getStringId()), is(BLOCKED));
772
773        DependencyDao dependencyDao = mDatabase.dependencyDao();
774        assertThat(dependencyDao.getPrerequisites(appendWork1.getStringId()),
775                containsInAnyOrder(originalWork3.getStringId(), originalWork4.getStringId()));
776        assertThat(dependencyDao.getPrerequisites(appendWork2.getStringId()),
777                containsInAnyOrder(appendWork1.getStringId()));
778    }
779
780    @Test
781    @SmallTest
782    public void testBeginUniqueWork_insertsExistingWorkWhenNothingToAppendTo() {
783        final String uniqueName = "myname";
784
785        OneTimeWorkRequest appendWork1 = new OneTimeWorkRequest.Builder(TestWorker.class).build();
786        OneTimeWorkRequest appendWork2 = new OneTimeWorkRequest.Builder(TestWorker.class).build();
787        mWorkManagerImpl
788                .beginUniqueWork(uniqueName, APPEND, appendWork1)
789                .then(appendWork2)
790                .synchronous()
791                .enqueueSync();
792
793        List<String> workSpecIds = mDatabase.workNameDao().getWorkSpecIdsWithName(uniqueName);
794        assertThat(workSpecIds,
795                containsInAnyOrder(appendWork1.getStringId(), appendWork2.getStringId()));
796    }
797
798    @Test
799    @SmallTest
800    public void testGetStatusByIdSync() {
801        OneTimeWorkRequest work = new OneTimeWorkRequest.Builder(TestWorker.class)
802                .setInitialState(SUCCEEDED)
803                .build();
804        insertWorkSpecAndTags(work);
805
806        WorkStatus workStatus = mWorkManagerImpl.getStatusByIdSync(work.getId());
807        assertThat(workStatus.getId().toString(), is(work.getStringId()));
808        assertThat(workStatus.getState(), is(SUCCEEDED));
809    }
810
811    @Test
812    @SmallTest
813    public void testGetStatusByIdSync_returnsNullIfNotInDatabase() {
814        WorkStatus workStatus = mWorkManagerImpl.getStatusByIdSync(UUID.randomUUID());
815        assertThat(workStatus, is(nullValue()));
816    }
817
818    @Test
819    @SmallTest
820    @SuppressWarnings("unchecked")
821    public void testGetStatusesById() {
822        OneTimeWorkRequest work0 = new OneTimeWorkRequest.Builder(TestWorker.class).build();
823        OneTimeWorkRequest work1 = new OneTimeWorkRequest.Builder(TestWorker.class).build();
824        insertWorkSpecAndTags(work0);
825        insertWorkSpecAndTags(work1);
826
827        Observer<List<WorkStatus>> mockObserver = mock(Observer.class);
828
829        TestLifecycleOwner testLifecycleOwner = new TestLifecycleOwner();
830        LiveData<List<WorkStatus>> liveData = mWorkManagerImpl.getStatusesById(
831                Arrays.asList(work0.getStringId(), work1.getStringId()));
832        liveData.observe(testLifecycleOwner, mockObserver);
833
834        ArgumentCaptor<List<WorkStatus>> captor = ArgumentCaptor.forClass(List.class);
835        verify(mockObserver).onChanged(captor.capture());
836        assertThat(captor.getValue(), is(not(nullValue())));
837        assertThat(captor.getValue().size(), is(2));
838
839        WorkStatus workStatus0 = new WorkStatus(
840                work0.getId(),
841                ENQUEUED,
842                Data.EMPTY,
843                Collections.<String>emptyList());
844        WorkStatus workStatus1 = new WorkStatus(
845                work1.getId(),
846                ENQUEUED,
847                Data.EMPTY,
848                Collections.<String>emptyList());
849        assertThat(captor.getValue(), containsInAnyOrder(workStatus0, workStatus1));
850
851        WorkSpecDao workSpecDao = mDatabase.workSpecDao();
852        workSpecDao.setState(RUNNING, work0.getStringId());
853
854        verify(mockObserver, times(2)).onChanged(captor.capture());
855        assertThat(captor.getValue(), is(not(nullValue())));
856        assertThat(captor.getValue().size(), is(2));
857
858        workStatus0 = new WorkStatus(
859                work0.getId(),
860                RUNNING,
861                Data.EMPTY,
862                Collections.<String>emptyList());
863        assertThat(captor.getValue(), containsInAnyOrder(workStatus0, workStatus1));
864
865        clearInvocations(mockObserver);
866        workSpecDao.setState(RUNNING, work1.getStringId());
867
868        verify(mockObserver).onChanged(captor.capture());
869        assertThat(captor.getValue(), is(not(nullValue())));
870        assertThat(captor.getValue().size(), is(2));
871
872        workStatus1 = new WorkStatus(
873                work1.getId(),
874                RUNNING,
875                Data.EMPTY,
876                Collections.<String>emptyList());
877        assertThat(captor.getValue(), containsInAnyOrder(workStatus0, workStatus1));
878
879        liveData.removeObservers(testLifecycleOwner);
880    }
881
882    @Test
883    @SmallTest
884    public void testGetStatusesByTagSync() {
885        final String firstTag = "first_tag";
886        final String secondTag = "second_tag";
887
888        OneTimeWorkRequest work0 = new OneTimeWorkRequest.Builder(TestWorker.class)
889                .addTag(firstTag)
890                .addTag(secondTag)
891                .setInitialState(RUNNING)
892                .build();
893        OneTimeWorkRequest work1 = new OneTimeWorkRequest.Builder(TestWorker.class)
894                .addTag(firstTag)
895                .setInitialState(BLOCKED)
896                .build();
897        OneTimeWorkRequest work2 = new OneTimeWorkRequest.Builder(TestWorker.class)
898                .addTag(secondTag)
899                .setInitialState(SUCCEEDED)
900                .build();
901        insertWorkSpecAndTags(work0);
902        insertWorkSpecAndTags(work1);
903        insertWorkSpecAndTags(work2);
904
905        WorkStatus workStatus0 = new WorkStatus(
906                work0.getId(),
907                RUNNING,
908                Data.EMPTY,
909                Arrays.asList(firstTag, secondTag));
910        WorkStatus workStatus1 = new WorkStatus(
911                work1.getId(),
912                BLOCKED,
913                Data.EMPTY,
914                Collections.singletonList(firstTag));
915        WorkStatus workStatus2 = new WorkStatus(
916                work2.getId(),
917                SUCCEEDED,
918                Data.EMPTY,
919                Collections.singletonList(secondTag));
920
921        List<WorkStatus> workStatuses = mWorkManagerImpl.getStatusesByTagSync(firstTag);
922        assertThat(workStatuses, containsInAnyOrder(workStatus0, workStatus1));
923
924        workStatuses = mWorkManagerImpl.getStatusesByTagSync(secondTag);
925        assertThat(workStatuses, containsInAnyOrder(workStatus0, workStatus2));
926
927        workStatuses = mWorkManagerImpl.getStatusesByTagSync("dummy");
928        assertThat(workStatuses.size(), is(0));
929    }
930
931    @Test
932    @SmallTest
933    @SuppressWarnings("unchecked")
934    public void testGetStatusesByTag() {
935        final String firstTag = "first_tag";
936        final String secondTag = "second_tag";
937        WorkSpecDao workSpecDao = mDatabase.workSpecDao();
938
939        OneTimeWorkRequest work0 = new OneTimeWorkRequest.Builder(TestWorker.class)
940                .addTag(firstTag)
941                .addTag(secondTag)
942                .setInitialState(RUNNING)
943                .build();
944        OneTimeWorkRequest work1 = new OneTimeWorkRequest.Builder(TestWorker.class)
945                .addTag(firstTag)
946                .setInitialState(BLOCKED)
947                .build();
948        OneTimeWorkRequest work2 = new OneTimeWorkRequest.Builder(TestWorker.class)
949                .addTag(secondTag)
950                .setInitialState(SUCCEEDED)
951                .build();
952        insertWorkSpecAndTags(work0);
953        insertWorkSpecAndTags(work1);
954        insertWorkSpecAndTags(work2);
955
956        Observer<List<WorkStatus>> mockObserver = mock(Observer.class);
957
958        TestLifecycleOwner testLifecycleOwner = new TestLifecycleOwner();
959        LiveData<List<WorkStatus>> liveData = mWorkManagerImpl.getStatusesByTag(firstTag);
960        liveData.observe(testLifecycleOwner, mockObserver);
961
962        ArgumentCaptor<List<WorkStatus>> captor = ArgumentCaptor.forClass(List.class);
963        verify(mockObserver).onChanged(captor.capture());
964        assertThat(captor.getValue(), is(not(nullValue())));
965        assertThat(captor.getValue().size(), is(2));
966
967        WorkStatus workStatus0 = new WorkStatus(
968                work0.getId(),
969                RUNNING,
970                Data.EMPTY,
971                Arrays.asList(firstTag, secondTag));
972        WorkStatus workStatus1 = new WorkStatus(
973                work1.getId(),
974                BLOCKED,
975                Data.EMPTY,
976                Collections.singletonList(firstTag));
977        assertThat(captor.getValue(), containsInAnyOrder(workStatus0, workStatus1));
978
979        workSpecDao.setState(ENQUEUED, work0.getStringId());
980
981        verify(mockObserver, times(2)).onChanged(captor.capture());
982        assertThat(captor.getValue(), is(not(nullValue())));
983        assertThat(captor.getValue().size(), is(2));
984
985        workStatus0 = new WorkStatus(
986                work0.getId(),
987                ENQUEUED,
988                Data.EMPTY,
989                Arrays.asList(firstTag, secondTag));
990        assertThat(captor.getValue(), containsInAnyOrder(workStatus0, workStatus1));
991
992        liveData.removeObservers(testLifecycleOwner);
993    }
994
995    @Test
996    @SmallTest
997    public void getStatusByNameSync() {
998        final String uniqueName = "myname";
999
1000        OneTimeWorkRequest work0 = new OneTimeWorkRequest.Builder(InfiniteTestWorker.class)
1001                .setInitialState(RUNNING)
1002                .build();
1003        OneTimeWorkRequest work1 = new OneTimeWorkRequest.Builder(InfiniteTestWorker.class)
1004                .setInitialState(BLOCKED)
1005                .build();
1006        OneTimeWorkRequest work2 = new OneTimeWorkRequest.Builder(InfiniteTestWorker.class)
1007                .setInitialState(BLOCKED)
1008                .build();
1009        insertNamedWorks(uniqueName, work0, work1, work2);
1010        insertDependency(work1, work0);
1011        insertDependency(work2, work1);
1012
1013        WorkStatus workStatus0 = new WorkStatus(
1014                work0.getId(),
1015                RUNNING,
1016                Data.EMPTY,
1017                Collections.<String>emptyList());
1018        WorkStatus workStatus1 = new WorkStatus(
1019                work1.getId(),
1020                BLOCKED,
1021                Data.EMPTY,
1022                Collections.<String>emptyList());
1023        WorkStatus workStatus2 = new WorkStatus(
1024                work2.getId(),
1025                BLOCKED,
1026                Data.EMPTY,
1027                Collections.<String>emptyList());
1028
1029        List<WorkStatus> workStatuses = mWorkManagerImpl.getStatusesForUniqueWorkSync(uniqueName);
1030        assertThat(workStatuses, containsInAnyOrder(workStatus0, workStatus1, workStatus2));
1031
1032        workStatuses = mWorkManagerImpl.getStatusesForUniqueWorkSync("dummy");
1033        assertThat(workStatuses.size(), is(0));
1034    }
1035
1036    @Test
1037    @SmallTest
1038    @SuppressWarnings("unchecked")
1039    public void testGetStatusesByName() {
1040        final String uniqueName = "myname";
1041        WorkSpecDao workSpecDao = mDatabase.workSpecDao();
1042
1043        OneTimeWorkRequest work0 = new OneTimeWorkRequest.Builder(InfiniteTestWorker.class)
1044                .setInitialState(RUNNING)
1045                .build();
1046        OneTimeWorkRequest work1 = new OneTimeWorkRequest.Builder(InfiniteTestWorker.class)
1047                .setInitialState(BLOCKED)
1048                .build();
1049        OneTimeWorkRequest work2 = new OneTimeWorkRequest.Builder(InfiniteTestWorker.class)
1050                .setInitialState(BLOCKED)
1051                .build();
1052        insertNamedWorks(uniqueName, work0, work1, work2);
1053        insertDependency(work1, work0);
1054        insertDependency(work2, work1);
1055
1056        Observer<List<WorkStatus>> mockObserver = mock(Observer.class);
1057
1058        TestLifecycleOwner testLifecycleOwner = new TestLifecycleOwner();
1059        LiveData<List<WorkStatus>> liveData = mWorkManagerImpl.getStatusesForUniqueWork(uniqueName);
1060        liveData.observe(testLifecycleOwner, mockObserver);
1061
1062        ArgumentCaptor<List<WorkStatus>> captor = ArgumentCaptor.forClass(List.class);
1063        verify(mockObserver).onChanged(captor.capture());
1064        assertThat(captor.getValue(), is(not(nullValue())));
1065        assertThat(captor.getValue().size(), is(3));
1066
1067        WorkStatus workStatus0 = new WorkStatus(
1068                work0.getId(),
1069                RUNNING,
1070                Data.EMPTY,
1071                Collections.<String>emptyList());
1072        WorkStatus workStatus1 = new WorkStatus(
1073                work1.getId(),
1074                BLOCKED,
1075                Data.EMPTY,
1076                Collections.<String>emptyList());
1077        WorkStatus workStatus2 = new WorkStatus(
1078                work2.getId(),
1079                BLOCKED,
1080                Data.EMPTY,
1081                Collections.<String>emptyList());
1082        assertThat(captor.getValue(), containsInAnyOrder(workStatus0, workStatus1, workStatus2));
1083
1084        workSpecDao.setState(ENQUEUED, work0.getStringId());
1085
1086        verify(mockObserver, times(2)).onChanged(captor.capture());
1087        assertThat(captor.getValue(), is(not(nullValue())));
1088        assertThat(captor.getValue().size(), is(3));
1089
1090        workStatus0 = new WorkStatus(
1091                work0.getId(),
1092                ENQUEUED,
1093                Data.EMPTY,
1094                Collections.<String>emptyList());
1095        assertThat(captor.getValue(), containsInAnyOrder(workStatus0, workStatus1, workStatus2));
1096
1097        liveData.removeObservers(testLifecycleOwner);
1098    }
1099
1100    @Test
1101    @SmallTest
1102    public void testCancelWorkById() {
1103        WorkSpecDao workSpecDao = mDatabase.workSpecDao();
1104
1105        OneTimeWorkRequest work0 = new OneTimeWorkRequest.Builder(TestWorker.class).build();
1106        OneTimeWorkRequest work1 = new OneTimeWorkRequest.Builder(TestWorker.class).build();
1107        insertWorkSpecAndTags(work0);
1108        insertWorkSpecAndTags(work1);
1109
1110        mWorkManagerImpl.synchronous().cancelWorkByIdSync(work0.getId());
1111        assertThat(workSpecDao.getState(work0.getStringId()), is(CANCELLED));
1112        assertThat(workSpecDao.getState(work1.getStringId()), is(not(CANCELLED)));
1113    }
1114
1115    @Test
1116    @SmallTest
1117    public void testCancelWorkById_cancelsDependentWork() {
1118        WorkSpecDao workSpecDao = mDatabase.workSpecDao();
1119
1120        OneTimeWorkRequest work0 = new OneTimeWorkRequest.Builder(TestWorker.class).build();
1121        OneTimeWorkRequest work1 = new OneTimeWorkRequest.Builder(TestWorker.class)
1122                .setInitialState(BLOCKED)
1123                .build();
1124        insertWorkSpecAndTags(work0);
1125        insertWorkSpecAndTags(work1);
1126        insertDependency(work1, work0);
1127
1128        mWorkManagerImpl.synchronous().cancelWorkByIdSync(work0.getId());
1129
1130        assertThat(workSpecDao.getState(work0.getStringId()), is(CANCELLED));
1131        assertThat(workSpecDao.getState(work1.getStringId()), is(CANCELLED));
1132    }
1133
1134    @Test
1135    @SmallTest
1136    public void testCancelWorkById_cancelsUnfinishedWorkOnly() {
1137        WorkSpecDao workSpecDao = mDatabase.workSpecDao();
1138
1139        OneTimeWorkRequest work0 = new OneTimeWorkRequest.Builder(TestWorker.class)
1140                .setInitialState(SUCCEEDED)
1141                .build();
1142        OneTimeWorkRequest work1 = new OneTimeWorkRequest.Builder(TestWorker.class)
1143                .setInitialState(ENQUEUED)
1144                .build();
1145        insertWorkSpecAndTags(work0);
1146        insertWorkSpecAndTags(work1);
1147        insertDependency(work1, work0);
1148
1149        mWorkManagerImpl.synchronous().cancelWorkByIdSync(work0.getId());
1150
1151        assertThat(workSpecDao.getState(work0.getStringId()), is(SUCCEEDED));
1152        assertThat(workSpecDao.getState(work1.getStringId()), is(CANCELLED));
1153    }
1154
1155    @Test
1156    @SmallTest
1157    public void testCancelAllWorkByTag() {
1158        WorkSpecDao workSpecDao = mDatabase.workSpecDao();
1159
1160        final String tagToClear = "tag_to_clear";
1161        final String tagNotToClear = "tag_not_to_clear";
1162
1163        OneTimeWorkRequest work0 = new OneTimeWorkRequest.Builder(TestWorker.class)
1164                .addTag(tagToClear)
1165                .build();
1166        OneTimeWorkRequest work1 = new OneTimeWorkRequest.Builder(TestWorker.class)
1167                .addTag(tagToClear)
1168                .build();
1169        OneTimeWorkRequest work2 = new OneTimeWorkRequest.Builder(TestWorker.class)
1170                .addTag(tagNotToClear)
1171                .build();
1172        OneTimeWorkRequest work3 = new OneTimeWorkRequest.Builder(TestWorker.class)
1173                .addTag(tagNotToClear)
1174                .build();
1175        insertWorkSpecAndTags(work0);
1176        insertWorkSpecAndTags(work1);
1177        insertWorkSpecAndTags(work2);
1178        insertWorkSpecAndTags(work3);
1179
1180        mWorkManagerImpl.synchronous().cancelAllWorkByTagSync(tagToClear);
1181
1182        assertThat(workSpecDao.getState(work0.getStringId()), is(CANCELLED));
1183        assertThat(workSpecDao.getState(work1.getStringId()), is(CANCELLED));
1184        assertThat(workSpecDao.getState(work2.getStringId()), is(not(CANCELLED)));
1185        assertThat(workSpecDao.getState(work3.getStringId()), is(not(CANCELLED)));
1186    }
1187
1188    @Test
1189    @SmallTest
1190    public void testCancelAllWorkByTag_cancelsDependentWork() {
1191        String tag = "tag";
1192
1193        OneTimeWorkRequest work0 = new OneTimeWorkRequest.Builder(TestWorker.class)
1194                .addTag(tag)
1195                .build();
1196
1197        OneTimeWorkRequest work1 = new OneTimeWorkRequest.Builder(TestWorker.class).build();
1198        OneTimeWorkRequest work2 = new OneTimeWorkRequest.Builder(TestWorker.class).build();
1199        OneTimeWorkRequest work3 = new OneTimeWorkRequest.Builder(TestWorker.class).build();
1200        OneTimeWorkRequest work4 = new OneTimeWorkRequest.Builder(TestWorker.class).build();
1201
1202        insertWorkSpecAndTags(work0);
1203        insertWorkSpecAndTags(work1);
1204        insertWorkSpecAndTags(work2);
1205        insertWorkSpecAndTags(work3);
1206        insertWorkSpecAndTags(work4);
1207
1208        // Dependency graph:
1209        //                             0
1210        //                             |
1211        //                       |------------|
1212        //            3          1            4
1213        //            |          |
1214        //            ------------
1215        //                 |
1216        //                 2
1217
1218        insertDependency(work2, work1);
1219        insertDependency(work2, work3);
1220        insertDependency(work1, work0);
1221        insertDependency(work4, work0);
1222
1223        mWorkManagerImpl.synchronous().cancelAllWorkByTagSync(tag);
1224
1225        WorkSpecDao workSpecDao = mDatabase.workSpecDao();
1226        assertThat(workSpecDao.getState(work0.getStringId()), is(CANCELLED));
1227        assertThat(workSpecDao.getState(work1.getStringId()), is(CANCELLED));
1228        assertThat(workSpecDao.getState(work2.getStringId()), is(CANCELLED));
1229        assertThat(workSpecDao.getState(work3.getStringId()), is(not(CANCELLED)));
1230        assertThat(workSpecDao.getState(work4.getStringId()), is(CANCELLED));
1231    }
1232
1233    @Test
1234    @SmallTest
1235    public void testCancelWorkByName() {
1236        final String uniqueName = "myname";
1237
1238        OneTimeWorkRequest work0 = new OneTimeWorkRequest.Builder(InfiniteTestWorker.class).build();
1239        OneTimeWorkRequest work1 = new OneTimeWorkRequest.Builder(InfiniteTestWorker.class).build();
1240        insertNamedWorks(uniqueName, work0, work1);
1241
1242        mWorkManagerImpl.synchronous().cancelUniqueWorkSync(uniqueName);
1243
1244        WorkSpecDao workSpecDao = mDatabase.workSpecDao();
1245        assertThat(workSpecDao.getState(work0.getStringId()), is(CANCELLED));
1246        assertThat(workSpecDao.getState(work1.getStringId()), is(CANCELLED));
1247    }
1248
1249    @Test
1250    @LargeTest
1251    public void testCancelWorkByName_ignoresFinishedWork() {
1252        final String uniqueName = "myname";
1253
1254        OneTimeWorkRequest work0 = new OneTimeWorkRequest.Builder(InfiniteTestWorker.class)
1255                .setInitialState(SUCCEEDED)
1256                .build();
1257        OneTimeWorkRequest work1 = new OneTimeWorkRequest.Builder(InfiniteTestWorker.class).build();
1258        insertNamedWorks(uniqueName, work0, work1);
1259
1260        mWorkManagerImpl.synchronous().cancelUniqueWorkSync(uniqueName);
1261
1262        WorkSpecDao workSpecDao = mDatabase.workSpecDao();
1263        assertThat(workSpecDao.getState(work0.getStringId()), is(SUCCEEDED));
1264        assertThat(workSpecDao.getState(work1.getStringId()), is(CANCELLED));
1265    }
1266
1267    @Test
1268    @SmallTest
1269    public void testSynchronousCancelAndGetStatus() {
1270        OneTimeWorkRequest work = new OneTimeWorkRequest.Builder(TestWorker.class).build();
1271        insertWorkSpecAndTags(work);
1272
1273        WorkSpecDao workSpecDao = mDatabase.workSpecDao();
1274        assertThat(workSpecDao.getState(work.getStringId()), is(ENQUEUED));
1275
1276        mWorkManagerImpl.synchronous().cancelWorkByIdSync(work.getId());
1277        assertThat(mWorkManagerImpl.getStatusByIdSync(work.getId()).getState(), is(CANCELLED));
1278    }
1279
1280    @Test
1281    @SmallTest
1282    public void testGenerateCleanupCallback_resetsRunningWorkStatuses() {
1283        WorkSpecDao workSpecDao = mDatabase.workSpecDao();
1284
1285        OneTimeWorkRequest work = new OneTimeWorkRequest.Builder(TestWorker.class)
1286                .setInitialState(RUNNING)
1287                .build();
1288        workSpecDao.insertWorkSpec(work.getWorkSpec());
1289
1290        assertThat(workSpecDao.getState(work.getStringId()), is(RUNNING));
1291
1292        SupportSQLiteOpenHelper openHelper = mDatabase.getOpenHelper();
1293        SupportSQLiteDatabase db = openHelper.getWritableDatabase();
1294        WorkDatabase.generateCleanupCallback().onOpen(db);
1295
1296        assertThat(workSpecDao.getState(work.getStringId()), is(ENQUEUED));
1297    }
1298
1299    @Test
1300    @SmallTest
1301    public void testGenerateCleanupCallback_deletesOldFinishedWork() {
1302        OneTimeWorkRequest work1 = new OneTimeWorkRequest.Builder(TestWorker.class)
1303                .setInitialState(SUCCEEDED)
1304                .setPeriodStartTime(WorkDatabase.getPruneDate() - 1L, TimeUnit.MILLISECONDS)
1305                .build();
1306        OneTimeWorkRequest work2 = new OneTimeWorkRequest.Builder(TestWorker.class)
1307                .setPeriodStartTime(Long.MAX_VALUE, TimeUnit.MILLISECONDS)
1308                .build();
1309
1310        insertWorkSpecAndTags(work1);
1311        insertWorkSpecAndTags(work2);
1312
1313        SupportSQLiteOpenHelper openHelper = mDatabase.getOpenHelper();
1314        SupportSQLiteDatabase db = openHelper.getWritableDatabase();
1315        WorkDatabase.generateCleanupCallback().onOpen(db);
1316
1317        WorkSpecDao workSpecDao = mDatabase.workSpecDao();
1318        assertThat(workSpecDao.getWorkSpec(work1.getStringId()), is(nullValue()));
1319        assertThat(workSpecDao.getWorkSpec(work2.getStringId()), is(not(nullValue())));
1320    }
1321
1322    @Test
1323    @SmallTest
1324    public void testGenerateCleanupCallback_doesNotDeleteOldFinishedWorkWithActiveDependents() {
1325        OneTimeWorkRequest work0 = new OneTimeWorkRequest.Builder(TestWorker.class)
1326                .setInitialState(SUCCEEDED)
1327                .setPeriodStartTime(WorkDatabase.getPruneDate() - 1L, TimeUnit.MILLISECONDS)
1328                .build();
1329        OneTimeWorkRequest work1 = new OneTimeWorkRequest.Builder(TestWorker.class)
1330                .setInitialState(SUCCEEDED)
1331                .setPeriodStartTime(WorkDatabase.getPruneDate() - 1L, TimeUnit.MILLISECONDS)
1332                .build();
1333        OneTimeWorkRequest work2 = new OneTimeWorkRequest.Builder(TestWorker.class)
1334                .setInitialState(ENQUEUED)
1335                .setPeriodStartTime(WorkDatabase.getPruneDate() - 1L, TimeUnit.MILLISECONDS)
1336                .build();
1337
1338        insertWorkSpecAndTags(work0);
1339        insertWorkSpecAndTags(work1);
1340        insertWorkSpecAndTags(work2);
1341
1342        // Dependency graph: 0 -> 1 -> 2
1343        insertDependency(work1, work0);
1344        insertDependency(work2, work1);
1345
1346        SupportSQLiteOpenHelper openHelper = mDatabase.getOpenHelper();
1347        SupportSQLiteDatabase db = openHelper.getWritableDatabase();
1348        WorkDatabase.generateCleanupCallback().onOpen(db);
1349
1350        WorkSpecDao workSpecDao = mDatabase.workSpecDao();
1351        assertThat(workSpecDao.getWorkSpec(work0.getStringId()), is(nullValue()));
1352        assertThat(workSpecDao.getWorkSpec(work1.getStringId()), is(not(nullValue())));
1353        assertThat(workSpecDao.getWorkSpec(work2.getStringId()), is(not(nullValue())));
1354    }
1355
1356    @Test
1357    @SmallTest
1358    @SdkSuppress(maxSdkVersion = 22)
1359    public void testEnqueueApi22OrLower_withBatteryNotLowConstraint_expectsOriginalWorker() {
1360        OneTimeWorkRequest work = new OneTimeWorkRequest.Builder(TestWorker.class)
1361                .setConstraints(new Constraints.Builder()
1362                        .setRequiresBatteryNotLow(true)
1363                        .build())
1364                .build();
1365        mWorkManagerImpl.beginWith(work).synchronous().enqueueSync();
1366
1367        WorkSpec workSpec = mDatabase.workSpecDao().getWorkSpec(work.getStringId());
1368        assertThat(workSpec.workerClassName, is(TestWorker.class.getName()));
1369    }
1370
1371    @Test
1372    @SmallTest
1373    @SdkSuppress(maxSdkVersion = 22)
1374    public void testEnqueueApi22OrLower_withStorageNotLowConstraint_expectsOriginalWorker() {
1375        OneTimeWorkRequest work = new OneTimeWorkRequest.Builder(TestWorker.class)
1376                .setConstraints(new Constraints.Builder()
1377                        .setRequiresStorageNotLow(true)
1378                        .build())
1379                .build();
1380        mWorkManagerImpl.beginWith(work).synchronous().enqueueSync();
1381
1382        WorkSpec workSpec = mDatabase.workSpecDao().getWorkSpec(work.getStringId());
1383        assertThat(workSpec.workerClassName, is(TestWorker.class.getName()));
1384    }
1385
1386    @Test
1387    @SmallTest
1388    @SdkSuppress(minSdkVersion = 23, maxSdkVersion = 25)
1389    public void testEnqueueApi23To25_withBatteryNotLowConstraint_expectsConstraintTrackingWorker() {
1390        OneTimeWorkRequest work = new OneTimeWorkRequest.Builder(TestWorker.class)
1391                .setConstraints(new Constraints.Builder()
1392                .setRequiresBatteryNotLow(true)
1393                .build())
1394                .build();
1395        mWorkManagerImpl.beginWith(work).synchronous().enqueueSync();
1396
1397        WorkSpec workSpec = mDatabase.workSpecDao().getWorkSpec(work.getStringId());
1398        assertThat(workSpec.workerClassName, is(ConstraintTrackingWorker.class.getName()));
1399        assertThat(workSpec.input.getString(
1400                ConstraintTrackingWorker.ARGUMENT_CLASS_NAME, null),
1401                is(TestWorker.class.getName()));
1402    }
1403
1404    @Test
1405    @SmallTest
1406    @SdkSuppress(minSdkVersion = 23, maxSdkVersion = 25)
1407    public void testEnqueueApi23To25_withStorageNotLowConstraint_expectsConstraintTrackingWorker() {
1408        OneTimeWorkRequest work = new OneTimeWorkRequest.Builder(TestWorker.class)
1409                .setConstraints(new Constraints.Builder()
1410                        .setRequiresStorageNotLow(true)
1411                        .build())
1412                .build();
1413        mWorkManagerImpl.beginWith(work).synchronous().enqueueSync();
1414
1415        WorkSpec workSpec = mDatabase.workSpecDao().getWorkSpec(work.getStringId());
1416        assertThat(workSpec.workerClassName, is(ConstraintTrackingWorker.class.getName()));
1417        assertThat(workSpec.input.getString(
1418                ConstraintTrackingWorker.ARGUMENT_CLASS_NAME, null),
1419                is(TestWorker.class.getName()));
1420    }
1421
1422    @Test
1423    @SmallTest
1424    @SdkSuppress(minSdkVersion = 26)
1425    public void testEnqueueApi26OrHigher_withBatteryNotLowConstraint_expectsOriginalWorker() {
1426        OneTimeWorkRequest work = new OneTimeWorkRequest.Builder(TestWorker.class)
1427                .setConstraints(new Constraints.Builder()
1428                        .setRequiresBatteryNotLow(true)
1429                        .build())
1430                .build();
1431        mWorkManagerImpl.beginWith(work).synchronous().enqueueSync();
1432
1433        WorkSpec workSpec = mDatabase.workSpecDao().getWorkSpec(work.getStringId());
1434        assertThat(workSpec.workerClassName, is(TestWorker.class.getName()));
1435    }
1436
1437    @Test
1438    @SmallTest
1439    @SdkSuppress(minSdkVersion = 26)
1440    public void testEnqueueApi26OrHigher_withStorageNotLowConstraint_expectsOriginalWorker() {
1441        OneTimeWorkRequest work = new OneTimeWorkRequest.Builder(TestWorker.class)
1442                .setConstraints(new Constraints.Builder()
1443                        .setRequiresStorageNotLow(true)
1444                        .build())
1445                .build();
1446        mWorkManagerImpl.beginWith(work).synchronous().enqueueSync();
1447
1448        WorkSpec workSpec = mDatabase.workSpecDao().getWorkSpec(work.getStringId());
1449        assertThat(workSpec.workerClassName, is(TestWorker.class.getName()));
1450    }
1451
1452    @Test
1453    @LargeTest
1454    @SdkSuppress(maxSdkVersion = 22)
1455    public void testSchedulerLimits() {
1456        for (int i = 0; i < NUM_WORKERS; i++) {
1457            mWorkManagerImpl.enqueue(OneTimeWorkRequest.from(TestWorker.class));
1458            List<WorkSpec> eligibleWorkSpecs = mWorkManagerImpl.getWorkDatabase()
1459                    .workSpecDao()
1460                    .getEligibleWorkForScheduling();
1461
1462            int size = eligibleWorkSpecs != null ? eligibleWorkSpecs.size() : 0;
1463            assertThat(size, lessThanOrEqualTo(Scheduler.MAX_SCHEDULER_LIMIT));
1464        }
1465    }
1466
1467    private void insertWorkSpecAndTags(WorkRequest work) {
1468        mDatabase.workSpecDao().insertWorkSpec(work.getWorkSpec());
1469        for (String tag : work.getTags()) {
1470            mDatabase.workTagDao().insert(new WorkTag(tag, work.getStringId()));
1471        }
1472    }
1473
1474    private void insertNamedWorks(String name, WorkRequest... works) {
1475        for (WorkRequest work : works) {
1476            insertWorkSpecAndTags(work);
1477            mDatabase.workNameDao().insert(new WorkName(name, work.getStringId()));
1478        }
1479    }
1480
1481    private void insertDependency(OneTimeWorkRequest work, OneTimeWorkRequest prerequisiteWork) {
1482        mDatabase.dependencyDao().insertDependency(
1483                new Dependency(work.getStringId(), prerequisiteWork.getStringId()));
1484    }
1485}
1486