mirror of
https://github.com/uutils/diffutils.git
synced 2026-06-10 15:48:59 -07:00
7ddc6c6c4b
On my Mac I see this test fail quite consistently. This change makes it more resilient in systems with slower startup times, while still allowing faster systems to finish as soon as possible.
891 lines
28 KiB
Rust
891 lines
28 KiB
Rust
// 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<dyn std::error::Error>> {
|
|
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("Unknown option: \"--foobar\""));
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn cannot_read_files() -> Result<(), Box<dyn std::error::Error>> {
|
|
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<dyn std::error::Error>> {
|
|
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<dyn std::error::Error>> {
|
|
// 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<dyn std::error::Error>> {
|
|
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<dyn std::error::Error>> {
|
|
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<dyn std::error::Error>> {
|
|
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<dyn std::error::Error>> {
|
|
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<dyn std::error::Error>> {
|
|
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<dyn std::error::Error>> {
|
|
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<dyn std::error::Error>> {
|
|
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<dyn std::error::Error>> {
|
|
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<dyn std::error::Error>> {
|
|
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<dyn std::error::Error>> {
|
|
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<dyn std::error::Error>> {
|
|
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<dyn std::error::Error>> {
|
|
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<dyn std::error::Error>> {
|
|
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<dyn std::error::Error>> {
|
|
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<dyn std::error::Error>> {
|
|
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<dyn std::error::Error>> {
|
|
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<dyn std::error::Error>> {
|
|
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(())
|
|
}
|
|
}
|