public async Task OverrideDueTime_DuringRun_AppliesToNextRun() { // Arrange const double runLength = 1.0; const double t1 = 1.0; const double tA = 1.2; const double t2 = 1.4; const double tB = 1.7; const double tC = 2.3; const double t3 = 2.5; const double tD = 2.8; const double tE = 3.8; const double t4 = 4.0; const double tF = 4.3; const double tG = 5.3; using IJobManager jobManager = TestHelper.CreateJobManager(true); var start = "2000-01-01Z".ToUtcDateOffset(); var timeMachine = ShiftedTimeProvider.CreateTimeMachine(start); TimeProvider.Override(timeMachine); var job = jobManager.Create("my-job"); job.IsEnabled = true; job.Schedule = new SimpleSchedule(SimpleScheduleKind.Second, 1, start); job.Routine = async(parameter, tracker, output, token) => { var msg = (string)parameter; await output.WriteAsync(msg); await Task.Delay(TimeSpan.FromSeconds(runLength), token); }; // Act job.Parameter = "Scheduled1"; await timeMachine.WaitUntilSecondsElapse(start, tA); var infoA = job.GetInfo(null); await timeMachine.WaitUntilSecondsElapse(start, t2); job.OverrideDueTime(start.AddSeconds(t3)); job.Parameter = "Overridden"; await timeMachine.WaitUntilSecondsElapse(start, tB); var infoB = job.GetInfo(null); await timeMachine.WaitUntilSecondsElapse(start, tC); var infoC = job.GetInfo(null); await timeMachine.WaitUntilSecondsElapse(start, tD); job.Parameter = "Scheduled2"; var infoD = job.GetInfo(null); await timeMachine.WaitUntilSecondsElapse(start, tE); var infoE = job.GetInfo(null); await timeMachine.WaitUntilSecondsElapse(start, tF); var infoF = job.GetInfo(null); await timeMachine.WaitUntilSecondsElapse(start, tG); var infoG = job.GetInfo(null); job.Dispose(); // Assert var DEFECT = TimeSpan.FromMilliseconds(30); #region ^A Assert.That(infoA.CurrentRun, Is.Not.Null); Assert.That(infoA.NextDueTime, Is.EqualTo(start.AddSeconds(2.0))); Assert.That(infoA.NextDueTimeIsOverridden, Is.False); Assert.That(infoA.RunCount, Is.Zero); Assert.That(infoA.Runs, Is.Empty); var currentA = infoA.CurrentRun.Value; Assert.That(currentA.RunIndex, Is.EqualTo(0)); Assert.That(currentA.StartReason, Is.EqualTo(JobStartReason.ScheduleDueTime)); Assert.That(currentA.DueTime, Is.EqualTo(start.AddSeconds(1.0))); Assert.That(currentA.DueTimeWasOverridden, Is.False); Assert.That(currentA.StartTime, Is.EqualTo(start.AddSeconds(t1)).Within(DEFECT)); Assert.That(currentA.EndTime, Is.Null); Assert.That(currentA.Status, Is.EqualTo(JobRunStatus.Running)); Assert.That(currentA.Output, Is.EqualTo("Scheduled1")); Assert.That(currentA.Exception, Is.Null); #endregion #region ^B Assert.That(infoB.CurrentRun, Is.Not.Null); Assert.That(infoB.NextDueTime, Is.EqualTo(start.AddSeconds(t3))); Assert.That(infoB.NextDueTimeIsOverridden, Is.True); Assert.That(infoB.RunCount, Is.Zero); Assert.That(infoB.Runs, Is.Empty); var currentB = infoB.CurrentRun.Value; Assert.That(currentA, Is.EqualTo(currentB)); #endregion #region ^C Assert.That(infoC.CurrentRun, Is.Null); Assert.That(infoC.NextDueTime, Is.EqualTo(start.AddSeconds(t3))); Assert.That(infoC.NextDueTimeIsOverridden, Is.True); Assert.That(infoC.RunCount, Is.EqualTo(1)); Assert.That(infoC.Runs, Has.Count.EqualTo(1)); var runC0 = infoC.Runs.Single(); Assert.That(runC0.RunIndex, Is.EqualTo(0)); Assert.That(runC0.StartReason, Is.EqualTo(JobStartReason.ScheduleDueTime)); Assert.That(runC0.DueTime, Is.EqualTo(start.AddSeconds(1.0))); Assert.That(runC0.DueTimeWasOverridden, Is.False); Assert.That(runC0.StartTime, Is.EqualTo(currentA.StartTime)); Assert.That(runC0.EndTime, Is.EqualTo(runC0.StartTime.AddSeconds(runLength)).Within(DEFECT)); Assert.That(runC0.Status, Is.EqualTo(JobRunStatus.Completed)); Assert.That(runC0.Output, Is.EqualTo("Scheduled1")); Assert.That(runC0.Exception, Is.Null); #endregion #region ^D Assert.That(infoD.CurrentRun, Is.Not.Null); Assert.That(infoD.NextDueTime, Is.EqualTo(start.AddSeconds(3.0))); Assert.That(infoD.NextDueTimeIsOverridden, Is.False); Assert.That(infoD.RunCount, Is.EqualTo(1)); Assert.That(infoD.Runs, Has.Count.EqualTo(1)); var currentD = infoD.CurrentRun.Value; Assert.That(currentD.RunIndex, Is.EqualTo(1)); Assert.That(currentD.StartReason, Is.EqualTo(JobStartReason.OverriddenDueTime)); Assert.That(currentD.DueTime, Is.EqualTo(start.AddSeconds(t3))); Assert.That(currentD.DueTimeWasOverridden, Is.True); Assert.That(currentD.StartTime, Is.EqualTo(start.AddSeconds(t3)).Within(DEFECT)); Assert.That(currentD.EndTime, Is.Null); Assert.That(currentD.Status, Is.EqualTo(JobRunStatus.Running)); Assert.That(currentD.Output, Is.EqualTo("Overridden")); Assert.That(currentD.Exception, Is.Null); Assert.That(infoD.Runs.Single(), Is.EqualTo(runC0)); #endregion #region ^E Assert.That(infoE.CurrentRun, Is.Null); Assert.That(infoE.NextDueTime, Is.EqualTo(start.AddSeconds(t4))); Assert.That(infoE.NextDueTimeIsOverridden, Is.False); Assert.That(infoE.RunCount, Is.EqualTo(2)); Assert.That(infoE.Runs, Has.Count.EqualTo(2)); Assert.That(infoE.Runs[0], Is.EqualTo(infoD.Runs[0])); var runE1 = infoE.Runs[1]; Assert.That(runE1.RunIndex, Is.EqualTo(1)); Assert.That(runE1.StartReason, Is.EqualTo(JobStartReason.OverriddenDueTime)); Assert.That(runE1.DueTime, Is.EqualTo(start.AddSeconds(t3))); Assert.That(runE1.DueTimeWasOverridden, Is.True); Assert.That(runE1.StartTime, Is.EqualTo(currentD.StartTime)); Assert.That(runE1.EndTime, Is.EqualTo(runE1.StartTime.AddSeconds(runLength)).Within(DEFECT)); Assert.That(runE1.Status, Is.EqualTo(JobRunStatus.Completed)); Assert.That(runE1.Output, Is.EqualTo("Overridden")); Assert.That(runE1.Exception, Is.Null); #endregion #region ^F Assert.That(infoF.CurrentRun, Is.Not.Null); Assert.That(infoF.NextDueTime, Is.EqualTo(start.AddSeconds(5.0))); Assert.That(infoF.NextDueTimeIsOverridden, Is.False); Assert.That(infoF.RunCount, Is.EqualTo(2)); Assert.That(infoF.Runs, Has.Count.EqualTo(2)); var currentF = infoF.CurrentRun.Value; Assert.That(currentF.RunIndex, Is.EqualTo(2)); Assert.That(currentF.StartReason, Is.EqualTo(JobStartReason.ScheduleDueTime)); Assert.That(currentF.DueTime, Is.EqualTo(start.AddSeconds(t4))); Assert.That(currentF.DueTimeWasOverridden, Is.False); Assert.That(currentF.StartTime, Is.EqualTo(start.AddSeconds(t4)).Within(DEFECT)); Assert.That(currentF.EndTime, Is.Null); Assert.That(currentF.Status, Is.EqualTo(JobRunStatus.Running)); Assert.That(currentF.Output, Is.EqualTo("Scheduled2")); Assert.That(currentF.Exception, Is.Null); Assert.That(infoF.Runs[0], Is.EqualTo(infoE.Runs[0])); Assert.That(infoF.Runs[1], Is.EqualTo(infoE.Runs[1])); #endregion #region ^G Assert.That(infoG.CurrentRun, Is.Null); Assert.That(infoG.NextDueTime, Is.EqualTo(start.AddSeconds(6.0))); Assert.That(infoG.NextDueTimeIsOverridden, Is.False); Assert.That(infoG.RunCount, Is.EqualTo(3)); Assert.That(infoG.Runs, Has.Count.EqualTo(3)); Assert.That(infoG.Runs[0], Is.EqualTo(infoF.Runs[0])); Assert.That(infoG.Runs[1], Is.EqualTo(infoF.Runs[1])); var runG2 = infoG.Runs[2]; Assert.That(runG2.RunIndex, Is.EqualTo(2)); Assert.That(runG2.StartReason, Is.EqualTo(JobStartReason.ScheduleDueTime)); Assert.That(runG2.DueTime, Is.EqualTo(start.AddSeconds(t4))); Assert.That(runG2.DueTimeWasOverridden, Is.False); Assert.That(runG2.StartTime, Is.EqualTo(currentF.StartTime)); Assert.That(runG2.EndTime, Is.EqualTo(runG2.StartTime.AddSeconds(runLength)).Within(DEFECT)); Assert.That(runG2.Status, Is.EqualTo(JobRunStatus.Completed)); Assert.That(runG2.Output, Is.EqualTo("Scheduled2")); Assert.That(runG2.Exception, Is.Null); #endregion }
public async Task OverrideDueTime_Null_DefaultsToSchedule() { // Arrange const double runLength = 0.7; const double t1 = 1.5; const double t2 = 1.9; const double tA = 2.2; const double t3 = 2.5; const double tB = 3.3; using IJobManager jobManager = TestHelper.CreateJobManager(true); var start = "2000-01-01Z".ToUtcDateOffset(); var timeMachine = ShiftedTimeProvider.CreateTimeMachine(start); TimeProvider.Override(timeMachine); var job = jobManager.Create("my-job"); job.IsEnabled = true; job.Routine = async(parameter, tracker, output, token) => { var msg = (string)parameter; await output.WriteAsync(msg); await Task.Delay(TimeSpan.FromSeconds(runLength), token); }; // Act await timeMachine.WaitUntilSecondsElapse(start, t1); job.OverrideDueTime(start.AddSeconds(t3)); job.Parameter = "Hello!"; await timeMachine.WaitUntilSecondsElapse(start, t2); job.OverrideDueTime(null); await timeMachine.WaitUntilSecondsElapse(start, tA); var infoA = job.GetInfo(null); await timeMachine.WaitUntilSecondsElapse(start, tB); var infoB = job.GetInfo(null); // Assert #region ^A Assert.That(infoA.CurrentRun, Is.Null); Assert.That(infoA.NextDueTime, Is.EqualTo(TestHelper.NeverCopy)); Assert.That(infoA.NextDueTimeIsOverridden, Is.False); Assert.That(infoA.RunCount, Is.Zero); Assert.That(infoA.Runs, Is.Empty); #endregion #region ^B Assert.That(infoB.CurrentRun, Is.Null); Assert.That(infoB.NextDueTime, Is.EqualTo(TestHelper.NeverCopy)); Assert.That(infoB.NextDueTimeIsOverridden, Is.False); Assert.That(infoB.RunCount, Is.Zero); Assert.That(infoB.Runs, Is.Empty); #endregion }
public async Task OverrideDueTime_NotNull_StartsAndDiscards() { // Arrange const double runLength = 0.7; const double t1 = 1.7; const double tA = 2.1; const double t2 = 2.5; const double tB = 3.1; const double tC = 4.0; using IJobManager jobManager = TestHelper.CreateJobManager(true); var start = "2000-01-01Z".ToUtcDateOffset(); var timeMachine = ShiftedTimeProvider.CreateTimeMachine(start); TimeProvider.Override(timeMachine); var job = jobManager.Create("my-job"); job.IsEnabled = true; job.Routine = async(parameter, tracker, output, token) => { var msg = (string)parameter; await output.WriteAsync(msg); await Task.Delay(TimeSpan.FromSeconds(runLength), token); }; // Act await timeMachine.WaitUntilSecondsElapse(start, t1); job.OverrideDueTime(start.AddSeconds(t2)); await timeMachine.WaitUntilSecondsElapse(start, tA); var infoA = job.GetInfo(null); job.Parameter = "Hello!"; await timeMachine.WaitUntilSecondsElapse(start, tB); var infoB = job.GetInfo(null); await timeMachine.WaitUntilSecondsElapse(start, tC); var infoC = job.GetInfo(null); // Assert var DEFECT = TimeSpan.FromMilliseconds(30); #region ^A Assert.That(infoA.CurrentRun, Is.Null); Assert.That(infoA.NextDueTime, Is.EqualTo(start.AddSeconds(t2))); Assert.That(infoA.NextDueTimeIsOverridden, Is.True); Assert.That(infoA.RunCount, Is.Zero); Assert.That(infoA.Runs, Is.Empty); #endregion #region ^B Assert.That(infoB.CurrentRun, Is.Not.Null); Assert.That(infoB.NextDueTime, Is.EqualTo(TestHelper.NeverCopy)); Assert.That(infoB.NextDueTimeIsOverridden, Is.False); Assert.That(infoB.RunCount, Is.Zero); Assert.That(infoB.Runs, Is.Empty); var currentB = infoB.CurrentRun.Value; Assert.That(currentB.RunIndex, Is.EqualTo(0)); Assert.That(currentB.StartReason, Is.EqualTo(JobStartReason.OverriddenDueTime)); Assert.That(currentB.DueTime, Is.EqualTo(start.AddSeconds(t2))); Assert.That(currentB.DueTimeWasOverridden, Is.True); Assert.That(currentB.StartTime, Is.EqualTo(start.AddSeconds(t2)).Within(DEFECT)); Assert.That(currentB.EndTime, Is.Null); Assert.That(currentB.Status, Is.EqualTo(JobRunStatus.Running)); Assert.That(currentB.Output, Is.EqualTo("Hello!")); Assert.That(currentB.Exception, Is.Null); #endregion #region ^C Assert.That(infoC.CurrentRun, Is.Null); Assert.That(infoC.NextDueTime, Is.EqualTo(TestHelper.NeverCopy)); Assert.That(infoC.NextDueTimeIsOverridden, Is.False); Assert.That(infoC.RunCount, Is.EqualTo(1)); Assert.That(infoC.Runs, Has.Count.EqualTo(1)); var infoCRun0 = infoC.Runs[0]; Assert.That(infoCRun0.RunIndex, Is.EqualTo(0)); Assert.That(infoCRun0.StartReason, Is.EqualTo(JobStartReason.OverriddenDueTime)); Assert.That(infoCRun0.DueTime, Is.EqualTo(start.AddSeconds(t2))); Assert.That(infoCRun0.DueTimeWasOverridden, Is.True); Assert.That(infoCRun0.StartTime, Is.EqualTo(currentB.StartTime)); Assert.That(infoCRun0.EndTime, Is.EqualTo(infoCRun0.StartTime.AddSeconds(runLength)).Within(DEFECT * 2)); Assert.That(infoCRun0.Status, Is.EqualTo(JobRunStatus.Completed)); Assert.That(infoCRun0.Output, Is.EqualTo("Hello!")); Assert.That(infoCRun0.Exception, Is.Null); #endregion }
public async Task Schedule_SetDuringRun_DoesNotAffectRunButAppliesToDueTime() { // Arrange using IJobManager jobManager = TestHelper.CreateJobManager(true); var start = "2000-01-01Z".ToUtcDateOffset(); var timeMachine = ShiftedTimeProvider.CreateTimeMachine(start); TimeProvider.Override(timeMachine); var job = jobManager.Create("my-job"); var schedule1 = new SimpleSchedule(SimpleScheduleKind.Second, 1, start); job.Schedule = schedule1; // job will be started by due time of this schedule DateTimeOffset dueTime1 = default; DateTimeOffset dueTime2 = default; DateTimeOffset dueTime3 = default; DateTimeOffset dueTime4 = default; job.Routine = async(parameter, tracker, output, token) => { Log.Debug("Entered routine."); // start + 1.2s: due time is (start + 2s), set by schedule1 await timeMachine.WaitUntilSecondsElapse(start, 1.2, token); dueTime1 = job.GetInfo(0).NextDueTime; // should be 2s await timeMachine.WaitUntilSecondsElapse(start, 1.7, token); dueTime2 = job.GetInfo(0).NextDueTime; await timeMachine.WaitUntilSecondsElapse(start, 2.2, token); dueTime3 = job.GetInfo(0).NextDueTime; await timeMachine.WaitUntilSecondsElapse(start, 3.2, token); dueTime4 = job.GetInfo(0).NextDueTime; await Task.Delay(TimeSpan.FromHours(2), token); Log.Debug("Exited routine."); }; job.IsEnabled = true; // Act var schedule2 = new SimpleSchedule(SimpleScheduleKind.Second, 1, start.AddSeconds(1.8)); await timeMachine.WaitUntilSecondsElapse(start, 1.4); job.Schedule = schedule2; await timeMachine.WaitUntilSecondsElapse(start, 4); // Assert try { Assert.That(dueTime1, Is.EqualTo(start.AddSeconds(2))); Assert.That(dueTime2, Is.EqualTo(start.AddSeconds(1.8))); Assert.That(dueTime3, Is.EqualTo(start.AddSeconds(2.8))); Assert.That(dueTime4, Is.EqualTo(start.AddSeconds(3.8))); } catch (Exception ex) { var sb = new StringBuilder(); sb.AppendLine("*** Test Failed ***"); sb.AppendLine(ex.ToString()); sb.AppendLine("*** Log: ***"); var log = _logWriter.ToString(); sb.AppendLine(log); Assert.Fail(sb.ToString()); } }
public async Task Dispose_JobsCreated_DisposesAndJobsAreCanceledAndDisposed() { // Arrange using IJobManager jobManager = TestHelper.CreateJobManager(true); var start = "2020-01-01Z".ToUtcDateOffset(); TimeProvider.Override(ShiftedTimeProvider.CreateTimeMachine(start)); var job1 = jobManager.Create("job1"); job1.IsEnabled = true; var job2 = jobManager.Create("job2"); job2.IsEnabled = true; job1.Output = new StringWriterWithEncoding(Encoding.UTF8); job2.Output = new StringWriterWithEncoding(Encoding.UTF8); async Task Routine(object parameter, IProgressTracker tracker, TextWriter output, CancellationToken token) { for (var i = 0; i < 100; i++) { var time = TimeProvider.GetCurrentTime(); await output.WriteLineAsync($"Iteration {i}: {time.Second:D2}:{time.Millisecond:D3}"); try { await Task.Delay(1000, token); } catch (TaskCanceledException) { time = TimeProvider.GetCurrentTime(); await output.WriteLineAsync($"Canceled! {time.Second:D2}:{time.Millisecond:D3}"); throw; } } } ISchedule schedule = new SimpleSchedule( SimpleScheduleKind.Second, 1, start.AddMilliseconds(400)); job1.Schedule = schedule; job2.Schedule = schedule; job1.Routine = Routine; job2.Routine = Routine; job1.IsEnabled = true; job2.IsEnabled = true; await Task.Delay(2500); // 3 iterations should be completed: ~400, ~1400, ~2400 todo: ut this // Act var jobInfoBeforeDispose1 = job1.GetInfo(null); var jobInfoBeforeDispose2 = job2.GetInfo(null); jobManager.Dispose(); await Task.Delay(50); // let background TPL work get done. // Assert Assert.That(jobManager.IsDisposed, Is.True); foreach (var job in new[] { job1, job2 }) { Assert.That(job.IsDisposed, Is.True); var info = job.GetInfo(null); var run = info.Runs.Single(); Assert.That(run.Status, Is.EqualTo(JobRunStatus.Canceled)); } }
public async Task Schedule_SetAndStartedAndCompleted_ReflectedInOldRuns() { // Arrange var DEFECT = TimeSpan.FromMilliseconds(30); using IJobManager jobManager = TestHelper.CreateJobManager(true); var start = "2000-01-01Z".ToUtcDateOffset(); var timeMachine = ShiftedTimeProvider.CreateTimeMachine(start); TimeProvider.Override(timeMachine); var job = jobManager.Create("my-job"); job.Routine = async(parameter, tracker, output, token) => { await Task.Delay(1500, token); // 1.5 second to complete }; ISchedule schedule = new SimpleSchedule(SimpleScheduleKind.Second, 1, start); job.IsEnabled = true; // Act job.Schedule = schedule; // will fire at 00:01 await timeMachine.WaitUntilSecondsElapse( start, 1.0 + DEFECT.TotalSeconds + 1.5 + DEFECT.TotalSeconds); // Assert try { var info = job.GetInfo(null); Assert.That(info.CurrentRun, Is.Null); Assert.That(info.NextDueTime, Is.EqualTo(start.AddSeconds(3))); var pastRun = info.Runs.Single(); Assert.That(pastRun.RunIndex, Is.EqualTo(0)); Assert.That(pastRun.StartReason, Is.EqualTo(JobStartReason.ScheduleDueTime)); Assert.That(pastRun.DueTime, Is.EqualTo(start.AddSeconds(1))); Assert.That(pastRun.DueTimeWasOverridden, Is.False); Assert.That(pastRun.StartTime, Is.EqualTo(start.AddSeconds(1)).Within(DEFECT)); Assert.That( pastRun.EndTime, Is.EqualTo(pastRun.StartTime.AddSeconds(1.5)).Within(DEFECT)); Assert.That(pastRun.Status, Is.EqualTo(JobRunStatus.Completed)); } catch (Exception ex) { // todo: need this block, here & in other places? // it is known now that the reason was slowpok TPL var sb = new StringBuilder(); sb.AppendLine("*** Test Failed ***"); sb.AppendLine(ex.ToString()); sb.AppendLine("*** Log: ***"); var log = _logWriter.ToString(); sb.AppendLine(log); Assert.Fail(sb.ToString()); } }
public async Task Schedule_SetAndStartedAndFaulted_ReflectedInOldRuns() { // Arrange var DEFECT = TimeSpan.FromMilliseconds(30); using IJobManager jobManager = TestHelper.CreateJobManager(true); var start = "2000-01-01Z".ToUtcDateOffset(); var timeMachine = ShiftedTimeProvider.CreateTimeMachine(start); TimeProvider.Override(timeMachine); var job = jobManager.Create("my-job"); job.Routine = async(parameter, tracker, output, token) => { await Task.Delay(1500, token); // 1.5 second runs with no problem... throw new ApplicationException("BAD_NEWS"); // ...and then throws! }; ISchedule schedule = new SimpleSchedule(SimpleScheduleKind.Second, 1, start); job.IsEnabled = true; // Act job.Schedule = schedule; // will fire at 00:01 await timeMachine.WaitUntilSecondsElapse(start, 2.8); // by this time, job will end due to the exception "BAD_NEWS" and finalize. // Assert var info = job.GetInfo(null); Assert.That(info.CurrentRun, Is.Null); Assert.That(info.NextDueTime, Is.EqualTo(start.AddSeconds(3))); // todo //Expected: 2000 - 01 - 01 00:00:03 + 00:00 //But was: 2000 - 01 - 01 00:00:01 + 00:00 Assert.That(info.NextDueTimeIsOverridden, Is.False); var pastRun = info.Runs.Single(); Assert.That(pastRun.RunIndex, Is.EqualTo(0)); Assert.That(pastRun.StartReason, Is.EqualTo(JobStartReason.ScheduleDueTime)); Assert.That(pastRun.DueTime, Is.EqualTo(start.AddSeconds(1))); Assert.That(pastRun.DueTimeWasOverridden, Is.False); Assert.That(pastRun.StartTime, Is.EqualTo(start.AddSeconds(1)).Within(DEFECT)); Assert.That( pastRun.EndTime, Is.EqualTo(pastRun.StartTime.AddSeconds(1.5)).Within(DEFECT * 2)); Assert.That(pastRun.Status, Is.EqualTo(JobRunStatus.Faulted)); Assert.That(pastRun.Exception, Is.TypeOf <ApplicationException>()); Assert.That(pastRun.Exception, Has.Message.EqualTo("BAD_NEWS")); }
public async Task Parameter_SetOnTheFly_RunsWithOldParameterAndNextTimeRunsWithNewParameter() { // Arrange using IJobManager jobManager = TestHelper.CreateJobManager(true); var start = "2000-01-01Z".ToUtcDateOffset(); var timeMachine = ShiftedTimeProvider.CreateTimeMachine(start); TimeProvider.Override(timeMachine); var job = jobManager.Create("my-job"); ISchedule schedule = new ConcreteSchedule( start.AddSeconds(1), start.AddSeconds(3)); job.Schedule = schedule; job.Output = new StringWriterWithEncoding(Encoding.UTF8); object parameter1 = "Olia"; object parameter2 = "Ira"; job.Routine = async(parameter, tracker, writer, token) => { await Task.Delay(1500, token); await writer.WriteAsync($"Hello, {parameter}!"); }; job.IsEnabled = true; // Act await timeMachine.WaitUntilSecondsElapse(start, 0.8); job.Parameter = parameter1; await timeMachine.WaitUntilSecondsElapse(start, 1.3); job.Parameter = parameter2; await timeMachine.WaitUntilSecondsElapse(start, 2.8); var output0 = job.Output.ToString(); await timeMachine.WaitUntilSecondsElapse(start, 4.8); var output1 = job.Output.ToString(); var info = job.GetInfo(null); // Assert Assert.That(info.CurrentRun, Is.Null); Assert.That(info.RunCount, Is.EqualTo(2)); Assert.That(info.Runs, Has.Count.EqualTo(2)); var run0 = info.Runs[0]; var run1 = info.Runs[1]; Assert.That(run0.Output, Is.EqualTo("Hello, Olia!")); Assert.That(output0, Is.EqualTo("Hello, Olia!")); Assert.That(run1.Output, Is.EqualTo("Hello, Ira!")); Assert.That(output1, Is.EqualTo("Hello, Olia!Hello, Ira!")); }
public async Task GetInfo_RunsSeveralTimes_ReturnsAllRuns() { // Arrange const double runTime = 0.7; const double t1 = 1.6; // !1 - force start const double t2 = 2.8; // !2 - due time is overridden const double t3 = 3.4; // !3 - overridden due time const double t4 = 5.0; // !4 - starts by schedule const double tA = 1.1; // ^A - before all runs const double tB = 1.9; // ^B - right after force start const double tC = 2.6; // ^C - after forced run completes, but before time override const double tD = 3.1; // ^D - after time override, but before overridden-due-time start const double tE = 3.9; // ^E - after overridden-due-time start const double tF = 4.8; // ^F - after overridden-due-time run completes, and before schedule-due-time start const double tG = 5.3; // ^G - after schedule-due-time start const double tH = 6.6; // ^H - after schedule-due-time run completes using IJobManager jobManager = TestHelper.CreateJobManager(true); var start = "2000-01-01Z".ToUtcDateOffset(); var timeMachine = ShiftedTimeProvider.CreateTimeMachine(start); TimeProvider.Override(timeMachine); var job = jobManager.Create("my-job"); job.IsEnabled = true; job.Routine = async(parameter, tracker, output, token) => { var msg = (string)parameter; await output.WriteAsync(msg); await Task.Delay(TimeSpan.FromSeconds(runTime), token); }; ISchedule schedule = new ConcreteSchedule( start.AddSeconds(2), start.AddSeconds(3), start.AddSeconds(4), start.AddSeconds(5)); job.Schedule = schedule; // Act // ^A - before all runs await timeMachine.WaitUntilSecondsElapse(start, tA); var infoA = job.GetInfo(null); // !1 - force start await timeMachine.WaitUntilSecondsElapse(start, t1); job.Parameter = "force"; job.ForceStart(); // ^B - right after force start await timeMachine.WaitUntilSecondsElapse(start, tB); var infoB = job.GetInfo(null); // ^C - after forced run completes, but before time override await timeMachine.WaitUntilSecondsElapse(start, tC); var infoC = job.GetInfo(null); // !2 - due time is overridden await timeMachine.WaitUntilSecondsElapse(start, t2); job.Parameter = "overridden"; job.OverrideDueTime(start.AddSeconds(t3)); // ^D - after time override, but before overridden-due-time start await timeMachine.WaitUntilSecondsElapse(start, tD); var infoD = job.GetInfo(null); // !3 - overridden due time await timeMachine.WaitUntilSecondsElapse(start, t3); await _logWriter.WriteLineAsync($"=== t3 came! {t3} ==="); // ^E - after overridden-due-time start await timeMachine.WaitUntilSecondsElapse(start, tE); var infoE = job.GetInfo(null); // ^F - after overridden-due-time run completes, and before schedule-due-time start await timeMachine.WaitUntilSecondsElapse(start, tF); var infoF = job.GetInfo(null); job.Parameter = "schedule"; // !4 - starts by schedule await timeMachine.WaitUntilSecondsElapse(start, t4); await _logWriter.WriteLineAsync($"=== t4 came! {t4} ==="); // ^G - after schedule-due-time start await timeMachine.WaitUntilSecondsElapse(start, tG); var infoG = job.GetInfo(null); // ^H - after schedule-due-time run completes await timeMachine.WaitUntilSecondsElapse(start, tH); var infoH = job.GetInfo(null); // dispose jobManager.Dispose(); var infoFinal = job.GetInfo(null); // Assert var DEFECT = TimeSpan.FromMilliseconds(30); #region ^A - before all runs Assert.That(infoA.CurrentRun, Is.Null); Assert.That(infoA.NextDueTime, Is.EqualTo(start.AddSeconds(2))); Assert.That(infoA.NextDueTimeIsOverridden, Is.False); Assert.That(infoA.RunCount, Is.Zero); Assert.That(infoA.Runs, Is.Empty); #endregion #region ^B - right after force start Assert.That(infoB.CurrentRun, Is.Not.Null); Assert.That(infoB.NextDueTime, Is.EqualTo(start.AddSeconds(2))); Assert.That(infoB.NextDueTimeIsOverridden, Is.False); Assert.That(infoB.RunCount, Is.Zero); Assert.That(infoB.Runs, Is.Empty); var currentB = infoB.CurrentRun.Value; Assert.That(currentB.RunIndex, Is.EqualTo(0)); Assert.That(currentB.StartReason, Is.EqualTo(JobStartReason.Force)); Assert.That(currentB.DueTime, Is.EqualTo(start.AddSeconds(2))); Assert.That(currentB.DueTimeWasOverridden, Is.False); Assert.That(currentB.StartTime, Is.EqualTo(start.AddSeconds(t1)).Within(DEFECT)); Assert.That(currentB.EndTime, Is.Null); Assert.That(currentB.Status, Is.EqualTo(JobRunStatus.Running)); Assert.That(currentB.Output, Is.EqualTo("force")); Assert.That(currentB.Exception, Is.Null); #endregion #region ^C - after forced run completes, but before time override Assert.That(infoC.CurrentRun, Is.Null); Assert.That(infoC.NextDueTime, Is.EqualTo(start.AddSeconds(3))); Assert.That(infoC.NextDueTimeIsOverridden, Is.False); Assert.That(infoC.RunCount, Is.EqualTo(1)); Assert.That(infoC.Runs, Has.Count.EqualTo(1)); var infoCRun0 = infoC.Runs[0]; Assert.That(infoCRun0.RunIndex, Is.EqualTo(0)); Assert.That(infoCRun0.StartReason, Is.EqualTo(JobStartReason.Force)); Assert.That(infoCRun0.DueTime, Is.EqualTo(start.AddSeconds(2))); Assert.That(infoCRun0.DueTimeWasOverridden, Is.False); Assert.That(infoCRun0.StartTime, Is.EqualTo(currentB.StartTime)); Assert.That(infoCRun0.EndTime, Is.EqualTo(infoCRun0.StartTime.AddSeconds(runTime)).Within(DEFECT * 2)); Assert.That(infoCRun0.Status, Is.EqualTo(JobRunStatus.Completed)); Assert.That(infoCRun0.Output, Is.EqualTo("force")); Assert.That(infoCRun0.Exception, Is.Null); #endregion #region ^D - after time override, but before overridden-due-time start Assert.That(infoD.CurrentRun, Is.Null); Assert.That(infoD.NextDueTime, Is.EqualTo(start.AddSeconds(t3))); Assert.That(infoD.NextDueTimeIsOverridden, Is.True); Assert.That(infoD.RunCount, Is.EqualTo(1)); Assert.That(infoD.Runs, Has.Count.EqualTo(1)); Assert.That(infoD.Runs[0], Is.EqualTo(infoC.Runs[0])); #endregion #region ^E - after overridden-due-time start Assert.That(infoE.CurrentRun, Is.Not.Null); Assert.That(infoE.NextDueTime, Is.EqualTo(start.AddSeconds(4))); Assert.That(infoE.NextDueTimeIsOverridden, Is.False); Assert.That(infoE.RunCount, Is.EqualTo(1)); Assert.That(infoE.Runs, Has.Count.EqualTo(1)); var currentE = infoE.CurrentRun.Value; Assert.That(currentE.RunIndex, Is.EqualTo(1)); Assert.That(currentE.StartReason, Is.EqualTo(JobStartReason.OverriddenDueTime)); Assert.That(currentE.DueTime, Is.EqualTo(start.AddSeconds(t3))); Assert.That(currentE.DueTimeWasOverridden, Is.True); Assert.That(currentE.StartTime, Is.EqualTo(start.AddSeconds(t3)).Within(DEFECT)); Assert.That(currentE.EndTime, Is.Null); Assert.That(currentE.Status, Is.EqualTo(JobRunStatus.Running)); Assert.That(currentE.Output, Is.EqualTo("overridden")); Assert.That(currentE.Exception, Is.Null); #endregion #region ^F - after overridden-due-time run completes, and before schedule-due-time start Assert.That(infoF.CurrentRun, Is.Null); Assert.That(infoF.NextDueTime, Is.EqualTo(start.AddSeconds(5))); Assert.That(infoF.NextDueTimeIsOverridden, Is.False); Assert.That(infoF.RunCount, Is.EqualTo(2)); Assert.That(infoF.Runs, Has.Count.EqualTo(2)); var infoFRun0 = infoF.Runs[0]; var infoFRun1 = infoF.Runs[1]; Assert.That(infoFRun0, Is.EqualTo(infoD.Runs[0])); Assert.That(infoFRun1.RunIndex, Is.EqualTo(1)); Assert.That(infoFRun1.StartReason, Is.EqualTo(JobStartReason.OverriddenDueTime)); Assert.That(infoFRun1.DueTime, Is.EqualTo(start.AddSeconds(t3))); Assert.That(infoFRun1.DueTimeWasOverridden, Is.True); Assert.That(infoFRun1.StartTime, Is.EqualTo(currentE.StartTime)); Assert.That(infoFRun1.EndTime, Is.EqualTo(infoFRun1.StartTime.AddSeconds(runTime)).Within(DEFECT * 2)); Assert.That(infoFRun1.Status, Is.EqualTo(JobRunStatus.Completed)); Assert.That(infoFRun1.Output, Is.EqualTo("overridden")); Assert.That(infoFRun1.Exception, Is.Null); #endregion #region ^G - after schedule-due-time start Assert.That(infoG.CurrentRun, Is.Not.Null); Assert.That(infoG.NextDueTime, Is.EqualTo(TestHelper.NeverCopy)); Assert.That(infoG.NextDueTimeIsOverridden, Is.False); Assert.That(infoG.RunCount, Is.EqualTo(2)); Assert.That(infoG.Runs, Has.Count.EqualTo(2)); var currentG = infoG.CurrentRun.Value; Assert.That(currentG.RunIndex, Is.EqualTo(2)); Assert.That(currentG.StartReason, Is.EqualTo(JobStartReason.ScheduleDueTime)); Assert.That(currentG.DueTime, Is.EqualTo(start.AddSeconds(5))); Assert.That(currentG.DueTimeWasOverridden, Is.False); Assert.That(currentG.StartTime, Is.EqualTo(start.AddSeconds(5)).Within(DEFECT)); Assert.That(currentG.EndTime, Is.Null); Assert.That(currentG.Status, Is.EqualTo(JobRunStatus.Running)); Assert.That(currentG.Output, Is.EqualTo("schedule")); Assert.That(currentG.Exception, Is.Null); CollectionAssert.AreEqual(infoF.Runs, infoG.Runs); #endregion #region ^H - after schedule-due-time run completes Assert.That(infoH.CurrentRun, Is.Null); Assert.That(infoH.NextDueTime, Is.EqualTo(TestHelper.NeverCopy)); Assert.That(infoH.NextDueTimeIsOverridden, Is.False); Assert.That(infoH.RunCount, Is.EqualTo(3)); Assert.That(infoH.Runs, Has.Count.EqualTo(3)); CollectionAssert.AreEqual(infoG.Runs.Take(2), infoH.Runs.Take(2)); var infoHRun2 = infoH.Runs[2]; Assert.That(infoHRun2.RunIndex, Is.EqualTo(2)); Assert.That(infoHRun2.StartReason, Is.EqualTo(JobStartReason.ScheduleDueTime)); Assert.That(infoHRun2.DueTime, Is.EqualTo(start.AddSeconds(5))); Assert.That(infoHRun2.DueTimeWasOverridden, Is.False); Assert.That(infoHRun2.StartTime, Is.EqualTo(currentG.StartTime)); Assert.That(infoHRun2.EndTime, Is.EqualTo(infoHRun2.StartTime.AddSeconds(runTime)).Within(DEFECT * 2)); Assert.That(infoHRun2.Status, Is.EqualTo(JobRunStatus.Completed)); Assert.That(infoHRun2.Output, Is.EqualTo("schedule")); Assert.That(infoHRun2.Exception, Is.Null); #endregion #region after disposal Assert.That(infoFinal.CurrentRun, Is.EqualTo(infoH.CurrentRun)); Assert.That(infoFinal.NextDueTime, Is.EqualTo(infoH.NextDueTime)); Assert.That(infoFinal.NextDueTimeIsOverridden, Is.EqualTo(infoH.NextDueTimeIsOverridden)); Assert.That(infoFinal.RunCount, Is.EqualTo(infoH.RunCount)); Assert.That(infoFinal.Runs.Count, Is.EqualTo(infoH.Runs.Count)); CollectionAssert.AreEqual(infoH.Runs, infoFinal.Runs); #endregion }
public async Task IsEnabled_SetToFalseDuringRun_RunCompletesThenDoesNotStart() { // Arrange const double runLength = 1.0; const double t1 = 1.0; const double t2 = 1.3; const double tA = 1.7; const double tB = 4.5; using IJobManager jobManager = TestHelper.CreateJobManager(true); var start = "2000-01-01Z".ToUtcDateOffset(); var timeMachine = ShiftedTimeProvider.CreateTimeMachine(start); TimeProvider.Override(timeMachine); var job = jobManager.Create("my-job"); job.IsEnabled = true; job.Schedule = new SimpleSchedule(SimpleScheduleKind.Second, 1, start); job.Routine = async(parameter, tracker, output, token) => { await output.WriteAsync("Hello!"); await Task.Delay(TimeSpan.FromSeconds(runLength), token); }; // Act await timeMachine.WaitUntilSecondsElapse(start, t2); job.IsEnabled = false; await timeMachine.WaitUntilSecondsElapse(start, tA); var infoA = job.GetInfo(null); await timeMachine.WaitUntilSecondsElapse(start, tB); var infoB = job.GetInfo(null); job.Dispose(); // Assert var DEFECT = TimeSpan.FromMilliseconds(30); #region ^A Assert.That(infoA.CurrentRun, Is.Not.Null); Assert.That(infoA.NextDueTime, Is.EqualTo(start.AddSeconds(2.0))); Assert.That(infoA.NextDueTimeIsOverridden, Is.False); Assert.That(infoA.RunCount, Is.Zero); Assert.That(infoA.Runs, Is.Empty); var currentA = infoA.CurrentRun.Value; Assert.That(currentA.RunIndex, Is.EqualTo(0)); Assert.That(currentA.StartReason, Is.EqualTo(JobStartReason.ScheduleDueTime)); Assert.That(currentA.DueTime, Is.EqualTo(start.AddSeconds(t1))); Assert.That(currentA.DueTimeWasOverridden, Is.False); Assert.That(currentA.StartTime, Is.EqualTo(start.AddSeconds(t1)).Within(DEFECT)); Assert.That(currentA.EndTime, Is.Null); Assert.That(currentA.Status, Is.EqualTo(JobRunStatus.Running)); #endregion #region ^B Assert.That(infoB.CurrentRun, Is.Null); Assert.That(infoB.NextDueTime, Is.EqualTo(start.AddSeconds(5.0))); Assert.That(infoB.NextDueTimeIsOverridden, Is.False); Assert.That(infoB.RunCount, Is.EqualTo(1)); Assert.That(infoB.Runs, Has.Count.EqualTo(1)); var runB0 = infoB.Runs.Single(); Assert.That(runB0.RunIndex, Is.EqualTo(0)); Assert.That(runB0.StartReason, Is.EqualTo(JobStartReason.ScheduleDueTime)); Assert.That(runB0.DueTime, Is.EqualTo(start.AddSeconds(t1))); Assert.That(runB0.DueTimeWasOverridden, Is.False); Assert.That(runB0.StartTime, Is.EqualTo(currentA.StartTime)); Assert.That(runB0.EndTime, Is.EqualTo(runB0.StartTime.AddSeconds(runLength)).Within(DEFECT)); Assert.That(runB0.Status, Is.EqualTo(JobRunStatus.Completed)); #endregion }
public async Task IsEnabled_SetToFalseBeforeOverriddenDueTime_DoesNotStartThenOverriddenDueTimeGetsDiscarded() { // Arrange const double runLength = 1.0; const double t1 = 0.8; const double t2 = 1.1; const double t3 = 1.5; const double tA = 1.9; const double tB = 4.5; using IJobManager jobManager = TestHelper.CreateJobManager(true); var start = "2000-01-01Z".ToUtcDateOffset(); var timeMachine = ShiftedTimeProvider.CreateTimeMachine(start); TimeProvider.Override(timeMachine); var job = jobManager.Create("my-job"); job.IsEnabled = true; job.Schedule = new SimpleSchedule(SimpleScheduleKind.Second, 1, start); job.Routine = async(parameter, tracker, output, token) => { await output.WriteAsync("Hello!"); await Task.Delay(TimeSpan.FromSeconds(runLength), token); }; // Act await timeMachine.WaitUntilSecondsElapse(start, t1); job.OverrideDueTime(start.AddSeconds(t3)); await timeMachine.WaitUntilSecondsElapse(start, t2); job.IsEnabled = false; await timeMachine.WaitUntilSecondsElapse(start, tA); var infoA = job.GetInfo(null); await timeMachine.WaitUntilSecondsElapse(start, tB); var infoB = job.GetInfo(null); job.Dispose(); // Assert #region ^A Assert.That(infoA.CurrentRun, Is.Null); Assert.That(infoA.NextDueTime, Is.EqualTo(start.AddSeconds(2.0))); Assert.That(infoA.NextDueTimeIsOverridden, Is.False); Assert.That(infoA.RunCount, Is.Zero); Assert.That(infoA.Runs, Is.Empty); #endregion #region ^B Assert.That(infoB.CurrentRun, Is.Null); Assert.That(infoB.NextDueTime, Is.EqualTo(start.AddSeconds(5.0))); Assert.That(infoB.NextDueTimeIsOverridden, Is.False); Assert.That(infoB.RunCount, Is.Zero); Assert.That(infoB.Runs, Is.Empty); #endregion }
public async Task Output_SetOnTheFly_RunsWithOldParameterAndNextTimeRunsWithNewOutput() { // Arrange using IJobManager jobManager = TestHelper.CreateJobManager(true); var start = "2000-01-01Z".ToUtcDateOffset(); var timeMachine = ShiftedTimeProvider.CreateTimeMachine(start); TimeProvider.Override(timeMachine); var job = jobManager.Create("my-job"); ISchedule schedule = new ConcreteSchedule( start.AddSeconds(1), start.AddSeconds(3)); job.Schedule = schedule; var writer1 = new StringWriterWithEncoding(Encoding.UTF8); var writer2 = new StringWriterWithEncoding(Encoding.UTF8); job.Routine = async(parameter, tracker, writer, token) => { for (var i = 0; i < 5; i++) { await writer.WriteAsync(i.ToString()); } await Task.Delay(200, token); }; job.IsEnabled = true; // Act var inTime = await timeMachine.WaitUntilSecondsElapse(start, 0.8); if (!inTime) { throw new Exception("Test failed. TPL was too slow."); } job.Output = writer1; await timeMachine.WaitUntilSecondsElapse(start, 1.3); job.Output = writer2; await timeMachine.WaitUntilSecondsElapse(start, 4.8); var info = job.GetInfo(null); // Assert try { Assert.That(info.CurrentRun, Is.Null); Assert.That(info.RunCount, Is.EqualTo(2)); Assert.That(info.Runs, Has.Count.EqualTo(2)); Assert.That(writer1.ToString(), Is.EqualTo("01234")); Assert.That(writer2.ToString(), Is.EqualTo("01234")); } catch (Exception ex) { var sb = new StringBuilder(); sb.AppendLine("*** Test Failed ***"); sb.AppendLine(ex.ToString()); sb.AppendLine("*** Log: ***"); var log = _logWriter.ToString(); sb.AppendLine(log); Assert.Fail(sb.ToString()); } }