Skip to content

Commit c15ef2c

Browse files
committed
fix(cmd): Panic on non-existent cargo_bin
Pulls in - assert-rs/assert_cmd#281 - assert-rs/assert_cmd#282 - assert-rs/assert_cmd#283
1 parent 8b196b9 commit c15ef2c

4 files changed

Lines changed: 83 additions & 17 deletions

File tree

crates/snapbox/src/cmd.rs

Lines changed: 72 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -849,29 +849,79 @@ fn wait(
849849
#[doc(inline)]
850850
pub use crate::cargo_bin;
851851

852-
/// Look up the path to a cargo-built binary within an integration test.
852+
/// Look up the path to a cargo-built binary within an integration test
853853
///
854-
/// **NOTE:** Prefer [`cargo_bin!`] as this makes assumptions about cargo
854+
/// Cargo support:
855+
/// - `>1.94`: works
856+
/// - `>=1.91,<=1.93`: works with default `build-dir`
857+
/// - `<=1.92`: works
858+
///
859+
/// # Panic
860+
///
861+
/// Panics if no binary is found
855862
pub fn cargo_bin(name: &str) -> std::path::PathBuf {
856-
let env_var = format!("CARGO_BIN_EXE_{name}");
863+
cargo_bin_opt(name).unwrap_or_else(|| missing_cargo_bin(name))
864+
}
865+
866+
/// Look up the path to a cargo-built binary within an integration test
867+
///
868+
/// Returns `None` if the binary doesn't exist
869+
///
870+
/// Cargo support:
871+
/// - `>1.94`: works
872+
/// - `>=1.91,<=1.93`: works with default `build-dir`
873+
/// - `<=1.92`: works
874+
pub fn cargo_bin_opt(name: &str) -> Option<std::path::PathBuf> {
875+
let env_var = format!("{CARGO_BIN_EXE_}{name}");
857876
std::env::var_os(env_var)
858877
.map(|p| p.into())
859-
.unwrap_or_else(|| target_dir().join(format!("{}{}", name, std::env::consts::EXE_SUFFIX)))
878+
.or_else(|| legacy_cargo_bin(name))
879+
}
880+
881+
const CARGO_BIN_EXE_: &str = "CARGO_BIN_EXE_";
882+
883+
fn missing_cargo_bin(name: &str) -> ! {
884+
let possible_names: Vec<_> = std::env::vars_os()
885+
.filter_map(|(k, _)| k.into_string().ok())
886+
.filter_map(|k| k.strip_prefix(CARGO_BIN_EXE_).map(|s| s.to_owned()))
887+
.collect();
888+
if possible_names.is_empty() {
889+
panic!("`CARGO_BIN_EXE_{name}` is unset
890+
help: if this is running within a unit test, move it to an integration test to gain access to `CARGO_BIN_EXE_{name}`")
891+
} else {
892+
let mut names = String::new();
893+
for (i, name) in possible_names.iter().enumerate() {
894+
use std::fmt::Write as _;
895+
if i != 0 {
896+
let _ = write!(&mut names, ", ");
897+
}
898+
let _ = write!(&mut names, "\"{name}\"");
899+
}
900+
panic!(
901+
"`CARGO_BIN_EXE_{name}` is unset
902+
help: available binary names are {names}"
903+
)
904+
}
905+
}
906+
907+
fn legacy_cargo_bin(name: &str) -> Option<std::path::PathBuf> {
908+
let target_dir = target_dir()?;
909+
let bin_path = target_dir.join(format!("{}{}", name, std::env::consts::EXE_SUFFIX));
910+
if !bin_path.exists() {
911+
return None;
912+
}
913+
Some(bin_path)
860914
}
861915

862916
// Adapted from
863917
// https://github.com/rust-lang/cargo/blob/485670b3983b52289a2f353d589c57fae2f60f82/tests/testsuite/support/mod.rs#L507
864-
fn target_dir() -> std::path::PathBuf {
865-
std::env::current_exe()
866-
.ok()
867-
.map(|mut path| {
868-
path.pop();
869-
if path.ends_with("deps") {
870-
path.pop();
871-
}
872-
path
873-
})
874-
.unwrap()
918+
fn target_dir() -> Option<std::path::PathBuf> {
919+
let mut path = std::env::current_exe().ok()?;
920+
let _test_bin_name = path.pop();
921+
if path.ends_with("deps") {
922+
let _deps = path.pop();
923+
}
924+
Some(path)
875925
}
876926

877927
#[cfg(feature = "examples")]
@@ -1009,3 +1059,10 @@ pub(crate) mod examples {
10091059
target.crate_types == ["bin"] && target.kind == ["example"]
10101060
}
10111061
}
1062+
1063+
#[test]
1064+
#[should_panic = "`CARGO_BIN_EXE_non-existent` is unset
1065+
help: if this is running within a unit test, move it to an integration test to gain access to `CARGO_BIN_EXE_non-existent`"]
1066+
fn cargo_bin_in_unit_test() {
1067+
cargo_bin("non-existent");
1068+
}

crates/snapbox/tests/testsuite/cmd.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,11 @@ fn large_stdout_single() {
3333
.assert()
3434
.success();
3535
}
36+
37+
#[test]
38+
#[cfg(feature = "cmd")]
39+
#[should_panic = "`CARGO_BIN_EXE_non-existent` is unset
40+
help: available binary names are \"snap-fixture\""]
41+
fn cargo_bin_non_existent() {
42+
let _ = snapbox::cmd::cargo_bin("non-existent");
43+
}

crates/trycmd/src/cargo.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
33
#[doc(inline)]
44
pub use snapbox::cmd::cargo_bin;
5+
#[doc(inline)]
6+
pub use snapbox::cmd::cargo_bin_opt;
57

68
/// Prepare an example for testing
79
///

crates/trycmd/src/registry.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,7 @@ impl BinRegistry {
4747
}
4848

4949
if self.fallback {
50-
let path = crate::cargo::cargo_bin(name);
51-
if path.exists() {
50+
if let Some(path) = crate::cargo::cargo_bin_opt(name) {
5251
return crate::schema::Bin::Path(path);
5352
}
5453
}

0 commit comments

Comments
 (0)