### Question:
RobustJobSchedulerWrapper { public JobHandle scheduleRecurring( Group group, long periodMillis, ThrowingAction<Exception> action ) { return delegate.scheduleRecurring( group, () -> withErrorHandling( action ), periodMillis, MILLISECONDS ); } RobustJobSchedulerWrapper( JobScheduler delegate, Log log ); JobHandle schedule( Group group, long delayMillis, ThrowingAction<Exception> action ); JobHandle scheduleRecurring( Group group, long periodMillis, ThrowingAction<Exception> action );  }### Answer:
@Test public void recurringJobWithExceptionShouldKeepRunning() throws Exception { RobustJobSchedulerWrapper robustWrapper = new RobustJobSchedulerWrapper( actualScheduler, log ); AtomicInteger count = new AtomicInteger(); IllegalStateException e = new IllegalStateException(); int nRuns = 100; JobHandle jobHandle = robustWrapper.scheduleRecurring( Group.HZ_TOPOLOGY_REFRESH, 1, () -> { if ( count.get() < nRuns ) { count.incrementAndGet(); throw e; } } ); assertEventually( "run count", count::get, Matchers.equalTo( nRuns ), DEFAULT_TIMEOUT_MS, MILLISECONDS ); jobHandle.cancel( true ); verify( log, timeout( DEFAULT_TIMEOUT_MS ).times( nRuns ) ).warn( "Uncaught exception", e ); }
@Test public void recurringJobWithErrorShouldStop() throws Exception { RobustJobSchedulerWrapper robustWrapper = new RobustJobSchedulerWrapper( actualScheduler, log ); AtomicInteger count = new AtomicInteger(); Error e = new Error(); JobHandle jobHandle = robustWrapper.scheduleRecurring( Group.HZ_TOPOLOGY_REFRESH, 1, () -> { count.incrementAndGet(); throw e; } ); Thread.sleep( 50 ); assertEventually( "run count", count::get, Matchers.equalTo( 1 ), DEFAULT_TIMEOUT_MS, MILLISECONDS ); jobHandle.cancel( true ); verify( log, timeout( DEFAULT_TIMEOUT_MS ).times( 1 ) ).error( "Uncaught error rethrown", e ); }