@Test
  public void testPubsubWiring() throws Exception {
    expect(cronScheduler.startAsync()).andReturn(cronScheduler);
    cronScheduler.awaitRunning();
    shutdownRegistry.addAction(EasyMock.<ExceptionalCommand<?>>anyObject());
    expect(storageUtil.jobStore.fetchJobs(MANAGER_KEY))
        .andReturn(ImmutableList.<IJobConfiguration>of());

    control.replay();

    Injector injector =
        Guice.createInjector(
            new AbstractModule() {
              @Override
              protected void configure() {
                bind(StateManager.class).toInstance(stateManager);
                bind(Storage.class).toInstance(storageUtil.storage);
                bind(CronScheduler.class).toInstance(cronScheduler);
                bind(ShutdownRegistry.class).toInstance(shutdownRegistry);
                bind(SchedulerCore.class).toInstance(scheduler);
                PubsubTestUtil.installPubsub(binder());
                StateModule.bindCronJobManager(binder());
              }
            });
    cron = injector.getInstance(CronJobManager.class);
    EventSink eventSink = PubsubTestUtil.startPubsub(injector);
    eventSink.post(new SchedulerActive());
  }
  @Test(expected = ScheduleException.class)
  public void testInvalidCronSchedule() throws Exception {
    expect(cronScheduler.isValidSchedule(job.getCronSchedule())).andReturn(false);

    control.replay();

    cron.receiveJob(sanitizedConfiguration);
  }
 private Capture<Runnable> expectJobAccepted(IJobConfiguration savedJob) throws Exception {
   expectJobValidated(savedJob);
   storageUtil.jobStore.saveAcceptedJob(MANAGER_KEY, savedJob);
   Capture<Runnable> jobTriggerCapture = createCapture();
   expect(cronScheduler.schedule(eq(savedJob.getCronSchedule()), capture(jobTriggerCapture)))
       .andReturn(DEFAULT_JOB_KEY);
   return jobTriggerCapture;
 }
  @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(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 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());
  }
  @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
  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 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));
  }
 private void expectJobValidated(IJobConfiguration savedJob) {
   expect(cronScheduler.isValidSchedule(savedJob.getCronSchedule())).andReturn(true);
 }