1
1
// Copyright (c) Microsoft. All rights reserved.
2
2
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
3
4
- using System . Reflection ;
4
+ using System . Collections . Generic ;
5
5
using System . Text ;
6
+ using System . Threading ;
6
7
using System . Threading . Tasks ;
7
8
using Xunit ;
8
9
@@ -191,20 +192,6 @@ public async Task DecompressFailsWithRealGzStream()
191
192
}
192
193
}
193
194
194
- [ Fact ]
195
- public void NullBaseStreamThrows ( )
196
- {
197
- Assert . Throws < ArgumentNullException > ( ( ) =>
198
- {
199
- var deflate = new DeflateStream ( null , CompressionMode . Decompress ) ;
200
- } ) ;
201
-
202
- Assert . Throws < ArgumentNullException > ( ( ) =>
203
- {
204
- var deflate = new DeflateStream ( null , CompressionMode . Compress ) ;
205
- } ) ;
206
- }
207
-
208
195
[ Fact ]
209
196
public void DisposedBaseStreamThrows ( )
210
197
{
@@ -330,6 +317,23 @@ private static void TestCtor(CompressionLevel level, bool? leaveOpen = null)
330
317
}
331
318
}
332
319
320
+ [ Fact ]
321
+ public void CtorArgumentValidation ( )
322
+ {
323
+ Assert . Throws < ArgumentNullException > ( ( ) => new DeflateStream ( null , CompressionLevel . Fastest ) ) ;
324
+ Assert . Throws < ArgumentNullException > ( ( ) => new DeflateStream ( null , CompressionMode . Decompress ) ) ;
325
+ Assert . Throws < ArgumentNullException > ( ( ) => new DeflateStream ( null , CompressionMode . Compress ) ) ;
326
+
327
+ Assert . Throws < ArgumentNullException > ( ( ) => new DeflateStream ( null , CompressionLevel . Fastest , true ) ) ;
328
+ Assert . Throws < ArgumentNullException > ( ( ) => new DeflateStream ( null , CompressionMode . Decompress , false ) ) ;
329
+ Assert . Throws < ArgumentNullException > ( ( ) => new DeflateStream ( null , CompressionMode . Compress , true ) ) ;
330
+
331
+ Assert . Throws < ArgumentException > ( ( ) => new DeflateStream ( new MemoryStream ( ) , ( CompressionMode ) 42 ) ) ;
332
+ Assert . Throws < ArgumentException > ( ( ) => new DeflateStream ( new MemoryStream ( ) , ( CompressionMode ) 43 , true ) ) ;
333
+
334
+ Assert . Throws < ArgumentException > ( ( ) => new DeflateStream ( new MemoryStream ( new byte [ 1 ] , writable : false ) , CompressionLevel . Optimal ) ) ;
335
+ }
336
+
333
337
[ Fact ]
334
338
public async Task Flush ( )
335
339
{
@@ -353,7 +357,6 @@ public void FlushFailsAfterDispose()
353
357
[ Fact ]
354
358
public async Task FlushAsyncFailsAfterDispose ( )
355
359
{
356
-
357
360
var ms = new MemoryStream ( ) ;
358
361
var ds = new DeflateStream ( ms , CompressionMode . Compress ) ;
359
362
ds . Dispose ( ) ;
@@ -376,7 +379,6 @@ public void TestSeekMethodsDecompress()
376
379
Assert . Throws < NotSupportedException > ( delegate { long value = zip . Position ; } ) ;
377
380
Assert . Throws < NotSupportedException > ( delegate { zip . Position = 100L ; } ) ;
378
381
Assert . Throws < NotSupportedException > ( delegate { zip . SetLength ( 100L ) ; } ) ;
379
- //Should we try all the enums? doesn't seem necessary
380
382
Assert . Throws < NotSupportedException > ( delegate { zip . Seek ( 100L , SeekOrigin . Begin ) ; } ) ;
381
383
}
382
384
@@ -392,8 +394,205 @@ public void TestSeekMethodsCompress()
392
394
Assert . Throws < NotSupportedException > ( delegate { long value = zip . Position ; } ) ;
393
395
Assert . Throws < NotSupportedException > ( delegate { zip . Position = 100L ; } ) ;
394
396
Assert . Throws < NotSupportedException > ( delegate { zip . SetLength ( 100L ) ; } ) ;
395
- //Should we try all the enums? doesn't seem necessary
396
397
Assert . Throws < NotSupportedException > ( delegate { zip . Seek ( 100L , SeekOrigin . Begin ) ; } ) ;
397
398
}
399
+
400
+ [ Fact ]
401
+ public void ReadWriteArgumentValidation ( )
402
+ {
403
+ using ( var ds = new DeflateStream ( new MemoryStream ( ) , CompressionMode . Compress ) )
404
+ {
405
+ Assert . Throws < ArgumentNullException > ( ( ) => ds . Write ( null , 0 , 0 ) ) ;
406
+ Assert . Throws < ArgumentOutOfRangeException > ( ( ) => ds . Write ( new byte [ 1 ] , - 1 , 0 ) ) ;
407
+ Assert . Throws < ArgumentOutOfRangeException > ( ( ) => ds . Write ( new byte [ 1 ] , 0 , - 1 ) ) ;
408
+ Assert . Throws < ArgumentException > ( ( ) => ds . Write ( new byte [ 1 ] , 0 , 2 ) ) ;
409
+ Assert . Throws < ArgumentException > ( ( ) => ds . Write ( new byte [ 1 ] , 1 , 1 ) ) ;
410
+ Assert . Throws < InvalidOperationException > ( ( ) => ds . Read ( new byte [ 1 ] , 0 , 1 ) ) ;
411
+ ds . Write ( new byte [ 1 ] , 0 , 0 ) ;
412
+ }
413
+ using ( var ds = new DeflateStream ( new MemoryStream ( ) , CompressionMode . Compress ) )
414
+ {
415
+ Assert . Throws < ArgumentNullException > ( ( ) => { ds . WriteAsync ( null , 0 , 0 ) ; } ) ;
416
+ Assert . Throws < ArgumentOutOfRangeException > ( ( ) => { ds . WriteAsync ( new byte [ 1 ] , - 1 , 0 ) ; } ) ;
417
+ Assert . Throws < ArgumentOutOfRangeException > ( ( ) => { ds . WriteAsync ( new byte [ 1 ] , 0 , - 1 ) ; } ) ;
418
+ Assert . Throws < ArgumentException > ( ( ) => { ds . WriteAsync ( new byte [ 1 ] , 0 , 2 ) ; } ) ;
419
+ Assert . Throws < ArgumentException > ( ( ) => { ds . WriteAsync ( new byte [ 1 ] , 1 , 1 ) ; } ) ;
420
+ Assert . Throws < InvalidOperationException > ( ( ) => { ds . Read ( new byte [ 1 ] , 0 , 1 ) ; } ) ;
421
+ }
422
+
423
+ using ( var ds = new DeflateStream ( new MemoryStream ( ) , CompressionMode . Decompress ) )
424
+ {
425
+ Assert . Throws < ArgumentNullException > ( ( ) => ds . Read ( null , 0 , 0 ) ) ;
426
+ Assert . Throws < ArgumentOutOfRangeException > ( ( ) => ds . Read ( new byte [ 1 ] , - 1 , 0 ) ) ;
427
+ Assert . Throws < ArgumentOutOfRangeException > ( ( ) => ds . Read ( new byte [ 1 ] , 0 , - 1 ) ) ;
428
+ Assert . Throws < ArgumentException > ( ( ) => ds . Read ( new byte [ 1 ] , 0 , 2 ) ) ;
429
+ Assert . Throws < ArgumentException > ( ( ) => ds . Read ( new byte [ 1 ] , 1 , 1 ) ) ;
430
+ Assert . Throws < InvalidOperationException > ( ( ) => ds . Write ( new byte [ 1 ] , 0 , 1 ) ) ;
431
+
432
+ var data = new byte [ 1 ] { 42 } ;
433
+ Assert . Equal ( 0 , ds . Read ( data , 0 , 0 ) ) ;
434
+ Assert . Equal ( 42 , data [ 0 ] ) ;
435
+ }
436
+ using ( var ds = new DeflateStream ( new MemoryStream ( ) , CompressionMode . Decompress ) )
437
+ {
438
+ Assert . Throws < ArgumentNullException > ( ( ) => { ds . ReadAsync ( null , 0 , 0 ) ; } ) ;
439
+ Assert . Throws < ArgumentOutOfRangeException > ( ( ) => { ds . ReadAsync ( new byte [ 1 ] , - 1 , 0 ) ; } ) ;
440
+ Assert . Throws < ArgumentOutOfRangeException > ( ( ) => { ds . ReadAsync ( new byte [ 1 ] , 0 , - 1 ) ; } ) ;
441
+ Assert . Throws < ArgumentException > ( ( ) => { ds . ReadAsync ( new byte [ 1 ] , 0 , 2 ) ; } ) ;
442
+ Assert . Throws < ArgumentException > ( ( ) => { ds . ReadAsync ( new byte [ 1 ] , 1 , 1 ) ; } ) ;
443
+ Assert . Throws < InvalidOperationException > ( ( ) => { ds . Write ( new byte [ 1 ] , 0 , 1 ) ; } ) ;
444
+ }
445
+ }
446
+
447
+ [ Fact ]
448
+ public void Precancellation ( )
449
+ {
450
+ var ms = new MemoryStream ( ) ;
451
+ using ( DeflateStream ds = new DeflateStream ( ms , CompressionMode . Compress , leaveOpen : true ) )
452
+ {
453
+ Assert . True ( ds . WriteAsync ( new byte [ 1 ] , 0 , 1 , new CancellationToken ( true ) ) . IsCanceled ) ;
454
+ Assert . True ( ds . FlushAsync ( new CancellationToken ( true ) ) . IsCanceled ) ;
455
+ }
456
+ using ( DeflateStream ds = new DeflateStream ( ms , CompressionMode . Decompress , leaveOpen : true ) )
457
+ {
458
+ Assert . True ( ds . ReadAsync ( new byte [ 1 ] , 0 , 1 , new CancellationToken ( true ) ) . IsCanceled ) ;
459
+ }
460
+ }
461
+
462
+ [ Fact ]
463
+ public async Task RoundtripCompressDecompress ( )
464
+ {
465
+ await RoundtripCompressDecompress ( useAsync : false , useGzip : false , chunkSize : 1 , totalSize : 10 , level : CompressionLevel . Fastest ) ;
466
+ await RoundtripCompressDecompress ( useAsync : true , useGzip : true , chunkSize : 1024 , totalSize : 8192 , level : CompressionLevel . Optimal ) ;
467
+ }
468
+
469
+ [ OuterLoop ]
470
+ [ Theory ]
471
+ [ MemberData ( "RoundtripCompressDecompressOuterData" ) ]
472
+ public Task RoundtripCompressDecompressOuter ( bool useAsync , bool useGzip , int chunkSize , int totalSize , CompressionLevel level )
473
+ {
474
+ return RoundtripCompressDecompress ( useAsync , useGzip , chunkSize , totalSize , level ) ;
475
+ }
476
+
477
+ public static IEnumerable < object [ ] > RoundtripCompressDecompressOuterData
478
+ {
479
+ get
480
+ {
481
+ foreach ( bool useAsync in new [ ] { true , false } ) // whether to use Read/Write or ReadAsync/WriteAsync
482
+ {
483
+ foreach ( bool useGzip in new [ ] { true , false } ) // whether to add on gzip headers/footers
484
+ {
485
+ foreach ( var level in new [ ] { CompressionLevel . Fastest , CompressionLevel . Optimal , CompressionLevel . NoCompression } ) // compression level
486
+ {
487
+ yield return new object [ ] { useAsync , useGzip , 1 , 5 , level } ; // smallest possible writes
488
+ yield return new object [ ] { useAsync , useGzip , 1023 , 1023 * 10 , level } ; // overflowing internal buffer
489
+ yield return new object [ ] { useAsync , useGzip , 1024 * 1024 , 1024 * 1024 , level } ; // large single write
490
+ }
491
+ }
492
+ }
493
+ }
494
+ }
495
+
496
+ private async Task RoundtripCompressDecompress ( bool useAsync , bool useGzip , int chunkSize , int totalSize , CompressionLevel level )
497
+ {
498
+ byte [ ] data = new byte [ totalSize ] ;
499
+ new Random ( 42 ) . NextBytes ( data ) ;
500
+
501
+ var compressed = new MemoryStream ( ) ;
502
+ using ( var compressor = useGzip ? ( Stream ) new GZipStream ( compressed , level , true ) : new DeflateStream ( compressed , level , true ) )
503
+ {
504
+ for ( int i = 0 ; i < data . Length ; i += chunkSize ) // not using CopyTo{Async} due to optimizations in MemoryStream's implementation that avoid what we're trying to test
505
+ {
506
+ switch ( useAsync )
507
+ {
508
+ case true : await compressor . WriteAsync ( data , i , chunkSize ) ; break ;
509
+ case false : compressor . Write ( data , i , chunkSize ) ; break ;
510
+ }
511
+ }
512
+ }
513
+ compressed . Position = 0 ;
514
+
515
+ var decompressed = new MemoryStream ( ) ;
516
+ using ( var decompressor = useGzip ? ( Stream ) new GZipStream ( compressed , CompressionMode . Decompress , true ) : new DeflateStream ( compressed , CompressionMode . Decompress , true ) )
517
+ {
518
+ if ( useAsync )
519
+ decompressor . CopyTo ( decompressed , chunkSize ) ;
520
+ else
521
+ await decompressor . CopyToAsync ( decompressed , chunkSize , CancellationToken . None ) ;
522
+ }
523
+
524
+ Assert . Equal < byte > ( data , decompressed . ToArray ( ) ) ;
525
+ }
526
+
527
+ [ Fact ]
528
+ public async Task WrapNullReturningTasksStream ( )
529
+ {
530
+ using ( var ds = new DeflateStream ( new BadWrappedStream ( BadWrappedStream . Mode . ReturnNullTasks ) , CompressionMode . Decompress ) )
531
+ await Assert . ThrowsAsync < InvalidOperationException > ( ( ) => ds . ReadAsync ( new byte [ 1024 ] , 0 , 1024 ) ) ;
532
+ }
533
+
534
+ [ Fact ]
535
+ public async Task WrapStreamReturningBadReadValues ( )
536
+ {
537
+ using ( var ds = new DeflateStream ( new BadWrappedStream ( BadWrappedStream . Mode . ReturnTooLargeCounts ) , CompressionMode . Decompress ) )
538
+ Assert . Throws < InvalidDataException > ( ( ) => ds . Read ( new byte [ 1024 ] , 0 , 1024 ) ) ;
539
+ using ( var ds = new DeflateStream ( new BadWrappedStream ( BadWrappedStream . Mode . ReturnTooLargeCounts ) , CompressionMode . Decompress ) )
540
+ await Assert . ThrowsAsync < InvalidDataException > ( ( ) => ds . ReadAsync ( new byte [ 1024 ] , 0 , 1024 ) ) ;
541
+
542
+ using ( var ds = new DeflateStream ( new BadWrappedStream ( BadWrappedStream . Mode . ReturnTooSmallCounts ) , CompressionMode . Decompress ) )
543
+ Assert . Equal ( 0 , ds . Read ( new byte [ 1024 ] , 0 , 1024 ) ) ;
544
+ using ( var ds = new DeflateStream ( new BadWrappedStream ( BadWrappedStream . Mode . ReturnTooSmallCounts ) , CompressionMode . Decompress ) )
545
+ Assert . Equal ( 0 , await ds . ReadAsync ( new byte [ 1024 ] , 0 , 1024 ) ) ;
546
+ }
547
+
548
+ private sealed class BadWrappedStream : Stream
549
+ {
550
+ public enum Mode
551
+ {
552
+ Default ,
553
+ ReturnNullTasks ,
554
+ ReturnTooSmallCounts ,
555
+ ReturnTooLargeCounts ,
556
+ }
557
+
558
+ private readonly Mode _mode ;
559
+
560
+ public BadWrappedStream ( Mode mode ) { _mode = mode ; }
561
+
562
+ public override int Read ( byte [ ] buffer , int offset , int count )
563
+ {
564
+ switch ( _mode )
565
+ {
566
+ case Mode . ReturnTooSmallCounts : return - 1 ;
567
+ case Mode . ReturnTooLargeCounts : return buffer . Length + 1 ;
568
+ default : return 0 ;
569
+ }
570
+ }
571
+
572
+ public override Task < int > ReadAsync ( byte [ ] buffer , int offset , int count , CancellationToken cancellationToken )
573
+ {
574
+ return _mode == Mode . ReturnNullTasks ?
575
+ null :
576
+ base . ReadAsync ( buffer , offset , count , cancellationToken ) ;
577
+ }
578
+
579
+ public override Task WriteAsync ( byte [ ] buffer , int offset , int count , CancellationToken cancellationToken )
580
+ {
581
+ return _mode == Mode . ReturnNullTasks ?
582
+ null :
583
+ base . WriteAsync ( buffer , offset , count , cancellationToken ) ;
584
+ }
585
+
586
+ public override void Write ( byte [ ] buffer , int offset , int count ) { }
587
+ public override void Flush ( ) { }
588
+ public override bool CanRead { get { return true ; } }
589
+ public override bool CanSeek { get { return false ; } }
590
+ public override bool CanWrite { get { return true ; } }
591
+ public override long Length { get { throw new NotSupportedException ( ) ; } }
592
+ public override long Position { get { throw new NotSupportedException ( ) ; } set { throw new NotSupportedException ( ) ; } }
593
+ public override long Seek ( long offset , SeekOrigin origin ) { throw new NotSupportedException ( ) ; }
594
+ public override void SetLength ( long value ) { throw new NotSupportedException ( ) ; }
595
+ }
596
+
398
597
}
399
598
}
0 commit comments