From bb41b9ce923724d77c2c0a200b4355c6cdfa5451 Mon Sep 17 00:00:00 2001 From: John Ky Date: Fri, 31 Jul 2026 13:17:06 +1000 Subject: [PATCH] fix(test): locate the CLI binary by target-dir depth, not a fixed deps/ pop Nightly Cargo defaulted to build-dir-layout-v2 (rust-lang/cargo#17258), nesting test binaries under target//build///out/ instead of the flat target//deps/. The succinctly_bin() helper in dsv_cli_tests.rs, text_cli_tests.rs, json_validate_tests.rs, and yaml_validate_tests.rs assumed the old layout and computed a bogus path, failing all 4 suites on the nightly CI leg. Derive target// by finding the `target` path component instead, which holds under both layouts. --- tests/dsv_cli_tests.rs | 24 +++++++++++++++++++----- tests/json_validate_tests.rs | 28 +++++++++++++++++++++------- tests/text_cli_tests.rs | 24 +++++++++++++++++++----- tests/yaml_validate_tests.rs | 24 +++++++++++++++++++----- 4 files changed, 78 insertions(+), 22 deletions(-) diff --git a/tests/dsv_cli_tests.rs b/tests/dsv_cli_tests.rs index c9c6ab65c..9f702e2ca 100644 --- a/tests/dsv_cli_tests.rs +++ b/tests/dsv_cli_tests.rs @@ -36,11 +36,7 @@ fn succinctly_bin() -> &'static Path { String::from_utf8_lossy(&output.stderr) ); - let mut path = std::env::current_exe().expect("resolve current_exe"); - path.pop(); // drop the test executable's file name -> `.../deps` - if path.file_name().and_then(|s| s.to_str()) == Some("deps") { - path.pop(); // drop `deps` -> `.../` - } + let mut path = target_profile_dir_from_test_exe(); path.push(format!("succinctly{}", std::env::consts::EXE_SUFFIX)); assert!( path.is_file(), @@ -51,6 +47,24 @@ fn succinctly_bin() -> &'static Path { }) } +/// Derive `//` from this test executable's own path. +/// +/// The classic flat layout places the test exe at +/// `//deps/-`, but nightly's build-dir-layout-v2 +/// (rust-lang/cargo#17258, defaulted on nightly toolchains from ~2026-07) +/// instead nests it at `//build///out/-`. +/// Both layouts keep `` as the path component immediately after +/// `target`, so locate it that way instead of assuming a fixed depth. +fn target_profile_dir_from_test_exe() -> PathBuf { + let current_exe = std::env::current_exe().expect("resolve current_exe"); + let components: Vec<_> = current_exe.components().collect(); + let target_idx = components + .iter() + .rposition(|c| c.as_os_str() == "target") + .expect("test executable path has no `target` component"); + components[..=target_idx + 1].iter().collect() +} + /// Run `dsv generate` and capture stdout, stderr, and exit code. fn run_generate(size: &str, extra_args: &[&str]) -> Result<(String, String, i32)> { let output = Command::new(succinctly_bin()) diff --git a/tests/json_validate_tests.rs b/tests/json_validate_tests.rs index 6aabab04d..c58dcd474 100644 --- a/tests/json_validate_tests.rs +++ b/tests/json_validate_tests.rs @@ -38,13 +38,9 @@ fn succinctly_bin() -> &'static Path { String::from_utf8_lossy(&output.stderr) ); - // The test executable lives at `//deps/-`; - // the CLI binary is its sibling at `//succinctly`. - let mut path = std::env::current_exe().expect("resolve current_exe"); - path.pop(); // drop the test executable's file name -> `.../deps` - if path.file_name().and_then(|s| s.to_str()) == Some("deps") { - path.pop(); // drop `deps` -> `.../` - } + // The CLI binary is `//succinctly`, a sibling of the + // test executable's own `//` ancestor. + let mut path = target_profile_dir_from_test_exe(); path.push(format!("succinctly{}", std::env::consts::EXE_SUFFIX)); assert!( path.is_file(), @@ -55,6 +51,24 @@ fn succinctly_bin() -> &'static Path { }) } +/// Derive `//` from this test executable's own path. +/// +/// The classic flat layout places the test exe at +/// `//deps/-`, but nightly's build-dir-layout-v2 +/// (rust-lang/cargo#17258, defaulted on nightly toolchains from ~2026-07) +/// instead nests it at `//build///out/-`. +/// Both layouts keep `` as the path component immediately after +/// `target`, so locate it that way instead of assuming a fixed depth. +fn target_profile_dir_from_test_exe() -> PathBuf { + let current_exe = std::env::current_exe().expect("resolve current_exe"); + let components: Vec<_> = current_exe.components().collect(); + let target_idx = components + .iter() + .rposition(|c| c.as_os_str() == "target") + .expect("test executable path has no `target` component"); + components[..=target_idx + 1].iter().collect() +} + /// Helper to run `json validate` with input from stdin. fn run_validate_stdin(input: &str, extra_args: &[&str]) -> Result<(String, String, i32)> { let mut cmd = Command::new(succinctly_bin()) diff --git a/tests/text_cli_tests.rs b/tests/text_cli_tests.rs index 20bd55272..7c3da359e 100644 --- a/tests/text_cli_tests.rs +++ b/tests/text_cli_tests.rs @@ -38,11 +38,7 @@ fn succinctly_bin() -> &'static Path { String::from_utf8_lossy(&output.stderr) ); - let mut path = std::env::current_exe().expect("resolve current_exe"); - path.pop(); // drop the test executable's file name -> `.../deps` - if path.file_name().and_then(|s| s.to_str()) == Some("deps") { - path.pop(); // drop `deps` -> `.../` - } + let mut path = target_profile_dir_from_test_exe(); path.push(format!("succinctly{}", std::env::consts::EXE_SUFFIX)); assert!( path.is_file(), @@ -53,6 +49,24 @@ fn succinctly_bin() -> &'static Path { }) } +/// Derive `//` from this test executable's own path. +/// +/// The classic flat layout places the test exe at +/// `//deps/-`, but nightly's build-dir-layout-v2 +/// (rust-lang/cargo#17258, defaulted on nightly toolchains from ~2026-07) +/// instead nests it at `//build///out/-`. +/// Both layouts keep `` as the path component immediately after +/// `target`, so locate it that way instead of assuming a fixed depth. +fn target_profile_dir_from_test_exe() -> PathBuf { + let current_exe = std::env::current_exe().expect("resolve current_exe"); + let components: Vec<_> = current_exe.components().collect(); + let target_idx = components + .iter() + .rposition(|c| c.as_os_str() == "target") + .expect("test executable path has no `target` component"); + components[..=target_idx + 1].iter().collect() +} + /// Run `text validate utf8` with raw bytes piped on stdin. fn run_validate_stdin(input: &[u8], extra_args: &[&str]) -> Result<(Vec, String, i32)> { let mut cmd = Command::new(succinctly_bin()) diff --git a/tests/yaml_validate_tests.rs b/tests/yaml_validate_tests.rs index 52d79c1d4..7cf52f6bb 100644 --- a/tests/yaml_validate_tests.rs +++ b/tests/yaml_validate_tests.rs @@ -28,11 +28,7 @@ fn succinctly_bin() -> &'static Path { String::from_utf8_lossy(&output.stderr) ); - let mut path = std::env::current_exe().expect("resolve current_exe"); - path.pop(); - if path.file_name().and_then(|s| s.to_str()) == Some("deps") { - path.pop(); - } + let mut path = target_profile_dir_from_test_exe(); path.push(format!("succinctly{}", std::env::consts::EXE_SUFFIX)); assert!( path.is_file(), @@ -43,6 +39,24 @@ fn succinctly_bin() -> &'static Path { }) } +/// Derive `//` from this test executable's own path. +/// +/// The classic flat layout places the test exe at +/// `//deps/-`, but nightly's build-dir-layout-v2 +/// (rust-lang/cargo#17258, defaulted on nightly toolchains from ~2026-07) +/// instead nests it at `//build///out/-`. +/// Both layouts keep `` as the path component immediately after +/// `target`, so locate it that way instead of assuming a fixed depth. +fn target_profile_dir_from_test_exe() -> PathBuf { + let current_exe = std::env::current_exe().expect("resolve current_exe"); + let components: Vec<_> = current_exe.components().collect(); + let target_idx = components + .iter() + .rposition(|c| c.as_os_str() == "target") + .expect("test executable path has no `target` component"); + components[..=target_idx + 1].iter().collect() +} + fn run_validate_stdin(input: &str, extra_args: &[&str]) -> Result<(String, String, i32)> { let mut cmd = Command::new(succinctly_bin()) .args(["yaml", "validate"])