-
Notifications
You must be signed in to change notification settings - Fork 855
/
TestTimeProvider.cs
149 lines (121 loc) · 3.91 KB
/
TestTimeProvider.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
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Xunit;
namespace Yarp.Tests.Common;
/// <summary>
/// Simulates passage of time, used for testing.
/// </summary>
/// <remarks>
/// This timer doesn't track real time, but instead tracks virtual time.
/// Time only advances when any of the following methods are called:
/// <list type="bullet">
/// <item><see cref="Advance"/></item>
/// <item><see cref="AdvanceTo(TimeSpan)"/></item>
/// </list>
/// </remarks>
public class TestTimeProvider : TimeProvider
{
private readonly List<TestTimer> _timers = new();
private TimeSpan _currentTime;
public int TimerCount => _timers.Count;
/// <summary>
/// Initializes a new instance of the <see cref="TestTimeProvider" /> class.
/// </summary>
/// <param name="initialTime">Initial value for current time. Zero if not specified.</param>
public TestTimeProvider(TimeSpan? initialTime = null)
{
_currentTime = initialTime ?? TimeSpan.Zero;
}
public TestTimeProvider(DateTimeOffset initialTime)
{
_currentTime = initialTime - DateTimeOffset.UnixEpoch;
}
/// <summary>
/// Advances time by the specified amount.
/// </summary>
/// <param name="howMuch">How much to advance <see cref="CurrentTime"/> by.</param>
public void Advance(TimeSpan howMuch)
{
AdvanceTo(_currentTime + howMuch);
}
/// <summary>
/// Advances time to the specified point.
/// </summary>
/// <param name="targetTime">Advances <see cref="CurrentTime"/> until it equals <paramref name="targetTime"/>.</param>
public void AdvanceTo(TimeSpan targetTime)
{
if (targetTime < _currentTime)
{
throw new InvalidOperationException("Time should not flow backwards");
}
// We could use this to fire timers, but timers are currently fired manually by tests.
_currentTime = targetTime;
}
public override DateTimeOffset GetUtcNow() => new DateTime(_currentTime.Ticks, DateTimeKind.Utc);
public override long GetTimestamp() => _currentTime.Ticks;
public override ITimer CreateTimer(TimerCallback callback, object state, TimeSpan dueTime, TimeSpan period)
{
Assert.Equal(Timeout.InfiniteTimeSpan, period);
var timer = new TestTimer(callback, state, dueTime, period);
_timers.Add(timer);
return timer;
}
public void FireTimer(int idx)
{
_timers[idx].Fire();
}
public void FireAllTimers()
{
for (var i = 0; i < _timers.Count; i++)
{
FireTimer(i);
}
}
public void VerifyTimer(int idx, TimeSpan dueTime)
{
Assert.Equal(dueTime, _timers[idx].DueTime);
}
public void AssertTimerDisposed(int idx)
{
Assert.True(_timers[idx].IsDisposed);
}
private class TestTimer : ITimer
{
public TestTimer(TimerCallback callback, object state, TimeSpan dueTime, TimeSpan period)
{
Callback = callback;
State = state;
DueTime = dueTime;
Period = period;
}
public TimeSpan DueTime { get; private set; }
public TimeSpan Period { get; private set; }
public TimerCallback Callback { get; private set; }
public object State { get; private set; }
public bool IsDisposed { get; private set; }
public bool Change(TimeSpan dueTime, TimeSpan period)
{
DueTime = dueTime;
Period = period;
return true;
}
public void Fire()
{
Callback(State);
}
public void Dispose()
{
IsDisposed = true;
}
public ValueTask DisposeAsync()
{
IsDisposed = true;
return default;
}
}
}