mirror of
https://github.com/uutils/findutils.git
synced 2026-06-10 15:48:30 -07:00
Added support for -exec[dir] (#17)
* Deleted vertical whitespace In an attempt to get around a mistake I made when merging from the master fork * Added a default implementation for has_side_effects And removed all the "return false" implementations (as specified by most of the Matchers). * Added support for -exec and -execdir * Fixed path_to_testing_commandline * Minor tweaks from code review
This commit is contained in:
+5
-1
@@ -14,4 +14,8 @@ regex = "0.2"
|
||||
|
||||
[[bin]]
|
||||
name = "find"
|
||||
path = "src/find/main.rs"
|
||||
path = "src/find/main.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "testing-commandline"
|
||||
path = "src/testing/commandline/main.rs"
|
||||
@@ -0,0 +1,97 @@
|
||||
// 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.
|
||||
|
||||
use std::error::Error;
|
||||
use std::ffi::OsString;
|
||||
use std::io::{Write, stderr};
|
||||
use std::path::Path;
|
||||
use std::process::Command;
|
||||
use walkdir::DirEntry;
|
||||
|
||||
use find::matchers::{Matcher, MatcherIO};
|
||||
|
||||
enum Arg {
|
||||
Filename,
|
||||
LiteralArg(OsString),
|
||||
}
|
||||
|
||||
pub struct SingleExecMatcher {
|
||||
executable: String,
|
||||
args: Vec<Arg>,
|
||||
exec_in_parent_dir: bool,
|
||||
}
|
||||
|
||||
impl SingleExecMatcher {
|
||||
pub fn new(executable: &str,
|
||||
args: &[&str],
|
||||
exec_in_parent_dir: bool)
|
||||
-> Result<SingleExecMatcher, Box<Error>> {
|
||||
|
||||
let transformed_args = args.iter()
|
||||
.map(|&a| match a {
|
||||
"{}" => Arg::Filename,
|
||||
_ => Arg::LiteralArg(OsString::from(a)),
|
||||
})
|
||||
.collect();
|
||||
|
||||
Ok(SingleExecMatcher {
|
||||
executable: executable.to_string(),
|
||||
args: transformed_args,
|
||||
exec_in_parent_dir: exec_in_parent_dir,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn new_box(executable: &str,
|
||||
args: &[&str],
|
||||
exec_in_parent_dir: bool)
|
||||
-> Result<Box<Matcher>, Box<Error>> {
|
||||
Ok(Box::new(SingleExecMatcher::new(executable, args, exec_in_parent_dir)?))
|
||||
}
|
||||
}
|
||||
|
||||
impl Matcher for SingleExecMatcher {
|
||||
fn matches(&self, file_info: &DirEntry, _: &mut MatcherIO) -> bool {
|
||||
let mut command = Command::new(&self.executable);
|
||||
let path_to_file = if self.exec_in_parent_dir {
|
||||
if let Some(f) = file_info.path().file_name() {
|
||||
Path::new(".").join(f)
|
||||
} else {
|
||||
file_info.path().to_path_buf()
|
||||
}
|
||||
} else {
|
||||
file_info.path().to_path_buf()
|
||||
};
|
||||
|
||||
for arg in &self.args {
|
||||
command.arg(match arg {
|
||||
&Arg::LiteralArg(ref a) => a.as_os_str(),
|
||||
&Arg::Filename => path_to_file.as_os_str(),
|
||||
});
|
||||
}
|
||||
if self.exec_in_parent_dir {
|
||||
if let Some(parent) = file_info.path().parent() {
|
||||
command.current_dir(parent);
|
||||
}
|
||||
}
|
||||
match command.status() {
|
||||
Ok(status) => return status.success(),
|
||||
Err(e) => {
|
||||
writeln!(&mut stderr(), "Failed to run {}: {}", self.executable, e).unwrap();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn has_side_effects(&self) -> bool {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
/// No tests here, because we need to call out to an external executable. See
|
||||
/// tests/exec_unit_tests.rs instead.
|
||||
mod tests {}
|
||||
@@ -11,6 +11,7 @@
|
||||
//! to "-foo -o ( -bar -baz )", not "( -foo -o -bar ) -baz").
|
||||
use std::error::Error;
|
||||
use std::iter::Iterator;
|
||||
use std::path::PathBuf;
|
||||
use walkdir::DirEntry;
|
||||
|
||||
use find::matchers::{Matcher, MatcherIO};
|
||||
@@ -41,6 +42,18 @@ impl Matcher for AndMatcher {
|
||||
fn has_side_effects(&self) -> bool {
|
||||
self.submatchers.iter().any(|x| x.has_side_effects())
|
||||
}
|
||||
|
||||
fn finished_dir(&self, dir: &PathBuf) {
|
||||
for m in &self.submatchers {
|
||||
m.finished_dir(dir);
|
||||
}
|
||||
}
|
||||
|
||||
fn finished(&self) {
|
||||
for m in &self.submatchers {
|
||||
m.finished();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AndMatcherBuilder {
|
||||
@@ -97,6 +110,18 @@ impl Matcher for OrMatcher {
|
||||
fn has_side_effects(&self) -> bool {
|
||||
self.submatchers.iter().any(|x| x.has_side_effects())
|
||||
}
|
||||
|
||||
fn finished_dir(&self, dir: &PathBuf) {
|
||||
for m in &self.submatchers {
|
||||
m.finished_dir(dir);
|
||||
}
|
||||
}
|
||||
|
||||
fn finished(&self) {
|
||||
for m in &self.submatchers {
|
||||
m.finished();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct OrMatcherBuilder {
|
||||
@@ -171,6 +196,18 @@ impl Matcher for ListMatcher {
|
||||
fn has_side_effects(&self) -> bool {
|
||||
self.submatchers.iter().any(|x| x.has_side_effects())
|
||||
}
|
||||
|
||||
fn finished_dir(&self, dir: &PathBuf) {
|
||||
for m in &self.submatchers {
|
||||
m.finished_dir(dir);
|
||||
}
|
||||
}
|
||||
|
||||
fn finished(&self) {
|
||||
for m in &self.submatchers {
|
||||
m.finished();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ListMatcherBuilder {
|
||||
@@ -249,10 +286,6 @@ impl Matcher for TrueMatcher {
|
||||
fn matches(&self, _dir_entry: &DirEntry, _: &mut MatcherIO) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn has_side_effects(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// A simple matcher that never matches.
|
||||
@@ -262,10 +295,6 @@ impl Matcher for FalseMatcher {
|
||||
fn matches(&self, _dir_entry: &DirEntry, _: &mut MatcherIO) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn has_side_effects(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl FalseMatcher {
|
||||
@@ -298,6 +327,14 @@ impl Matcher for NotMatcher {
|
||||
fn has_side_effects(&self) -> bool {
|
||||
self.submatcher.has_side_effects()
|
||||
}
|
||||
|
||||
fn finished_dir(&self, dir: &PathBuf) {
|
||||
self.submatcher.finished_dir(dir);
|
||||
}
|
||||
|
||||
fn finished(&self) {
|
||||
self.submatcher.finished();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
// license that can be found in the LICENSE file or at
|
||||
// https://opensource.org/licenses/MIT.
|
||||
|
||||
pub mod exec;
|
||||
mod logical_matchers;
|
||||
mod name;
|
||||
mod printer;
|
||||
@@ -14,6 +15,7 @@ mod type_matcher;
|
||||
|
||||
use regex::Regex;
|
||||
use std::error::Error;
|
||||
use std::path::PathBuf;
|
||||
use std::time::SystemTime;
|
||||
use walkdir::DirEntry;
|
||||
|
||||
@@ -57,11 +59,23 @@ pub trait Matcher {
|
||||
/// Returns whether the given file matches the object's predicate.
|
||||
fn matches(&self, file_info: &DirEntry, matcher_io: &mut MatcherIO) -> bool;
|
||||
|
||||
/// Returns whether the matcher has any side-effects. Iff no such matcher
|
||||
/// exists in the chain, then the filename will be printed to stdout. While
|
||||
/// this is a compile-time fact for most matchers, it's run-time for matchers
|
||||
/// that contain a collection of sub-Matchers.
|
||||
fn has_side_effects(&self) -> bool;
|
||||
/// Returns whether the matcher has any side-effects (e.g. executing a
|
||||
/// command, deleting a file). Iff no such matcher exists in the chain, then
|
||||
/// the filename will be printed to stdout. While this is a compile-time
|
||||
/// fact for most matchers, it's run-time for matchers that contain a
|
||||
/// collection of sub-Matchers.
|
||||
fn has_side_effects(&self) -> bool {
|
||||
// most matchers don't have side-effects, so supply a default implementation.
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Notification that find has finished processing a given directory.
|
||||
fn finished_dir(&self, _finished_directory: &PathBuf) {}
|
||||
|
||||
/// Notification that find has finished processing all directories -
|
||||
/// allowing for any cleanup that isn't suitable for destructors (e.g.
|
||||
/// blocking calls, I/O etc.)
|
||||
fn finished(&self) {}
|
||||
}
|
||||
|
||||
pub enum ComparableValue {
|
||||
@@ -239,7 +253,30 @@ fn build_matcher_tree(args: &[&str],
|
||||
i += 1;
|
||||
Some(size::SizeMatcher::new_box(size, &unit)?)
|
||||
}
|
||||
|
||||
"-exec" | "-execdir" => {
|
||||
let mut arg_index = i + 1;
|
||||
while arg_index < args.len() && args[arg_index] != ";" {
|
||||
if args[arg_index] == "+" {
|
||||
// MultiExecMatcher isn't written yet
|
||||
return Err(From::from(format!("{} [args...] + isn't supported yet. \
|
||||
Only {} [args...] ;",
|
||||
args[i],
|
||||
args[i])));
|
||||
}
|
||||
arg_index += 1;
|
||||
}
|
||||
if arg_index < i + 2 || arg_index == args.len() {
|
||||
// at the minimum we need the executable and the ';'
|
||||
return Err(From::from(format!("missing argument to {}", args[i])));
|
||||
}
|
||||
let expression = args[i];
|
||||
let executable = args[i + 1];
|
||||
let exec_args = &args[i + 2..arg_index];
|
||||
i = arg_index;
|
||||
Some(exec::SingleExecMatcher::new_box(executable,
|
||||
exec_args,
|
||||
expression == "-execdir")?)
|
||||
}
|
||||
"-prune" => Some(prune::PruneMatcher::new_box()),
|
||||
"-not" | "!" => {
|
||||
if !are_more_expressions(args, i) {
|
||||
@@ -728,4 +765,35 @@ mod tests {
|
||||
panic!("parsing a bad ctime value should fail");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn build_top_level_exec_not_enough_args() {
|
||||
let mut config = Config::default();
|
||||
|
||||
if let Err(e) = build_top_level_matcher(&["-exec"], &mut config) {
|
||||
assert!(e.description().contains("missing argument"));
|
||||
} else {
|
||||
panic!("parsing argument list with exec and no executable or semi-colon should fail");
|
||||
}
|
||||
|
||||
if let Err(e) = build_top_level_matcher(&["-exec", ";"], &mut config) {
|
||||
assert!(e.description().contains("missing argument"));
|
||||
} else {
|
||||
panic!("parsing argument list with exec and no executable should fail");
|
||||
}
|
||||
|
||||
if let Err(e) = build_top_level_matcher(&["-exec", "foo"], &mut config) {
|
||||
assert!(e.description().contains("missing argument"));
|
||||
} else {
|
||||
panic!("parsing argument list with exec and no executable should fail");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn build_top_level_exec_should_eat_args() {
|
||||
let mut config = Config::default();
|
||||
build_top_level_matcher(&["-exec", "foo", "-o", "(", ";"], &mut config)
|
||||
.expect("parsing argument list with exec that takes brackets and -os should work");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -32,10 +32,6 @@ impl Matcher for NameMatcher {
|
||||
fn matches(&self, file_info: &DirEntry, _: &mut MatcherIO) -> bool {
|
||||
self.pattern.matches(file_info.file_name().to_string_lossy().as_ref())
|
||||
}
|
||||
|
||||
fn has_side_effects(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// This matcher makes a case-insensitive comparison of the name against a
|
||||
@@ -61,10 +57,6 @@ impl super::Matcher for CaselessNameMatcher {
|
||||
self.pattern
|
||||
.matches(file_info.file_name().to_string_lossy().to_lowercase().as_ref())
|
||||
}
|
||||
|
||||
fn has_side_effects(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -26,10 +26,6 @@ impl Matcher for PruneMatcher {
|
||||
matcher_io.mark_current_dir_to_be_skipped();
|
||||
true
|
||||
}
|
||||
|
||||
fn has_side_effects(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
#[cfg(test)]
|
||||
|
||||
|
||||
@@ -103,10 +103,6 @@ impl Matcher for SizeMatcher {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn has_side_effects(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -57,10 +57,6 @@ impl Matcher for NewerMatcher {
|
||||
Ok(t) => t,
|
||||
}
|
||||
}
|
||||
|
||||
fn has_side_effects(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
@@ -102,10 +98,6 @@ impl Matcher for FileTimeMatcher {
|
||||
Ok(t) => t,
|
||||
}
|
||||
}
|
||||
|
||||
fn has_side_effects(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -37,10 +37,6 @@ impl Matcher for TypeMatcher {
|
||||
fn matches(&self, file_info: &DirEntry, _: &mut MatcherIO) -> bool {
|
||||
(self.file_type_fn)(&file_info.file_type())
|
||||
}
|
||||
|
||||
fn has_side_effects(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
#[cfg(test)]
|
||||
|
||||
|
||||
+2
-2
@@ -4,7 +4,7 @@
|
||||
// license that can be found in the LICENSE file or at
|
||||
// https://opensource.org/licenses/MIT.
|
||||
|
||||
mod matchers;
|
||||
pub mod matchers;
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::error::Error;
|
||||
@@ -171,6 +171,7 @@ Early alpha implementation. Currently the only expressions supported are
|
||||
-atime [+-]N
|
||||
-mtime [+-]N
|
||||
-newer path_to_file
|
||||
-exec[dir] executable [args] [{{}}] [more args] ;
|
||||
-sorted
|
||||
a non-standard extension that sorts directory contents by name before
|
||||
processing them. Less efficient, but allows for deterministic output.
|
||||
@@ -499,5 +500,4 @@ mod tests {
|
||||
assert_eq!(deps.get_output_as_string(), "");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
// 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.
|
||||
|
||||
use std::env;
|
||||
use std::fs::{self, File, OpenOptions};
|
||||
use std::io::Write;
|
||||
use std::path::PathBuf;
|
||||
|
||||
fn usage() -> ! {
|
||||
println!("Simple command-line app just used for testing -exec flags!");
|
||||
std::process::exit(2);
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct Config {
|
||||
exit_with_failure: bool,
|
||||
destination_dir: String,
|
||||
}
|
||||
|
||||
fn open_file(destination_dir: &str) -> File {
|
||||
let mut file_number =
|
||||
fs::read_dir(destination_dir).expect("failed to read destination").count();
|
||||
|
||||
loop {
|
||||
file_number += 1;
|
||||
let mut file_path: PathBuf = PathBuf::from(destination_dir);
|
||||
file_path.push(format!("{}.txt", file_number));
|
||||
if let Ok(f) = OpenOptions::new()
|
||||
.write(true)
|
||||
.create_new(true)
|
||||
.open(file_path) {
|
||||
return f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let args = env::args().collect::<Vec<String>>();
|
||||
if args.len() < 2 || args[1] == "-h" || args[1] == "--help" {
|
||||
usage();
|
||||
}
|
||||
let mut config = Config::default();
|
||||
config.destination_dir = args[1].clone();
|
||||
for arg in &args[2..] {
|
||||
if arg.starts_with("--") {
|
||||
match arg.as_ref() {
|
||||
"--exit_with_failure" => {
|
||||
config.exit_with_failure = true;
|
||||
}
|
||||
_ => {
|
||||
usage();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
let mut f = open_file(&config.destination_dir);
|
||||
// first two args are going to be the path to this executable and
|
||||
// the destination_dir we want to write to. Don't write either of those
|
||||
// as they'll be non-deterministic.
|
||||
f.write_fmt(format_args!("cwd={}\nargs={:?}\n",
|
||||
env::current_dir().unwrap().to_string_lossy(),
|
||||
&args[2..]))
|
||||
.expect("failed to write to file");
|
||||
}
|
||||
std::process::exit(if config.exit_with_failure { 2 } else { 0 });
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
// 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.
|
||||
|
||||
|
||||
// As this module is included by all the integration tests, any function used
|
||||
// in one test but not another can cause a dead code warning.
|
||||
#[allow(dead_code)]
|
||||
pub mod test_helpers;
|
||||
@@ -0,0 +1,86 @@
|
||||
// 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.
|
||||
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::env;
|
||||
use std::io::{Cursor, Read, Write};
|
||||
use std::time::SystemTime;
|
||||
use std::vec::Vec;
|
||||
use walkdir::{DirEntry, WalkDir};
|
||||
|
||||
use findutils::find::matchers::MatcherIO;
|
||||
use findutils::find::Dependencies;
|
||||
|
||||
/// A copy of find::tests::FakeDependencies.
|
||||
/// TODO: find out how to share #[cfg(test)] functions/structs between unit
|
||||
/// and integration tests.
|
||||
pub struct FakeDependencies {
|
||||
pub output: RefCell<Cursor<Vec<u8>>>,
|
||||
now: SystemTime,
|
||||
}
|
||||
|
||||
impl<'a> FakeDependencies {
|
||||
pub fn new() -> FakeDependencies {
|
||||
FakeDependencies {
|
||||
output: RefCell::new(Cursor::new(Vec::<u8>::new())),
|
||||
now: SystemTime::now(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_matcher_io(&'a self) -> MatcherIO<'a> {
|
||||
MatcherIO::new(self)
|
||||
}
|
||||
|
||||
pub fn get_output_as_string(&self) -> String {
|
||||
let mut cursor = self.output.borrow_mut();
|
||||
cursor.set_position(0);
|
||||
let mut contents = String::new();
|
||||
cursor.read_to_string(&mut contents).unwrap();
|
||||
contents
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Dependencies<'a> for FakeDependencies {
|
||||
fn get_output(&'a self) -> &'a RefCell<Write> {
|
||||
&self.output
|
||||
}
|
||||
|
||||
fn now(&'a self) -> SystemTime {
|
||||
self.now
|
||||
}
|
||||
}
|
||||
|
||||
pub fn path_to_testing_commandline() -> String {
|
||||
|
||||
let mut path_to_use = env::current_exe()
|
||||
// this will be something along the lines of /my/homedir/findutils/target/debug/deps/findutils-5532804878869ef1
|
||||
.expect("can't find path of this executable")
|
||||
.parent()
|
||||
.expect("can't find parent directory of this executable")
|
||||
.to_path_buf();
|
||||
// and we want /my/homedir/findutils/target/debug/testing-commandline
|
||||
if path_to_use.ends_with("deps") {
|
||||
path_to_use.pop();
|
||||
}
|
||||
path_to_use = path_to_use.join("testing-commandline");
|
||||
path_to_use.to_string_lossy()
|
||||
.to_string()
|
||||
}
|
||||
|
||||
|
||||
/// A copy of find::tests::FakeDependencies.
|
||||
/// TODO: find out how to share #[cfg(test)] functions/structs between unit
|
||||
/// and integration tests.
|
||||
pub fn get_dir_entry_for(directory: &str, filename: &str) -> DirEntry {
|
||||
for wrapped_dir_entry in WalkDir::new(directory) {
|
||||
let dir_entry = wrapped_dir_entry.unwrap();
|
||||
if dir_entry.file_name().to_string_lossy() == filename {
|
||||
return dir_entry;
|
||||
}
|
||||
}
|
||||
panic!("Couldn't find {} in {}", directory, filename);
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
// 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.
|
||||
extern crate findutils;
|
||||
extern crate tempdir;
|
||||
extern crate walkdir;
|
||||
|
||||
|
||||
use std::env;
|
||||
use std::fs::File;
|
||||
use std::io::Read;
|
||||
use tempdir::TempDir;
|
||||
|
||||
|
||||
use findutils::find::matchers::Matcher;
|
||||
use findutils::find::matchers::exec::*;
|
||||
use common::test_helpers::*;
|
||||
|
||||
mod common;
|
||||
|
||||
#[test]
|
||||
fn matching_executes_code() {
|
||||
|
||||
let temp_dir = TempDir::new("matching_executes_code").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(),
|
||||
&vec![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,
|
||||
format!("cwd={}\nargs=[\"abc\", \"test_data/simple/abbbc\", \"xyz\"]\n",
|
||||
env::current_dir().unwrap().to_string_lossy()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn matching_executes_code_in_files_directory() {
|
||||
|
||||
let temp_dir = TempDir::new("matching_executes_code_in_files_directory").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(),
|
||||
&vec![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,
|
||||
format!("cwd={}/test_data/simple\nargs=[\"abc\", \"./abbbc\", \"xyz\"]\n",
|
||||
env::current_dir().unwrap().to_string_lossy()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn matching_fails_if_executable_fails() {
|
||||
|
||||
let temp_dir = TempDir::new("matching_fails_if_executable_fails").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(),
|
||||
&vec![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,
|
||||
format!("cwd={}/test_data/simple\nargs=[\"--exit_with_failure\", \"abc\", \
|
||||
\"./abbbc\", \"xyz\"]\n",
|
||||
env::current_dir().unwrap().to_string_lossy()));
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
// 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::find_main
|
||||
/// ! related to -exec[dir] and ok[dir] clauses.
|
||||
/// ! 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.
|
||||
extern crate findutils;
|
||||
extern crate tempdir;
|
||||
extern crate walkdir;
|
||||
|
||||
|
||||
use std::env;
|
||||
use std::fs::File;
|
||||
use std::io::Read;
|
||||
use tempdir::TempDir;
|
||||
|
||||
use findutils::find::find_main;
|
||||
use common::test_helpers::*;
|
||||
|
||||
mod common;
|
||||
#[test]
|
||||
fn find_exec() {
|
||||
let temp_dir = TempDir::new("find_exec").unwrap();
|
||||
let temp_dir_path = temp_dir.path().to_string_lossy();
|
||||
let deps = FakeDependencies::new();
|
||||
|
||||
let rc = find_main(&["find",
|
||||
"./test_data/simple/subdir",
|
||||
"-type",
|
||||
"f",
|
||||
"-exec",
|
||||
&path_to_testing_commandline(),
|
||||
temp_dir_path.as_ref(),
|
||||
"(",
|
||||
"{}",
|
||||
"-o",
|
||||
";"],
|
||||
&deps);
|
||||
|
||||
assert_eq!(rc, 0);
|
||||
// exec has side effects, so we won't output anything unless -print is
|
||||
// explicitly passed in.
|
||||
assert_eq!(deps.get_output_as_string(), "");
|
||||
|
||||
// check the executable ran as expected
|
||||
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,
|
||||
format!("cwd={}\nargs=[\"(\", \"./test_data/simple/subdir/ABBBC\", \"-o\"]\n",
|
||||
env::current_dir().unwrap().to_string_lossy()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn find_execdir() {
|
||||
let temp_dir = TempDir::new("find_execdir").unwrap();
|
||||
let temp_dir_path = temp_dir.path().to_string_lossy();
|
||||
let deps = FakeDependencies::new();
|
||||
// only look at files because the "size" of a directory is a system (and filesystem)
|
||||
// dependent thing and we want these tests to be universal.
|
||||
let rc = find_main(&["find",
|
||||
"./test_data/simple/subdir",
|
||||
"-type",
|
||||
"f",
|
||||
"-execdir",
|
||||
&path_to_testing_commandline(),
|
||||
temp_dir_path.as_ref(),
|
||||
")",
|
||||
"{}",
|
||||
",",
|
||||
";"],
|
||||
&deps);
|
||||
|
||||
assert_eq!(rc, 0);
|
||||
// exec has side effects, so we won't output anything unless -print is
|
||||
// explicitly passed in.
|
||||
assert_eq!(deps.get_output_as_string(), "");
|
||||
|
||||
// check the executable ran as expected
|
||||
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,
|
||||
format!("cwd={}/test_data/simple/subdir\nargs=[\")\", \"./ABBBC\", \",\"]\n",
|
||||
env::current_dir().unwrap().to_string_lossy()));
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user