@@ -95,6 +95,8 @@ mod common {
9595}
9696
9797mod diff {
98+ use std:: process:: Stdio ;
99+
98100 use diffutilslib:: assert_diff_eq;
99101
100102 use super :: * ;
@@ -341,6 +343,97 @@ mod diff {
341343
342344 Ok ( ( ) )
343345 }
346+
347+ fn str_bar_diff ( bar : & [ u8 ] ) -> String {
348+ String :: from_utf8 (
349+ bar. split ( |b| * b == b'\n' )
350+ . filter ( |b| b != b"" )
351+ . flat_map ( |b| [ b">" , b" " , b, b"\n " ] . concat ( ) )
352+ . collect :: < Vec < u8 > > ( ) ,
353+ )
354+ . unwrap ( )
355+ }
356+
357+ #[ test]
358+ fn large_similar_files ( ) -> Result < ( ) , Box < dyn std:: error:: Error > > {
359+ // Large similar files should still produce ideal diffs, not
360+ // triggering the total cost heuristic.
361+ let foo = b"f\n " . repeat ( 16 * 1024 * 1024 ) ;
362+ let bar = b"b\n " . repeat ( 26 ) ;
363+
364+ let mut file1 = NamedTempFile :: new ( ) ?;
365+ file1. write_all ( & foo) ?;
366+ file1. write_all ( & foo) ?;
367+ let mut file2 = NamedTempFile :: new ( ) ?;
368+ file2. write_all ( & foo) ?;
369+ file2. write_all ( & bar) ?;
370+ file2. write_all ( & foo) ?;
371+
372+ let mut cmd = Command :: cargo_bin ( "diffutils" ) ?;
373+ cmd. arg ( "diff" ) . arg ( file1. path ( ) ) . arg ( file2. path ( ) ) ;
374+ cmd. assert ( )
375+ . code ( predicate:: eq ( 1 ) )
376+ . failure ( )
377+ . stdout ( predicate:: eq ( format ! (
378+ "16777216a16777217,16777242\n {}" ,
379+ str_bar_diff( & bar)
380+ ) ) ) ;
381+
382+ let mut file1 = NamedTempFile :: new ( ) ?;
383+ file1. write_all ( & bar) ?;
384+ file1. write_all ( & foo) ?;
385+ file1. write_all ( & foo) ?;
386+ let mut file2 = NamedTempFile :: new ( ) ?;
387+ file2. write_all ( & foo) ?;
388+ file2. write_all ( & foo) ?;
389+
390+ let mut cmd = Command :: cargo_bin ( "diffutils" ) ?;
391+ cmd. arg ( "diff" ) . arg ( file1. path ( ) ) . arg ( file2. path ( ) ) ;
392+ cmd. assert ( )
393+ . code ( predicate:: eq ( 1 ) )
394+ . failure ( )
395+ . stdout ( predicate:: eq ( format ! (
396+ "1,26d0\n {}" ,
397+ str_bar_diff( & bar) . replace( ">" , "<" )
398+ ) ) ) ;
399+
400+ Ok ( ( ) )
401+ }
402+
403+ #[ test]
404+ fn large_different_files ( ) -> Result < ( ) , Box < dyn std:: error:: Error > > {
405+ let foo = b"f\n " . repeat ( 4 * 1024 * 1024 ) ;
406+ let bar = b"b\n " . repeat ( 26 ) ;
407+ let baz = b"z\n " . repeat ( 4 * 1024 * 1024 ) ;
408+
409+ let mut file1 = NamedTempFile :: new ( ) ?;
410+ file1. write_all ( & foo) ?;
411+ file1. write_all ( & bar) ?;
412+ let mut file2 = NamedTempFile :: new ( ) ?;
413+ file2. write_all ( & baz) ?;
414+ file2. write_all ( & bar) ?;
415+
416+ let mut child = std:: process:: Command :: new ( assert_cmd:: cargo:: cargo_bin ( "diffutils" ) )
417+ . arg ( "diff" )
418+ . arg ( file1. path ( ) )
419+ . arg ( file2. path ( ) )
420+ . stdout ( Stdio :: null ( ) )
421+ . spawn ( )
422+ . unwrap ( ) ;
423+
424+ // The total cost heuristic should give up trying to find good split points
425+ // in a reasonable amount of time (can still be a fairly big in debug builds)
426+ for retries in 0 .. {
427+ std:: thread:: sleep ( std:: time:: Duration :: from_secs ( 1 ) ) ;
428+ if let Some ( status) = child. try_wait ( ) ? {
429+ assert_eq ! ( status. code( ) , Some ( 1 ) ) ;
430+ break ;
431+ }
432+ assert ! ( retries < 10 ) ;
433+ }
434+
435+ Ok ( ( ) )
436+ }
344437}
345438
346439mod cmp {
0 commit comments