This repository has been archived by the owner on Nov 20, 2023. It is now read-only.
-
-
Notifications
You must be signed in to change notification settings - Fork 1
/
TestTimer.cs
215 lines (192 loc) · 8.7 KB
/
TestTimer.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
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
using System;
using System.Collections.Generic;
using System.Threading;
namespace ITimer;
/// <summary>
/// Provides a 'timer' to be used in unittests. The <see cref="BaseTimer.Elapsed" /> event will not be raised
/// automatically by this timer but only when one of the <c>Tick</c> overloads is invoked.
/// </summary>
public class TestTimer : BaseTimer, ISignaler
{
/// <summary>
/// Occurs when the <see cref="TestTimer" /> is started.
/// </summary>
public event EventHandler<StartedEventArgs>? Started;
/// <summary>
/// Occurs when the <see cref="TestTimer" /> is stopped.
/// </summary>
public event EventHandler<StoppedEventArgs>? Stopped;
/// <summary>
/// Gets the number of times the timer has ticked since starting the time (or since the last <see cref="Reset" />).
/// </summary>
public int TickCount => _tickcount;
/// <summary>
/// Gets the number of times the timer was (re)started since starting the time (or since the last <see cref="Reset" />).
/// </summary>
public int StartCount => _startcount;
/// <summary>
/// Gets the number of times the timer was (re)stopped since starting the time (or since the last <see cref="Reset" />).
/// </summary>
public int StopCount => _stopcount;
private bool _enabled;
private int _tickcount;
private int _startcount;
private int _stopcount;
private readonly bool _requirestart;
/// <summary>
/// Initializes a new instance of the <see cref="TestTimer" /> class with the specified (optional) settings.
/// </summary>
/// <param name="timeProvider">The default <see cref="BaseTimer.DefaultTimeProvider" /> to use.</param>
/// <param name="requireStart">
/// When <c>true</c>, the timer will not raise the <see cref="BaseTimer.Elapsed" /> event unless the timer
/// has been started using the <see cref="Start()" /> method. When <c>false</c> the timer doesn't need to be
/// started.
/// </param>
/// <param name="interval">
/// This value has no effect other than the value being reflected in the <see cref="BaseTimer.Interval" /> propery.
/// </param>
/// <param name="autoReset">
/// This value has no effect other than the value being reflected in the <see cref="BaseTimer.AutoReset" /> propery.
/// </param>
/// <remarks>
/// Since the <see cref="TestTimer" /> is no actual timer, the <paramref name="interval"/> and <paramref name="autoReset"/>
/// values have no effect other than being reflected in the <see cref="TestTimer" />'s corresponding properties.
/// </remarks>
public TestTimer(Func<DateTimeOffset>? timeProvider = null, bool requireStart = false, TimeSpan? interval = null, bool autoReset = false)
: base(interval ?? TimeSpan.Zero, autoReset, timeProvider) => _requirestart = requireStart;
/// <summary>
/// Causes the timer to 'tick', i.e. to raise the <see cref="BaseTimer.Elapsed" /> event.
/// </summary>
/// <param name="signalTime">
/// When specified, the <see cref="TimerElapsedEventArgs" /> <see cref="TimerElapsedEventArgs.SignalTime" />
/// will be set to this value. When <c>null</c>, the <see cref="TestTimer" />'s default time provider is used.
/// </param>
/// <exception cref="InvalidOperationException">
/// Thrown when the timer was constructed with the <c>requireStart</c> argument set to <c>true</c> and the <see cref="Start()" />
/// method has not been invoked before invoking this method.
/// </exception>
public void Tick(DateTimeOffset? signalTime = null) => Tick(1, (i) => signalTime ?? TimeProvider.Invoke());
/// <summary>
/// Causes the timer to 'tick', i.e. to raise the <see cref="BaseTimer.Elapsed" /> event for the specified
/// amount of times.
/// </summary>
/// <param name="ticks">The amount of times the <see cref="BaseTimer.Elapsed" /> event should be raised.</param>
/// <param name="timeProvider">
/// The time provider to use when determining the <see cref="TimerElapsedEventArgs.SignalTime" /> for the event.
/// </param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when the number of ticks is less than zero.</exception>
/// <exception cref="InvalidOperationException">
/// Thrown when the timer was constructed with the <c>requireStart</c> argument set to <c>true</c> and the <see cref="Start()" />
/// method has not been invoked before invoking this method.
/// </exception>
public void Tick(int ticks, Func<int, DateTimeOffset>? timeProvider = null)
{
if (ticks < 0)
{
throw new ArgumentOutOfRangeException(nameof(ticks));
}
for (var i = 0; i < ticks; i++)
{
if (timeProvider != null)
{
RaiseElapsed(timeProvider(i));
}
else
{
RaiseElapsed(TimeProvider.Invoke());
}
}
}
/// <summary>
/// Causes the timer to 'tick', i.e. to raise the <see cref="BaseTimer.Elapsed" /> event for the number of specified
/// signaltimes.
/// </summary>
/// <param name="signalTimes"></param>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="signalTimes"/> is <c>null</c>.</exception>
/// <exception cref="InvalidOperationException">
/// Thrown when the timer was constructed with the <c>requireStart</c> argument set to <c>true</c> and the <see cref="Start()" />
/// method has not been invoked before invoking this method.
/// </exception>
public void Tick(IEnumerable<DateTimeOffset> signalTimes)
{
if (signalTimes == null)
{
throw new ArgumentNullException(nameof(signalTimes));
}
foreach (var s in signalTimes)
{
RaiseElapsed(s);
}
}
private void RaiseElapsed(DateTimeOffset signalTime)
{
if (_requirestart && !_enabled)
{
throw new InvalidOperationException("Timer must be started");
}
OnElapsed(new TestTimerElapsedEventArgs(Interlocked.Increment(ref _tickcount), signalTime));
}
/// <summary>
/// 'Starts' the timer.
/// </summary>
/// <remarks>
/// The <see cref="BaseTimer.Elapsed" /> event won't be automatically raised as normal
/// timers do. To raise the event, one of the <c>Tick</c> overloads need to be invoked. The <see cref="Started" />
/// event will be raised to keep track of a <see cref="TestTimer" /> being started. Also, when the <see cref="TestTimer" />
/// was constructed with the <c>requireStart</c> argument set to <c>true</c>, this method needs to be invoked before
/// invoking one of the <c>Tick</c> overloads otherwise a <see cref="InvalidOperationException" /> will be thrown.
/// </remarks>
public override void Start()
{
_enabled = true;
Interlocked.Increment(ref _startcount);
Started?.Invoke(this, new StartedEventArgs());
}
/// <summary>
/// 'Starts' the timer.
/// </summary>
/// <remarks>
/// The <see cref="BaseTimer.Elapsed" /> event won't be automatically raised as normal
/// timers do. To raise the event, one of the <c>Tick</c> overloads need to be invoked. The <see cref="Started" />
/// event will be raised to keep track of a <see cref="TestTimer" /> being started. Also, when the <see cref="TestTimer" />
/// was constructed with the <c>requireStart</c> argument set to <c>true</c>, this method needs to be invoked before
/// invoking one of the <c>Tick</c> overloads otherwise a <see cref="InvalidOperationException" /> will be thrown.
/// </remarks>
/// <param name="interval">The time interval between raising the <see cref="BaseTimer.Elapsed" /> event.</param>
public override void Start(TimeSpan interval)
=> Start();
/// <summary>
/// 'Stops' the timer.
/// </summary>
public override void Stop()
{
_enabled = false;
Interlocked.Increment(ref _stopcount);
Stopped?.Invoke(this, new StoppedEventArgs());
}
/// <summary>
/// Resets the <see cref="TickCount" />, <see cref="StartCount" /> and <see cref="StopCount" /> properties.
/// </summary>
public void Reset()
{
_tickcount = 0;
_startcount = 0;
_stopcount = 0;
}
/// <inheritdoc/>
public override bool Enabled => _enabled;
#region IDisposable
/// <inheritdoc/>
public override void Dispose() =>
//CA1816
GC.SuppressFinalize(this);
#endregion
}
/// <summary>
/// Provides data for the <see cref="TestTimer.Started" /> event.
/// </summary>
public class StartedEventArgs : EventArgs { };
/// <summary>
/// Provides data for the <see cref="TestTimer.Stopped" /> event.
/// </summary>
public class StoppedEventArgs : EventArgs { };