mirror of
https://github.com/uutils/findutils.git
synced 2026-06-10 15:48:30 -07:00
e7c34ab12f
The test from #647 used {} only in an argument, so it passed on unpatched code. Rewrite it to make the matched entry the testing-commandline binary and pass {} as the utility name, so the placeholder must resolve to the entry's path and execute.
519 lines
17 KiB
Rust
519 lines
17 KiB
Rust
// Copyright 2017 Google Inc.
|
|
//
|
|
// Use of this source code is governed by a MIT-style
|
|
// license that can be found in the LICENSE file or at
|
|
// https://opensource.org/licenses/MIT.
|
|
|
|
/// ! This file contains what would be normally be unit tests for `find::matchers::exec`.
|
|
/// ! But as the tests require running an external executable, they need to be run
|
|
/// ! as integration tests so we can ensure that our testing-commandline binary
|
|
/// ! has been built.
|
|
use std::env;
|
|
use std::fs::File;
|
|
use std::io::Read;
|
|
use std::path::Path;
|
|
use tempfile::Builder;
|
|
|
|
use common::test_helpers::{
|
|
fix_up_slashes, get_dir_entry_for, path_to_testing_commandline, FakeDependencies,
|
|
};
|
|
use findutils::find::matchers::exec::{MultiExecMatcher, SingleExecMatcher};
|
|
use findutils::find::matchers::{Matcher, MatcherIO};
|
|
|
|
mod common;
|
|
|
|
#[test]
|
|
fn matching_executes_code() {
|
|
let temp_dir = Builder::new()
|
|
.prefix("matching_executes_code")
|
|
.tempdir()
|
|
.unwrap();
|
|
let temp_dir_path = temp_dir.path().to_string_lossy();
|
|
|
|
let abbbc = get_dir_entry_for("test_data/simple", "abbbc");
|
|
let matcher = SingleExecMatcher::new(
|
|
&path_to_testing_commandline(),
|
|
&[temp_dir_path.as_ref(), "abc", "{}", "xyz"],
|
|
false,
|
|
)
|
|
.expect("Failed to create matcher");
|
|
let deps = FakeDependencies::new();
|
|
assert!(matcher.matches(&abbbc, &mut deps.new_matcher_io()));
|
|
|
|
let mut f = File::open(temp_dir.path().join("1.txt")).expect("Failed to open output file");
|
|
let mut s = String::new();
|
|
f.read_to_string(&mut s)
|
|
.expect("failed to read output file");
|
|
assert_eq!(
|
|
s,
|
|
fix_up_slashes(&format!(
|
|
"cwd={}\nargs=\nabc\ntest_data/simple/abbbc\nxyz\n",
|
|
env::current_dir().unwrap().to_string_lossy()
|
|
))
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn matching_executes_code_in_files_directory() {
|
|
let temp_dir = Builder::new()
|
|
.prefix("matching_executes_code_in_files_directory")
|
|
.tempdir()
|
|
.unwrap();
|
|
let temp_dir_path = temp_dir.path().to_string_lossy();
|
|
|
|
let abbbc = get_dir_entry_for("test_data/simple", "abbbc");
|
|
let matcher = SingleExecMatcher::new(
|
|
&path_to_testing_commandline(),
|
|
&[temp_dir_path.as_ref(), "abc", "{}", "xyz"],
|
|
true,
|
|
)
|
|
.expect("Failed to create matcher");
|
|
let deps = FakeDependencies::new();
|
|
assert!(matcher.matches(&abbbc, &mut deps.new_matcher_io()));
|
|
|
|
let mut f = File::open(temp_dir.path().join("1.txt")).expect("Failed to open output file");
|
|
let mut s = String::new();
|
|
f.read_to_string(&mut s)
|
|
.expect("failed to read output file");
|
|
assert_eq!(
|
|
s,
|
|
fix_up_slashes(&format!(
|
|
"cwd={}/test_data/simple\nargs=\nabc\n./abbbc\nxyz\n",
|
|
env::current_dir().unwrap().to_string_lossy()
|
|
))
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn matching_embedded_filename() {
|
|
let temp_dir = Builder::new()
|
|
.prefix("matching_embedded_filename")
|
|
.tempdir()
|
|
.unwrap();
|
|
let temp_dir_path = temp_dir.path().to_string_lossy();
|
|
|
|
let abbbc = get_dir_entry_for("test_data/simple", "abbbc");
|
|
let matcher = SingleExecMatcher::new(
|
|
&path_to_testing_commandline(),
|
|
&[temp_dir_path.as_ref(), "abc{}x{}yz"],
|
|
false,
|
|
)
|
|
.expect("Failed to create matcher");
|
|
let deps = FakeDependencies::new();
|
|
assert!(matcher.matches(&abbbc, &mut deps.new_matcher_io()));
|
|
|
|
let mut f = File::open(temp_dir.path().join("1.txt")).expect("Failed to open output file");
|
|
let mut s = String::new();
|
|
f.read_to_string(&mut s)
|
|
.expect("failed to read output file");
|
|
assert_eq!(
|
|
s,
|
|
fix_up_slashes(&format!(
|
|
"cwd={}\nargs=\nabctest_data/simple/abbbcxtest_data/simple/abbbcyz\n",
|
|
env::current_dir().unwrap().to_string_lossy()
|
|
))
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
/// Running "find . -execdir whatever \;" failed with a No such file or directory error.
|
|
/// It's now fixed, and this is a regression test to check that it stays fixed.
|
|
fn execdir_in_current_directory() {
|
|
let temp_dir = Builder::new()
|
|
.prefix("execdir_in_current_directory")
|
|
.tempdir()
|
|
.unwrap();
|
|
let temp_dir_path = temp_dir.path().to_string_lossy();
|
|
|
|
let current_dir_entry = get_dir_entry_for(".", "");
|
|
let matcher = SingleExecMatcher::new(
|
|
&path_to_testing_commandline(),
|
|
&[temp_dir_path.as_ref(), "abc", "{}", "xyz"],
|
|
true,
|
|
)
|
|
.expect("Failed to create matcher");
|
|
let deps = FakeDependencies::new();
|
|
assert!(matcher.matches(¤t_dir_entry, &mut deps.new_matcher_io()));
|
|
|
|
let mut f = File::open(temp_dir.path().join("1.txt")).expect("Failed to open output file");
|
|
let mut s = String::new();
|
|
f.read_to_string(&mut s)
|
|
.expect("failed to read output file");
|
|
assert_eq!(
|
|
s,
|
|
fix_up_slashes(&format!(
|
|
"cwd={}\nargs=\nabc\n./.\nxyz\n",
|
|
env::current_dir().unwrap().to_string_lossy()
|
|
))
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
/// Regression test for "find / -execdir whatever \;"
|
|
fn execdir_in_root_directory() {
|
|
let temp_dir = Builder::new()
|
|
.prefix("execdir_in_root_directory")
|
|
.tempdir()
|
|
.unwrap();
|
|
let temp_dir_path = temp_dir.path().to_string_lossy();
|
|
|
|
let cwd = env::current_dir().expect("no current directory");
|
|
let root_dir = cwd
|
|
.ancestors()
|
|
.last()
|
|
.expect("current directory has no root");
|
|
let root_dir_entry = get_dir_entry_for(root_dir.to_str().unwrap(), "");
|
|
|
|
let matcher = SingleExecMatcher::new(
|
|
&path_to_testing_commandline(),
|
|
&[temp_dir_path.as_ref(), "abc", "{}", "xyz"],
|
|
true,
|
|
)
|
|
.expect("Failed to create matcher");
|
|
let deps = FakeDependencies::new();
|
|
assert!(matcher.matches(&root_dir_entry, &mut deps.new_matcher_io()));
|
|
|
|
let mut f = File::open(temp_dir.path().join("1.txt")).expect("Failed to open output file");
|
|
let mut s = String::new();
|
|
f.read_to_string(&mut s)
|
|
.expect("failed to read output file");
|
|
assert_eq!(
|
|
s,
|
|
fix_up_slashes(&format!(
|
|
"cwd={}\nargs=\nabc\n{}\nxyz\n",
|
|
root_dir.to_string_lossy(),
|
|
root_dir.to_string_lossy(),
|
|
))
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn matching_fails_if_executable_fails() {
|
|
let temp_dir = Builder::new()
|
|
.prefix("matching_fails_if_executable_fails")
|
|
.tempdir()
|
|
.unwrap();
|
|
let temp_dir_path = temp_dir.path().to_string_lossy();
|
|
|
|
let abbbc = get_dir_entry_for("test_data/simple", "abbbc");
|
|
let matcher = SingleExecMatcher::new(
|
|
&path_to_testing_commandline(),
|
|
&[
|
|
temp_dir_path.as_ref(),
|
|
"--exit_with_failure",
|
|
"abc",
|
|
"{}",
|
|
"xyz",
|
|
],
|
|
true,
|
|
)
|
|
.expect("Failed to create matcher");
|
|
let deps = FakeDependencies::new();
|
|
assert!(!matcher.matches(&abbbc, &mut deps.new_matcher_io()));
|
|
|
|
let mut f = File::open(temp_dir.path().join("1.txt")).expect("Failed to open output file");
|
|
let mut s = String::new();
|
|
f.read_to_string(&mut s)
|
|
.expect("failed to read output file");
|
|
assert_eq!(
|
|
s,
|
|
fix_up_slashes(&format!(
|
|
"cwd={}/test_data/simple\nargs=\n--exit_with_failure\nabc\n.\
|
|
/abbbc\nxyz\n",
|
|
env::current_dir().unwrap().to_string_lossy()
|
|
))
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn placeholder_in_utility_name() {
|
|
let temp_dir = Builder::new()
|
|
.prefix("placeholder_in_utility_name")
|
|
.tempdir()
|
|
.unwrap();
|
|
let temp_dir_path = temp_dir.path().to_string_lossy();
|
|
|
|
// To exercise `{}` in the utility-name position we make the matched entry
|
|
// *be* the testing-commandline binary, then pass `{}` as the executable.
|
|
// The placeholder must be replaced with the entry's path and executed --
|
|
// this is exactly the behavior that regressed in #614, where the literal
|
|
// string "{}" was handed to Command::new and the command failed to run.
|
|
let testing_commandline = path_to_testing_commandline();
|
|
let binary_path = Path::new(&testing_commandline);
|
|
let root = binary_path
|
|
.parent()
|
|
.expect("testing-commandline has no parent directory")
|
|
.to_string_lossy();
|
|
let binary_name = binary_path
|
|
.file_name()
|
|
.expect("testing-commandline has no file name")
|
|
.to_string_lossy();
|
|
let entry = get_dir_entry_for(&root, &binary_name);
|
|
|
|
let matcher = SingleExecMatcher::new("{}", &[temp_dir_path.as_ref(), "executed"], false)
|
|
.expect("Failed to create matcher");
|
|
let deps = FakeDependencies::new();
|
|
assert!(
|
|
matcher.matches(&entry, &mut deps.new_matcher_io()),
|
|
"matcher should resolve {{}} to the binary path and run it"
|
|
);
|
|
|
|
let mut f = File::open(temp_dir.path().join("1.txt")).expect("Failed to open output file");
|
|
let mut s = String::new();
|
|
f.read_to_string(&mut s)
|
|
.expect("failed to read output file");
|
|
assert_eq!(
|
|
s,
|
|
fix_up_slashes(&format!(
|
|
"cwd={}\nargs=\nexecuted\n",
|
|
env::current_dir().unwrap().to_string_lossy()
|
|
))
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn matching_multi_executes_code() {
|
|
let temp_dir = Builder::new()
|
|
.prefix("matching_executes_code")
|
|
.tempdir()
|
|
.unwrap();
|
|
let temp_dir_path = temp_dir.path().to_string_lossy();
|
|
|
|
let abbbc = get_dir_entry_for("test_data/simple", "abbbc");
|
|
let matcher = MultiExecMatcher::new(
|
|
&path_to_testing_commandline(),
|
|
&[temp_dir_path.as_ref(), "abc"],
|
|
false,
|
|
)
|
|
.expect("Failed to create matcher");
|
|
let deps = FakeDependencies::new();
|
|
let mut matcher_io = MatcherIO::new(&deps);
|
|
assert!(matcher.matches(&abbbc, &mut deps.new_matcher_io()));
|
|
matcher.finished(&mut matcher_io);
|
|
|
|
let mut f = File::open(temp_dir.path().join("1.txt")).expect("Failed to open output file");
|
|
let mut s = String::new();
|
|
f.read_to_string(&mut s)
|
|
.expect("failed to read output file");
|
|
assert_eq!(
|
|
s,
|
|
fix_up_slashes(&format!(
|
|
"cwd={}\nargs=\nabc\ntest_data/simple/abbbc\n",
|
|
env::current_dir().unwrap().to_string_lossy()
|
|
))
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn execdir_multi_in_current_directory() {
|
|
let temp_dir = Builder::new()
|
|
.prefix("execdir_in_current_directory")
|
|
.tempdir()
|
|
.unwrap();
|
|
let temp_dir_path = temp_dir.path().to_string_lossy();
|
|
|
|
let current_dir_entry = get_dir_entry_for(".", "");
|
|
let matcher = MultiExecMatcher::new(
|
|
&path_to_testing_commandline(),
|
|
&[temp_dir_path.as_ref(), "abc"],
|
|
true,
|
|
)
|
|
.expect("Failed to create matcher");
|
|
let deps = FakeDependencies::new();
|
|
let mut matcher_io = MatcherIO::new(&deps);
|
|
assert!(matcher.matches(¤t_dir_entry, &mut deps.new_matcher_io()));
|
|
matcher.finished_dir(Path::new(""), &mut matcher_io);
|
|
matcher.finished(&mut matcher_io);
|
|
|
|
let mut f = File::open(temp_dir.path().join("1.txt")).expect("Failed to open output file");
|
|
let mut s = String::new();
|
|
f.read_to_string(&mut s)
|
|
.expect("failed to read output file");
|
|
assert_eq!(
|
|
s,
|
|
fix_up_slashes(&format!(
|
|
"cwd={}\nargs=\nabc\n./.\n",
|
|
env::current_dir().unwrap().to_string_lossy()
|
|
))
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn multi_set_exit_code_if_executable_fails() {
|
|
let temp_dir = Builder::new()
|
|
.prefix("multi_set_exit_code_if_executable_fails")
|
|
.tempdir()
|
|
.unwrap();
|
|
let temp_dir_path = temp_dir.path().to_string_lossy();
|
|
|
|
let abbbc = get_dir_entry_for("test_data/simple", "abbbc");
|
|
let matcher = MultiExecMatcher::new(
|
|
&path_to_testing_commandline(),
|
|
&[temp_dir_path.as_ref(), "--exit_with_failure", "abc"],
|
|
true,
|
|
)
|
|
.expect("Failed to create matcher");
|
|
let deps = FakeDependencies::new();
|
|
assert!(matcher.matches(&abbbc, &mut deps.new_matcher_io()));
|
|
let mut matcher_io = deps.new_matcher_io();
|
|
matcher.finished_dir(Path::new("test_data/simple"), &mut matcher_io);
|
|
assert!(matcher_io.exit_code() == 1);
|
|
|
|
let mut f = File::open(temp_dir.path().join("1.txt")).expect("Failed to open output file");
|
|
let mut s = String::new();
|
|
f.read_to_string(&mut s)
|
|
.expect("failed to read output file");
|
|
assert_eq!(
|
|
s,
|
|
fix_up_slashes(&format!(
|
|
"cwd={}/test_data/simple\nargs=\n--exit_with_failure\nabc\n./abbbc\n",
|
|
env::current_dir().unwrap().to_string_lossy()
|
|
))
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn multi_set_exit_code_if_command_fails() {
|
|
let abbbc = get_dir_entry_for("test_data/simple", "abbbc");
|
|
let matcher = MultiExecMatcher::new("1337", &["abc"], true).expect("Failed to create matcher");
|
|
let deps = FakeDependencies::new();
|
|
assert!(matcher.matches(&abbbc, &mut deps.new_matcher_io()));
|
|
let mut matcher_io = deps.new_matcher_io();
|
|
matcher.finished_dir(Path::new("test_data/simple"), &mut matcher_io);
|
|
assert!(matcher_io.exit_code() == 1);
|
|
}
|
|
|
|
// -ok / -okdir tests
|
|
//
|
|
// These use FakeDependencies, which answers confirm() from a preset queue
|
|
// rather than a real terminal. That tests both the "confirmed" and
|
|
// "declined" paths without needing a TTY. The integration tests in
|
|
// test_find.rs cover the stdin-fallback path (no controlling terminal).
|
|
|
|
#[test]
|
|
fn ok_executes_when_confirmed() {
|
|
// When the user confirms, -ok runs the command and returns true on success.
|
|
let temp_dir = Builder::new()
|
|
.prefix("ok_executes_when_confirmed")
|
|
.tempdir()
|
|
.unwrap();
|
|
let temp_dir_path = temp_dir.path().to_string_lossy();
|
|
|
|
let abbbc = get_dir_entry_for("test_data/simple", "abbbc");
|
|
let matcher = SingleExecMatcher::new_interactive(
|
|
&path_to_testing_commandline(),
|
|
&[temp_dir_path.as_ref(), "abc", "{}", "xyz"],
|
|
false,
|
|
)
|
|
.expect("Failed to create matcher");
|
|
|
|
let deps = FakeDependencies::new();
|
|
deps.push_confirm_response(true);
|
|
assert!(matcher.matches(&abbbc, &mut deps.new_matcher_io()));
|
|
|
|
let mut f = File::open(temp_dir.path().join("1.txt")).expect("Failed to open output file");
|
|
let mut s = String::new();
|
|
f.read_to_string(&mut s)
|
|
.expect("failed to read output file");
|
|
assert_eq!(
|
|
s,
|
|
fix_up_slashes(&format!(
|
|
"cwd={}\nargs=\nabc\ntest_data/simple/abbbc\nxyz\n",
|
|
env::current_dir().unwrap().to_string_lossy()
|
|
))
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn ok_skips_when_declined() {
|
|
// When the user declines, -ok returns false without running the command.
|
|
let temp_dir = Builder::new()
|
|
.prefix("ok_skips_when_declined")
|
|
.tempdir()
|
|
.unwrap();
|
|
let temp_dir_path = temp_dir.path().to_string_lossy();
|
|
|
|
let abbbc = get_dir_entry_for("test_data/simple", "abbbc");
|
|
let matcher = SingleExecMatcher::new_interactive(
|
|
&path_to_testing_commandline(),
|
|
&[temp_dir_path.as_ref(), "abc", "{}", "xyz"],
|
|
false,
|
|
)
|
|
.expect("Failed to create matcher");
|
|
|
|
let deps = FakeDependencies::new();
|
|
deps.push_confirm_response(false);
|
|
assert!(!matcher.matches(&abbbc, &mut deps.new_matcher_io()));
|
|
|
|
// The command was not run, so no output file should exist.
|
|
assert!(
|
|
!temp_dir.path().join("1.txt").exists(),
|
|
"command should not have run when user declined"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn okdir_executes_in_parent_dir_when_confirmed() {
|
|
// -okdir runs the command in the file's parent directory, same as -execdir.
|
|
let temp_dir = Builder::new()
|
|
.prefix("okdir_executes_when_confirmed")
|
|
.tempdir()
|
|
.unwrap();
|
|
let temp_dir_path = temp_dir.path().to_string_lossy();
|
|
|
|
let abbbc = get_dir_entry_for("test_data/simple", "abbbc");
|
|
let matcher = SingleExecMatcher::new_interactive(
|
|
&path_to_testing_commandline(),
|
|
&[temp_dir_path.as_ref(), "abc", "{}", "xyz"],
|
|
true, // exec_in_parent_dir = true → -okdir behaviour
|
|
)
|
|
.expect("Failed to create matcher");
|
|
|
|
let deps = FakeDependencies::new();
|
|
deps.push_confirm_response(true);
|
|
assert!(matcher.matches(&abbbc, &mut deps.new_matcher_io()));
|
|
|
|
let mut f = File::open(temp_dir.path().join("1.txt")).expect("Failed to open output file");
|
|
let mut s = String::new();
|
|
f.read_to_string(&mut s)
|
|
.expect("failed to read output file");
|
|
assert_eq!(
|
|
s,
|
|
fix_up_slashes(&format!(
|
|
"cwd={}/test_data/simple\nargs=\nabc\n./abbbc\nxyz\n",
|
|
env::current_dir().unwrap().to_string_lossy()
|
|
))
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn ok_returns_false_when_command_fails() {
|
|
// When the user confirms but the command exits non-zero, -ok returns false.
|
|
let temp_dir = Builder::new()
|
|
.prefix("ok_returns_false_when_command_fails")
|
|
.tempdir()
|
|
.unwrap();
|
|
let temp_dir_path = temp_dir.path().to_string_lossy();
|
|
|
|
let abbbc = get_dir_entry_for("test_data/simple", "abbbc");
|
|
let matcher = SingleExecMatcher::new_interactive(
|
|
&path_to_testing_commandline(),
|
|
&[
|
|
temp_dir_path.as_ref(),
|
|
"--exit_with_failure",
|
|
"abc",
|
|
"{}",
|
|
"xyz",
|
|
],
|
|
false,
|
|
)
|
|
.expect("Failed to create matcher");
|
|
|
|
let deps = FakeDependencies::new();
|
|
deps.push_confirm_response(true);
|
|
assert!(!matcher.matches(&abbbc, &mut deps.new_matcher_io()));
|
|
|
|
// The command did run (output file exists), but it failed.
|
|
assert!(temp_dir.path().join("1.txt").exists());
|
|
}
|