Skip to content

Commit 66bf798

Browse files
Update ConcurrentConsumingTaskCollectionCoyoteTests.cs
1 parent e69c257 commit 66bf798

File tree

1 file changed

+184
-0
lines changed

1 file changed

+184
-0
lines changed

src/WouterVanRanst.Utils.Tests/ConcurrentConsumingTaskCollectionCoyoteTests.cs

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,187 @@
1+
using Microsoft.Coyote;
2+
using Microsoft.Coyote.Actors;
3+
using Microsoft.Coyote.SystematicTesting;
4+
using System;
5+
using System.Collections.Concurrent;
6+
using System.Collections.Generic;
7+
using System.Threading.Tasks;
8+
using WouterVanRanst.Utils.Collections;
9+
10+
public class TaskCompletionBufferCoyoteTests
11+
{
12+
/// <summary>
13+
/// Tests concurrent addition of tasks and completion signaling to ensure
14+
/// all tasks are processed and no invalid operations occur.
15+
/// </summary>
16+
[Test]
17+
public static async Task TestConcurrentAddAndCompleteAdding()
18+
{
19+
var configuration = Configuration.Create().WithTestingIterations(100);
20+
var engine = TestingEngine.Create(configuration, async (runtime) =>
21+
{
22+
var buffer = new TaskCompletionBuffer<int>();
23+
var pendingTasks = new List<TaskCompletionSource<int>>();
24+
int numTasks = 10;
25+
26+
// Add tasks concurrently
27+
var producer = Task.Run(() =>
28+
{
29+
for (int i = 0; i < numTasks; i++)
30+
{
31+
var tcs = new TaskCompletionSource<int>();
32+
lock (buffer)
33+
{
34+
buffer.Add(tcs.Task);
35+
pendingTasks.Add(tcs);
36+
}
37+
}
38+
});
39+
40+
// Signal completion concurrently
41+
var completer = Task.Run(() => buffer.CompleteAdding());
42+
43+
await Task.WhenAll(producer, completer);
44+
45+
// Complete all tasks
46+
foreach (var tcs in pendingTasks)
47+
{
48+
tcs.SetResult(42);
49+
}
50+
51+
// Collect results
52+
var results = new ConcurrentBag<int>();
53+
await foreach (var task in buffer.GetConsumingEnumerable())
54+
{
55+
results.Add(await task);
56+
}
57+
58+
Assert.Equal(results.Count, numTasks);
59+
});
60+
61+
engine.Run();
62+
var testResult = engine.TestReport;
63+
64+
Assert.Equal(0, testResult.NumOfFoundBugs);
65+
}
66+
67+
///// <summary>
68+
///// Verifies that tasks added after CompleteAdding is called throw exceptions
69+
///// and that existing tasks are processed correctly.
70+
///// </summary>
71+
//[Test]
72+
//public static async Task TestAddAfterCompleteAddingThrows()
73+
//{
74+
// await RunCoyoteTest(async (buffer) =>
75+
// {
76+
// buffer.CompleteAdding();
77+
78+
// bool exceptionThrown = false;
79+
// try
80+
// {
81+
// buffer.Add(Task.FromResult(1));
82+
// }
83+
// catch (InvalidOperationException)
84+
// {
85+
// exceptionThrown = true;
86+
// }
87+
88+
// Assert(exceptionThrown, "Add after CompleteAdding should throw");
89+
// });
90+
//}
91+
92+
///// <summary>
93+
///// Ensures all tasks are processed exactly once even with multiple consumers.
94+
///// </summary>
95+
//[Test]
96+
//public static async Task TestMultipleConsumersProcessAllTasks()
97+
//{
98+
// await RunCoyoteTest(async (buffer) =>
99+
// {
100+
// int numTasks = 20;
101+
// var pendingTasks = new List<TaskCompletionSource<int>>();
102+
103+
// for (int i = 0; i < numTasks; i++)
104+
// {
105+
// var tcs = new TaskCompletionSource<int>();
106+
// buffer.Add(tcs.Task);
107+
// pendingTasks.Add(tcs);
108+
// }
109+
110+
// buffer.CompleteAdding();
111+
112+
// var results = new ConcurrentBag<int>();
113+
// var consumer1 = ConsumeAsync(buffer, results);
114+
// var consumer2 = ConsumeAsync(buffer, results);
115+
116+
// // Complete tasks in random order
117+
// var random = new Random();
118+
// while (pendingTasks.Count > 0)
119+
// {
120+
// int index = random.Next(pendingTasks.Count);
121+
// pendingTasks[index].SetResult(pendingTasks.Count);
122+
// pendingTasks.RemoveAt(index);
123+
// }
124+
125+
// await Task.WhenAll(consumer1, consumer2);
126+
127+
// Assert(results.Count == numTasks, $"Expected {numTasks} results, got {results.Count}");
128+
// });
129+
//}
130+
131+
///// <summary>
132+
///// Validates that the buffer handles the completion order correctly when
133+
///// tasks complete before being awaited.
134+
///// </summary>
135+
//[Test]
136+
//public static async Task TestCompletionOrderWithControlledTasks()
137+
//{
138+
// await RunCoyoteTest(async (buffer) =>
139+
// {
140+
// var tcs1 = new TaskCompletionSource<int>();
141+
// var tcs2 = new TaskCompletionSource<int>();
142+
143+
// buffer.Add(tcs2.Task); // This task completes first
144+
// buffer.Add(tcs1.Task);
145+
146+
// tcs2.SetResult(2);
147+
// tcs1.SetResult(1);
148+
// buffer.CompleteAdding();
149+
150+
// var results = new List<int>();
151+
// await foreach (var task in buffer.GetConsumingEnumerable())
152+
// {
153+
// results.Add(await task);
154+
// }
155+
156+
// Assert(results.Count == 2);
157+
// Assert(results[0] == 2, "First completed task should be result 2");
158+
// Assert(results[1] == 1, "Second completed task should be result 1");
159+
// });
160+
//}
161+
162+
//// Helper methods
163+
//private static async Task RunCoyoteTest(Func<TaskCompletionBuffer<int>, Task> testFunc)
164+
//{
165+
// var config = Configuration.Create().WithTestingIterations(100);
166+
// var testResult = await TestingEngine.Execute(config, async (runtime) =>
167+
// {
168+
// var buffer = new TaskCompletionBuffer<int>();
169+
// await testFunc(buffer);
170+
// });
171+
172+
// Assert(testResult.NumOfFoundBugs == 0);
173+
//}
174+
175+
//private static async Task ConsumeAsync(TaskCompletionBuffer<int> buffer, ConcurrentBag<int> results)
176+
//{
177+
// await foreach (var task in buffer.GetConsumingEnumerable())
178+
// {
179+
// results.Add(await task);
180+
// }
181+
//}
182+
}
183+
184+
1185
//using FluentAssertions;
2186
//using Microsoft.Coyote;
3187
//using Microsoft.Coyote.Actors;

0 commit comments

Comments
 (0)