Skip to content

Commit 4dbfe0e

Browse files
JamesNKcaptainsafia
authored andcommitted
Attempted fix on flaky counter tests (#59892)
1 parent 75f4798 commit 4dbfe0e

File tree

7 files changed

+71
-77
lines changed

7 files changed

+71
-77
lines changed

src/Hosting/Hosting/test/HostingApplicationDiagnosticsTests.cs

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,9 @@ public async Task EventCountersAndMetricsValues()
4040
using CancellationTokenSource timeoutTokenSource = new CancellationTokenSource(timeout);
4141
timeoutTokenSource.Token.Register(() => Logger.LogError("Timeout while waiting for counter value."));
4242

43-
var totalRequestValues = eventListener.GetCounterValues("total-requests", timeoutTokenSource.Token).GetAsyncEnumerator();
44-
var currentRequestValues = eventListener.GetCounterValues("current-requests", timeoutTokenSource.Token).GetAsyncEnumerator();
45-
var failedRequestValues = eventListener.GetCounterValues("failed-requests", timeoutTokenSource.Token).GetAsyncEnumerator();
43+
var totalRequestValues = eventListener.GetCounterValues("total-requests", timeoutTokenSource.Token);
44+
var currentRequestValues = eventListener.GetCounterValues("current-requests", timeoutTokenSource.Token);
45+
var failedRequestValues = eventListener.GetCounterValues("failed-requests", timeoutTokenSource.Token);
4646

4747
eventListener.EnableEvents(hostingEventSource, EventLevel.Informational, EventKeywords.None,
4848
new Dictionary<string, string>
@@ -69,18 +69,18 @@ public async Task EventCountersAndMetricsValues()
6969
var context1 = hostingApplication1.CreateContext(features1);
7070
var context2 = hostingApplication2.CreateContext(features2);
7171

72-
await totalRequestValues.WaitForSumValueAsync(2);
73-
await currentRequestValues.WaitForValueAsync(2);
74-
await failedRequestValues.WaitForValueAsync(0);
72+
await WaitForCounterValue(totalRequestValues, expectedValue: 2, Logger);
73+
await WaitForCounterValue(currentRequestValues, expectedValue: 2, Logger);
74+
await WaitForCounterValue(failedRequestValues, expectedValue: 0, Logger);
7575

7676
Logger.LogInformation(nameof(HostingApplication.DisposeContext));
7777

7878
hostingApplication1.DisposeContext(context1, null);
7979
hostingApplication2.DisposeContext(context2, null);
8080

81-
await totalRequestValues.WaitForSumValueAsync(2);
82-
await currentRequestValues.WaitForValueAsync(0);
83-
await failedRequestValues.WaitForValueAsync(0);
81+
await WaitForCounterValue(totalRequestValues, expectedValue: 2, Logger);
82+
await WaitForCounterValue(currentRequestValues, expectedValue: 0, Logger);
83+
await WaitForCounterValue(failedRequestValues, expectedValue: 0, Logger);
8484

8585
Assert.Collection(activeRequestsCollector1.GetMeasurementSnapshot(),
8686
m => Assert.Equal(1, m.Value),
@@ -100,9 +100,9 @@ public async Task EventCountersAndMetricsValues()
100100
context1 = hostingApplication1.CreateContext(features1);
101101
context2 = hostingApplication2.CreateContext(features2);
102102

103-
await totalRequestValues.WaitForSumValueAsync(4);
104-
await currentRequestValues.WaitForValueAsync(2);
105-
await failedRequestValues.WaitForValueAsync(0);
103+
await WaitForCounterValue(totalRequestValues, expectedValue: 4, Logger);
104+
await WaitForCounterValue(currentRequestValues, expectedValue: 2, Logger);
105+
await WaitForCounterValue(failedRequestValues, expectedValue: 0, Logger);
106106

107107
context1.HttpContext.Response.StatusCode = StatusCodes.Status500InternalServerError;
108108
context2.HttpContext.Response.StatusCode = StatusCodes.Status500InternalServerError;
@@ -112,9 +112,9 @@ public async Task EventCountersAndMetricsValues()
112112
hostingApplication1.DisposeContext(context1, null);
113113
hostingApplication2.DisposeContext(context2, null);
114114

115-
await totalRequestValues.WaitForSumValueAsync(4);
116-
await currentRequestValues.WaitForValueAsync(0);
117-
await failedRequestValues.WaitForValueAsync(2);
115+
await WaitForCounterValue(totalRequestValues, expectedValue: 4, Logger);
116+
await WaitForCounterValue(currentRequestValues, expectedValue: 0, Logger);
117+
await WaitForCounterValue(failedRequestValues, expectedValue: 2, Logger);
118118

119119
Assert.Collection(activeRequestsCollector1.GetMeasurementSnapshot(),
120120
m => Assert.Equal(1, m.Value),
@@ -134,6 +134,11 @@ public async Task EventCountersAndMetricsValues()
134134
m => Assert.True(m.Value > 0));
135135
}
136136

137+
private static async Task WaitForCounterValue(CounterValues values, double expectedValue, ILogger logger)
138+
{
139+
await values.Values.WaitForValueAsync(expectedValue, values.CounterName, logger);
140+
}
141+
137142
[Fact]
138143
public void EventCountersEnabled()
139144
{

src/Hosting/Hosting/test/Internal/HostingEventSourceTests.cs

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -193,9 +193,9 @@ public async Task VerifyCountersFireWithCorrectValues()
193193
using var timeoutTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(30));
194194
timeoutTokenSource.Token.Register(() => Logger.LogError("Timeout while waiting for counter value."));
195195

196-
var totalRequestValues = eventListener.GetCounterValues("total-requests", timeoutTokenSource.Token).GetAsyncEnumerator();
197-
var currentRequestValues = eventListener.GetCounterValues("current-requests", timeoutTokenSource.Token).GetAsyncEnumerator();
198-
var failedRequestValues = eventListener.GetCounterValues("failed-requests", timeoutTokenSource.Token).GetAsyncEnumerator();
196+
var totalRequestValues = eventListener.GetCounterValues("total-requests", timeoutTokenSource.Token);
197+
var currentRequestValues = eventListener.GetCounterValues("current-requests", timeoutTokenSource.Token);
198+
var failedRequestValues = eventListener.GetCounterValues("failed-requests", timeoutTokenSource.Token);
199199

200200
eventListener.EnableEvents(hostingEventSource, EventLevel.Informational, EventKeywords.None,
201201
new Dictionary<string, string>
@@ -207,32 +207,37 @@ public async Task VerifyCountersFireWithCorrectValues()
207207
Logger.LogInformation(nameof(HostingEventSource.RequestStart));
208208
hostingEventSource.RequestStart("GET", "/");
209209

210-
await totalRequestValues.WaitForSumValueAsync(1);
211-
await currentRequestValues.WaitForValueAsync(1);
212-
await failedRequestValues.WaitForValueAsync(0);
210+
await WaitForCounterValue(totalRequestValues, expectedValue: 1, Logger);
211+
await WaitForCounterValue(currentRequestValues, expectedValue: 1, Logger);
212+
await WaitForCounterValue(failedRequestValues, expectedValue: 0, Logger);
213213

214214
Logger.LogInformation(nameof(HostingEventSource.RequestStop));
215215
hostingEventSource.RequestStop();
216216

217-
await totalRequestValues.WaitForSumValueAsync(1);
218-
await currentRequestValues.WaitForValueAsync(0);
219-
await failedRequestValues.WaitForValueAsync(0);
217+
await WaitForCounterValue(totalRequestValues, expectedValue: 1, Logger);
218+
await WaitForCounterValue(currentRequestValues, expectedValue: 0, Logger);
219+
await WaitForCounterValue(failedRequestValues, expectedValue: 0, Logger);
220220

221221
Logger.LogInformation(nameof(HostingEventSource.RequestStart));
222222
hostingEventSource.RequestStart("POST", "/");
223223

224-
await totalRequestValues.WaitForSumValueAsync(2);
225-
await currentRequestValues.WaitForValueAsync(1);
226-
await failedRequestValues.WaitForValueAsync(0);
224+
await WaitForCounterValue(totalRequestValues, expectedValue: 2, Logger);
225+
await WaitForCounterValue(currentRequestValues, expectedValue: 1, Logger);
226+
await WaitForCounterValue(failedRequestValues, expectedValue: 0, Logger);
227227

228228
Logger.LogInformation(nameof(HostingEventSource.RequestFailed));
229229
hostingEventSource.RequestFailed();
230230
Logger.LogInformation(nameof(HostingEventSource.RequestStop));
231231
hostingEventSource.RequestStop();
232232

233-
await totalRequestValues.WaitForSumValueAsync(2);
234-
await currentRequestValues.WaitForValueAsync(0);
235-
await failedRequestValues.WaitForValueAsync(1);
233+
await WaitForCounterValue(totalRequestValues, expectedValue: 2, Logger);
234+
await WaitForCounterValue(currentRequestValues, expectedValue: 0, Logger);
235+
await WaitForCounterValue(failedRequestValues, expectedValue: 1, Logger);
236+
}
237+
238+
private static async Task WaitForCounterValue(CounterValues values, double expectedValue, ILogger logger)
239+
{
240+
await values.Values.WaitForValueAsync(expectedValue, values.CounterName, logger);
236241
}
237242

238243
private static HostingEventSource GetHostingEventSource()

src/Hosting/Hosting/test/Microsoft.AspNetCore.Hosting.Tests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
<Compile Include="$(SharedSourceRoot)EventSource.Testing\TestCounterListener.cs" />
1212
<Compile Include="$(SharedSourceRoot)SyncPoint\SyncPoint.cs" />
1313
<Compile Include="$(SharedSourceRoot)Metrics\TestMeterFactory.cs" LinkBase="shared" />
14+
<Compile Include="$(SharedSourceRoot)AsyncEnumerableExtensions.cs" LinkBase="shared" />
1415
<Content Include="testroot\**\*" CopyToOutputDirectory="PreserveNewest" CopyToPublishDirectory="PreserveNewest" />
1516
<Content Include="Microsoft.AspNetCore.Hosting.StaticWebAssets.xml" CopyToOutputDirectory="PreserveNewest" />
1617
</ItemGroup>

src/Middleware/ConcurrencyLimiter/test/ConcurrencyLimiterEventSourceTests.cs

Lines changed: 14 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System.Diagnostics.Tracing;
66
using Microsoft.AspNetCore.Internal;
77
using Microsoft.AspNetCore.InternalTesting;
8+
using Microsoft.Extensions.Logging;
89

910
namespace Microsoft.AspNetCore.ConcurrencyLimiter.Tests;
1011

@@ -55,7 +56,7 @@ public async Task TracksQueueLength()
5556

5657
using var timeoutTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(30));
5758

58-
var lengthValues = eventListener.GetCounterValues("queue-length", timeoutTokenSource.Token).GetAsyncEnumerator();
59+
var lengthValues = eventListener.GetCounterValues("queue-length", timeoutTokenSource.Token);
5960

6061
eventListener.EnableEvents(eventSource, EventLevel.Informational, EventKeywords.None,
6162
new Dictionary<string, string>
@@ -66,20 +67,20 @@ public async Task TracksQueueLength()
6667
// Act
6768
eventSource.RequestRejected();
6869

69-
Assert.True(await UntilValueMatches(lengthValues, 0));
70+
await WaitForCounterValue(lengthValues, expectedValue: 0, Logger);
7071
using (eventSource.QueueTimer())
7172
{
72-
Assert.True(await UntilValueMatches(lengthValues, 1));
73+
await WaitForCounterValue(lengthValues, expectedValue: 1, Logger);
7374

7475
using (eventSource.QueueTimer())
7576
{
76-
Assert.True(await UntilValueMatches(lengthValues, 2));
77+
await WaitForCounterValue(lengthValues, expectedValue: 2, Logger);
7778
}
7879

79-
Assert.True(await UntilValueMatches(lengthValues, 1));
80+
await WaitForCounterValue(lengthValues, expectedValue: 1, Logger);
8081
}
8182

82-
Assert.True(await UntilValueMatches(lengthValues, 0));
83+
await WaitForCounterValue(lengthValues, expectedValue: 0, Logger);
8384
}
8485

8586
[Fact]
@@ -96,7 +97,7 @@ public async Task TracksDurationSpentInQueue()
9697

9798
using var timeoutTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(5));
9899

99-
var durationValues = eventListener.GetCounterValues("queue-duration", timeoutTokenSource.Token).GetAsyncEnumerator();
100+
var durationValues = eventListener.GetCounterValues("queue-duration", timeoutTokenSource.Token);
100101

101102
eventListener.EnableEvents(eventSource, EventLevel.Informational, EventKeywords.None,
102103
new Dictionary<string, string>
@@ -105,17 +106,17 @@ public async Task TracksDurationSpentInQueue()
105106
});
106107

107108
// Act
108-
Assert.True(await UntilValueMatches(durationValues, 0));
109+
await WaitForCounterValue(durationValues, expectedValue: 0, Logger);
109110

110111
using (eventSource.QueueTimer())
111112
{
112-
Assert.True(await UntilValueMatches(durationValues, 0));
113+
await WaitForCounterValue(durationValues, expectedValue: 0, Logger);
113114
}
114115

115116
// check that something (anything!) has been written
116-
while (await durationValues.MoveNextAsync())
117+
while (await durationValues.Values.MoveNextAsync())
117118
{
118-
if (durationValues.Current > 0)
119+
if (durationValues.Values.Current > 0)
119120
{
120121
return;
121122
}
@@ -124,17 +125,9 @@ public async Task TracksDurationSpentInQueue()
124125
throw new TimeoutException();
125126
}
126127

127-
private async Task<bool> UntilValueMatches(IAsyncEnumerator<double> enumerator, int value)
128+
private static async Task WaitForCounterValue(CounterValues values, double expectedValue, ILogger logger)
128129
{
129-
while (await enumerator.MoveNextAsync())
130-
{
131-
if (enumerator.Current == value)
132-
{
133-
return true;
134-
}
135-
}
136-
137-
return false;
130+
await values.Values.WaitForValueAsync(expectedValue, values.CounterName, logger);
138131
}
139132

140133
private static ConcurrencyLimiterEventSource GetConcurrencyLimiterEventSource()

src/Middleware/ConcurrencyLimiter/test/Microsoft.AspNetCore.ConcurrencyLimiter.Tests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
<ItemGroup>
88
<Compile Include="$(SharedSourceRoot)EventSource.Testing\TestCounterListener.cs" />
99
<Compile Include="$(SharedSourceRoot)EventSource.Testing\TestEventListener.cs" />
10+
<Compile Include="$(SharedSourceRoot)AsyncEnumerableExtensions.cs" LinkBase="shared" />
1011
</ItemGroup>
1112

1213
<ItemGroup>

src/Hosting/Hosting/test/Internal/AsyncEnumerableExtensions.cs renamed to src/Shared/AsyncEnumerableExtensions.cs

Lines changed: 6 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,13 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using System.Numerics;
5+
using Microsoft.Extensions.Logging;
56

6-
namespace System.Collections.Generic;
7+
namespace Microsoft.AspNetCore.InternalTesting;
78

89
internal static class AsyncEnumerableExtensions
910
{
10-
public static async Task WaitForValueAsync<T>(this IAsyncEnumerator<T> values, T expectedValue) where T : INumber<T>
11+
public static async Task WaitForValueAsync<T>(this IAsyncEnumerator<T> values, T expectedValue, string operationName, ILogger logger) where T : INumber<T>
1112
{
1213
T value = T.Zero;
1314
try
@@ -17,37 +18,18 @@ public static async Task WaitForValueAsync<T>(this IAsyncEnumerator<T> values, T
1718
value = values.Current;
1819
if (value == expectedValue)
1920
{
21+
logger.LogDebug("Operation {OperationName} completed with value {Value}.", operationName, value);
2022
return;
2123
}
22-
}
23-
24-
throw new InvalidOperationException("Data ended without match.");
25-
}
26-
catch (Exception ex)
27-
{
28-
throw new InvalidOperationException($"Results ended with final value of {value}. Expected value of {expectedValue}.", ex);
29-
}
30-
}
3124

32-
public static async Task WaitForSumValueAsync<T>(this IAsyncEnumerator<T> values, T expectedValue) where T: INumber<T>
33-
{
34-
T value = T.Zero;
35-
try
36-
{
37-
while (await values.MoveNextAsync())
38-
{
39-
value += values.Current;
40-
if (value == expectedValue)
41-
{
42-
return;
43-
}
25+
logger.LogDebug("Operation {OperationName} expected {ExpectedValue} but got {Value}.", operationName, expectedValue, value);
4426
}
4527

4628
throw new InvalidOperationException("Data ended without match.");
4729
}
4830
catch (Exception ex)
4931
{
50-
throw new InvalidOperationException($"Results ended with final sum value of {value}. Expected sum value of {expectedValue}.", ex);
32+
throw new InvalidOperationException($"Results ended with final value of {value}. Expected value of {expectedValue}.", ex);
5133
}
5234
}
5335
}

src/Shared/EventSource.Testing/TestCounterListener.cs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,12 @@
99

1010
namespace Microsoft.AspNetCore.Internal;
1111

12+
internal sealed class CounterValues(string counterName, IAsyncEnumerator<double> values)
13+
{
14+
public string CounterName { get; } = counterName;
15+
public IAsyncEnumerator<double> Values { get; } = values;
16+
}
17+
1218
internal sealed class TestCounterListener : EventListener
1319
{
1420
private readonly Dictionary<string, Channel<double>> _counters = new Dictionary<string, Channel<double>>();
@@ -30,9 +36,10 @@ public TestCounterListener(ILoggerFactory loggerFactory, string eventSourceName,
3036
_eventSourceName = eventSourceName;
3137
}
3238

33-
public IAsyncEnumerable<double> GetCounterValues(string counterName, CancellationToken cancellationToken = default)
39+
public CounterValues GetCounterValues(string counterName, CancellationToken cancellationToken = default)
3440
{
35-
return _counters[counterName].Reader.ReadAllAsync(cancellationToken);
41+
var values = _counters[counterName].Reader.ReadAllAsync(cancellationToken).GetAsyncEnumerator(cancellationToken);
42+
return new CounterValues(counterName, values);
3643
}
3744

3845
protected override void OnEventWritten(EventWrittenEventArgs eventData)

0 commit comments

Comments
 (0)