diff --git a/src/tools/run-make-support/src/assertion_helpers.rs b/src/tools/run-make-support/src/assertion_helpers.rs new file mode 100644 index 00000000000..96f6296b304 --- /dev/null +++ b/src/tools/run-make-support/src/assertion_helpers.rs @@ -0,0 +1,142 @@ +//! Collection of assertions and assertion-related helpers. + +use std::path::{Path, PathBuf}; +use std::panic; + +use crate::fs_wrapper; +use crate::path_helpers::cwd; + +/// Browse the directory `path` non-recursively and return all files which respect the parameters +/// outlined by `closure`. +#[track_caller] +pub fn shallow_find_files, F: Fn(&PathBuf) -> bool>( + path: P, + filter: F, +) -> Vec { + let mut matching_files = Vec::new(); + for entry in fs_wrapper::read_dir(path) { + let entry = entry.expect("failed to read directory entry."); + let path = entry.path(); + + if path.is_file() && filter(&path) { + matching_files.push(path); + } + } + matching_files +} + +/// Returns true if the filename at `path` starts with `prefix`. +pub fn has_prefix>(path: P, prefix: &str) -> bool { + path.as_ref().file_name().is_some_and(|name| name.to_str().unwrap().starts_with(prefix)) +} + +/// Returns true if the filename at `path` has the extension `extension`. +pub fn has_extension>(path: P, extension: &str) -> bool { + path.as_ref().extension().is_some_and(|ext| ext == extension) +} + +/// Returns true if the filename at `path` does not contain `expected`. +pub fn not_contains>(path: P, expected: &str) -> bool { + !path.as_ref().file_name().is_some_and(|name| name.to_str().unwrap().contains(expected)) +} + +/// Returns true if the filename at `path` is not in `expected`. +pub fn filename_not_in_denylist, V: AsRef<[String]>>(path: P, expected: V) -> bool { + let expected = expected.as_ref(); + path.as_ref() + .file_name() + .is_some_and(|name| !expected.contains(&name.to_str().unwrap().to_owned())) +} + +/// Returns true if the filename at `path` ends with `suffix`. +pub fn has_suffix>(path: P, suffix: &str) -> bool { + path.as_ref().file_name().is_some_and(|name| name.to_str().unwrap().ends_with(suffix)) +} + +/// Gathers all files in the current working directory that have the extension `ext`, and counts +/// the number of lines within that contain a match with the regex pattern `re`. +pub fn count_regex_matches_in_files_with_extension(re: ®ex::Regex, ext: &str) -> usize { + let fetched_files = shallow_find_files(cwd(), |path| has_extension(path, ext)); + + let mut count = 0; + for file in fetched_files { + let content = fs_wrapper::read_to_string(file); + count += content.lines().filter(|line| re.is_match(&line)).count(); + } + + count +} + +/// Read the contents of a file that cannot simply be read by +/// [`read_to_string`][crate::fs_wrapper::read_to_string], due to invalid UTF-8 data, then assert +/// that it contains `expected`. +#[track_caller] +pub fn invalid_utf8_contains, S: AsRef>(path: P, expected: S) { + let buffer = fs_wrapper::read(path.as_ref()); + let expected = expected.as_ref(); + if !String::from_utf8_lossy(&buffer).contains(expected) { + eprintln!("=== FILE CONTENTS (LOSSY) ==="); + eprintln!("{}", String::from_utf8_lossy(&buffer)); + eprintln!("=== SPECIFIED TEXT ==="); + eprintln!("{}", expected); + panic!("specified text was not found in file"); + } +} + +/// Read the contents of a file that cannot simply be read by +/// [`read_to_string`][crate::fs_wrapper::read_to_string], due to invalid UTF-8 data, then assert +/// that it does not contain `expected`. +#[track_caller] +pub fn invalid_utf8_not_contains, S: AsRef>(path: P, expected: S) { + let buffer = fs_wrapper::read(path.as_ref()); + let expected = expected.as_ref(); + if String::from_utf8_lossy(&buffer).contains(expected) { + eprintln!("=== FILE CONTENTS (LOSSY) ==="); + eprintln!("{}", String::from_utf8_lossy(&buffer)); + eprintln!("=== SPECIFIED TEXT ==="); + eprintln!("{}", expected); + panic!("specified text was unexpectedly found in file"); + } +} + +/// Assert that `actual` is equal to `expected`. +#[track_caller] +pub fn assert_equals, E: AsRef>(actual: A, expected: E) { + let actual = actual.as_ref(); + let expected = expected.as_ref(); + if actual != expected { + eprintln!("=== ACTUAL TEXT ==="); + eprintln!("{}", actual); + eprintln!("=== EXPECTED ==="); + eprintln!("{}", expected); + panic!("expected text was not found in actual text"); + } +} + +/// Assert that `haystack` contains `needle`. +#[track_caller] +pub fn assert_contains, N: AsRef>(haystack: H, needle: N) { + let haystack = haystack.as_ref(); + let needle = needle.as_ref(); + if !haystack.contains(needle) { + eprintln!("=== HAYSTACK ==="); + eprintln!("{}", haystack); + eprintln!("=== NEEDLE ==="); + eprintln!("{}", needle); + panic!("needle was not found in haystack"); + } +} + +/// Assert that `haystack` does not contain `needle`. +#[track_caller] +pub fn assert_not_contains, N: AsRef>(haystack: H, needle: N) { + let haystack = haystack.as_ref(); + let needle = needle.as_ref(); + if haystack.contains(needle) { + eprintln!("=== HAYSTACK ==="); + eprintln!("{}", haystack); + eprintln!("=== NEEDLE ==="); + eprintln!("{}", needle); + panic!("needle was unexpectedly found in haystack"); + } +} diff --git a/src/tools/run-make-support/src/lib.rs b/src/tools/run-make-support/src/lib.rs index 0f8fc6af0b3..60b77508289 100644 --- a/src/tools/run-make-support/src/lib.rs +++ b/src/tools/run-make-support/src/lib.rs @@ -8,6 +8,7 @@ pub mod ar; pub mod artifact_names; +pub mod assertion_helpers; pub mod diff; pub mod env_checked; pub mod external_deps; @@ -18,7 +19,6 @@ pub mod scoped_run; pub mod targets; -use std::panic; use std::path::{Path, PathBuf}; // Re-exports of third-party library crates. @@ -77,42 +77,15 @@ /// Helpers for scoped test execution where certain properties are attempted to be maintained. pub use scoped_run::{run_in_tmpdir, test_while_readonly}; +pub use assertion_helpers::{ + assert_contains, assert_equals, assert_not_contains, + count_regex_matches_in_files_with_extension, filename_not_in_denylist, has_extension, + has_prefix, has_suffix, invalid_utf8_contains, invalid_utf8_not_contains, not_contains, + shallow_find_files, +}; + use command::{Command, CompletedProcess}; -/// Browse the directory `path` non-recursively and return all files which respect the parameters -/// outlined by `closure`. -#[track_caller] -pub fn shallow_find_files, F: Fn(&PathBuf) -> bool>( - path: P, - filter: F, -) -> Vec { - let mut matching_files = Vec::new(); - for entry in fs_wrapper::read_dir(path) { - let entry = entry.expect("failed to read directory entry."); - let path = entry.path(); - - if path.is_file() && filter(&path) { - matching_files.push(path); - } - } - matching_files -} - -/// Returns true if the filename at `path` starts with `prefix`. -pub fn has_prefix>(path: P, prefix: &str) -> bool { - path.as_ref().file_name().is_some_and(|name| name.to_str().unwrap().starts_with(prefix)) -} - -/// Returns true if the filename at `path` has the extension `extension`. -pub fn has_extension>(path: P, extension: &str) -> bool { - path.as_ref().extension().is_some_and(|ext| ext == extension) -} - -/// Returns true if the filename at `path` does not contain `expected`. -pub fn not_contains>(path: P, expected: &str) -> bool { - !path.as_ref().file_name().is_some_and(|name| name.to_str().unwrap().contains(expected)) -} - /// Builds a static lib (`.lib` on Windows MSVC and `.a` for the rest) with the given name. #[track_caller] pub fn build_native_static_lib(lib_name: &str) -> PathBuf { @@ -133,33 +106,6 @@ pub fn build_native_static_lib(lib_name: &str) -> PathBuf { path(lib_path) } -/// Returns true if the filename at `path` is not in `expected`. -pub fn filename_not_in_denylist, V: AsRef<[String]>>(path: P, expected: V) -> bool { - let expected = expected.as_ref(); - path.as_ref() - .file_name() - .is_some_and(|name| !expected.contains(&name.to_str().unwrap().to_owned())) -} - -/// Returns true if the filename at `path` ends with `suffix`. -pub fn has_suffix>(path: P, suffix: &str) -> bool { - path.as_ref().file_name().is_some_and(|name| name.to_str().unwrap().ends_with(suffix)) -} - -/// Gathers all files in the current working directory that have the extension `ext`, and counts -/// the number of lines within that contain a match with the regex pattern `re`. -pub fn count_regex_matches_in_files_with_extension(re: ®ex::Regex, ext: &str) -> usize { - let fetched_files = shallow_find_files(cwd(), |path| has_extension(path, ext)); - - let mut count = 0; - for file in fetched_files { - let content = fs_wrapper::read_to_string(file); - count += content.lines().filter(|line| re.is_match(&line)).count(); - } - - count -} - pub(crate) fn handle_failed_output( cmd: &Command, output: CompletedProcess, @@ -191,36 +137,6 @@ pub fn set_host_rpath(cmd: &mut Command) { }); } -/// Read the contents of a file that cannot simply be read by -/// read_to_string, due to invalid utf8 data, then assert that it contains `expected`. -#[track_caller] -pub fn invalid_utf8_contains, S: AsRef>(path: P, expected: S) { - let buffer = fs_wrapper::read(path.as_ref()); - let expected = expected.as_ref(); - if !String::from_utf8_lossy(&buffer).contains(expected) { - eprintln!("=== FILE CONTENTS (LOSSY) ==="); - eprintln!("{}", String::from_utf8_lossy(&buffer)); - eprintln!("=== SPECIFIED TEXT ==="); - eprintln!("{}", expected); - panic!("specified text was not found in file"); - } -} - -/// Read the contents of a file that cannot simply be read by -/// read_to_string, due to invalid utf8 data, then assert that it does not contain `expected`. -#[track_caller] -pub fn invalid_utf8_not_contains, S: AsRef>(path: P, expected: S) { - let buffer = fs_wrapper::read(path.as_ref()); - let expected = expected.as_ref(); - if String::from_utf8_lossy(&buffer).contains(expected) { - eprintln!("=== FILE CONTENTS (LOSSY) ==="); - eprintln!("{}", String::from_utf8_lossy(&buffer)); - eprintln!("=== SPECIFIED TEXT ==="); - eprintln!("{}", expected); - panic!("specified text was unexpectedly found in file"); - } -} - /// Check that all files in `dir1` exist and have the same content in `dir2`. Panic otherwise. pub fn recursive_diff(dir1: impl AsRef, dir2: impl AsRef) { let dir2 = dir2.as_ref(); @@ -245,45 +161,3 @@ pub fn recursive_diff(dir1: impl AsRef, dir2: impl AsRef) { } }); } - -/// Check that `actual` is equal to `expected`. Panic otherwise. -#[track_caller] -pub fn assert_equals, S2: AsRef>(actual: S1, expected: S2) { - let actual = actual.as_ref(); - let expected = expected.as_ref(); - if actual != expected { - eprintln!("=== ACTUAL TEXT ==="); - eprintln!("{}", actual); - eprintln!("=== EXPECTED ==="); - eprintln!("{}", expected); - panic!("expected text was not found in actual text"); - } -} - -/// Check that `haystack` contains `needle`. Panic otherwise. -#[track_caller] -pub fn assert_contains, S2: AsRef>(haystack: S1, needle: S2) { - let haystack = haystack.as_ref(); - let needle = needle.as_ref(); - if !haystack.contains(needle) { - eprintln!("=== HAYSTACK ==="); - eprintln!("{}", haystack); - eprintln!("=== NEEDLE ==="); - eprintln!("{}", needle); - panic!("needle was not found in haystack"); - } -} - -/// Check that `haystack` does not contain `needle`. Panic otherwise. -#[track_caller] -pub fn assert_not_contains, S2: AsRef>(haystack: S1, needle: S2) { - let haystack = haystack.as_ref(); - let needle = needle.as_ref(); - if haystack.contains(needle) { - eprintln!("=== HAYSTACK ==="); - eprintln!("{}", haystack); - eprintln!("=== NEEDLE ==="); - eprintln!("{}", needle); - panic!("needle was unexpectedly found in haystack"); - } -}