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