/** * Cancel either the pending {@link JobRequest} or the running {@link Job}. * * @param jobId The unique ID of the {@link JobRequest} or running {@link Job}. * @return {@code true} if a request or job were found and canceled. */ public boolean cancel(int jobId) { // call both methods boolean result = cancelInner(getJobRequest(jobId, true)) | cancelInner(getJob(jobId)); JobProxy.Common.cleanUpOrphanedJob(mContext, jobId); // do this as well, just in case return result; }
/** * @param jobId The unique ID of the pending {@link JobRequest}. * @return The {@link JobRequest} if it's pending or {@code null} otherwise. */ public JobRequest getJobRequest(int jobId) { JobRequest request = getJobRequest(jobId, false); if (request != null && request.isTransient() && !request.getJobApi().getProxy(mContext).isPlatformJobScheduled(request)) { getJobStorage().remove(request); return null; } else { return request; } }
@SuppressWarnings("ConstantConditions") @Test public void verifyEarlyExecution() { TestClock clock = new TestClock(); clock.setTime(13, 0); JobRequest request = verifyExecutionAndSuccessfulReschedule(clock, TimeUnit.HOURS.toMillis(14), TimeUnit.HOURS.toMillis(15)); assertThat(request.getStartMs()).isEqualTo(TimeUnit.HOURS.toMillis(25)); assertThat(request.getEndMs()).isEqualTo(TimeUnit.HOURS.toMillis(26)); int id = DailyJob.schedule(DummyJobs.createBuilder(DummyJobs.SuccessDailyJob.class), TimeUnit.HOURS.toMillis(14), TimeUnit.HOURS.toMillis(15)); request = manager().getJobRequest(id); assertThat(request.getStartMs()).isEqualTo(TimeUnit.HOURS.toMillis(1)); assertThat(request.getEndMs()).isEqualTo(TimeUnit.HOURS.toMillis(2)); }
@Test public void verifyAlarmIsCanceledAfterStart() throws Exception { assumeTrue(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP); JobConfig.forceApi(JobApi.V_21); int jobId = scheduleJob(); final Intent intent = PlatformAlarmServiceExact.createIntent(context(), jobId, null); PendingIntent pendingIntent = PendingIntent.getService(context(), jobId, intent, PendingIntent.FLAG_NO_CREATE); assertThat(pendingIntent).isNotNull(); boolean started = TransientBundleCompat.startWithTransientBundle(context(), mJobManagerRule.getManager().getJobRequest(jobId)); assertThat(started).isTrue(); pendingIntent = PendingIntent.getService(context(), jobId, intent, PendingIntent.FLAG_NO_CREATE); assertThat(pendingIntent).isNull(); }
@Test public void testCancel() { int jobId = new JobRequest.Builder(TAG) .setExecutionWindow(TimeUnit.HOURS.toMillis(4), TimeUnit.HOURS.toMillis(5)) .build() .schedule(); JobRequest request = mWorkManagerRule.getManager().getJobRequest(jobId); JobProxyWorkManager jobProxyWorkManager = new JobProxyWorkManager(InstrumentationRegistry.getTargetContext()); assertThat(jobProxyWorkManager.isPlatformJobScheduled(request)).isTrue(); String tag = JobProxyWorkManager.createTag(jobId); List<WorkInfo> statuses = mWorkManagerRule.getWorkStatus(tag); assertThat(statuses).isNotNull().hasSize(1); assertThat(statuses.get(0).getState()).isEqualTo(WorkInfo.State.ENQUEUED); mWorkManagerRule.getManager().cancel(jobId); assertThat(mWorkManagerRule.getWorkStatus(tag).get(0).getState()).isEqualTo(WorkInfo.State.CANCELLED); assertThat(jobProxyWorkManager.isPlatformJobScheduled(request)).isFalse(); }
@Test public void verifyJobIdAsyncDailyJob() throws Exception { JobConfig.setClock(new TestClock()); final AtomicInteger jobId = new AtomicInteger(-2); DailyJob.scheduleAsync(DummyJobs.createBuilder(DummyJobs.SuccessJob.class), 1000, 2000, new JobRequest.JobScheduledCallback() { @Override public void onJobScheduled(int id, @NonNull String tag, @Nullable Exception exception) { jobId.set(id); } }); waitUntilScheduled(); assertThat(manager().getJobRequest(jobId.get())).isNotNull(); }
@Test public void verifyDailyJobIsNotExact() { long time = 1L; int jobId = DailyJob.schedule(DummyJobs.createBuilder(DummyJobs.SuccessJob.class), time, time); JobRequest request = manager().getJobRequest(jobId); assertThat(request).isNotNull(); assertThat(request.isExact()).isFalse(); }
@Test public void incrementPeriodicJobFailureCount() { int jobId = DummyJobs.createBuilder(DummyJobs.FailureJob.class) .setPeriodic(TimeUnit.MINUTES.toMillis(15)) .build() .schedule(); executeJob(jobId, Job.Result.FAILURE); assertThat(manager().getJobRequest(jobId).getFailureCount()).isEqualTo(1); resetJob(jobId); executeJob(jobId, Job.Result.FAILURE); assertThat(manager().getJobRequest(jobId).getFailureCount()).isEqualTo(2); }
@Test public void testPeriodicJobNotInStartedState() throws Throwable { int jobId = DummyJobs.createBuilder(DummyJobs.TwoSecondPauseJob.class) .setPeriodic(TimeUnit.MINUTES.toMillis(15)) .build() .schedule(); executeJobAsync(jobId, Job.Result.SUCCESS); // wait until the job is started Thread.sleep(100); // request should be in started state, running but not removed from DB JobRequest startedRequest = manager().getJobRequest(jobId, true); assertThat(startedRequest).isNotNull(); assertThat(startedRequest.isStarted()).isFalse(); }
@Test public void verifyAlarmNotCanceledForPeriodicAfterStart() throws Exception { assumeTrue(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP); JobConfig.forceApi(JobApi.V_21); Bundle extras = new Bundle(); extras.putString("key", "value"); int jobId = new JobRequest.Builder("tag") .setPeriodic(TimeUnit.DAYS.toMillis(1)) .setTransientExtras(extras) .build() .schedule(); assertThat(mJobManagerRule.getAllPendingJobsFromScheduler()).isNotNull().isNotEmpty(); JobRequest request = mJobManagerRule.getManager().getJobRequest(jobId); assertThat(request.isTransient()).isTrue(); final Intent intent = PlatformAlarmServiceExact.createIntent(context(), jobId, null); PendingIntent pendingIntent = PendingIntent.getService(context(), jobId, intent, PendingIntent.FLAG_NO_CREATE); assertThat(pendingIntent).isNotNull(); boolean started = TransientBundleCompat.startWithTransientBundle(context(), request); assertThat(started).isTrue(); pendingIntent = PendingIntent.getService(context(), jobId, intent, PendingIntent.FLAG_NO_CREATE); assertThat(pendingIntent).isNotNull(); }
private void testPeriodicJob(Class<? extends Job> clazz, Job.Result result) throws Exception { int jobId = DummyJobs.createBuilder(clazz) .setPeriodic(TimeUnit.MINUTES.toMillis(15)) .build() .schedule(); assertThat(manager().getJobRequest(jobId).getLastRun()).isEqualTo(0); executeJob(jobId, result); long lastRun = manager().getJobRequest(jobId).getLastRun(); assertThat(lastRun).isGreaterThan(0); Thread.sleep(2L); resetJob(jobId); executeJob(jobId, result); assertThat(manager().getJobRequest(jobId).getLastRun()).isGreaterThan(lastRun); }
@Test public void verifyHasExtras() { long start = 0; long end = 1L; int jobId = DailyJob.schedule(DummyJobs.createBuilder(DummyJobs.SuccessJob.class), start, end); JobRequest request = manager().getJobRequest(jobId); assertThat(request).isNotNull(); assertThat(request.getExtras().getLong(DailyJob.EXTRA_START_MS, -1)).isEqualTo(0L); assertThat(request.getExtras().getLong(DailyJob.EXTRA_END_MS, -1)).isEqualTo(1L); assertThat(request.getExtras().size()).isEqualTo(2); }
private JobRequest verifyExecutionAndSuccessfulReschedule(Clock clock, long start, long end) { JobConfig.setClock(clock); int jobId = DailyJob.schedule(DummyJobs.createBuilder(DummyJobs.SuccessDailyJob.class), start, end); assertThat(jobId).isEqualTo(1); executeJob(jobId, Job.Result.SUCCESS); assertThat(manager().getAllJobRequests()).hasSize(1); JobRequest newRequest = manager().getJobRequest(jobId + 1); assertThat(newRequest).isNotNull(); return newRequest; }
@Test public void testUpdateDoesNotCrash() { JobRequest request = DummyJobs.createOneOff(); int jobId = request.schedule(); assertThat(request.getScheduledAt()).isGreaterThan(0L); assertThat(request.getFailureCount()).isEqualTo(0); assertThat(request.getLastRun()).isEqualTo(0); SQLiteDatabase database = mock(SQLiteDatabase.class); when(database.update(anyString(), any(ContentValues.class), nullable(String.class), any(String[].class))).thenThrow(SQLException.class); manager().getJobStorage().injectDatabase(database); request.updateStats(true, true); // updates the database value, but fails in this case assertThat(request.getFailureCount()).isEqualTo(1); // in memory value was updated, keep that assertThat(request.getLastRun()).isGreaterThan(0); // kinda hacky, this removes the request from the cache, but doesn't delete it in the database, // because we're using the mock at the moment manager().getJobStorage().remove(request); manager().getJobStorage().injectDatabase(null); // reset request = manager().getJobRequest(jobId); assertThat(request.getFailureCount()).isEqualTo(0); assertThat(request.getLastRun()).isEqualTo(0); }
extras.getLong(EXTRA_START_MS, 0) % DAY, extras.getLong(EXTRA_END_MS, 0L) % DAY, true); request = JobManager.instance().getJobRequest(newJobId); if (request != null) { request.updateStats(false, true);
@Test public void verifyExtraValuesAreOverwritten() { long start = 0; long end = 1L; PersistableBundleCompat extras = new PersistableBundleCompat(); extras.putLong("something", 9L); // make sure this value is not overwritten extras.putLong(DailyJob.EXTRA_START_MS, 9L); // make sure they're overwritten extras.putLong(DailyJob.EXTRA_END_MS, 9L); int jobId = DailyJob.schedule(DummyJobs.createBuilder(DummyJobs.SuccessJob.class).setExtras(extras), start, end); JobRequest request = manager().getJobRequest(jobId); assertThat(request).isNotNull(); assertThat(request.getExtras().getLong(DailyJob.EXTRA_START_MS, -1)).isEqualTo(0L); assertThat(request.getExtras().getLong(DailyJob.EXTRA_END_MS, -1)).isEqualTo(1L); assertThat(request.getExtras().size()).isEqualTo(3); }
@Test public void verifyImmediateExecution() { long time = 1L; int nowJobId = DailyJob.startNowOnce(DummyJobs.createBuilder(DummyJobs.SuccessJob.class)); int normalJobId = DailyJob.schedule(DummyJobs.createBuilder(DummyJobs.SuccessJob.class), time, time); assertThat(manager().getAllJobRequests()).hasSize(2); executeJob(nowJobId, Job.Result.SUCCESS); assertThat(manager().getAllJobRequests()).hasSize(1); assertThat(manager().getJobRequest(normalJobId)).isNotNull(); }
@Test public void testScheduleAndCancel() { JobRequest request = DummyJobs.createOneOff(); int jobId = request.schedule(); assertThat(manager().getJobRequest(jobId)).isNotNull(); assertThat(manager().getJob(jobId)).isNull(); assertThat(manager().cancel(jobId)).isTrue(); assertThat(manager().getAllJobRequests()).isEmpty(); assertThat(manager().getAllJobs()).isEmpty(); request.schedule(); assertThat(manager().getAllJobRequests()).hasSize(1); assertThat(manager().cancelAll()).isEqualTo(1); assertThat(manager().getAllJobRequests()).isEmpty(); assertThat(manager().getAllJobs()).isEmpty(); }
/** * Cancel either the pending {@link JobRequest} or the running {@link Job}. * * @param jobId The unique ID of the {@link JobRequest} or running {@link Job}. * @return {@code true} if a request or job were found and canceled. */ public boolean cancel(int jobId) { // call both methods boolean result = cancelInner(getJobRequest(jobId, true)) | cancelInner(getJob(jobId)); JobProxy.Common.cleanUpOrphanedJob(mContext, jobId); // do this as well, just in case return result; }
/** * @param jobId The unique ID of the pending {@link JobRequest}. * @return The {@link JobRequest} if it's pending or {@code null} otherwise. */ public JobRequest getJobRequest(int jobId) { JobRequest request = getJobRequest(jobId, false); if (request != null && request.isTransient() && !request.getJobApi().getProxy(mContext).isPlatformJobScheduled(request)) { getJobStorage().remove(request); return null; } else { return request; } }