diff --git a/src/Build.UnitTests/BackEnd/Scheduler_Tests.cs b/src/Build.UnitTests/BackEnd/Scheduler_Tests.cs index d7997e94888..dab27885584 100644 --- a/src/Build.UnitTests/BackEnd/Scheduler_Tests.cs +++ b/src/Build.UnitTests/BackEnd/Scheduler_Tests.cs @@ -968,5 +968,51 @@ private void ReportDefaultParentRequestIsFinished() var buildResult = new BuildResult(_defaultParentRequest); _scheduler.ReportResult(_defaultParentRequest.NodeRequestId, buildResult); } + + [Fact] + public void ScheduleTimeRecord_AccumulatedTime_DoesNotThrowWhenTimerIsRunning() + { + var record = new ScheduleTimeRecord(); + DateTime startTime = new DateTime(2026, 1, 1, 0, 0, 0, DateTimeKind.Utc); + + // Before starting: accumulated time should be zero. + record.AccumulatedTime.ShouldBe(TimeSpan.Zero); + + // Start the timer. + record.StartState(startTime); + + // While running: should NOT throw — should return a positive elapsed time. + record.AccumulatedTime.ShouldBeGreaterThan(TimeSpan.Zero); + + // Stop the timer. + DateTime endTime = startTime.AddMilliseconds(500); + record.EndState(endTime); + + // After stopping: should return the accumulated time. + record.AccumulatedTime.ShouldBe(TimeSpan.FromMilliseconds(500)); + } + + [Fact] + public void ScheduleTimeRecord_AccumulatedTime_IncludesPreviousAccumulation() + { + var record = new ScheduleTimeRecord(); + DateTime t0 = new DateTime(2026, 1, 1, 0, 0, 0, DateTimeKind.Utc); + + // First interval: 200ms. + record.StartState(t0); + record.EndState(t0.AddMilliseconds(200)); + record.AccumulatedTime.ShouldBe(TimeSpan.FromMilliseconds(200)); + + // Start a second interval. + record.StartState(t0.AddMilliseconds(300)); + + // While second interval is running: should include the 200ms from + // the first interval plus the elapsed time of the current interval. + record.AccumulatedTime.ShouldBeGreaterThan(TimeSpan.FromMilliseconds(200)); + + // Stop second interval after 100ms. + record.EndState(t0.AddMilliseconds(400)); + record.AccumulatedTime.ShouldBe(TimeSpan.FromMilliseconds(300)); + } } } diff --git a/src/Build/BackEnd/Components/Scheduler/ScheduleTimeRecord.cs b/src/Build/BackEnd/Components/Scheduler/ScheduleTimeRecord.cs index 8a79db3505b..3ff10fb52a4 100644 --- a/src/Build/BackEnd/Components/Scheduler/ScheduleTimeRecord.cs +++ b/src/Build/BackEnd/Components/Scheduler/ScheduleTimeRecord.cs @@ -32,12 +32,20 @@ public ScheduleTimeRecord() /// /// Retrieve the accumulated time. + /// If the timer is still running, returns the accumulated time so far + /// (elapsed time since the timer started plus any previously accumulated time) + /// instead of throwing. /// public TimeSpan AccumulatedTime { get { - ErrorUtilities.VerifyThrow(_startTimeForCurrentState == DateTime.MinValue, "Can't get the accumulated time while the timer is still running."); + if (_startTimeForCurrentState != DateTime.MinValue) + { + // Timer is still running — return best-effort elapsed time. + return _accumulatedTime + (DateTime.UtcNow - _startTimeForCurrentState); + } + return _accumulatedTime; } }