@@ -78,6 +78,168 @@ public void TestRead_Span()
7878 Assert . IsTrue ( Encoding . UTF8 . GetString ( buffer ) . Equals ( fileString ) ) ;
7979 }
8080
81+ #if ! FEATURE_STREAM_READEXACTLY
82+ [ Test ]
83+ public void TestReadExactly_ZeroLength ( )
84+ {
85+ using var ms = new MemoryStream ( ) ;
86+ Span < byte > buffer = Array . Empty < byte > ( ) ;
87+ ms . ReadExactly ( buffer ) ; // should succeed
88+ }
89+
90+ [ Test ]
91+ public void TestReadExactly_Success_FromStart ( )
92+ {
93+ var bytes = new byte [ ] { 1 , 2 , 3 , 4 } ;
94+ using var ms = new MemoryStream ( bytes ) ;
95+
96+ Span < byte > buffer = stackalloc byte [ 2 ] ;
97+ ms . ReadExactly ( buffer ) ;
98+
99+ Assert . AreEqual ( ( byte ) 1 , buffer [ 0 ] ) ;
100+ Assert . AreEqual ( ( byte ) 2 , buffer [ 1 ] ) ;
101+ }
102+
103+ [ Test ]
104+ public void TestReadExactly_Success_FromMiddle ( )
105+ {
106+ var bytes = new byte [ ] { 1 , 2 , 3 , 4 } ;
107+ using var ms = new MemoryStream ( bytes ) ;
108+ ms . Seek ( 2 , SeekOrigin . Begin ) ;
109+
110+ Span < byte > buffer = stackalloc byte [ 2 ] ;
111+ ms . ReadExactly ( buffer ) ;
112+
113+ Assert . AreEqual ( ( byte ) 3 , buffer [ 0 ] ) ;
114+ Assert . AreEqual ( ( byte ) 4 , buffer [ 1 ] ) ;
115+ }
116+
117+ [ Test ]
118+ public void TestReadExactly_Success_IntoMiddle ( )
119+ {
120+ var bytes = new byte [ ] { 1 , 2 , 3 , 4 } ;
121+ using var ms = new MemoryStream ( bytes ) ;
122+
123+ Span < byte > buffer = stackalloc byte [ 4 ] ;
124+ ms . ReadExactly ( buffer . Slice ( 2 ) ) ;
125+
126+ Assert . AreEqual ( ( byte ) 1 , buffer [ 2 ] ) ;
127+ Assert . AreEqual ( ( byte ) 2 , buffer [ 3 ] ) ;
128+ }
129+
130+ [ Test ]
131+ public void TestReadExactly_EndOfStream ( )
132+ {
133+ var bytes = new byte [ ] { 1 , 2 , 3 , 4 } ;
134+
135+ Assert . Throws < EndOfStreamException > ( ( ) =>
136+ {
137+ using var ms = new MemoryStream ( bytes ) ;
138+
139+ Span < byte > buffer = stackalloc byte [ 5 ] ;
140+ ms . ReadExactly ( buffer ) ;
141+ } ) ;
142+ }
143+
144+ [ Test ]
145+ public void TestReadExactly_PartialReads ( )
146+ {
147+ var bytes = new byte [ ] { 1 , 2 , 3 , 4 } ;
148+ var partialStream = new MaxBytesPerReadStream ( bytes , maxBytesPerRead : 1 ) ;
149+
150+ Span < byte > buffer = stackalloc byte [ 4 ] ;
151+ partialStream . ReadExactly ( buffer ) ;
152+
153+ Assert . AreEqual ( ( byte ) 1 , buffer [ 0 ] ) ;
154+ Assert . AreEqual ( ( byte ) 2 , buffer [ 1 ] ) ;
155+ Assert . AreEqual ( ( byte ) 3 , buffer [ 2 ] ) ;
156+ Assert . AreEqual ( ( byte ) 4 , buffer [ 3 ] ) ;
157+ }
158+
159+ [ Test ]
160+ public async Task TestReadExactlyAsync_ZeroLength ( )
161+ {
162+ using var ms = new MemoryStream ( ) ;
163+ var buffer = Array . Empty < byte > ( ) ;
164+ await ms . ReadExactlyAsync ( buffer , 0 , 0 ) ; // should succeed
165+ }
166+
167+ [ Test ]
168+ public async Task TestReadExactlyAsync_Success_FromStart ( )
169+ {
170+ var bytes = new byte [ ] { 1 , 2 , 3 , 4 } ;
171+ using var ms = new MemoryStream ( bytes ) ;
172+
173+ var buffer = new byte [ 2 ] ;
174+ await ms . ReadExactlyAsync ( buffer , 0 , 2 ) ;
175+
176+ Assert . AreEqual ( ( byte ) 1 , buffer [ 0 ] ) ;
177+ Assert . AreEqual ( ( byte ) 2 , buffer [ 1 ] ) ;
178+ }
179+
180+ [ Test ]
181+ public async Task TestReadExactlyAsync_Success_FromMiddle ( )
182+ {
183+ var bytes = new byte [ ] { 1 , 2 , 3 , 4 } ;
184+ using var ms = new MemoryStream ( bytes ) ;
185+ ms . Seek ( 2 , SeekOrigin . Begin ) ;
186+
187+ var buffer = new byte [ 2 ] ;
188+ await ms . ReadExactlyAsync ( buffer , 0 , 2 ) ;
189+
190+ Assert . AreEqual ( ( byte ) 3 , buffer [ 0 ] ) ;
191+ Assert . AreEqual ( ( byte ) 4 , buffer [ 1 ] ) ;
192+ }
193+
194+ [ Test ]
195+ public async Task TestReadExactlyAsync_Success_IntoMiddle ( )
196+ {
197+ var bytes = new byte [ ] { 1 , 2 , 3 , 4 } ;
198+ using var ms = new MemoryStream ( bytes ) ;
199+
200+ var buffer = new byte [ 4 ] ;
201+ await ms . ReadExactlyAsync ( buffer , 2 , 2 ) ;
202+
203+ Assert . AreEqual ( ( byte ) 1 , buffer [ 2 ] ) ;
204+ Assert . AreEqual ( ( byte ) 2 , buffer [ 3 ] ) ;
205+ }
206+
207+ [ Test ]
208+ public async Task TestReadExactlyAsync_EndOfStream ( )
209+ {
210+ var bytes = new byte [ ] { 1 , 2 , 3 , 4 } ;
211+
212+ try
213+ {
214+ using var ms = new MemoryStream ( bytes ) ;
215+
216+ var buffer = new byte [ 5 ] ;
217+ await ms . ReadExactlyAsync ( buffer , 0 , 5 ) ;
218+
219+ Assert . Fail ( "Should have thrown an exception" ) ;
220+ }
221+ catch ( EndOfStreamException )
222+ {
223+ Assert . Pass ( "Expected EndOfStreamException thrown" ) ;
224+ }
225+ }
226+
227+ [ Test ]
228+ public async Task TestReadExactlyAsync_PartialReads ( )
229+ {
230+ var bytes = new byte [ ] { 1 , 2 , 3 , 4 } ;
231+ var partialStream = new MaxBytesPerReadStream ( bytes , maxBytesPerRead : 1 ) ;
232+
233+ var buffer = new byte [ 4 ] ;
234+ await partialStream . ReadExactlyAsync ( buffer , 0 , 4 ) ;
235+
236+ Assert . AreEqual ( ( byte ) 1 , buffer [ 0 ] ) ;
237+ Assert . AreEqual ( ( byte ) 2 , buffer [ 1 ] ) ;
238+ Assert . AreEqual ( ( byte ) 3 , buffer [ 2 ] ) ;
239+ Assert . AreEqual ( ( byte ) 4 , buffer [ 3 ] ) ;
240+ }
241+ #endif
242+
81243 [ Test ]
82244 public void TestWrite_Span ( )
83245 {
@@ -237,7 +399,7 @@ public async Task TestReadUTFAsync()
237399 /// Helper method to calculate expected UTF stream length for validation
238400 /// Matches DataOutput.writeUTF() spec (Java)
239401 /// </summary>
240- private long CalculateExpectedUTFStreamLength ( string value )
402+ private static long CalculateExpectedUTFStreamLength ( string value )
241403 {
242404 long utfCount = 0 ;
243405 foreach ( char ch in value )
@@ -260,10 +422,71 @@ private void ResetStreamForReading() // LUCENENET - was "OpenDataInputStream" in
260422 stream . Position = 0 ;
261423 }
262424
425+ // Partial-read async tests: verify that async read methods handle
426+ // streams that return fewer bytes than requested per call.
427+
428+ [ Test ]
429+ public async Task TestReadInt32BigEndianAsync_PartialReads ( )
430+ {
431+ await stream . WriteInt32BigEndianAsync ( 768347202 ) ;
432+ var partialStream = new MaxBytesPerReadStream ( ( ( MemoryStream ) stream ) . ToArray ( ) , maxBytesPerRead : 1 ) ;
433+
434+ int result = await partialStream . ReadInt32BigEndianAsync ( ) ;
435+ Assert . AreEqual ( 768347202 , result , "Incorrect int read with partial reads (async)" ) ;
436+ }
437+
438+ [ Test ]
439+ public async Task TestReadInt64BigEndianAsync_PartialReads ( )
440+ {
441+ await stream . WriteInt64BigEndianAsync ( 9875645283333L ) ;
442+ var partialStream = new MaxBytesPerReadStream ( ( ( MemoryStream ) stream ) . ToArray ( ) , maxBytesPerRead : 1 ) ;
443+
444+ long result = await partialStream . ReadInt64BigEndianAsync ( ) ;
445+ Assert . AreEqual ( 9875645283333L , result , "Incorrect long read with partial reads (async)" ) ;
446+ }
447+
448+ [ Test ]
449+ public async Task TestReadUTFAsync_PartialReads ( )
450+ {
451+ await stream . WriteUTFAsync ( unihw ) ;
452+ var partialStream = new MaxBytesPerReadStream ( ( ( MemoryStream ) stream ) . ToArray ( ) , maxBytesPerRead : 1 ) ;
453+
454+ string result = await partialStream . ReadUTFAsync ( ) ;
455+ Assert . AreEqual ( unihw , result , "Incorrect string read with partial reads (async)" ) ;
456+ }
457+
263458 public override void SetUp ( )
264459 {
265460 base . SetUp ( ) ;
266461 stream = new MemoryStream ( ) ;
267462 }
268463 }
464+
465+ /// <summary>
466+ /// A stream wrapper that returns at most <c>maxBytesPerRead</c> bytes per
467+ /// <see cref="ReadAsync(byte[], int, int, System.Threading.CancellationToken)"/> call,
468+ /// simulating partial reads (e.g. network streams).
469+ /// </summary>
470+ internal sealed class MaxBytesPerReadStream : MemoryStream
471+ {
472+ private readonly int maxBytesPerRead ;
473+
474+ public MaxBytesPerReadStream ( byte [ ] data , int maxBytesPerRead )
475+ : base ( data , writable : false )
476+ {
477+ this . maxBytesPerRead = maxBytesPerRead ;
478+ }
479+
480+ public override int Read ( byte [ ] buffer , int offset , int count )
481+ {
482+ int clamped = Math . Min ( count , maxBytesPerRead ) ;
483+ return base . Read ( buffer , offset , clamped ) ;
484+ }
485+
486+ public override Task < int > ReadAsync ( byte [ ] buffer , int offset , int count , System . Threading . CancellationToken cancellationToken )
487+ {
488+ int clamped = Math . Min ( count , maxBytesPerRead ) ;
489+ return base . ReadAsync ( buffer , offset , clamped , cancellationToken ) ;
490+ }
491+ }
269492}
0 commit comments