diff options
Diffstat (limited to 'vendor/escargot/src')
-rw-r--r-- | vendor/escargot/src/bin/bin_fixture.rs | 33 | ||||
-rw-r--r-- | vendor/escargot/src/build.rs | 340 | ||||
-rw-r--r-- | vendor/escargot/src/cargo.rs | 60 | ||||
-rw-r--r-- | vendor/escargot/src/error.rs | 91 | ||||
-rw-r--r-- | vendor/escargot/src/format/diagnostic.rs | 158 | ||||
-rw-r--r-- | vendor/escargot/src/format/mod.rs | 266 | ||||
-rw-r--r-- | vendor/escargot/src/format/test.rs | 242 | ||||
-rw-r--r-- | vendor/escargot/src/lib.rs | 59 | ||||
-rw-r--r-- | vendor/escargot/src/msg.rs | 117 | ||||
-rw-r--r-- | vendor/escargot/src/run.rs | 195 | ||||
-rw-r--r-- | vendor/escargot/src/test.rs | 201 |
11 files changed, 1762 insertions, 0 deletions
diff --git a/vendor/escargot/src/bin/bin_fixture.rs b/vendor/escargot/src/bin/bin_fixture.rs new file mode 100644 index 000000000..f47540f85 --- /dev/null +++ b/vendor/escargot/src/bin/bin_fixture.rs @@ -0,0 +1,33 @@ +use std::env; +use std::error::Error; +use std::io; +use std::io::Write; +use std::process; + +fn run() -> Result<(), Box<dyn Error>> { + if let Ok(text) = env::var("stdout") { + println!("{}", text); + } + if let Ok(text) = env::var("stderr") { + eprintln!("{}", text); + } + + let code = env::var("exit") + .ok() + .map(|v| v.parse::<i32>()) + .map(|r| r.map(Some)) + .unwrap_or(Ok(None))? + .unwrap_or(0); + process::exit(code); +} + +fn main() { + let code = match run() { + Ok(_) => 0, + Err(ref e) => { + write!(&mut io::stderr(), "{}", e).expect("writing to stderr won't fail"); + 1 + } + }; + process::exit(code); +} diff --git a/vendor/escargot/src/build.rs b/vendor/escargot/src/build.rs new file mode 100644 index 000000000..f37c1fb9e --- /dev/null +++ b/vendor/escargot/src/build.rs @@ -0,0 +1,340 @@ +use std::ffi::{self, OsStr}; +use std::process; + +use crate::cargo::Cargo; +use crate::cargo::CURRENT_TARGET; +use crate::error::*; +use crate::msg::*; +use crate::run::CargoRun; +#[cfg(feature = "test_unstable")] +use crate::test::CargoTest; + +/// The `build` subcommand. +/// +/// # Example +/// +/// ```rust +/// extern crate escargot; +/// extern crate assert_fs; +/// +/// let temp = assert_fs::TempDir::new().unwrap(); +/// escargot::CargoBuild::new() +/// .bin("bin") +/// .current_release() +/// .current_target() +/// .manifest_path("tests/fixtures/bin/Cargo.toml") +/// .target_dir(temp.path()) +/// .exec() +/// .unwrap(); +/// ``` +pub struct CargoBuild { + cmd: process::Command, + bin: bool, + example: bool, +} + +impl CargoBuild { + /// Shortcut to create a `build` subcommand. + /// + /// See also [`Cargo`]. + /// + /// # Example + /// + /// ```rust + /// extern crate escargot; + /// extern crate assert_fs; + /// + /// let temp = assert_fs::TempDir::new().unwrap(); + /// escargot::CargoBuild::new() + /// .bin("bin") + /// .manifest_path("tests/fixtures/bin/Cargo.toml") + /// .target_dir(temp.path()) + /// .exec() + /// .unwrap(); + /// ``` + /// + pub fn new() -> Self { + Cargo::new().build() + } + + pub(crate) fn with_command(cmd: process::Command) -> Self { + Self { + cmd, + bin: false, + example: false, + } + } + + /// Build from `name` package in workspaces. + /// + /// # Example + /// + /// ```rust + /// extern crate escargot; + /// extern crate assert_fs; + /// + /// let temp = assert_fs::TempDir::new().unwrap(); + /// escargot::CargoBuild::new() + /// .package("bin") + /// .bin("bin") + /// .manifest_path("tests/fixtures/bin/Cargo.toml") + /// .target_dir(temp.path()) + /// .exec() + /// .unwrap(); + /// ``` + pub fn package<S: AsRef<ffi::OsStr>>(self, name: S) -> Self { + self.arg("--package").arg(name) + } + /// Build only `name` binary. + /// + /// # Example + /// + /// ```rust + /// extern crate escargot; + /// extern crate assert_fs; + /// + /// let temp = assert_fs::TempDir::new().unwrap(); + /// escargot::CargoBuild::new() + /// .bin("bin") + /// .manifest_path("tests/fixtures/bin/Cargo.toml") + /// .target_dir(temp.path()) + /// .exec() + /// .unwrap(); + /// ``` + pub fn bin<S: AsRef<ffi::OsStr>>(mut self, name: S) -> Self { + self.bin = true; + self.arg("--bin").arg(name) + } + + /// Build all examples + /// + /// # Example + /// + /// ```rust + /// extern crate escargot; + /// extern crate assert_fs; + /// + /// let temp = assert_fs::TempDir::new().unwrap(); + /// escargot::CargoBuild::new() + /// .examples() + /// .manifest_path("tests/fixtures/example/Cargo.toml") + /// .target_dir(temp.path()) + /// .exec() + /// .unwrap(); + /// ``` + pub fn examples(mut self) -> Self { + self.example = true; + self.arg("--examples") + } + + /// Build only `name` example. + /// + /// # Example + /// + /// ```rust + /// extern crate escargot; + /// extern crate assert_fs; + /// + /// let temp = assert_fs::TempDir::new().unwrap(); + /// escargot::CargoBuild::new() + /// .example("example_fixture") + /// .manifest_path("tests/fixtures/example/Cargo.toml") + /// .target_dir(temp.path()) + /// .exec() + /// .unwrap(); + /// ``` + pub fn example<S: AsRef<ffi::OsStr>>(mut self, name: S) -> Self { + self.example = true; + self.arg("--example").arg(name) + } + + /// Build all tests + /// + /// # Example + /// + /// ```rust + /// extern crate escargot; + /// extern crate assert_fs; + /// + /// let temp = assert_fs::TempDir::new().unwrap(); + /// escargot::CargoBuild::new() + /// .tests() + /// .manifest_path("tests/fixtures/test/Cargo.toml") + /// .target_dir(temp.path()) + /// .exec() + /// .unwrap(); + /// ``` + pub fn tests(self) -> Self { + self.arg("--tests") + } + + /// Build only `name` test. + /// + /// # Example + /// + /// ```rust + /// extern crate escargot; + /// extern crate assert_fs; + /// + /// let temp = assert_fs::TempDir::new().unwrap(); + /// escargot::CargoBuild::new() + /// .test("test") + /// .manifest_path("tests/fixtures/test/Cargo.toml") + /// .target_dir(temp.path()) + /// .exec() + /// .unwrap(); + /// ``` + pub fn test<S: AsRef<ffi::OsStr>>(self, name: S) -> Self { + self.arg("--test").arg(name) + } + + /// Path to Cargo.toml + pub fn manifest_path<S: AsRef<ffi::OsStr>>(self, path: S) -> Self { + self.arg("--manifest-path").arg(path) + } + + /// Build artifacts in release mode, with optimizations. + pub fn release(self) -> Self { + self.arg("--release") + } + + /// Inserts or updates an environment variable mapping. + pub fn env<K, V>(mut self, key: K, val: V) -> Self + where + K: AsRef<OsStr>, + V: AsRef<OsStr>, + { + self.cmd.env(key, val); + + self + } + + /// Removes an environment variable + pub fn env_remove<K>(mut self, key: K) -> Self + where + K: AsRef<OsStr>, + { + self.cmd.env_remove(key); + self + } + + /// Build artifacts in release mode if the current process has, with optimizations. + #[cfg(debug_assertions)] + pub fn current_release(self) -> Self { + self + } + + /// Build artifacts in release mode if the current process has, with optimizations. + #[cfg(not(debug_assertions))] + pub fn current_release(self) -> Self { + self.release() + } + + /// Build for the target triplet. + pub fn target<S: AsRef<ffi::OsStr>>(self, triplet: S) -> Self { + self.arg("--target").arg(triplet) + } + + /// Build for the current process' triplet. + pub fn current_target(self) -> Self { + self.target(CURRENT_TARGET) + } + + /// Directory for all generated artifacts + pub fn target_dir<S: AsRef<ffi::OsStr>>(self, dir: S) -> Self { + self.arg("--target-dir").arg(dir) + } + + /// Activate all available features + pub fn all_features(self) -> Self { + self.arg("--all-features") + } + + /// Do not activate the `default` feature + pub fn no_default_features(self) -> Self { + self.arg("--no-default-features") + } + + /// Space-separated list of features to activate + pub fn features<S: AsRef<ffi::OsStr>>(self, features: S) -> Self { + self.arg("--features").arg(features) + } + + /// Manually pass an argument that is unsupported. + /// + /// Caution: Passing in `--` can throw off the API. + pub fn arg<S: AsRef<ffi::OsStr>>(mut self, arg: S) -> Self { + self.cmd.arg(arg); + self + } + + /// Manually pass arguments that are unsupported. + /// + /// Caution: Passing in `--` can throw off the API. + pub fn args<I: IntoIterator<Item = S>, S: AsRef<ffi::OsStr>>(mut self, args: I) -> Self { + self.cmd.args(args); + self + } + + /// Build the configured target, returning compiler messages. + pub fn exec(self) -> CargoResult<CommandMessages> { + CommandMessages::with_command(self.cmd) + } + + /// Provide a proxy for running the built target. + /// + /// # Example + /// + /// ```rust + /// extern crate escargot; + /// extern crate assert_fs; + /// + /// let temp = assert_fs::TempDir::new().unwrap(); + /// let run = escargot::CargoBuild::new() + /// .bin("bin") + /// .current_release() + /// .current_target() + /// .manifest_path("tests/fixtures/bin/Cargo.toml") + /// .target_dir(temp.path()) + /// .run() + /// .unwrap(); + /// println!("artifact={}", run.path().display()); + /// ``` + pub fn run(self) -> CargoResult<CargoRun> { + let msgs = CommandMessages::with_command(self.cmd)?; + CargoRun::from_message(msgs, self.bin, self.example) + } + + /// Provide a proxy for running the built target. + /// + /// Required feature: `test_unstable` since the format parsed is unstable. + /// + /// # Example + /// + /// ```rust + /// extern crate escargot; + /// extern crate assert_fs; + /// + /// let temp = assert_fs::TempDir::new().unwrap(); + /// let run = escargot::CargoBuild::new() + /// .test("test") + /// .current_release() + /// .current_target() + /// .manifest_path("tests/fixtures/test/Cargo.toml") + /// .target_dir(temp.path()) + /// .run_tests().unwrap() + /// .next().unwrap().unwrap(); + /// println!("artifact={}", run.path().display()); + /// ``` + #[cfg(feature = "test_unstable")] + pub fn run_tests(self) -> CargoResult<impl Iterator<Item = Result<CargoTest, CargoError>>> { + let msgs = CommandMessages::with_command(self.cmd)?; + Ok(CargoTest::with_messages(msgs)) + } +} + +impl Default for CargoBuild { + fn default() -> Self { + Self::new() + } +} diff --git a/vendor/escargot/src/cargo.rs b/vendor/escargot/src/cargo.rs new file mode 100644 index 000000000..6e8efd220 --- /dev/null +++ b/vendor/escargot/src/cargo.rs @@ -0,0 +1,60 @@ +use std::env; +use std::ffi; +use std::process; +use std::str; + +use crate::build::CargoBuild; + +/// The current process' target triplet. +pub const CURRENT_TARGET: &str = include_str!(concat!(env!("OUT_DIR"), "/current_target.txt")); + +static CARBO_BIN: once_cell::sync::Lazy<ffi::OsString> = + once_cell::sync::Lazy::new(|| env::var_os("CARGO").unwrap_or_else(|| "cargo".into())); + +/// Top-level command. +#[derive(Debug)] +pub struct Cargo { + cmd: process::Command, +} + +impl Cargo { + /// Create a top-level command. + pub fn new() -> Self { + Self { + cmd: process::Command::new(CARBO_BIN.as_os_str()), + } + } + + /// Manually pass an argument that is unsupported. + /// + /// Caution: Passing in a sub-command or `--` can throw off the API. + pub fn arg<S: AsRef<ffi::OsStr>>(mut self, arg: S) -> Self { + self.cmd.arg(arg); + self + } + + /// Manually pass arguments that are unsupported. + /// + /// Caution: Passing in a sub-command or `--` can throw off the API. + pub fn args<I: IntoIterator<Item = S>, S: AsRef<ffi::OsStr>>(mut self, args: I) -> Self { + self.cmd.args(args); + self + } + + /// Run the `build` subcommand. + pub fn build(self) -> CargoBuild { + self.build_with("build") + } + + /// Run a custom `build` subcommand. + pub fn build_with<S: AsRef<ffi::OsStr>>(mut self, name: S) -> CargoBuild { + self.cmd.arg(name).arg("--message-format=json"); + CargoBuild::with_command(self.cmd) + } +} + +impl Default for Cargo { + fn default() -> Self { + Self::new() + } +} diff --git a/vendor/escargot/src/error.rs b/vendor/escargot/src/error.rs new file mode 100644 index 000000000..4f19c4013 --- /dev/null +++ b/vendor/escargot/src/error.rs @@ -0,0 +1,91 @@ +//! Error reporting API. + +use std::error::Error; +use std::fmt; + +/// Result of a cargo command. +pub type CargoResult<T> = Result<T, CargoError>; + +/// For programmatically processing failures. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum ErrorKind { + /// Spawning the cargo subommand failed. + InvalidCommand, + /// The cargo subcommand returned an error. + CommandFailed, + /// Parsing the cargo subcommand's output failed. + InvalidOutput, +} + +impl fmt::Display for ErrorKind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + ErrorKind::InvalidOutput => write!(f, "Spawning the cargo subommand failed."), + ErrorKind::CommandFailed => write!(f, "The cargo subcommand returned an error."), + ErrorKind::InvalidCommand => write!(f, "Parsing the cargo subcommand's output failed."), + } + } +} + +/// Cargo command failure information. +#[derive(Debug)] +pub struct CargoError { + kind: ErrorKind, + context: Option<String>, + cause: Option<Box<dyn Error + Send + Sync + 'static>>, +} + +impl CargoError { + pub(crate) fn new(kind: ErrorKind) -> Self { + Self { + kind, + context: None, + cause: None, + } + } + + pub(crate) fn set_context<S>(mut self, context: S) -> Self + where + S: Into<String>, + { + let context = context.into(); + self.context = Some(context); + self + } + + pub(crate) fn set_cause<E>(mut self, cause: E) -> Self + where + E: Error + Send + Sync + 'static, + { + let cause = Box::new(cause); + self.cause = Some(cause); + self + } + + /// For programmatically processing failures. + pub fn kind(&self) -> ErrorKind { + self.kind + } +} + +impl Error for CargoError { + fn cause(&self) -> Option<&dyn Error> { + self.cause.as_ref().map(|c| { + let c: &dyn Error = c.as_ref(); + c + }) + } +} + +impl fmt::Display for CargoError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + writeln!(f, "Cargo command failed: {}", self.kind)?; + if let Some(ref context) = self.context { + writeln!(f, "{}", context)?; + } + if let Some(ref cause) = self.cause { + writeln!(f, "Cause: {}", cause)?; + } + Ok(()) + } +} diff --git a/vendor/escargot/src/format/diagnostic.rs b/vendor/escargot/src/format/diagnostic.rs new file mode 100644 index 000000000..0a2698d17 --- /dev/null +++ b/vendor/escargot/src/format/diagnostic.rs @@ -0,0 +1,158 @@ +//! This module contains `Diagnostic` and the types/functions it uses for deserialization. + +use std::borrow; +use std::path; + +type CowPath<'a> = borrow::Cow<'a, path::Path>; +type CowStr<'a> = borrow::Cow<'a, str>; + +/// The error code associated to this diagnostic. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[cfg_attr(feature = "strict_unstable", serde(deny_unknown_fields))] +#[non_exhaustive] +pub struct DiagnosticCode<'a> { + /// The code itself. + #[serde(borrow)] + pub code: CowStr<'a>, + /// An explanation for the code + #[serde(borrow)] + pub explanation: Option<CowStr<'a>>, +} + +/// A line of code associated with the Diagnostic +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[cfg_attr(feature = "strict_unstable", serde(deny_unknown_fields))] +#[non_exhaustive] +pub struct DiagnosticSpanLine<'a> { + /// The line of code associated with the error + #[serde(borrow)] + pub text: CowStr<'a>, + /// Start of the section of the line to highlight. 1-based, character offset in self.text + pub highlight_start: usize, + /// End of the section of the line to highlight. 1-based, character offset in self.text + pub highlight_end: usize, +} + +/// Macro expansion information associated with a diagnostic. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[cfg_attr(feature = "strict_unstable", serde(deny_unknown_fields))] +#[non_exhaustive] +pub struct DiagnosticSpanMacroExpansion<'a> { + /// span where macro was applied to generate this code; note that + /// this may itself derive from a macro (if + /// `span.expansion.is_some()`) + #[serde(borrow)] + pub span: DiagnosticSpan<'a>, + + /// name of macro that was applied (e.g., "foo!" or "#[derive(Eq)]") + #[serde(borrow)] + pub macro_decl_name: CowStr<'a>, + + /// span where macro was defined (if known) + #[serde(borrow)] + pub def_site_span: Option<DiagnosticSpan<'a>>, +} + +/// A section of the source code associated with a Diagnostic +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[cfg_attr(feature = "strict_unstable", serde(deny_unknown_fields))] +#[non_exhaustive] +pub struct DiagnosticSpan<'a> { + /// The file name this diagnostic comes from. + #[serde(borrow)] + pub file_name: CowPath<'a>, + /// The byte offset in the file where this diagnostic starts from. + pub byte_start: u32, + /// The byte offset in the file where this diagnostic ends. + pub byte_end: u32, + /// 1-based. The line in the file. + pub line_start: usize, + /// 1-based. The line in the file. + pub line_end: usize, + /// 1-based, character offset. + pub column_start: usize, + /// 1-based, character offset. + pub column_end: usize, + /// Is this a "primary" span -- meaning the point, or one of the points, + /// where the error occurred? + pub is_primary: bool, + /// Source text from the start of line_start to the end of line_end. + #[serde(borrow)] + pub text: Vec<DiagnosticSpanLine<'a>>, + /// Label that should be placed at this location (if any) + #[serde(borrow)] + pub label: Option<CowStr<'a>>, + /// If we are suggesting a replacement, this will contain text + /// that should be sliced in atop this span. + #[serde(borrow)] + pub suggested_replacement: Option<CowStr<'a>>, + /// If the suggestion is approximate + pub suggestion_applicability: Option<Applicability>, + /// Macro invocations that created the code at this span, if any. + #[serde(borrow)] + pub expansion: Option<Box<DiagnosticSpanMacroExpansion<'a>>>, +} + +/// Whether a suggestion can be safely applied. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +pub enum Applicability { + /// The suggested replacement can be applied automatically safely + MachineApplicable, + /// The suggested replacement has placeholders that will need to be manually + /// replaced. + HasPlaceholders, + /// The suggested replacement may be incorrect in some circumstances. Needs + /// human review. + MaybeIncorrect, + /// The suggested replacement will probably not work. + Unspecified, + #[cfg(not(feature = "strict_unstable"))] + #[doc(hidden)] + #[serde(other)] + Unknown, +} + +/// A diagnostic message generated by rustc +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[cfg_attr(feature = "strict_unstable", serde(deny_unknown_fields))] +#[non_exhaustive] +pub struct Diagnostic<'a> { + /// The error message of this diagnostic. + #[serde(borrow)] + pub message: CowStr<'a>, + /// The associated error code for this diagnostic + #[serde(borrow)] + pub code: Option<DiagnosticCode<'a>>, + /// The severity of the diagnostic. + pub level: DiagnosticLevel, + /// A list of source code spans this diagnostic is associated with. + #[serde(borrow)] + pub spans: Vec<DiagnosticSpan<'a>>, + /// Associated diagnostic messages. + #[serde(borrow)] + pub children: Vec<Diagnostic<'a>>, + /// The message as rustc would render it + #[serde(borrow)] + pub rendered: Option<CowStr<'a>>, +} + +/// The diagnostic level +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "lowercase")] +pub enum DiagnosticLevel { + /// Internal compiler error + #[serde(rename = "error: internal compiler error")] + Ice, + /// Error + Error, + /// Warning + Warning, + /// Note + Note, + /// Help + Help, + #[cfg(not(feature = "strict_unstable"))] + #[doc(hidden)] + #[serde(other)] + Unknown, +} diff --git a/vendor/escargot/src/format/mod.rs b/vendor/escargot/src/format/mod.rs new file mode 100644 index 000000000..9c9686139 --- /dev/null +++ b/vendor/escargot/src/format/mod.rs @@ -0,0 +1,266 @@ +//! Serialization formats for cargo messages. + +use std::borrow; +use std::path; + +pub mod diagnostic; + +#[cfg(feature = "test_unstable")] +pub mod test; + +type CowPath<'a> = borrow::Cow<'a, path::Path>; +type CowStr<'a> = borrow::Cow<'a, str>; + +/// A cargo message +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(tag = "reason", rename_all = "kebab-case")] +#[allow(clippy::large_enum_variant)] +pub enum Message<'a> { + /// Build completed, all further output should not be parsed + BuildFinished(BuildFinished), + /// The compiler generated an artifact + #[serde(borrow)] + CompilerArtifact(Artifact<'a>), + /// The compiler wants to display a message + #[serde(borrow)] + CompilerMessage(FromCompiler<'a>), + /// A build script successfully executed. + #[serde(borrow)] + BuildScriptExecuted(BuildScript<'a>), + #[cfg(not(feature = "strict_unstable"))] + #[doc(hidden)] + #[serde(other)] + Unknown, +} + +/// Build completed, all further output should not be parsed +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[cfg_attr(feature = "strict_unstable", serde(deny_unknown_fields))] +#[non_exhaustive] +pub struct BuildFinished { + success: bool, +} + +/// A compiler-generated file. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[cfg_attr(feature = "strict_unstable", serde(deny_unknown_fields))] +#[non_exhaustive] +pub struct Artifact<'a> { + /// The workspace member this artifact belongs to + #[serde(borrow)] + pub package_id: WorkspaceMember<'a>, + /// The full path to the artifact's manifest + #[serde(borrow)] + pub manifest_path: Option<CowPath<'a>>, + /// The target this artifact was compiled for + #[serde(borrow)] + pub target: Target<'a>, + /// The profile this artifact was compiled with + #[serde(borrow)] + pub profile: ArtifactProfile<'a>, + /// The enabled features for this artifact + #[serde(borrow)] + pub features: Vec<CowStr<'a>>, + /// The full paths to the generated artifacts + #[serde(borrow)] + pub filenames: Vec<CowPath<'a>>, + /// The full paths to the generated artifacts + #[serde(borrow)] + #[serde(default)] + pub executable: Option<CowPath<'a>>, + /// If true, then the files were already generated + pub fresh: bool, +} + +/// A single target (lib, bin, example, ...) provided by a crate +#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)] +#[cfg_attr(feature = "strict_unstable", serde(deny_unknown_fields))] +#[non_exhaustive] +pub struct Target<'a> { + /// Name as given in the `Cargo.toml` or generated from the file name + #[serde(borrow)] + pub name: CowStr<'a>, + /// Kind of target ("bin", "example", "test", "bench", "lib") + #[serde(borrow)] + pub kind: Vec<CowStr<'a>>, + /// Almost the same as `kind`, except when an example is a library instead of an executable. + /// In that case `crate_types` contains things like `rlib` and `dylib` while `kind` is `example` + #[serde(default)] + #[serde(borrow)] + pub crate_types: Vec<CowStr<'a>>, + /// Whether this is a doctest or not + #[serde(default)] + pub doctest: Option<bool>, + /// Whether this is documentation or not + #[serde(default)] + pub doc: Option<bool>, + /// Whether this is a test file + #[serde(default)] + pub test: bool, + + #[serde(default)] + #[serde(rename = "required-features")] + /// This target is built only if these features are enabled. + /// It doesn't apply to `lib` targets. + #[serde(borrow)] + pub required_features: Vec<CowStr<'a>>, + /// Path to the main source file of the target + #[serde(borrow)] + pub src_path: CowPath<'a>, + /// Rust edition for this target + #[serde(default = "edition_default")] + #[serde(borrow)] + pub edition: CowStr<'a>, +} + +fn edition_default() -> CowStr<'static> { + "2015".into() +} + +/// A workspace member. This is basically identical to `cargo::core::package_id::PackageId`, except +/// that this does not use `Arc` internally. +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] +#[serde(transparent)] +#[cfg_attr(feature = "strict_unstable", serde(deny_unknown_fields))] +pub struct WorkspaceMember<'a> { + /// The raw package id as given by cargo + #[serde(borrow)] + raw: CowStr<'a>, +} + +/// Profile settings used to determine which compiler flags to use for a +/// target. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[cfg_attr(feature = "strict_unstable", serde(deny_unknown_fields))] +#[non_exhaustive] +pub struct ArtifactProfile<'a> { + /// Optimization level. Possible values are 0-3, s or z. + #[serde(borrow)] + pub opt_level: CowStr<'a>, + /// The amount of debug info. 0 for none, 1 for limited, 2 for full + pub debuginfo: Option<u32>, + /// State of the `cfg(debug_assertions)` directive, enabling macros like + /// `debug_assert!` + pub debug_assertions: bool, + /// State of the overflow checks. + pub overflow_checks: bool, + /// Whether this profile is a test + pub test: bool, +} + +/// Message left by the compiler +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[cfg_attr(feature = "strict_unstable", serde(deny_unknown_fields))] +#[non_exhaustive] +pub struct FromCompiler<'a> { + /// The workspace member this message belongs to + #[serde(borrow)] + pub package_id: WorkspaceMember<'a>, + /// The full path to the artifact's manifest + #[serde(borrow)] + pub manifest_path: Option<CowPath<'a>>, + /// The target this message is aimed at + #[serde(borrow)] + pub target: Target<'a>, + /// The message the compiler sent. + #[serde(borrow)] + pub message: diagnostic::Diagnostic<'a>, +} + +/// Output of a Build Script execution. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[cfg_attr(feature = "strict_unstable", serde(deny_unknown_fields))] +#[non_exhaustive] +pub struct BuildScript<'a> { + /// The workspace member this build script execution belongs to + #[serde(borrow)] + pub package_id: WorkspaceMember<'a>, + /// The outdir used. + #[serde(borrow)] + #[serde(default)] + pub out_dir: Option<CowPath<'a>>, + /// The libs to link + #[serde(borrow)] + pub linked_libs: Vec<CowStr<'a>>, + /// The paths to search when resolving libs + #[serde(borrow)] + pub linked_paths: Vec<CowPath<'a>>, + /// The paths to search when resolving libs + #[serde(borrow)] + pub cfgs: Vec<CowPath<'a>>, + /// The environment variables to add to the compilation + #[serde(borrow)] + pub env: Vec<(CowStr<'a>, CowStr<'a>)>, +} + +#[cfg(not(feature = "print"))] +pub(crate) fn log_message(msg: &Message<'_>) { + match msg { + Message::BuildFinished(ref finished) => { + log::trace!("Build Finished: {:?}", finished.success); + } + Message::CompilerArtifact(ref art) => { + log::trace!("Building {:#?}", art.package_id,); + } + Message::CompilerMessage(ref comp) => { + let content = comp + .message + .rendered + .as_ref() + .map(|s| s.as_ref()) + .unwrap_or_else(|| comp.message.message.as_ref()); + match comp.message.level { + diagnostic::DiagnosticLevel::Ice => log::error!("{}", content), + diagnostic::DiagnosticLevel::Error => log::error!("{}", content), + diagnostic::DiagnosticLevel::Warning => log::warn!("{}", content), + diagnostic::DiagnosticLevel::Note => log::info!("{}", content), + diagnostic::DiagnosticLevel::Help => log::info!("{}", content), + #[cfg(not(feature = "strict_unstable"))] + _ => log::warn!("Unknown message: {:#?}", msg), + } + } + Message::BuildScriptExecuted(ref script) => { + log::trace!("Ran script from {:#?}", script.package_id); + } + #[cfg(not(feature = "strict_unstable"))] + _ => { + log::warn!("Unknown message: {:#?}", msg); + } + } +} + +#[cfg(feature = "print")] +pub(crate) fn log_message(msg: &Message<'_>) { + match msg { + Message::BuildFinished(ref finished) => { + eprintln!("Build Finished: {:?}", finished.success); + } + Message::CompilerArtifact(ref art) => { + eprintln!("Building {:#?}", art.package_id,); + } + Message::CompilerMessage(ref comp) => { + let content = comp + .message + .rendered + .as_ref() + .map(|s| s.as_ref()) + .unwrap_or_else(|| comp.message.message.as_ref()); + match comp.message.level { + diagnostic::DiagnosticLevel::Ice => eprintln!("{}", content), + diagnostic::DiagnosticLevel::Error => eprintln!("{}", content), + diagnostic::DiagnosticLevel::Warning => eprintln!("{}", content), + diagnostic::DiagnosticLevel::Note => eprintln!("{}", content), + diagnostic::DiagnosticLevel::Help => eprintln!("{}", content), + #[cfg(not(feature = "strict_unstable"))] + _ => eprintln!("Unknown message: {:#?}", msg), + } + } + Message::BuildScriptExecuted(ref script) => { + eprintln!("Ran script from {:#?}", script.package_id); + } + #[cfg(not(feature = "strict_unstable"))] + _ => { + eprintln!("Unknown message: {:#?}", msg); + } + } +} diff --git a/vendor/escargot/src/format/test.rs b/vendor/escargot/src/format/test.rs new file mode 100644 index 000000000..f046df9cf --- /dev/null +++ b/vendor/escargot/src/format/test.rs @@ -0,0 +1,242 @@ +//! Test runner emitted events. +//! +//! Required feature: `test_unstable` since the format parsed is unstable. + +use serde::Deserialize; + +// See https://github.com/rust-lang/rust/tree/master/src/libtest/formatters/json.rs + +/// Test-runner event. +#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)] +#[serde(rename_all = "snake_case")] +#[serde(tag = "type")] +pub enum Event { + /// Suite event. + Suite(Suite), + /// Test case event. + Test(Test), + /// Benchmark event. + Bench(Bench), + #[cfg(not(feature = "strict_unstable"))] + #[doc(hidden)] + #[serde(other)] + Unknown, +} + +/// Suite event. +#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)] +#[serde(rename_all = "snake_case")] +#[serde(tag = "event")] +pub enum Suite { + /// Suite-started event. + Started(SuiteStarted), + /// Suite-finished successfully event. + Ok(SuiteOk), + /// Suite-finished with failure event. + Failed(SuiteFailed), + #[cfg(not(feature = "strict_unstable"))] + #[doc(hidden)] + #[serde(other)] + Unknown, +} + +/// Suite-started event. +#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)] +#[non_exhaustive] +pub struct SuiteStarted { + /// Number of test cases in the suite. + pub test_count: usize, +} + +/// Suite-finished successfully event. +#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)] +#[non_exhaustive] +pub struct SuiteOk { + /// Cases that passed. + pub passed: usize, + /// Cases that failed. + pub failed: usize, + /// Cases that were allowed to fail. + pub allowed_fail: usize, + /// Ignored cases. + pub ignored: usize, + /// Benchmarks + pub measured: usize, + /// Cases filtered out by caller. + pub filtered_out: usize, +} + +/// Suite-finished with failure event. +#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)] +#[non_exhaustive] +pub struct SuiteFailed { + /// Cases that passed. + pub passed: usize, + /// Cases that failed. + pub failed: usize, + /// Cases that were allowed to fail. + pub allowed_fail: usize, + /// Ignored cases. + pub ignored: usize, + /// Benchmarks + pub measured: usize, + /// Cases filtered out by caller. + pub filtered_out: usize, +} + +/// Test case event. +#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)] +#[serde(rename_all = "snake_case")] +#[serde(tag = "event")] +pub enum Test { + /// Case-started event. + Started(TestStarted), + /// Case-finished successfully event. + Ok(TestOk), + /// Case-finished with failure event. + Failed(TestFailed), + /// Case-ignored event. + Ignored(TestIgnored), + /// Case-allowed-failure event. + AllowedFailure(TestAllowedFailured), + /// Case-timeout event. + Timeout(TestTimeout), + #[cfg(not(feature = "strict_unstable"))] + #[doc(hidden)] + #[serde(other)] + Unknown, +} + +/// Case-started event. +#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)] +#[non_exhaustive] +pub struct TestStarted { + /// Test case name. + pub name: String, +} + +/// Case-finished successfully event. +#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)] +#[non_exhaustive] +pub struct TestOk { + /// Test case name. + pub name: String, +} + +/// Case-finished with failure event. +#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)] +#[non_exhaustive] +pub struct TestFailed { + /// Test case name. + pub name: String, + /// Test's stdout + pub stdout: Option<String>, + /// Test failure mssage + pub message: Option<String>, +} + +/// Case-ignored event. +#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)] +#[non_exhaustive] +pub struct TestIgnored { + /// Test case name. + pub name: String, +} + +/// Case-allowed-failure event. +#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)] +#[non_exhaustive] +pub struct TestAllowedFailured { + /// Test case name. + pub name: String, +} + +/// Case-timeout event. +#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)] +#[non_exhaustive] +pub struct TestTimeout { + /// Test case name. + pub name: String, +} + +/// Benchmark event. +#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)] +#[non_exhaustive] +pub struct Bench { + /// Benchmark name. + pub name: String, + /// Median performance. + pub median: usize, + /// Deviation from median. + pub deviation: usize, + /// Mb/s + pub mib_per_second: Option<usize>, +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn suite_started() { + let input = r#"{ "type": "suite", "event": "started", "test_count": 10 }"#; + let _data: Event = serde_json::from_str(input).unwrap(); + } + + #[test] + fn suite_ok() { + let input = "{ \"type\": \"suite\", \ + \"event\": \"ok\", \ + \"passed\": 6, \ + \"failed\": 5, \ + \"allowed_fail\": 4, \ + \"ignored\": 3, \ + \"measured\": 2, \ + \"filtered_out\": 1 }"; + let _data: Event = serde_json::from_str(input).unwrap(); + } + + #[test] + fn suite_failed() { + let input = "{ \"type\": \"suite\", \ + \"event\": \"failed\", \ + \"passed\": 6, \ + \"failed\": 5, \ + \"allowed_fail\": 4, \ + \"ignored\": 3, \ + \"measured\": 2, \ + \"filtered_out\": 1 }"; + let _data: Event = serde_json::from_str(input).unwrap(); + } + + #[test] + fn test_started() { + let input = r#"{ "type": "test", "event": "started", "name": "foo" }"#; + let _data: Event = serde_json::from_str(input).unwrap(); + } + + #[test] + fn test_timeout() { + let input = r#"{ "type": "test", "event": "timeout", "name": "foo" }"#; + let _data: Event = serde_json::from_str(input).unwrap(); + } + + #[test] + fn bench() { + let input = "{ \"type\": \"bench\", \ + \"name\": \"foo\", \ + \"median\": 10, \ + \"deviation\": 2 }"; + let _data: Event = serde_json::from_str(input).unwrap(); + } + + #[test] + fn bench_full() { + let input = "{ \"type\": \"bench\", \ + \"name\": \"foo\", \ + \"median\": 10, \ + \"deviation\": 2, \ + \"mib_per_second\": 1 }"; + let _data: Event = serde_json::from_str(input).unwrap(); + } +} diff --git a/vendor/escargot/src/lib.rs b/vendor/escargot/src/lib.rs new file mode 100644 index 000000000..dd6559131 --- /dev/null +++ b/vendor/escargot/src/lib.rs @@ -0,0 +1,59 @@ +//! # Escargot: A Cargo API +//! +//! ## Features +//! +//! Features: +//! - `print` for logged output to be printed instead, generally for test writing. +//! +//! ## Why escargot +//! +//! Compared to depending on `cargo`: +//! - Faster compile times. +//! - Simpler API. +//! - Better interop with projects relying on other cargo versions. +//! - Probably slower execution, especially on platforms without an optimized `fork` (e.g. Windows). +//! +//! ## Relevant crates +//! +//! Other related crates: +//! * [cargo](https://crates.io/crates/cargo) for the real thing +//! * [cargo-metadata](https://crates.io/crates/cargo_metadata) for a similar project specifically geared to the `metadata` subcommand. +//! +//! # Example +//! +//! ```rust +//! extern crate escargot; +//! extern crate assert_fs; +//! +//! let temp = assert_fs::TempDir::new().unwrap(); +//! escargot::CargoBuild::new() +//! .bin("bin") +//! .current_release() +//! .current_target() +//! .manifest_path("tests/fixtures/bin/Cargo.toml") +//! .target_dir(temp.path()) +//! .exec() +//! .unwrap(); +//! ``` + +#![warn(missing_docs)] +#![cfg_attr(docsrs, feature(doc_auto_cfg))] + +#[macro_use] +extern crate serde; + +mod build; +pub use crate::build::*; +mod cargo; +pub use crate::cargo::*; +mod msg; +pub use crate::msg::*; +mod run; +pub use crate::run::*; +#[cfg(feature = "test_unstable")] +mod test; +#[cfg(feature = "test_unstable")] +pub use test::*; + +pub mod error; +pub mod format; diff --git a/vendor/escargot/src/msg.rs b/vendor/escargot/src/msg.rs new file mode 100644 index 000000000..b54a1d410 --- /dev/null +++ b/vendor/escargot/src/msg.rs @@ -0,0 +1,117 @@ +use std::io; +use std::io::BufRead; +use std::io::Read; +use std::process; + +use crate::error::*; +use crate::format; + +/// Messages returned from a cargo sub-command. +pub struct CommandMessages(InnerCommandMessages); + +struct InnerCommandMessages { + done: bool, + child: process::Child, + stdout: io::BufReader<process::ChildStdout>, + stderr: io::BufReader<process::ChildStderr>, +} + +impl CommandMessages { + /// Run the command, allowing iteration over ndjson messages. + pub fn with_command(mut cmd: process::Command) -> CargoResult<Self> { + let mut child = cmd + .stdout(process::Stdio::piped()) + .stderr(process::Stdio::piped()) + .spawn() + .map_err(|e| CargoError::new(ErrorKind::InvalidCommand).set_cause(e))?; + let stdout = child.stdout.take().expect("piped above"); + let stdout = io::BufReader::new(stdout); + let stderr = child.stderr.take().expect("piped above"); + let stderr = io::BufReader::new(stderr); + let msgs = InnerCommandMessages { + done: false, + child, + stdout, + stderr, + }; + Ok(CommandMessages(msgs)) + } + + #[inline] + fn next_msg(&mut self) -> CargoResult<Option<Message>> { + #![allow(clippy::branches_sharing_code)] + + let mut content = String::new(); + let len = self + .0 + .stdout + .read_line(&mut content) + .map_err(|e| CargoError::new(ErrorKind::InvalidOutput).set_cause(e))?; + if 0 < len { + Ok(Some(Message(content))) + } else { + let status = self + .0 + .child + .wait() + .map_err(|e| CargoError::new(ErrorKind::InvalidOutput).set_cause(e))?; + if !status.success() && !self.0.done { + self.0.done = true; + + let mut data = vec![]; + self.0 + .stderr + .read_to_end(&mut data) + .map_err(|e| CargoError::new(ErrorKind::InvalidOutput).set_cause(e))?; + let err = CargoError::new(ErrorKind::CommandFailed) + .set_context(String::from_utf8_lossy(&data)); + Err(err) + } else { + self.0.done = true; + Ok(None) + } + } + } +} + +impl Drop for CommandMessages { + fn drop(&mut self) { + if !self.0.done { + let _ = self.0.child.wait(); + } + } +} + +impl Iterator for CommandMessages { + type Item = CargoResult<Message>; + + #[inline] + fn next(&mut self) -> Option<CargoResult<Message>> { + match self.next_msg() { + Ok(Some(x)) => Some(Ok(x)), + Ok(None) => None, + Err(e) => Some(Err(e)), + } + } +} + +/// An individual message from a cargo sub-command. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Message(String); + +impl Message { + /// Deserialize the message. + pub fn decode(&self) -> CargoResult<format::Message<'_>> { + self.decode_custom() + } + + /// Deserialize the message. + pub fn decode_custom<'a, T>(&'a self) -> CargoResult<T> + where + T: serde::Deserialize<'a>, + { + let data = serde_json::from_str(self.0.as_str()) + .map_err(|e| CargoError::new(ErrorKind::InvalidOutput).set_cause(e))?; + Ok(data) + } +} diff --git a/vendor/escargot/src/run.rs b/vendor/escargot/src/run.rs new file mode 100644 index 000000000..e8aed96bb --- /dev/null +++ b/vendor/escargot/src/run.rs @@ -0,0 +1,195 @@ +use std::path; +use std::process; + +use crate::error::*; +use crate::format; +use crate::msg::*; + +/// The `run` subcommand (emulated). +/// +/// Created via [`CargoBuild::run`][crate::CargoBuild::run]. +/// +/// Benefits over spawning `cargo run`: +/// - Able to cache binary path, avoiding cargo overhead. +/// - Independent of CWD. +/// - stdout/stderr are clean of `cargo run` output. +/// +/// Relevant features +/// - `print` for logged output to be printed instead, generally for test writing. +/// +/// # Example +/// +/// To create a [`CargoRun`]: +/// ```rust +/// let temp = assert_fs::TempDir::new().unwrap(); +/// let run = escargot::CargoBuild::new() +/// .bin("bin") +/// .current_release() +/// .current_target() +/// .manifest_path("tests/fixtures/bin/Cargo.toml") +/// .target_dir(temp.path()) +/// .run() +/// .unwrap(); +/// println!("artifact={}", run.path().display()); +/// ``` +/// See [`CargoRun::path`] for how to then run the newly compiled +/// program. +pub struct CargoRun { + bin_path: path::PathBuf, +} + +impl CargoRun { + pub(crate) fn from_message( + msgs: CommandMessages, + is_bin: bool, + is_example: bool, + ) -> CargoResult<Self> { + let kind = match (is_bin, is_example) { + (true, true) => { + return Err(CargoError::new(ErrorKind::CommandFailed) + .set_context("Ambiguous which binary is intended, multiple selected")); + } + (false, true) => "example", + _ => "bin", + }; + let bin_path = extract_binary_path(msgs, kind)?; + Ok(Self { bin_path }) + } + + /// Path to the specified binary. + /// + /// This is to support alternative ways of launching the binary besides [`Command`]. + /// + /// # Example + /// + /// ```rust + /// let temp = assert_fs::TempDir::new().unwrap(); + /// let run = escargot::CargoBuild::new() + /// .bin("bin") + /// .current_release() + /// .current_target() + /// .manifest_path("tests/fixtures/bin/Cargo.toml") + /// .target_dir(temp.path()) + /// .run() + /// .unwrap(); + /// println!("artifact={}", run.path().display()); + /// ``` + /// or + /// ```rust,no_run + /// let temp = assert_fs::TempDir::new().unwrap(); + /// let run = escargot::CargoBuild::new() + /// .example("example_fixture") + /// .current_release() + /// .current_target() + /// .manifest_path("tests/fixtures/example/Cargo.toml") + /// .target_dir(temp.path()) + /// .run() + /// .unwrap(); + /// println!("artifact={}", run.path().display()); + /// ``` + /// + /// [`Command`]: std::process::Command + pub fn path(&self) -> &path::Path { + &self.bin_path + } + + /// Run the build artifact. + /// + /// # Example + /// + /// ```rust,no_run + /// let temp = assert_fs::TempDir::new().unwrap(); + /// let run = escargot::CargoBuild::new() + /// .bin("bin") + /// .current_release() + /// .current_target() + /// .manifest_path("tests/fixtures/bin/Cargo.toml") + /// .target_dir(temp.path()) + /// .run() + /// .unwrap() + /// .command() + /// .arg("--help") + /// .status() + /// .unwrap(); + /// ``` + /// or + /// ```rust + /// extern crate escargot; + /// extern crate assert_fs; + /// + /// let temp = assert_fs::TempDir::new().unwrap(); + /// let run = escargot::CargoBuild::new() + /// .example("example_fixture") + /// .current_release() + /// .current_target() + /// .manifest_path("tests/fixtures/example/Cargo.toml") + /// .target_dir(temp.path()) + /// .run() + /// .unwrap() + /// .command() + /// .arg("--help") + /// .status() + /// .unwrap(); + /// ``` + pub fn command(&self) -> process::Command { + process::Command::new(self.path()) + } +} + +fn extract_bin<'a>(msg: &'a format::Message<'_>, desired_kind: &str) -> Option<&'a path::Path> { + match msg { + format::Message::CompilerArtifact(art) => { + if !art.profile.test + && art.target.crate_types == ["bin"] + && art.target.kind == [desired_kind] + { + Some(art.filenames.get(0).expect("files must exist")) + } else { + None + } + } + _ => None, + } +} + +fn transpose<T, E>(r: Result<Option<T>, E>) -> Option<Result<T, E>> { + match r { + Ok(Some(x)) => Some(Ok(x)), + Ok(None) => None, + Err(e) => Some(Err(e)), + } +} + +fn extract_binary_paths( + msgs: CommandMessages, + kind: &'static str, +) -> impl Iterator<Item = Result<path::PathBuf, CargoError>> { + msgs.filter_map(move |m| { + let m = m.and_then(|m| { + let m = m.decode()?; + format::log_message(&m); + let p = extract_bin(&m, kind).map(|p| p.to_path_buf()); + Ok(p) + }); + transpose(m) + }) +} + +fn extract_binary_path( + msgs: CommandMessages, + kind: &'static str, +) -> Result<path::PathBuf, CargoError> { + let bins: Result<Vec<_>, CargoError> = extract_binary_paths(msgs, kind).collect(); + let bins = bins?; + if bins.is_empty() { + return Err(CargoError::new(ErrorKind::CommandFailed).set_context("No binaries in crate")); + } else if bins.len() != 1 { + return Err( + CargoError::new(ErrorKind::CommandFailed).set_context(std::format!( + "Ambiguous which binary is intended: {:?}", + bins + )), + ); + } + Ok(bins.into_iter().next().expect("already validated")) +} diff --git a/vendor/escargot/src/test.rs b/vendor/escargot/src/test.rs new file mode 100644 index 000000000..5aebacfec --- /dev/null +++ b/vendor/escargot/src/test.rs @@ -0,0 +1,201 @@ +use std::path; +use std::process; + +use crate::error::*; +use crate::format; +use crate::msg::*; + +/// The `test` subcommand (emulated). +/// +/// Created via [`CargoBuild::run_tests`]. +/// +/// Benefits over spawning `cargo test`: +/// - Able to cache binary path, avoiding cargo overhead. +/// - Independent of CWD. +/// - stdout/stderr are clean of `cargo test` output. +/// +/// Required feature: `test_unstable` since the format parsed is unstable. +/// +/// Relevant features +/// - `print` for logged output to be printed instead, generally for test writing. +/// +/// # Example +/// +/// ```rust +/// extern crate escargot; +/// extern crate assert_fs; +/// +/// let temp = assert_fs::TempDir::new().unwrap(); +/// let run = escargot::CargoBuild::new() +/// .test("test") +/// .manifest_path("tests/fixtures/test/Cargo.toml") +/// .target_dir(temp.path()) +/// .run_tests().unwrap() +/// .next().unwrap().unwrap(); +/// println!("artifact={}", run.path().display()); +/// ``` +/// +/// [`CargoBuild::run_tests`]: crate::CargoBuild::run_tests() +pub struct CargoTest { + bin_path: path::PathBuf, + kind: String, + name: String, +} + +impl CargoTest { + pub(crate) fn with_messages( + msgs: CommandMessages, + ) -> impl Iterator<Item = Result<Self, CargoError>> { + extract_binary_paths(msgs) + } + + /// The `name` of test + /// + /// Used to offer filtering or displays. + /// + /// # Example + /// + /// ```rust + /// extern crate escargot; + /// extern crate assert_fs; + /// + /// let temp = assert_fs::TempDir::new().unwrap(); + /// let run: Result<Vec<_>, _> = escargot::CargoBuild::new() + /// .tests() + /// .current_release() + /// .current_target() + /// .manifest_path("tests/fixtures/test/Cargo.toml") + /// .target_dir(temp.path()) + /// .run_tests() + /// .unwrap() + /// .collect(); + /// let run = run.unwrap(); + /// let mut names: Vec<_> = run.iter().map(|r| r.name()).collect(); + /// names.sort_unstable(); + /// assert_eq!(names, ["test", "test_fixture", "test_fixture"]); + /// ``` + pub fn name(&self) -> &str { + self.name.as_str() + } + + /// The `kind` of test + /// + /// Used to distinguish between integration tests (`test`) and unit tests (`bin`, `lib`). + /// + /// # Example + /// + /// ```rust + /// extern crate escargot; + /// extern crate assert_fs; + /// + /// let temp = assert_fs::TempDir::new().unwrap(); + /// let run: Result<Vec<_>, _> = escargot::CargoBuild::new() + /// .tests() + /// .current_release() + /// .current_target() + /// .manifest_path("tests/fixtures/test/Cargo.toml") + /// .target_dir(temp.path()) + /// .run_tests() + /// .unwrap() + /// .collect(); + /// let run = run.unwrap(); + /// let mut kinds: Vec<_> = run.iter().map(|r| r.kind()).collect(); + /// kinds.sort_unstable(); + /// assert_eq!(kinds, ["bin", "lib", "test"]); + /// ``` + pub fn kind(&self) -> &str { + self.kind.as_str() + } + + /// Path to the specified binary. + /// + /// This is to support alternative ways of launching the binary besides [`Command`]. + /// + /// # Example + /// + /// ```rust + /// extern crate escargot; + /// extern crate assert_fs; + /// + /// let temp = assert_fs::TempDir::new().unwrap(); + /// let run: Vec<_> = escargot::CargoBuild::new() + /// .tests() + /// .current_release() + /// .current_target() + /// .manifest_path("tests/fixtures/test/Cargo.toml") + /// .target_dir(temp.path()) + /// .run_tests() + /// .unwrap() + /// .collect(); + /// assert_eq!(run.len(), 3); + /// ``` + /// + /// [`Command`]: std::process::Command + pub fn path(&self) -> &path::Path { + &self.bin_path + } + + /// Run the build artifact. + pub fn command(&self) -> process::Command { + let mut cmd = process::Command::new(self.path()); + cmd.arg("-Z").arg("unstable-options").arg("--format=json"); + cmd + } + + /// Run the configured test, returning test events. + pub fn exec(&self) -> CargoResult<CommandMessages> { + CommandMessages::with_command(self.command()) + } +} + +fn extract_bin(msg: &format::Message<'_>) -> Option<CargoTest> { + match msg { + format::Message::CompilerArtifact(art) => { + if art.profile.test { + let bin_path = art + .filenames + .get(0) + .expect("files must exist") + .to_path_buf(); + let kind = art + .target + .kind + .get(0) + .expect("kind must exist") + .as_ref() + .to_owned(); + let name = art.target.name.as_ref().to_owned(); + Some(CargoTest { + bin_path, + kind, + name, + }) + } else { + None + } + } + _ => None, + } +} + +fn transpose<T, E>(r: Result<Option<T>, E>) -> Option<Result<T, E>> { + match r { + Ok(Some(x)) => Some(Ok(x)), + Ok(None) => None, + Err(e) => Some(Err(e)), + } +} + +fn extract_binary_paths( + msgs: CommandMessages, +) -> impl Iterator<Item = Result<CargoTest, CargoError>> { + msgs.filter_map(move |m| { + let m = m.and_then(|m| { + let m = m.decode()?; + format::log_message(&m); + let p = extract_bin(&m); + Ok(p) + }); + transpose(m) + }) +} |