WorkerWrapperTest.java revision 9f91ee8c71606f36a51177cd0b5c3005834be1ff
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.State.BLOCKED; 20import static androidx.work.State.CANCELLED; 21import static androidx.work.State.ENQUEUED; 22import static androidx.work.State.FAILED; 23import static androidx.work.State.RUNNING; 24import static androidx.work.State.SUCCEEDED; 25 26import static org.hamcrest.CoreMatchers.equalTo; 27import static org.hamcrest.CoreMatchers.is; 28import static org.hamcrest.CoreMatchers.notNullValue; 29import static org.hamcrest.MatcherAssert.assertThat; 30import static org.hamcrest.Matchers.contains; 31import static org.hamcrest.Matchers.containsInAnyOrder; 32import static org.hamcrest.Matchers.greaterThan; 33import static org.mockito.Mockito.mock; 34import static org.mockito.Mockito.spy; 35import static org.mockito.Mockito.times; 36import static org.mockito.Mockito.verify; 37 38import android.content.Context; 39import android.support.test.InstrumentationRegistry; 40import android.support.test.filters.LargeTest; 41import android.support.test.filters.SmallTest; 42import android.support.test.runner.AndroidJUnit4; 43 44import androidx.work.Arguments; 45import androidx.work.ArrayCreatingInputMerger; 46import androidx.work.DatabaseTest; 47import androidx.work.PeriodicWork; 48import androidx.work.Work; 49import androidx.work.Worker; 50import androidx.work.impl.model.Dependency; 51import androidx.work.impl.model.DependencyDao; 52import androidx.work.impl.model.WorkSpec; 53import androidx.work.impl.model.WorkSpecDao; 54import androidx.work.impl.utils.taskexecutor.InstantTaskExecutorRule; 55import androidx.work.worker.ChainedArgumentWorker; 56import androidx.work.worker.EchoingWorker; 57import androidx.work.worker.FailureWorker; 58import androidx.work.worker.RetryWorker; 59import androidx.work.worker.SleepTestWorker; 60import androidx.work.worker.TestWorker; 61 62import org.junit.Before; 63import org.junit.Rule; 64import org.junit.Test; 65import org.junit.runner.RunWith; 66import org.mockito.ArgumentCaptor; 67 68import java.util.Arrays; 69import java.util.Collections; 70import java.util.List; 71import java.util.concurrent.Executors; 72import java.util.concurrent.TimeUnit; 73 74@RunWith(AndroidJUnit4.class) 75public class WorkerWrapperTest extends DatabaseTest { 76 private WorkSpecDao mWorkSpecDao; 77 private DependencyDao mDependencyDao; 78 private Context mContext; 79 private ExecutionListener mMockListener; 80 private Scheduler mMockScheduler; 81 82 @Rule 83 public InstantTaskExecutorRule mRule = new InstantTaskExecutorRule(); 84 85 @Before 86 public void setUp() { 87 mContext = InstrumentationRegistry.getTargetContext(); 88 mWorkSpecDao = spy(mDatabase.workSpecDao()); 89 mDependencyDao = mDatabase.dependencyDao(); 90 mMockListener = mock(ExecutionListener.class); 91 mMockScheduler = mock(Scheduler.class); 92 } 93 94 @Test 95 @SmallTest 96 public void testSuccess() throws InterruptedException { 97 Work work = new Work.Builder(TestWorker.class).build(); 98 insertWork(work); 99 new WorkerWrapper.Builder(mContext, mDatabase, work.getId()) 100 .withListener(mMockListener) 101 .build() 102 .run(); 103 verify(mMockListener).onExecuted(work.getId(), true, false); 104 assertThat(mWorkSpecDao.getState(work.getId()), is(SUCCEEDED)); 105 } 106 107 @Test 108 @SmallTest 109 public void testRunAttemptCountIncremented_successfulExecution() { 110 Work work = new Work.Builder(TestWorker.class).build(); 111 insertWork(work); 112 new WorkerWrapper.Builder(mContext, mDatabase, work.getId()) 113 .withSchedulers(Collections.singletonList(mMockScheduler)) 114 .withListener(mMockListener) 115 .build() 116 .run(); 117 WorkSpec latestWorkSpec = mWorkSpecDao.getWorkSpec(work.getId()); 118 assertThat(latestWorkSpec.runAttemptCount, is(1)); 119 } 120 121 @Test 122 @SmallTest 123 public void testRunAttemptCountIncremented_failedExecution() { 124 Work work = new Work.Builder(FailureWorker.class).build(); 125 insertWork(work); 126 new WorkerWrapper.Builder(mContext, mDatabase, work.getId()) 127 .withSchedulers(Collections.singletonList(mMockScheduler)) 128 .withListener(mMockListener) 129 .build() 130 .run(); 131 WorkSpec latestWorkSpec = mWorkSpecDao.getWorkSpec(work.getId()); 132 assertThat(latestWorkSpec.runAttemptCount, is(1)); 133 } 134 135 @Test 136 @SmallTest 137 public void testPermanentErrorWithInvalidWorkSpecId() throws InterruptedException { 138 final String invalidWorkSpecId = "INVALID_ID"; 139 new WorkerWrapper.Builder(mContext, mDatabase, invalidWorkSpecId) 140 .withListener(mMockListener) 141 .build() 142 .run(); 143 verify(mMockListener).onExecuted(invalidWorkSpecId, false, false); 144 } 145 146 @Test 147 @SmallTest 148 public void testNotEnqueued() throws InterruptedException { 149 Work work = new Work.Builder(TestWorker.class).withInitialState(RUNNING).build(); 150 insertWork(work); 151 new WorkerWrapper.Builder(mContext, mDatabase, work.getId()) 152 .withListener(mMockListener) 153 .build() 154 .run(); 155 verify(mMockListener).onExecuted(work.getId(), false, true); 156 } 157 158 @Test 159 @SmallTest 160 public void testCancelled() throws InterruptedException { 161 Work work = new Work.Builder(TestWorker.class).withInitialState(CANCELLED).build(); 162 insertWork(work); 163 new WorkerWrapper.Builder(mContext, mDatabase, work.getId()) 164 .withListener(mMockListener) 165 .build() 166 .run(); 167 verify(mMockListener).onExecuted(work.getId(), false, false); 168 assertThat(mWorkSpecDao.getState(work.getId()), is(CANCELLED)); 169 } 170 171 @Test 172 @SmallTest 173 public void testPermanentErrorWithInvalidWorkerClass() throws InterruptedException { 174 Work work = new Work.Builder(TestWorker.class).build(); 175 getWorkSpec(work).workerClassName = "INVALID_CLASS_NAME"; 176 insertWork(work); 177 new WorkerWrapper.Builder(mContext, mDatabase, work.getId()) 178 .withListener(mMockListener) 179 .build() 180 .run(); 181 verify(mMockListener).onExecuted(work.getId(), false, false); 182 assertThat(mWorkSpecDao.getState(work.getId()), is(FAILED)); 183 } 184 185 @Test 186 @SmallTest 187 public void testPermanentErrorWithInvalidInputMergerClass() throws InterruptedException { 188 Work work = new Work.Builder(TestWorker.class).build(); 189 getWorkSpec(work).inputMergerClassName = "INVALID_CLASS_NAME"; 190 insertWork(work); 191 new WorkerWrapper.Builder(mContext, mDatabase, work.getId()) 192 .withSchedulers(Collections.singletonList(mMockScheduler)) 193 .withListener(mMockListener) 194 .build() 195 .run(); 196 verify(mMockListener).onExecuted(work.getId(), false, false); 197 assertThat(mWorkSpecDao.getState(work.getId()), is(FAILED)); 198 } 199 200 @Test 201 @SmallTest 202 public void testFailed() throws InterruptedException { 203 Work work = new Work.Builder(FailureWorker.class).build(); 204 insertWork(work); 205 new WorkerWrapper.Builder(mContext, mDatabase, work.getId()) 206 .withListener(mMockListener) 207 .build() 208 .run(); 209 verify(mMockListener).onExecuted(work.getId(), false, false); 210 assertThat(mWorkSpecDao.getState(work.getId()), is(FAILED)); 211 } 212 213 @Test 214 @LargeTest 215 public void testRunning() throws InterruptedException { 216 Work work = new Work.Builder(SleepTestWorker.class).build(); 217 insertWork(work); 218 WorkerWrapper wrapper = new WorkerWrapper.Builder(mContext, mDatabase, work.getId()) 219 .withListener(mMockListener) 220 .build(); 221 Executors.newSingleThreadExecutor().submit(wrapper); 222 Thread.sleep(2000L); // Async wait duration. 223 assertThat(mWorkSpecDao.getState(work.getId()), is(RUNNING)); 224 Thread.sleep(SleepTestWorker.SLEEP_DURATION); 225 verify(mMockListener).onExecuted(work.getId(), true, false); 226 } 227 228 @Test 229 @SmallTest 230 public void testDependencies() { 231 Work prerequisiteWork = new Work.Builder(TestWorker.class).build(); 232 Work work = new Work.Builder(TestWorker.class) 233 .withInitialState(BLOCKED).build(); 234 Dependency dependency = new Dependency(work.getId(), prerequisiteWork.getId()); 235 236 mDatabase.beginTransaction(); 237 try { 238 insertWork(prerequisiteWork); 239 insertWork(work); 240 mDependencyDao.insertDependency(dependency); 241 mDatabase.setTransactionSuccessful(); 242 } finally { 243 mDatabase.endTransaction(); 244 } 245 246 assertThat(mWorkSpecDao.getState(prerequisiteWork.getId()), is(ENQUEUED)); 247 assertThat(mWorkSpecDao.getState(work.getId()), is(BLOCKED)); 248 assertThat(mDependencyDao.hasCompletedAllPrerequisites(work.getId()), is(false)); 249 250 new WorkerWrapper.Builder(mContext, mDatabase, prerequisiteWork.getId()) 251 .withListener(mMockListener) 252 .withSchedulers(Collections.singletonList(mMockScheduler)) 253 .build() 254 .run(); 255 256 assertThat(mWorkSpecDao.getState(prerequisiteWork.getId()), is(SUCCEEDED)); 257 assertThat(mWorkSpecDao.getState(work.getId()), is(ENQUEUED)); 258 assertThat(mDependencyDao.hasCompletedAllPrerequisites(work.getId()), is(true)); 259 260 ArgumentCaptor<WorkSpec> captor = ArgumentCaptor.forClass(WorkSpec.class); 261 verify(mMockScheduler).schedule(captor.capture()); 262 assertThat(captor.getValue().id, is(work.getId())); 263 } 264 265 @Test 266 @SmallTest 267 public void testDependencies_passesOutputs() { 268 Work prerequisiteWork = new Work.Builder(ChainedArgumentWorker.class).build(); 269 Work work = new Work.Builder(TestWorker.class).withInitialState(BLOCKED).build(); 270 Dependency dependency = new Dependency(work.getId(), prerequisiteWork.getId()); 271 272 mDatabase.beginTransaction(); 273 try { 274 insertWork(prerequisiteWork); 275 insertWork(work); 276 mDependencyDao.insertDependency(dependency); 277 mDatabase.setTransactionSuccessful(); 278 } finally { 279 mDatabase.endTransaction(); 280 } 281 282 new WorkerWrapper.Builder(mContext, mDatabase, prerequisiteWork.getId()) 283 .withSchedulers(Collections.singletonList(mMockScheduler)) 284 .build().run(); 285 286 List<Arguments> arguments = mWorkSpecDao.getInputsFromPrerequisites(work.getId()); 287 assertThat(arguments.size(), is(1)); 288 assertThat(arguments, contains(ChainedArgumentWorker.getChainedArguments())); 289 } 290 291 @Test 292 @SmallTest 293 public void testDependencies_passesMergedOutputs() { 294 String key = "key"; 295 String value1 = "value1"; 296 String value2 = "value2"; 297 298 Work prerequisiteWork1 = new Work.Builder(EchoingWorker.class) 299 .withArguments(new Arguments.Builder().putString(key, value1).build()) 300 .build(); 301 Work prerequisiteWork2 = new Work.Builder(EchoingWorker.class) 302 .withArguments(new Arguments.Builder().putString(key, value2).build()) 303 .build(); 304 Work work = new Work.Builder(TestWorker.class) 305 .withInputMerger(ArrayCreatingInputMerger.class) 306 .build(); 307 Dependency dependency1 = new Dependency(work.getId(), prerequisiteWork1.getId()); 308 Dependency dependency2 = new Dependency(work.getId(), prerequisiteWork2.getId()); 309 310 mDatabase.beginTransaction(); 311 try { 312 insertWork(prerequisiteWork1); 313 insertWork(prerequisiteWork2); 314 insertWork(work); 315 mDependencyDao.insertDependency(dependency1); 316 mDependencyDao.insertDependency(dependency2); 317 mDatabase.setTransactionSuccessful(); 318 } finally { 319 mDatabase.endTransaction(); 320 } 321 322 // Run the prerequisites. 323 new WorkerWrapper.Builder(mContext, mDatabase, prerequisiteWork1.getId()) 324 .withSchedulers(Collections.singletonList(mMockScheduler)) 325 .build().run(); 326 327 new WorkerWrapper.Builder(mContext, mDatabase, prerequisiteWork2.getId()) 328 .withSchedulers(Collections.singletonList(mMockScheduler)) 329 .build().run(); 330 331 // Create and run the dependent work. 332 WorkerWrapper workerWrapper = new WorkerWrapper.Builder(mContext, mDatabase, work.getId()) 333 .withSchedulers(Collections.singletonList(mMockScheduler)) 334 .build(); 335 workerWrapper.run(); 336 337 Arguments arguments = workerWrapper.mWorker.getArguments(); 338 assertThat(arguments.size(), is(1)); 339 assertThat(Arrays.asList(arguments.getStringArray(key)), 340 containsInAnyOrder(value1, value2)); 341 } 342 343 @Test 344 @SmallTest 345 public void testDependencies_setsPeriodStartTimesForUnblockedWork() { 346 Work prerequisiteWork = new Work.Builder(TestWorker.class).build(); 347 Work work = new Work.Builder(TestWorker.class).withInitialState(BLOCKED).build(); 348 Dependency dependency = new Dependency(work.getId(), prerequisiteWork.getId()); 349 350 mDatabase.beginTransaction(); 351 try { 352 insertWork(prerequisiteWork); 353 insertWork(work); 354 mDependencyDao.insertDependency(dependency); 355 mDatabase.setTransactionSuccessful(); 356 } finally { 357 mDatabase.endTransaction(); 358 } 359 360 long beforeUnblockedTime = System.currentTimeMillis(); 361 362 new WorkerWrapper.Builder(mContext, mDatabase, prerequisiteWork.getId()) 363 .withListener(mMockListener) 364 .withSchedulers(Collections.singletonList(mMockScheduler)) 365 .build() 366 .run(); 367 368 WorkSpec workSpec = mWorkSpecDao.getWorkSpec(work.getId()); 369 assertThat(workSpec.periodStartTime, is(greaterThan(beforeUnblockedTime))); 370 } 371 372 @Test 373 @SmallTest 374 public void testDependencies_failsUncancelledDependentsOnFailure() { 375 Work prerequisiteWork = new Work.Builder(FailureWorker.class).build(); 376 Work work = new Work.Builder(TestWorker.class).withInitialState(BLOCKED).build(); 377 Work cancelledWork = new Work.Builder(TestWorker.class).withInitialState(CANCELLED).build(); 378 Dependency dependency1 = new Dependency(work.getId(), prerequisiteWork.getId()); 379 Dependency dependency2 = new Dependency(cancelledWork.getId(), prerequisiteWork.getId()); 380 381 mDatabase.beginTransaction(); 382 try { 383 insertWork(prerequisiteWork); 384 insertWork(work); 385 insertWork(cancelledWork); 386 mDependencyDao.insertDependency(dependency1); 387 mDependencyDao.insertDependency(dependency2); 388 mDatabase.setTransactionSuccessful(); 389 } finally { 390 mDatabase.endTransaction(); 391 } 392 393 new WorkerWrapper.Builder(mContext, mDatabase, prerequisiteWork.getId()).build().run(); 394 395 assertThat(mWorkSpecDao.getState(prerequisiteWork.getId()), is(FAILED)); 396 assertThat(mWorkSpecDao.getState(work.getId()), is(FAILED)); 397 assertThat(mWorkSpecDao.getState(cancelledWork.getId()), is(CANCELLED)); 398 } 399 400 @Test 401 @SmallTest 402 public void testRun_periodicWork_success_updatesPeriodStartTime() { 403 long intervalDuration = PeriodicWork.MIN_PERIODIC_INTERVAL_MILLIS; 404 long periodStartTime = System.currentTimeMillis(); 405 long expectedNextPeriodStartTime = periodStartTime + intervalDuration; 406 407 PeriodicWork periodicWork = new PeriodicWork.Builder( 408 TestWorker.class, intervalDuration, TimeUnit.MILLISECONDS).build(); 409 410 getWorkSpec(periodicWork).periodStartTime = periodStartTime; 411 412 insertWork(periodicWork); 413 414 new WorkerWrapper.Builder(mContext, mDatabase, periodicWork.getId()) 415 .withListener(mMockListener) 416 .build() 417 .run(); 418 419 WorkSpec updatedWorkSpec = mWorkSpecDao.getWorkSpec(periodicWork.getId()); 420 assertThat(updatedWorkSpec.periodStartTime, is(expectedNextPeriodStartTime)); 421 } 422 423 @Test 424 @SmallTest 425 public void testRun_periodicWork_failure_updatesPeriodStartTime() { 426 long intervalDuration = PeriodicWork.MIN_PERIODIC_INTERVAL_MILLIS; 427 long periodStartTime = System.currentTimeMillis(); 428 long expectedNextPeriodStartTime = periodStartTime + intervalDuration; 429 430 PeriodicWork periodicWork = new PeriodicWork.Builder( 431 FailureWorker.class, intervalDuration, TimeUnit.MILLISECONDS).build(); 432 433 getWorkSpec(periodicWork).periodStartTime = periodStartTime; 434 435 insertWork(periodicWork); 436 437 new WorkerWrapper.Builder(mContext, mDatabase, periodicWork.getId()) 438 .withListener(mMockListener) 439 .build() 440 .run(); 441 442 WorkSpec updatedWorkSpec = mWorkSpecDao.getWorkSpec(periodicWork.getId()); 443 assertThat(updatedWorkSpec.periodStartTime, is(expectedNextPeriodStartTime)); 444 } 445 446 @Test 447 @SmallTest 448 public void testPeriodicWork_success() throws InterruptedException { 449 PeriodicWork periodicWork = new PeriodicWork.Builder( 450 TestWorker.class, 451 PeriodicWork.MIN_PERIODIC_INTERVAL_MILLIS, 452 TimeUnit.MILLISECONDS) 453 .build(); 454 455 final String periodicWorkId = periodicWork.getId(); 456 insertWork(periodicWork); 457 new WorkerWrapper.Builder(mContext, mDatabase, periodicWorkId) 458 .withListener(mMockListener) 459 .build() 460 .run(); 461 462 WorkSpec periodicWorkSpecAfterFirstRun = mWorkSpecDao.getWorkSpec(periodicWorkId); 463 verify(mMockListener).onExecuted(periodicWorkId, true, false); 464 assertThat(periodicWorkSpecAfterFirstRun.runAttemptCount, is(0)); 465 assertThat(periodicWorkSpecAfterFirstRun.state, is(ENQUEUED)); 466 } 467 468 @Test 469 @SmallTest 470 public void testPeriodicWork_fail() throws InterruptedException { 471 PeriodicWork periodicWork = new PeriodicWork.Builder( 472 FailureWorker.class, 473 PeriodicWork.MIN_PERIODIC_INTERVAL_MILLIS, 474 TimeUnit.MILLISECONDS) 475 .build(); 476 477 final String periodicWorkId = periodicWork.getId(); 478 insertWork(periodicWork); 479 new WorkerWrapper.Builder(mContext, mDatabase, periodicWorkId) 480 .withListener(mMockListener) 481 .build() 482 .run(); 483 484 WorkSpec periodicWorkSpecAfterFirstRun = mWorkSpecDao.getWorkSpec(periodicWorkId); 485 verify(mMockListener).onExecuted(periodicWorkId, false, false); 486 assertThat(periodicWorkSpecAfterFirstRun.runAttemptCount, is(0)); 487 assertThat(periodicWorkSpecAfterFirstRun.state, is(ENQUEUED)); 488 } 489 490 @Test 491 @SmallTest 492 public void testPeriodicWork_retry() throws InterruptedException { 493 PeriodicWork periodicWork = new PeriodicWork.Builder( 494 RetryWorker.class, 495 PeriodicWork.MIN_PERIODIC_INTERVAL_MILLIS, 496 TimeUnit.MILLISECONDS) 497 .build(); 498 499 final String periodicWorkId = periodicWork.getId(); 500 insertWork(periodicWork); 501 new WorkerWrapper.Builder(mContext, mDatabase, periodicWorkId) 502 .withListener(mMockListener) 503 .build() 504 .run(); 505 506 WorkSpec periodicWorkSpecAfterFirstRun = mWorkSpecDao.getWorkSpec(periodicWorkId); 507 verify(mMockListener).onExecuted(periodicWorkId, false, true); 508 assertThat(periodicWorkSpecAfterFirstRun.runAttemptCount, is(1)); 509 assertThat(periodicWorkSpecAfterFirstRun.state, is(ENQUEUED)); 510 } 511 512 @Test 513 @SmallTest 514 public void testScheduler() throws InterruptedException { 515 Work work = new Work.Builder(TestWorker.class).build(); 516 insertWork(work); 517 Scheduler mockScheduler = mock(Scheduler.class); 518 519 new WorkerWrapper.Builder(mContext, mDatabase, work.getId()) 520 .withSchedulers(Collections.singletonList(mockScheduler)) 521 .build() 522 .run(); 523 524 verify(mockScheduler).schedule(); 525 } 526 527 @Test 528 @SmallTest 529 public void testFromWorkSpec_hasAppContext() throws InterruptedException { 530 Work work = new Work.Builder(TestWorker.class).build(); 531 Worker worker = 532 WorkerWrapper.workerFromWorkSpec(mContext, getWorkSpec(work), Arguments.EMPTY); 533 534 assertThat(worker, is(notNullValue())); 535 assertThat(worker.getAppContext(), is(equalTo(mContext.getApplicationContext()))); 536 } 537 538 @Test 539 @SmallTest 540 public void testFromWorkSpec_hasCorrectArguments() throws InterruptedException { 541 String key = "KEY"; 542 String expectedValue = "VALUE"; 543 Arguments arguments = new Arguments.Builder().putString(key, expectedValue).build(); 544 545 Work work = new Work.Builder(TestWorker.class).withArguments(arguments).build(); 546 Worker worker = WorkerWrapper.workerFromWorkSpec(mContext, getWorkSpec(work), arguments); 547 548 assertThat(worker, is(notNullValue())); 549 assertThat(worker.getArguments().getString(key, null), is(expectedValue)); 550 551 work = new Work.Builder(TestWorker.class).build(); 552 worker = WorkerWrapper.workerFromWorkSpec(mContext, getWorkSpec(work), Arguments.EMPTY); 553 554 assertThat(worker, is(notNullValue())); 555 assertThat(worker.getArguments().size(), is(0)); 556 } 557 558 @Test 559 @SmallTest 560 public void testSuccess_withPendingScheduledWork() { 561 Work work = new Work.Builder(TestWorker.class).build(); 562 insertWork(work); 563 564 Work unscheduled = new Work.Builder(TestWorker.class).build(); 565 insertWork(unscheduled); 566 567 new WorkerWrapper.Builder(mContext, mDatabase, work.getId()) 568 .withSchedulers(Collections.singletonList(mMockScheduler)) 569 .withListener(mMockListener) 570 .build() 571 .run(); 572 573 verify(mMockScheduler, times(1)).schedule(unscheduled.getWorkSpec()); 574 verify(mMockListener).onExecuted(work.getId(), true, false); 575 assertThat(mWorkSpecDao.getState(work.getId()), is(SUCCEEDED)); 576 } 577 578 @Test 579 @SmallTest 580 public void testFailure_withPendingScheduledWork() { 581 Work work = new Work.Builder(FailureWorker.class).build(); 582 insertWork(work); 583 584 Work unscheduled = new Work.Builder(TestWorker.class).build(); 585 insertWork(unscheduled); 586 587 new WorkerWrapper.Builder(mContext, mDatabase, work.getId()) 588 .withSchedulers(Collections.singletonList(mMockScheduler)) 589 .withListener(mMockListener) 590 .build() 591 .run(); 592 593 verify(mMockScheduler, times(1)).schedule(unscheduled.getWorkSpec()); 594 verify(mMockListener).onExecuted(work.getId(), false, false); 595 assertThat(mWorkSpecDao.getState(work.getId()), is(FAILED)); 596 } 597} 598