-
-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathTaskWrapperStructValidationTests.cs
More file actions
121 lines (102 loc) · 4.27 KB
/
Copy pathTaskWrapperStructValidationTests.cs
File metadata and controls
121 lines (102 loc) · 4.27 KB
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
using System;
using System.Threading;
using System.Threading.Tasks;
namespace EnumerableAsyncProcessor.UnitTests;
/// <summary>
/// Simple validation tests to demonstrate TaskWrapper struct behavior and performance benefits.
/// </summary>
public class TaskWrapperStructValidationTests
{
[Test]
public async Task TaskWrapper_IsValueType_Confirmation()
{
// Verify all TaskWrapper types are now structs (value types)
await Assert.That(typeof(ActionTaskWrapper).IsValueType).IsTrue();
await Assert.That(typeof(ItemTaskWrapper<int>).IsValueType).IsTrue();
await Assert.That(typeof(ItemTaskWrapper<int, string>).IsValueType).IsTrue();
await Assert.That(typeof(ActionTaskWrapper<string>).IsValueType).IsTrue();
}
[Test]
public async Task ActionTaskWrapper_ProcessesCorrectly()
{
// Arrange
var executed = false;
var tcs = new TaskCompletionSource();
var wrapper = new ActionTaskWrapper(() =>
{
executed = true;
return Task.CompletedTask;
}, tcs);
// Act
await wrapper.Process(CancellationToken.None);
// Assert
await Assert.That(executed).IsTrue();
await Assert.That(tcs.Task.IsCompletedSuccessfully).IsTrue();
}
[Test]
public async Task ItemTaskWrapper_ProcessesWithInput()
{
// Arrange
var processedValue = 0;
var tcs = new TaskCompletionSource();
var wrapper = new ItemTaskWrapper<int>(42, value =>
{
processedValue = value;
return Task.CompletedTask;
}, tcs);
// Act
await wrapper.Process(CancellationToken.None);
// Assert
await Assert.That(processedValue).IsEqualTo(42);
await Assert.That(tcs.Task.IsCompletedSuccessfully).IsTrue();
}
[Test]
public async Task TaskWrapper_ArrayStorage_WorksWithoutBoxing()
{
// Arrange
var wrappers = new ActionTaskWrapper[5];
// Act - Fill array with structs
for (int i = 0; i < wrappers.Length; i++)
{
wrappers[i] = new ActionTaskWrapper(() => Task.CompletedTask, new TaskCompletionSource());
}
// Assert - No boxing should occur
await Assert.That(wrappers.Length).IsEqualTo(5);
var hasNonNullFactory = wrappers[0].TaskFactory != null;
await Assert.That(hasNonNullFactory).IsTrue();
}
[Test]
public async Task TaskWrapper_PassByValue_CopiesBehavior()
{
// Arrange
var originalTcs = new TaskCompletionSource();
var originalWrapper = new ActionTaskWrapper(() => Task.CompletedTask, originalTcs);
// Act - Modify copy
var copiedWrapper = originalWrapper;
copiedWrapper = new ActionTaskWrapper(() => Task.CompletedTask, new TaskCompletionSource());
// Assert - Original should remain unchanged (struct copy semantics)
await Assert.That(originalWrapper.TaskCompletionSource).IsEqualTo(originalTcs);
await Assert.That(copiedWrapper.TaskCompletionSource).IsNotEqualTo(originalTcs);
}
[Test]
public async Task TaskWrapper_MemoryFootprint_IsSmallerThanObjects()
{
// This test demonstrates that structs don't allocate on the heap for the wrapper itself
// Only the TaskCompletionSource instances are heap-allocated
// Arrange & Act
Func<Task> taskFactory = static () => Task.CompletedTask;
var initialAllocatedBytes = GC.GetAllocatedBytesForCurrentThread();
var wrappers = new ActionTaskWrapper[1000];
for (int i = 0; i < wrappers.Length; i++)
{
// Only the TaskCompletionSource allocates on the heap, not the wrapper struct
wrappers[i] = new ActionTaskWrapper(taskFactory, new TaskCompletionSource());
}
var allocatedBytes = GC.GetAllocatedBytesForCurrentThread() - initialAllocatedBytes;
// Assert - Memory allocation should be only for TaskCompletionSource instances
// Each struct itself doesn't allocate heap memory
await Assert.That(allocatedBytes).IsLessThan(wrappers.Length * 200); // Conservative estimate
var hasNonNullFactory = wrappers[0].TaskFactory != null; // Prevent optimization
await Assert.That(hasNonNullFactory).IsTrue();
}
}