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.Collections.Generic;
6 using Xunit;
7 
8 namespace System.Runtime.InteropServices.WindowsRuntime.Tests
9 {
10     public class WindowsRuntimeMarshalTests
11     {
12         [Fact]
AddEventHandler_RemoveMethodHasTarget_Success()13         public void AddEventHandler_RemoveMethodHasTarget_Success()
14         {
15             bool addMethodCalled = false;
16             bool removeMethodCalled = false;
17             Delegate handler = new EventHandler(EventHandlerMethod1);
18 
19             Func<Delegate, EventRegistrationToken> addMethod = eventHandler =>
20             {
21                 Assert.False(addMethodCalled);
22                 addMethodCalled = true;
23                 Assert.Same(handler, eventHandler);
24 
25                 return new EventRegistrationToken();
26             };
27 
28             WindowsRuntimeMarshal.AddEventHandler(addMethod, token => removeMethodCalled = true, handler);
29             Assert.True(addMethodCalled);
30             Assert.False(removeMethodCalled);
31         }
32 
33         [Fact]
AddEventHandler_RemoveMethodHasNoTarget_Success()34         public void AddEventHandler_RemoveMethodHasNoTarget_Success()
35         {
36             bool removeMethodCalled = false;
37             ExpectedHandler = new EventHandler(EventHandlerMethod1);
38 
39             WindowsRuntimeMarshal.AddEventHandler(AddMethod, token => removeMethodCalled = true, ExpectedHandler);
40             Assert.True(AddMethodCalled);
41             Assert.False(removeMethodCalled);
42         }
43 
44         private static bool AddMethodCalled { get; set; }
45         private static Delegate ExpectedHandler { get; set; }
46 
AddMethod(Delegate eventHandler)47         private static EventRegistrationToken AddMethod(Delegate eventHandler)
48         {
49             Assert.False(AddMethodCalled);
50             AddMethodCalled = true;
51             Assert.Same(ExpectedHandler, eventHandler);
52 
53             return new EventRegistrationToken();
54         }
55 
56         [Fact]
AddEventHandler_NullHandler_Nop()57         public void AddEventHandler_NullHandler_Nop()
58         {
59             bool addMethodCalled = false;
60             bool removeMethodCalled = false;
61             WindowsRuntimeMarshal.AddEventHandler<Delegate>(eventHandler => {
62                 addMethodCalled = true;
63                 return new EventRegistrationToken();
64             }, token => removeMethodCalled = true, null);
65 
66             Assert.False(addMethodCalled);
67             Assert.False(removeMethodCalled);
68         }
69 
70         [Fact]
AddEventHandler_NullAddMethod_ThrowsArgumentNullException()71         public void AddEventHandler_NullAddMethod_ThrowsArgumentNullException()
72         {
73             AssertExtensions.Throws<ArgumentNullException>("addMethod", () => WindowsRuntimeMarshal.AddEventHandler(null, token => { }, new EventHandler(EventHandlerMethod1)));
74         }
75 
76         [Fact]
AddEventHandler_NullRemoveMethod_ThrowsArgumentNullException()77         public void AddEventHandler_NullRemoveMethod_ThrowsArgumentNullException()
78         {
79             AssertExtensions.Throws<ArgumentNullException>("removeMethod", () => WindowsRuntimeMarshal.AddEventHandler(eventHandler => new EventRegistrationToken(), null, new EventHandler(EventHandlerMethod1)));
80         }
81 
82         [Fact]
RemoveEventHandler_RemoveMethodHasTarget_Success()83         public void RemoveEventHandler_RemoveMethodHasTarget_Success()
84         {
85             bool removeMethodCalled = false;
86             Delegate handler = new EventHandler(EventHandlerMethod1);
87 
88             var tokenTable = new EventRegistrationTokenTable<Delegate>();
89             EventRegistrationToken addToken = tokenTable.AddEventHandler(handler);
90 
91             Action<EventRegistrationToken> removeMethod = token =>
92             {
93                 Assert.False(removeMethodCalled);
94                 removeMethodCalled = true;
95                 Assert.Equal(addToken, token);
96             };
97             WindowsRuntimeMarshal.AddEventHandler(eventHandler => addToken, removeMethod, handler);
98 
99             // Removing with the same handler but with a different method is a nop.
100             WindowsRuntimeMarshal.RemoveEventHandler(token => removeMethodCalled = true, handler);
101             Assert.False(removeMethodCalled);
102 
103             WindowsRuntimeMarshal.RemoveEventHandler(DifferentRemoveMethod, handler);
104             Assert.False(removeMethodCalled);
105 
106             // Removing with a different handler but with the same method is a nop.
107             WindowsRuntimeMarshal.RemoveEventHandler(removeMethod, new EventHandler(EventHandlerMethod2));
108             Assert.False(removeMethodCalled);
109 
110             // Removing the same handler and the same method works.
111             WindowsRuntimeMarshal.RemoveEventHandler(removeMethod, handler);
112             Assert.True(removeMethodCalled);
113         }
114 
115         [Fact]
RemoveEventHandler_RemoveMethodHasNoTarget_Success()116         public void RemoveEventHandler_RemoveMethodHasNoTarget_Success()
117         {
118             Delegate handler = new EventHandler(EventHandlerMethod1);
119 
120             var tokenTable = new EventRegistrationTokenTable<Delegate>();
121             ExpectedRemoveToken = tokenTable.AddEventHandler(handler);
122 
123             Action<EventRegistrationToken> removeMethod = RemoveMethod;
124             WindowsRuntimeMarshal.AddEventHandler(eventHandler => ExpectedRemoveToken, removeMethod, handler);
125 
126             // Removing with the same handler but with a different method is a nop.
127             WindowsRuntimeMarshal.RemoveEventHandler(token => RemoveMethodCalled = true, handler);
128             Assert.False(RemoveMethodCalled);
129 
130             WindowsRuntimeMarshal.RemoveEventHandler(DifferentRemoveMethod, handler);
131             Assert.False(RemoveMethodCalled);
132 
133             // Removing with a different handler but with the same method is a nop.
134             WindowsRuntimeMarshal.RemoveEventHandler(removeMethod, new EventHandler(EventHandlerMethod2));
135             Assert.False(RemoveMethodCalled);
136 
137             // Removing the same handler and the same method works.
138             WindowsRuntimeMarshal.RemoveEventHandler(removeMethod, handler);
139             Assert.True(RemoveMethodCalled);
140         }
141 
142         private static EventRegistrationToken ExpectedRemoveToken { get; set; }
143         private static bool RemoveMethodCalled { get; set; }
144 
RemoveMethod(EventRegistrationToken token)145         private static void RemoveMethod(EventRegistrationToken token)
146         {
147             Assert.False(RemoveMethodCalled);
148             RemoveMethodCalled = true;
149             Assert.Equal(ExpectedRemoveToken, token);
150         }
151 
152         private static void DifferentRemoveMethod(EventRegistrationToken token) => RemoveMethodCalled = true;
153 
154         [Fact]
RemoveEventHandler_NullHandler_Nop()155         public void RemoveEventHandler_NullHandler_Nop()
156         {
157             bool removeMethodCalled = false;
158             Delegate handler = new EventHandler(EventHandlerMethod1);
159 
160             var tokenTable = new EventRegistrationTokenTable<Delegate>();
161             EventRegistrationToken addToken = tokenTable.AddEventHandler(handler);
162 
163             Action<EventRegistrationToken> removeMethod = token => removeMethodCalled = true;
164             WindowsRuntimeMarshal.AddEventHandler(eventHandler => addToken, removeMethod, handler);
165 
166             WindowsRuntimeMarshal.RemoveEventHandler<Delegate>(removeMethod, null);
167             Assert.False(removeMethodCalled);
168         }
169 
170         [Fact]
RemoveEventHandler_NullRemoveMethod_ThrowsArgumentNullException()171         public void RemoveEventHandler_NullRemoveMethod_ThrowsArgumentNullException()
172         {
173             AssertExtensions.Throws<ArgumentNullException>("removeMethod", () => WindowsRuntimeMarshal.RemoveEventHandler<Delegate>(null, new EventHandler(EventHandlerMethod1)));
174         }
175 
176         [Fact]
RemoveAllEventHandlers_RemoveMethodHasTarget_Success()177         public void RemoveAllEventHandlers_RemoveMethodHasTarget_Success()
178         {
179             Delegate handler = new EventHandler(EventHandlerMethod1);
180 
181             var tokenTable = new EventRegistrationTokenTable<Delegate>();
182             EventRegistrationToken token1 = tokenTable.AddEventHandler(handler);
183             EventRegistrationToken token2 = tokenTable.AddEventHandler(handler);
184             EventRegistrationToken token3 = tokenTable.AddEventHandler(handler);
185 
186             var removedTokens = new List<EventRegistrationToken>();
187             Action<EventRegistrationToken> removeMethod = token => removedTokens.Add(token);
188 
189             WindowsRuntimeMarshal.AddEventHandler(eventHandler => token1, removeMethod, handler);
190             WindowsRuntimeMarshal.AddEventHandler(eventHandler => token2, removeMethod, handler);
191 
192             bool removeMethodWithTargetCalled = false;
193             WindowsRuntimeMarshal.AddEventHandler(eventHandler => token3, token => removeMethodWithTargetCalled = false, handler);
194 
195             // Removing with the same handler but with a different method is a nop.
196             WindowsRuntimeMarshal.RemoveAllEventHandlers(token => RemoveMethodCalled = true);
197             Assert.Empty(removedTokens);
198             Assert.False(DifferentRemoveAllMethodCalled);
199             Assert.False(removeMethodWithTargetCalled);
200 
201             WindowsRuntimeMarshal.RemoveAllEventHandlers(DifferentRemoveAllMethod);
202             Assert.Empty(removedTokens);
203             Assert.False(DifferentRemoveAllMethodCalled);
204             Assert.False(removeMethodWithTargetCalled);
205 
206             // Removing the same handler and the same method works.
207             WindowsRuntimeMarshal.RemoveAllEventHandlers(removeMethod);
208             Assert.Equal(new EventRegistrationToken[] { token1, token2 }, removedTokens);
209             Assert.False(DifferentRemoveAllMethodCalled);
210             Assert.False(removeMethodWithTargetCalled);
211         }
212 
213         [Fact]
RemoveAllEventHandlers_RemoveMethodHasNoTarget_Success()214         public void RemoveAllEventHandlers_RemoveMethodHasNoTarget_Success()
215         {
216             Delegate handler = new EventHandler(EventHandlerMethod1);
217 
218             var tokenTable = new EventRegistrationTokenTable<Delegate>();
219             EventRegistrationToken token1 = tokenTable.AddEventHandler(handler);
220             EventRegistrationToken token2 = tokenTable.AddEventHandler(handler);
221             EventRegistrationToken token3 = tokenTable.AddEventHandler(handler);
222 
223             Action<EventRegistrationToken> removeMethod = RemoveAllMethod;
224             WindowsRuntimeMarshal.AddEventHandler(eventHandler => token1, removeMethod, handler);
225             WindowsRuntimeMarshal.AddEventHandler(eventHandler => token2, removeMethod, handler);
226 
227             bool removeMethodWithTargetCalled = false;
228             WindowsRuntimeMarshal.AddEventHandler(eventHandler => token3, token => removeMethodWithTargetCalled = false, handler);
229 
230             // Removing with the same handler but with a different method is a nop.
231             WindowsRuntimeMarshal.RemoveAllEventHandlers(token => RemoveMethodCalled = true);
232             Assert.Empty(RemoveAllTokens);
233             Assert.False(DifferentRemoveAllMethodCalled);
234             Assert.False(removeMethodWithTargetCalled);
235 
236             WindowsRuntimeMarshal.RemoveAllEventHandlers(DifferentRemoveAllMethod);
237             Assert.Empty(RemoveAllTokens);
238             Assert.False(DifferentRemoveAllMethodCalled);
239             Assert.False(removeMethodWithTargetCalled);
240 
241             // Removing the same handler and the same method works.
242             WindowsRuntimeMarshal.RemoveAllEventHandlers(removeMethod);
243             Assert.Equal(new EventRegistrationToken[] { token1, token2 }, RemoveAllTokens);
244             Assert.False(DifferentRemoveAllMethodCalled);
245             Assert.False(removeMethodWithTargetCalled);
246         }
247 
248         private static List<EventRegistrationToken> RemoveAllTokens { get; } = new List<EventRegistrationToken>();
249         private static void RemoveAllMethod(EventRegistrationToken token) => RemoveAllTokens.Add(token);
250 
251         private static bool DifferentRemoveAllMethodCalled { get; set; }
252         private static void DifferentRemoveAllMethod(EventRegistrationToken token) => DifferentRemoveAllMethodCalled = true;
253 
254         [Fact]
RemoveAllEventHandlers_NullRemoveMethod_ThrowsArgumentNullException()255         public void RemoveAllEventHandlers_NullRemoveMethod_ThrowsArgumentNullException()
256         {
257             AssertExtensions.Throws<ArgumentNullException>("removeMethod", () => WindowsRuntimeMarshal.RemoveAllEventHandlers(null));
258         }
259 
260         [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsWinRTSupported))]
261         [InlineData("")]
262         [InlineData("HString")]
StringToHString_PtrToHString_ReturnsExpected(string s)263         public void StringToHString_PtrToHString_ReturnsExpected(string s)
264         {
265             IntPtr ptr = WindowsRuntimeMarshal.StringToHString(s);
266             try
267             {
268                 if (s.Length == 0)
269                 {
270                     Assert.Equal(IntPtr.Zero, ptr);
271                 }
272                 else
273                 {
274                     Assert.NotEqual(IntPtr.Zero, ptr);
275                 }
276                 Assert.Equal(s, WindowsRuntimeMarshal.PtrToStringHString(ptr));
277             }
278             finally
279             {
280                 WindowsRuntimeMarshal.FreeHString(ptr);
281             }
282         }
283 
284         [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsWinRTSupported))]
StringToHString_NullString_ThrowsArgumentNullException()285         public void StringToHString_NullString_ThrowsArgumentNullException()
286         {
287             AssertExtensions.Throws<ArgumentNullException>("s", () => WindowsRuntimeMarshal.StringToHString(null));
288         }
289 
290         [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWinRTSupported))]
StringToHString_WinRTNotSupported_ThrowsPlatformNotSupportedException()291         public void StringToHString_WinRTNotSupported_ThrowsPlatformNotSupportedException()
292         {
293             Assert.Throws<PlatformNotSupportedException>(() => WindowsRuntimeMarshal.StringToHString(null));
294         }
295 
296         [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWinRTSupported))]
PtrToStringHString_WinRTNotSupported_ThrowsPlatformNotSupportedException()297         public void PtrToStringHString_WinRTNotSupported_ThrowsPlatformNotSupportedException()
298         {
299             Assert.Throws<PlatformNotSupportedException>(() => WindowsRuntimeMarshal.PtrToStringHString(IntPtr.Zero));
300         }
301 
302         [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWinRTSupported))]
FreeHString_WinRTNotSupported_ThrowsPlatformNotSupportedException()303         public void FreeHString_WinRTNotSupported_ThrowsPlatformNotSupportedException()
304         {
305             Assert.Throws<PlatformNotSupportedException>(() => WindowsRuntimeMarshal.FreeHString(IntPtr.Zero));
306         }
307 
308         [Fact]
GetActivationFactory_NullType_ThrowsArgumentNullException()309         public void GetActivationFactory_NullType_ThrowsArgumentNullException()
310         {
311             AssertExtensions.Throws<ArgumentNullException>("type", () => WindowsRuntimeMarshal.GetActivationFactory(null));
312         }
313 
314         [Fact]
315         [SkipOnTargetFramework(TargetFrameworkMonikers.UapAot, "No reliable way to check if a type is WinRT in AOT")]
GetActivationFactory_NotExportedType_ThrowsArgumentException()316         public void GetActivationFactory_NotExportedType_ThrowsArgumentException()
317         {
318             AssertExtensions.Throws<ArgumentException>("type", () => WindowsRuntimeMarshal.GetActivationFactory(typeof(int)));
319         }
320 
EventHandlerMethod1(object sender, EventArgs e)321         private static void EventHandlerMethod1(object sender, EventArgs e) { }
EventHandlerMethod2(object sender, EventArgs e)322         private static void EventHandlerMethod2(object sender, EventArgs e) { }
323     }
324 }
325