// This file is part of the uutils diffutils package. // // For the full copyright and license information, please view the LICENSE-* // files that was distributed with this source code. use assert_cmd::cargo::cargo_bin_cmd; use predicates::prelude::*; use std::fs::File; #[cfg(not(windows))] use std::fs::OpenOptions; use std::io::Write; use tempfile::{tempdir, NamedTempFile}; // Integration tests for the diffutils command mod common { use super::*; #[test] fn unknown_param() -> Result<(), Box> { let mut cmd = cargo_bin_cmd!("diffutils"); cmd.arg("patch"); cmd.assert() .code(predicate::eq(2)) .failure() .stderr(predicate::eq("patch: utility not supported\n")); let mut cmd = cargo_bin_cmd!("diffutils"); cmd.assert() .code(predicate::eq(0)) .success() .stderr(predicate::str::starts_with( "Expected utility name as second argument, got nothing.\n", )); for subcmd in ["diff", "cmp"] { let mut cmd = cargo_bin_cmd!("diffutils"); cmd.arg(subcmd); cmd.arg("--foobar"); cmd.assert() .code(predicate::eq(2)) .failure() .stderr(predicate::str::starts_with( "unrecognized option: \"--foobar\"", )); } Ok(()) } #[test] fn cannot_read_files() -> Result<(), Box> { let file = NamedTempFile::new()?; let nofile = NamedTempFile::new()?; let nopath = nofile.into_temp_path(); std::fs::remove_file(&nopath)?; #[cfg(not(windows))] let error_message = "No such file or directory"; #[cfg(windows)] let error_message = "The system cannot find the file specified."; for subcmd in ["diff", "cmp"] { let mut cmd = cargo_bin_cmd!("diffutils"); cmd.arg(subcmd); cmd.arg(&nopath).arg(file.path()); cmd.assert() .code(predicate::eq(2)) .failure() .stderr(predicate::str::ends_with(format!( ": {}: {error_message}\n", &nopath.as_os_str().to_string_lossy() ))); let mut cmd = cargo_bin_cmd!("diffutils"); cmd.arg(subcmd); cmd.arg(file.path()).arg(&nopath); cmd.assert() .code(predicate::eq(2)) .failure() .stderr(predicate::str::ends_with(format!( ": {}: {error_message}\n", &nopath.as_os_str().to_string_lossy() ))); } let mut cmd = cargo_bin_cmd!("diffutils"); cmd.arg("diff"); cmd.arg(&nopath).arg(&nopath); cmd.assert().code(predicate::eq(2)).failure().stderr( predicate::str::contains(format!( ": {}: {error_message}\n", &nopath.as_os_str().to_string_lossy() )) .count(2), ); Ok(()) } } mod diff { use diffutilslib::assert_diff_eq; use super::*; #[test] fn no_differences() -> Result<(), Box> { let file = NamedTempFile::new()?; for option in ["", "-u", "-c", "-e"] { let mut cmd = cargo_bin_cmd!("diffutils"); cmd.arg("diff"); if !option.is_empty() { cmd.arg(option); } cmd.arg(file.path()).arg(file.path()); cmd.assert() .code(predicate::eq(0)) .success() .stdout(predicate::str::is_empty()); } Ok(()) } #[test] fn no_differences_report_identical_files() -> Result<(), Box> { // same file let mut file1 = NamedTempFile::new()?; file1.write_all("foo\n".as_bytes())?; for option in ["", "-u", "-c", "-e"] { let mut cmd = cargo_bin_cmd!("diffutils"); cmd.arg("diff"); if !option.is_empty() { cmd.arg(option); } cmd.arg("-s").arg(file1.path()).arg(file1.path()); cmd.assert() .code(predicate::eq(0)) .success() .stdout(predicate::eq(format!( "Files {} and {} are identical\n", file1.path().to_string_lossy(), file1.path().to_string_lossy(), ))); } // two files with the same content let mut file2 = NamedTempFile::new()?; file2.write_all("foo\n".as_bytes())?; for option in ["", "-u", "-c", "-e"] { let mut cmd = cargo_bin_cmd!("diffutils"); cmd.arg("diff"); if !option.is_empty() { cmd.arg(option); } cmd.arg("-s").arg(file1.path()).arg(file2.path()); cmd.assert() .code(predicate::eq(0)) .success() .stdout(predicate::eq(format!( "Files {} and {} are identical\n", file1.path().to_string_lossy(), file2.path().to_string_lossy(), ))); } Ok(()) } #[test] fn differences() -> Result<(), Box> { let mut file1 = NamedTempFile::new()?; file1.write_all("foo\n".as_bytes())?; let mut file2 = NamedTempFile::new()?; file2.write_all("bar\n".as_bytes())?; for option in ["", "-u", "-c", "-e"] { let mut cmd = cargo_bin_cmd!("diffutils"); cmd.arg("diff"); if !option.is_empty() { cmd.arg(option); } cmd.arg(file1.path()).arg(file2.path()); cmd.assert() .code(predicate::eq(1)) .failure() .stdout(predicate::str::is_empty().not()); } Ok(()) } #[test] fn differences_brief() -> Result<(), Box> { let mut file1 = NamedTempFile::new()?; file1.write_all("foo\n".as_bytes())?; let mut file2 = NamedTempFile::new()?; file2.write_all("bar\n".as_bytes())?; for option in ["", "-u", "-c", "-e"] { let mut cmd = cargo_bin_cmd!("diffutils"); cmd.arg("diff"); if !option.is_empty() { cmd.arg(option); } cmd.arg("-q").arg(file1.path()).arg(file2.path()); cmd.assert() .code(predicate::eq(1)) .failure() .stdout(predicate::eq(format!( "Files {} and {} differ\n", file1.path().to_string_lossy(), file2.path().to_string_lossy() ))); } Ok(()) } #[test] fn missing_newline() -> Result<(), Box> { let mut file1 = NamedTempFile::new()?; file1.write_all("foo".as_bytes())?; let mut file2 = NamedTempFile::new()?; file2.write_all("bar".as_bytes())?; let mut cmd = cargo_bin_cmd!("diffutils"); cmd.arg("diff"); cmd.arg("-e").arg(file1.path()).arg(file2.path()); cmd.assert() .code(predicate::eq(2)) .failure() .stderr(predicate::str::starts_with("No newline at end of file")); Ok(()) } #[test] fn read_from_stdin() -> Result<(), Box> { let mut file1 = NamedTempFile::new()?; file1.write_all("foo\n".as_bytes())?; let mut file2 = NamedTempFile::new()?; file2.write_all("bar\n".as_bytes())?; let mut cmd = cargo_bin_cmd!("diffutils"); cmd.arg("diff"); cmd.arg("-u") .arg(file1.path()) .arg("-") .write_stdin("bar\n"); cmd.assert().code(predicate::eq(1)).failure(); let output = cmd.output().unwrap().stdout; assert_diff_eq!( output, format!( "--- {}\tTIMESTAMP\n+++ -\tTIMESTAMP\n@@ -1 +1 @@\n-foo\n+bar\n", file1.path().to_string_lossy() ) ); let mut cmd = cargo_bin_cmd!("diffutils"); cmd.arg("diff"); cmd.arg("-u") .arg("-") .arg(file2.path()) .write_stdin("foo\n"); cmd.assert().code(predicate::eq(1)).failure(); let output = cmd.output().unwrap().stdout; assert_diff_eq!( output, format!( "--- -\tTIMESTAMP\n+++ {}\tTIMESTAMP\n@@ -1 +1 @@\n-foo\n+bar\n", file2.path().to_string_lossy() ) ); let mut cmd = cargo_bin_cmd!("diffutils"); cmd.arg("diff"); cmd.arg("-u").arg("-").arg("-"); cmd.assert() .code(predicate::eq(0)) .success() .stdout(predicate::str::is_empty()); #[cfg(unix)] { let mut cmd = cargo_bin_cmd!("diffutils"); cmd.arg("diff"); cmd.arg("-u") .arg(file1.path()) .arg("/dev/stdin") .write_stdin("bar\n"); cmd.assert().code(predicate::eq(1)).failure(); let output = cmd.output().unwrap().stdout; assert_diff_eq!( output, format!( "--- {}\tTIMESTAMP\n+++ /dev/stdin\tTIMESTAMP\n@@ -1 +1 @@\n-foo\n+bar\n", file1.path().to_string_lossy() ) ); } Ok(()) } #[test] fn compare_file_to_directory() -> Result<(), Box> { let tmp_dir = tempdir()?; let directory = tmp_dir.path().join("d"); let _ = std::fs::create_dir(&directory); let a_path = tmp_dir.path().join("a"); let mut a = File::create(&a_path).unwrap(); a.write_all(b"a\n").unwrap(); let da_path = directory.join("a"); let mut da = File::create(&da_path).unwrap(); da.write_all(b"da\n").unwrap(); let mut cmd = cargo_bin_cmd!("diffutils"); cmd.arg("diff"); cmd.arg("-u").arg(&directory).arg(&a_path); cmd.assert().code(predicate::eq(1)).failure(); let output = cmd.output().unwrap().stdout; assert_diff_eq!( output, format!( "--- {}\tTIMESTAMP\n+++ {}\tTIMESTAMP\n@@ -1 +1 @@\n-da\n+a\n", da_path.display(), a_path.display() ) ); let mut cmd = cargo_bin_cmd!("diffutils"); cmd.arg("diff"); cmd.arg("-u").arg(&a_path).arg(&directory); cmd.assert().code(predicate::eq(1)).failure(); let output = cmd.output().unwrap().stdout; assert_diff_eq!( output, format!( "--- {}\tTIMESTAMP\n+++ {}\tTIMESTAMP\n@@ -1 +1 @@\n-a\n+da\n", a_path.display(), da_path.display() ) ); Ok(()) } } mod cmp { use super::*; #[test] fn cmp_incompatible_params() -> Result<(), Box> { let mut cmd = cargo_bin_cmd!("diffutils"); cmd.arg("cmp"); cmd.arg("-l"); cmd.arg("-s"); cmd.arg("/etc/passwd").arg("/etc/group"); cmd.assert() .code(predicate::eq(2)) .failure() .stderr(predicate::str::ends_with( ": options -l and -s are incompatible\n", )); Ok(()) } #[test] fn cmp_stdin() -> Result<(), Box> { let tmp_dir = tempdir()?; let a_path = tmp_dir.path().join("a"); let mut a = File::create(&a_path).unwrap(); a.write_all(b"a\n").unwrap(); let mut cmd = cargo_bin_cmd!("diffutils"); cmd.arg("cmp"); cmd.arg(&a_path); cmd.write_stdin("a\n"); cmd.assert() .code(predicate::eq(0)) .success() .stderr(predicate::str::is_empty()) .stdout(predicate::str::is_empty()); let mut cmd = cargo_bin_cmd!("diffutils"); cmd.env("LC_ALL", "C"); cmd.arg("cmp"); cmd.arg(&a_path); cmd.write_stdin("b\n"); cmd.assert() .code(predicate::eq(1)) .failure() .stderr(predicate::str::is_empty()) .stdout(predicate::str::ends_with(" - differ: char 1, line 1\n")); Ok(()) } #[test] fn cmp_equal_files() -> Result<(), Box> { let tmp_dir = tempdir()?; let a_path = tmp_dir.path().join("a"); let mut a = File::create(&a_path).unwrap(); a.write_all(b"a\n").unwrap(); let b_path = tmp_dir.path().join("b"); let mut b = File::create(&b_path).unwrap(); b.write_all(b"a\n").unwrap(); let mut cmd = cargo_bin_cmd!("diffutils"); cmd.arg("cmp"); cmd.arg(&a_path).arg(&b_path); cmd.assert() .code(predicate::eq(0)) .success() .stderr(predicate::str::is_empty()) .stdout(predicate::str::is_empty()); Ok(()) } #[test] fn cmp_one_file_empty() -> Result<(), Box> { let tmp_dir = tempdir()?; let a_path = tmp_dir.path().join("a"); let mut a = File::create(&a_path).unwrap(); a.write_all(b"a\n").unwrap(); let b_path = tmp_dir.path().join("b"); let _ = File::create(&b_path).unwrap(); let mut cmd = cargo_bin_cmd!("diffutils"); cmd.arg("cmp"); cmd.arg(&a_path).arg(&b_path); cmd.assert() .code(predicate::eq(1)) .failure() .stderr(predicate::str::contains(" EOF on ")) .stderr(predicate::str::ends_with(" which is empty\n")); Ok(()) } #[test] fn cmp_immediate_difference() -> Result<(), Box> { let tmp_dir = tempdir()?; let a_path = tmp_dir.path().join("a"); let mut a = File::create(&a_path).unwrap(); a.write_all(b"abc\n").unwrap(); let b_path = tmp_dir.path().join("b"); let mut b = File::create(&b_path).unwrap(); b.write_all(b"bcd\n").unwrap(); let mut cmd = cargo_bin_cmd!("diffutils"); cmd.env("LC_ALL", "C"); cmd.arg("cmp"); cmd.arg(&a_path).arg(&b_path); cmd.assert() .code(predicate::eq(1)) .failure() .stdout(predicate::str::ends_with(" differ: char 1, line 1\n")); let mut cmd = cargo_bin_cmd!("diffutils"); cmd.env("LC_ALL", "C"); cmd.arg("cmp"); cmd.arg("-b"); cmd.arg(&a_path).arg(&b_path); cmd.assert() .code(predicate::eq(1)) .failure() .stderr(predicate::str::is_empty()) .stdout(predicate::str::ends_with( " differ: byte 1, line 1 is 141 a 142 b\n", )); let mut cmd = cargo_bin_cmd!("diffutils"); cmd.env("LC_ALL", "C"); cmd.arg("cmp"); cmd.arg("-l"); cmd.arg(&a_path).arg(&b_path); cmd.assert() .code(predicate::eq(1)) .failure() .stderr(predicate::str::is_empty()) .stdout(predicate::eq("1 141 142\n2 142 143\n3 143 144\n")); let mut cmd = cargo_bin_cmd!("diffutils"); cmd.env("LC_ALL", "C"); cmd.arg("cmp"); cmd.arg("-l"); cmd.arg("-b"); cmd.arg(&a_path).arg(&b_path); cmd.assert() .code(predicate::eq(1)) .failure() .stderr(predicate::str::is_empty()) .stdout(predicate::eq( "1 141 a 142 b\n2 142 b 143 c\n3 143 c 144 d\n", )); Ok(()) } #[test] fn cmp_newline_difference() -> Result<(), Box> { let tmp_dir = tempdir()?; let a_path = tmp_dir.path().join("a"); let mut a = File::create(&a_path).unwrap(); a.write_all(b"abc\ndefg").unwrap(); let b_path = tmp_dir.path().join("b"); let mut b = File::create(&b_path).unwrap(); b.write_all(b"abc\ndef\ng").unwrap(); let mut cmd = cargo_bin_cmd!("diffutils"); cmd.env("LC_ALL", "C"); cmd.arg("cmp"); cmd.arg(&a_path).arg(&b_path); cmd.assert() .code(predicate::eq(1)) .failure() .stderr(predicate::str::is_empty()) .stdout(predicate::str::ends_with(" differ: char 8, line 2\n")); let mut cmd = cargo_bin_cmd!("diffutils"); cmd.env("LC_ALL", "C"); cmd.arg("cmp"); cmd.arg("-b"); cmd.arg(&a_path).arg(&b_path); cmd.assert() .code(predicate::eq(1)) .failure() .stderr(predicate::str::is_empty()) .stdout(predicate::str::ends_with( " differ: byte 8, line 2 is 147 g 12 ^J\n", )); let mut cmd = cargo_bin_cmd!("diffutils"); cmd.env("LC_ALL", "C"); cmd.arg("cmp"); cmd.arg("-l"); cmd.arg(&a_path).arg(&b_path); cmd.assert() .code(predicate::eq(1)) .failure() .stdout(predicate::str::starts_with("8 147 12\n")) .stderr(predicate::str::contains(" EOF on")) .stderr(predicate::str::ends_with(" after byte 8\n")); let mut cmd = cargo_bin_cmd!("diffutils"); cmd.env("LC_ALL", "C"); cmd.arg("cmp"); cmd.arg("-b"); cmd.arg("-l"); cmd.arg(&a_path).arg(&b_path); cmd.assert() .code(predicate::eq(1)) .failure() .stdout(predicate::str::starts_with("8 147 g 12 ^J\n")) .stderr(predicate::str::contains(" EOF on")) .stderr(predicate::str::ends_with(" after byte 8\n")); Ok(()) } #[test] fn cmp_max_bytes() -> Result<(), Box> { let tmp_dir = tempdir()?; let a_path = tmp_dir.path().join("a"); let mut a = File::create(&a_path).unwrap(); a.write_all(b"abc efg ijkl\n").unwrap(); let b_path = tmp_dir.path().join("b"); let mut b = File::create(&b_path).unwrap(); b.write_all(b"abcdefghijkl\n").unwrap(); let mut cmd = cargo_bin_cmd!("diffutils"); cmd.arg("cmp"); cmd.arg("-l"); cmd.arg("-b"); cmd.arg("-n"); cmd.arg("3"); cmd.arg(&a_path).arg(&b_path); cmd.assert() .code(predicate::eq(0)) .success() .stderr(predicate::str::is_empty()) .stdout(predicate::str::is_empty()); let mut cmd = cargo_bin_cmd!("diffutils"); cmd.arg("cmp"); cmd.arg("-l"); cmd.arg("-b"); cmd.arg("-n"); cmd.arg("4"); cmd.arg(&a_path).arg(&b_path); cmd.assert() .code(predicate::eq(1)) .failure() .stderr(predicate::str::is_empty()) .stdout(predicate::eq("4 40 144 d\n")); let mut cmd = cargo_bin_cmd!("diffutils"); cmd.arg("cmp"); cmd.arg("-l"); cmd.arg("-b"); cmd.arg("-n"); cmd.arg("13"); cmd.arg(&a_path).arg(&b_path); cmd.assert() .code(predicate::eq(1)) .failure() .stderr(predicate::str::is_empty()) .stdout(predicate::eq(" 4 40 144 d\n 8 40 150 h\n")); Ok(()) } #[test] fn cmp_skip_args_parsing() -> Result<(), Box> { let tmp_dir = tempdir()?; let a_path = tmp_dir.path().join("a"); let mut a = File::create(&a_path).unwrap(); a.write_all(b"---abc\n").unwrap(); let b_path = tmp_dir.path().join("b"); let mut b = File::create(&b_path).unwrap(); b.write_all(b"###abc\n").unwrap(); let mut cmd = cargo_bin_cmd!("diffutils"); cmd.env("LC_ALL", "C"); cmd.arg("cmp"); cmd.arg("-i"); cmd.arg("3"); cmd.arg(&a_path).arg(&b_path); cmd.assert() .code(predicate::eq(0)) .success() .stderr(predicate::str::is_empty()) .stdout(predicate::str::is_empty()); // Positional skips should be ignored let mut cmd = cargo_bin_cmd!("diffutils"); cmd.env("LC_ALL", "C"); cmd.arg("cmp"); cmd.arg("-i"); cmd.arg("3"); cmd.arg(&a_path).arg(&b_path); cmd.arg("1").arg("1"); cmd.assert() .code(predicate::eq(0)) .success() .stderr(predicate::str::is_empty()) .stdout(predicate::str::is_empty()); // Single positional argument should only affect first file. let mut cmd = cargo_bin_cmd!("diffutils"); cmd.env("LC_ALL", "C"); cmd.arg("cmp"); cmd.arg(&a_path).arg(&b_path); cmd.arg("3"); cmd.assert() .code(predicate::eq(1)) .failure() .stderr(predicate::str::is_empty()) .stdout(predicate::str::ends_with(" differ: char 1, line 1\n")); let mut cmd = cargo_bin_cmd!("diffutils"); cmd.env("LC_ALL", "C"); cmd.arg("cmp"); cmd.arg(&a_path).arg(&b_path); cmd.arg("3"); cmd.arg("3"); cmd.assert() .code(predicate::eq(0)) .success() .stderr(predicate::str::is_empty()) .stdout(predicate::str::is_empty()); Ok(()) } #[test] fn cmp_skip_suffix_parsing() -> Result<(), Box> { let tmp_dir = tempdir()?; let a_path = tmp_dir.path().join("a"); let mut a = File::create(&a_path).unwrap(); writeln!(a, "{}c", "a".repeat(1024)).unwrap(); a.flush().unwrap(); let b_path = tmp_dir.path().join("b"); let mut b = File::create(&b_path).unwrap(); writeln!(b, "{}c", "b".repeat(1024)).unwrap(); b.flush().unwrap(); let mut cmd = cargo_bin_cmd!("diffutils"); cmd.arg("cmp"); cmd.arg("--ignore-initial=1K"); cmd.arg(&a_path).arg(&b_path); cmd.assert() .code(predicate::eq(0)) .success() .stderr(predicate::str::is_empty()) .stdout(predicate::str::is_empty()); Ok(()) } #[test] fn cmp_skip() -> Result<(), Box> { let tmp_dir = tempdir()?; let a_path = tmp_dir.path().join("a"); let mut a = File::create(&a_path).unwrap(); a.write_all(b"abc efg ijkl\n").unwrap(); let b_path = tmp_dir.path().join("b"); let mut b = File::create(&b_path).unwrap(); b.write_all(b"abcdefghijkl\n").unwrap(); let mut cmd = cargo_bin_cmd!("diffutils"); cmd.arg("cmp"); cmd.arg("-l"); cmd.arg("-b"); cmd.arg("-i"); cmd.arg("8"); cmd.arg(&a_path).arg(&b_path); cmd.assert() .code(predicate::eq(0)) .success() .stderr(predicate::str::is_empty()) .stdout(predicate::str::is_empty()); let mut cmd = cargo_bin_cmd!("diffutils"); cmd.arg("cmp"); cmd.arg("-b"); cmd.arg("-i"); cmd.arg("4"); cmd.arg(&a_path).arg(&b_path); cmd.assert() .code(predicate::eq(1)) .failure() .stderr(predicate::str::is_empty()) .stdout(predicate::str::ends_with( " differ: byte 4, line 1 is 40 150 h\n", )); Ok(()) } #[test] fn cmp_binary() -> Result<(), Box> { let tmp_dir = tempdir()?; let mut bytes = vec![0, 15, 31, 32, 33, 40, 64, 126, 127, 128, 129, 200, 254, 255]; let a_path = tmp_dir.path().join("a"); let mut a = File::create(&a_path).unwrap(); a.write_all(&bytes).unwrap(); bytes.reverse(); let b_path = tmp_dir.path().join("b"); let mut b = File::create(&b_path).unwrap(); b.write_all(&bytes).unwrap(); let mut cmd = cargo_bin_cmd!("diffutils"); cmd.arg("cmp"); cmd.arg("-l"); cmd.arg("-b"); cmd.arg(&a_path).arg(&b_path); cmd.assert() .code(predicate::eq(1)) .failure() .stdout(predicate::eq(concat!( " 1 0 ^@ 377 M-^?\n", " 2 17 ^O 376 M-~\n", " 3 37 ^_ 310 M-H\n", " 4 40 201 M-^A\n", " 5 41 ! 200 M-^@\n", " 6 50 ( 177 ^?\n", " 7 100 @ 176 ~\n", " 8 176 ~ 100 @\n", " 9 177 ^? 50 (\n", "10 200 M-^@ 41 !\n", "11 201 M-^A 40 \n", "12 310 M-H 37 ^_\n", "13 376 M-~ 17 ^O\n", "14 377 M-^? 0 ^@\n" ))); Ok(()) } #[test] #[cfg(not(windows))] fn cmp_fast_paths() -> Result<(), Box> { let tmp_dir = tempdir()?; // This test mimics one found in the GNU cmp test suite. It is used for // validating the /dev/null optimization. let a_path = tmp_dir.path().join("a"); let a = File::create(&a_path).unwrap(); a.set_len(14 * 1024 * 1024 * 1024 * 1024).unwrap(); let b_path = tmp_dir.path().join("b"); let b = File::create(&b_path).unwrap(); b.set_len(15 * 1024 * 1024 * 1024 * 1024).unwrap(); let dev_null = OpenOptions::new().write(true).open("/dev/null").unwrap(); let mut child = std::process::Command::new(assert_cmd::cargo::cargo_bin!("diffutils")) .arg("cmp") .arg(&a_path) .arg(&b_path) .stdout(dev_null) .spawn() .unwrap(); // Bound the runtime to a very short time that still allows for some resource // constraint to slow it down while also allowing very fast systems to exit as // early as possible. const MAX_TRIES: u8 = 50; for tries in 0..=MAX_TRIES { if tries == MAX_TRIES { panic!("cmp took too long to run, /dev/null optimization probably not working") } match child.try_wait() { Ok(Some(status)) => { assert_eq!(status.code(), Some(1)); break; } Ok(None) => (), Err(e) => panic!("{e:#?}"), } std::thread::sleep(std::time::Duration::from_millis(10)); } // Two stdins should be equal let mut cmd = cargo_bin_cmd!("diffutils"); cmd.arg("cmp"); cmd.arg("-"); cmd.arg("-"); cmd.assert() .code(predicate::eq(0)) .success() .stdout(predicate::str::is_empty()) .stderr(predicate::str::is_empty()); // Files with longer than block size equal segments should still report // the correct line number for the difference. Assumes 8KB block size (see // https://github.com/rust-lang/rust/blob/master/library/std/src/sys_common/io.rs), // create a 24KB equality. let mut bytes = " ".repeat(4095); bytes.push('\n'); bytes.push_str(&" ".repeat(4096)); let bytes = bytes.repeat(3); let bytes = bytes.as_bytes(); let a_path = tmp_dir.path().join("a"); let mut a = File::create(&a_path).unwrap(); a.write_all(bytes).unwrap(); a.write_all(b"A").unwrap(); let b_path = tmp_dir.path().join("b"); let mut b = File::create(&b_path).unwrap(); b.write_all(bytes).unwrap(); b.write_all(b"B").unwrap(); let mut cmd = cargo_bin_cmd!("diffutils"); cmd.arg("cmp"); cmd.arg(&a_path).arg(&b_path); cmd.env("LC_ALL", "en_US"); cmd.assert() .code(predicate::eq(1)) .failure() .stdout(predicate::str::ends_with(" differ: byte 24577, line 4\n")); Ok(()) } }