userland: search and exec tools, spawn library (T14)
rg (ripgrep 15.2), grep (ours, on ripgrep's regex crates), find and xargs (ours), sed (uutils 0.3 wrapper), diff and cmp (uutils diffutils 0.6 with -r), fd 10.5, jaq 2.3, env and timeout. userland/spawn is a small Command-like client of the process interface. search-test.nu: 42 cases match native tools.
This commit is contained in:
@@ -0,0 +1,945 @@
|
||||
// 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", "uu-diff", "uucmp"] {
|
||||
let mut cmd = cargo_bin_cmd!("diffutils");
|
||||
cmd.arg(subcmd);
|
||||
cmd.arg("--foobar");
|
||||
cmd.assert()
|
||||
.code(predicate::eq(2))
|
||||
.failure()
|
||||
.stderr(predicate::str::contains("unrecognized 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(())
|
||||
}
|
||||
|
||||
// Regression for #269: the `-y` gutter marker must sit at the middle of the gutter.
|
||||
#[test]
|
||||
fn sdiff_gutter_marker_column() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut file1 = NamedTempFile::new()?;
|
||||
file1.write_all("aa\n".as_bytes())?;
|
||||
let mut file2 = NamedTempFile::new()?;
|
||||
file2.write_all("bb\n".as_bytes())?;
|
||||
|
||||
// --expand-tabs: padding is spaces.
|
||||
let mut cmd = cargo_bin_cmd!("diffutils");
|
||||
cmd.arg("diff")
|
||||
.arg("-y")
|
||||
.arg("--width=40")
|
||||
.arg("--expand-tabs")
|
||||
.arg(file1.path())
|
||||
.arg(file2.path());
|
||||
let output = cmd.output().unwrap().stdout;
|
||||
assert_eq!(
|
||||
output,
|
||||
b"aa <\n > bb\n"
|
||||
);
|
||||
|
||||
// Default (expand_tabs: false): padding goes through the tab path.
|
||||
let mut cmd = cargo_bin_cmd!("diffutils");
|
||||
cmd.arg("diff")
|
||||
.arg("-y")
|
||||
.arg("--width=40")
|
||||
.arg(file1.path())
|
||||
.arg(file2.path());
|
||||
let output = cmd.output().unwrap().stdout;
|
||||
assert_eq!(output, b"aa\t\t <\n\t\t >\tbb\n");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
mod cmp {
|
||||
use super::*;
|
||||
|
||||
// A file whose metadata length is 0 but which still yields bytes (/dev/zero)
|
||||
// collapses the offset column to two characters, so the padding subtraction
|
||||
// underflowed once the running offset reached three digits.
|
||||
#[test]
|
||||
#[cfg(unix)]
|
||||
fn cmp_verbose_zero_length_metadata() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut file = NamedTempFile::new()?;
|
||||
file.write_all(&[0xffu8; 150])?;
|
||||
|
||||
let mut cmd = cargo_bin_cmd!("diffutils");
|
||||
cmd.arg("cmp")
|
||||
.arg("--verbose")
|
||||
.arg("/dev/zero")
|
||||
.arg(file.path());
|
||||
cmd.assert()
|
||||
.code(predicate::eq(1))
|
||||
.stdout(predicate::str::contains("150 0 377"));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[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(())
|
||||
}
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Print the test results written to a JSON file by run-upstream-testsuite.sh
|
||||
# in a markdown format. The printout includes the name of the test, the result,
|
||||
# the URL to the test script and the contents of stdout and stderr.
|
||||
# It can be used verbatim as the description when filing an issue for a test
|
||||
# with an unexpected result.
|
||||
|
||||
json="test-results.json"
|
||||
[[ -n $1 ]] && json="$1"
|
||||
|
||||
codeblock () { echo -e "\`\`\`\n$1\n\`\`\`"; }
|
||||
|
||||
jq -c '.tests[]' "$json" | while read -r test
|
||||
do
|
||||
name=$(echo "$test" | jq -r '.test')
|
||||
echo "# test: $name"
|
||||
result=$(echo "$test" | jq -r '.result')
|
||||
echo "result: $result"
|
||||
url=$(echo "$test" | jq -r '.url')
|
||||
echo "url: $url"
|
||||
if [[ "$result" != "SKIP" ]]
|
||||
then
|
||||
stdout=$(echo "$test" | jq -r '.stdout' | base64 -d)
|
||||
if [[ -n "$stdout" ]]
|
||||
then
|
||||
echo "## stdout"
|
||||
codeblock "$stdout"
|
||||
fi
|
||||
stderr=$(echo "$test" | jq -r '.stderr' | base64 -d)
|
||||
if [[ -n "$stderr" ]]
|
||||
then
|
||||
echo "## stderr"
|
||||
codeblock "$stderr"
|
||||
fi
|
||||
fi
|
||||
echo ""
|
||||
done
|
||||
+168
@@ -0,0 +1,168 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Run the GNU upstream test suite for diffutils against a local build of the
|
||||
# Rust implementation, print out a summary of the test results, and writes a
|
||||
# JSON file ('test-results.json') containing detailed information about the
|
||||
# test run.
|
||||
|
||||
# The JSON file contains metadata about the test run, and for each test the
|
||||
# result as well as the contents of stdout, stderr, and of all the files
|
||||
# written by the test script, if any (excluding subdirectories).
|
||||
|
||||
# The script takes a shortcut to fetch only the test suite from the upstream
|
||||
# repository and carefully avoids running the autotools machinery which is
|
||||
# time-consuming and resource-intensive, and doesn't offer the option to not
|
||||
# build the upstream binaries. As a consequence, the environment in which the
|
||||
# tests are run might not match exactly that used when the upstream tests are
|
||||
# run through the autotools.
|
||||
|
||||
# Exit codes: 0 if all tests passed, 1 if at least one test failed, and 2 if
|
||||
# the test suite could not be run at all (e.g. the upstream repository couldn't
|
||||
# be fetched). Callers must treat 2 as an infrastructure error: no meaningful
|
||||
# result was produced.
|
||||
|
||||
# By default it expects a release build of the diffutils binary, but a
|
||||
# different build profile can be specified as an argument
|
||||
# (e.g. 'dev' or 'test').
|
||||
# Unless overridden by the $TESTS environment variable, all tests in the test
|
||||
# suite will be run. Tests targeting a command that is not yet implemented
|
||||
# (e.g. diff3 or sdiff) are skipped.
|
||||
|
||||
scriptpath=$(dirname "$(readlink -f "$0")")
|
||||
rev=$(git rev-parse HEAD)
|
||||
gnu_version="3.12"
|
||||
gnu_tarball_blake2b=5b4593b39da71578d7f975603abe9359be215b9ac76548a6ab0d6e3838bb103c7ffcddf7fa01abcd5c6289db9a2f16b43aa3d5e846a9aa4b8db866763c2660de
|
||||
|
||||
# Report an infrastructure error: the test suite could not be run at all
|
||||
die() {
|
||||
echo "ERROR: $*" >&2
|
||||
exit 2
|
||||
}
|
||||
|
||||
# Allow passing a specific profile as parameter (default to "release")
|
||||
profile="release"
|
||||
[[ -n $1 ]] && profile="$1"
|
||||
|
||||
# Verify that the diffutils binary was built for the requested profile
|
||||
binary="$scriptpath/../target/$profile/diffutils"
|
||||
if [[ ! -x "$binary" ]]
|
||||
then
|
||||
die "Missing build for profile $profile"
|
||||
fi
|
||||
|
||||
# Work in a temporary directory
|
||||
tempdir=$(mktemp -d)
|
||||
trap 'rm -rf "$tempdir"' EXIT
|
||||
cd "$tempdir" || die "Cannot enter temporary directory $tempdir"
|
||||
|
||||
# Fetch the upstream test suite
|
||||
echo "Fetching upstream test suite"
|
||||
curl -fsSL --retry 5 https://ftpmirror.gnu.org/diffutils/diffutils-${gnu_version}.tar.xz -o gnu-diffutils.tar.xz || die "Failed to fetch test suite"
|
||||
echo ${gnu_tarball_blake2b} gnu-diffutils.tar.xz \
|
||||
| b2sum --check || die "Failed to verify checksum of tarball of GNU diffutils"
|
||||
mkdir -p diffutils
|
||||
tar xJf gnu-diffutils.tar.xz --strip-components=1 -C diffutils || die "Failed to extract test suite"
|
||||
|
||||
cd diffutils || die "Test suite src is broken"
|
||||
|
||||
# Ensure that calling `diff` invokes the built `diffutils` binary instead of
|
||||
# the upstream `diff` binary that is most likely installed on the system
|
||||
cd src || die "Missing the directory holding the diff and cmp symlinks"
|
||||
ln -s "$binary" diff
|
||||
ln -s "$binary" cmp
|
||||
cd ../tests || die "Cannot enter the upstream tests directory"
|
||||
|
||||
if [[ -n "$TESTS" ]]
|
||||
then
|
||||
tests="$TESTS"
|
||||
else
|
||||
# Get a list of all upstream tests (default if $TESTS isn't set)
|
||||
echo -e '\n\nprinttests:\n\t@echo "${TESTS}"' >> Makefile.am
|
||||
tests=$(make -f Makefile.am printtests)
|
||||
fi
|
||||
total=$(echo "$tests" | wc -w)
|
||||
(( total > 0 )) || die "No test to run: the upstream test list is empty"
|
||||
echo "Running $total tests"
|
||||
export LC_ALL=C
|
||||
export KEEP=yes
|
||||
timestamp=$(date -Iseconds)
|
||||
passed=0
|
||||
failed=0
|
||||
skipped=0
|
||||
normal="$(tput sgr0)"
|
||||
for test in $tests
|
||||
do
|
||||
result="FAIL"
|
||||
# Run only the tests that invoke `diff` or `cmp`,
|
||||
# because other binaries aren't implemented yet
|
||||
if ! grep -E -s -q "(diff3|sdiff)" "$test"
|
||||
then
|
||||
sh "$test" 1> stdout.txt 2> stderr.txt && result="PASS"
|
||||
if [[ $? = 77 ]]
|
||||
then
|
||||
result="SKIP"
|
||||
else
|
||||
json+="{\"test\":\"$test\",\"result\":\"$result\","
|
||||
json+="\"stdout\":\"$(base64 -w0 < stdout.txt)\","
|
||||
json+="\"stderr\":\"$(base64 -w0 < stderr.txt)\","
|
||||
json+="\"files\":{"
|
||||
cd gt-$test.*
|
||||
# Note: this doesn't include the contents of subdirectories,
|
||||
# but there isn't much value added in doing so
|
||||
for file in *
|
||||
do
|
||||
# Encode the name with jq: some tests create files whose name contains
|
||||
# quotes or control characters, which would produce invalid JSON
|
||||
[[ -f "$file" ]] && json+="$(jq -Rn --arg name "$file" '$name'):\"$(base64 -w0 < "$file")\","
|
||||
done
|
||||
json="${json%,}}},"
|
||||
cd - > /dev/null
|
||||
[[ "$result" = "PASS" ]] && (( passed++ ))
|
||||
[[ "$result" = "FAIL" ]] && (( failed++ ))
|
||||
fi
|
||||
else
|
||||
result="SKIP"
|
||||
fi
|
||||
color=2 # green
|
||||
[[ "$result" = "FAIL" ]] && color=1 # red
|
||||
if [[ $result = "SKIP" ]]
|
||||
then
|
||||
(( skipped++ ))
|
||||
json+="{\"test\":\"$test\",\"result\":\"$result\"},"
|
||||
color=3 # yellow
|
||||
fi
|
||||
printf " %-40s $(tput setaf $color)$result$(tput sgr0)\n" "$test"
|
||||
done
|
||||
echo ""
|
||||
echo -n "Summary: TOTAL: $total / "
|
||||
echo -n "$(tput setaf 2)PASS$normal: $passed / "
|
||||
echo -n "$(tput setaf 1)FAIL$normal: $failed / "
|
||||
echo "$(tput setaf 3)SKIP$normal: $skipped"
|
||||
echo ""
|
||||
|
||||
json="\"tests\":[${json%,}]"
|
||||
metadata="\"timestamp\":\"$timestamp\","
|
||||
metadata+="\"revision\":\"$rev\","
|
||||
metadata+="\"upstream-version\":\"$gnu_version\","
|
||||
if [[ -n "$GITHUB_ACTIONS" ]]
|
||||
then
|
||||
metadata+="\"branch\":\"$GITHUB_REF\","
|
||||
fi
|
||||
json="{$metadata $json}"
|
||||
|
||||
# Clean up
|
||||
cd "$scriptpath"
|
||||
|
||||
# Write the results out only once they are known to be valid JSON, so that a
|
||||
# malformed (or truncated) file is never left behind for the caller to consume
|
||||
resultsfile="test-results.json"
|
||||
if ! echo "$json" | jq > "$resultsfile.tmp"
|
||||
then
|
||||
rm -f "$resultsfile.tmp"
|
||||
die "Generated invalid JSON results"
|
||||
fi
|
||||
mv "$resultsfile.tmp" "$resultsfile"
|
||||
echo "Results written to $scriptpath/$resultsfile"
|
||||
|
||||
(( failed > 0 )) && exit 1
|
||||
exit 0
|
||||
Reference in New Issue
Block a user