@Test public void testDelayedStart() throws Exception { expectJobAccepted(); expectJobFetch(); // Query to test if live tasks exist for the job. expectActiveTaskFetch(TASK); // Live tasks exist, so the cron manager must delay the cron launch. delayExecutor.execute(capture(delayLaunchCapture)); // The cron manager will then try to initiate the kill. scheduler.killTasks((Query.Builder) anyObject(), eq(CronJobManager.CRON_USER)); // Immediate query and delayed query. expectActiveTaskFetch(TASK).times(2); // Simulate the live task disappearing. expectActiveTaskFetch(); stateManager.insertPendingTasks(sanitizedConfiguration.getTaskConfigs()); control.replay(); cron.receiveJob(sanitizedConfiguration); cron.startJobNow(job.getKey()); assertEquals(ImmutableSet.of(job.getKey()), cron.getPendingRuns()); delayLaunchCapture.getValue().run(); assertEquals(ImmutableSet.<IJobKey>of(), cron.getPendingRuns()); }
@Test public void testUpdate() throws Exception { SanitizedConfiguration updated = new SanitizedConfiguration( IJobConfiguration.build(job.newBuilder().setCronSchedule("1 2 3 4 5"))); expectJobAccepted(); cronScheduler.deschedule(DEFAULT_JOB_KEY); expectJobAccepted(updated.getJobConfig()); control.replay(); cron.receiveJob(sanitizedConfiguration); cron.updateJob(updated); }
@Test(expected = ScheduleException.class) public void testInvalidCronSchedule() throws Exception { expect(cronScheduler.isValidSchedule(job.getCronSchedule())).andReturn(false); control.replay(); cron.receiveJob(sanitizedConfiguration); }
@Test(expected = ScheduleException.class) public void testRunOverlapRejected() throws Exception { IJobConfiguration killExisting = IJobConfiguration.build( job.newBuilder().setCronCollisionPolicy(CronCollisionPolicy.RUN_OVERLAP)); control.replay(); cron.receiveJob(new SanitizedConfiguration(killExisting)); }
@Test public void testDelayedStartResets() throws Exception { expectJobAccepted(); expectJobFetch(); // Query to test if live tasks exist for the job. expectActiveTaskFetch(TASK); // Live tasks exist, so the cron manager must delay the cron launch. delayExecutor.execute(capture(delayLaunchCapture)); // The cron manager will then try to initiate the kill. scheduler.killTasks((Query.Builder) anyObject(), eq(CronJobManager.CRON_USER)); // Immediate query and delayed query. expectActiveTaskFetch(TASK).times(2); // Simulate the live task disappearing. expectActiveTaskFetch(); // Round two. expectJobFetch(); expectActiveTaskFetch(TASK); delayExecutor.execute(capture(delayLaunchCapture)); scheduler.killTasks((Query.Builder) anyObject(), eq(CronJobManager.CRON_USER)); expectActiveTaskFetch(TASK).times(2); expectActiveTaskFetch(); stateManager.insertPendingTasks(sanitizedConfiguration.getTaskConfigs()); expectLastCall().times(2); control.replay(); cron.receiveJob(sanitizedConfiguration); cron.startJobNow(job.getKey()); delayLaunchCapture.getValue().run(); // Start the job again. Since the previous delayed start completed, this should repeat the // entire process. cron.startJobNow(job.getKey()); delayLaunchCapture.getValue().run(); }
@Test(expected = ScheduleException.class) public void testScheduleFails() throws Exception { expectJobValidated(job); storageUtil.jobStore.saveAcceptedJob(MANAGER_KEY, sanitizedConfiguration.getJobConfig()); expect(cronScheduler.schedule(eq(job.getCronSchedule()), EasyMock.<Runnable>anyObject())) .andThrow(new CronException("injected")); control.replay(); cron.receiveJob(sanitizedConfiguration); }
@Test public void testDeleteInconsistent() throws Exception { // Tests a case where a job exists in the storage, but is not registered with the cron system. expect(storageUtil.jobStore.fetchJob(MANAGER_KEY, job.getKey())) .andReturn(Optional.of(sanitizedConfiguration.getJobConfig())); control.replay(); assertTrue(cron.deleteJob(job.getKey())); }
@Test public void testStart() throws Exception { expectJobAccepted(); expectJobFetch(); expectActiveTaskFetch(); // Job is executed immediately since there are no existing tasks to kill. stateManager.insertPendingTasks(sanitizedConfiguration.getTaskConfigs()); expect(cronScheduler.getSchedule(DEFAULT_JOB_KEY)) .andReturn(Optional.of(job.getCronSchedule())) .times(2); control.replay(); assertEquals(ImmutableMap.<IJobKey, String>of(), cron.getScheduledJobs()); cron.receiveJob(sanitizedConfiguration); assertEquals(ImmutableMap.of(job.getKey(), job.getCronSchedule()), cron.getScheduledJobs()); cron.startJobNow(job.getKey()); assertEquals(ImmutableMap.of(job.getKey(), job.getCronSchedule()), cron.getScheduledJobs()); }
@Test public void testCancelNewCollision() throws Exception { IJobConfiguration killExisting = IJobConfiguration.build( job.newBuilder().setCronCollisionPolicy(CronCollisionPolicy.CANCEL_NEW)); Capture<Runnable> jobTriggerCapture = expectJobAccepted(killExisting); expectActiveTaskFetch(TASK); control.replay(); cron.receiveJob(new SanitizedConfiguration(killExisting)); jobTriggerCapture.getValue().run(); }
@Test public void testKillExistingCollisionFailedKill() throws Exception { IJobConfiguration killExisting = IJobConfiguration.build( job.newBuilder().setCronCollisionPolicy(CronCollisionPolicy.KILL_EXISTING)); Capture<Runnable> jobTriggerCapture = expectJobAccepted(killExisting); expectActiveTaskFetch(TASK); scheduler.killTasks(Query.jobScoped(killExisting.getKey()).active(), CRON_USER); expectLastCall().andThrow(new ScheduleException("injected")); control.replay(); cron.receiveJob(new SanitizedConfiguration(killExisting)); jobTriggerCapture.getValue().run(); }
@Test public void testConsistentState() throws Exception { IJobConfiguration updated = IJobConfiguration.build(makeJob().newBuilder().setCronSchedule("1 2 3 4 5")); expectJobAccepted(); cronScheduler.deschedule(DEFAULT_JOB_KEY); expectJobAccepted(updated); control.replay(); cron.receiveJob(sanitizedConfiguration); IJobConfiguration failedUpdate = IJobConfiguration.build(updated.newBuilder().setCronSchedule(null)); try { cron.updateJob(new SanitizedConfiguration(failedUpdate)); fail(); } catch (ScheduleException e) { // Expected. } cron.updateJob(new SanitizedConfiguration(updated)); }
@Test public void testDelayedStartMultiple() throws Exception { expectJobAccepted(); expectJobFetch(); // Query to test if live tasks exist for the job. expectActiveTaskFetch(TASK).times(3); // Live tasks exist, so the cron manager must delay the cron launch. delayExecutor.execute(capture(delayLaunchCapture)); // The cron manager will then try to initiate the kill. expectJobFetch(); expectJobFetch(); scheduler.killTasks((Query.Builder) anyObject(), eq(CronJobManager.CRON_USER)); expectLastCall().times(3); // Immediate queries and delayed query. expectActiveTaskFetch(TASK).times(4); // Simulate the live task disappearing. expectActiveTaskFetch(); stateManager.insertPendingTasks(sanitizedConfiguration.getTaskConfigs()); control.replay(); cron.receiveJob(sanitizedConfiguration); // Attempt to trick the cron manager into launching multiple times, or launching multiple // pollers. cron.startJobNow(job.getKey()); cron.startJobNow(job.getKey()); cron.startJobNow(job.getKey()); delayLaunchCapture.getValue().run(); }
@Before public void setUp() throws Exception { scheduler = createMock(SchedulerCore.class); stateManager = createMock(StateManager.class); delayExecutor = createMock(Executor.class); delayLaunchCapture = createCapture(); storageUtil = new StorageTestUtil(this); storageUtil.expectOperations(); cronScheduler = createMock(CronScheduler.class); shutdownRegistry = createMock(ShutdownRegistry.class); cron = new CronJobManager( stateManager, storageUtil.storage, cronScheduler, shutdownRegistry, delayExecutor); cron.schedulerCore = scheduler; job = makeJob(); sanitizedConfiguration = SanitizedConfiguration.fromUnsanitized(job); }
@Test public void testInvalidStoredJob() throws Exception { // Invalid jobs are left alone, but doesn't halt operation. expect(cronScheduler.startAsync()).andReturn(cronScheduler); cronScheduler.awaitRunning(); shutdownRegistry.addAction(EasyMock.<ExceptionalCommand<?>>anyObject()); IJobConfiguration jobA = IJobConfiguration.build(makeJob().newBuilder().setCronSchedule("1 2 3 4 5 6 7")); IJobConfiguration jobB = IJobConfiguration.build(makeJob().newBuilder().setCronSchedule(null)); expect(storageUtil.jobStore.fetchJobs(MANAGER_KEY)).andReturn(ImmutableList.of(jobA, jobB)); expect(cronScheduler.isValidSchedule(jobA.getCronSchedule())).andReturn(false); control.replay(); cron.schedulerActive(new SchedulerActive()); }
@Test public void testRunOverlapLoadedSuccessfully() throws Exception { // Existing RUN_OVERLAP jobs should still load and map. expect(cronScheduler.startAsync()).andReturn(cronScheduler); cronScheduler.awaitRunning(); shutdownRegistry.addAction(EasyMock.<ExceptionalCommand<?>>anyObject()); IJobConfiguration jobA = IJobConfiguration.build( makeJob().newBuilder().setCronCollisionPolicy(CronCollisionPolicy.RUN_OVERLAP)); expect(storageUtil.jobStore.fetchJobs(MANAGER_KEY)).andReturn(ImmutableList.of(jobA)); expect(cronScheduler.isValidSchedule(jobA.getCronSchedule())).andReturn(true); expect(cronScheduler.schedule(eq(jobA.getCronSchedule()), EasyMock.<Runnable>anyObject())) .andReturn("keyA"); control.replay(); cron.schedulerActive(new SchedulerActive()); }
@Test(expected = IllegalStateException.class) public void testJobStoredTwice() throws Exception { // Simulate an inconsistent storage that contains two cron jobs under the same key. expect(cronScheduler.startAsync()).andReturn(cronScheduler); cronScheduler.awaitRunning(); shutdownRegistry.addAction(EasyMock.<ExceptionalCommand<?>>anyObject()); IJobConfiguration jobA = IJobConfiguration.build(makeJob().newBuilder().setCronSchedule("1 2 3 4 5")); IJobConfiguration jobB = IJobConfiguration.build(makeJob().newBuilder().setCronSchedule("* * * * *")); expect(storageUtil.jobStore.fetchJobs(MANAGER_KEY)).andReturn(ImmutableList.of(jobA, jobB)); expectJobValidated(jobA); expect(cronScheduler.schedule(eq(jobA.getCronSchedule()), EasyMock.<Runnable>anyObject())) .andReturn("keyA"); expectJobValidated(jobB); control.replay(); cron.schedulerActive(new SchedulerActive()); }