2021-02-23 22:17:58 -07:00
|
|
|
use std::collections::VecDeque;
|
|
|
|
|
use std::io::Write;
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, PartialEq)]
|
|
|
|
|
pub enum DiffLine {
|
|
|
|
|
Context(Vec<u8>),
|
|
|
|
|
Expected(Vec<u8>),
|
2021-02-24 00:23:38 -07:00
|
|
|
Actual(Vec<u8>),
|
2021-02-23 22:17:58 -07:00
|
|
|
MissingNL,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, PartialEq)]
|
|
|
|
|
struct Mismatch {
|
2021-02-24 00:23:38 -07:00
|
|
|
pub line_number_expected: u32,
|
|
|
|
|
pub line_number_actual: u32,
|
2021-02-23 22:17:58 -07:00
|
|
|
pub lines: Vec<DiffLine>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Mismatch {
|
2021-02-24 00:23:38 -07:00
|
|
|
fn new(line_number_expected: u32, line_number_actual: u32) -> Mismatch {
|
2021-02-23 22:17:58 -07:00
|
|
|
Mismatch {
|
2021-02-24 00:23:38 -07:00
|
|
|
line_number_expected,
|
|
|
|
|
line_number_actual,
|
2021-02-23 22:17:58 -07:00
|
|
|
lines: Vec::new(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Produces a diff between the expected output and actual output.
|
|
|
|
|
fn make_diff(expected: &[u8], actual: &[u8], context_size: usize) -> Vec<Mismatch> {
|
2021-02-24 00:23:38 -07:00
|
|
|
let mut line_number_expected = 1;
|
|
|
|
|
let mut line_number_actual = 1;
|
2021-02-23 22:17:58 -07:00
|
|
|
let mut context_queue: VecDeque<&[u8]> = VecDeque::with_capacity(context_size);
|
|
|
|
|
let mut lines_since_mismatch = context_size + 1;
|
|
|
|
|
let mut results = Vec::new();
|
|
|
|
|
let mut mismatch = Mismatch::new(0, 0);
|
|
|
|
|
|
|
|
|
|
let mut expected_lines: Vec<&[u8]> = expected.split(|&c| c == b'\n').collect();
|
|
|
|
|
let mut actual_lines: Vec<&[u8]> = actual.split(|&c| c == b'\n').collect();
|
|
|
|
|
|
2021-02-24 00:13:41 -07:00
|
|
|
debug_assert_eq!(b"".split(|&c| c == b'\n').count(), 1);
|
|
|
|
|
// ^ means that underflow here is impossible
|
|
|
|
|
let expected_lines_count = expected_lines.len() as u32 - 1;
|
|
|
|
|
let actual_lines_count = actual_lines.len() as u32 - 1;
|
2021-02-23 22:17:58 -07:00
|
|
|
|
|
|
|
|
if expected_lines.last() == Some(&&b""[..]) {
|
|
|
|
|
expected_lines.pop();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if actual_lines.last() == Some(&&b""[..]) {
|
|
|
|
|
actual_lines.pop();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for result in diff::slice(&expected_lines, &actual_lines) {
|
|
|
|
|
match result {
|
|
|
|
|
diff::Result::Left(str) => {
|
|
|
|
|
if lines_since_mismatch >= context_size && lines_since_mismatch > 0 {
|
|
|
|
|
results.push(mismatch);
|
|
|
|
|
mismatch = Mismatch::new(
|
2021-02-24 00:23:38 -07:00
|
|
|
line_number_expected - context_queue.len() as u32,
|
|
|
|
|
line_number_actual - context_queue.len() as u32,
|
2021-02-23 22:17:58 -07:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while let Some(line) = context_queue.pop_front() {
|
|
|
|
|
mismatch.lines.push(DiffLine::Context(line.to_vec()));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if mismatch.lines.last() == Some(&DiffLine::MissingNL) {
|
|
|
|
|
mismatch.lines.pop();
|
|
|
|
|
match mismatch.lines.pop() {
|
2021-02-24 00:23:38 -07:00
|
|
|
Some(DiffLine::Actual(res)) => {
|
|
|
|
|
// We have to make sure that Actual (the + lines)
|
2021-02-24 00:13:41 -07:00
|
|
|
// always come after Expected (the - lines)
|
2021-02-23 22:17:58 -07:00
|
|
|
mismatch.lines.push(DiffLine::Expected(str.to_vec()));
|
2021-02-24 00:23:38 -07:00
|
|
|
if line_number_expected > expected_lines_count {
|
2021-02-23 22:17:58 -07:00
|
|
|
mismatch.lines.push(DiffLine::MissingNL)
|
|
|
|
|
}
|
2021-02-24 00:23:38 -07:00
|
|
|
mismatch.lines.push(DiffLine::Actual(res));
|
2021-02-23 22:17:58 -07:00
|
|
|
mismatch.lines.push(DiffLine::MissingNL);
|
|
|
|
|
}
|
2021-02-24 00:13:41 -07:00
|
|
|
_ => unreachable!("unterminated Left and Common lines shouldn't be followed by more Left lines"),
|
2021-02-23 22:17:58 -07:00
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
mismatch.lines.push(DiffLine::Expected(str.to_vec()));
|
2021-02-24 00:23:38 -07:00
|
|
|
if line_number_expected > expected_lines_count {
|
2021-02-23 22:17:58 -07:00
|
|
|
mismatch.lines.push(DiffLine::MissingNL)
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-02-24 00:23:38 -07:00
|
|
|
line_number_expected += 1;
|
2021-02-23 22:17:58 -07:00
|
|
|
lines_since_mismatch = 0;
|
|
|
|
|
}
|
|
|
|
|
diff::Result::Right(str) => {
|
|
|
|
|
if lines_since_mismatch >= context_size && lines_since_mismatch > 0 {
|
|
|
|
|
results.push(mismatch);
|
|
|
|
|
mismatch = Mismatch::new(
|
2021-02-24 00:23:38 -07:00
|
|
|
line_number_expected - context_queue.len() as u32,
|
|
|
|
|
line_number_actual - context_queue.len() as u32,
|
2021-02-23 22:17:58 -07:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while let Some(line) = context_queue.pop_front() {
|
|
|
|
|
assert!(mismatch.lines.last() != Some(&DiffLine::MissingNL));
|
|
|
|
|
mismatch.lines.push(DiffLine::Context(line.to_vec()));
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-24 00:23:38 -07:00
|
|
|
mismatch.lines.push(DiffLine::Actual(str.to_vec()));
|
|
|
|
|
if line_number_actual > actual_lines_count {
|
2021-02-23 22:17:58 -07:00
|
|
|
mismatch.lines.push(DiffLine::MissingNL)
|
|
|
|
|
}
|
2021-02-24 00:23:38 -07:00
|
|
|
line_number_actual += 1;
|
2021-02-23 22:17:58 -07:00
|
|
|
lines_since_mismatch = 0;
|
|
|
|
|
}
|
|
|
|
|
diff::Result::Both(str, _) => {
|
2021-02-24 00:23:38 -07:00
|
|
|
if (line_number_actual > actual_lines_count)
|
|
|
|
|
|| (line_number_expected > expected_lines_count)
|
2021-02-23 22:17:58 -07:00
|
|
|
{
|
|
|
|
|
// if one of them is missing a newline and the other isn't, then they don't actually match
|
|
|
|
|
if lines_since_mismatch >= context_size
|
|
|
|
|
&& lines_since_mismatch > 0
|
2021-02-24 00:23:38 -07:00
|
|
|
&& (line_number_actual > actual_lines_count)
|
|
|
|
|
!= (line_number_expected > expected_lines_count)
|
2021-02-23 22:17:58 -07:00
|
|
|
{
|
|
|
|
|
results.push(mismatch);
|
|
|
|
|
mismatch = Mismatch::new(
|
2021-02-24 00:23:38 -07:00
|
|
|
line_number_expected - context_queue.len() as u32,
|
|
|
|
|
line_number_actual - context_queue.len() as u32,
|
2021-02-23 22:17:58 -07:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
while let Some(line) = context_queue.pop_front() {
|
|
|
|
|
assert!(mismatch.lines.last() != Some(&DiffLine::MissingNL));
|
|
|
|
|
mismatch.lines.push(DiffLine::Context(line.to_vec()));
|
|
|
|
|
}
|
|
|
|
|
lines_since_mismatch = 0;
|
2021-02-24 00:23:38 -07:00
|
|
|
if line_number_actual > actual_lines_count
|
|
|
|
|
&& line_number_expected > expected_lines_count
|
2021-02-23 22:17:58 -07:00
|
|
|
{
|
|
|
|
|
mismatch.lines.push(DiffLine::Context(str.to_vec()));
|
|
|
|
|
mismatch.lines.push(DiffLine::MissingNL);
|
2021-02-24 00:23:38 -07:00
|
|
|
} else if line_number_expected > expected_lines_count {
|
2021-02-23 22:17:58 -07:00
|
|
|
mismatch.lines.push(DiffLine::Expected(str.to_vec()));
|
|
|
|
|
mismatch.lines.push(DiffLine::MissingNL);
|
2021-02-24 00:23:38 -07:00
|
|
|
mismatch.lines.push(DiffLine::Actual(str.to_vec()));
|
|
|
|
|
} else if line_number_actual > actual_lines_count {
|
2021-02-23 22:17:58 -07:00
|
|
|
mismatch.lines.push(DiffLine::Expected(str.to_vec()));
|
2021-02-24 00:23:38 -07:00
|
|
|
mismatch.lines.push(DiffLine::Actual(str.to_vec()));
|
2021-02-23 22:17:58 -07:00
|
|
|
mismatch.lines.push(DiffLine::MissingNL);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if context_queue.len() >= context_size {
|
|
|
|
|
let _ = context_queue.pop_front();
|
|
|
|
|
}
|
|
|
|
|
if lines_since_mismatch < context_size {
|
|
|
|
|
mismatch.lines.push(DiffLine::Context(str.to_vec()));
|
|
|
|
|
} else if context_size > 0 {
|
|
|
|
|
context_queue.push_back(str);
|
|
|
|
|
}
|
|
|
|
|
lines_since_mismatch += 1;
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-24 00:23:38 -07:00
|
|
|
line_number_expected += 1;
|
|
|
|
|
line_number_actual += 1;
|
2021-02-23 22:17:58 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
results.push(mismatch);
|
|
|
|
|
results.remove(0);
|
|
|
|
|
|
|
|
|
|
if results.len() == 0 && expected_lines_count != actual_lines_count {
|
|
|
|
|
let mut mismatch = Mismatch::new(expected_lines.len() as u32, actual_lines.len() as u32);
|
|
|
|
|
// empty diff and only expected lines has a missing line at end
|
|
|
|
|
if expected_lines_count != expected_lines.len() as u32 {
|
2021-02-24 00:23:38 -07:00
|
|
|
mismatch.lines.push(DiffLine::Expected(
|
|
|
|
|
expected_lines
|
|
|
|
|
.pop()
|
|
|
|
|
.expect("can't be empty; produced by split()")
|
|
|
|
|
.to_vec(),
|
|
|
|
|
));
|
2021-02-23 22:17:58 -07:00
|
|
|
mismatch.lines.push(DiffLine::MissingNL);
|
2021-02-24 00:23:38 -07:00
|
|
|
mismatch.lines.push(DiffLine::Actual(
|
2021-02-24 00:13:41 -07:00
|
|
|
actual_lines
|
|
|
|
|
.pop()
|
2021-02-24 00:23:38 -07:00
|
|
|
.expect("can't be empty; produced by split()")
|
2021-02-24 00:13:41 -07:00
|
|
|
.to_vec(),
|
|
|
|
|
));
|
2021-02-23 22:17:58 -07:00
|
|
|
results.push(mismatch);
|
|
|
|
|
} else if actual_lines_count != actual_lines.len() as u32 {
|
2021-02-24 00:13:41 -07:00
|
|
|
mismatch.lines.push(DiffLine::Expected(
|
|
|
|
|
expected_lines
|
|
|
|
|
.pop()
|
2021-02-24 00:23:38 -07:00
|
|
|
.expect("can't be empty; produced by split()")
|
|
|
|
|
.to_vec(),
|
|
|
|
|
));
|
|
|
|
|
mismatch.lines.push(DiffLine::Actual(
|
|
|
|
|
actual_lines
|
|
|
|
|
.pop()
|
|
|
|
|
.expect("can't be empty; produced by split()")
|
2021-02-24 00:13:41 -07:00
|
|
|
.to_vec(),
|
|
|
|
|
));
|
2021-02-23 22:17:58 -07:00
|
|
|
mismatch.lines.push(DiffLine::MissingNL);
|
|
|
|
|
results.push(mismatch);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
results
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn diff(
|
|
|
|
|
expected: &[u8],
|
|
|
|
|
expected_filename: &str,
|
|
|
|
|
actual: &[u8],
|
|
|
|
|
actual_filename: &str,
|
|
|
|
|
context_size: usize,
|
|
|
|
|
) -> Vec<u8> {
|
|
|
|
|
let mut output =
|
|
|
|
|
format!("--- {}\t\n+++ {}\t\n", expected_filename, actual_filename).into_bytes();
|
|
|
|
|
let diff_results = make_diff(expected, actual, context_size);
|
|
|
|
|
if diff_results.len() == 0 {
|
|
|
|
|
return Vec::new();
|
|
|
|
|
};
|
|
|
|
|
for result in diff_results {
|
2021-02-24 00:23:38 -07:00
|
|
|
let line_number_expected = result.line_number_expected;
|
|
|
|
|
let line_number_actual = result.line_number_actual;
|
2021-02-23 22:17:58 -07:00
|
|
|
let mut expected_count = 0;
|
2021-02-24 00:23:38 -07:00
|
|
|
let mut actual_count = 0;
|
2021-02-23 22:17:58 -07:00
|
|
|
for line in &result.lines {
|
|
|
|
|
match line {
|
|
|
|
|
DiffLine::Expected(_) => {
|
|
|
|
|
expected_count += 1;
|
|
|
|
|
}
|
|
|
|
|
DiffLine::Context(_) => {
|
|
|
|
|
expected_count += 1;
|
2021-02-24 00:23:38 -07:00
|
|
|
actual_count += 1;
|
2021-02-23 22:17:58 -07:00
|
|
|
}
|
2021-02-24 00:23:38 -07:00
|
|
|
DiffLine::Actual(_) => {
|
|
|
|
|
actual_count += 1;
|
2021-02-23 22:17:58 -07:00
|
|
|
}
|
|
|
|
|
DiffLine::MissingNL => {}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
writeln!(
|
|
|
|
|
output,
|
|
|
|
|
"@@ -{},{} +{},{} @@",
|
2021-02-24 00:23:38 -07:00
|
|
|
line_number_expected, expected_count, line_number_actual, actual_count
|
2021-02-23 22:17:58 -07:00
|
|
|
)
|
2021-02-24 00:13:41 -07:00
|
|
|
.expect("write to Vec is infallible");
|
2021-02-23 22:17:58 -07:00
|
|
|
for line in result.lines {
|
|
|
|
|
match line {
|
|
|
|
|
DiffLine::Expected(e) => {
|
2021-02-24 00:13:41 -07:00
|
|
|
write!(output, "-").expect("write to Vec is infallible");
|
|
|
|
|
output.write_all(&e).expect("write to Vec is infallible");
|
2021-02-23 22:17:58 -07:00
|
|
|
writeln!(output).unwrap();
|
|
|
|
|
}
|
|
|
|
|
DiffLine::Context(c) => {
|
2021-02-24 00:13:41 -07:00
|
|
|
write!(output, " ").expect("write to Vec is infallible");
|
|
|
|
|
output.write_all(&c).expect("write to Vec is infallible");
|
2021-02-23 22:17:58 -07:00
|
|
|
writeln!(output).unwrap();
|
|
|
|
|
}
|
2021-02-24 00:23:38 -07:00
|
|
|
DiffLine::Actual(r) => {
|
2021-02-24 00:13:41 -07:00
|
|
|
write!(output, "+",).expect("write to Vec is infallible");
|
|
|
|
|
output.write_all(&r).expect("write to Vec is infallible");
|
2021-02-23 22:17:58 -07:00
|
|
|
writeln!(output).unwrap();
|
|
|
|
|
}
|
|
|
|
|
DiffLine::MissingNL => {
|
2021-02-24 00:13:41 -07:00
|
|
|
writeln!(output, r"\ No newline at end of file")
|
|
|
|
|
.expect("write to Vec is infallible");
|
2021-02-23 22:17:58 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
output
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_permutations() {
|
|
|
|
|
// test all possible six-line files.
|
|
|
|
|
for &a in &[0, 1, 2] {
|
|
|
|
|
for &b in &[0, 1, 2] {
|
|
|
|
|
for &c in &[0, 1, 2] {
|
|
|
|
|
for &d in &[0, 1, 2] {
|
|
|
|
|
for &e in &[0, 1, 2] {
|
|
|
|
|
for &f in &[0, 1, 2] {
|
|
|
|
|
use std::fs::{self, File};
|
|
|
|
|
use std::io::Write;
|
|
|
|
|
use std::process::Command;
|
|
|
|
|
let mut alef = Vec::new();
|
|
|
|
|
let mut bet = Vec::new();
|
|
|
|
|
alef.write_all(if a == 0 { b"a\n" } else { b"b\n" })
|
|
|
|
|
.unwrap();
|
|
|
|
|
if a != 2 {
|
|
|
|
|
bet.write_all(b"b\n").unwrap();
|
|
|
|
|
}
|
|
|
|
|
alef.write_all(if b == 0 { b"c\n" } else { b"d\n" })
|
|
|
|
|
.unwrap();
|
|
|
|
|
if b != 2 {
|
|
|
|
|
bet.write_all(b"d\n").unwrap();
|
|
|
|
|
}
|
|
|
|
|
alef.write_all(if c == 0 { b"e\n" } else { b"f\n" })
|
|
|
|
|
.unwrap();
|
|
|
|
|
if c != 2 {
|
|
|
|
|
bet.write_all(b"f\n").unwrap();
|
|
|
|
|
}
|
|
|
|
|
alef.write_all(if d == 0 { b"g\n" } else { b"h\n" })
|
|
|
|
|
.unwrap();
|
|
|
|
|
if d != 2 {
|
|
|
|
|
bet.write_all(b"h\n").unwrap();
|
|
|
|
|
}
|
|
|
|
|
alef.write_all(if e == 0 { b"i\n" } else { b"j\n" })
|
|
|
|
|
.unwrap();
|
|
|
|
|
if e != 2 {
|
|
|
|
|
bet.write_all(b"j\n").unwrap();
|
|
|
|
|
}
|
|
|
|
|
alef.write_all(if f == 0 { b"k\n" } else { b"l\n" })
|
|
|
|
|
.unwrap();
|
|
|
|
|
if f != 2 {
|
|
|
|
|
bet.write_all(b"l\n").unwrap();
|
|
|
|
|
}
|
|
|
|
|
// This test diff is intentionally reversed.
|
|
|
|
|
// We want it to turn the alef into bet.
|
|
|
|
|
let diff = diff(&alef, "a/alef", &bet, "target/alef", 2);
|
|
|
|
|
File::create("target/ab.diff")
|
|
|
|
|
.unwrap()
|
|
|
|
|
.write_all(&diff)
|
|
|
|
|
.unwrap();
|
|
|
|
|
let mut fa = File::create("target/alef").unwrap();
|
|
|
|
|
fa.write_all(&alef[..]).unwrap();
|
|
|
|
|
let mut fb = File::create("target/bet").unwrap();
|
|
|
|
|
fb.write_all(&bet[..]).unwrap();
|
|
|
|
|
let _ = fa;
|
|
|
|
|
let _ = fb;
|
|
|
|
|
let output = Command::new("patch")
|
|
|
|
|
.arg("-p0")
|
|
|
|
|
.stdin(File::open("target/ab.diff").unwrap())
|
|
|
|
|
.output()
|
|
|
|
|
.unwrap();
|
|
|
|
|
if !output.status.success() {
|
|
|
|
|
panic!("{:?}", output);
|
|
|
|
|
}
|
|
|
|
|
//println!("{}", String::from_utf8_lossy(&output.stdout));
|
|
|
|
|
//println!("{}", String::from_utf8_lossy(&output.stderr));
|
|
|
|
|
let alef = fs::read("target/alef").unwrap();
|
|
|
|
|
assert_eq!(alef, bet);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_permutations_missing_line_ending() {
|
|
|
|
|
// test all possible six-line files with missing newlines.
|
|
|
|
|
for &a in &[0, 1, 2] {
|
|
|
|
|
for &b in &[0, 1, 2] {
|
|
|
|
|
for &c in &[0, 1, 2] {
|
|
|
|
|
for &d in &[0, 1, 2] {
|
|
|
|
|
for &e in &[0, 1, 2] {
|
|
|
|
|
for &f in &[0, 1, 2] {
|
|
|
|
|
for &g in &[0, 1, 2] {
|
|
|
|
|
use std::fs::{self, File};
|
|
|
|
|
use std::io::Write;
|
|
|
|
|
use std::process::Command;
|
|
|
|
|
let mut alef = Vec::new();
|
|
|
|
|
let mut bet = Vec::new();
|
|
|
|
|
alef.write_all(if a == 0 { b"a\n" } else { b"b\n" })
|
|
|
|
|
.unwrap();
|
|
|
|
|
if a != 2 {
|
|
|
|
|
bet.write_all(b"b\n").unwrap();
|
|
|
|
|
}
|
|
|
|
|
alef.write_all(if b == 0 { b"c\n" } else { b"d\n" })
|
|
|
|
|
.unwrap();
|
|
|
|
|
if b != 2 {
|
|
|
|
|
bet.write_all(b"d\n").unwrap();
|
|
|
|
|
}
|
|
|
|
|
alef.write_all(if c == 0 { b"e\n" } else { b"f\n" })
|
|
|
|
|
.unwrap();
|
|
|
|
|
if c != 2 {
|
|
|
|
|
bet.write_all(b"f\n").unwrap();
|
|
|
|
|
}
|
|
|
|
|
alef.write_all(if d == 0 { b"g\n" } else { b"h\n" })
|
|
|
|
|
.unwrap();
|
|
|
|
|
if d != 2 {
|
|
|
|
|
bet.write_all(b"h\n").unwrap();
|
|
|
|
|
}
|
|
|
|
|
alef.write_all(if e == 0 { b"i\n" } else { b"j\n" })
|
|
|
|
|
.unwrap();
|
|
|
|
|
if e != 2 {
|
|
|
|
|
bet.write_all(b"j\n").unwrap();
|
|
|
|
|
}
|
|
|
|
|
alef.write_all(if f == 0 { b"k\n" } else { b"l\n" })
|
|
|
|
|
.unwrap();
|
|
|
|
|
if f != 2 {
|
|
|
|
|
bet.write_all(b"l\n").unwrap();
|
|
|
|
|
}
|
|
|
|
|
match g {
|
|
|
|
|
0 => {
|
|
|
|
|
alef.pop();
|
|
|
|
|
}
|
|
|
|
|
1 => {
|
|
|
|
|
bet.pop();
|
|
|
|
|
}
|
|
|
|
|
2 => {
|
|
|
|
|
alef.pop();
|
|
|
|
|
bet.pop();
|
|
|
|
|
}
|
|
|
|
|
_ => unreachable!(),
|
|
|
|
|
}
|
|
|
|
|
// This test diff is intentionally reversed.
|
|
|
|
|
// We want it to turn the alef into bet.
|
|
|
|
|
let diff = diff(&alef, "a/alefn", &bet, "target/alefn", 2);
|
|
|
|
|
File::create("target/abn.diff")
|
|
|
|
|
.unwrap()
|
|
|
|
|
.write_all(&diff)
|
|
|
|
|
.unwrap();
|
|
|
|
|
let mut fa = File::create("target/alefn").unwrap();
|
|
|
|
|
fa.write_all(&alef[..]).unwrap();
|
|
|
|
|
let mut fb = File::create("target/betn").unwrap();
|
|
|
|
|
fb.write_all(&bet[..]).unwrap();
|
|
|
|
|
let _ = fa;
|
|
|
|
|
let _ = fb;
|
|
|
|
|
let output = Command::new("patch")
|
|
|
|
|
.arg("-p0")
|
|
|
|
|
.stdin(File::open("target/abn.diff").unwrap())
|
|
|
|
|
.output()
|
|
|
|
|
.unwrap();
|
|
|
|
|
if !output.status.success() {
|
|
|
|
|
panic!("{:?}", output);
|
|
|
|
|
}
|
|
|
|
|
//println!("{}", String::from_utf8_lossy(&output.stdout));
|
|
|
|
|
//println!("{}", String::from_utf8_lossy(&output.stderr));
|
|
|
|
|
let alef = fs::read("target/alefn").unwrap();
|
|
|
|
|
assert_eq!(alef, bet);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_permutations_empty_lines() {
|
|
|
|
|
// test all possible six-line files with missing newlines.
|
|
|
|
|
for &a in &[0, 1, 2] {
|
|
|
|
|
for &b in &[0, 1, 2] {
|
|
|
|
|
for &c in &[0, 1, 2] {
|
|
|
|
|
for &d in &[0, 1, 2] {
|
|
|
|
|
for &e in &[0, 1, 2] {
|
|
|
|
|
for &f in &[0, 1, 2] {
|
|
|
|
|
for &g in &[0, 1, 2, 3] {
|
|
|
|
|
use std::fs::{self, File};
|
|
|
|
|
use std::io::Write;
|
|
|
|
|
use std::process::Command;
|
|
|
|
|
let mut alef = Vec::new();
|
|
|
|
|
let mut bet = Vec::new();
|
|
|
|
|
alef.write_all(if a == 0 { b"\n" } else { b"b\n" }).unwrap();
|
|
|
|
|
if a != 2 {
|
|
|
|
|
bet.write_all(b"b\n").unwrap();
|
|
|
|
|
}
|
|
|
|
|
alef.write_all(if b == 0 { b"\n" } else { b"d\n" }).unwrap();
|
|
|
|
|
if b != 2 {
|
|
|
|
|
bet.write_all(b"d\n").unwrap();
|
|
|
|
|
}
|
|
|
|
|
alef.write_all(if c == 0 { b"\n" } else { b"f\n" }).unwrap();
|
|
|
|
|
if c != 2 {
|
|
|
|
|
bet.write_all(b"f\n").unwrap();
|
|
|
|
|
}
|
|
|
|
|
alef.write_all(if d == 0 { b"\n" } else { b"h\n" }).unwrap();
|
|
|
|
|
if d != 2 {
|
|
|
|
|
bet.write_all(b"h\n").unwrap();
|
|
|
|
|
}
|
|
|
|
|
alef.write_all(if e == 0 { b"\n" } else { b"j\n" }).unwrap();
|
|
|
|
|
if e != 2 {
|
|
|
|
|
bet.write_all(b"j\n").unwrap();
|
|
|
|
|
}
|
|
|
|
|
alef.write_all(if f == 0 { b"\n" } else { b"l\n" }).unwrap();
|
|
|
|
|
if f != 2 {
|
|
|
|
|
bet.write_all(b"l\n").unwrap();
|
|
|
|
|
}
|
|
|
|
|
match g {
|
|
|
|
|
0 => {
|
|
|
|
|
alef.pop();
|
|
|
|
|
}
|
|
|
|
|
1 => {
|
|
|
|
|
bet.pop();
|
|
|
|
|
}
|
|
|
|
|
2 => {
|
|
|
|
|
alef.pop();
|
|
|
|
|
bet.pop();
|
|
|
|
|
}
|
|
|
|
|
3 => {}
|
|
|
|
|
_ => unreachable!(),
|
|
|
|
|
}
|
|
|
|
|
// This test diff is intentionally reversed.
|
|
|
|
|
// We want it to turn the alef into bet.
|
|
|
|
|
let diff = diff(&alef, "a/alef_", &bet, "target/alef_", 2);
|
|
|
|
|
File::create("target/ab_.diff")
|
|
|
|
|
.unwrap()
|
|
|
|
|
.write_all(&diff)
|
|
|
|
|
.unwrap();
|
|
|
|
|
let mut fa = File::create("target/alef_").unwrap();
|
|
|
|
|
fa.write_all(&alef[..]).unwrap();
|
|
|
|
|
let mut fb = File::create("target/bet_").unwrap();
|
|
|
|
|
fb.write_all(&bet[..]).unwrap();
|
|
|
|
|
let _ = fa;
|
|
|
|
|
let _ = fb;
|
|
|
|
|
let output = Command::new("patch")
|
|
|
|
|
.arg("-p0")
|
|
|
|
|
.stdin(File::open("target/ab_.diff").unwrap())
|
|
|
|
|
.output()
|
|
|
|
|
.unwrap();
|
|
|
|
|
if !output.status.success() {
|
|
|
|
|
panic!("{:?}", output);
|
|
|
|
|
}
|
|
|
|
|
//println!("{}", String::from_utf8_lossy(&output.stdout));
|
|
|
|
|
//println!("{}", String::from_utf8_lossy(&output.stderr));
|
|
|
|
|
let alef = fs::read("target/alef_").unwrap();
|
|
|
|
|
assert_eq!(alef, bet);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_permutations_missing_lines() {
|
|
|
|
|
// test all possible six-line files.
|
|
|
|
|
for &a in &[0, 1, 2] {
|
|
|
|
|
for &b in &[0, 1, 2] {
|
|
|
|
|
for &c in &[0, 1, 2] {
|
|
|
|
|
for &d in &[0, 1, 2] {
|
|
|
|
|
for &e in &[0, 1, 2] {
|
|
|
|
|
for &f in &[0, 1, 2] {
|
|
|
|
|
use std::fs::{self, File};
|
|
|
|
|
use std::io::Write;
|
|
|
|
|
use std::process::Command;
|
|
|
|
|
let mut alef = Vec::new();
|
|
|
|
|
let mut bet = Vec::new();
|
|
|
|
|
alef.write_all(if a == 0 { b"a\n" } else { b"" }).unwrap();
|
|
|
|
|
if a != 2 {
|
|
|
|
|
bet.write_all(b"b\n").unwrap();
|
|
|
|
|
}
|
|
|
|
|
alef.write_all(if b == 0 { b"c\n" } else { b"" }).unwrap();
|
|
|
|
|
if b != 2 {
|
|
|
|
|
bet.write_all(b"d\n").unwrap();
|
|
|
|
|
}
|
|
|
|
|
alef.write_all(if c == 0 { b"e\n" } else { b"" }).unwrap();
|
|
|
|
|
if c != 2 {
|
|
|
|
|
bet.write_all(b"f\n").unwrap();
|
|
|
|
|
}
|
|
|
|
|
alef.write_all(if d == 0 { b"g\n" } else { b"" }).unwrap();
|
|
|
|
|
if d != 2 {
|
|
|
|
|
bet.write_all(b"h\n").unwrap();
|
|
|
|
|
}
|
|
|
|
|
alef.write_all(if e == 0 { b"i\n" } else { b"" }).unwrap();
|
|
|
|
|
if e != 2 {
|
|
|
|
|
bet.write_all(b"j\n").unwrap();
|
|
|
|
|
}
|
|
|
|
|
alef.write_all(if f == 0 { b"k\n" } else { b"" }).unwrap();
|
|
|
|
|
if f != 2 {
|
|
|
|
|
bet.write_all(b"l\n").unwrap();
|
|
|
|
|
}
|
|
|
|
|
// This test diff is intentionally reversed.
|
|
|
|
|
// We want it to turn the alef into bet.
|
|
|
|
|
let diff = diff(&alef, "a/alefx", &bet, "target/alefx", 2);
|
|
|
|
|
File::create("target/abx.diff")
|
|
|
|
|
.unwrap()
|
|
|
|
|
.write_all(&diff)
|
|
|
|
|
.unwrap();
|
|
|
|
|
let mut fa = File::create("target/alefx").unwrap();
|
|
|
|
|
fa.write_all(&alef[..]).unwrap();
|
|
|
|
|
let mut fb = File::create("target/betx").unwrap();
|
|
|
|
|
fb.write_all(&bet[..]).unwrap();
|
|
|
|
|
let _ = fa;
|
|
|
|
|
let _ = fb;
|
|
|
|
|
let output = Command::new("patch")
|
|
|
|
|
.arg("-p0")
|
|
|
|
|
.stdin(File::open("target/abx.diff").unwrap())
|
|
|
|
|
.output()
|
|
|
|
|
.unwrap();
|
|
|
|
|
if !output.status.success() {
|
|
|
|
|
panic!("{:?}", output);
|
|
|
|
|
}
|
|
|
|
|
//println!("{}", String::from_utf8_lossy(&output.stdout));
|
|
|
|
|
//println!("{}", String::from_utf8_lossy(&output.stderr));
|
|
|
|
|
let alef = fs::read("target/alefx").unwrap();
|
|
|
|
|
assert_eq!(alef, bet);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-02-23 22:22:21 -07:00
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_permutations_reverse() {
|
|
|
|
|
// test all possible six-line files.
|
|
|
|
|
for &a in &[0, 1, 2] {
|
|
|
|
|
for &b in &[0, 1, 2] {
|
|
|
|
|
for &c in &[0, 1, 2] {
|
|
|
|
|
for &d in &[0, 1, 2] {
|
|
|
|
|
for &e in &[0, 1, 2] {
|
|
|
|
|
for &f in &[0, 1, 2] {
|
|
|
|
|
use std::fs::{self, File};
|
|
|
|
|
use std::io::Write;
|
|
|
|
|
use std::process::Command;
|
|
|
|
|
let mut alef = Vec::new();
|
|
|
|
|
let mut bet = Vec::new();
|
2021-02-23 23:53:32 -07:00
|
|
|
alef.write_all(if a == 0 { b"a\n" } else { b"f\n" })
|
|
|
|
|
.unwrap();
|
2021-02-23 22:22:21 -07:00
|
|
|
if a != 2 {
|
|
|
|
|
bet.write_all(b"a\n").unwrap();
|
|
|
|
|
}
|
2021-02-23 23:53:32 -07:00
|
|
|
alef.write_all(if b == 0 { b"b\n" } else { b"e\n" })
|
|
|
|
|
.unwrap();
|
2021-02-23 22:22:21 -07:00
|
|
|
if b != 2 {
|
|
|
|
|
bet.write_all(b"b\n").unwrap();
|
|
|
|
|
}
|
2021-02-23 23:53:32 -07:00
|
|
|
alef.write_all(if c == 0 { b"c\n" } else { b"d\n" })
|
|
|
|
|
.unwrap();
|
2021-02-23 22:22:21 -07:00
|
|
|
if c != 2 {
|
|
|
|
|
bet.write_all(b"c\n").unwrap();
|
|
|
|
|
}
|
2021-02-23 23:53:32 -07:00
|
|
|
alef.write_all(if d == 0 { b"d\n" } else { b"c\n" })
|
|
|
|
|
.unwrap();
|
2021-02-23 22:22:21 -07:00
|
|
|
if d != 2 {
|
|
|
|
|
bet.write_all(b"d\n").unwrap();
|
|
|
|
|
}
|
2021-02-23 23:53:32 -07:00
|
|
|
alef.write_all(if e == 0 { b"e\n" } else { b"b\n" })
|
|
|
|
|
.unwrap();
|
2021-02-23 22:22:21 -07:00
|
|
|
if e != 2 {
|
|
|
|
|
bet.write_all(b"e\n").unwrap();
|
|
|
|
|
}
|
2021-02-23 23:53:32 -07:00
|
|
|
alef.write_all(if f == 0 { b"f\n" } else { b"a\n" })
|
|
|
|
|
.unwrap();
|
2021-02-23 22:22:21 -07:00
|
|
|
if f != 2 {
|
|
|
|
|
bet.write_all(b"f\n").unwrap();
|
|
|
|
|
}
|
|
|
|
|
// This test diff is intentionally reversed.
|
|
|
|
|
// We want it to turn the alef into bet.
|
|
|
|
|
let diff = diff(&alef, "a/alefr", &bet, "target/alefr", 2);
|
|
|
|
|
File::create("target/abr.diff")
|
|
|
|
|
.unwrap()
|
|
|
|
|
.write_all(&diff)
|
|
|
|
|
.unwrap();
|
|
|
|
|
let mut fa = File::create("target/alefr").unwrap();
|
|
|
|
|
fa.write_all(&alef[..]).unwrap();
|
|
|
|
|
let mut fb = File::create("target/betr").unwrap();
|
|
|
|
|
fb.write_all(&bet[..]).unwrap();
|
|
|
|
|
let _ = fa;
|
|
|
|
|
let _ = fb;
|
|
|
|
|
let output = Command::new("patch")
|
|
|
|
|
.arg("-p0")
|
|
|
|
|
.stdin(File::open("target/abr.diff").unwrap())
|
|
|
|
|
.output()
|
|
|
|
|
.unwrap();
|
|
|
|
|
if !output.status.success() {
|
|
|
|
|
panic!("{:?}", output);
|
|
|
|
|
}
|
|
|
|
|
//println!("{}", String::from_utf8_lossy(&output.stdout));
|
|
|
|
|
//println!("{}", String::from_utf8_lossy(&output.stderr));
|
|
|
|
|
let alef = fs::read("target/alefr").unwrap();
|
|
|
|
|
assert_eq!(alef, bet);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|