Repository: reubeno/brush
Pull request #403 (2025-03-11 05:30:10)

chore: rewrite dir diffing test code to minimize deps
Removes dependency on `dir-cmp` to avoid a transitive dependency on an unsupported crate getting flagged by `cargo deny`.


Patch:
diff --git a/Cargo.lock b/Cargo.lock
index 73b69ef..3508415 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -191,7 +191,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.99",
+ "syn 2.0.100",
 ]
 
 [[package]]
@@ -202,7 +202,7 @@ checksum = "d556ec1359574147ec0c4fc5eb525f3f23263a592b1a9c07e0a75b427de55c97"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.99",
+ "syn 2.0.100",
 ]
 
 [[package]]
@@ -353,7 +353,6 @@ dependencies = [
  "const_format",
  "descape",
  "diff",
- "dir-cmp",
  "expectrl",
  "git-version",
  "glob",
@@ -361,7 +360,6 @@ dependencies = [
  "indent",
  "junit-report",
  "lazy_static",
- "pathdiff",
  "regex",
  "serde",
  "serde_yaml",
@@ -370,6 +368,7 @@ dependencies = [
  "tracing",
  "tracing-subscriber",
  "version-compare",
+ "walkdir",
 ]
 
 [[package]]
@@ -395,12 +394,6 @@ version = "1.22.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b6b1fc10dbac614ebc03540c9dbd60e83887fda27794998c6528f1782047d540"
 
-[[package]]
-name = "byteorder"
-version = "1.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
-
 [[package]]
 name = "bytes"
 version = "1.10.1"
@@ -431,7 +424,7 @@ dependencies = [
  "darling",
  "proc-macro2",
  "quote",
- "syn 2.0.99",
+ "syn 2.0.100",
 ]
 
 [[package]]
@@ -513,9 +506,9 @@ dependencies = [
 
 [[package]]
 name = "clap"
-version = "4.5.31"
+version = "4.5.32"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "027bb0d98429ae334a8698531da7077bdf906419543a35a55c2cb1b66437d767"
+checksum = "6088f3ae8c3608d19260cd7445411865a485688711b78b5be70d78cd96136f83"
 dependencies = [
  "clap_builder",
  "clap_derive",
@@ -532,9 +525,9 @@ dependencies = [
 
 [[package]]
 name = "clap_builder"
-version = "4.5.31"
+version = "4.5.32"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5589e0cba072e0f3d23791efac0fd8627b49c829c196a492e88168e6a669d863"
+checksum = "22a7ef7f676155edfb82daa97f99441f3ebf4a58d5e32f295a56259f1b6facc8"
 dependencies = [
  "anstream",
  "anstyle",
@@ -545,14 +538,14 @@ dependencies = [
 
 [[package]]
 name = "clap_derive"
-version = "4.5.28"
+version = "4.5.32"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bf4ced95c6f4a675af3da73304b9ac4ed991640c36374e4b46795c49e17cf1ed"
+checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7"
 dependencies = [
  "heck",
  "proc-macro2",
  "quote",
- "syn 2.0.99",
+ "syn 2.0.100",
 ]
 
 [[package]]
@@ -766,7 +759,7 @@ dependencies = [
  "proc-macro2",
  "quote",
  "strsim",
- "syn 2.0.99",
+ "syn 2.0.100",
 ]
 
 [[package]]
@@ -777,7 +770,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806"
 dependencies = [
  "darling_core",
  "quote",
- "syn 2.0.99",
+ "syn 2.0.100",
 ]
 
 [[package]]
@@ -817,7 +810,7 @@ checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.99",
+ "syn 2.0.100",
 ]
 
 [[package]]
@@ -838,20 +831,6 @@ version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8"
 
-[[package]]
-name = "dir-cmp"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "76924c3bfb58021d733dc17d96d9c461a768555bbd68c82702f08cef5fdbae2d"
-dependencies = [
- "clap",
- "env_logger",
- "log",
- "regex",
- "serde",
- "serde_derive",
-]
-
 [[package]]
 name = "doc-comment"
 version = "0.3.3"
@@ -864,19 +843,6 @@ version = "1.15.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
 
-[[package]]
-name = "env_logger"
-version = "0.10.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580"
-dependencies = [
- "humantime",
- "is-terminal",
- "log",
- "regex",
- "termcolor",
-]
-
 [[package]]
 name = "equator"
 version = "0.4.2"
@@ -894,7 +860,7 @@ checksum = "44f23cf4b44bfce11a86ace86f8a73ffdec849c9fd00a386a53d278bd9e81fb3"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.99",
+ "syn 2.0.100",
 ]
 
 [[package]]
@@ -943,12 +909,12 @@ checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
 
 [[package]]
 name = "fd-lock"
-version = "4.0.3"
+version = "4.0.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c44818c96aec5cadc9dacfb97bbcbcfc19a0de75b218412d56f57fbaab94e439"
+checksum = "0ce92ff622d6dadf7349484f42c93271a0d49b7cc4d466a936405bacbe10aa78"
 dependencies = [
  "cfg-if",
- "rustix 0.38.44",
+ "rustix 1.0.2",
  "windows-sys 0.59.0",
 ]
 
@@ -1036,7 +1002,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.99",
+ "syn 2.0.100",
 ]
 
 [[package]]
@@ -1115,7 +1081,7 @@ checksum = "53010ccb100b96a67bc32c0175f0ed1426b31b655d562898e57325f81c023ac0"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.99",
+ "syn 2.0.100",
 ]
 
 [[package]]
@@ -1231,12 +1197,6 @@ dependencies = [
  "uuid",
 ]
 
-[[package]]
-name = "humantime"
-version = "2.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
-
 [[package]]
 name = "iana-time-zone"
 version = "0.1.61"
@@ -1296,9 +1256,9 @@ checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683"
 
 [[package]]
 name = "indexmap"
-version = "2.7.1"
+version = "2.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652"
+checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058"
 dependencies = [
  "equivalent",
  "hashbrown 0.15.2",
@@ -1589,9 +1549,9 @@ dependencies = [
 
 [[package]]
 name = "once_cell"
-version = "1.20.3"
+version = "1.21.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e"
+checksum = "cde51589ab56b20a6f686b2c68f7a0bd6add753d697abf720d63f8db3ab7b1ad"
 
 [[package]]
 name = "oorandom"
@@ -1649,12 +1609,6 @@ dependencies = [
  "windows-targets 0.52.6",
 ]
 
-[[package]]
-name = "pathdiff"
-version = "0.2.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3"
-
 [[package]]
 name = "peg"
 version = "0.8.5"
@@ -1753,11 +1707,11 @@ dependencies = [
 
 [[package]]
 name = "ppv-lite86"
-version = "0.2.20"
+version = "0.2.21"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
+checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
 dependencies = [
- "zerocopy 0.7.35",
+ "zerocopy 0.8.23",
 ]
 
 [[package]]
@@ -2011,9 +1965,9 @@ dependencies = [
 
 [[package]]
 name = "rustix"
-version = "1.0.1"
+version = "1.0.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dade4812df5c384711475be5fcd8c162555352945401aed22a35bffeab61f657"
+checksum = "f7178faa4b75a30e269c71e61c353ce2748cf3d76f0c44c393f4e60abf49b825"
 dependencies = [
  "bitflags 2.9.0",
  "errno",
@@ -2066,7 +2020,7 @@ checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.99",
+ "syn 2.0.100",
 ]
 
 [[package]]
@@ -2206,7 +2160,7 @@ dependencies = [
  "proc-macro2",
  "quote",
  "rustversion",
- "syn 2.0.99",
+ "syn 2.0.100",
 ]
 
 [[package]]
@@ -2245,9 +2199,9 @@ dependencies = [
 
 [[package]]
 name = "syn"
-version = "2.0.99"
+version = "2.0.100"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e02e925281e18ffd9d640e234264753c43edc62d64b2d4cf898f1bc5e75f3fc2"
+checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -2264,26 +2218,17 @@ dependencies = [
  "fastrand",
  "getrandom 0.3.1",
  "once_cell",
- "rustix 1.0.1",
+ "rustix 1.0.2",
  "windows-sys 0.59.0",
 ]
 
-[[package]]
-name = "termcolor"
-version = "1.4.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755"
-dependencies = [
- "winapi-util",
-]
-
 [[package]]
 name = "terminal_size"
-version = "0.4.1"
+version = "0.4.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5352447f921fda68cf61b4101566c0bdb5104eff6804d0678e5227580ab6a4e9"
+checksum = "45c6481c4829e4cc63825e62c49186a34538b7b2750b73b266581ffb612fb5ed"
 dependencies = [
- "rustix 0.38.44",
+ "rustix 1.0.2",
  "windows-sys 0.59.0",
 ]
 
@@ -2319,7 +2264,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.99",
+ "syn 2.0.100",
 ]
 
 [[package]]
@@ -2330,7 +2275,7 @@ checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.99",
+ "syn 2.0.100",
 ]
 
 [[package]]
@@ -2408,7 +2353,7 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.99",
+ "syn 2.0.100",
 ]
 
 [[package]]
@@ -2463,7 +2408,7 @@ checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.99",
+ "syn 2.0.100",
 ]
 
 [[package]]
@@ -2654,7 +2599,7 @@ dependencies = [
  "log",
  "proc-macro2",
  "quote",
- "syn 2.0.99",
+ "syn 2.0.100",
  "wasm-bindgen-shared",
 ]
 
@@ -2676,7 +2621,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.99",
+ "syn 2.0.100",
  "wasm-bindgen-backend",
  "wasm-bindgen-shared",
 ]
@@ -2816,7 +2761,7 @@ checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.99",
+ "syn 2.0.100",
 ]
 
 [[package]]
@@ -2827,7 +2772,7 @@ checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.99",
+ "syn 2.0.100",
 ]
 
 [[package]]
@@ -3016,7 +2961,6 @@ version = "0.7.35"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
 dependencies = [
- "byteorder",
  "zerocopy-derive 0.7.35",
 ]
 
@@ -3037,7 +2981,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.99",
+ "syn 2.0.100",
 ]
 
 [[package]]
@@ -3048,5 +2992,5 @@ checksum = "6352c01d0edd5db859a63e2605f4ea3183ddbd15e2c4a9e7d32184df75e4f154"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.99",
+ "syn 2.0.100",
 ]
diff --git a/brush-shell/Cargo.toml b/brush-shell/Cargo.toml
index 40520b8..725a290 100644
--- a/brush-shell/Cargo.toml
+++ b/brush-shell/Cargo.toml
@@ -71,14 +71,13 @@ assert_fs = "1.1.2"
 colored = "2.2.0"
 descape = "2.0.3"
 diff = "0.1.13"
-dir-cmp = "0.1.0"
 expectrl = { git = "https://github.com/zhiburt/expectrl", rev = "a0f4f7816b9a47a191dd858080e8fd80ff71cd96" }
 glob = "0.3.2"
 indent = "0.1.1"
 junit-report = "0.8.3"
-pathdiff = "0.2.3"
 regex = "1.11.1"
 serde = { version = "1.0.219", features = ["derive"] }
 serde_yaml = "0.9.34"
 strip-ansi-escapes = "0.2.0"
 version-compare = "0.2.0"
+walkdir = "2.5.0"
diff --git a/deny.toml b/deny.toml
index 3a85f6c..460403a 100644
--- a/deny.toml
+++ b/deny.toml
@@ -35,7 +35,15 @@ version = 2
 # List of explicitly allowed licenses
 # See https://spdx.org/licenses/ for list of possible licenses
 # [possible values: any SPDX 3.11 short identifier (+ optional exception)].
-allow = ["Apache-2.0", "BSD-2-Clause", "BSL-1.0", "MIT", "NCSA", "Unicode-3.0", "Zlib"]
+allow = [
+    "Apache-2.0",
+    "BSD-2-Clause",
+    "BSL-1.0",
+    "MIT",
+    "NCSA",
+    "Unicode-3.0",
+    "Zlib",
+]
 # The confidence threshold for detecting a license from license text.
 # The higher the value, the more closely the license text must be to the
 # canonical license text of a valid SPDX license file.
@@ -78,12 +86,14 @@ deny = []
 
 # Certain crates/versions that will be skipped when doing duplicate detection.
 skip = [
-    { crate = "nu-ansi-term" },
-    { crate = "itertools" },
-    { crate = "hashbrown" },
     { crate = "getrandom" },
-    { crate = "zerocopy" },
+    { crate = "hashbrown" },
+    { crate = "itertools" },
+    { crate = "linux-raw-sys" },
+    { crate = "nu-ansi-term" },
+    { crate = "rustix" },
     { crate = "wasi" },
+    { crate = "zerocopy" },
 ]
 skip-tree = [
     { crate = "thiserror", reason = "Multiple crates use different versions" },


Test patch:
diff --git a/brush-shell/tests/compat_tests.rs b/brush-shell/tests/compat_tests.rs
index 4521792..99b51ed 100644
--- a/brush-shell/tests/compat_tests.rs
+++ b/brush-shell/tests/compat_tests.rs
@@ -10,6 +10,7 @@ use clap::Parser;
 use colored::Colorize;
 use descape::UnescapeExt;
 use serde::{Deserialize, Serialize};
+use std::fs;
 use std::os::unix::{fs::PermissionsExt, process::ExitStatusExt};
 use std::{
     collections::{HashMap, HashSet},
@@ -1268,40 +1269,88 @@ impl DirComparison {
     }
 }
 
+fn get_dir_entries(dir_path: &Path) -> Result<HashMap<String, std::fs::FileType>> {
+    let mut entries = HashMap::new();
+    for entry in fs::read_dir(dir_path)? {
+        let entry = entry?;
+        let file_type = entry.file_type()?;
+        let filename = entry.file_name().to_string_lossy().to_string();
+
+        // N.B. We ignore raw coverage profile data files.
+        if filename.ends_with(".profraw") {
+            continue;
+        }
+
+        entries.insert(filename, file_type);
+    }
+
+    Ok(entries)
+}
+
 fn diff_dirs(oracle_path: &Path, test_path: &Path) -> Result<DirComparison> {
-    let profraw_regex = regex::Regex::new(r"\.profraw$")?;
-    let filter = dir_cmp::Filter::Exclude(vec![profraw_regex]);
-
-    let options = dir_cmp::Options {
-        ignore_equal: true,
-        ignore_left_only: false,
-        ignore_right_only: false,
-        filter: Some(filter),
-        recursive: true,
-    };
+    let mut entries = vec![];
+
+    let oracle_entries = get_dir_entries(oracle_path)?;
+    let test_entries = get_dir_entries(test_path)?;
+
+    // Look through all the files in the oracle directory
+    for (filename, file_type) in &oracle_entries {
+        if !test_entries.contains_key(filename) {
+            for left_only_file in walkdir::WalkDir::new(oracle_path.join(filename)) {
+                let entry = left_only_file?;
+                let left_only_path = entry.path();
+                entries.push(DirComparisonEntry::LeftOnly(left_only_path.to_owned()));
+            }
+
+            continue;
+        }
+
+        let oracle_file_path = oracle_path.join(filename);
+        let test_file_path = test_path.join(filename);
+
+        if file_type.is_file() {
+            let oracle_contents = fs::read_to_string(&oracle_file_path)?;
+            let test_contents = fs::read_to_string(&test_file_path)?;
 
-    let result: Vec<_> = dir_cmp::full::compare_dirs(oracle_path, test_path, options)?
-        .iter()
-        .map(|entry| match entry {
-            dir_cmp::full::DirCmpEntry::Left(p) => {
-                DirComparisonEntry::LeftOnly(pathdiff::diff_paths(p, oracle_path).unwrap())
+            if oracle_contents != test_contents {
+                entries.push(DirComparisonEntry::Different(
+                    oracle_file_path,
+                    oracle_contents,
+                    test_file_path,
+                    test_contents,
+                ));
             }
-            dir_cmp::full::DirCmpEntry::Right(p) => {
-                DirComparisonEntry::RightOnly(pathdiff::diff_paths(p, test_path).unwrap())
+        } else if file_type.is_dir() {
+            let subdir_comparison =
+                diff_dirs(oracle_file_path.as_path(), test_file_path.as_path())?;
+            if let DirComparison::TestDiffers(subdir_entries) = subdir_comparison {
+                entries.extend(subdir_entries);
             }
-            dir_cmp::full::DirCmpEntry::Both(l, r, _) => DirComparisonEntry::Different(
-                pathdiff::diff_paths(l, oracle_path).unwrap(),
-                std::fs::read_to_string(l).unwrap(),
-                pathdiff::diff_paths(r, test_path).unwrap(),
-                std::fs::read_to_string(r).unwrap(),
-            ),
-        })
-        .collect();
+        } else {
+            // Ignore other file types (e.g., symlinks).
+        }
+    }
+
+    for (filename, file_type) in &test_entries {
+        if oracle_entries.contains_key(filename) {
+            continue;
+        }
+
+        if file_type.is_dir() {
+            for right_only_file in walkdir::WalkDir::new(test_path.join(filename)) {
+                let entry = right_only_file?;
+                let right_only_path = entry.path();
+                entries.push(DirComparisonEntry::RightOnly(right_only_path.to_owned()));
+            }
+        } else {
+            entries.push(DirComparisonEntry::RightOnly(test_path.join(filename)));
+        }
+    }
 
-    if result.is_empty() {
+    if entries.is_empty() {
         Ok(DirComparison::Same)
     } else {
-        Ok(DirComparison::TestDiffers(result))
+        Ok(DirComparison::TestDiffers(entries))
     }
 }