diff options
Diffstat (limited to 'vendor/snapbox/src/cmd.rs')
-rw-r--r-- | vendor/snapbox/src/cmd.rs | 146 |
1 files changed, 143 insertions, 3 deletions
diff --git a/vendor/snapbox/src/cmd.rs b/vendor/snapbox/src/cmd.rs index 72de3563c..8529852f9 100644 --- a/vendor/snapbox/src/cmd.rs +++ b/vendor/snapbox/src/cmd.rs @@ -1,5 +1,8 @@ //! Run commands and assert on their behavior +#[cfg(feature = "color")] +use anstream::panic; + /// Process spawning for testing of non-interactive commands #[derive(Debug)] pub struct Command { @@ -923,10 +926,9 @@ pub fn display_exit_status(status: std::process::ExitStatus) -> String { #[cfg(windows)] fn detailed_exit_status(status: std::process::ExitStatus) -> Option<String> { - use winapi::shared::minwindef::DWORD; - use winapi::um::winnt::*; + use windows_sys::Win32::Foundation::*; - let extra = match status.code().unwrap() as DWORD { + let extra = match status.code().unwrap() as NTSTATUS { STATUS_ACCESS_VIOLATION => "STATUS_ACCESS_VIOLATION", STATUS_IN_PAGE_ERROR => "STATUS_IN_PAGE_ERROR", STATUS_INVALID_HANDLE => "STATUS_INVALID_HANDLE", @@ -1028,3 +1030,141 @@ fn target_dir() -> std::path::PathBuf { }) .unwrap() } + +#[cfg(feature = "examples")] +pub use examples::{compile_example, compile_examples}; + +#[cfg(feature = "examples")] +pub(crate) mod examples { + /// Prepare an example for testing + /// + /// Unlike `cargo_bin!`, this does not inherit all of the current compiler settings. It + /// will match the current target and profile but will not get feature flags. Pass those arguments + /// to the compiler via `args`. + /// + /// ## Example + /// + /// ```rust,no_run + /// snapbox::cmd::compile_example("snap-example-fixture", []); + /// ``` + #[cfg(feature = "examples")] + pub fn compile_example<'a>( + target_name: &str, + args: impl IntoIterator<Item = &'a str>, + ) -> Result<std::path::PathBuf, crate::Error> { + crate::debug!("Compiling example {}", target_name); + let messages = escargot::CargoBuild::new() + .current_target() + .current_release() + .example(target_name) + .args(args) + .exec() + .map_err(|e| crate::Error::new(e.to_string()))?; + for message in messages { + let message = message.map_err(|e| crate::Error::new(e.to_string()))?; + let message = message + .decode() + .map_err(|e| crate::Error::new(e.to_string()))?; + crate::debug!("Message: {:?}", message); + if let Some(bin) = decode_example_message(&message) { + let (name, bin) = bin?; + assert_eq!(target_name, name); + return bin; + } + } + + Err(crate::Error::new(format!( + "Unknown error building example {}", + target_name + ))) + } + + /// Prepare all examples for testing + /// + /// Unlike `cargo_bin!`, this does not inherit all of the current compiler settings. It + /// will match the current target and profile but will not get feature flags. Pass those arguments + /// to the compiler via `args`. + /// + /// ## Example + /// + /// ```rust,no_run + /// let examples = snapbox::cmd::compile_examples([]).unwrap().collect::<Vec<_>>(); + /// ``` + #[cfg(feature = "examples")] + pub fn compile_examples<'a>( + args: impl IntoIterator<Item = &'a str>, + ) -> Result< + impl Iterator<Item = (String, Result<std::path::PathBuf, crate::Error>)>, + crate::Error, + > { + crate::debug!("Compiling examples"); + let mut examples = std::collections::BTreeMap::new(); + + let messages = escargot::CargoBuild::new() + .current_target() + .current_release() + .examples() + .args(args) + .exec() + .map_err(|e| crate::Error::new(e.to_string()))?; + for message in messages { + let message = message.map_err(|e| crate::Error::new(e.to_string()))?; + let message = message + .decode() + .map_err(|e| crate::Error::new(e.to_string()))?; + crate::debug!("Message: {:?}", message); + if let Some(bin) = decode_example_message(&message) { + let (name, bin) = bin?; + examples.insert(name.to_owned(), bin); + } + } + + Ok(examples.into_iter()) + } + + #[allow(clippy::type_complexity)] + fn decode_example_message<'m>( + message: &'m escargot::format::Message, + ) -> Option<Result<(&'m str, Result<std::path::PathBuf, crate::Error>), crate::Error>> { + match message { + escargot::format::Message::CompilerMessage(msg) => { + let level = msg.message.level; + if level == escargot::format::diagnostic::DiagnosticLevel::Ice + || level == escargot::format::diagnostic::DiagnosticLevel::Error + { + let output = msg + .message + .rendered + .as_deref() + .unwrap_or_else(|| msg.message.message.as_ref()) + .to_owned(); + if is_example_target(&msg.target) { + let bin = Err(crate::Error::new(output)); + Some(Ok((msg.target.name.as_ref(), bin))) + } else { + Some(Err(crate::Error::new(output))) + } + } else { + None + } + } + escargot::format::Message::CompilerArtifact(artifact) => { + if !artifact.profile.test && is_example_target(&artifact.target) { + let path = artifact + .executable + .clone() + .expect("cargo is new enough for this to be present"); + let bin = Ok(path.into_owned()); + Some(Ok((artifact.target.name.as_ref(), bin))) + } else { + None + } + } + _ => None, + } + } + + fn is_example_target(target: &escargot::format::Target) -> bool { + target.crate_types == ["bin"] && target.kind == ["example"] + } +} |