@@ -676,5 +676,158 @@ public void StreamReader_WithOptionalArguments()
676676 Assert . False ( tempStream . CanRead ) ;
677677 }
678678 }
679+
680+ [ Fact ]
681+ public void Read_ShortStream_PerformsFinalFlushCorrectly ( )
682+ {
683+ MemoryStream memStream = new MemoryStream ( new byte [ ] { 0x61 /* 'a' */ , 0xF0 } ) ;
684+
685+ // First, use ReadToEnd API.
686+
687+ memStream . Position = 0 ;
688+ StreamReader reader = new StreamReader ( memStream , Encoding . UTF8 ) ;
689+ Assert . Equal ( "a\uFFFD " , reader . ReadToEnd ( ) ) ;
690+
691+ // Next, use Read() API.
692+
693+ memStream . Position = 0 ;
694+ reader = new StreamReader ( memStream , Encoding . UTF8 ) ;
695+ Assert . Equal ( 'a' , reader . Read ( ) ) ;
696+ Assert . Equal ( '\uFFFD ' , reader . Read ( ) ) ;
697+ Assert . Equal ( - 1 , reader . Read ( ) ) ;
698+
699+ // Next, use Read(Span<char>) API.
700+
701+ StringBuilder builder = new StringBuilder ( ) ;
702+ Span < char > destBuffer = new char [ 1024 ] ;
703+ memStream . Position = 0 ;
704+ reader = new StreamReader ( memStream , Encoding . UTF8 ) ;
705+ int charsRead ;
706+ while ( ( charsRead = reader . Read ( destBuffer ) ) > 0 )
707+ {
708+ builder . Append ( destBuffer . Slice ( 0 , charsRead ) ) ;
709+ }
710+ Assert . Equal ( "a\uFFFD " , builder . ToString ( ) ) ;
711+
712+ // Finally, use ReadLine API.
713+
714+ memStream . Position = 0 ;
715+ reader = new StreamReader ( memStream , Encoding . UTF8 ) ;
716+ Assert . Equal ( "a\uFFFD " , reader . ReadLine ( ) ) ;
717+ Assert . Null ( reader . ReadLine ( ) ) ;
718+ }
719+
720+ [ Fact ]
721+ public void Read_LongStreamIntoShortBuffer_PerformsFinalFlushCorrectly ( )
722+ {
723+ MemoryStream memStream = new MemoryStream ( ) ;
724+ memStream . Write ( Enumerable . Repeat ( ( byte ) 'a' , 32 * 1024 ) . ToArray ( ) ) ;
725+ memStream . WriteByte ( 0xF0 ) ;
726+ string expected = new string ( 'a' , 32 * 1024 ) + "\uFFFD " ;
727+
728+ // First, use ReadToEnd API.
729+
730+ memStream . Position = 0 ;
731+ StreamReader reader = new StreamReader ( memStream , encoding : Encoding . UTF8 , bufferSize : 32 ) ;
732+ Assert . Equal ( expected , reader . ReadToEnd ( ) ) ;
733+
734+ // Next, use Read() API.
735+
736+ memStream . Position = 0 ;
737+ reader = new StreamReader ( memStream , encoding : Encoding . UTF8 , bufferSize : 32 ) ;
738+ for ( int i = 0 ; i < 32 * 1024 ; i ++ )
739+ {
740+ Assert . Equal ( 'a' , reader . Read ( ) ) ;
741+ }
742+ Assert . Equal ( '\uFFFD ' , reader . Read ( ) ) ;
743+ Assert . Equal ( - 1 , reader . Read ( ) ) ;
744+
745+ // Next, use Read(Span<char>) API.
746+
747+ StringBuilder builder = new StringBuilder ( ) ;
748+ Span < char > destBuffer = new char [ 47 ] ; // prime number, because why not
749+ memStream . Position = 0 ;
750+ reader = new StreamReader ( memStream , encoding : Encoding . UTF8 , bufferSize : 32 ) ;
751+ int charsRead ;
752+ while ( ( charsRead = reader . Read ( destBuffer ) ) > 0 )
753+ {
754+ builder . Append ( destBuffer . Slice ( 0 , charsRead ) ) ;
755+ }
756+ Assert . Equal ( expected , builder . ToString ( ) ) ;
757+
758+ // Finally, use ReadLine API.
759+
760+ memStream . Position = 0 ;
761+ reader = new StreamReader ( memStream , encoding : Encoding . UTF8 , bufferSize : 32 ) ;
762+ Assert . Equal ( expected , reader . ReadLine ( ) ) ;
763+ Assert . Null ( reader . ReadLine ( ) ) ;
764+ }
765+
766+ [ Fact ]
767+ public async Task ReadAsync_ShortStream_PerformsFinalFlushCorrectly ( )
768+ {
769+ MemoryStream memStream = new MemoryStream ( new byte [ ] { 0x61 /* 'a' */ , 0xF0 } ) ;
770+
771+ // First, use ReadToEndAsync API.
772+
773+ memStream . Position = 0 ;
774+ StreamReader reader = new StreamReader ( memStream , Encoding . UTF8 ) ;
775+ Assert . Equal ( "a\uFFFD " , await reader . ReadToEndAsync ( ) ) ;
776+
777+ // Next, use ReadAsync(Memory<char>) API.
778+
779+ StringBuilder builder = new StringBuilder ( ) ;
780+ Memory < char > destBuffer = new char [ 1024 ] ;
781+ memStream . Position = 0 ;
782+ reader = new StreamReader ( memStream , Encoding . UTF8 ) ;
783+ int charsRead ;
784+ while ( ( charsRead = await reader . ReadAsync ( destBuffer ) ) > 0 )
785+ {
786+ builder . Append ( destBuffer . Slice ( 0 , charsRead ) ) ;
787+ }
788+ Assert . Equal ( "a\uFFFD " , builder . ToString ( ) ) ;
789+
790+ // Finally, use ReadLineAsync API.
791+
792+ memStream . Position = 0 ;
793+ reader = new StreamReader ( memStream , Encoding . UTF8 ) ;
794+ Assert . Equal ( "a\uFFFD " , await reader . ReadLineAsync ( ) ) ;
795+ Assert . Null ( await reader . ReadLineAsync ( ) ) ;
796+ }
797+
798+ [ Fact ]
799+ public async Task ReadAsync_LongStreamIntoShortBuffer_PerformsFinalFlushCorrectly ( )
800+ {
801+ MemoryStream memStream = new MemoryStream ( ) ;
802+ memStream . Write ( Enumerable . Repeat ( ( byte ) 'a' , 32 * 1024 ) . ToArray ( ) ) ;
803+ memStream . WriteByte ( 0xF0 ) ;
804+ string expected = new string ( 'a' , 32 * 1024 ) + "\uFFFD " ;
805+
806+ // First, use ReadToEndAsync API.
807+
808+ memStream . Position = 0 ;
809+ StreamReader reader = new StreamReader ( memStream , encoding : Encoding . UTF8 , bufferSize : 32 ) ;
810+ Assert . Equal ( expected , await reader . ReadToEndAsync ( ) ) ;
811+
812+ // Next, use Read(Memory<char>) API.
813+
814+ StringBuilder builder = new StringBuilder ( ) ;
815+ Memory < char > destBuffer = new char [ 47 ] ; // prime number, because why not
816+ memStream . Position = 0 ;
817+ reader = new StreamReader ( memStream , encoding : Encoding . UTF8 , bufferSize : 32 ) ;
818+ int charsRead ;
819+ while ( ( charsRead = await reader . ReadAsync ( destBuffer ) ) > 0 )
820+ {
821+ builder . Append ( destBuffer . Slice ( 0 , charsRead ) ) ;
822+ }
823+ Assert . Equal ( expected , builder . ToString ( ) ) ;
824+
825+ // Finally, use ReadLineAsync API.
826+
827+ memStream . Position = 0 ;
828+ reader = new StreamReader ( memStream , encoding : Encoding . UTF8 , bufferSize : 32 ) ;
829+ Assert . Equal ( expected , await reader . ReadLineAsync ( ) ) ;
830+ Assert . Null ( await reader . ReadLineAsync ( ) ) ;
831+ }
679832 }
680833}
0 commit comments