mirror of
https://github.com/uutils/diffutils.git
synced 2026-06-10 15:48:59 -07:00
@@ -41,6 +41,8 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
test-target:
|
||||
- { name: fuzz_cmp, should_pass: true }
|
||||
- { name: fuzz_cmp_args, should_pass: true }
|
||||
- { name: fuzz_ed, should_pass: true }
|
||||
- { name: fuzz_normal, should_pass: true }
|
||||
- { name: fuzz_patch, should_pass: true }
|
||||
|
||||
Generated
+7
@@ -128,6 +128,7 @@ dependencies = [
|
||||
"assert_cmd",
|
||||
"chrono",
|
||||
"diff",
|
||||
"itoa",
|
||||
"predicates",
|
||||
"pretty_assertions",
|
||||
"regex",
|
||||
@@ -190,6 +191,12 @@ dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.69"
|
||||
|
||||
@@ -17,6 +17,7 @@ path = "src/main.rs"
|
||||
[dependencies]
|
||||
chrono = "0.4.38"
|
||||
diff = "0.1.13"
|
||||
itoa = "1.0.11"
|
||||
regex = "1.10.4"
|
||||
same-file = "1.0.6"
|
||||
unicode-width = "0.2.0"
|
||||
|
||||
@@ -16,6 +16,18 @@ diffutils = { path = "../" }
|
||||
[workspace]
|
||||
members = ["."]
|
||||
|
||||
[[bin]]
|
||||
name = "fuzz_cmp"
|
||||
path = "fuzz_targets/fuzz_cmp.rs"
|
||||
test = false
|
||||
doc = false
|
||||
|
||||
[[bin]]
|
||||
name = "fuzz_cmp_args"
|
||||
path = "fuzz_targets/fuzz_cmp_args.rs"
|
||||
test = false
|
||||
doc = false
|
||||
|
||||
[[bin]]
|
||||
name = "fuzz_patch"
|
||||
path = "fuzz_targets/fuzz_patch.rs"
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
"-l"
|
||||
"--verbose"
|
||||
"-b"
|
||||
"--print-bytes"
|
||||
"-lb"
|
||||
"-bl"
|
||||
"-n"
|
||||
"--bytes"
|
||||
"--bytes="
|
||||
"--bytes=1024"
|
||||
"--bytes=99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999"
|
||||
"-i"
|
||||
"--ignore-initial"
|
||||
"--ignore-initial="
|
||||
"--ignore-initial=1024"
|
||||
"--ignore-initial=99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999:9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999"
|
||||
"-s"
|
||||
"-q"
|
||||
"--quiet"
|
||||
"--silent"
|
||||
"-"
|
||||
"--"
|
||||
"1kB"
|
||||
"1G"
|
||||
"1GB"
|
||||
"1T"
|
||||
"1TB"
|
||||
"1P"
|
||||
"1PB"
|
||||
"1Z"
|
||||
"1ZB"
|
||||
"1Y"
|
||||
"1YB"
|
||||
"1Y"
|
||||
"0"
|
||||
"1:2"
|
||||
@@ -0,0 +1,51 @@
|
||||
#![no_main]
|
||||
#[macro_use]
|
||||
extern crate libfuzzer_sys;
|
||||
use diffutilslib::cmp::{self, Cmp};
|
||||
|
||||
use std::ffi::OsString;
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
|
||||
fn os(s: &str) -> OsString {
|
||||
OsString::from(s)
|
||||
}
|
||||
|
||||
fuzz_target!(|x: (Vec<u8>, Vec<u8>)| {
|
||||
let args = vec!["cmp", "-l", "-b", "target/fuzz.cmp.a", "target/fuzz.cmp.b"]
|
||||
.into_iter()
|
||||
.map(|s| os(s))
|
||||
.peekable();
|
||||
|
||||
let (from, to) = x;
|
||||
|
||||
File::create("target/fuzz.cmp.a")
|
||||
.unwrap()
|
||||
.write_all(&from)
|
||||
.unwrap();
|
||||
|
||||
File::create("target/fuzz.cmp.b")
|
||||
.unwrap()
|
||||
.write_all(&to)
|
||||
.unwrap();
|
||||
|
||||
let params =
|
||||
cmp::parse_params(args).unwrap_or_else(|e| panic!("Failed to parse params: {}", e));
|
||||
let ret = cmp::cmp(¶ms);
|
||||
if from == to && !matches!(ret, Ok(Cmp::Equal)) {
|
||||
panic!(
|
||||
"target/fuzz.cmp.a and target/fuzz.cmp.b are equal, but cmp returned {:?}.",
|
||||
ret
|
||||
);
|
||||
} else if from != to && !matches!(ret, Ok(Cmp::Different)) {
|
||||
panic!(
|
||||
"target/fuzz.cmp.a and target/fuzz.cmp.b are different, but cmp returned {:?}.",
|
||||
ret
|
||||
);
|
||||
} else if ret.is_err() {
|
||||
panic!(
|
||||
"target/fuzz.cmp.a and target/fuzz.cmp.b caused cmp to error ({:?}).",
|
||||
ret
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,23 @@
|
||||
#![no_main]
|
||||
#[macro_use]
|
||||
extern crate libfuzzer_sys;
|
||||
use diffutilslib::cmp;
|
||||
|
||||
use libfuzzer_sys::Corpus;
|
||||
use std::ffi::OsString;
|
||||
|
||||
fn os(s: &str) -> OsString {
|
||||
OsString::from(s)
|
||||
}
|
||||
|
||||
fuzz_target!(|x: Vec<OsString>| -> Corpus {
|
||||
if x.len() > 6 {
|
||||
// Make sure we try to parse an option when we get longer args. x[0] will be
|
||||
// the executable name.
|
||||
if ![os("-l"), os("-b"), os("-s"), os("-n"), os("-i")].contains(&x[1]) {
|
||||
return Corpus::Reject;
|
||||
}
|
||||
}
|
||||
let _ = cmp::parse_params(x.into_iter().peekable());
|
||||
Corpus::Keep
|
||||
});
|
||||
+1185
File diff suppressed because it is too large
Load Diff
+98
@@ -0,0 +1,98 @@
|
||||
// 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 crate::params::{parse_params, Format};
|
||||
use crate::utils::report_failure_to_read_input_file;
|
||||
use crate::{context_diff, ed_diff, normal_diff, unified_diff};
|
||||
use std::env::ArgsOs;
|
||||
use std::ffi::OsString;
|
||||
use std::fs;
|
||||
use std::io::{self, Read, Write};
|
||||
use std::iter::Peekable;
|
||||
use std::process::{exit, ExitCode};
|
||||
|
||||
// Exit codes are documented at
|
||||
// https://www.gnu.org/software/diffutils/manual/html_node/Invoking-diff.html.
|
||||
// An exit status of 0 means no differences were found,
|
||||
// 1 means some differences were found,
|
||||
// and 2 means trouble.
|
||||
pub fn main(opts: Peekable<ArgsOs>) -> ExitCode {
|
||||
let params = parse_params(opts).unwrap_or_else(|error| {
|
||||
eprintln!("{error}");
|
||||
exit(2);
|
||||
});
|
||||
// if from and to are the same file, no need to perform any comparison
|
||||
let maybe_report_identical_files = || {
|
||||
if params.report_identical_files {
|
||||
println!(
|
||||
"Files {} and {} are identical",
|
||||
params.from.to_string_lossy(),
|
||||
params.to.to_string_lossy(),
|
||||
);
|
||||
}
|
||||
};
|
||||
if params.from == "-" && params.to == "-"
|
||||
|| same_file::is_same_file(¶ms.from, ¶ms.to).unwrap_or(false)
|
||||
{
|
||||
maybe_report_identical_files();
|
||||
return ExitCode::SUCCESS;
|
||||
}
|
||||
|
||||
// read files
|
||||
fn read_file_contents(filepath: &OsString) -> io::Result<Vec<u8>> {
|
||||
if filepath == "-" {
|
||||
let mut content = Vec::new();
|
||||
io::stdin().read_to_end(&mut content).and(Ok(content))
|
||||
} else {
|
||||
fs::read(filepath)
|
||||
}
|
||||
}
|
||||
let mut io_error = false;
|
||||
let from_content = match read_file_contents(¶ms.from) {
|
||||
Ok(from_content) => from_content,
|
||||
Err(e) => {
|
||||
report_failure_to_read_input_file(¶ms.executable, ¶ms.from, &e);
|
||||
io_error = true;
|
||||
vec![]
|
||||
}
|
||||
};
|
||||
let to_content = match read_file_contents(¶ms.to) {
|
||||
Ok(to_content) => to_content,
|
||||
Err(e) => {
|
||||
report_failure_to_read_input_file(¶ms.executable, ¶ms.to, &e);
|
||||
io_error = true;
|
||||
vec![]
|
||||
}
|
||||
};
|
||||
if io_error {
|
||||
return ExitCode::from(2);
|
||||
}
|
||||
|
||||
// run diff
|
||||
let result: Vec<u8> = match params.format {
|
||||
Format::Normal => normal_diff::diff(&from_content, &to_content, ¶ms),
|
||||
Format::Unified => unified_diff::diff(&from_content, &to_content, ¶ms),
|
||||
Format::Context => context_diff::diff(&from_content, &to_content, ¶ms),
|
||||
Format::Ed => ed_diff::diff(&from_content, &to_content, ¶ms).unwrap_or_else(|error| {
|
||||
eprintln!("{error}");
|
||||
exit(2);
|
||||
}),
|
||||
};
|
||||
if params.brief && !result.is_empty() {
|
||||
println!(
|
||||
"Files {} and {} differ",
|
||||
params.from.to_string_lossy(),
|
||||
params.to.to_string_lossy()
|
||||
);
|
||||
} else {
|
||||
io::stdout().write_all(&result).unwrap();
|
||||
}
|
||||
if result.is_empty() {
|
||||
maybe_report_identical_files();
|
||||
ExitCode::SUCCESS
|
||||
} else {
|
||||
ExitCode::from(1)
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
pub mod cmp;
|
||||
pub mod context_diff;
|
||||
pub mod ed_diff;
|
||||
pub mod macros;
|
||||
|
||||
+58
-99
@@ -3,15 +3,17 @@
|
||||
// For the full copyright and license information, please view the LICENSE-*
|
||||
// files that was distributed with this source code.
|
||||
|
||||
use crate::params::{parse_params, Format};
|
||||
use regex::Regex;
|
||||
use std::env;
|
||||
use std::ffi::OsString;
|
||||
use std::fs;
|
||||
use std::io::{self, Read, Write};
|
||||
use std::process::{exit, ExitCode};
|
||||
use std::{
|
||||
env::ArgsOs,
|
||||
ffi::{OsStr, OsString},
|
||||
iter::Peekable,
|
||||
path::{Path, PathBuf},
|
||||
process::ExitCode,
|
||||
};
|
||||
|
||||
mod cmp;
|
||||
mod context_diff;
|
||||
mod diff;
|
||||
mod ed_diff;
|
||||
mod macros;
|
||||
mod normal_diff;
|
||||
@@ -19,103 +21,60 @@ mod params;
|
||||
mod unified_diff;
|
||||
mod utils;
|
||||
|
||||
fn report_failure_to_read_input_file(
|
||||
executable: &OsString,
|
||||
filepath: &OsString,
|
||||
error: &std::io::Error,
|
||||
) {
|
||||
// std::io::Error's display trait outputs "{detail} (os error {code})"
|
||||
// but we want only the {detail} (error string) part
|
||||
let error_code_re = Regex::new(r"\ \(os\ error\ \d+\)$").unwrap();
|
||||
eprintln!(
|
||||
"{}: {}: {}",
|
||||
executable.to_string_lossy(),
|
||||
filepath.to_string_lossy(),
|
||||
error_code_re.replace(error.to_string().as_str(), ""),
|
||||
);
|
||||
/// # Panics
|
||||
/// Panics if the binary path cannot be determined
|
||||
fn binary_path(args: &mut Peekable<ArgsOs>) -> PathBuf {
|
||||
match args.peek() {
|
||||
Some(ref s) if !s.is_empty() => PathBuf::from(s),
|
||||
_ => std::env::current_exe().unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
/// #Panics
|
||||
/// Panics if path has no UTF-8 valid name
|
||||
fn name(binary_path: &Path) -> &OsStr {
|
||||
binary_path.file_stem().unwrap()
|
||||
}
|
||||
|
||||
const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
|
||||
fn usage(name: &str) {
|
||||
println!("{name} {VERSION} (multi-call binary)\n");
|
||||
println!("Usage: {name} [function [arguments...]]\n");
|
||||
println!("Currently defined functions:\n");
|
||||
println!(" cmp, diff\n");
|
||||
}
|
||||
|
||||
fn second_arg_error(name: &OsStr) -> ! {
|
||||
eprintln!("Expected utility name as second argument, got nothing.");
|
||||
usage(&name.to_string_lossy());
|
||||
std::process::exit(0);
|
||||
}
|
||||
|
||||
// Exit codes are documented at
|
||||
// https://www.gnu.org/software/diffutils/manual/html_node/Invoking-diff.html.
|
||||
// An exit status of 0 means no differences were found,
|
||||
// 1 means some differences were found,
|
||||
// and 2 means trouble.
|
||||
fn main() -> ExitCode {
|
||||
let opts = env::args_os();
|
||||
let params = parse_params(opts).unwrap_or_else(|error| {
|
||||
eprintln!("{error}");
|
||||
exit(2);
|
||||
});
|
||||
// if from and to are the same file, no need to perform any comparison
|
||||
let maybe_report_identical_files = || {
|
||||
if params.report_identical_files {
|
||||
println!(
|
||||
"Files {} and {} are identical",
|
||||
params.from.to_string_lossy(),
|
||||
params.to.to_string_lossy(),
|
||||
);
|
||||
}
|
||||
};
|
||||
if params.from == "-" && params.to == "-"
|
||||
|| same_file::is_same_file(¶ms.from, ¶ms.to).unwrap_or(false)
|
||||
{
|
||||
maybe_report_identical_files();
|
||||
return ExitCode::SUCCESS;
|
||||
}
|
||||
let mut args = std::env::args_os().peekable();
|
||||
|
||||
// read files
|
||||
fn read_file_contents(filepath: &OsString) -> io::Result<Vec<u8>> {
|
||||
if filepath == "-" {
|
||||
let mut content = Vec::new();
|
||||
io::stdin().read_to_end(&mut content).and(Ok(content))
|
||||
} else {
|
||||
fs::read(filepath)
|
||||
}
|
||||
}
|
||||
let mut io_error = false;
|
||||
let from_content = match read_file_contents(¶ms.from) {
|
||||
Ok(from_content) => from_content,
|
||||
Err(e) => {
|
||||
report_failure_to_read_input_file(¶ms.executable, ¶ms.from, &e);
|
||||
io_error = true;
|
||||
vec![]
|
||||
}
|
||||
};
|
||||
let to_content = match read_file_contents(¶ms.to) {
|
||||
Ok(to_content) => to_content,
|
||||
Err(e) => {
|
||||
report_failure_to_read_input_file(¶ms.executable, ¶ms.to, &e);
|
||||
io_error = true;
|
||||
vec![]
|
||||
}
|
||||
};
|
||||
if io_error {
|
||||
return ExitCode::from(2);
|
||||
}
|
||||
let exe_path = binary_path(&mut args);
|
||||
let exe_name = name(&exe_path);
|
||||
|
||||
// run diff
|
||||
let result: Vec<u8> = match params.format {
|
||||
Format::Normal => normal_diff::diff(&from_content, &to_content, ¶ms),
|
||||
Format::Unified => unified_diff::diff(&from_content, &to_content, ¶ms),
|
||||
Format::Context => context_diff::diff(&from_content, &to_content, ¶ms),
|
||||
Format::Ed => ed_diff::diff(&from_content, &to_content, ¶ms).unwrap_or_else(|error| {
|
||||
eprintln!("{error}");
|
||||
exit(2);
|
||||
}),
|
||||
let util_name = if exe_name == "diffutils" {
|
||||
// Discard the item we peeked.
|
||||
let _ = args.next();
|
||||
|
||||
args.peek()
|
||||
.cloned()
|
||||
.unwrap_or_else(|| second_arg_error(exe_name))
|
||||
} else {
|
||||
OsString::from(exe_name)
|
||||
};
|
||||
if params.brief && !result.is_empty() {
|
||||
println!(
|
||||
"Files {} and {} differ",
|
||||
params.from.to_string_lossy(),
|
||||
params.to.to_string_lossy()
|
||||
);
|
||||
} else {
|
||||
io::stdout().write_all(&result).unwrap();
|
||||
}
|
||||
if result.is_empty() {
|
||||
maybe_report_identical_files();
|
||||
ExitCode::SUCCESS
|
||||
} else {
|
||||
ExitCode::from(1)
|
||||
|
||||
match util_name.to_str() {
|
||||
Some("diff") => diff::main(args),
|
||||
Some("cmp") => cmp::main(args),
|
||||
Some(name) => {
|
||||
eprintln!("{}: utility not supported", name);
|
||||
ExitCode::from(2)
|
||||
}
|
||||
None => second_arg_error(exe_name),
|
||||
}
|
||||
}
|
||||
|
||||
+108
-28
@@ -1,4 +1,5 @@
|
||||
use std::ffi::OsString;
|
||||
use std::iter::Peekable;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use regex::Regex;
|
||||
@@ -41,8 +42,7 @@ impl Default for Params {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_params<I: IntoIterator<Item = OsString>>(opts: I) -> Result<Params, String> {
|
||||
let mut opts = opts.into_iter().peekable();
|
||||
pub fn parse_params<I: Iterator<Item = OsString>>(mut opts: Peekable<I>) -> Result<Params, String> {
|
||||
// parse CLI
|
||||
|
||||
let Some(executable) = opts.next() else {
|
||||
@@ -323,7 +323,12 @@ mod tests {
|
||||
to: os("bar"),
|
||||
..Default::default()
|
||||
}),
|
||||
parse_params([os("diff"), os("foo"), os("bar")].iter().cloned())
|
||||
parse_params(
|
||||
[os("diff"), os("foo"), os("bar")]
|
||||
.iter()
|
||||
.cloned()
|
||||
.peekable()
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
Ok(Params {
|
||||
@@ -336,6 +341,7 @@ mod tests {
|
||||
[os("diff"), os("--normal"), os("foo"), os("bar")]
|
||||
.iter()
|
||||
.cloned()
|
||||
.peekable()
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -350,7 +356,12 @@ mod tests {
|
||||
format: Format::Ed,
|
||||
..Default::default()
|
||||
}),
|
||||
parse_params([os("diff"), os(arg), os("foo"), os("bar")].iter().cloned())
|
||||
parse_params(
|
||||
[os("diff"), os(arg), os("foo"), os("bar")]
|
||||
.iter()
|
||||
.cloned()
|
||||
.peekable()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -368,7 +379,7 @@ mod tests {
|
||||
format: Format::Context,
|
||||
..Default::default()
|
||||
}),
|
||||
parse_params(params.iter().map(|x| os(x)))
|
||||
parse_params(params.iter().map(|x| os(x)).peekable())
|
||||
);
|
||||
}
|
||||
for args in [
|
||||
@@ -390,7 +401,7 @@ mod tests {
|
||||
context_count: 42,
|
||||
..Default::default()
|
||||
}),
|
||||
parse_params(params.iter().map(|x| os(x)))
|
||||
parse_params(params.iter().map(|x| os(x)).peekable())
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -410,7 +421,7 @@ mod tests {
|
||||
let mut params = vec!["diff"];
|
||||
params.extend(args);
|
||||
params.extend(["foo", "bar"]);
|
||||
assert!(parse_params(params.iter().map(|x| os(x))).is_err());
|
||||
assert!(parse_params(params.iter().map(|x| os(x)).peekable()).is_err());
|
||||
}
|
||||
}
|
||||
#[test]
|
||||
@@ -427,7 +438,7 @@ mod tests {
|
||||
format: Format::Unified,
|
||||
..Default::default()
|
||||
}),
|
||||
parse_params(params.iter().map(|x| os(x)))
|
||||
parse_params(params.iter().map(|x| os(x)).peekable())
|
||||
);
|
||||
}
|
||||
for args in [
|
||||
@@ -449,7 +460,7 @@ mod tests {
|
||||
context_count: 42,
|
||||
..Default::default()
|
||||
}),
|
||||
parse_params(params.iter().map(|x| os(x)))
|
||||
parse_params(params.iter().map(|x| os(x)).peekable())
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -469,7 +480,7 @@ mod tests {
|
||||
let mut params = vec!["diff"];
|
||||
params.extend(args);
|
||||
params.extend(["foo", "bar"]);
|
||||
assert!(parse_params(params.iter().map(|x| os(x))).is_err());
|
||||
assert!(parse_params(params.iter().map(|x| os(x)).peekable()).is_err());
|
||||
}
|
||||
}
|
||||
#[test]
|
||||
@@ -487,6 +498,7 @@ mod tests {
|
||||
[os("diff"), os("-u54"), os("foo"), os("bar")]
|
||||
.iter()
|
||||
.cloned()
|
||||
.peekable()
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
@@ -502,6 +514,7 @@ mod tests {
|
||||
[os("diff"), os("-U54"), os("foo"), os("bar")]
|
||||
.iter()
|
||||
.cloned()
|
||||
.peekable()
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
@@ -517,6 +530,7 @@ mod tests {
|
||||
[os("diff"), os("-U"), os("54"), os("foo"), os("bar")]
|
||||
.iter()
|
||||
.cloned()
|
||||
.peekable()
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
@@ -532,6 +546,7 @@ mod tests {
|
||||
[os("diff"), os("-c54"), os("foo"), os("bar")]
|
||||
.iter()
|
||||
.cloned()
|
||||
.peekable()
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -544,7 +559,12 @@ mod tests {
|
||||
to: os("bar"),
|
||||
..Default::default()
|
||||
}),
|
||||
parse_params([os("diff"), os("foo"), os("bar")].iter().cloned())
|
||||
parse_params(
|
||||
[os("diff"), os("foo"), os("bar")]
|
||||
.iter()
|
||||
.cloned()
|
||||
.peekable()
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
Ok(Params {
|
||||
@@ -554,7 +574,12 @@ mod tests {
|
||||
report_identical_files: true,
|
||||
..Default::default()
|
||||
}),
|
||||
parse_params([os("diff"), os("-s"), os("foo"), os("bar")].iter().cloned())
|
||||
parse_params(
|
||||
[os("diff"), os("-s"), os("foo"), os("bar")]
|
||||
.iter()
|
||||
.cloned()
|
||||
.peekable()
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
Ok(Params {
|
||||
@@ -573,6 +598,7 @@ mod tests {
|
||||
]
|
||||
.iter()
|
||||
.cloned()
|
||||
.peekable()
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -585,7 +611,12 @@ mod tests {
|
||||
to: os("bar"),
|
||||
..Default::default()
|
||||
}),
|
||||
parse_params([os("diff"), os("foo"), os("bar")].iter().cloned())
|
||||
parse_params(
|
||||
[os("diff"), os("foo"), os("bar")]
|
||||
.iter()
|
||||
.cloned()
|
||||
.peekable()
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
Ok(Params {
|
||||
@@ -595,7 +626,12 @@ mod tests {
|
||||
brief: true,
|
||||
..Default::default()
|
||||
}),
|
||||
parse_params([os("diff"), os("-q"), os("foo"), os("bar")].iter().cloned())
|
||||
parse_params(
|
||||
[os("diff"), os("-q"), os("foo"), os("bar")]
|
||||
.iter()
|
||||
.cloned()
|
||||
.peekable()
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
Ok(Params {
|
||||
@@ -609,6 +645,7 @@ mod tests {
|
||||
[os("diff"), os("--brief"), os("foo"), os("bar"),]
|
||||
.iter()
|
||||
.cloned()
|
||||
.peekable()
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -621,7 +658,12 @@ mod tests {
|
||||
to: os("bar"),
|
||||
..Default::default()
|
||||
}),
|
||||
parse_params([os("diff"), os("foo"), os("bar")].iter().cloned())
|
||||
parse_params(
|
||||
[os("diff"), os("foo"), os("bar")]
|
||||
.iter()
|
||||
.cloned()
|
||||
.peekable()
|
||||
)
|
||||
);
|
||||
for option in ["-t", "--expand-tabs"] {
|
||||
assert_eq!(
|
||||
@@ -636,6 +678,7 @@ mod tests {
|
||||
[os("diff"), os(option), os("foo"), os("bar")]
|
||||
.iter()
|
||||
.cloned()
|
||||
.peekable()
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -649,7 +692,12 @@ mod tests {
|
||||
to: os("bar"),
|
||||
..Default::default()
|
||||
}),
|
||||
parse_params([os("diff"), os("foo"), os("bar")].iter().cloned())
|
||||
parse_params(
|
||||
[os("diff"), os("foo"), os("bar")]
|
||||
.iter()
|
||||
.cloned()
|
||||
.peekable()
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
Ok(Params {
|
||||
@@ -663,6 +711,7 @@ mod tests {
|
||||
[os("diff"), os("--tabsize=0"), os("foo"), os("bar")]
|
||||
.iter()
|
||||
.cloned()
|
||||
.peekable()
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
@@ -677,36 +726,42 @@ mod tests {
|
||||
[os("diff"), os("--tabsize=42"), os("foo"), os("bar")]
|
||||
.iter()
|
||||
.cloned()
|
||||
.peekable()
|
||||
)
|
||||
);
|
||||
assert!(parse_params(
|
||||
[os("diff"), os("--tabsize"), os("foo"), os("bar")]
|
||||
.iter()
|
||||
.cloned()
|
||||
.peekable()
|
||||
)
|
||||
.is_err());
|
||||
assert!(parse_params(
|
||||
[os("diff"), os("--tabsize="), os("foo"), os("bar")]
|
||||
.iter()
|
||||
.cloned()
|
||||
.peekable()
|
||||
)
|
||||
.is_err());
|
||||
assert!(parse_params(
|
||||
[os("diff"), os("--tabsize=r2"), os("foo"), os("bar")]
|
||||
.iter()
|
||||
.cloned()
|
||||
.peekable()
|
||||
)
|
||||
.is_err());
|
||||
assert!(parse_params(
|
||||
[os("diff"), os("--tabsize=-1"), os("foo"), os("bar")]
|
||||
.iter()
|
||||
.cloned()
|
||||
.peekable()
|
||||
)
|
||||
.is_err());
|
||||
assert!(parse_params(
|
||||
[os("diff"), os("--tabsize=r2"), os("foo"), os("bar")]
|
||||
.iter()
|
||||
.cloned()
|
||||
.peekable()
|
||||
)
|
||||
.is_err());
|
||||
assert!(parse_params(
|
||||
@@ -718,6 +773,7 @@ mod tests {
|
||||
]
|
||||
.iter()
|
||||
.cloned()
|
||||
.peekable()
|
||||
)
|
||||
.is_err());
|
||||
}
|
||||
@@ -730,7 +786,12 @@ mod tests {
|
||||
to: os("-h"),
|
||||
..Default::default()
|
||||
}),
|
||||
parse_params([os("diff"), os("--"), os("-g"), os("-h")].iter().cloned())
|
||||
parse_params(
|
||||
[os("diff"), os("--"), os("-g"), os("-h")]
|
||||
.iter()
|
||||
.cloned()
|
||||
.peekable()
|
||||
)
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
@@ -742,7 +803,7 @@ mod tests {
|
||||
to: os("-"),
|
||||
..Default::default()
|
||||
}),
|
||||
parse_params([os("diff"), os("foo"), os("-")].iter().cloned())
|
||||
parse_params([os("diff"), os("foo"), os("-")].iter().cloned().peekable())
|
||||
);
|
||||
assert_eq!(
|
||||
Ok(Params {
|
||||
@@ -751,7 +812,7 @@ mod tests {
|
||||
to: os("bar"),
|
||||
..Default::default()
|
||||
}),
|
||||
parse_params([os("diff"), os("-"), os("bar")].iter().cloned())
|
||||
parse_params([os("diff"), os("-"), os("bar")].iter().cloned().peekable())
|
||||
);
|
||||
assert_eq!(
|
||||
Ok(Params {
|
||||
@@ -760,27 +821,45 @@ mod tests {
|
||||
to: os("-"),
|
||||
..Default::default()
|
||||
}),
|
||||
parse_params([os("diff"), os("-"), os("-")].iter().cloned())
|
||||
parse_params([os("diff"), os("-"), os("-")].iter().cloned().peekable())
|
||||
);
|
||||
assert!(parse_params([os("diff"), os("foo"), os("bar"), os("-")].iter().cloned()).is_err());
|
||||
assert!(parse_params([os("diff"), os("-"), os("-"), os("-")].iter().cloned()).is_err());
|
||||
assert!(parse_params(
|
||||
[os("diff"), os("foo"), os("bar"), os("-")]
|
||||
.iter()
|
||||
.cloned()
|
||||
.peekable()
|
||||
)
|
||||
.is_err());
|
||||
assert!(parse_params(
|
||||
[os("diff"), os("-"), os("-"), os("-")]
|
||||
.iter()
|
||||
.cloned()
|
||||
.peekable()
|
||||
)
|
||||
.is_err());
|
||||
}
|
||||
#[test]
|
||||
fn missing_arguments() {
|
||||
assert!(parse_params([os("diff")].iter().cloned()).is_err());
|
||||
assert!(parse_params([os("diff"), os("foo")].iter().cloned()).is_err());
|
||||
assert!(parse_params([os("diff")].iter().cloned().peekable()).is_err());
|
||||
assert!(parse_params([os("diff"), os("foo")].iter().cloned().peekable()).is_err());
|
||||
}
|
||||
#[test]
|
||||
fn unknown_argument() {
|
||||
assert!(parse_params(
|
||||
[os("diff"), os("-g"), os("foo"), os("bar")]
|
||||
.iter()
|
||||
.cloned()
|
||||
.peekable()
|
||||
)
|
||||
.is_err());
|
||||
assert!(
|
||||
parse_params([os("diff"), os("-g"), os("foo"), os("bar")].iter().cloned()).is_err()
|
||||
parse_params([os("diff"), os("-g"), os("bar")].iter().cloned().peekable()).is_err()
|
||||
);
|
||||
assert!(parse_params([os("diff"), os("-g"), os("bar")].iter().cloned()).is_err());
|
||||
assert!(parse_params([os("diff"), os("-g")].iter().cloned()).is_err());
|
||||
assert!(parse_params([os("diff"), os("-g")].iter().cloned().peekable()).is_err());
|
||||
}
|
||||
#[test]
|
||||
fn empty() {
|
||||
assert!(parse_params([].iter().cloned()).is_err());
|
||||
assert!(parse_params([].iter().cloned().peekable()).is_err());
|
||||
}
|
||||
#[test]
|
||||
fn conflicting_output_styles() {
|
||||
@@ -797,6 +876,7 @@ mod tests {
|
||||
[os("diff"), os(arg1), os(arg2), os("foo"), os("bar")]
|
||||
.iter()
|
||||
.cloned()
|
||||
.peekable()
|
||||
)
|
||||
.is_err());
|
||||
}
|
||||
|
||||
+29
-1
@@ -3,8 +3,9 @@
|
||||
// For the full copyright and license information, please view the LICENSE-*
|
||||
// files that was distributed with this source code.
|
||||
|
||||
use std::io::Write;
|
||||
use std::{ffi::OsString, io::Write};
|
||||
|
||||
use regex::Regex;
|
||||
use unicode_width::UnicodeWidthStr;
|
||||
|
||||
/// Replace tabs by spaces in the input line.
|
||||
@@ -71,6 +72,33 @@ pub fn get_modification_time(file_path: &str) -> String {
|
||||
modification_time
|
||||
}
|
||||
|
||||
pub fn format_failure_to_read_input_file(
|
||||
executable: &OsString,
|
||||
filepath: &OsString,
|
||||
error: &std::io::Error,
|
||||
) -> String {
|
||||
// std::io::Error's display trait outputs "{detail} (os error {code})"
|
||||
// but we want only the {detail} (error string) part
|
||||
let error_code_re = Regex::new(r"\ \(os\ error\ \d+\)$").unwrap();
|
||||
format!(
|
||||
"{}: {}: {}",
|
||||
executable.to_string_lossy(),
|
||||
filepath.to_string_lossy(),
|
||||
error_code_re.replace(error.to_string().as_str(), ""),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn report_failure_to_read_input_file(
|
||||
executable: &OsString,
|
||||
filepath: &OsString,
|
||||
error: &std::io::Error,
|
||||
) {
|
||||
eprintln!(
|
||||
"{}",
|
||||
format_failure_to_read_input_file(executable, filepath, error)
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
+821
-250
File diff suppressed because it is too large
Load Diff
@@ -21,7 +21,7 @@
|
||||
# (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. cmp, diff3 or sdiff) are skipped.
|
||||
# (e.g. diff3 or sdiff) are skipped.
|
||||
|
||||
scriptpath=$(dirname "$(readlink -f "$0")")
|
||||
rev=$(git rev-parse HEAD)
|
||||
@@ -57,6 +57,7 @@ upstreamrev=$(git rev-parse HEAD)
|
||||
mkdir src
|
||||
cd src
|
||||
ln -s "$binary" diff
|
||||
ln -s "$binary" cmp
|
||||
cd ../tests
|
||||
|
||||
# Fetch tests/init.sh from the gnulib repository (needed since
|
||||
@@ -86,9 +87,9 @@ for test in $tests
|
||||
do
|
||||
result="FAIL"
|
||||
url="$urlroot$test?id=$upstreamrev"
|
||||
# Run only the tests that invoke `diff`,
|
||||
# Run only the tests that invoke `diff` or `cmp`,
|
||||
# because other binaries aren't implemented yet
|
||||
if ! grep -E -s -q "(cmp|diff3|sdiff)" "$test"
|
||||
if ! grep -E -s -q "(diff3|sdiff)" "$test"
|
||||
then
|
||||
sh "$test" 1> stdout.txt 2> stderr.txt && result="PASS" || exitcode=1
|
||||
json+="{\"test\":\"$test\",\"result\":\"$result\","
|
||||
|
||||
Reference in New Issue
Block a user