@@ -6,6 +6,8 @@ struct Mismatch {
66 pub line_number_actual : usize ,
77 pub expected : Vec < Vec < u8 > > ,
88 pub actual : Vec < Vec < u8 > > ,
9+ pub expected_missing_nl : bool ,
10+ pub actual_missing_nl : bool ,
911}
1012
1113impl Mismatch {
@@ -15,6 +17,8 @@ impl Mismatch {
1517 line_number_actual,
1618 expected : Vec :: new ( ) ,
1719 actual : Vec :: new ( ) ,
20+ expected_missing_nl : false ,
21+ actual_missing_nl : false ,
1822 }
1923 }
2024}
@@ -31,8 +35,8 @@ fn make_diff(expected: &[u8], actual: &[u8]) -> Vec<Mismatch> {
3135
3236 debug_assert_eq ! ( b"" . split( |& c| c == b'\n' ) . count( ) , 1 ) ;
3337 // ^ means that underflow here is impossible
34- let _expected_lines_count = expected_lines. len ( ) - 1 ;
35- let _actual_lines_count = actual_lines. len ( ) - 1 ;
38+ let expected_lines_count = expected_lines. len ( ) - 1 ;
39+ let actual_lines_count = actual_lines. len ( ) - 1 ;
3640
3741 if expected_lines. last ( ) == Some ( & & b"" [ ..] ) {
3842 expected_lines. pop ( ) ;
@@ -45,26 +49,46 @@ fn make_diff(expected: &[u8], actual: &[u8]) -> Vec<Mismatch> {
4549 for result in diff:: slice ( & expected_lines, & actual_lines) {
4650 match result {
4751 diff:: Result :: Left ( str) => {
48- if mismatch. actual . len ( ) != 0 {
52+ if mismatch. actual . len ( ) != 0 && !mismatch . actual_missing_nl {
4953 results. push ( mismatch) ;
5054 mismatch = Mismatch :: new ( line_number_expected, line_number_actual) ;
5155 }
5256 mismatch. expected . push ( str. to_vec ( ) ) ;
57+ mismatch. expected_missing_nl = line_number_expected > expected_lines_count;
5358 line_number_expected += 1 ;
5459 }
5560 diff:: Result :: Right ( str) => {
5661 mismatch. actual . push ( str. to_vec ( ) ) ;
62+ mismatch. actual_missing_nl = line_number_actual > actual_lines_count;
5763 line_number_actual += 1 ;
5864 }
59- diff:: Result :: Both ( _str, _) => {
60- line_number_expected += 1 ;
61- line_number_actual += 1 ;
62- if mismatch. actual . len ( ) != 0 || mismatch. expected . len ( ) != 0 {
63- results. push ( mismatch) ;
64- mismatch = Mismatch :: new ( line_number_expected, line_number_actual) ;
65- } else {
66- mismatch. line_number_expected = line_number_expected;
67- mismatch. line_number_actual = line_number_actual;
65+ diff:: Result :: Both ( str, _) => {
66+ match ( line_number_expected > expected_lines_count, line_number_actual > actual_lines_count) {
67+ ( true , false ) => {
68+ line_number_expected += 1 ;
69+ line_number_actual += 1 ;
70+ mismatch. expected . push ( str. to_vec ( ) ) ;
71+ mismatch. expected_missing_nl = true ;
72+ mismatch. actual . push ( str. to_vec ( ) ) ;
73+ }
74+ ( false , true ) => {
75+ line_number_expected += 1 ;
76+ line_number_actual += 1 ;
77+ mismatch. actual . push ( str. to_vec ( ) ) ;
78+ mismatch. actual_missing_nl = true ;
79+ mismatch. expected . push ( str. to_vec ( ) ) ;
80+ }
81+ ( true , true ) | ( false , false ) => {
82+ line_number_expected += 1 ;
83+ line_number_actual += 1 ;
84+ if mismatch. actual . len ( ) != 0 || mismatch. expected . len ( ) != 0 {
85+ results. push ( mismatch) ;
86+ mismatch = Mismatch :: new ( line_number_expected, line_number_actual) ;
87+ } else {
88+ mismatch. line_number_expected = line_number_expected;
89+ mismatch. line_number_actual = line_number_actual;
90+ }
91+ } ,
6892 }
6993 }
7094 }
@@ -118,6 +142,9 @@ pub fn diff(expected: &[u8], actual: &[u8]) -> Vec<u8> {
118142 output. write_all ( expected) . unwrap ( ) ;
119143 writeln ! ( & mut output, "" ) . unwrap ( ) ;
120144 }
145+ if result. expected_missing_nl {
146+ writeln ! ( & mut output, r"\ No newline at end of file" ) . unwrap ( ) ;
147+ }
121148 if expected_count != 0 && actual_count != 0 {
122149 writeln ! ( & mut output, "---" ) . unwrap ( ) ;
123150 }
@@ -126,6 +153,9 @@ pub fn diff(expected: &[u8], actual: &[u8]) -> Vec<u8> {
126153 output. write_all ( actual) . unwrap ( ) ;
127154 writeln ! ( & mut output, "" ) . unwrap ( ) ;
128155 }
156+ if result. actual_missing_nl {
157+ writeln ! ( & mut output, r"\ No newline at end of file" ) . unwrap ( ) ;
158+ }
129159 }
130160 output
131161}
@@ -209,6 +239,101 @@ fn test_permutations() {
209239 }
210240}
211241
242+ #[ test]
243+ fn test_permutations_missing_line_ending ( ) {
244+ // test all possible six-line files with missing newlines.
245+ let _ = std:: fs:: create_dir ( "target" ) ;
246+ for & a in & [ 0 , 1 , 2 ] {
247+ for & b in & [ 0 , 1 , 2 ] {
248+ for & c in & [ 0 , 1 , 2 ] {
249+ for & d in & [ 0 , 1 , 2 ] {
250+ for & e in & [ 0 , 1 , 2 ] {
251+ for & f in & [ 0 , 1 , 2 ] {
252+ for & g in & [ 0 , 1 , 2 ] {
253+ use std:: fs:: { self , File } ;
254+ use std:: io:: Write ;
255+ use std:: process:: Command ;
256+ let mut alef = Vec :: new ( ) ;
257+ let mut bet = Vec :: new ( ) ;
258+ alef. write_all ( if a == 0 { b"a\n " } else { b"b\n " } )
259+ . unwrap ( ) ;
260+ if a != 2 {
261+ bet. write_all ( b"b\n " ) . unwrap ( ) ;
262+ }
263+ alef. write_all ( if b == 0 { b"c\n " } else { b"d\n " } )
264+ . unwrap ( ) ;
265+ if b != 2 {
266+ bet. write_all ( b"d\n " ) . unwrap ( ) ;
267+ }
268+ alef. write_all ( if c == 0 { b"e\n " } else { b"f\n " } )
269+ . unwrap ( ) ;
270+ if c != 2 {
271+ bet. write_all ( b"f\n " ) . unwrap ( ) ;
272+ }
273+ alef. write_all ( if d == 0 { b"g\n " } else { b"h\n " } )
274+ . unwrap ( ) ;
275+ if d != 2 {
276+ bet. write_all ( b"h\n " ) . unwrap ( ) ;
277+ }
278+ alef. write_all ( if e == 0 { b"i\n " } else { b"j\n " } )
279+ . unwrap ( ) ;
280+ if e != 2 {
281+ bet. write_all ( b"j\n " ) . unwrap ( ) ;
282+ }
283+ alef. write_all ( if f == 0 { b"k\n " } else { b"l\n " } )
284+ . unwrap ( ) ;
285+ if f != 2 {
286+ bet. write_all ( b"l\n " ) . unwrap ( ) ;
287+ }
288+ match g {
289+ 0 => {
290+ alef. pop ( ) ;
291+ }
292+ 1 => {
293+ bet. pop ( ) ;
294+ }
295+ 2 => {
296+ alef. pop ( ) ;
297+ bet. pop ( ) ;
298+ }
299+ _ => unreachable ! ( ) ,
300+ }
301+ // This test diff is intentionally reversed.
302+ // We want it to turn the alef into bet.
303+ let diff = diff ( & alef, & bet) ;
304+ File :: create ( "target/abn.diff" )
305+ . unwrap ( )
306+ . write_all ( & diff)
307+ . unwrap ( ) ;
308+ let mut fa = File :: create ( "target/alefn" ) . unwrap ( ) ;
309+ fa. write_all ( & alef[ ..] ) . unwrap ( ) ;
310+ let mut fb = File :: create ( "target/betn" ) . unwrap ( ) ;
311+ fb. write_all ( & bet[ ..] ) . unwrap ( ) ;
312+ let _ = fa;
313+ let _ = fb;
314+ let output = Command :: new ( "patch" )
315+ . arg ( "-p0" )
316+ . arg ( "--normal" )
317+ . arg ( "target/alefn" )
318+ . stdin ( File :: open ( "target/abn.diff" ) . unwrap ( ) )
319+ . output ( )
320+ . unwrap ( ) ;
321+ if !output. status . success ( ) {
322+ panic ! ( "{:?}" , output) ;
323+ }
324+ //println!("{}", String::from_utf8_lossy(&output.stdout));
325+ //println!("{}", String::from_utf8_lossy(&output.stderr));
326+ let alef = fs:: read ( "target/alefn" ) . unwrap ( ) ;
327+ assert_eq ! ( alef, bet) ;
328+ }
329+ }
330+ }
331+ }
332+ }
333+ }
334+ }
335+ }
336+
212337#[ test]
213338fn test_permutations_empty_lines ( ) {
214339 // test all possible six-line files with missing newlines.
0 commit comments