Skip to content

Commit 23aaba6

Browse files
committed
TestTaskRunner absolute time support, custom wait
1 parent 3669217 commit 23aaba6

File tree

3 files changed

+228
-18
lines changed

3 files changed

+228
-18
lines changed

async-enumerable-dotnet-test/AmbTest.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ public async void First_Win_Sync()
105105
{
106106
var t0 = t.MoveNextAsync();
107107

108-
await ttr.TaskQueued();
108+
await ttr.TaskQueued(i * 500);
109109

110110
ttr.AdvanceTimeBy(500);
111111

@@ -143,7 +143,7 @@ public async void Second_Win_Sync()
143143
{
144144
var t0 = t.MoveNextAsync();
145145

146-
await ttr.TaskQueued();
146+
await ttr.TaskQueued((i - 5) * 500);
147147

148148
ttr.AdvanceTimeBy(500);
149149

async-enumerable-dotnet-test/TestTaskRunnerTest.cs

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// See LICENSE file in the project root for full license information.
44

55
using System;
6+
using System.Threading.Tasks;
67
using Xunit;
78
using async_enumerable_dotnet;
89

@@ -33,6 +34,25 @@ public void Simple()
3334
Assert.False(ttr.HasTasks, "more tasks?");
3435
Assert.True(t1.IsCompleted, "t1 is not completed");
3536
}
37+
38+
[Fact]
39+
public void Simple_Absolute()
40+
{
41+
var ttr = new TestTaskRunner(50);
42+
43+
Assert.Equal(50L, ttr.Now);
44+
Assert.False(ttr.HasTasks);
45+
46+
var t1 = ttr.CreateCompleteTask(100, true);
47+
48+
Assert.True(ttr.HasTasks);
49+
Assert.False(t1.IsCompleted, "t1 is completed");
50+
51+
ttr.AdvanceTimeBy(50);
52+
53+
Assert.False(ttr.HasTasks, "more tasks?");
54+
Assert.True(t1.IsCompleted, "t1 is not completed");
55+
}
3656

3757
[Fact]
3858
public void Mixed()
@@ -104,6 +124,76 @@ public void Mixed()
104124
Assert.True(t6.IsCanceled);
105125
}
106126

127+
[Fact]
128+
public void Mixed_Absolute()
129+
{
130+
var ttr = new TestTaskRunner(50);
131+
132+
var ex = new InvalidOperationException();
133+
134+
var t1 = ttr.CreateErrorTask(ex, 0, true);
135+
var t2 = ttr.CreateErrorTask<int>(ex, 100, true);
136+
137+
var t3 = ttr.CreateValueTask("hello", 100, true);
138+
var t4 = ttr.CreateValueTask("world", 200, true);
139+
140+
var t5 = ttr.CreateCancelTask(200, true);
141+
142+
var t6 = ttr.CreateCancelTask<int>(250, true);
143+
144+
// T = 50 ------------------------------------------
145+
146+
ttr.AdvanceTimeBy(0);
147+
Assert.Equal(50, ttr.Now);
148+
Assert.True(ttr.HasTasks);
149+
150+
Assert.False(t2.IsCompleted);
151+
Assert.False(t3.IsCompleted);
152+
Assert.False(t4.IsCompleted);
153+
Assert.False(t5.IsCompleted);
154+
Assert.False(t6.IsCompleted);
155+
156+
Assert.True(t1.IsFaulted);
157+
Assert.Equal(ex, t1.Exception.InnerException);
158+
159+
// T = 100 ------------------------------------------
160+
161+
ttr.AdvanceTimeBy(50);
162+
Assert.Equal(100, ttr.Now);
163+
Assert.True(ttr.HasTasks);
164+
165+
Assert.False(t4.IsCompleted);
166+
Assert.False(t5.IsCompleted);
167+
Assert.False(t6.IsCompleted);
168+
169+
Assert.True(t2.IsFaulted);
170+
Assert.Equal(ex, t2.Exception.InnerException);
171+
172+
Assert.True(t3.IsCompleted);
173+
Assert.Equal("hello", t3.Result);
174+
175+
// T = 200 ------------------------------------------
176+
177+
ttr.AdvanceTimeBy(100);
178+
Assert.Equal(200, ttr.Now);
179+
Assert.True(ttr.HasTasks);
180+
181+
Assert.False(t6.IsCompleted);
182+
183+
Assert.True(t4.IsCompleted);
184+
Assert.Equal("world", t4.Result);
185+
186+
Assert.True(t5.IsCanceled);
187+
188+
// T = 250 ------------------------------------------
189+
190+
ttr.AdvanceTimeBy(50);
191+
Assert.Equal(250, ttr.Now);
192+
Assert.False(ttr.HasTasks);
193+
194+
Assert.True(t6.IsCanceled);
195+
}
196+
107197
[Fact]
108198
public void Callback()
109199
{
@@ -145,5 +235,92 @@ public void Callback()
145235
Assert.True(t4.IsFaulted);
146236
Assert.True(t4.Exception.InnerException is InvalidOperationException);
147237
}
238+
239+
240+
[Fact]
241+
public void Callback_Absolute()
242+
{
243+
var ttr = new TestTaskRunner(50);
244+
var count = 0;
245+
246+
var t1 = ttr.CreateActionTask(() => count++, 50, true);
247+
248+
var t2 = ttr.CreateActionTask(() => throw new InvalidOperationException(), 100, true);
249+
250+
var t3 = ttr.CreateLambdaTask<long>(tcs => tcs.SetResult(ttr.Now), 150, true);
251+
var t4 = ttr.CreateLambdaTask<long>(tcs => throw new InvalidOperationException(), 150, true);
252+
253+
Assert.Equal(0, count);
254+
255+
// T = 50 ----------------------------------------------
256+
257+
ttr.AdvanceTimeBy(0);
258+
259+
Assert.True(t1.IsCompleted);
260+
Assert.Equal(1, count);
261+
262+
// T = 100 ----------------------------------------------
263+
264+
ttr.AdvanceTimeBy(50);
265+
266+
Assert.True(t2.IsFaulted);
267+
Assert.True(t2.Exception.InnerException is InvalidOperationException);
268+
Assert.False(t3.IsCompleted);
269+
Assert.False(t4.IsCompleted);
270+
271+
// T = 150 ----------------------------------------------
272+
273+
ttr.AdvanceTimeBy(50);
274+
275+
Assert.True(t3.IsCompleted);
276+
Assert.Equal(150L, t3.Result);
277+
278+
Assert.True(t4.IsFaulted);
279+
Assert.True(t4.Exception.InnerException is InvalidOperationException);
280+
}
281+
282+
[Fact]
283+
public async void RightTaskQueued()
284+
{
285+
for (var i = 0; i < 1000; i++)
286+
{
287+
var ttr = new TestTaskRunner();
288+
289+
var t1 = Task.Run(() => { ttr.CreateCompleteTask(100); });
290+
var t2 = Task.Run(() => { ttr.CreateCompleteTask(101); });
291+
292+
await ttr.TaskQueued(101);
293+
294+
await t1;
295+
296+
await t2;
297+
298+
ttr.AdvanceTimeBy(101);
299+
300+
Assert.False(ttr.HasTasks);
301+
}
302+
}
303+
304+
[Fact]
305+
public async void RightTaskQueued_Absolute()
306+
{
307+
for (var i = 0; i < 1000; i++)
308+
{
309+
var ttr = new TestTaskRunner(50);
310+
311+
var t1 = Task.Run(() => { ttr.CreateCompleteTask(100, true); });
312+
var t2 = Task.Run(() => { ttr.CreateCompleteTask(101, true); });
313+
314+
await ttr.TaskQueued(101);
315+
316+
await t1;
317+
318+
await t2;
319+
320+
ttr.AdvanceTimeBy(101);
321+
322+
Assert.False(ttr.HasTasks);
323+
}
324+
}
148325
}
149326
}

async-enumerable-dotnet/TestTaskRunner.cs

Lines changed: 49 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,31 @@ public async Task TaskQueued()
7575
await ResumeHelper.Await(ref _taskQueued);
7676
ResumeHelper.Clear(ref _taskQueued);
7777
}
78+
79+
/// <summary>
80+
/// Resumes only when a specific task with a given delay
81+
/// has been queued up.
82+
/// </summary>
83+
/// <param name="dueTime">The due time of the task that should be present.</param>
84+
/// <returns>The task to await</returns>
85+
public async Task TaskQueued(long dueTime)
86+
{
87+
for (;;)
88+
{
89+
lock (_queue)
90+
{
91+
foreach (var t in _queue.Values)
92+
{
93+
if (t.DueTime == dueTime)
94+
{
95+
return;
96+
}
97+
}
98+
}
99+
100+
await TaskQueued();
101+
}
102+
}
78103

79104
/// <summary>
80105
/// Advance the virtual time by the given amount and
@@ -136,10 +161,11 @@ private void Enqueue(TaskTask task)
136161
/// </summary>
137162
/// <param name="error">The error to signal</param>
138163
/// <param name="delayMillis">The time to delay this task.</param>
164+
/// <param name="absolute">Should the <paramref name="delayMillis"/> be interpreted as absolute time?</param>
139165
/// <returns>The task to await</returns>
140-
public Task CreateErrorTask(Exception error, long delayMillis = 0L)
166+
public Task CreateErrorTask(Exception error, long delayMillis = 0L, bool absolute = false)
141167
{
142-
var now = Volatile.Read(ref _now) + delayMillis;
168+
var now = (absolute ? 0L : Volatile.Read(ref _now)) + delayMillis;
143169
var tcs = new TaskCompletionSource<bool>();
144170
Enqueue(new ErrorTaskTask<bool>(now, tcs, error));
145171
return tcs.Task;
@@ -152,10 +178,11 @@ public Task CreateErrorTask(Exception error, long delayMillis = 0L)
152178
/// </summary>
153179
/// <param name="error">The error to signal</param>
154180
/// <param name="delayMillis">The time to delay this task.</param>
181+
/// <param name="absolute">Should the <paramref name="delayMillis"/> be interpreted as absolute time?</param>
155182
/// <returns>The task to await</returns>
156-
public Task<T> CreateErrorTask<T>(Exception error, long delayMillis = 0L)
183+
public Task<T> CreateErrorTask<T>(Exception error, long delayMillis = 0L, bool absolute = false)
157184
{
158-
var now = Volatile.Read(ref _now) + delayMillis;
185+
var now = (absolute ? 0L : Volatile.Read(ref _now)) + delayMillis;
159186
var tcs = new TaskCompletionSource<T>();
160187
Enqueue(new ErrorTaskTask<T>(now, tcs, error));
161188
return tcs.Task;
@@ -166,10 +193,11 @@ public Task<T> CreateErrorTask<T>(Exception error, long delayMillis = 0L)
166193
/// after the specified virtual time elapses
167194
/// </summary>
168195
/// <param name="delayMillis">The time to delay this task.</param>
196+
/// <param name="absolute">Should the <paramref name="delayMillis"/> be interpreted as absolute time?</param>
169197
/// <returns>The task to await</returns>
170-
public Task CreateCompleteTask(long delayMillis = 0L)
198+
public Task CreateCompleteTask(long delayMillis = 0L, bool absolute = false)
171199
{
172-
var now = Volatile.Read(ref _now) + delayMillis;
200+
var now = (absolute ? 0L : Volatile.Read(ref _now)) + delayMillis;
173201
var tcs = new TaskCompletionSource<bool>();
174202
Enqueue(new ValueTaskTask<bool>(now, tcs, true));
175203
return tcs.Task;
@@ -183,10 +211,11 @@ public Task CreateCompleteTask(long delayMillis = 0L)
183211
/// <typeparam name="T">The value type.</typeparam>
184212
/// <param name="item">The item to signal as the result.</param>
185213
/// <param name="delayMillis">The time to delay this task.</param>
214+
/// <param name="absolute">Should the <paramref name="delayMillis"/> be interpreted as absolute time?</param>
186215
/// <returns>The task to await</returns>
187-
public Task<T> CreateValueTask<T>(T item, long delayMillis = 0L)
216+
public Task<T> CreateValueTask<T>(T item, long delayMillis = 0L, bool absolute = false)
188217
{
189-
var now = Volatile.Read(ref _now) + delayMillis;
218+
var now = (absolute ? 0L : Volatile.Read(ref _now)) + delayMillis;
190219
var tcs = new TaskCompletionSource<T>();
191220
Enqueue(new ValueTaskTask<T>(now, tcs, item));
192221
return tcs.Task;
@@ -197,10 +226,11 @@ public Task<T> CreateValueTask<T>(T item, long delayMillis = 0L)
197226
/// the specified virtual time elapses.
198227
/// </summary>
199228
/// <param name="delayMillis">The time to delay this task.</param>
229+
/// <param name="absolute">Should the <paramref name="delayMillis"/> be interpreted as absolute time?</param>
200230
/// <returns>The task to await</returns>
201-
public Task CreateCancelTask(long delayMillis = 0L)
231+
public Task CreateCancelTask(long delayMillis = 0L, bool absolute = false)
202232
{
203-
var now = Volatile.Read(ref _now) + delayMillis;
233+
var now = (absolute ? 0L : Volatile.Read(ref _now)) + delayMillis;
204234
var tcs = new TaskCompletionSource<bool>();
205235
Enqueue(new CanceledTaskTask<bool>(now, tcs));
206236
return tcs.Task;
@@ -212,10 +242,11 @@ public Task CreateCancelTask(long delayMillis = 0L)
212242
/// </summary>
213243
/// <typeparam name="T">The value type.</typeparam>
214244
/// <param name="delayMillis">The time to delay this task.</param>
245+
/// <param name="absolute">Should the <paramref name="delayMillis"/> be interpreted as absolute time?</param>
215246
/// <returns>The task to await</returns>
216-
public Task<T> CreateCancelTask<T>(long delayMillis = 0L)
247+
public Task<T> CreateCancelTask<T>(long delayMillis = 0L, bool absolute = false)
217248
{
218-
var now = Volatile.Read(ref _now) + delayMillis;
249+
var now = (absolute ? 0L : Volatile.Read(ref _now)) + delayMillis;
219250
var tcs = new TaskCompletionSource<T>();
220251
Enqueue(new CanceledTaskTask<T>(now, tcs));
221252
return tcs.Task;
@@ -228,10 +259,11 @@ public Task<T> CreateCancelTask<T>(long delayMillis = 0L)
228259
/// </summary>
229260
/// <param name="action">The action to invoke before completing the task.</param>
230261
/// <param name="delayMillis">The time to delay this task.</param>
262+
/// <param name="absolute">Should the <paramref name="delayMillis"/> be interpreted as absolute time?</param>
231263
/// <returns>The task to await</returns>
232-
public Task CreateActionTask(Action action, long delayMillis = 0L)
264+
public Task CreateActionTask(Action action, long delayMillis = 0L, bool absolute = false)
233265
{
234-
var now = Volatile.Read(ref _now) + delayMillis;
266+
var now = (absolute ? 0L : Volatile.Read(ref _now)) + delayMillis;
235267
var tcs = new TaskCompletionSource<bool>();
236268
Enqueue(new ActionTaskTask(now, tcs, action));
237269
return tcs.Task;
@@ -245,10 +277,11 @@ public Task CreateActionTask(Action action, long delayMillis = 0L)
245277
/// </summary>
246278
/// <param name="action">The action to invoke.</param>
247279
/// <param name="delayMillis">The time to delay this task.</param>
280+
/// <param name="absolute">Should the <paramref name="delayMillis"/> be interpreted as absolute time?</param>
248281
/// <returns>The task to await</returns>
249-
public Task<T> CreateLambdaTask<T>(Action<TaskCompletionSource<T>> action, long delayMillis = 0L)
282+
public Task<T> CreateLambdaTask<T>(Action<TaskCompletionSource<T>> action, long delayMillis = 0L, bool absolute = false)
250283
{
251-
var now = Volatile.Read(ref _now) + delayMillis;
284+
var now = (absolute ? 0L : Volatile.Read(ref _now)) + delayMillis;
252285
var tcs = new TaskCompletionSource<T>();
253286
Enqueue(new LambdaTaskTask<T>(now, tcs, action));
254287
return tcs.Task;

0 commit comments

Comments
 (0)