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.Runtime.InteropServices.WindowsRuntime; 7 using System.Threading; 8 using Windows.Foundation; 9 using Windows.Storage.Streams; 10 using Xunit; 11 12 namespace System.IO 13 { 14 public class AsWinRTStreamTests 15 { 16 [Fact] AsInputStream_FromReadOnlyStream()17 public static void AsInputStream_FromReadOnlyStream() 18 { 19 Stream managedStream = TestStreamProvider.CreateReadOnlyStream(); 20 using (IInputStream ins = managedStream.AsInputStream()) 21 { 22 Assert.NotNull(ins); 23 24 // Adapting a read-only managed Stream to IOutputStream must throw a NotSupportedException 25 Assert.Throws<NotSupportedException>(() => { IOutputStream outs = managedStream.AsOutputStream(); }); 26 } 27 } 28 29 [Fact] AsOutputStream_FromWriteOnlyStream()30 public static void AsOutputStream_FromWriteOnlyStream() 31 { 32 Stream managedStream = TestStreamProvider.CreateWriteOnlyStream(); 33 using (IOutputStream outs = managedStream.AsOutputStream()) 34 { 35 Assert.NotNull(outs); 36 37 // Adapting a write-only managed Stream to IInputStream must throw a NotSupportedException 38 Assert.Throws<NotSupportedException>(() => { IInputStream ins = managedStream.AsInputStream(); }); 39 } 40 } 41 42 [Fact] AsInputStream_WrapsToSameInstance()43 public static void AsInputStream_WrapsToSameInstance() 44 { 45 Stream managedStream = TestStreamProvider.CreateReadOnlyStream(); 46 using (IInputStream ins = managedStream.AsInputStream()) 47 { 48 Assert.NotNull(ins); 49 Assert.Same(ins, managedStream.AsInputStream()); 50 } 51 } 52 53 [Fact] AsOutputStream_WrapsToSameInstance()54 public static void AsOutputStream_WrapsToSameInstance() 55 { 56 Stream managedStream = TestStreamProvider.CreateWriteOnlyStream(); 57 using (IOutputStream outs = managedStream.AsOutputStream()) 58 { 59 Assert.NotNull(outs); 60 Assert.Same(outs, managedStream.AsOutputStream()); 61 } 62 } 63 64 [Fact] AsInputStream_RoundtripUnwrap()65 public static void AsInputStream_RoundtripUnwrap() 66 { 67 // NetFx Stream -> IInputStream -> NetFx Stream -> roundtrip reference equality is preserved 68 Stream managedStream = TestStreamProvider.CreateReadOnlyStream(); 69 using (IInputStream ins = managedStream.AsInputStream()) 70 { 71 Assert.Same(managedStream, ins.AsStreamForRead()); 72 } 73 } 74 75 [Fact] AsOutputStream_RoundtripUnwrap()76 public static void AsOutputStream_RoundtripUnwrap() 77 { 78 // NetFx Stream -> IOutputStream -> NetFx Stream -> roundtrip reference equality is preserved 79 Stream managedStream = TestStreamProvider.CreateWriteOnlyStream(); 80 using (IOutputStream outs = managedStream.AsOutputStream()) 81 { 82 Assert.Same(managedStream, outs.AsStreamForWrite()); 83 } 84 } 85 86 [Fact] AsInputStream_Equal()87 public static void AsInputStream_Equal() 88 { 89 Stream stream = TestStreamProvider.CreateReadOnlyStream(); 90 91 using (IInputStream insOne = stream.AsInputStream()) 92 using (IInputStream insTwo = stream.AsInputStream()) 93 { 94 Assert.Equal(insOne, insTwo); 95 } 96 } 97 98 [Fact] AsInputStream_NotEqual()99 public static void AsInputStream_NotEqual() 100 { 101 Stream streamOne = TestStreamProvider.CreateReadOnlyStream(); 102 Stream streamTwo = TestStreamProvider.CreateReadOnlyStream(); 103 104 Assert.NotEqual(streamOne, streamTwo); 105 106 using (IInputStream insOne = streamOne.AsInputStream()) 107 using (IInputStream insTwo = streamTwo.AsInputStream()) 108 { 109 Assert.NotEqual(insOne, insTwo); 110 } 111 } 112 113 [Fact] AsOutputStream_Equal()114 public static void AsOutputStream_Equal() 115 { 116 Stream stream = TestStreamProvider.CreateWriteOnlyStream(); 117 118 using (IOutputStream outsOne = stream.AsOutputStream()) 119 using (IOutputStream outsTwo = stream.AsOutputStream()) 120 { 121 Assert.Equal(outsOne, outsOne); 122 } 123 } 124 125 [Fact] AsOutputStream_NotEqual()126 public static void AsOutputStream_NotEqual() 127 { 128 Stream streamOne = TestStreamProvider.CreateWriteOnlyStream(); 129 Stream streamTwo = TestStreamProvider.CreateWriteOnlyStream(); 130 131 Assert.NotEqual(streamOne, streamTwo); 132 133 using (IOutputStream outsOne = streamOne.AsOutputStream()) 134 using (IOutputStream outsTwo = streamTwo.AsOutputStream()) 135 { 136 Assert.NotEqual(outsOne, outsTwo); 137 } 138 } 139 140 [Fact] TestRead_MemoryStream_None()141 public static void TestRead_MemoryStream_None() 142 { 143 DoTestRead(TestStreamProvider.CreateMemoryStreamAsInputStream, InputStreamOptions.None, mustInvokeProgressHandler: false, completesSynchronously: true); 144 } 145 146 [Fact] TestRead_MemoryStream_Partial()147 public static void TestRead_MemoryStream_Partial() 148 { 149 DoTestRead(TestStreamProvider.CreateMemoryStreamAsInputStream, InputStreamOptions.Partial, mustInvokeProgressHandler: false, completesSynchronously: true); 150 } 151 152 [Fact] TestWrite_MemoryStream()153 public static void TestWrite_MemoryStream() 154 { 155 DoTestWrite(TestStreamProvider.CreateMemoryStream, mustInvokeProgressHandler: false); 156 } 157 DoTestRead(Func<IInputStream> createStreamFunc, InputStreamOptions inputStreamOptions, bool mustInvokeProgressHandler, bool completesSynchronously)158 private static void DoTestRead(Func<IInputStream> createStreamFunc, InputStreamOptions inputStreamOptions, bool mustInvokeProgressHandler, bool completesSynchronously) 159 { 160 161 IInputStream stream = createStreamFunc(); 162 IBuffer buffer = WindowsRuntimeBuffer.Create(TestStreamProvider.ModelStreamLength); 163 164 IAsyncOperationWithProgress<IBuffer, uint> readOp = stream.ReadAsync(buffer, (uint)TestStreamProvider.ModelStreamLength, inputStreamOptions); 165 166 if (completesSynchronously) 167 { 168 // New readOp for a stream where we know that reading is sycnhronous must have Status = Completed 169 Assert.Equal(AsyncStatus.Completed, readOp.Status); 170 } 171 else 172 { 173 // Note the race. By the tie we get here, the status of the op may be started or already completed. 174 AsyncStatus readOpStatus = readOp.Status; 175 Assert.True(readOpStatus == AsyncStatus.Completed || readOpStatus == AsyncStatus.Started, "New readOp must have Status = Started or Completed (race)"); 176 } 177 178 bool progressCallbackInvoked = false; 179 bool completedCallbackInvoked = false; 180 181 uint readOpId = readOp.Id; 182 EventWaitHandle waitHandle = new ManualResetEvent(false); 183 184 readOp.Progress = (asyncReadOp, bytesCompleted) => 185 { 186 progressCallbackInvoked = true; 187 188 // asyncReadOp.Id in a progress callback must match the ID of the asyncReadOp to which the callback was assigned 189 Assert.Equal(readOpId, asyncReadOp.Id); 190 191 // asyncReadOp.Status must be 'Started' for an asyncReadOp in progress 192 Assert.Equal(AsyncStatus.Started, asyncReadOp.Status); 193 194 // bytesCompleted must be in range [0, maxBytesToRead] asyncReadOp in progress 195 Assert.InRange(bytesCompleted, 0u, (uint)TestStreamProvider.ModelStreamLength); 196 }; 197 198 readOp.Completed = (asyncReadOp, passedStatus) => 199 { 200 try 201 { 202 completedCallbackInvoked = true; 203 204 // asyncReadOp.Id in a completion callback must match the ID of the asyncReadOp to which the callback was assigned 205 Assert.Equal(readOpId, asyncReadOp.Id); 206 207 // asyncReadOp.Status must match passedStatus for a completed asyncReadOp 208 Assert.Equal(passedStatus, asyncReadOp.Status); 209 210 // asyncReadOp.Status must be 'Completed' for a completed asyncReadOp 211 Assert.Equal(AsyncStatus.Completed, asyncReadOp.Status); 212 213 IBuffer resultBuffer = asyncReadOp.GetResults(); 214 215 // asyncReadOp.GetResults() must not return null for a completed asyncReadOp 216 Assert.NotNull(resultBuffer); 217 218 AssertExtensions.GreaterThan(resultBuffer.Capacity, 0u, "resultBuffer.Capacity should be more than zero in completed callback"); 219 AssertExtensions.GreaterThan(resultBuffer.Length, 0u, "resultBuffer.Length should be more than zero in completed callback"); 220 AssertExtensions.LessThanOrEqualTo(resultBuffer.Length, resultBuffer.Capacity, "resultBuffer.Length should be <= Capacity in completed callback"); 221 222 if (inputStreamOptions == InputStreamOptions.None) 223 { 224 // resultBuffer.Length must be equal to requested number of bytes when an asyncReadOp with 225 // InputStreamOptions.None completes successfully 226 Assert.Equal(resultBuffer.Length, (uint)TestStreamProvider.ModelStreamLength); 227 } 228 229 if (inputStreamOptions == InputStreamOptions.Partial) 230 { 231 AssertExtensions.LessThanOrEqualTo(resultBuffer.Length, (uint)TestStreamProvider.ModelStreamLength, 232 "resultBuffer.Length must be <= requested number of bytes with InputStreamOptions.Partial in completed callback"); 233 } 234 buffer = resultBuffer; 235 } 236 finally 237 { 238 waitHandle.Set(); 239 } 240 }; 241 242 // Now, let's block until the read op is complete. 243 // We speculate that it will complete within 3500 msec, although under high load it may not be. 244 // If the test fails we should use a better way to determine if callback is really not invoked, or if it's just too slow. 245 waitHandle.WaitOne(500); 246 waitHandle.WaitOne(1000); 247 waitHandle.WaitOne(2000); 248 249 if (mustInvokeProgressHandler) 250 { 251 Assert.True(progressCallbackInvoked, 252 "Progress callback specified to ReadAsync callback must be invoked when reading from this kind of stream"); 253 } 254 255 Assert.True(completedCallbackInvoked, 256 "Completion callback specified to ReadAsync callback must be invoked"); 257 258 // readOp.Status must be 'Completed' for a completed async readOp 259 Assert.Equal(AsyncStatus.Completed, readOp.Status); 260 261 AssertExtensions.GreaterThan(buffer.Capacity, 0u, "buffer.Capacity should be greater than zero bytes"); 262 AssertExtensions.GreaterThan(buffer.Length, 0u, "buffer.Length should be greater than zero bytes"); 263 AssertExtensions.LessThanOrEqualTo(buffer.Length, buffer.Capacity, "buffer.Length <= buffer.Capacity is required for a completed async readOp"); 264 265 if (inputStreamOptions == InputStreamOptions.None) 266 { 267 // buffer.Length must be equal to requested number of bytes when an async readOp with 268 // InputStreamOptions.None completes successfully 269 Assert.Equal((uint)TestStreamProvider.ModelStreamLength, buffer.Length); 270 } 271 272 if (inputStreamOptions == InputStreamOptions.Partial) 273 { 274 AssertExtensions.LessThanOrEqualTo(buffer.Length, (uint)TestStreamProvider.ModelStreamLength, 275 "resultBuffer.Length must be <= requested number of bytes with InputStreamOptions.Partial"); 276 } 277 278 byte[] results = new byte[buffer.Length]; 279 buffer.CopyTo(0, results, 0, (int)buffer.Length); 280 281 Assert.True(TestStreamProvider.CheckContent(results, 0, (int)buffer.Length), 282 "Result data returned from AsyncRead must be the same as expected from the test data source"); 283 } 284 DoTestWrite(Func<Stream> createStreamFunc, bool mustInvokeProgressHandler)285 private static void DoTestWrite(Func<Stream> createStreamFunc, bool mustInvokeProgressHandler) 286 { 287 Stream backingStream = createStreamFunc(); 288 using (IOutputStream stream = backingStream.AsOutputStream()) 289 { 290 // Create test data 291 Random rnd = new Random(20100720); // Must be a different seed than used for TestStreamProvider.ModelStreamContents 292 byte[] modelWriteData = new byte[0xA000]; 293 rnd.NextBytes(modelWriteData); 294 295 // Start test 296 297 IBuffer buffer = modelWriteData.AsBuffer(); 298 299 // ibuffer.Length for IBuffer created by Array.ToBuffer(void) must equal to array.Length 300 Assert.Equal((uint)modelWriteData.Length, buffer.Length); 301 302 // ibuffer.Capacity for IBuffer created by Array.ToBuffer(void) must equal to array.Length 303 Assert.Equal((uint)modelWriteData.Length, buffer.Capacity); 304 305 IAsyncOperationWithProgress<uint, uint> writeOp = stream.WriteAsync(buffer); 306 307 // Note the race. By the tie we get here, the status of the op may be started or already completed. 308 AsyncStatus writeOpStatus = writeOp.Status; 309 Assert.True(writeOpStatus == AsyncStatus.Completed || writeOpStatus == AsyncStatus.Started, "New writeOp must have Status = Started or Completed (race)"); 310 311 uint writeOpId = writeOp.Id; 312 bool progressCallbackInvoked = false; 313 bool completedCallbackInvoked = false; 314 uint resultBytesWritten = 0; 315 316 EventWaitHandle waitHandle = new ManualResetEvent(false); 317 318 writeOp.Progress = (asyncWriteOp, bytesCompleted) => 319 { 320 progressCallbackInvoked = true; 321 322 // asyncWriteOp.Id in a progress callback must match the ID of the asyncWriteOp to which the callback was assigned 323 Assert.Equal(writeOpId, asyncWriteOp.Id); 324 325 // asyncWriteOp.Status must be 'Started' for an asyncWriteOp in progress 326 Assert.Equal(AsyncStatus.Started, asyncWriteOp.Status); 327 328 // bytesCompleted must be in range [0, maxBytesToWrite] asyncWriteOp in progress 329 Assert.InRange(bytesCompleted, 0u, (uint)TestStreamProvider.ModelStreamLength); 330 }; 331 332 writeOp.Completed = (asyncWriteOp, passedStatus) => 333 { 334 try 335 { 336 completedCallbackInvoked = true; 337 338 // asyncWriteOp.Id in a completion callback must match the ID of the asyncWriteOp to which the callback was assigned 339 Assert.Equal(writeOpId, asyncWriteOp.Id); 340 341 // asyncWriteOp.Status must match passedStatus for a completed asyncWriteOp 342 Assert.Equal(passedStatus, asyncWriteOp.Status); 343 344 // asyncWriteOp.Status must be 'Completed' for a completed asyncWriteOp 345 Assert.Equal(AsyncStatus.Completed, asyncWriteOp.Status); 346 347 uint bytesWritten = asyncWriteOp.GetResults(); 348 349 // asyncWriteOp.GetResults() must return that all required bytes were written for a completed asyncWriteOp 350 Assert.Equal((uint)modelWriteData.Length, bytesWritten); 351 352 resultBytesWritten = bytesWritten; 353 } 354 finally 355 { 356 waitHandle.Set(); 357 } 358 }; 359 360 // Now, let's block until the write op is complete. 361 // We speculate that it will complete within 3500 msec, although under high load it may not be. 362 // If the test fails we should use a better way to determine if callback is really not invoked, or if it's just too slow. 363 waitHandle.WaitOne(500); 364 waitHandle.WaitOne(1000); 365 waitHandle.WaitOne(2000); 366 367 if (mustInvokeProgressHandler) 368 { 369 Assert.True(progressCallbackInvoked, 370 "Progress callback specified to WriteAsync callback must be invoked when reading from this kind of stream"); 371 } 372 373 Assert.True(completedCallbackInvoked, "Completion callback specified to WriteAsync callback must be invoked"); 374 375 // writeOp.Status must be 'Completed' for a completed async writeOp 376 Assert.Equal(AsyncStatus.Completed, writeOp.Status); 377 378 // writeOp.GetResults() must return that all required bytes were written for a completed async writeOp 379 Assert.Equal((uint)modelWriteData.Length, resultBytesWritten); 380 381 // Check contents 382 383 backingStream.Seek(0, SeekOrigin.Begin); 384 byte[] verifyBuff = new byte[modelWriteData.Length + 1024]; 385 386 int r = backingStream.Read(verifyBuff, 0, verifyBuff.Length); 387 388 for (int i = 0; i < modelWriteData.Length; i++) 389 { 390 Assert.Equal(modelWriteData[i], verifyBuff[i]); 391 } 392 } 393 } 394 } 395 } 396 397