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