summaryrefslogtreecommitdiffstats
path: root/vendor/snapbox/src/cmd.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/snapbox/src/cmd.rs')
-rw-r--r--vendor/snapbox/src/cmd.rs146
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"]
+ }
+}