@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
  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());
  }