@@ -312,4 +312,154 @@ public void FilterLines_MultipleConsecutiveRemoved()
312312
313313 Assert . Equal ( "keep1\n keep2" , builder . ToString ( ) ) ;
314314 }
315+
316+ [ Fact ]
317+ public void FilterLines_LineSpansMultipleChunks ( )
318+ {
319+ // StringBuilder default chunk size is ~8000 chars
320+ // Create a line that's definitely longer than a chunk
321+ var longLine = new string ( 'a' , 10_000 ) ;
322+ var builder = new StringBuilder ( ) ;
323+ builder . Append ( "keep1\n " ) ;
324+ builder . Append ( longLine ) ;
325+ builder . Append ( "\n keep2" ) ;
326+
327+ builder . FilterLines ( line => line == longLine ) ;
328+
329+ Assert . Equal ( "keep1\n keep2" , builder . ToString ( ) ) ;
330+ }
331+
332+ [ Fact ]
333+ public void FilterLines_NewlineAtChunkBoundary ( )
334+ {
335+ // Fill first chunk, then add newline at boundary
336+ var chunkFiller = new string ( 'x' , 8000 ) ;
337+ var builder = new StringBuilder ( ) ;
338+ builder . Append ( chunkFiller ) ;
339+ builder . Append ( '\n ' ) ;
340+ builder . Append ( "line2" ) ;
341+
342+ builder . FilterLines ( line => line == chunkFiller ) ;
343+
344+ Assert . Equal ( "line2" , builder . ToString ( ) ) ;
345+ }
346+
347+ [ Fact ]
348+ public void FilterLines_CrLfSplitAcrossChunks ( )
349+ {
350+ // Create scenario where \r is at end of one chunk and \n at start of next
351+ var lineContent = new string ( 'y' , 7999 ) ;
352+ var builder = new StringBuilder ( ) ;
353+ builder . Append ( lineContent ) ;
354+ builder . Append ( "\r \n " ) ;
355+ builder . Append ( "nextline" ) ;
356+
357+ builder . FilterLines ( line => line == lineContent ) ;
358+
359+ Assert . Equal ( "nextline" , builder . ToString ( ) ) ;
360+ }
361+
362+ [ Fact ]
363+ public void FilterLines_MultipleLinesSpanningChunks ( )
364+ {
365+ var builder = new StringBuilder ( ) ;
366+ var lines = new List < string > ( ) ;
367+
368+ // Create lines of varying lengths that will span chunk boundaries
369+ for ( var i = 0 ; i < 20 ; i ++ )
370+ {
371+ var line = $ "line{ i } _" + new string ( ( char ) ( 'a' + i % 26 ) , 1000 + i * 100 ) ;
372+ lines . Add ( line ) ;
373+ builder . Append ( line ) ;
374+ if ( i < 19 )
375+ {
376+ builder . Append ( '\n ' ) ;
377+ }
378+ }
379+
380+ // Remove even-indexed lines
381+ builder . FilterLines ( line => lines . IndexOf ( line ) % 2 == 0 ) ;
382+
383+ var expected = string . Join ( '\n ' , lines . Where ( ( _ , i ) => i % 2 == 1 ) ) ;
384+ Assert . Equal ( expected , builder . ToString ( ) ) ;
385+ }
386+
387+ [ Fact ]
388+ public void FilterLines_VeryLongLineKept ( )
389+ {
390+ var longLine = new string ( 'z' , 20_000 ) ;
391+ var builder = new StringBuilder ( ) ;
392+ builder . Append ( "remove\n " ) ;
393+ builder . Append ( longLine ) ;
394+ builder . Append ( "\n remove" ) ;
395+
396+ builder . FilterLines ( line => line == "remove" ) ;
397+
398+ Assert . Equal ( longLine , builder . ToString ( ) ) ;
399+ }
400+
401+ [ Fact ]
402+ public void FilterLines_MultipleChunksAllRemoved ( )
403+ {
404+ var builder = new StringBuilder ( ) ;
405+ for ( var i = 0 ; i < 10 ; i ++ )
406+ {
407+ builder . Append ( new string ( 'a' , 2000 ) ) ;
408+ builder . Append ( '\n ' ) ;
409+ }
410+
411+ builder . FilterLines ( _ => true ) ;
412+
413+ Assert . Equal ( string . Empty , builder . ToString ( ) ) ;
414+ }
415+
416+ [ Fact ]
417+ public void FilterLines_MultipleChunksNoneRemoved ( )
418+ {
419+ var builder = new StringBuilder ( ) ;
420+ for ( var i = 0 ; i < 10 ; i ++ )
421+ {
422+ var line = new string ( ( char ) ( 'a' + i ) , 2000 ) ;
423+ builder . Append ( line ) ;
424+ builder . Append ( '\n ' ) ;
425+ }
426+
427+ var original = builder . ToString ( ) ;
428+
429+ builder . FilterLines ( _ => false ) ;
430+
431+ Assert . Equal ( original , builder . ToString ( ) ) ;
432+ }
433+
434+ [ Fact ]
435+ public void FilterLines_ChunkBoundaryInMiddleOfLine ( )
436+ {
437+ // Specifically target chunk boundary within line content
438+ var prefix = new string ( 'p' , 7990 ) ;
439+ var suffix = new string ( 's' , 100 ) ;
440+ var fullLine = prefix + suffix ;
441+
442+ var builder = new StringBuilder ( ) ;
443+ builder . Append ( "before\n " ) ;
444+ builder . Append ( fullLine ) ;
445+ builder . Append ( "\n after" ) ;
446+
447+ builder . FilterLines ( line => line == "before" ) ;
448+
449+ Assert . Equal ( fullLine + "\n after" , builder . ToString ( ) ) ;
450+ }
451+
452+ [ Fact ]
453+ public void FilterLines_EmptyLinesAroundChunkBoundaries ( )
454+ {
455+ var longLine = new string ( 'x' , 8000 ) ;
456+ var builder = new StringBuilder ( ) ;
457+ builder . Append ( longLine ) ;
458+ builder . Append ( "\n \n \n " ) ;
459+ builder . Append ( "keep" ) ;
460+
461+ builder . FilterLines ( string . IsNullOrEmpty ) ;
462+
463+ Assert . Equal ( longLine + "\n keep" , builder . ToString ( ) ) ;
464+ }
315465}
0 commit comments