"Fossies" - the Fresh Open Source Software Archive

Member "fd-8.1.1/tests/testenv/mod.rs" (25 May 2020, 9528 Bytes) of package /linux/privat/fd-8.1.1.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Rust source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. See also the last Fossies "Diffs" side-by-side code changes report for "mod.rs": 8.0.0_vs_8.1.0.

    1 use std::env;
    2 use std::fs;
    3 use std::io::{self, Write};
    4 #[cfg(unix)]
    5 use std::os::unix;
    6 #[cfg(windows)]
    7 use std::os::windows;
    8 use std::path::{Path, PathBuf};
    9 use std::process;
   10 
   11 use tempdir::TempDir;
   12 
   13 /// Environment for the integration tests.
   14 pub struct TestEnv {
   15     /// Temporary working directory.
   16     temp_dir: TempDir,
   17 
   18     /// Path to the *fd* executable.
   19     fd_exe: PathBuf,
   20 
   21     /// Normalize each line by sorting the whitespace-separated words
   22     normalize_line: bool,
   23 }
   24 
   25 /// Create the working directory and the test files.
   26 fn create_working_directory(
   27     directories: &[&'static str],
   28     files: &[&'static str],
   29 ) -> Result<TempDir, io::Error> {
   30     let temp_dir = TempDir::new("fd-tests")?;
   31 
   32     {
   33         let root = temp_dir.path();
   34 
   35         // Pretend that this is a Git repository in order for `.gitignore` files to be respected
   36         fs::create_dir_all(root.join(".git"))?;
   37 
   38         for directory in directories {
   39             fs::create_dir_all(root.join(directory))?;
   40         }
   41 
   42         for file in files {
   43             fs::File::create(root.join(file))?;
   44         }
   45 
   46         #[cfg(unix)]
   47         unix::fs::symlink(root.join("one/two"), root.join("symlink"))?;
   48 
   49         // Note: creating symlinks on Windows requires the `SeCreateSymbolicLinkPrivilege` which
   50         // is by default only granted for administrators.
   51         #[cfg(windows)]
   52         windows::fs::symlink_dir(root.join("one/two"), root.join("symlink"))?;
   53 
   54         fs::File::create(root.join(".fdignore"))?.write_all(b"fdignored.foo")?;
   55 
   56         fs::File::create(root.join(".gitignore"))?.write_all(b"gitignored.foo")?;
   57     }
   58 
   59     Ok(temp_dir)
   60 }
   61 
   62 /// Find the *fd* executable.
   63 fn find_fd_exe() -> PathBuf {
   64     // Tests exe is in target/debug/deps, the *fd* exe is in target/debug
   65     let root = env::current_exe()
   66         .expect("tests executable")
   67         .parent()
   68         .expect("tests executable directory")
   69         .parent()
   70         .expect("fd executable directory")
   71         .to_path_buf();
   72 
   73     let exe_name = if cfg!(windows) { "fd.exe" } else { "fd" };
   74 
   75     root.join(exe_name)
   76 }
   77 
   78 /// Format an error message for when *fd* did not exit successfully.
   79 fn format_exit_error(args: &[&str], output: &process::Output) -> String {
   80     format!(
   81         "`fd {}` did not exit successfully.\nstdout:\n---\n{}---\nstderr:\n---\n{}---",
   82         args.join(" "),
   83         String::from_utf8_lossy(&output.stdout),
   84         String::from_utf8_lossy(&output.stderr)
   85     )
   86 }
   87 
   88 /// Format an error message for when the output of *fd* did not match the expected output.
   89 fn format_output_error(args: &[&str], expected: &str, actual: &str) -> String {
   90     // Generate diff text.
   91     let diff_text = diff::lines(expected, actual)
   92         .into_iter()
   93         .map(|diff| match diff {
   94             diff::Result::Left(l) => format!("-{}", l),
   95             diff::Result::Both(l, _) => format!(" {}", l),
   96             diff::Result::Right(r) => format!("+{}", r),
   97         })
   98         .collect::<Vec<_>>()
   99         .join("\n");
  100 
  101     format!(
  102         concat!(
  103             "`fd {}` did not produce the expected output.\n",
  104             "Showing diff between expected and actual:\n{}\n"
  105         ),
  106         args.join(" "),
  107         diff_text
  108     )
  109 }
  110 
  111 /// Normalize the output for comparison.
  112 fn normalize_output(s: &str, trim_start: bool, normalize_line: bool) -> String {
  113     // Split into lines and normalize separators.
  114     let mut lines = s
  115         .replace('\0', "NULL\n")
  116         .lines()
  117         .map(|line| {
  118             let line = if trim_start { line.trim_start() } else { line };
  119             let line = line.replace('/', &std::path::MAIN_SEPARATOR.to_string());
  120             if normalize_line {
  121                 let mut words: Vec<_> = line.split_whitespace().collect();
  122                 words.sort();
  123                 return words.join(" ");
  124             }
  125             line
  126         })
  127         .collect::<Vec<_>>();
  128 
  129     lines.sort();
  130     lines.join("\n")
  131 }
  132 
  133 impl TestEnv {
  134     pub fn new(directories: &[&'static str], files: &[&'static str]) -> TestEnv {
  135         let temp_dir = create_working_directory(directories, files).expect("working directory");
  136         let fd_exe = find_fd_exe();
  137 
  138         TestEnv {
  139             temp_dir,
  140             fd_exe,
  141             normalize_line: false,
  142         }
  143     }
  144 
  145     pub fn normalize_line(self, normalize: bool) -> TestEnv {
  146         TestEnv {
  147             temp_dir: self.temp_dir,
  148             fd_exe: self.fd_exe,
  149             normalize_line: normalize,
  150         }
  151     }
  152 
  153     /// Create a broken symlink at the given path in the temp_dir.
  154     pub fn create_broken_symlink<P: AsRef<Path>>(
  155         &mut self,
  156         link_path: P,
  157     ) -> Result<PathBuf, io::Error> {
  158         let root = self.test_root();
  159         let broken_symlink_link = root.join(link_path);
  160         {
  161             let temp_target_dir = TempDir::new("fd-tests-broken-symlink")?;
  162             let broken_symlink_target = temp_target_dir.path().join("broken_symlink_target");
  163             fs::File::create(&broken_symlink_target)?;
  164             #[cfg(unix)]
  165             unix::fs::symlink(&broken_symlink_target, &broken_symlink_link)?;
  166             #[cfg(windows)]
  167             windows::fs::symlink_file(&broken_symlink_target, &broken_symlink_link)?;
  168         }
  169         Ok(broken_symlink_link)
  170     }
  171 
  172     /// Get the root directory for the tests.
  173     pub fn test_root(&self) -> PathBuf {
  174         self.temp_dir.path().to_path_buf()
  175     }
  176 
  177     /// Get the root directory of the file system.
  178     pub fn system_root(&self) -> PathBuf {
  179         let mut components = self.temp_dir.path().components();
  180         PathBuf::from(components.next().expect("root directory").as_os_str())
  181     }
  182 
  183     /// Assert that calling *fd* in the specified path under the root working directory,
  184     /// and with the specified arguments produces the expected output.
  185     pub fn assert_success_and_get_output<P: AsRef<Path>>(
  186         &self,
  187         path: P,
  188         args: &[&str],
  189     ) -> process::Output {
  190         // Setup *fd* command.
  191         let mut cmd = process::Command::new(&self.fd_exe);
  192         cmd.current_dir(self.temp_dir.path().join(path));
  193         cmd.arg("--no-global-ignore-file").args(args);
  194 
  195         // Run *fd*.
  196         let output = cmd.output().expect("fd output");
  197 
  198         // Check for exit status.
  199         if !output.status.success() {
  200             panic!(format_exit_error(args, &output));
  201         }
  202 
  203         output
  204     }
  205 
  206     /// Assert that calling *fd* with the specified arguments produces the expected output.
  207     pub fn assert_output(&self, args: &[&str], expected: &str) {
  208         self.assert_output_subdirectory(".", args, expected)
  209     }
  210 
  211     /// Similar to assert_output, but able to handle non-utf8 output
  212     #[cfg(all(unix, not(target_os = "macos")))]
  213     pub fn assert_output_raw(&self, args: &[&str], expected: &[u8]) {
  214         let output = self.assert_success_and_get_output(".", args);
  215 
  216         assert_eq!(expected, &output.stdout[..]);
  217     }
  218 
  219     /// Assert that calling *fd* in the specified path under the root working directory,
  220     /// and with the specified arguments produces the expected output.
  221     pub fn assert_output_subdirectory<P: AsRef<Path>>(
  222         &self,
  223         path: P,
  224         args: &[&str],
  225         expected: &str,
  226     ) {
  227         let output = self.assert_success_and_get_output(path, args);
  228 
  229         // Normalize both expected and actual output.
  230         let expected = normalize_output(expected, true, self.normalize_line);
  231         let actual = normalize_output(
  232             &String::from_utf8_lossy(&output.stdout),
  233             false,
  234             self.normalize_line,
  235         );
  236 
  237         // Compare actual output to expected output.
  238         if expected != actual {
  239             panic!(format_output_error(args, &expected, &actual));
  240         }
  241     }
  242 
  243     /// Assert that calling *fd* with the specified arguments produces the expected error,
  244     /// and does not succeed.
  245     pub fn assert_failure_with_error(&self, args: &[&str], expected: &str) {
  246         let status = self.assert_error_subdirectory(".", args, expected);
  247         if status.success() {
  248             panic!("error '{}' did not occur.", expected);
  249         }
  250     }
  251 
  252     /// Assert that calling *fd* with the specified arguments produces the expected error.
  253     pub fn assert_error(&self, args: &[&str], expected: &str) -> process::ExitStatus {
  254         self.assert_error_subdirectory(".", args, expected)
  255     }
  256 
  257     /// Assert that calling *fd* in the specified path under the root working directory,
  258     /// and with the specified arguments produces an error with the expected message.
  259     fn assert_error_subdirectory<P: AsRef<Path>>(
  260         &self,
  261         path: P,
  262         args: &[&str],
  263         expected: &str,
  264     ) -> process::ExitStatus {
  265         // Setup *fd* command.
  266         let mut cmd = process::Command::new(&self.fd_exe);
  267         cmd.current_dir(self.temp_dir.path().join(path));
  268         cmd.arg("--no-global-ignore-file").args(args);
  269 
  270         // Run *fd*.
  271         let output = cmd.output().expect("fd output");
  272 
  273         // Normalize both expected and actual output.
  274         let expected_error = normalize_output(expected, true, self.normalize_line);
  275         let actual_err = normalize_output(
  276             &String::from_utf8_lossy(&output.stderr),
  277             false,
  278             self.normalize_line,
  279         );
  280 
  281         // Compare actual output to expected output.
  282         if !actual_err.trim_start().starts_with(&expected_error) {
  283             panic!(format_output_error(args, &expected_error, &actual_err));
  284         }
  285 
  286         return output.status;
  287     }
  288 }