| // Copyright 2021 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| use crate::{ArtifactMetadata, MaybeUnknown, Outcome, SuiteResult, TestCaseResult, TestRunResult}; |
| use std::{ |
| collections::{HashMap, HashSet}, |
| ops::Deref, |
| path::{Path, PathBuf}, |
| }; |
| use test_list::TestTag; |
| |
| enum MatchOption<T> { |
| AnyOrNone, |
| None, |
| Any, |
| Specified(T), |
| } |
| |
| macro_rules! assert_match_option { |
| ($expected:expr, $actual:expr, $field:expr) => { |
| match $expected { |
| MatchOption::AnyOrNone => (), |
| MatchOption::None => { |
| assert_eq!(None, $actual, "Expected {} to be None but was {:?}", $field, $actual) |
| } |
| MatchOption::Any => { |
| assert!($actual.is_some(), "Expected {} to contain a value but was None", $field) |
| } |
| MatchOption::Specified(val) => assert_eq!( |
| Some(val), |
| $actual, |
| "Expected {} to be {:?} but was {:?}", |
| $field, |
| Some(val), |
| $actual |
| ), |
| } |
| }; |
| } |
| |
| /// Container that identifies the entity that is being verified in an assertion. |
| #[derive(Clone, Copy)] |
| enum EntityContext<'a> { |
| Run, |
| Suite(&'a ExpectedSuite), |
| Case(&'a ExpectedSuite, &'a ExpectedTestCase), |
| } |
| |
| /// Container that identifies the artifact that is being verified in an assertion. |
| #[derive(Clone, Copy)] |
| struct ArtifactContext<'a, 'b> { |
| entity: &'a EntityContext<'b>, |
| metadata: &'a ArtifactMetadata, |
| } |
| |
| impl std::fmt::Display for EntityContext<'_> { |
| fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
| match self { |
| Self::Run => write!(f, "TEST RUN"), |
| Self::Suite(suite) => write!(f, "SUITE {}", suite.name), |
| Self::Case(suite, case) => write!(f, "SUITE {}: CASE {}", suite.name, case.name), |
| } |
| } |
| } |
| |
| impl std::fmt::Display for ArtifactContext<'_, '_> { |
| fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
| write!(f, "Entity: {}, Metadata: {:?}", self.entity, self.metadata) |
| } |
| } |
| |
| /// A mapping from artifact metadata to assertions made on the artifact. |
| type ArtifactMetadataToAssertionMap = HashMap<ArtifactMetadata, ExpectedArtifact>; |
| |
| /// Assert that the run results contained in `actual_run` and the directory specified by `root` |
| /// contain the results and artifacts in `expected_run`. |
| pub fn assert_run_result(root: &Path, expected_run: &ExpectedTestRun) { |
| let context = EntityContext::Run; |
| let actual_run = TestRunResult::from_dir(root).expect("Parse output directory"); |
| let TestRunResult { common, suites } = actual_run; |
| assert_match_option!( |
| expected_run.duration_milliseconds, |
| common.deref().duration_milliseconds, |
| format!("Run duration for {}", context) |
| ); |
| assert_match_option!( |
| expected_run.start_time, |
| common.deref().start_time, |
| format!("Start time for {}", context) |
| ); |
| assert_eq!(common.deref().outcome, expected_run.outcome, "Outcome for {}", context); |
| assert_artifacts( |
| root, |
| &common.deref().artifact_dir.root, |
| &common.deref().artifact_dir.artifacts, |
| &expected_run.artifacts, |
| EntityContext::Run, |
| ); |
| assert_suite_results(root, &suites, &expected_run.suites); |
| } |
| |
| /// Assert that the suite results contained in `actual_suites` and the directory specified by `root` |
| /// contain the suites, results, artifacts, and test cases in `expected_suite`. |
| /// Note that this currently does not support duplicate suite names. |
| fn assert_suite_results( |
| root: &Path, |
| actual_suites: &Vec<SuiteResult<'_>>, |
| expected_suites: &Vec<ExpectedSuite>, |
| ) { |
| assert_eq!(actual_suites.len(), expected_suites.len()); |
| let mut expected_suites_map = HashMap::new(); |
| for suite in expected_suites.iter() { |
| expected_suites_map.insert(suite.name.clone(), suite); |
| } |
| assert_eq!( |
| actual_suites.len(), |
| expected_suites_map.len(), |
| "Run contains multiple suites with the same name. \ |
| This is currently unsupported by assert_suite_results" |
| ); |
| for suite in actual_suites.iter() { |
| assert_suite_result( |
| root, |
| suite, |
| expected_suites_map |
| .get(&suite.common.deref().name) |
| .expect("No matching expected suite"), |
| ); |
| } |
| } |
| |
| /// Assert that the suite results contained in `actual_suite` and the directory specified by `root` |
| /// contain the results, artifacts, and test cases in `expected_suite`. |
| pub fn assert_suite_result( |
| root: &Path, |
| actual_suite: &SuiteResult<'_>, |
| expected_suite: &ExpectedSuite, |
| ) { |
| let context = EntityContext::Suite(expected_suite); |
| let &SuiteResult { common, cases, tags, summary_file_hint: _ } = &actual_suite; |
| assert_eq!(common.deref().outcome, expected_suite.outcome, "Outcome for {}", context); |
| assert_eq!(common.deref().name, expected_suite.name, "Name for {}", context); |
| assert_match_option!( |
| expected_suite.duration_milliseconds, |
| common.deref().duration_milliseconds, |
| format!("Duration for {}", context) |
| ); |
| assert_match_option!( |
| expected_suite.start_time, |
| common.deref().start_time, |
| format!("Start time for {}", context) |
| ); |
| |
| let mut tags: Vec<TestTag> = tags.clone().into_owned(); |
| tags.sort(); |
| |
| let mut expected_tags = expected_suite.tags.clone(); |
| expected_tags.sort(); |
| |
| assert_eq!(tags, expected_tags); |
| |
| assert_artifacts( |
| root, |
| &common.deref().artifact_dir.root, |
| &common.deref().artifact_dir.artifacts, |
| &expected_suite.artifacts, |
| context, |
| ); |
| |
| assert_eq!(cases.len(), expected_suite.cases.len()); |
| for case in cases.iter() { |
| let expected_case = expected_suite.cases.get(&case.common.deref().name); |
| assert!( |
| expected_case.is_some(), |
| "Found unexpected case {} in {}", |
| case.common.deref().name, |
| context |
| ); |
| assert_case_result(root, case, expected_case.unwrap(), expected_suite); |
| } |
| } |
| |
| fn assert_case_result( |
| root: &Path, |
| actual_case: &TestCaseResult<'_>, |
| expected_case: &ExpectedTestCase, |
| parent_suite: &ExpectedSuite, |
| ) { |
| let context = EntityContext::Case(parent_suite, expected_case); |
| assert_eq!(actual_case.common.deref().name, expected_case.name, "Name for {}", context); |
| assert_eq!( |
| actual_case.common.deref().outcome, |
| expected_case.outcome, |
| "Outcome for {}", |
| context |
| ); |
| assert_match_option!( |
| expected_case.duration_milliseconds, |
| actual_case.common.deref().duration_milliseconds, |
| format!("Duration for {}", context) |
| ); |
| assert_match_option!( |
| expected_case.start_time, |
| actual_case.common.deref().start_time, |
| format!("Start time for {}", context) |
| ); |
| assert_artifacts( |
| root, |
| &actual_case.common.deref().artifact_dir.root, |
| &actual_case.common.deref().artifact_dir.artifacts, |
| &expected_case.artifacts, |
| context, |
| ); |
| } |
| |
| fn assert_artifacts( |
| root: &Path, |
| artifact_dir: &Path, |
| actual_artifacts: &HashMap<PathBuf, ArtifactMetadata>, |
| expected_artifacts: &ArtifactMetadataToAssertionMap, |
| entity_context: EntityContext<'_>, |
| ) { |
| // TODO(fxbug.dev/100463): add options so that the test author can explicitly declare whether |
| // artifacts should be an exact match, should contain (and may contain more) artifacts, |
| // or any number of artifacts is accesptable. |
| // This skips artifact assertion for the typical case where verifying artifacts isn't |
| // necessary and allows the author to avoid listing out every artifact that is generated |
| // by the test. |
| if expected_artifacts.is_empty() { |
| return; |
| } |
| |
| let actual_artifacts_by_metadata: HashMap<ArtifactMetadata, PathBuf> = |
| actual_artifacts.iter().map(|(key, value)| (value.clone(), key.clone())).collect(); |
| // For now, artifact metadata should be unique for each artifact. |
| assert_eq!( |
| actual_artifacts_by_metadata.len(), |
| actual_artifacts.len(), |
| "Artifacts for {} do not have unique metadata. Actual artifacts: {:?}", |
| entity_context, |
| actual_artifacts |
| ); |
| |
| let expected_metadata: HashSet<_> = expected_artifacts.keys().collect(); |
| let actual_metadata: HashSet<_> = actual_artifacts_by_metadata.keys().collect(); |
| |
| assert_eq!( |
| expected_metadata, actual_metadata, |
| "Artifacts for {} do not have matching metadata.", |
| entity_context, |
| ); |
| |
| for (expected_metadata, expected_artifact) in expected_artifacts.iter() { |
| let actual_filepath = |
| artifact_dir.join(actual_artifacts_by_metadata.get(expected_metadata).unwrap()); |
| match expected_artifact { |
| ExpectedArtifact::File { name, assertion_fn } => { |
| assert_file( |
| &root.join(&actual_filepath), |
| name, |
| assertion_fn, |
| ArtifactContext { entity: &entity_context, metadata: expected_metadata }, |
| ); |
| } |
| ExpectedArtifact::Directory { files, name } => { |
| match name { |
| None => (), |
| Some(name) => assert_eq!( |
| name.as_str(), |
| actual_filepath.file_name().unwrap().to_str().unwrap(), |
| "Expected filename {} for artifact matching {:?} but got {}", |
| name, |
| expected_metadata, |
| actual_filepath.file_name().unwrap().to_str().unwrap() |
| ), |
| } |
| let actual_entries: HashSet<_> = std::fs::read_dir(root.join(&actual_filepath)) |
| .expect("Failed to read directory artifact path") |
| .map(|entry| match entry { |
| Ok(dir_entry) if dir_entry.file_type().unwrap().is_file() => { |
| dir_entry.file_name().to_str().unwrap().to_string() |
| } |
| // TODO(fxbugdev/85528) - support directory artifacts with subdirectories |
| Ok(_) => panic!("Directory artifact with subdirectories unsupported"), |
| Err(e) => panic!("Error reading directory artifact: {:?}", e), |
| }) |
| .collect(); |
| let expected_entries: HashSet<_> = |
| files.iter().map(|(name, _)| name.to_string()).collect(); |
| assert_eq!( |
| actual_entries, expected_entries, |
| "Expected files {:?} in directory artifact, got {:?}", |
| &expected_entries, &actual_entries |
| ); |
| for (name, assertion) in files { |
| assert_file( |
| &root.join(&actual_filepath).join(name), |
| &None, |
| assertion, |
| ArtifactContext { entity: &entity_context, metadata: expected_metadata }, |
| ); |
| } |
| } |
| } |
| } |
| } |
| |
| fn assert_file( |
| file_path: &Path, |
| name: &Option<String>, |
| assertion_fn: &Box<dyn Fn(&str)>, |
| artifact_context: ArtifactContext<'_, '_>, |
| ) { |
| match name { |
| None => (), |
| Some(name) => assert_eq!( |
| name.as_str(), |
| file_path.file_name().unwrap().to_str().unwrap(), |
| "Got incorrect filename while checking file for artifact {}", |
| artifact_context |
| ), |
| } |
| let actual_contents = std::fs::read_to_string(&file_path); |
| (assertion_fn)(&actual_contents.unwrap()); |
| } |
| |
| /// The expected contents of an artifact. |
| enum ExpectedArtifact { |
| /// An artifact contained in a single file, such as stdout. |
| File { |
| /// If given, the expected name of the file. |
| name: Option<String>, |
| /// Assertion run against the contents of the file. |
| assertion_fn: Box<dyn Fn(&str)>, |
| }, |
| /// An artifact consisting of files in a directory. |
| Directory { |
| /// List of expected files, as (name, assertion) pairs. The name |
| /// is the expected name of the file, and the assertion fn is run |
| /// against the contents of the file. |
| files: Vec<(String, Box<dyn Fn(&str)>)>, |
| /// If given, the expected name of the directory. |
| name: Option<String>, |
| }, |
| } |
| |
| /// Contents of an expected directory artifact. |
| pub struct ExpectedDirectory { |
| files: Vec<(String, Box<dyn Fn(&str)>)>, |
| } |
| |
| impl ExpectedDirectory { |
| /// Create a new empty expected directory. |
| pub fn new() -> Self { |
| Self { files: vec![] } |
| } |
| |
| /// Add a file with expected |contents|. |
| pub fn with_file(self, name: impl AsRef<str>, contents: impl AsRef<str>) -> Self { |
| let owned_expected = contents.as_ref().to_string(); |
| let owned_name = name.as_ref().to_string(); |
| self.with_matching_file(name, move |actual| { |
| assert_eq!( |
| &owned_expected, actual, |
| "Mismatch in contents of file {}. Expected: '{}', actual:'{}'", |
| owned_name, &owned_expected, actual |
| ) |
| }) |
| } |
| |
| pub fn with_matching_file( |
| mut self, |
| name: impl AsRef<str>, |
| matcher: impl 'static + Fn(&str), |
| ) -> Self { |
| self.files.push((name.as_ref().to_string(), Box::new(matcher))); |
| self |
| } |
| } |
| |
| /// A version of a test run result that contains all output in memory. This should only be used |
| /// for making assertions in a test. |
| pub struct ExpectedTestRun { |
| artifacts: ArtifactMetadataToAssertionMap, |
| outcome: MaybeUnknown<Outcome>, |
| start_time: MatchOption<u64>, |
| duration_milliseconds: MatchOption<u64>, |
| suites: Vec<ExpectedSuite>, |
| } |
| |
| /// A version of a suite run result that contains all output in memory. This should only be used |
| /// for making assertions in a test. |
| pub struct ExpectedSuite { |
| artifacts: ArtifactMetadataToAssertionMap, |
| name: String, |
| outcome: MaybeUnknown<Outcome>, |
| cases: HashMap<String, ExpectedTestCase>, |
| start_time: MatchOption<u64>, |
| duration_milliseconds: MatchOption<u64>, |
| tags: Vec<TestTag>, |
| } |
| |
| /// A version of a test case result that contains all output in memory. This should only be used |
| /// for making assertions in a test. |
| pub struct ExpectedTestCase { |
| artifacts: ArtifactMetadataToAssertionMap, |
| name: String, |
| outcome: MaybeUnknown<Outcome>, |
| start_time: MatchOption<u64>, |
| duration_milliseconds: MatchOption<u64>, |
| } |
| |
| macro_rules! common_impl { |
| {} => { |
| /// Add an artifact matching the exact contents. Artifacts are checked by finding |
| /// an entry matching the given metadata, then checking the contents of the corresponding |
| /// file. If |name| is provided, the name of the file is verified. Artifacts are keyed by |
| /// metadata rather than by name as the names of files are not guaranteed to be stable. |
| pub fn with_artifact<S, T, U>( |
| self, metadata: U, name: Option<S>, contents: T |
| ) -> Self |
| where |
| S: AsRef<str>, |
| T: AsRef<str>, |
| U: Into<ArtifactMetadata> |
| { |
| let owned_expected = contents.as_ref().to_string(); |
| let metadata = metadata.into(); |
| let metadata_clone = metadata.clone(); |
| self.with_matching_artifact(metadata, name, move |actual| { |
| assert_eq!( |
| &owned_expected, actual, |
| "Mismatch in artifact with metadata {:?}. Expected: '{}', actual:'{}'", |
| metadata_clone, &owned_expected, actual |
| ) |
| }) |
| } |
| |
| /// Add an artifact matching the exact contents. Artifacts are checked by finding |
| /// an entry matching the given metadata, then running |matcher| against the contents of |
| /// the file. If |name| is provided, the name of the file is verified. Artifacts are keyed |
| /// by metadata rather than by name as the names of files are not guaranteed to be stable. |
| pub fn with_matching_artifact<S, F, U>( |
| mut self, |
| metadata: U, |
| name: Option<S>, |
| matcher: F, |
| ) -> Self |
| where |
| S: AsRef<str>, |
| F: 'static + Fn(&str), |
| U: Into<ArtifactMetadata> |
| { |
| self.artifacts.insert( |
| metadata.into(), |
| ExpectedArtifact::File { |
| name: name.map(|s| s.as_ref().to_string()), |
| assertion_fn: Box::new(matcher), |
| } |
| ); |
| self |
| } |
| |
| /// Add a directory based artifact containing the entries described in |directory|. |
| pub fn with_directory_artifact<S, U>( |
| mut self, |
| metadata: U, |
| name: Option<S>, |
| directory: ExpectedDirectory, |
| ) -> Self |
| where |
| S: AsRef<str>, |
| U: Into<ArtifactMetadata> |
| { |
| self.artifacts.insert( |
| metadata.into(), |
| ExpectedArtifact::Directory { |
| name: name.map(|s| s.as_ref().to_string()), |
| files: directory.files, |
| } |
| ); |
| self |
| } |
| |
| /// Verify an exact start time. |
| pub fn with_start_time(mut self, millis: u64) -> Self { |
| self.start_time = MatchOption::Specified(millis); |
| self |
| } |
| |
| /// Verify an exact run duration. |
| pub fn with_run_duration(mut self, millis: u64) -> Self { |
| self.duration_milliseconds = MatchOption::Specified(millis); |
| self |
| } |
| |
| /// Verify that a start time is present. |
| pub fn with_any_start_time(mut self) -> Self { |
| self.start_time = MatchOption::Any; |
| self |
| } |
| |
| /// Verify that a run duration is present. |
| pub fn with_any_run_duration(mut self) -> Self { |
| self.duration_milliseconds = MatchOption::Any; |
| self |
| } |
| |
| /// Verify that no start time is present. |
| pub fn with_no_start_time(mut self) -> Self { |
| self.start_time = MatchOption::None; |
| self |
| } |
| |
| /// Verify that no run duration is present. |
| pub fn with_no_run_duration(mut self) -> Self { |
| self.duration_milliseconds = MatchOption::None; |
| self |
| } |
| }; |
| } |
| |
| impl ExpectedTestRun { |
| /// Create a new `ExpectedTestRun` with the given `outcome`. |
| pub fn new(outcome: Outcome) -> Self { |
| Self { |
| artifacts: ArtifactMetadataToAssertionMap::new(), |
| outcome: outcome.into(), |
| start_time: MatchOption::AnyOrNone, |
| duration_milliseconds: MatchOption::AnyOrNone, |
| suites: vec![], |
| } |
| } |
| |
| pub fn with_suite(mut self, suite: ExpectedSuite) -> Self { |
| self.suites.push(suite); |
| self |
| } |
| |
| common_impl! {} |
| } |
| |
| impl ExpectedSuite { |
| /// Create a new `ExpectedTestRun` with the given `name` and `outcome`. |
| pub fn new<S: AsRef<str>>(name: S, outcome: Outcome) -> Self { |
| Self { |
| artifacts: ArtifactMetadataToAssertionMap::new(), |
| name: name.as_ref().to_string(), |
| outcome: outcome.into(), |
| cases: HashMap::new(), |
| start_time: MatchOption::AnyOrNone, |
| duration_milliseconds: MatchOption::AnyOrNone, |
| tags: vec![], |
| } |
| } |
| |
| /// Add a test case to the suite. |
| pub fn with_case(mut self, case: ExpectedTestCase) -> Self { |
| self.cases.insert(case.name.clone(), case); |
| self |
| } |
| |
| /// Add a tag to the suite. |
| pub fn with_tag(mut self, tag: TestTag) -> Self { |
| self.tags.push(tag); |
| self |
| } |
| |
| common_impl! {} |
| } |
| |
| impl ExpectedTestCase { |
| /// Create a new `ExpectedTestCase` with the given `name` and `outcome`. |
| pub fn new<S: AsRef<str>>(name: S, outcome: Outcome) -> Self { |
| Self { |
| artifacts: ArtifactMetadataToAssertionMap::new(), |
| name: name.as_ref().to_string(), |
| outcome: outcome.into(), |
| start_time: MatchOption::AnyOrNone, |
| duration_milliseconds: MatchOption::AnyOrNone, |
| } |
| } |
| |
| common_impl! {} |
| } |
| |
| #[cfg(test)] |
| mod test { |
| use super::*; |
| use crate::{ArtifactType, CommonResult, OutputDirectoryBuilder, SchemaVersion, RUN_NAME}; |
| use std::borrow::Cow; |
| use std::io::Write; |
| |
| fn test_with_directory<F: Fn(OutputDirectoryBuilder)>(_test_name: &str, test_fn: F) { |
| for version in SchemaVersion::all_variants() { |
| let dir = tempfile::TempDir::new().unwrap(); |
| let directory_builder = |
| OutputDirectoryBuilder::new(dir.path(), version).expect("Create directory builder"); |
| test_fn(directory_builder); |
| } |
| } |
| |
| #[fixture::fixture(test_with_directory)] |
| #[test] |
| fn assert_run_result_check_outcome_only(output_dir: OutputDirectoryBuilder) { |
| let actual = TestRunResult { |
| common: Cow::Owned(CommonResult { |
| name: RUN_NAME.to_string(), |
| artifact_dir: output_dir.new_artifact_dir("artifacts").expect("new artifact dir"), |
| outcome: Outcome::Passed.into(), |
| start_time: Some(64), |
| duration_milliseconds: Some(128), |
| }), |
| suites: vec![], |
| }; |
| |
| output_dir.save_summary(&actual).expect("save summary"); |
| assert_run_result( |
| output_dir.path(), |
| &ExpectedTestRun::new(Outcome::Passed).with_any_start_time().with_any_run_duration(), |
| ); |
| } |
| |
| #[fixture::fixture(test_with_directory)] |
| #[test] |
| fn assert_run_result_check_exact_timing(output_dir: OutputDirectoryBuilder) { |
| let actual = TestRunResult { |
| common: Cow::Owned(CommonResult { |
| name: RUN_NAME.to_string(), |
| artifact_dir: output_dir.new_artifact_dir("artifacts").expect("new artifact dir"), |
| outcome: Outcome::Passed.into(), |
| start_time: Some(64), |
| duration_milliseconds: Some(128), |
| }), |
| suites: vec![], |
| }; |
| |
| output_dir.save_summary(&actual).expect("save summary"); |
| assert_run_result( |
| output_dir.path(), |
| &ExpectedTestRun::new(Outcome::Passed).with_start_time(64).with_run_duration(128), |
| ); |
| } |
| |
| #[fixture::fixture(test_with_directory)] |
| #[test] |
| fn assert_run_result_check_timing_unspecified(output_dir: OutputDirectoryBuilder) { |
| let actual = TestRunResult { |
| common: Cow::Owned(CommonResult { |
| name: RUN_NAME.to_string(), |
| artifact_dir: output_dir.new_artifact_dir("artifacts").expect("new artifact dir"), |
| outcome: Outcome::Passed.into(), |
| start_time: None, |
| duration_milliseconds: None, |
| }), |
| suites: vec![], |
| }; |
| |
| output_dir.save_summary(&actual).expect("save summary"); |
| assert_run_result( |
| output_dir.path(), |
| &ExpectedTestRun::new(Outcome::Passed).with_no_start_time().with_no_run_duration(), |
| ); |
| } |
| |
| #[fixture::fixture(test_with_directory)] |
| #[test] |
| fn assert_run_result_single_artifact_unspecified_name(output_dir: OutputDirectoryBuilder) { |
| let mut artifact_dir = output_dir.new_artifact_dir("artifacts").expect("new artifact dir"); |
| let mut artifact = |
| artifact_dir.new_artifact(ArtifactType::Syslog, "b.txt").expect("create artifact"); |
| write!(artifact, "hello").expect("write to artifact"); |
| drop(artifact); |
| |
| let actual = TestRunResult { |
| common: Cow::Owned(CommonResult { |
| name: RUN_NAME.to_string(), |
| artifact_dir, |
| outcome: Outcome::Passed.into(), |
| start_time: None, |
| duration_milliseconds: None, |
| }), |
| suites: vec![], |
| }; |
| |
| output_dir.save_summary(&actual).expect("save summary"); |
| assert_run_result( |
| output_dir.path(), |
| &ExpectedTestRun::new(Outcome::Passed).with_artifact( |
| ArtifactType::Syslog, |
| Option::<&str>::None, |
| "hello", |
| ), |
| ); |
| } |
| |
| #[fixture::fixture(test_with_directory)] |
| #[test] |
| fn assert_run_result_single_artifact_specified_name(output_dir: OutputDirectoryBuilder) { |
| let mut artifact_dir = output_dir.new_artifact_dir("artifacts").expect("new artifact dir"); |
| let mut artifact = |
| artifact_dir.new_artifact(ArtifactType::Syslog, "b.txt").expect("create artifact"); |
| write!(artifact, "hello").expect("write to artifact"); |
| drop(artifact); |
| |
| let actual = TestRunResult { |
| common: Cow::Owned(CommonResult { |
| name: RUN_NAME.to_string(), |
| artifact_dir, |
| outcome: Outcome::Passed.into(), |
| start_time: None, |
| duration_milliseconds: None, |
| }), |
| suites: vec![], |
| }; |
| |
| output_dir.save_summary(&actual).expect("save summary"); |
| assert_run_result( |
| output_dir.path(), |
| &ExpectedTestRun::new(Outcome::Passed).with_artifact( |
| ArtifactType::Syslog, |
| "b.txt".into(), |
| "hello", |
| ), |
| ); |
| } |
| |
| #[fixture::fixture(test_with_directory)] |
| #[test] |
| #[should_panic(expected = "Outcome for TEST RUN")] |
| fn assert_run_outcome_mismatch(output_dir: OutputDirectoryBuilder) { |
| let actual = TestRunResult { |
| common: Cow::Owned(CommonResult { |
| name: RUN_NAME.to_string(), |
| artifact_dir: output_dir.new_artifact_dir("artifacts").expect("new artifact dir"), |
| outcome: Outcome::Failed.into(), |
| start_time: None, |
| duration_milliseconds: None, |
| }), |
| suites: vec![], |
| }; |
| |
| output_dir.save_summary(&actual).expect("save summary"); |
| assert_run_result(output_dir.path(), &ExpectedTestRun::new(Outcome::Passed)); |
| } |
| |
| #[fixture::fixture(test_with_directory)] |
| #[test] |
| #[should_panic(expected = "Start time for TEST RUN")] |
| fn assert_run_start_time_mismatch(output_dir: OutputDirectoryBuilder) { |
| let actual = TestRunResult { |
| common: Cow::Owned(CommonResult { |
| name: RUN_NAME.to_string(), |
| artifact_dir: output_dir.new_artifact_dir("artifacts").expect("new artifact dir"), |
| outcome: Outcome::Failed.into(), |
| start_time: Some(64), |
| duration_milliseconds: None, |
| }), |
| suites: vec![], |
| }; |
| |
| output_dir.save_summary(&actual).expect("save summary"); |
| assert_run_result( |
| output_dir.path(), |
| &ExpectedTestRun::new(Outcome::Passed).with_start_time(23), |
| ); |
| } |
| |
| #[fixture::fixture(test_with_directory)] |
| #[test] |
| #[should_panic(expected = "Run duration for TEST RUN")] |
| fn assert_run_duration_mismatch(output_dir: OutputDirectoryBuilder) { |
| let actual = TestRunResult { |
| common: Cow::Owned(CommonResult { |
| name: RUN_NAME.to_string(), |
| artifact_dir: output_dir.new_artifact_dir("artifacts").expect("new artifact dir"), |
| outcome: Outcome::Failed.into(), |
| start_time: None, |
| duration_milliseconds: None, |
| }), |
| suites: vec![], |
| }; |
| |
| output_dir.save_summary(&actual).expect("save summary"); |
| assert_run_result( |
| output_dir.path(), |
| &ExpectedTestRun::new(Outcome::Passed).with_run_duration(23), |
| ); |
| } |
| |
| #[fixture::fixture(test_with_directory)] |
| #[test] |
| #[should_panic] |
| fn assert_run_artifact_mismatch(output_dir: OutputDirectoryBuilder) { |
| let mut artifact_dir = output_dir.new_artifact_dir("artifacts").expect("new artifact dir"); |
| let mut artifact = |
| artifact_dir.new_artifact(ArtifactType::Syslog, "missing").expect("create artifact"); |
| write!(artifact, "hello").expect("write to artifact"); |
| drop(artifact); |
| |
| let actual = TestRunResult { |
| common: Cow::Owned(CommonResult { |
| name: RUN_NAME.to_string(), |
| artifact_dir, |
| outcome: Outcome::Failed.into(), |
| start_time: None, |
| duration_milliseconds: None, |
| }), |
| suites: vec![], |
| }; |
| |
| output_dir.save_summary(&actual).expect("save summary"); |
| assert_run_result( |
| output_dir.path(), |
| &ExpectedTestRun::new(Outcome::Failed).with_artifact( |
| ArtifactType::Stderr, |
| "stderr.txt".into(), |
| "", |
| ), |
| ); |
| } |
| |
| fn passing_run_with_single_suite<'a>( |
| output_dir: &OutputDirectoryBuilder, |
| suite: SuiteResult<'a>, |
| ) -> TestRunResult<'a> { |
| TestRunResult { |
| common: Cow::Owned(CommonResult { |
| name: RUN_NAME.to_string(), |
| artifact_dir: output_dir.new_artifact_dir("artifacts").expect("new artifact dir"), |
| outcome: Outcome::Passed.into(), |
| start_time: Some(64), |
| duration_milliseconds: Some(128), |
| }), |
| suites: vec![suite], |
| } |
| } |
| |
| #[fixture::fixture(test_with_directory)] |
| #[test] |
| fn assert_run_result_with_suite(output_dir: OutputDirectoryBuilder) { |
| let actual = passing_run_with_single_suite( |
| &output_dir, |
| SuiteResult { |
| common: Cow::Owned(CommonResult { |
| name: "suite".to_string(), |
| artifact_dir: output_dir |
| .new_artifact_dir("artifacts-suite") |
| .expect("new artifact dir"), |
| outcome: Outcome::Passed.into(), |
| start_time: Some(64), |
| duration_milliseconds: Some(128), |
| }), |
| summary_file_hint: Cow::Owned("summary.json".into()), |
| cases: vec![], |
| tags: Cow::Owned(vec![]), |
| }, |
| ); |
| |
| output_dir.save_summary(&actual).expect("save summary"); |
| assert_run_result( |
| output_dir.path(), |
| &ExpectedTestRun::new(Outcome::Passed) |
| .with_any_start_time() |
| .with_any_run_duration() |
| .with_suite( |
| ExpectedSuite::new("suite", Outcome::Passed) |
| .with_any_start_time() |
| .with_any_run_duration(), |
| ), |
| ); |
| } |
| |
| #[fixture::fixture(test_with_directory)] |
| #[test] |
| fn assert_run_result_with_suite_exact_times(output_dir: OutputDirectoryBuilder) { |
| let actual = passing_run_with_single_suite( |
| &output_dir, |
| SuiteResult { |
| common: Cow::Owned(CommonResult { |
| name: "suite".to_string(), |
| artifact_dir: output_dir |
| .new_artifact_dir("artifacts-suite") |
| .expect("new artifact dir"), |
| outcome: Outcome::Passed.into(), |
| start_time: Some(64), |
| duration_milliseconds: Some(128), |
| }), |
| summary_file_hint: Cow::Owned("summary.json".into()), |
| cases: vec![], |
| tags: Cow::Owned(vec![]), |
| }, |
| ); |
| |
| output_dir.save_summary(&actual).expect("save summary"); |
| assert_run_result( |
| output_dir.path(), |
| &ExpectedTestRun::new(Outcome::Passed) |
| .with_any_start_time() |
| .with_any_run_duration() |
| .with_suite( |
| ExpectedSuite::new("suite", Outcome::Passed) |
| .with_start_time(64) |
| .with_run_duration(128), |
| ), |
| ); |
| } |
| |
| #[fixture::fixture(test_with_directory)] |
| #[test] |
| fn assert_run_result_with_suite_no_times(output_dir: OutputDirectoryBuilder) { |
| let actual = passing_run_with_single_suite( |
| &output_dir, |
| SuiteResult { |
| common: Cow::Owned(CommonResult { |
| name: "suite".to_string(), |
| artifact_dir: output_dir |
| .new_artifact_dir("artifacts-suite") |
| .expect("new artifact dir"), |
| outcome: Outcome::Passed.into(), |
| start_time: None, |
| duration_milliseconds: None, |
| }), |
| summary_file_hint: Cow::Owned("summary.json".into()), |
| cases: vec![], |
| tags: Cow::Owned(vec![]), |
| }, |
| ); |
| |
| output_dir.save_summary(&actual).expect("save summary"); |
| assert_run_result( |
| output_dir.path(), |
| &ExpectedTestRun::new(Outcome::Passed) |
| .with_any_start_time() |
| .with_any_run_duration() |
| .with_suite( |
| ExpectedSuite::new("suite", Outcome::Passed) |
| .with_no_start_time() |
| .with_no_run_duration(), |
| ), |
| ); |
| } |
| |
| #[fixture::fixture(test_with_directory)] |
| #[test] |
| fn assert_run_result_suite_with_artifact(output_dir: OutputDirectoryBuilder) { |
| let mut artifact_dir = |
| output_dir.new_artifact_dir("artifacts-suite").expect("new artifact dir"); |
| let mut artifact = |
| artifact_dir.new_artifact(ArtifactType::Syslog, "b.txt").expect("create artifact"); |
| write!(artifact, "hello").expect("write to artifact"); |
| drop(artifact); |
| |
| let actual = passing_run_with_single_suite( |
| &output_dir, |
| SuiteResult { |
| common: Cow::Owned(CommonResult { |
| name: "suite".to_string(), |
| artifact_dir, |
| outcome: Outcome::Passed.into(), |
| start_time: None, |
| duration_milliseconds: None, |
| }), |
| summary_file_hint: Cow::Owned("summary.json".into()), |
| cases: vec![], |
| tags: Cow::Owned(vec![]), |
| }, |
| ); |
| |
| output_dir.save_summary(&actual).expect("save summary"); |
| assert_run_result( |
| output_dir.path(), |
| &ExpectedTestRun::new(Outcome::Passed) |
| .with_any_start_time() |
| .with_any_run_duration() |
| .with_suite(ExpectedSuite::new("suite", Outcome::Passed).with_artifact( |
| ArtifactType::Syslog, |
| "b.txt".into(), |
| "hello", |
| )), |
| ); |
| } |
| |
| #[fixture::fixture(test_with_directory)] |
| #[test] |
| fn assert_run_result_suite_with_case(output_dir: OutputDirectoryBuilder) { |
| let actual = passing_run_with_single_suite( |
| &output_dir, |
| SuiteResult { |
| common: Cow::Owned(CommonResult { |
| name: "suite".to_string(), |
| artifact_dir: output_dir |
| .new_artifact_dir("artifacts-suite") |
| .expect("new artifact dir"), |
| outcome: Outcome::Passed.into(), |
| start_time: None, |
| duration_milliseconds: None, |
| }), |
| summary_file_hint: Cow::Owned("summary.json".into()), |
| cases: vec![TestCaseResult { |
| common: Cow::Owned(CommonResult { |
| name: "case".to_string(), |
| artifact_dir: output_dir |
| .new_artifact_dir("artifacts-case") |
| .expect("new artifact dir"), |
| outcome: Outcome::Passed.into(), |
| start_time: None, |
| duration_milliseconds: None, |
| }), |
| }], |
| tags: Cow::Owned(vec![]), |
| }, |
| ); |
| |
| output_dir.save_summary(&actual).expect("save summary"); |
| assert_run_result( |
| output_dir.path(), |
| &ExpectedTestRun::new(Outcome::Passed) |
| .with_any_start_time() |
| .with_any_run_duration() |
| .with_suite( |
| ExpectedSuite::new("suite", Outcome::Passed).with_case( |
| ExpectedTestCase::new("case", Outcome::Passed) |
| .with_no_run_duration() |
| .with_no_start_time(), |
| ), |
| ), |
| ); |
| } |
| |
| #[fixture::fixture(test_with_directory)] |
| #[test] |
| fn assert_run_result_suite_with_tags(output_dir: OutputDirectoryBuilder) { |
| let actual = passing_run_with_single_suite( |
| &output_dir, |
| SuiteResult { |
| common: Cow::Owned(CommonResult { |
| name: "suite".to_string(), |
| artifact_dir: output_dir |
| .new_artifact_dir("artifacts-suite") |
| .expect("new artifact dir"), |
| outcome: Outcome::Passed.into(), |
| start_time: None, |
| duration_milliseconds: None, |
| }), |
| summary_file_hint: Cow::Owned("summary.json".into()), |
| cases: vec![], |
| tags: Cow::Owned(vec![ |
| TestTag { key: "os".to_string(), value: "fuchsia".to_string() }, |
| TestTag { key: "cpu".to_string(), value: "arm64".to_string() }, |
| ]), |
| }, |
| ); |
| |
| output_dir.save_summary(&actual).expect("save summary"); |
| assert_run_result( |
| output_dir.path(), |
| &ExpectedTestRun::new(Outcome::Passed) |
| .with_any_start_time() |
| .with_any_run_duration() |
| .with_suite( |
| ExpectedSuite::new("suite", Outcome::Passed) |
| .with_tag(TestTag { key: "cpu".to_string(), value: "arm64".to_string() }) |
| .with_tag(TestTag { key: "os".to_string(), value: "fuchsia".to_string() }), |
| ), |
| ); |
| } |
| |
| #[fixture::fixture(test_with_directory)] |
| #[test] |
| #[should_panic(expected = "Outcome for SUITE suite")] |
| fn assert_suite_outcome_mismatch(output_dir: OutputDirectoryBuilder) { |
| let actual = passing_run_with_single_suite( |
| &output_dir, |
| SuiteResult { |
| common: Cow::Owned(CommonResult { |
| name: "suite".to_string(), |
| artifact_dir: output_dir |
| .new_artifact_dir("artifacts-suite") |
| .expect("new artifact dir"), |
| outcome: Outcome::Failed.into(), |
| start_time: None, |
| duration_milliseconds: None, |
| }), |
| summary_file_hint: Cow::Owned("summary.json".into()), |
| cases: vec![], |
| tags: Cow::Owned(vec![]), |
| }, |
| ); |
| |
| output_dir.save_summary(&actual).expect("save summary"); |
| assert_run_result( |
| output_dir.path(), |
| &ExpectedTestRun::new(Outcome::Passed) |
| .with_any_start_time() |
| .with_any_run_duration() |
| .with_suite(ExpectedSuite::new("suite", Outcome::Passed)), |
| ); |
| } |
| |
| #[fixture::fixture(test_with_directory)] |
| #[test] |
| #[should_panic(expected = "Start time for SUITE suite")] |
| fn assert_suite_start_time_mismatch(output_dir: OutputDirectoryBuilder) { |
| let actual = passing_run_with_single_suite( |
| &output_dir, |
| SuiteResult { |
| common: Cow::Owned(CommonResult { |
| name: "suite".to_string(), |
| artifact_dir: output_dir |
| .new_artifact_dir("artifacts-suite") |
| .expect("new artifact dir"), |
| outcome: Outcome::Passed.into(), |
| start_time: None, |
| duration_milliseconds: Some(128), |
| }), |
| summary_file_hint: Cow::Owned("summary.json".into()), |
| cases: vec![], |
| tags: Cow::Owned(vec![]), |
| }, |
| ); |
| |
| output_dir.save_summary(&actual).expect("save summary"); |
| assert_run_result( |
| output_dir.path(), |
| &ExpectedTestRun::new(Outcome::Passed) |
| .with_any_start_time() |
| .with_any_run_duration() |
| .with_suite(ExpectedSuite::new("suite", Outcome::Passed).with_any_start_time()), |
| ); |
| } |
| |
| #[fixture::fixture(test_with_directory)] |
| #[test] |
| #[should_panic(expected = "Duration for SUITE suite")] |
| fn assert_suite_duration_mismatch(output_dir: OutputDirectoryBuilder) { |
| let actual = passing_run_with_single_suite( |
| &output_dir, |
| SuiteResult { |
| common: Cow::Owned(CommonResult { |
| name: "suite".to_string(), |
| artifact_dir: output_dir |
| .new_artifact_dir("artifacts-suite") |
| .expect("new artifact dir"), |
| outcome: Outcome::Passed.into(), |
| start_time: None, |
| duration_milliseconds: Some(128), |
| }), |
| summary_file_hint: Cow::Owned("summary.json".into()), |
| cases: vec![], |
| tags: Cow::Owned(vec![]), |
| }, |
| ); |
| |
| output_dir.save_summary(&actual).expect("save summary"); |
| assert_run_result( |
| output_dir.path(), |
| &ExpectedTestRun::new(Outcome::Passed) |
| .with_any_start_time() |
| .with_any_run_duration() |
| .with_suite(ExpectedSuite::new("suite", Outcome::Passed).with_run_duration(32)), |
| ); |
| } |
| |
| #[fixture::fixture(test_with_directory)] |
| #[test] |
| #[should_panic] |
| fn assert_suite_artifact_mismatch(output_dir: OutputDirectoryBuilder) { |
| let actual = passing_run_with_single_suite( |
| &output_dir, |
| SuiteResult { |
| common: Cow::Owned(CommonResult { |
| name: "suite".to_string(), |
| artifact_dir: output_dir |
| .new_artifact_dir("artifacts-suite") |
| .expect("new artifact dir"), |
| outcome: Outcome::Passed.into(), |
| start_time: None, |
| duration_milliseconds: Some(128), |
| }), |
| summary_file_hint: Cow::Owned("summary.json".into()), |
| cases: vec![], |
| tags: Cow::Owned(vec![]), |
| }, |
| ); |
| |
| output_dir.save_summary(&actual).expect("save summary"); |
| assert_run_result( |
| output_dir.path(), |
| &ExpectedTestRun::new(Outcome::Passed) |
| .with_any_start_time() |
| .with_any_run_duration() |
| .with_suite(ExpectedSuite::new("suite", Outcome::Passed).with_artifact( |
| ArtifactType::Stderr, |
| Option::<&str>::None, |
| "missing contents", |
| )), |
| ); |
| } |
| |
| #[fixture::fixture(test_with_directory)] |
| #[test] |
| #[should_panic(expected = "Found unexpected case")] |
| fn assert_suite_case_mismatch(output_dir: OutputDirectoryBuilder) { |
| let actual = passing_run_with_single_suite( |
| &output_dir, |
| SuiteResult { |
| common: Cow::Owned(CommonResult { |
| name: "suite".to_string(), |
| artifact_dir: output_dir |
| .new_artifact_dir("artifacts-suite") |
| .expect("new artifact dir"), |
| outcome: Outcome::Failed.into(), |
| start_time: None, |
| duration_milliseconds: None, |
| }), |
| summary_file_hint: Cow::Owned("summary.json".into()), |
| cases: vec![TestCaseResult { |
| common: Cow::Owned(CommonResult { |
| name: "case".to_string(), |
| artifact_dir: output_dir |
| .new_artifact_dir("artifacts-case") |
| .expect("new artifact dir"), |
| outcome: Outcome::Passed.into(), |
| start_time: None, |
| duration_milliseconds: None, |
| }), |
| }], |
| tags: Cow::Owned(vec![]), |
| }, |
| ); |
| |
| output_dir.save_summary(&actual).expect("save summary"); |
| assert_run_result( |
| output_dir.path(), |
| &ExpectedTestRun::new(Outcome::Passed).with_any_start_time().with_suite( |
| ExpectedSuite::new("suite", Outcome::Failed) |
| .with_case(ExpectedTestCase::new("wrong name", Outcome::Passed)), |
| ), |
| ); |
| } |
| |
| #[fixture::fixture(test_with_directory)] |
| #[test] |
| fn assert_artifacts_empty(output_dir: OutputDirectoryBuilder) { |
| let actual = TestRunResult { |
| common: Cow::Owned(CommonResult { |
| name: RUN_NAME.to_string(), |
| artifact_dir: output_dir.new_artifact_dir("artifacts").expect("new artifact dir"), |
| outcome: Outcome::Passed.into(), |
| start_time: None, |
| duration_milliseconds: None, |
| }), |
| suites: vec![], |
| }; |
| |
| output_dir.save_summary(&actual).expect("save summary"); |
| assert_run_result(output_dir.path(), &ExpectedTestRun::new(Outcome::Passed)); |
| } |
| |
| #[fixture::fixture(test_with_directory)] |
| #[test] |
| fn assert_artifacts_exact_content(output_dir: OutputDirectoryBuilder) { |
| let mut artifact_dir = output_dir.new_artifact_dir("artifacts").expect("new artifact dir"); |
| let mut artifact = |
| artifact_dir.new_artifact(ArtifactType::Stderr, "b.txt").expect("new artifact"); |
| write!(artifact, "hello").expect("write to artifact"); |
| let actual = TestRunResult { |
| common: Cow::Owned(CommonResult { |
| name: RUN_NAME.to_string(), |
| artifact_dir, |
| outcome: Outcome::Passed.into(), |
| start_time: None, |
| duration_milliseconds: None, |
| }), |
| suites: vec![], |
| }; |
| |
| output_dir.save_summary(&actual).expect("save summary"); |
| assert_run_result( |
| output_dir.path(), |
| &ExpectedTestRun::new(Outcome::Passed).with_artifact( |
| ArtifactType::Stderr, |
| Option::<&str>::None, |
| "hello", |
| ), |
| ); |
| } |
| |
| #[fixture::fixture(test_with_directory)] |
| #[test] |
| fn assert_artifacts_exact_content_exact_name(output_dir: OutputDirectoryBuilder) { |
| let mut artifact_dir = output_dir.new_artifact_dir("artifacts").expect("new artifact dir"); |
| let mut artifact = |
| artifact_dir.new_artifact(ArtifactType::Stderr, "b.txt").expect("new artifact"); |
| write!(artifact, "hello").expect("write to artifact"); |
| let actual = TestRunResult { |
| common: Cow::Owned(CommonResult { |
| name: RUN_NAME.to_string(), |
| artifact_dir, |
| outcome: Outcome::Passed.into(), |
| start_time: None, |
| duration_milliseconds: None, |
| }), |
| suites: vec![], |
| }; |
| |
| output_dir.save_summary(&actual).expect("save summary"); |
| assert_run_result( |
| output_dir.path(), |
| &ExpectedTestRun::new(Outcome::Passed).with_artifact( |
| ArtifactType::Stderr, |
| Some("b.txt"), |
| "hello", |
| ), |
| ); |
| } |
| |
| #[fixture::fixture(test_with_directory)] |
| #[test] |
| fn assert_artifacts_matching_content(output_dir: OutputDirectoryBuilder) { |
| let mut artifact_dir = output_dir.new_artifact_dir("artifacts").expect("new artifact dir"); |
| let mut artifact = |
| artifact_dir.new_artifact(ArtifactType::Stderr, "b.txt").expect("new artifact"); |
| write!(artifact, "hello").expect("write to artifact"); |
| let actual = TestRunResult { |
| common: Cow::Owned(CommonResult { |
| name: RUN_NAME.to_string(), |
| artifact_dir, |
| outcome: Outcome::Passed.into(), |
| start_time: None, |
| duration_milliseconds: None, |
| }), |
| suites: vec![], |
| }; |
| |
| output_dir.save_summary(&actual).expect("save summary"); |
| assert_run_result( |
| output_dir.path(), |
| &ExpectedTestRun::new(Outcome::Passed).with_matching_artifact( |
| ArtifactType::Stderr, |
| Some("b.txt"), |
| |content| assert_eq!(content, "hello"), |
| ), |
| ); |
| } |
| |
| #[fixture::fixture(test_with_directory)] |
| #[test] |
| fn assert_artifacts_moniker_specified(output_dir: OutputDirectoryBuilder) { |
| let mut artifact_dir = output_dir.new_artifact_dir("artifacts").expect("new artifact dir"); |
| let mut artifact = artifact_dir |
| .new_artifact( |
| ArtifactMetadata { |
| artifact_type: ArtifactType::Syslog.into(), |
| component_moniker: Some("moniker".into()), |
| }, |
| "b.txt", |
| ) |
| .expect("new artifact"); |
| write!(artifact, "hello").expect("write to artifact"); |
| let actual = TestRunResult { |
| common: Cow::Owned(CommonResult { |
| name: RUN_NAME.to_string(), |
| artifact_dir, |
| outcome: Outcome::Passed.into(), |
| start_time: None, |
| duration_milliseconds: None, |
| }), |
| suites: vec![], |
| }; |
| |
| output_dir.save_summary(&actual).expect("save summary"); |
| assert_run_result( |
| output_dir.path(), |
| &ExpectedTestRun::new(Outcome::Passed).with_artifact( |
| ArtifactMetadata { |
| artifact_type: ArtifactType::Syslog.into(), |
| component_moniker: Some("moniker".into()), |
| }, |
| Some("b.txt"), |
| "hello", |
| ), |
| ); |
| } |
| |
| #[fixture::fixture(test_with_directory)] |
| #[test] |
| fn assert_artifacts_directory_artifact(output_dir: OutputDirectoryBuilder) { |
| let mut artifact_dir = output_dir.new_artifact_dir("artifacts").expect("new artifact dir"); |
| let dir_artifact = |
| artifact_dir.new_directory_artifact(ArtifactType::Custom, "b").expect("new artifact"); |
| std::fs::write(dir_artifact.join("c.txt"), "hello c").unwrap(); |
| std::fs::write(dir_artifact.join("d.txt"), "hello d").unwrap(); |
| let actual = TestRunResult { |
| common: Cow::Owned(CommonResult { |
| name: RUN_NAME.to_string(), |
| artifact_dir, |
| outcome: Outcome::Passed.into(), |
| start_time: None, |
| duration_milliseconds: None, |
| }), |
| suites: vec![], |
| }; |
| |
| output_dir.save_summary(&actual).expect("save summary"); |
| assert_run_result( |
| output_dir.path(), |
| &ExpectedTestRun::new(Outcome::Passed).with_directory_artifact( |
| ArtifactType::Custom, |
| Some("b"), |
| ExpectedDirectory::new() |
| .with_file("c.txt", "hello c") |
| .with_matching_file("d.txt", |contents| assert_eq!(contents, "hello d")), |
| ), |
| ); |
| } |
| |
| #[fixture::fixture(test_with_directory)] |
| #[test] |
| #[should_panic(expected = "Artifacts for TEST RUN")] |
| fn assert_artifacts_missing(output_dir: OutputDirectoryBuilder) { |
| let artifact_dir = output_dir.new_artifact_dir("artifacts").expect("new artifact dir"); |
| let actual = TestRunResult { |
| common: Cow::Owned(CommonResult { |
| name: RUN_NAME.to_string(), |
| artifact_dir, |
| outcome: Outcome::Passed.into(), |
| start_time: None, |
| duration_milliseconds: None, |
| }), |
| suites: vec![], |
| }; |
| |
| output_dir.save_summary(&actual).expect("save summary"); |
| assert_run_result( |
| output_dir.path(), |
| &ExpectedTestRun::new(Outcome::Passed).with_artifact( |
| ArtifactType::Syslog, |
| Some("missing"), |
| "missing contents", |
| ), |
| ); |
| } |
| |
| #[fixture::fixture(test_with_directory)] |
| #[test] |
| #[should_panic(expected = "Artifacts for TEST RUN")] |
| fn assert_artifacts_extra_artifact(output_dir: OutputDirectoryBuilder) { |
| let mut artifact_dir = output_dir.new_artifact_dir("artifacts").expect("new artifact dir"); |
| let mut file_b = |
| artifact_dir.new_artifact(ArtifactType::Stderr, "b.txt").expect("create artifact"); |
| write!(file_b, "hello").unwrap(); |
| let mut file_c = |
| artifact_dir.new_artifact(ArtifactType::Stdout, "c.txt").expect("create artifact"); |
| write!(file_c, "hello").unwrap(); |
| drop(file_b); |
| drop(file_c); |
| let actual = TestRunResult { |
| common: Cow::Owned(CommonResult { |
| name: RUN_NAME.to_string(), |
| artifact_dir, |
| outcome: Outcome::Passed.into(), |
| start_time: None, |
| duration_milliseconds: None, |
| }), |
| suites: vec![], |
| }; |
| |
| output_dir.save_summary(&actual).expect("save summary"); |
| assert_run_result( |
| output_dir.path(), |
| &ExpectedTestRun::new(Outcome::Passed).with_artifact( |
| ArtifactType::Stderr, |
| "c.txt".into(), |
| "hello", |
| ), |
| ); |
| } |
| |
| #[fixture::fixture(test_with_directory)] |
| #[test] |
| #[should_panic] |
| fn assert_artifacts_content_not_equal(output_dir: OutputDirectoryBuilder) { |
| let mut artifact_dir = output_dir.new_artifact_dir("artifacts").expect("new artifact dir"); |
| let mut file_b = |
| artifact_dir.new_artifact(ArtifactType::Stderr, "b.txt").expect("create artifact"); |
| write!(file_b, "wrong content").unwrap(); |
| drop(file_b); |
| let actual = TestRunResult { |
| common: Cow::Owned(CommonResult { |
| name: RUN_NAME.to_string(), |
| artifact_dir, |
| outcome: Outcome::Passed.into(), |
| start_time: None, |
| duration_milliseconds: None, |
| }), |
| suites: vec![], |
| }; |
| |
| output_dir.save_summary(&actual).expect("save summary"); |
| assert_run_result( |
| output_dir.path(), |
| &ExpectedTestRun::new(Outcome::Passed).with_artifact( |
| ArtifactType::Syslog, |
| Option::<&str>::None, |
| "expected content", |
| ), |
| ); |
| } |
| |
| #[fixture::fixture(test_with_directory)] |
| #[test] |
| #[should_panic] |
| fn assert_artifacts_content_does_not_match(output_dir: OutputDirectoryBuilder) { |
| let mut artifact_dir = output_dir.new_artifact_dir("artifacts").expect("new artifact dir"); |
| let mut file_b = |
| artifact_dir.new_artifact(ArtifactType::Stderr, "b.txt").expect("create artifact"); |
| write!(file_b, "wrong content").unwrap(); |
| drop(file_b); |
| let actual = TestRunResult { |
| common: Cow::Owned(CommonResult { |
| name: RUN_NAME.to_string(), |
| artifact_dir, |
| outcome: Outcome::Passed.into(), |
| start_time: None, |
| duration_milliseconds: None, |
| }), |
| suites: vec![], |
| }; |
| |
| output_dir.save_summary(&actual).expect("save summary"); |
| assert_run_result( |
| output_dir.path(), |
| &ExpectedTestRun::new(Outcome::Passed).with_matching_artifact( |
| ArtifactType::Syslog, |
| Option::<&str>::None, |
| |content| assert_eq!(content, "expected content"), |
| ), |
| ); |
| } |
| |
| #[fixture::fixture(test_with_directory)] |
| #[test] |
| #[should_panic] |
| fn assert_artifacts_directory_mismatch(output_dir: OutputDirectoryBuilder) { |
| let mut artifact_dir = output_dir.new_artifact_dir("artifacts").expect("new artifact dir"); |
| let dir_artifact = |
| artifact_dir.new_directory_artifact(ArtifactType::Custom, "b").expect("new artifact"); |
| std::fs::write(dir_artifact.join("c.txt"), "unexpected file").unwrap(); |
| let actual = TestRunResult { |
| common: Cow::Owned(CommonResult { |
| name: RUN_NAME.to_string(), |
| artifact_dir, |
| outcome: Outcome::Passed.into(), |
| start_time: None, |
| duration_milliseconds: None, |
| }), |
| suites: vec![], |
| }; |
| |
| output_dir.save_summary(&actual).expect("save summary"); |
| assert_run_result( |
| output_dir.path(), |
| &ExpectedTestRun::new(Outcome::Passed).with_directory_artifact( |
| ArtifactType::Custom, |
| Option::<&str>::None, |
| ExpectedDirectory::new(), |
| ), |
| ); |
| } |
| |
| #[fixture::fixture(test_with_directory)] |
| #[test] |
| fn assert_artifacts_not_checked_if_unspecified(output_dir: OutputDirectoryBuilder) { |
| let mut artifact_dir = output_dir.new_artifact_dir("artifacts").expect("new artifact dir"); |
| let mut file_c = |
| artifact_dir.new_artifact(ArtifactType::Stderr, "c.txt").expect("create artifact"); |
| write!(file_c, "unexpected file").unwrap(); |
| drop(file_c); |
| let actual = TestRunResult { |
| common: Cow::Owned(CommonResult { |
| name: RUN_NAME.to_string(), |
| artifact_dir, |
| outcome: Outcome::Passed.into(), |
| start_time: None, |
| duration_milliseconds: None, |
| }), |
| suites: vec![], |
| }; |
| |
| output_dir.save_summary(&actual).expect("save summary"); |
| assert_run_result(output_dir.path(), &ExpectedTestRun::new(Outcome::Passed)); |
| } |
| } |