Skip to content

Commit 8e7c48f

Browse files
committed
Move MVVM Toolkit internals tests to separate project
1 parent af03bdf commit 8e7c48f

File tree

7 files changed

+281
-196
lines changed

7 files changed

+281
-196
lines changed

CommunityToolkit.Mvvm/Properties/AssemblyInfo.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@
44

55
using System.Runtime.CompilerServices;
66

7-
[assembly: InternalsVisibleTo("CommunityToolkit.Mvvm.UnitTests")]
7+
[assembly: InternalsVisibleTo("CommunityToolkit.Mvvm.Internals.UnitTests")]

dotnet Community Toolkit.sln

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tools", "Tools", "{11CB82CB
7070
EndProject
7171
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Configuration", "Configuration", "{6640D447-C28D-4DBB-91F4-3ADCE0CA64AD}"
7272
EndProject
73-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CommunityToolkit.Mvvm.DisableINotifyPropertyChanging.UnitTests", "tests\CommunityToolkit.Mvvm.DisableINotifyPropertyChanging.UnitTests\CommunityToolkit.Mvvm.DisableINotifyPropertyChanging.UnitTests.csproj", "{9E09DA49-4389-4ECE-8B68-EBDB1221DA90}"
73+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CommunityToolkit.Mvvm.DisableINotifyPropertyChanging.UnitTests", "tests\CommunityToolkit.Mvvm.DisableINotifyPropertyChanging.UnitTests\CommunityToolkit.Mvvm.DisableINotifyPropertyChanging.UnitTests.csproj", "{9E09DA49-4389-4ECE-8B68-EBDB1221DA90}"
74+
EndProject
75+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CommunityToolkit.Mvvm.Internals.UnitTests", "tests\CommunityToolkit.Mvvm.Internals.UnitTests\CommunityToolkit.Mvvm.Internals.UnitTests.csproj", "{743D74BA-12AE-4639-AD77-B9DDA9C03255}"
7476
EndProject
7577
Global
7678
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -326,6 +328,26 @@ Global
326328
{9E09DA49-4389-4ECE-8B68-EBDB1221DA90}.Release|x64.Build.0 = Release|Any CPU
327329
{9E09DA49-4389-4ECE-8B68-EBDB1221DA90}.Release|x86.ActiveCfg = Release|Any CPU
328330
{9E09DA49-4389-4ECE-8B68-EBDB1221DA90}.Release|x86.Build.0 = Release|Any CPU
331+
{743D74BA-12AE-4639-AD77-B9DDA9C03255}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
332+
{743D74BA-12AE-4639-AD77-B9DDA9C03255}.Debug|Any CPU.Build.0 = Debug|Any CPU
333+
{743D74BA-12AE-4639-AD77-B9DDA9C03255}.Debug|ARM.ActiveCfg = Debug|Any CPU
334+
{743D74BA-12AE-4639-AD77-B9DDA9C03255}.Debug|ARM.Build.0 = Debug|Any CPU
335+
{743D74BA-12AE-4639-AD77-B9DDA9C03255}.Debug|ARM64.ActiveCfg = Debug|Any CPU
336+
{743D74BA-12AE-4639-AD77-B9DDA9C03255}.Debug|ARM64.Build.0 = Debug|Any CPU
337+
{743D74BA-12AE-4639-AD77-B9DDA9C03255}.Debug|x64.ActiveCfg = Debug|Any CPU
338+
{743D74BA-12AE-4639-AD77-B9DDA9C03255}.Debug|x64.Build.0 = Debug|Any CPU
339+
{743D74BA-12AE-4639-AD77-B9DDA9C03255}.Debug|x86.ActiveCfg = Debug|Any CPU
340+
{743D74BA-12AE-4639-AD77-B9DDA9C03255}.Debug|x86.Build.0 = Debug|Any CPU
341+
{743D74BA-12AE-4639-AD77-B9DDA9C03255}.Release|Any CPU.ActiveCfg = Release|Any CPU
342+
{743D74BA-12AE-4639-AD77-B9DDA9C03255}.Release|Any CPU.Build.0 = Release|Any CPU
343+
{743D74BA-12AE-4639-AD77-B9DDA9C03255}.Release|ARM.ActiveCfg = Release|Any CPU
344+
{743D74BA-12AE-4639-AD77-B9DDA9C03255}.Release|ARM.Build.0 = Release|Any CPU
345+
{743D74BA-12AE-4639-AD77-B9DDA9C03255}.Release|ARM64.ActiveCfg = Release|Any CPU
346+
{743D74BA-12AE-4639-AD77-B9DDA9C03255}.Release|ARM64.Build.0 = Release|Any CPU
347+
{743D74BA-12AE-4639-AD77-B9DDA9C03255}.Release|x64.ActiveCfg = Release|Any CPU
348+
{743D74BA-12AE-4639-AD77-B9DDA9C03255}.Release|x64.Build.0 = Release|Any CPU
349+
{743D74BA-12AE-4639-AD77-B9DDA9C03255}.Release|x86.ActiveCfg = Release|Any CPU
350+
{743D74BA-12AE-4639-AD77-B9DDA9C03255}.Release|x86.Build.0 = Release|Any CPU
329351
EndGlobalSection
330352
GlobalSection(SolutionProperties) = preSolution
331353
HideSolutionNode = FALSE
@@ -342,6 +364,7 @@ Global
342364
{11CB82CB-F72A-4690-9C0F-0AD5303C79B6} = {CD16E790-7B7B-411E-9CE7-768E759CC22D}
343365
{6640D447-C28D-4DBB-91F4-3ADCE0CA64AD} = {B30036C4-D514-4E5B-A323-587A061772CE}
344366
{9E09DA49-4389-4ECE-8B68-EBDB1221DA90} = {6640D447-C28D-4DBB-91F4-3ADCE0CA64AD}
367+
{743D74BA-12AE-4639-AD77-B9DDA9C03255} = {B30036C4-D514-4E5B-A323-587A061772CE}
345368
EndGlobalSection
346369
GlobalSection(ExtensibilityGlobals) = postSolution
347370
SolutionGuid = {5403B0C4-F244-4F73-A35C-FE664D0F4345}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFrameworks>net472;netcoreapp3.1;net6.0</TargetFrameworks>
5+
</PropertyGroup>
6+
7+
<ItemGroup>
8+
<PackageReference Include="MSTest.TestAdapter" Version="2.2.7" />
9+
<PackageReference Include="MSTest.TestFramework" Version="2.2.7" />
10+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
11+
</ItemGroup>
12+
13+
<ItemGroup>
14+
<ProjectReference Include="..\..\CommunityToolkit.Mvvm\CommunityToolkit.Mvvm.csproj" />
15+
</ItemGroup>
16+
17+
</Project>

tests/CommunityToolkit.Mvvm.UnitTests/Internals/Test_ConditionalWeakTable2.cs renamed to tests/CommunityToolkit.Mvvm.Internals.UnitTests/Test_ConditionalWeakTable2.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,9 @@
99
using System.Linq;
1010
using System.Runtime.CompilerServices;
1111
using System.Threading.Tasks;
12-
using CommunityToolkit.Mvvm.Messaging.Internals;
1312
using Microsoft.VisualStudio.TestTools.UnitTesting;
1413

15-
namespace CommunityToolkit.Mvvm.UnitTests.Internals;
14+
namespace CommunityToolkit.Mvvm.Internals.UnitTests;
1615

1716
[TestClass]
1817
public class Test_ConditionalWeakTable2

tests/CommunityToolkit.Mvvm.UnitTests/Internals/Test_Dictionary2.cs renamed to tests/CommunityToolkit.Mvvm.Internals.UnitTests/Test_Dictionary2.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
using System.Runtime.CompilerServices;
99
using Microsoft.VisualStudio.TestTools.UnitTesting;
1010

11-
namespace CommunityToolkit.Mvvm.UnitTests.Internals;
11+
namespace CommunityToolkit.Mvvm.Internals.UnitTests;
1212

1313
[TestClass]
1414
public class Test_Dictionary2
Lines changed: 237 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
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.Collections.Generic;
7+
using System.Reflection;
8+
using CommunityToolkit.Mvvm.Messaging;
9+
using CommunityToolkit.Mvvm.Messaging.Internals;
10+
using Microsoft.VisualStudio.TestTools.UnitTesting;
11+
12+
namespace CommunityToolkit.Mvvm.Internals.UnitTests;
13+
14+
[TestClass]
15+
public partial class Test_Messenger
16+
{
17+
#if NETCOREAPP // Auto-trimming is disabled on .NET Framework
18+
[TestMethod]
19+
public void Test_WeakReferenceMessenger_AutoCleanup()
20+
{
21+
IMessenger messenger = new WeakReferenceMessenger();
22+
23+
static int GetRecipientsMapCount(IMessenger messenger)
24+
{
25+
object recipientsMap =
26+
typeof(WeakReferenceMessenger)
27+
.GetField("recipientsMap", BindingFlags.Instance | BindingFlags.NonPublic)!
28+
.GetValue(messenger)!;
29+
30+
return (int)recipientsMap.GetType().GetProperty("Count")!.GetValue(recipientsMap)!;
31+
}
32+
33+
WeakReference weakRecipient;
34+
35+
void Test()
36+
{
37+
RecipientWithSomeMessages? recipient = new();
38+
weakRecipient = new WeakReference(recipient);
39+
40+
messenger.Register<MessageA>(recipient);
41+
42+
Assert.IsTrue(messenger.IsRegistered<MessageA>(recipient));
43+
44+
Assert.AreEqual(GetRecipientsMapCount(messenger), 1);
45+
46+
GC.KeepAlive(recipient);
47+
}
48+
49+
Test();
50+
51+
GC.Collect();
52+
53+
Assert.IsFalse(weakRecipient.IsAlive);
54+
55+
// Now that the recipient is collected, trigger another full GC collection
56+
// to let the automatic cleanup callback run and trim the messenger data
57+
GC.Collect();
58+
GC.WaitForPendingFinalizers();
59+
60+
Assert.AreEqual(GetRecipientsMapCount(messenger), 0);
61+
62+
GC.KeepAlive(messenger);
63+
}
64+
#endif
65+
66+
[TestMethod]
67+
public void Test_StrongReferenceMessenger_AutoTrimming_UnregisterAll()
68+
{
69+
StrongReferenceMessenger messenger = new();
70+
IDictionary2 recipientsMap = (IDictionary2)typeof(StrongReferenceMessenger).GetField("recipientsMap", BindingFlags.NonPublic | BindingFlags.Instance)!.GetValue(messenger)!;
71+
IDictionary2<Type2, object> typesMap = (IDictionary2<Type2, object>)typeof(StrongReferenceMessenger).GetField("typesMap", BindingFlags.NonPublic | BindingFlags.Instance)!.GetValue(messenger)!;
72+
73+
RecipientWithSomeMessages recipientA = new();
74+
RecipientWithSomeMessages recipientB = new();
75+
76+
messenger.Register<MessageA>(recipientA);
77+
78+
Assert.IsTrue(messenger.IsRegistered<MessageA>(recipientA));
79+
80+
// There's one registered handler
81+
Assert.AreEqual(1, recipientsMap.Count);
82+
Assert.AreEqual(1, typesMap.Count);
83+
84+
messenger.Register<MessageB>(recipientA);
85+
86+
Assert.IsTrue(messenger.IsRegistered<MessageB>(recipientA));
87+
88+
// There's two message types, for the same recipient
89+
Assert.AreEqual(1, recipientsMap.Count);
90+
Assert.AreEqual(2, typesMap.Count);
91+
92+
messenger.Register<MessageA>(recipientB);
93+
94+
Assert.IsTrue(messenger.IsRegistered<MessageA>(recipientB));
95+
96+
// Now there's two recipients too
97+
Assert.AreEqual(2, recipientsMap.Count);
98+
Assert.AreEqual(2, typesMap.Count);
99+
100+
messenger.UnregisterAll(recipientA);
101+
102+
Assert.IsFalse(messenger.IsRegistered<MessageA>(recipientA));
103+
Assert.IsFalse(messenger.IsRegistered<MessageB>(recipientA));
104+
105+
// Only the second recipient is left, which has a single message type
106+
Assert.AreEqual(1, recipientsMap.Count);
107+
Assert.AreEqual(1, typesMap.Count);
108+
109+
messenger.UnregisterAll(recipientB);
110+
111+
Assert.IsFalse(messenger.IsRegistered<MessageB>(recipientA));
112+
113+
// Now both lists should be empty
114+
Assert.AreEqual(0, recipientsMap.Count);
115+
Assert.AreEqual(0, typesMap.Count);
116+
}
117+
118+
[TestMethod]
119+
public void Test_StrongReferenceMessenger_AutoTrimming_UnregisterAllWithToken()
120+
{
121+
StrongReferenceMessenger messenger = new();
122+
IDictionary2 recipientsMap = (IDictionary2)typeof(StrongReferenceMessenger).GetField("recipientsMap", BindingFlags.NonPublic | BindingFlags.Instance)!.GetValue(messenger)!;
123+
IDictionary2<Type2, object> typesMap = (IDictionary2<Type2, object>)typeof(StrongReferenceMessenger).GetField("typesMap", BindingFlags.NonPublic | BindingFlags.Instance)!.GetValue(messenger)!;
124+
125+
RecipientWithSomeMessages recipientA = new();
126+
RecipientWithSomeMessages recipientB = new();
127+
128+
messenger.Register<MessageA, int>(recipientA, 42);
129+
messenger.Register<MessageB, int>(recipientA, 42);
130+
messenger.Register<MessageA, int>(recipientB, 42);
131+
132+
Assert.IsTrue(messenger.IsRegistered<MessageA, int>(recipientA, 42));
133+
Assert.IsTrue(messenger.IsRegistered<MessageB, int>(recipientA, 42));
134+
Assert.IsTrue(messenger.IsRegistered<MessageA, int>(recipientB, 42));
135+
136+
// There are two recipients, for two message types
137+
Assert.AreEqual(2, recipientsMap.Count);
138+
Assert.AreEqual(2, typesMap.Count);
139+
140+
messenger.UnregisterAll(recipientA, 42);
141+
142+
Assert.IsFalse(messenger.IsRegistered<MessageA, int>(recipientA, 42));
143+
Assert.IsFalse(messenger.IsRegistered<MessageB, int>(recipientA, 42));
144+
145+
// Only the second recipient is left again
146+
Assert.AreEqual(1, recipientsMap.Count);
147+
Assert.AreEqual(1, typesMap.Count);
148+
149+
messenger.UnregisterAll(recipientB, 42);
150+
151+
Assert.IsFalse(messenger.IsRegistered<MessageA, int>(recipientB, 42));
152+
153+
// Now both lists should be empty
154+
Assert.AreEqual(0, recipientsMap.Count);
155+
Assert.AreEqual(0, typesMap.Count);
156+
}
157+
158+
[TestMethod]
159+
public void Test_StrongReferenceMessenger_AutoTrimming_UnregisterAllWithMessageTypeAndToken()
160+
{
161+
StrongReferenceMessenger messenger = new();
162+
IDictionary2 recipientsMap = (IDictionary2)typeof(StrongReferenceMessenger).GetField("recipientsMap", BindingFlags.NonPublic | BindingFlags.Instance)!.GetValue(messenger)!;
163+
IDictionary2<Type2, object> typesMap = (IDictionary2<Type2, object>)typeof(StrongReferenceMessenger).GetField("typesMap", BindingFlags.NonPublic | BindingFlags.Instance)!.GetValue(messenger)!;
164+
165+
RecipientWithSomeMessages recipientA = new();
166+
RecipientWithSomeMessages recipientB = new();
167+
168+
messenger.Register<MessageA, int>(recipientA, 42);
169+
messenger.Register<MessageB, int>(recipientA, 42);
170+
messenger.Register<MessageA, int>(recipientB, 42);
171+
172+
messenger.Unregister<MessageA, int>(recipientA, 42);
173+
174+
Assert.IsFalse(messenger.IsRegistered<MessageA, int>(recipientA, 42));
175+
Assert.IsTrue(messenger.IsRegistered<MessageB, int>(recipientA, 42));
176+
Assert.IsTrue(messenger.IsRegistered<MessageA, int>(recipientB, 42));
177+
178+
// First recipient is still subscribed to MessageB.
179+
// The second one has a single subscription to MessageA.
180+
Assert.AreEqual(2, recipientsMap.Count);
181+
Assert.AreEqual(2, typesMap.Count);
182+
183+
messenger.Unregister<MessageB, int>(recipientA, 42);
184+
185+
Assert.IsFalse(messenger.IsRegistered<MessageA, int>(recipientA, 42));
186+
Assert.IsFalse(messenger.IsRegistered<MessageB, int>(recipientA, 42));
187+
Assert.IsTrue(messenger.IsRegistered<MessageA, int>(recipientB, 42));
188+
189+
// Only the second recipient is left
190+
Assert.AreEqual(1, recipientsMap.Count);
191+
Assert.AreEqual(1, typesMap.Count);
192+
193+
messenger.Unregister<MessageA, int>(recipientB, 42);
194+
195+
Assert.IsFalse(messenger.IsRegistered<MessageA, int>(recipientA, 42));
196+
Assert.IsFalse(messenger.IsRegistered<MessageB, int>(recipientA, 42));
197+
Assert.IsFalse(messenger.IsRegistered<MessageA, int>(recipientB, 42));
198+
199+
// Now both lists should be empty
200+
Assert.AreEqual(0, recipientsMap.Count);
201+
Assert.AreEqual(0, typesMap.Count);
202+
}
203+
204+
public sealed class RecipientWithSomeMessages :
205+
IRecipient<MessageA>,
206+
IRecipient<MessageB>,
207+
ICloneable
208+
{
209+
public int As { get; private set; }
210+
211+
public int Bs { get; private set; }
212+
213+
public void Receive(MessageA message)
214+
{
215+
As++;
216+
}
217+
218+
public void Receive(MessageB message)
219+
{
220+
Bs++;
221+
}
222+
223+
// We also add the ICloneable interface to test that the message
224+
// interfaces are all handled correctly even when inteleaved
225+
// by other unrelated interfaces in the type declaration.
226+
public object Clone() => throw new NotImplementedException();
227+
}
228+
229+
public sealed class MessageA
230+
{
231+
public string? Text { get; set; }
232+
}
233+
234+
public sealed class MessageB
235+
{
236+
}
237+
}

0 commit comments

Comments
 (0)