Skip to content

Commit

Permalink
Integrate with rstest.
Browse files Browse the repository at this point in the history
This change refactors tests with fixtures and cases using `rstest`. This
reports a more complete list of failures (each case is attempted
regardless of the status of other cases) and makes adding cases easier.
The names of tests now generally describe an action, a subject, a
qualifier, and finally an expectation, such as
`new_glob_with_literal_is_ok` and
`walk_path_with_not_excludes_only_matching_paths`. Some of these
components are absent when described by or dependent upon cases.

Assertions and testing functions are exported in `harness` modules,
which can be composed in some tests.

Notably, a bug regarding depth variance is also fixed by this change.
Previously, the depth of patterns using tree wildcards did not consider
adjacent components (because such components do not have accompanying
separators). For example, `a/**/b/**/c` had unbounded depth, but now has
depth in the range [3,] (three or more).
  • Loading branch information
olson-sean-k committed Feb 29, 2024
1 parent 8dd36ec commit 93c5926
Show file tree
Hide file tree
Showing 10 changed files with 1,889 additions and 1,322 deletions.
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -64,4 +64,6 @@ optional = true
[dev-dependencies]
build-fs-tree = "^0.6.0"
dunce = "^1.0.0"
expect_macro = "^0.2.0"
rstest = "^0.18.0"
tempfile = "^3.8.0"
78 changes: 58 additions & 20 deletions src/diagnostics/miette.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,32 +139,70 @@ where
}

#[cfg(test)]
mod tests {
pub mod harness {
use expect_macro::expect;
use tardar::{BoxedDiagnostic, DiagnosticResult, DiagnosticResultExt as _};

use crate::Glob;

// It is non-trivial to downcast `&dyn Diagnostic`, so diagnostics are identified in tests by
// their code.
const CODE_SEMANTIC_LITERAL: &str = "wax::glob::semantic_literal";
const CODE_TERMINATING_SEPARATOR: &str = "wax::glob::terminating_separator";
// code.
pub const CODE_SEMANTIC_LITERAL: &str = "wax::glob::semantic_literal";
pub const CODE_TERMINATING_SEPARATOR: &str = "wax::glob::terminating_separator";

#[cfg(any(unix, windows))]
#[test]
fn diagnose_glob_semantic_literal_warning() {
let glob = Glob::new("../foo").unwrap();
let diagnostics: Vec<_> = glob.diagnose().collect();

assert!(diagnostics.iter().any(|diagnostic| diagnostic
.code()
.map_or(false, |code| code.to_string() == CODE_SEMANTIC_LITERAL)));
pub fn assert_diagnosed_glob_is_ok(expression: &str) -> (Glob<'_>, Vec<BoxedDiagnostic<'_>>) {
expect!(
Glob::diagnosed(expression),
"`Glob::diagnosed` is `Err`, but expected `Ok`: expression: `{}`",
expression,
)
}

#[test]
fn diagnose_glob_terminating_separator_warning() {
let glob = Glob::new("**/foo/").unwrap();
let diagnostics: Vec<_> = glob.diagnose().collect();
// TODO: This function does not compose well: the glob expression may not be available, so the
// message is less clear.
pub fn assert_diagnosed_glob_has_code<'t>(
result: DiagnosticResult<'t, Glob<'t>>,
expected: &str,
) -> DiagnosticResult<'t, Glob<'t>> {
let diagnostics: Vec<_> = result.diagnostics().iter().collect();
assert!(
diagnostics.iter().any(|diagnostic| diagnostic
.code()
.map_or(false, |code| code.to_string() == expected)),
"expected diagnostic code `{}`, but not found: diagnostics: `{:?}`",
expected,
diagnostics,
);
result
}
}

#[cfg(test)]
mod tests {
use rstest::rstest;

use crate::diagnostics::miette::harness;

#[cfg(any(unix, windows))]
#[rstest]
#[case("../a")]
#[case("a/..")]
#[case("a/../b")]
#[case("{a/,../,b/}")]
fn diagnosed_glob_has_semantic_literal_warning(#[case] expression: &str) {
let _ = harness::assert_diagnosed_glob_has_code(
Ok(harness::assert_diagnosed_glob_is_ok(expression)),
harness::CODE_SEMANTIC_LITERAL,
);
}

assert!(diagnostics.iter().any(|diagnostic| diagnostic
.code()
.map_or(false, |code| code.to_string() == CODE_TERMINATING_SEPARATOR)));
#[rstest]
#[case("a/b/c/")]
#[case("**/a/")]
fn diagnosed_glob_has_terminating_separator_warning(#[case] expression: &str) {
let _ = harness::assert_diagnosed_glob_has_code(
Ok(harness::assert_diagnosed_glob_is_ok(expression)),
harness::CODE_TERMINATING_SEPARATOR,
);
}
}
56 changes: 48 additions & 8 deletions src/encode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -321,16 +321,56 @@ fn encode<'t, T>(
}

#[cfg(test)]
mod tests {
pub mod harness {
use crate::encode;

#[test]
fn case_folded_eq() {
assert!(encode::case_folded_eq("a", "a"));
assert!(encode::case_folded_eq("a", "A"));
pub fn assert_case_folded_eq_eq(lhs: &str, rhs: &str, expected: bool) -> bool {
let eq = encode::case_folded_eq(lhs, rhs);
assert!(
eq == expected,
"`encode::case_folded_eq` is `{}`, but expected `{}`",
eq,
expected,
);
eq
}
}

#[cfg(test)]
mod tests {
use rstest::rstest;

use crate::encode::harness;

#[rstest]
#[case::ascii_with_no_casing("", "")]
#[case::ascii_with_no_casing("0", "0")]
#[case::ascii_with_no_casing("_", "_")]
#[case::ascii_with_casing("a", "a")]
#[case::ascii_with_casing("a", "A")]
#[case::ascii_with_casing("aa", "aa")]
#[case::ascii_with_casing("aa", "aA")]
#[case::ascii_with_casing("aa", "Aa")]
#[case::ascii_with_casing("aa", "AA")]
#[case::ascii_with_casing("z", "z")]
#[case::ascii_with_casing("z", "Z")]
#[case::cjk("愛", "愛")]
#[case::cjk("グロブ", "グロブ")]
fn case_folded_eq_eq(#[case] lhs: &str, #[case] rhs: &str) {
harness::assert_case_folded_eq_eq(lhs, rhs, true);
}

assert!(!encode::case_folded_eq("a", "b"));
assert!(!encode::case_folded_eq("aa", "a"));
assert!(!encode::case_folded_eq("a", "aa"));
#[rstest]
#[case::whitespace("", " ")]
#[case::whitespace(" ", "\t")]
#[case::whitespace(" ", " ")]
#[case::length("a", "aa")]
#[case::ascii_with_no_casing("0", "1")]
#[case::ascii_with_casing("a", "b")]
#[case::ascii_with_casing("a", "B")]
#[case::cjk("金", "銀")]
#[case::cjk("もー", "もぉ")]
fn case_folded_eq_not_eq(#[case] lhs: &str, #[case] rhs: &str) {
harness::assert_case_folded_eq_eq(lhs, rhs, false);
}
}
Loading

0 comments on commit 93c5926

Please sign in to comment.