1 | use std::collections::VecDeque; |
2 | use std::io::Write; |
3 | |
4 | #[derive (Debug, PartialEq)] |
5 | pub enum DiffLine { |
6 | Context(Vec<u8>), |
7 | Expected(Vec<u8>), |
8 | Actual(Vec<u8>), |
9 | MissingNL, |
10 | } |
11 | |
12 | #[derive (Debug, PartialEq)] |
13 | struct Mismatch { |
14 | pub line_number_expected: u32, |
15 | pub line_number_actual: u32, |
16 | pub lines: Vec<DiffLine>, |
17 | } |
18 | |
19 | impl Mismatch { |
20 | fn new(line_number_expected: u32, line_number_actual: u32) -> Mismatch { |
21 | Mismatch { |
22 | line_number_expected, |
23 | line_number_actual, |
24 | lines: Vec::new(), |
25 | } |
26 | } |
27 | } |
28 | |
29 | // Produces a diff between the expected output and actual output. |
30 | fn make_diff(expected: &[u8], actual: &[u8], context_size: usize) -> Vec<Mismatch> { |
31 | let mut line_number_expected = 1; |
32 | let mut line_number_actual = 1; |
33 | let mut context_queue: VecDeque<&[u8]> = VecDeque::with_capacity(context_size); |
34 | let mut lines_since_mismatch = context_size + 1; |
35 | let mut results = Vec::new(); |
36 | let mut mismatch = Mismatch::new(0, 0); |
37 | |
38 | let mut expected_lines: Vec<&[u8]> = expected.split(|&c| c == b' \n' ).collect(); |
39 | let mut actual_lines: Vec<&[u8]> = actual.split(|&c| c == b' \n' ).collect(); |
40 | |
41 | debug_assert_eq!(b"" .split(|&c| c == b' \n' ).count(), 1); |
42 | // ^ means that underflow here is impossible |
43 | let expected_lines_count = expected_lines.len() as u32 - 1; |
44 | let actual_lines_count = actual_lines.len() as u32 - 1; |
45 | |
46 | if expected_lines.last() == Some(&&b"" [..]) { |
47 | expected_lines.pop(); |
48 | } |
49 | |
50 | if actual_lines.last() == Some(&&b"" [..]) { |
51 | actual_lines.pop(); |
52 | } |
53 | |
54 | for result in diff::slice(&expected_lines, &actual_lines) { |
55 | match result { |
56 | diff::Result::Left(str) => { |
57 | if lines_since_mismatch >= context_size && lines_since_mismatch > 0 { |
58 | results.push(mismatch); |
59 | mismatch = Mismatch::new( |
60 | line_number_expected - context_queue.len() as u32, |
61 | line_number_actual - context_queue.len() as u32, |
62 | ); |
63 | } |
64 | |
65 | while let Some(line) = context_queue.pop_front() { |
66 | mismatch.lines.push(DiffLine::Context(line.to_vec())); |
67 | } |
68 | |
69 | if mismatch.lines.last() == Some(&DiffLine::MissingNL) { |
70 | mismatch.lines.pop(); |
71 | match mismatch.lines.pop() { |
72 | Some(DiffLine::Actual(res)) => { |
73 | // We have to make sure that Actual (the + lines) |
74 | // always come after Expected (the - lines) |
75 | mismatch.lines.push(DiffLine::Expected(str.to_vec())); |
76 | if line_number_expected > expected_lines_count { |
77 | mismatch.lines.push(DiffLine::MissingNL) |
78 | } |
79 | mismatch.lines.push(DiffLine::Actual(res)); |
80 | mismatch.lines.push(DiffLine::MissingNL); |
81 | } |
82 | _ => unreachable!("unterminated Left and Common lines shouldn't be followed by more Left lines" ), |
83 | } |
84 | } else { |
85 | mismatch.lines.push(DiffLine::Expected(str.to_vec())); |
86 | if line_number_expected > expected_lines_count { |
87 | mismatch.lines.push(DiffLine::MissingNL) |
88 | } |
89 | } |
90 | line_number_expected += 1; |
91 | lines_since_mismatch = 0; |
92 | } |
93 | diff::Result::Right(str) => { |
94 | if lines_since_mismatch >= context_size && lines_since_mismatch > 0 { |
95 | results.push(mismatch); |
96 | mismatch = Mismatch::new( |
97 | line_number_expected - context_queue.len() as u32, |
98 | line_number_actual - context_queue.len() as u32, |
99 | ); |
100 | } |
101 | |
102 | while let Some(line) = context_queue.pop_front() { |
103 | debug_assert!(mismatch.lines.last() != Some(&DiffLine::MissingNL)); |
104 | mismatch.lines.push(DiffLine::Context(line.to_vec())); |
105 | } |
106 | |
107 | mismatch.lines.push(DiffLine::Actual(str.to_vec())); |
108 | if line_number_actual > actual_lines_count { |
109 | mismatch.lines.push(DiffLine::MissingNL) |
110 | } |
111 | line_number_actual += 1; |
112 | lines_since_mismatch = 0; |
113 | } |
114 | diff::Result::Both(str, _) => { |
115 | // if one of them is missing a newline and the other isn't, then they don't actually match |
116 | if (line_number_actual > actual_lines_count) |
117 | && (line_number_expected > expected_lines_count) |
118 | { |
119 | if context_queue.len() < context_size { |
120 | while let Some(line) = context_queue.pop_front() { |
121 | debug_assert!(mismatch.lines.last() != Some(&DiffLine::MissingNL)); |
122 | mismatch.lines.push(DiffLine::Context(line.to_vec())); |
123 | } |
124 | if lines_since_mismatch < context_size { |
125 | mismatch.lines.push(DiffLine::Context(str.to_vec())); |
126 | mismatch.lines.push(DiffLine::MissingNL); |
127 | } |
128 | } |
129 | lines_since_mismatch = 0; |
130 | } else if line_number_actual > actual_lines_count { |
131 | if lines_since_mismatch >= context_size && lines_since_mismatch > 0 { |
132 | results.push(mismatch); |
133 | mismatch = Mismatch::new( |
134 | line_number_expected - context_queue.len() as u32, |
135 | line_number_actual - context_queue.len() as u32, |
136 | ); |
137 | } |
138 | while let Some(line) = context_queue.pop_front() { |
139 | debug_assert!(mismatch.lines.last() != Some(&DiffLine::MissingNL)); |
140 | mismatch.lines.push(DiffLine::Context(line.to_vec())); |
141 | } |
142 | mismatch.lines.push(DiffLine::Expected(str.to_vec())); |
143 | mismatch.lines.push(DiffLine::Actual(str.to_vec())); |
144 | mismatch.lines.push(DiffLine::MissingNL); |
145 | lines_since_mismatch = 0; |
146 | } else if line_number_expected > expected_lines_count { |
147 | if lines_since_mismatch >= context_size && lines_since_mismatch > 0 { |
148 | results.push(mismatch); |
149 | mismatch = Mismatch::new( |
150 | line_number_expected - context_queue.len() as u32, |
151 | line_number_actual - context_queue.len() as u32, |
152 | ); |
153 | } |
154 | while let Some(line) = context_queue.pop_front() { |
155 | debug_assert!(mismatch.lines.last() != Some(&DiffLine::MissingNL)); |
156 | mismatch.lines.push(DiffLine::Context(line.to_vec())); |
157 | } |
158 | mismatch.lines.push(DiffLine::Expected(str.to_vec())); |
159 | mismatch.lines.push(DiffLine::MissingNL); |
160 | mismatch.lines.push(DiffLine::Actual(str.to_vec())); |
161 | lines_since_mismatch = 0; |
162 | } else { |
163 | debug_assert!(context_queue.len() <= context_size); |
164 | if context_queue.len() >= context_size { |
165 | let _ = context_queue.pop_front(); |
166 | } |
167 | if lines_since_mismatch < context_size { |
168 | mismatch.lines.push(DiffLine::Context(str.to_vec())); |
169 | } else if context_size > 0 { |
170 | context_queue.push_back(str); |
171 | } |
172 | lines_since_mismatch += 1; |
173 | } |
174 | line_number_expected += 1; |
175 | line_number_actual += 1; |
176 | } |
177 | } |
178 | } |
179 | |
180 | results.push(mismatch); |
181 | results.remove(0); |
182 | |
183 | if results.len() == 0 && expected_lines_count != actual_lines_count { |
184 | let mut mismatch = Mismatch::new(expected_lines.len() as u32, actual_lines.len() as u32); |
185 | // empty diff and only expected lines has a missing line at end |
186 | if expected_lines_count != expected_lines.len() as u32 { |
187 | mismatch.lines.push(DiffLine::Expected( |
188 | expected_lines |
189 | .pop() |
190 | .expect("can't be empty; produced by split()" ) |
191 | .to_vec(), |
192 | )); |
193 | mismatch.lines.push(DiffLine::MissingNL); |
194 | mismatch.lines.push(DiffLine::Actual( |
195 | actual_lines |
196 | .pop() |
197 | .expect("can't be empty; produced by split()" ) |
198 | .to_vec(), |
199 | )); |
200 | results.push(mismatch); |
201 | } else if actual_lines_count != actual_lines.len() as u32 { |
202 | mismatch.lines.push(DiffLine::Expected( |
203 | expected_lines |
204 | .pop() |
205 | .expect("can't be empty; produced by split()" ) |
206 | .to_vec(), |
207 | )); |
208 | mismatch.lines.push(DiffLine::Actual( |
209 | actual_lines |
210 | .pop() |
211 | .expect("can't be empty; produced by split()" ) |
212 | .to_vec(), |
213 | )); |
214 | mismatch.lines.push(DiffLine::MissingNL); |
215 | results.push(mismatch); |
216 | } |
217 | } |
218 | |
219 | results |
220 | } |
221 | |
222 | pub fn diff( |
223 | expected: &[u8], |
224 | expected_filename: &str, |
225 | actual: &[u8], |
226 | actual_filename: &str, |
227 | context_size: usize, |
228 | ) -> Vec<u8> { |
229 | let mut output = |
230 | format!("--- {}\t\n+++ {}\t\n" , expected_filename, actual_filename).into_bytes(); |
231 | let diff_results = make_diff(expected, actual, context_size); |
232 | if diff_results.len() == 0 { |
233 | return Vec::new(); |
234 | }; |
235 | for result in diff_results { |
236 | let mut line_number_expected = result.line_number_expected; |
237 | let mut line_number_actual = result.line_number_actual; |
238 | let mut expected_count = 0; |
239 | let mut actual_count = 0; |
240 | for line in &result.lines { |
241 | match line { |
242 | DiffLine::Expected(_) => { |
243 | expected_count += 1; |
244 | } |
245 | DiffLine::Context(_) => { |
246 | expected_count += 1; |
247 | actual_count += 1; |
248 | } |
249 | DiffLine::Actual(_) => { |
250 | actual_count += 1; |
251 | } |
252 | DiffLine::MissingNL => {} |
253 | } |
254 | } |
255 | // Let's imagine this diff file |
256 | // |
257 | // --- a/something |
258 | // +++ b/something |
259 | // @@ -2,0 +3,1 @@ |
260 | // + x |
261 | // |
262 | // In the unified diff format as implemented by GNU diff and patch, |
263 | // this is an instruction to insert the x *after* the preexisting line 2, |
264 | // not before. You can demonstrate it this way: |
265 | // |
266 | // $ echo -ne '--- a/something\t\n+++ b/something\t\n@@ -2,0 +3,1 @@\n+ x\n' > diff |
267 | // $ echo -ne 'a\nb\nc\nd\n' > something |
268 | // $ patch -p1 < diff |
269 | // patching file something |
270 | // $ cat something |
271 | // a |
272 | // b |
273 | // x |
274 | // c |
275 | // d |
276 | // |
277 | // Notice how the x winds up at line 3, not line 2. This requires contortions to |
278 | // work with our diffing algorithm, which keeps track of the "intended destination line", |
279 | // not a line that things are supposed to be placed after. It's changing the first number, |
280 | // not the second, that actually affects where the x goes. |
281 | // |
282 | // # change the first number from 2 to 3, and now the x is on line 4 (it's placed after line 3) |
283 | // $ echo -ne '--- a/something\t\n+++ b/something\t\n@@ -3,0 +3,1 @@\n+ x\n' > diff |
284 | // $ echo -ne 'a\nb\nc\nd\n' > something |
285 | // $ patch -p1 < diff |
286 | // patching file something |
287 | // $ cat something |
288 | // a |
289 | // b |
290 | // c |
291 | // x |
292 | // d |
293 | // # change the third number from 3 to 1000, and it's obvious that it's the first number that's |
294 | // # actually being read |
295 | // $ echo -ne '--- a/something\t\n+++ b/something\t\n@@ -2,0 +1000,1 @@\n+ x\n' > diff |
296 | // $ echo -ne 'a\nb\nc\nd\n' > something |
297 | // $ patch -p1 < diff |
298 | // patching file something |
299 | // $ cat something |
300 | // a |
301 | // b |
302 | // x |
303 | // c |
304 | // d |
305 | // |
306 | // Now watch what happens if I add a context line: |
307 | // |
308 | // $ echo -ne '--- a/something\t\n+++ b/something\t\n@@ -2,1 +3,2 @@\n+ x\n c\n' > diff |
309 | // $ echo -ne 'a\nb\nc\nd\n' > something |
310 | // $ patch -p1 < diff |
311 | // patching file something |
312 | // Hunk #1 succeeded at 3 (offset 1 line). |
313 | // |
314 | // It technically "succeeded", but this is a warning. We want to produce clean diffs. |
315 | // Now that I have a context line, I'm supposed to say what line it's actually on, which is the |
316 | // line that the x will wind up on, and not the line immediately before. |
317 | // |
318 | // $ echo -ne '--- a/something\t\n+++ b/something\t\n@@ -3,1 +3,2 @@\n+ x\n c\n' > diff |
319 | // $ echo -ne 'a\nb\nc\nd\n' > something |
320 | // $ patch -p1 < diff |
321 | // patching file something |
322 | // $ cat something |
323 | // a |
324 | // b |
325 | // x |
326 | // c |
327 | // d |
328 | // |
329 | // I made this comment because this stuff is not obvious from GNU's |
330 | // documentation on the format at all. |
331 | if expected_count == 0 { |
332 | line_number_expected -= 1 |
333 | } |
334 | if actual_count == 0 { |
335 | line_number_actual -= 1 |
336 | } |
337 | let exp_ct = if expected_count == 1 { |
338 | String::new() |
339 | } else { |
340 | format!(", {}" , expected_count) |
341 | }; |
342 | let act_ct = if actual_count == 1 { |
343 | String::new() |
344 | } else { |
345 | format!(", {}" , actual_count) |
346 | }; |
347 | writeln!( |
348 | output, |
349 | "@@ - {}{} + {}{} @@" , |
350 | line_number_expected, exp_ct, line_number_actual, act_ct |
351 | ) |
352 | .expect("write to Vec is infallible" ); |
353 | for line in result.lines { |
354 | match line { |
355 | DiffLine::Expected(e) => { |
356 | write!(output, "-" ).expect("write to Vec is infallible" ); |
357 | output.write_all(&e).expect("write to Vec is infallible" ); |
358 | writeln!(output).unwrap(); |
359 | } |
360 | DiffLine::Context(c) => { |
361 | write!(output, " " ).expect("write to Vec is infallible" ); |
362 | output.write_all(&c).expect("write to Vec is infallible" ); |
363 | writeln!(output).unwrap(); |
364 | } |
365 | DiffLine::Actual(r) => { |
366 | write!(output, "+" ,).expect("write to Vec is infallible" ); |
367 | output.write_all(&r).expect("write to Vec is infallible" ); |
368 | writeln!(output).unwrap(); |
369 | } |
370 | DiffLine::MissingNL => { |
371 | writeln!(output, r"\ No newline at end of file" ) |
372 | .expect("write to Vec is infallible" ); |
373 | } |
374 | } |
375 | } |
376 | } |
377 | output |
378 | } |
379 | |
380 | #[test ] |
381 | fn test_permutations() { |
382 | // test all possible six-line files. |
383 | for &a in &[0, 1, 2] { |
384 | for &b in &[0, 1, 2] { |
385 | for &c in &[0, 1, 2] { |
386 | for &d in &[0, 1, 2] { |
387 | for &e in &[0, 1, 2] { |
388 | for &f in &[0, 1, 2] { |
389 | use std::fs::{self, File}; |
390 | use std::io::Write; |
391 | use std::process::Command; |
392 | let mut alef = Vec::new(); |
393 | let mut bet = Vec::new(); |
394 | alef.write_all(if a == 0 { b"a \n" } else { b"b \n" }) |
395 | .unwrap(); |
396 | if a != 2 { |
397 | bet.write_all(b"b \n" ).unwrap(); |
398 | } |
399 | alef.write_all(if b == 0 { b"c \n" } else { b"d \n" }) |
400 | .unwrap(); |
401 | if b != 2 { |
402 | bet.write_all(b"d \n" ).unwrap(); |
403 | } |
404 | alef.write_all(if c == 0 { b"e \n" } else { b"f \n" }) |
405 | .unwrap(); |
406 | if c != 2 { |
407 | bet.write_all(b"f \n" ).unwrap(); |
408 | } |
409 | alef.write_all(if d == 0 { b"g \n" } else { b"h \n" }) |
410 | .unwrap(); |
411 | if d != 2 { |
412 | bet.write_all(b"h \n" ).unwrap(); |
413 | } |
414 | alef.write_all(if e == 0 { b"i \n" } else { b"j \n" }) |
415 | .unwrap(); |
416 | if e != 2 { |
417 | bet.write_all(b"j \n" ).unwrap(); |
418 | } |
419 | alef.write_all(if f == 0 { b"k \n" } else { b"l \n" }) |
420 | .unwrap(); |
421 | if f != 2 { |
422 | bet.write_all(b"l \n" ).unwrap(); |
423 | } |
424 | // This test diff is intentionally reversed. |
425 | // We want it to turn the alef into bet. |
426 | let diff = diff(&alef, "a/alef" , &bet, "target/alef" , 2); |
427 | File::create("target/ab.diff" ) |
428 | .unwrap() |
429 | .write_all(&diff) |
430 | .unwrap(); |
431 | let mut fa = File::create("target/alef" ).unwrap(); |
432 | fa.write_all(&alef[..]).unwrap(); |
433 | let mut fb = File::create("target/bet" ).unwrap(); |
434 | fb.write_all(&bet[..]).unwrap(); |
435 | let _ = fa; |
436 | let _ = fb; |
437 | let output = Command::new("patch" ) |
438 | .arg("-p0" ) |
439 | .stdin(File::open("target/ab.diff" ).unwrap()) |
440 | .output() |
441 | .unwrap(); |
442 | if !output.status.success() { |
443 | panic!(" {:?}" , output); |
444 | } |
445 | //println!("{}", String::from_utf8_lossy(&output.stdout)); |
446 | //println!("{}", String::from_utf8_lossy(&output.stderr)); |
447 | let alef = fs::read("target/alef" ).unwrap(); |
448 | assert_eq!(alef, bet); |
449 | } |
450 | } |
451 | } |
452 | } |
453 | } |
454 | } |
455 | } |
456 | |
457 | #[test ] |
458 | fn test_permutations_missing_line_ending() { |
459 | // test all possible six-line files with missing newlines. |
460 | for &a in &[0, 1, 2] { |
461 | for &b in &[0, 1, 2] { |
462 | for &c in &[0, 1, 2] { |
463 | for &d in &[0, 1, 2] { |
464 | for &e in &[0, 1, 2] { |
465 | for &f in &[0, 1, 2] { |
466 | for &g in &[0, 1, 2] { |
467 | use std::fs::{self, File}; |
468 | use std::io::Write; |
469 | use std::process::Command; |
470 | let mut alef = Vec::new(); |
471 | let mut bet = Vec::new(); |
472 | alef.write_all(if a == 0 { b"a \n" } else { b"b \n" }) |
473 | .unwrap(); |
474 | if a != 2 { |
475 | bet.write_all(b"b \n" ).unwrap(); |
476 | } |
477 | alef.write_all(if b == 0 { b"c \n" } else { b"d \n" }) |
478 | .unwrap(); |
479 | if b != 2 { |
480 | bet.write_all(b"d \n" ).unwrap(); |
481 | } |
482 | alef.write_all(if c == 0 { b"e \n" } else { b"f \n" }) |
483 | .unwrap(); |
484 | if c != 2 { |
485 | bet.write_all(b"f \n" ).unwrap(); |
486 | } |
487 | alef.write_all(if d == 0 { b"g \n" } else { b"h \n" }) |
488 | .unwrap(); |
489 | if d != 2 { |
490 | bet.write_all(b"h \n" ).unwrap(); |
491 | } |
492 | alef.write_all(if e == 0 { b"i \n" } else { b"j \n" }) |
493 | .unwrap(); |
494 | if e != 2 { |
495 | bet.write_all(b"j \n" ).unwrap(); |
496 | } |
497 | alef.write_all(if f == 0 { b"k \n" } else { b"l \n" }) |
498 | .unwrap(); |
499 | if f != 2 { |
500 | bet.write_all(b"l \n" ).unwrap(); |
501 | } |
502 | match g { |
503 | 0 => { |
504 | alef.pop(); |
505 | } |
506 | 1 => { |
507 | bet.pop(); |
508 | } |
509 | 2 => { |
510 | alef.pop(); |
511 | bet.pop(); |
512 | } |
513 | _ => unreachable!(), |
514 | } |
515 | // This test diff is intentionally reversed. |
516 | // We want it to turn the alef into bet. |
517 | let diff = diff(&alef, "a/alefn" , &bet, "target/alefn" , 2); |
518 | File::create("target/abn.diff" ) |
519 | .unwrap() |
520 | .write_all(&diff) |
521 | .unwrap(); |
522 | let mut fa = File::create("target/alefn" ).unwrap(); |
523 | fa.write_all(&alef[..]).unwrap(); |
524 | let mut fb = File::create("target/betn" ).unwrap(); |
525 | fb.write_all(&bet[..]).unwrap(); |
526 | let _ = fa; |
527 | let _ = fb; |
528 | let output = Command::new("patch" ) |
529 | .arg("-p0" ) |
530 | .stdin(File::open("target/abn.diff" ).unwrap()) |
531 | .output() |
532 | .unwrap(); |
533 | if !output.status.success() { |
534 | panic!(" {:?}" , output); |
535 | } |
536 | //println!("{}", String::from_utf8_lossy(&output.stdout)); |
537 | //println!("{}", String::from_utf8_lossy(&output.stderr)); |
538 | let alef = fs::read("target/alefn" ).unwrap(); |
539 | assert_eq!(alef, bet); |
540 | } |
541 | } |
542 | } |
543 | } |
544 | } |
545 | } |
546 | } |
547 | } |
548 | |
549 | #[test ] |
550 | fn test_permutations_empty_lines() { |
551 | // test all possible six-line files with missing newlines. |
552 | for &a in &[0, 1, 2] { |
553 | for &b in &[0, 1, 2] { |
554 | for &c in &[0, 1, 2] { |
555 | for &d in &[0, 1, 2] { |
556 | for &e in &[0, 1, 2] { |
557 | for &f in &[0, 1, 2] { |
558 | for &g in &[0, 1, 2, 3] { |
559 | use std::fs::{self, File}; |
560 | use std::io::Write; |
561 | use std::process::Command; |
562 | let mut alef = Vec::new(); |
563 | let mut bet = Vec::new(); |
564 | alef.write_all(if a == 0 { b" \n" } else { b"b \n" }).unwrap(); |
565 | if a != 2 { |
566 | bet.write_all(b"b \n" ).unwrap(); |
567 | } |
568 | alef.write_all(if b == 0 { b" \n" } else { b"d \n" }).unwrap(); |
569 | if b != 2 { |
570 | bet.write_all(b"d \n" ).unwrap(); |
571 | } |
572 | alef.write_all(if c == 0 { b" \n" } else { b"f \n" }).unwrap(); |
573 | if c != 2 { |
574 | bet.write_all(b"f \n" ).unwrap(); |
575 | } |
576 | alef.write_all(if d == 0 { b" \n" } else { b"h \n" }).unwrap(); |
577 | if d != 2 { |
578 | bet.write_all(b"h \n" ).unwrap(); |
579 | } |
580 | alef.write_all(if e == 0 { b" \n" } else { b"j \n" }).unwrap(); |
581 | if e != 2 { |
582 | bet.write_all(b"j \n" ).unwrap(); |
583 | } |
584 | alef.write_all(if f == 0 { b" \n" } else { b"l \n" }).unwrap(); |
585 | if f != 2 { |
586 | bet.write_all(b"l \n" ).unwrap(); |
587 | } |
588 | match g { |
589 | 0 => { |
590 | alef.pop(); |
591 | } |
592 | 1 => { |
593 | bet.pop(); |
594 | } |
595 | 2 => { |
596 | alef.pop(); |
597 | bet.pop(); |
598 | } |
599 | 3 => {} |
600 | _ => unreachable!(), |
601 | } |
602 | // This test diff is intentionally reversed. |
603 | // We want it to turn the alef into bet. |
604 | let diff = diff(&alef, "a/alef_" , &bet, "target/alef_" , 2); |
605 | File::create("target/ab_.diff" ) |
606 | .unwrap() |
607 | .write_all(&diff) |
608 | .unwrap(); |
609 | let mut fa = File::create("target/alef_" ).unwrap(); |
610 | fa.write_all(&alef[..]).unwrap(); |
611 | let mut fb = File::create("target/bet_" ).unwrap(); |
612 | fb.write_all(&bet[..]).unwrap(); |
613 | let _ = fa; |
614 | let _ = fb; |
615 | let output = Command::new("patch" ) |
616 | .arg("-p0" ) |
617 | .stdin(File::open("target/ab_.diff" ).unwrap()) |
618 | .output() |
619 | .unwrap(); |
620 | if !output.status.success() { |
621 | panic!(" {:?}" , output); |
622 | } |
623 | //println!("{}", String::from_utf8_lossy(&output.stdout)); |
624 | //println!("{}", String::from_utf8_lossy(&output.stderr)); |
625 | let alef = fs::read("target/alef_" ).unwrap(); |
626 | assert_eq!(alef, bet); |
627 | } |
628 | } |
629 | } |
630 | } |
631 | } |
632 | } |
633 | } |
634 | } |
635 | |
636 | #[test ] |
637 | fn test_permutations_missing_lines() { |
638 | // test all possible six-line files. |
639 | for &a in &[0, 1, 2] { |
640 | for &b in &[0, 1, 2] { |
641 | for &c in &[0, 1, 2] { |
642 | for &d in &[0, 1, 2] { |
643 | for &e in &[0, 1, 2] { |
644 | for &f in &[0, 1, 2] { |
645 | use std::fs::{self, File}; |
646 | use std::io::Write; |
647 | use std::process::Command; |
648 | let mut alef = Vec::new(); |
649 | let mut bet = Vec::new(); |
650 | alef.write_all(if a == 0 { b"a \n" } else { b"" }).unwrap(); |
651 | if a != 2 { |
652 | bet.write_all(b"b \n" ).unwrap(); |
653 | } |
654 | alef.write_all(if b == 0 { b"c \n" } else { b"" }).unwrap(); |
655 | if b != 2 { |
656 | bet.write_all(b"d \n" ).unwrap(); |
657 | } |
658 | alef.write_all(if c == 0 { b"e \n" } else { b"" }).unwrap(); |
659 | if c != 2 { |
660 | bet.write_all(b"f \n" ).unwrap(); |
661 | } |
662 | alef.write_all(if d == 0 { b"g \n" } else { b"" }).unwrap(); |
663 | if d != 2 { |
664 | bet.write_all(b"h \n" ).unwrap(); |
665 | } |
666 | alef.write_all(if e == 0 { b"i \n" } else { b"" }).unwrap(); |
667 | if e != 2 { |
668 | bet.write_all(b"j \n" ).unwrap(); |
669 | } |
670 | alef.write_all(if f == 0 { b"k \n" } else { b"" }).unwrap(); |
671 | if f != 2 { |
672 | bet.write_all(b"l \n" ).unwrap(); |
673 | } |
674 | // This test diff is intentionally reversed. |
675 | // We want it to turn the alef into bet. |
676 | let diff = diff(&alef, "a/alefx" , &bet, "target/alefx" , 2); |
677 | File::create("target/abx.diff" ) |
678 | .unwrap() |
679 | .write_all(&diff) |
680 | .unwrap(); |
681 | let mut fa = File::create("target/alefx" ).unwrap(); |
682 | fa.write_all(&alef[..]).unwrap(); |
683 | let mut fb = File::create("target/betx" ).unwrap(); |
684 | fb.write_all(&bet[..]).unwrap(); |
685 | let _ = fa; |
686 | let _ = fb; |
687 | let output = Command::new("patch" ) |
688 | .arg("-p0" ) |
689 | .stdin(File::open("target/abx.diff" ).unwrap()) |
690 | .output() |
691 | .unwrap(); |
692 | if !output.status.success() { |
693 | panic!(" {:?}" , output); |
694 | } |
695 | //println!("{}", String::from_utf8_lossy(&output.stdout)); |
696 | //println!("{}", String::from_utf8_lossy(&output.stderr)); |
697 | let alef = fs::read("target/alefx" ).unwrap(); |
698 | assert_eq!(alef, bet); |
699 | } |
700 | } |
701 | } |
702 | } |
703 | } |
704 | } |
705 | } |
706 | |
707 | #[test ] |
708 | fn test_permutations_reverse() { |
709 | // test all possible six-line files. |
710 | for &a in &[0, 1, 2] { |
711 | for &b in &[0, 1, 2] { |
712 | for &c in &[0, 1, 2] { |
713 | for &d in &[0, 1, 2] { |
714 | for &e in &[0, 1, 2] { |
715 | for &f in &[0, 1, 2] { |
716 | use std::fs::{self, File}; |
717 | use std::io::Write; |
718 | use std::process::Command; |
719 | let mut alef = Vec::new(); |
720 | let mut bet = Vec::new(); |
721 | alef.write_all(if a == 0 { b"a \n" } else { b"f \n" }) |
722 | .unwrap(); |
723 | if a != 2 { |
724 | bet.write_all(b"a \n" ).unwrap(); |
725 | } |
726 | alef.write_all(if b == 0 { b"b \n" } else { b"e \n" }) |
727 | .unwrap(); |
728 | if b != 2 { |
729 | bet.write_all(b"b \n" ).unwrap(); |
730 | } |
731 | alef.write_all(if c == 0 { b"c \n" } else { b"d \n" }) |
732 | .unwrap(); |
733 | if c != 2 { |
734 | bet.write_all(b"c \n" ).unwrap(); |
735 | } |
736 | alef.write_all(if d == 0 { b"d \n" } else { b"c \n" }) |
737 | .unwrap(); |
738 | if d != 2 { |
739 | bet.write_all(b"d \n" ).unwrap(); |
740 | } |
741 | alef.write_all(if e == 0 { b"e \n" } else { b"b \n" }) |
742 | .unwrap(); |
743 | if e != 2 { |
744 | bet.write_all(b"e \n" ).unwrap(); |
745 | } |
746 | alef.write_all(if f == 0 { b"f \n" } else { b"a \n" }) |
747 | .unwrap(); |
748 | if f != 2 { |
749 | bet.write_all(b"f \n" ).unwrap(); |
750 | } |
751 | // This test diff is intentionally reversed. |
752 | // We want it to turn the alef into bet. |
753 | let diff = diff(&alef, "a/alefr" , &bet, "target/alefr" , 2); |
754 | File::create("target/abr.diff" ) |
755 | .unwrap() |
756 | .write_all(&diff) |
757 | .unwrap(); |
758 | let mut fa = File::create("target/alefr" ).unwrap(); |
759 | fa.write_all(&alef[..]).unwrap(); |
760 | let mut fb = File::create("target/betr" ).unwrap(); |
761 | fb.write_all(&bet[..]).unwrap(); |
762 | let _ = fa; |
763 | let _ = fb; |
764 | let output = Command::new("patch" ) |
765 | .arg("-p0" ) |
766 | .stdin(File::open("target/abr.diff" ).unwrap()) |
767 | .output() |
768 | .unwrap(); |
769 | if !output.status.success() { |
770 | panic!(" {:?}" , output); |
771 | } |
772 | //println!("{}", String::from_utf8_lossy(&output.stdout)); |
773 | //println!("{}", String::from_utf8_lossy(&output.stderr)); |
774 | let alef = fs::read("target/alefr" ).unwrap(); |
775 | assert_eq!(alef, bet); |
776 | } |
777 | } |
778 | } |
779 | } |
780 | } |
781 | } |
782 | } |
783 | |