forked from Azure/azure-signalr
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathCustomizedTimerTests.cs
169 lines (145 loc) · 5.96 KB
/
CustomizedTimerTests.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Azure.SignalR.Tests;
using Microsoft.Azure.SignalR.Tests.Common;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Xunit;
using Xunit.Abstractions;
namespace Microsoft.Azure.SignalR.Common.Tests;
public class CustomizedTimerTests(ITestOutputHelper output) : VerifiableLoggedTest(output)
{
private const int BasePeriodMs = 400;
private static readonly TimeSpan BaseTs = TimeSpan.FromMilliseconds(BasePeriodMs);
private static readonly TimeSpan BaseTsPlus = TimeSpan.FromMilliseconds(BasePeriodMs * 1.2); // +20% leeway to avoid false positives
[RetryTheory]
// #stops == #starts
[InlineData(0, 1, 1, 0)]
[InlineData(3, 1, 1, 3)]
// #starts < #stops
[InlineData(1, 2, 3, 1)]
// #starts > #stops
[InlineData(1, 3, 2, 2)]
public async Task BasicStartStopTest(int timerTicks, int numStarts, int numStops, int expectedCallbacks)
{
var loggerFactory = NullLoggerFactory.Instance;
await RetryWhenExceptionThrows(async () =>
{
var callbackCount = 0;
using var timer = CreatePingTimer(loggerFactory, () => Interlocked.Increment(ref callbackCount));
for (var i = 0; i < numStarts; i++)
{
timer.Start();
}
await Task.Delay(BaseTsPlus * timerTicks);
for (var i = 0; i < numStops; i++)
{
timer.Stop();
}
// special case check when numStops < numStarts
Assert.Equal(numStarts <= numStops ? expectedCallbacks : timerTicks, callbackCount);
await Task.Delay(BaseTsPlus * timerTicks);
Assert.Equal(expectedCallbacks, callbackCount);
});
}
[Fact]
public async Task StartStopStartStop()
{
if (Environment.OSVersion.Platform == PlatformID.Unix)
{
// it will fail in osx in github action, due to slow machine.
// skip it for now.
return;
}
using (StartVerifiableLog(out var loggerFactory, LogLevel.Warning))
{
var callbackCount = 0;
using var timer = CreatePingTimer(loggerFactory, () => Interlocked.Increment(ref callbackCount));
timer.Start();
await Task.Delay(BaseTsPlus);
timer.Stop();
Assert.Equal(1, callbackCount);
await Task.Delay(BaseTsPlus * 5);
timer.Start();
await Task.Delay(BaseTsPlus);
timer.Stop();
Assert.Equal(2, callbackCount);
}
}
[Fact]
public async Task StartStopDispose_StartDisposeStop()
{
if (Environment.OSVersion.Platform == PlatformID.Unix)
{
// it will fail in osx in github action, due to slow machine.
// skip it for now.
return;
}
using (StartVerifiableLog(out var loggerFactory, LogLevel.Warning))
{
var callbackCount = 0;
var timer = CreatePingTimer(loggerFactory, () => Interlocked.Increment(ref callbackCount));
timer.Start();
await Task.Delay(BaseTsPlus);
timer.Stop();
Assert.Equal(1, callbackCount);
await Task.Delay(BaseTsPlus * 5);
timer.Dispose();
Assert.Equal(1, callbackCount);
timer.Start();
await Task.Delay(BaseTsPlus);
timer.Dispose();
Assert.Equal(2, callbackCount);
await Task.Delay(BaseTsPlus * 5);
timer.Stop();
Assert.Equal(2, callbackCount);
}
}
[Theory]
[InlineData(2)]
[InlineData(3)]
public async Task LongRunningCallback(int timerTicks)
{
using (StartVerifiableLog(out var loggerFactory, LogLevel.Warning))
{
var callbackCount = 0;
using var timer = CustomizedPingTimerFactory.CreateCustomizedPingTimer(loggerFactory.CreateLogger(
nameof(BasicStartStopTest)), nameof(BasicStartStopTest),
async () =>
{
Interlocked.Increment(ref callbackCount);
// long running task to make timer skip timerTicks callbacks
await Task.Delay(BaseTsPlus * timerTicks);
},
BaseTs, BaseTs);
timer.Start();
await Task.Delay(BaseTsPlus * 2 * timerTicks);
timer.Stop();
Assert.Equal(2, callbackCount);
// extra check it really stopped
await Task.Delay(BaseTsPlus * 2 * timerTicks);
Assert.Equal(2, callbackCount);
}
}
private static ServiceConnectionContainerBase.CustomizedPingTimer CreatePingTimer(ILoggerFactory loggerFactory, Action counter) =>
CustomizedPingTimerFactory.CreateCustomizedPingTimer(loggerFactory.CreateLogger(
nameof(BasicStartStopTest)), nameof(BasicStartStopTest),
() =>
{
counter();
return Task.CompletedTask;
},
BaseTs, BaseTs);
private sealed class CustomizedPingTimerFactory : ServiceConnectionContainerBase
{
public CustomizedPingTimerFactory(IServiceConnectionFactory serviceConnectionFactory, int minConnectionCount, HubServiceEndpoint endpoint, IReadOnlyList<IServiceConnection> initialConnections = null, ILogger logger = null, AckHandler ackHandler = null) : base(serviceConnectionFactory, minConnectionCount, endpoint, initialConnections, logger, ackHandler)
{
}
internal static CustomizedPingTimer CreateCustomizedPingTimer(ILogger logger, string name, Func<Task> func, TimeSpan due, TimeSpan interval) =>
new CustomizedPingTimer(logger, name, func, due, interval);
}
}