From 8435d44d9452960b1d9a6bb18007fc6a33429cba Mon Sep 17 00:00:00 2001 From: Dan Moseley Date: Mon, 20 Apr 2026 23:28:45 -0600 Subject: [PATCH 1/4] Fix flaky CounterSampleCalculator_ElapsedTime test Use Stopwatch for both elapsed time measurements so they share the same clock source (QPC), instead of comparing QPC-based counter values against DateTime.Now. Under CI load the gap between the two clock sources could exceed the 0.3s tolerance. Also removed unused using directives. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../tests/CounterSampleCalculatorTests.cs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/libraries/System.Diagnostics.PerformanceCounter/tests/CounterSampleCalculatorTests.cs b/src/libraries/System.Diagnostics.PerformanceCounter/tests/CounterSampleCalculatorTests.cs index 37b34bf3730195..d675aa678dcd24 100644 --- a/src/libraries/System.Diagnostics.PerformanceCounter/tests/CounterSampleCalculatorTests.cs +++ b/src/libraries/System.Diagnostics.PerformanceCounter/tests/CounterSampleCalculatorTests.cs @@ -2,8 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Collections; -using System.Collections.Specialized; using Xunit; namespace System.Diagnostics.Tests @@ -18,15 +16,15 @@ public static void CounterSampleCalculator_ElapsedTime() PerformanceCounter counterSample = CreateCounter(categoryName, PerformanceCounterType.ElapsedTime); counterSample.RawValue = Stopwatch.GetTimestamp(); - DateTime Start = DateTime.Now; + long startTicks = Stopwatch.GetTimestamp(); Helpers.RetryOnAllPlatforms(() => counterSample.NextValue()); System.Threading.Thread.Sleep(500); var counterVal = Helpers.RetryOnAllPlatforms(() => counterSample.NextValue()); - var dateTimeVal = DateTime.Now.Subtract(Start).TotalSeconds; + var elapsed = (double)(Stopwatch.GetTimestamp() - startTicks) / Stopwatch.Frequency; Helpers.DeleteCategory(categoryName); - Assert.True(Math.Abs(dateTimeVal - counterVal) < .3); + Assert.True(Math.Abs(elapsed - counterVal) < .3); } public static PerformanceCounter CreateCounter(string categoryName, PerformanceCounterType counterType) From a6bcec0da53910059a55842477f0a5a74c60c80d Mon Sep 17 00:00:00 2001 From: Dan Moseley Date: Mon, 20 Apr 2026 23:36:03 -0600 Subject: [PATCH 2/4] Address review: single timestamp, rename to startTimestamp Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../tests/CounterSampleCalculatorTests.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libraries/System.Diagnostics.PerformanceCounter/tests/CounterSampleCalculatorTests.cs b/src/libraries/System.Diagnostics.PerformanceCounter/tests/CounterSampleCalculatorTests.cs index d675aa678dcd24..ef1207102f8a30 100644 --- a/src/libraries/System.Diagnostics.PerformanceCounter/tests/CounterSampleCalculatorTests.cs +++ b/src/libraries/System.Diagnostics.PerformanceCounter/tests/CounterSampleCalculatorTests.cs @@ -15,14 +15,14 @@ public static void CounterSampleCalculator_ElapsedTime() PerformanceCounter counterSample = CreateCounter(categoryName, PerformanceCounterType.ElapsedTime); - counterSample.RawValue = Stopwatch.GetTimestamp(); - long startTicks = Stopwatch.GetTimestamp(); + long startTimestamp = Stopwatch.GetTimestamp(); + counterSample.RawValue = startTimestamp; Helpers.RetryOnAllPlatforms(() => counterSample.NextValue()); System.Threading.Thread.Sleep(500); var counterVal = Helpers.RetryOnAllPlatforms(() => counterSample.NextValue()); - var elapsed = (double)(Stopwatch.GetTimestamp() - startTicks) / Stopwatch.Frequency; + var elapsed = (double)(Stopwatch.GetTimestamp() - startTimestamp) / Stopwatch.Frequency; Helpers.DeleteCategory(categoryName); Assert.True(Math.Abs(elapsed - counterVal) < .3); } From 2dfcc8199fd0e27ba84f78e5218610e3cd061ff7 Mon Sep 17 00:00:00 2001 From: Dan Moseley Date: Tue, 21 Apr 2026 09:27:57 -0600 Subject: [PATCH 3/4] Add RetryHelper around timing assertion Wrap the timing-sensitive measurement and assertion in RetryHelper.Execute (3 attempts) so transient CI scheduling delays don't cause flaky failures. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../tests/CounterSampleCalculatorTests.cs | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/libraries/System.Diagnostics.PerformanceCounter/tests/CounterSampleCalculatorTests.cs b/src/libraries/System.Diagnostics.PerformanceCounter/tests/CounterSampleCalculatorTests.cs index ef1207102f8a30..e2ca1d461a55e0 100644 --- a/src/libraries/System.Diagnostics.PerformanceCounter/tests/CounterSampleCalculatorTests.cs +++ b/src/libraries/System.Diagnostics.PerformanceCounter/tests/CounterSampleCalculatorTests.cs @@ -15,16 +15,21 @@ public static void CounterSampleCalculator_ElapsedTime() PerformanceCounter counterSample = CreateCounter(categoryName, PerformanceCounterType.ElapsedTime); - long startTimestamp = Stopwatch.GetTimestamp(); - counterSample.RawValue = startTimestamp; - Helpers.RetryOnAllPlatforms(() => counterSample.NextValue()); + // Timing comparisons can be flaky under CI load, so retry. + RetryHelper.Execute(() => + { + long startTimestamp = Stopwatch.GetTimestamp(); + counterSample.RawValue = startTimestamp; + Helpers.RetryOnAllPlatforms(() => counterSample.NextValue()); - System.Threading.Thread.Sleep(500); + System.Threading.Thread.Sleep(500); + + var counterVal = Helpers.RetryOnAllPlatforms(() => counterSample.NextValue()); + var elapsed = (double)(Stopwatch.GetTimestamp() - startTimestamp) / Stopwatch.Frequency; + Assert.True(Math.Abs(elapsed - counterVal) < .3, $"Expected elapsed ({elapsed:F3}s) and counterVal ({counterVal:F3}s) to be within 0.3s"); + }, maxAttempts: 3); - var counterVal = Helpers.RetryOnAllPlatforms(() => counterSample.NextValue()); - var elapsed = (double)(Stopwatch.GetTimestamp() - startTimestamp) / Stopwatch.Frequency; Helpers.DeleteCategory(categoryName); - Assert.True(Math.Abs(elapsed - counterVal) < .3); } public static PerformanceCounter CreateCounter(string categoryName, PerformanceCounterType counterType) From 2160c8e4a6ed4efde4bb324e18146e517e3d5603 Mon Sep 17 00:00:00 2001 From: Dan Moseley Date: Tue, 21 Apr 2026 10:10:14 -0600 Subject: [PATCH 4/4] Address review: try/finally cleanup and retryWhen filter Wrap test body in try/finally so DeleteCategory and Dispose always run even if retries are exhausted. Restrict retries to XunitException (assertion failures) so unexpected errors propagate immediately. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../tests/CounterSampleCalculatorTests.cs | 35 +++++++++++-------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/src/libraries/System.Diagnostics.PerformanceCounter/tests/CounterSampleCalculatorTests.cs b/src/libraries/System.Diagnostics.PerformanceCounter/tests/CounterSampleCalculatorTests.cs index e2ca1d461a55e0..158442337b90ef 100644 --- a/src/libraries/System.Diagnostics.PerformanceCounter/tests/CounterSampleCalculatorTests.cs +++ b/src/libraries/System.Diagnostics.PerformanceCounter/tests/CounterSampleCalculatorTests.cs @@ -3,6 +3,7 @@ using System; using Xunit; +using Xunit.Sdk; namespace System.Diagnostics.Tests { @@ -15,21 +16,27 @@ public static void CounterSampleCalculator_ElapsedTime() PerformanceCounter counterSample = CreateCounter(categoryName, PerformanceCounterType.ElapsedTime); - // Timing comparisons can be flaky under CI load, so retry. - RetryHelper.Execute(() => + try { - long startTimestamp = Stopwatch.GetTimestamp(); - counterSample.RawValue = startTimestamp; - Helpers.RetryOnAllPlatforms(() => counterSample.NextValue()); - - System.Threading.Thread.Sleep(500); - - var counterVal = Helpers.RetryOnAllPlatforms(() => counterSample.NextValue()); - var elapsed = (double)(Stopwatch.GetTimestamp() - startTimestamp) / Stopwatch.Frequency; - Assert.True(Math.Abs(elapsed - counterVal) < .3, $"Expected elapsed ({elapsed:F3}s) and counterVal ({counterVal:F3}s) to be within 0.3s"); - }, maxAttempts: 3); - - Helpers.DeleteCategory(categoryName); + // Timing comparisons can be flaky under CI load, so retry. + RetryHelper.Execute(() => + { + long startTimestamp = Stopwatch.GetTimestamp(); + counterSample.RawValue = startTimestamp; + Helpers.RetryOnAllPlatforms(() => counterSample.NextValue()); + + System.Threading.Thread.Sleep(500); + + var counterVal = Helpers.RetryOnAllPlatforms(() => counterSample.NextValue()); + var elapsed = (double)(Stopwatch.GetTimestamp() - startTimestamp) / Stopwatch.Frequency; + Assert.True(Math.Abs(elapsed - counterVal) < .3, $"Expected elapsed ({elapsed:F3}s) and counterVal ({counterVal:F3}s) to be within 0.3s"); + }, maxAttempts: 3, retryWhen: e => e is XunitException); + } + finally + { + counterSample.Dispose(); + Helpers.DeleteCategory(categoryName); + } } public static PerformanceCounter CreateCounter(string categoryName, PerformanceCounterType counterType)