Skip to content

Commit f0ded9e

Browse files
committed
Adds Unit Tests and reverts IDisposable pattern
1 parent 28394c5 commit f0ded9e

File tree

4 files changed

+146
-62
lines changed

4 files changed

+146
-62
lines changed

Microsoft.Toolkit/Deferred/DeferredEventArgs.cs

Lines changed: 4 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,15 @@
44

55
using System;
66
using System.ComponentModel;
7-
using System.Runtime.CompilerServices;
7+
8+
#pragma warning disable CA1001
89

910
namespace Microsoft.Toolkit.Deferred
1011
{
1112
/// <summary>
1213
/// <see cref="EventArgs"/> which can retrieve a <see cref="EventDeferral"/> in order to process data asynchronously before an <see cref="EventHandler"/> completes and returns to the calling control.
1314
/// </summary>
14-
public class DeferredEventArgs : EventArgs, IDisposable
15+
public class DeferredEventArgs : EventArgs
1516
{
1617
/// <summary>
1718
/// Gets a new <see cref="DeferredEventArgs"/> to use in cases where no <see cref="EventArgs"/> wish to be provided.
@@ -21,12 +22,6 @@ public class DeferredEventArgs : EventArgs, IDisposable
2122
private readonly object _eventDeferralLock = new object();
2223

2324
private EventDeferral? _eventDeferral;
24-
private bool _disposed;
25-
26-
/// <summary>
27-
/// Finalizes an instance of the <see cref="DeferredEventArgs"/> class.
28-
/// </summary>
29-
~DeferredEventArgs() => Dispose(false);
3025

3126
/// <summary>
3227
/// Returns an <see cref="EventDeferral"/> which can be completed when deferred event is ready to continue.
@@ -36,7 +31,7 @@ public EventDeferral GetDeferral()
3631
{
3732
lock (_eventDeferralLock)
3833
{
39-
return _eventDeferral ?? (_eventDeferral = new EventDeferral());
34+
return _eventDeferral ??= new EventDeferral();
4035
}
4136
}
4237

@@ -61,31 +56,5 @@ public EventDeferral GetDeferral()
6156
return eventDeferral;
6257
}
6358
}
64-
65-
/// <summary>
66-
/// Recommended helper method pattern for <see cref="IDisposable"/>.
67-
/// </summary>
68-
/// <param name="disposing">Source of dispose request.</param>
69-
protected virtual void Dispose(bool disposing)
70-
{
71-
if (_disposed)
72-
{
73-
return;
74-
}
75-
76-
_eventDeferral?.Dispose();
77-
78-
_disposed = true;
79-
}
80-
81-
/// <inheritdoc/>
82-
public void Dispose()
83-
{
84-
// Dispose of unmanaged resources.
85-
Dispose(true);
86-
87-
// Suppress finalization.
88-
GC.SuppressFinalize(this);
89-
}
9059
}
9160
}

Microsoft.Toolkit/Deferred/EventDeferral.cs

Lines changed: 3 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
using System.Threading;
88
using System.Threading.Tasks;
99

10+
#pragma warning disable CA1063
11+
1012
namespace Microsoft.Toolkit.Deferred
1113
{
1214
/// <summary>
@@ -16,17 +18,11 @@ public class EventDeferral : IDisposable
1618
{
1719
//// TODO: If/when .NET 5 is base, we can upgrade to non-generic version
1820
private readonly TaskCompletionSource<object?> _taskCompletionSource = new TaskCompletionSource<object?>();
19-
private bool _disposed = false;
2021

2122
internal EventDeferral()
2223
{
2324
}
2425

25-
/// <summary>
26-
/// Finalizes an instance of the <see cref="EventDeferral"/> class.
27-
/// </summary>
28-
~EventDeferral() => Dispose(false);
29-
3026
/// <summary>
3127
/// Call when finished with the Deferral.
3228
/// </summary>
@@ -50,30 +46,10 @@ public async Task WaitForCompletion(CancellationToken cancellationToken)
5046
}
5147
}
5248

53-
/// <summary>
54-
/// Recommended helper method pattern for <see cref="IDisposable"/>.
55-
/// </summary>
56-
/// <param name="disposing">Source of dispose request.</param>
57-
protected virtual void Dispose(bool disposing)
58-
{
59-
if (_disposed)
60-
{
61-
return;
62-
}
63-
64-
Complete();
65-
66-
_disposed = true;
67-
}
68-
6949
/// <inheritdoc/>
7050
public void Dispose()
7151
{
72-
// Dispose of unmanaged resources.
73-
Dispose(true);
74-
75-
// Suppress finalization.
76-
GC.SuppressFinalize(this);
52+
Complete();
7753
}
7854
}
7955
}
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using System;
6+
using System.Threading.Tasks;
7+
using Microsoft.Toolkit.Deferred;
8+
using Microsoft.VisualStudio.TestTools.UnitTesting;
9+
10+
namespace UnitTests.Extensions
11+
{
12+
[TestClass]
13+
public class Test_EventHandlerExtensions
14+
{
15+
[TestCategory("Deferred")]
16+
[TestMethod]
17+
public void Test_EventHandlerExtensions_GettingDeferralCausesAwait()
18+
{
19+
var tsc = new TaskCompletionSource<bool>();
20+
21+
var testClass = new TestClass();
22+
23+
testClass.TestEvent += async (s, e) =>
24+
{
25+
var deferral = e.GetDeferral();
26+
27+
await tsc.Task;
28+
29+
deferral.Complete();
30+
};
31+
32+
var handlersTask = testClass.RaiseTestEvent();
33+
34+
Assert.IsFalse(handlersTask.IsCompleted);
35+
36+
tsc.SetResult(true);
37+
38+
Assert.IsTrue(handlersTask.IsCompleted);
39+
}
40+
41+
[TestCategory("Deferred")]
42+
[TestMethod]
43+
public void Test_EventHandlerExtensions_NotGettingDeferralCausesNoAwait()
44+
{
45+
var tsc = new TaskCompletionSource<bool>();
46+
47+
var testClass = new TestClass();
48+
49+
testClass.TestEvent += async (s, e) =>
50+
{
51+
await tsc.Task;
52+
};
53+
54+
var handlersTask = testClass.RaiseTestEvent();
55+
56+
Assert.IsTrue(handlersTask.IsCompleted);
57+
58+
tsc.SetResult(true);
59+
}
60+
61+
[TestCategory("Deferred")]
62+
[TestMethod]
63+
public void Test_EventHandlerExtensions_UsingDeferralCausesAwait()
64+
{
65+
var tsc = new TaskCompletionSource<bool>();
66+
67+
var testClass = new TestClass();
68+
69+
testClass.TestEvent += async (s, e) =>
70+
{
71+
using (e.GetDeferral())
72+
{
73+
await tsc.Task;
74+
}
75+
};
76+
77+
var handlersTask = testClass.RaiseTestEvent();
78+
79+
Assert.IsFalse(handlersTask.IsCompleted);
80+
81+
tsc.SetResult(true);
82+
83+
Assert.IsTrue(handlersTask.IsCompleted);
84+
}
85+
86+
[TestCategory("Deferred")]
87+
[DataTestMethod]
88+
[DataRow(0, 1)]
89+
[DataRow(1, 0)]
90+
public void Test_EventHandlerExtensions_MultipleHandlersCauseAwait(int firstToReleaseDeferral, int lastToReleaseDeferral)
91+
{
92+
var tsc = new[]
93+
{
94+
new TaskCompletionSource<bool>(),
95+
new TaskCompletionSource<bool>()
96+
};
97+
98+
var testClass = new TestClass();
99+
100+
testClass.TestEvent += async (s, e) =>
101+
{
102+
var deferral = e.GetDeferral();
103+
104+
await tsc[0].Task;
105+
106+
deferral.Complete();
107+
};
108+
109+
testClass.TestEvent += async (s, e) =>
110+
{
111+
var deferral = e.GetDeferral();
112+
113+
await tsc[1].Task;
114+
115+
deferral.Complete();
116+
};
117+
118+
var handlersTask = testClass.RaiseTestEvent();
119+
120+
Assert.IsFalse(handlersTask.IsCompleted);
121+
122+
tsc[firstToReleaseDeferral].SetResult(true);
123+
124+
Assert.IsFalse(handlersTask.IsCompleted);
125+
126+
tsc[lastToReleaseDeferral].SetResult(true);
127+
128+
Assert.IsTrue(handlersTask.IsCompleted);
129+
}
130+
131+
private class TestClass
132+
{
133+
public event EventHandler<DeferredEventArgs> TestEvent;
134+
135+
public Task RaiseTestEvent() => TestEvent.InvokeAsync(this, new DeferredEventArgs());
136+
}
137+
}
138+
}

UnitTests/UnitTests.Shared/UnitTests.Shared.projitems

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
<Compile Include="$(MSBuildThisFileDirectory)Diagnostics\Test_Guard.Array.cs" />
2020
<Compile Include="$(MSBuildThisFileDirectory)Diagnostics\Test_Guard.Comparable.Numeric.cs" />
2121
<Compile Include="$(MSBuildThisFileDirectory)Diagnostics\Test_Guard.cs" />
22+
<Compile Include="$(MSBuildThisFileDirectory)Extensions\Test_EventHandlerExtensions.cs" />
2223
<Compile Include="$(MSBuildThisFileDirectory)Extensions\Test_TaskExtensions.cs" />
2324
<Compile Include="$(MSBuildThisFileDirectory)Extensions\Test_ArrayExtensions.cs" />
2425
<Compile Include="$(MSBuildThisFileDirectory)Extensions\Test_TypeExtensions.cs" />

0 commit comments

Comments
 (0)