1use crate::{
6 ArtifactMetadata, ArtifactType, MaybeUnknown, Outcome, SuiteResult, TestCaseResult,
7 TestRunResult,
8};
9use std::collections::{HashMap, HashSet};
10use std::ops::Deref;
11use std::path::{Path, PathBuf};
12use test_list::TestTag;
13
14enum MatchOption<T> {
15 AnyOrNone,
16 None,
17 Any,
18 Specified(T),
19}
20
21macro_rules! assert_match_option {
22 ($expected:expr, $actual:expr, $field:expr) => {
23 match $expected {
24 MatchOption::AnyOrNone => (),
25 MatchOption::None => {
26 assert_eq!(None, $actual, "Expected {} to be None but was {:?}", $field, $actual)
27 }
28 MatchOption::Any => {
29 assert!($actual.is_some(), "Expected {} to contain a value but was None", $field)
30 }
31 MatchOption::Specified(val) => assert_eq!(
32 Some(val),
33 $actual,
34 "Expected {} to be {:?} but was {:?}",
35 $field,
36 Some(val),
37 $actual
38 ),
39 }
40 };
41}
42
43#[derive(Clone, Copy)]
45enum EntityContext<'a> {
46 Run,
47 Suite(&'a ExpectedSuite),
48 Case(&'a ExpectedSuite, &'a ExpectedTestCase),
49}
50
51#[derive(Clone, Copy)]
53struct ArtifactContext<'a, 'b> {
54 entity: &'a EntityContext<'b>,
55 metadata: &'a ArtifactMetadata,
56}
57
58impl std::fmt::Display for EntityContext<'_> {
59 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
60 match self {
61 Self::Run => write!(f, "TEST RUN"),
62 Self::Suite(suite) => write!(f, "SUITE {}", suite.name),
63 Self::Case(suite, case) => write!(f, "SUITE {}: CASE {}", suite.name, case.name),
64 }
65 }
66}
67
68impl std::fmt::Display for ArtifactContext<'_, '_> {
69 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
70 write!(f, "Entity: {}, Metadata: {:?}", self.entity, self.metadata)
71 }
72}
73
74type ArtifactMetadataToAssertionMap = HashMap<ArtifactMetadata, ExpectedArtifact>;
76
77pub fn assert_run_result(root: &Path, expected_run: &ExpectedTestRun) {
80 let context = EntityContext::Run;
81 let actual_run = TestRunResult::from_dir(root).expect("Parse output directory");
82 let TestRunResult { common, suites } = actual_run;
83 assert_match_option!(
84 expected_run.duration_milliseconds,
85 common.deref().duration_milliseconds,
86 format!("Run duration for {}", context)
87 );
88 assert_match_option!(
89 expected_run.start_time,
90 common.deref().start_time,
91 format!("Start time for {}", context)
92 );
93 assert_eq!(common.deref().outcome, expected_run.outcome, "Outcome for {}", context);
94 assert_artifacts(
95 root,
96 &common.deref().artifact_dir.root,
97 &common.deref().artifact_dir.artifacts,
98 &expected_run.artifacts,
99 EntityContext::Run,
100 );
101 assert_suite_results(root, &suites, &expected_run.suites);
102}
103
104fn assert_suite_results(
108 root: &Path,
109 actual_suites: &Vec<SuiteResult<'_>>,
110 expected_suites: &Vec<ExpectedSuite>,
111) {
112 assert_eq!(actual_suites.len(), expected_suites.len());
113 let mut expected_suites_map = HashMap::new();
114 for suite in expected_suites.iter() {
115 expected_suites_map.insert(suite.name.clone(), suite);
116 }
117 assert_eq!(
118 actual_suites.len(),
119 expected_suites_map.len(),
120 "Run contains multiple suites with the same name. \
121 This is currently unsupported by assert_suite_results"
122 );
123 for suite in actual_suites.iter() {
124 assert_suite_result(
125 root,
126 suite,
127 expected_suites_map
128 .get(&suite.common.deref().name)
129 .expect("No matching expected suite"),
130 );
131 }
132}
133
134pub fn assert_suite_result(
137 root: &Path,
138 actual_suite: &SuiteResult<'_>,
139 expected_suite: &ExpectedSuite,
140) {
141 let context = EntityContext::Suite(expected_suite);
142 let &SuiteResult { common, cases, tags } = &actual_suite;
143 assert_eq!(common.deref().outcome, expected_suite.outcome, "Outcome for {}", context);
144 assert_eq!(common.deref().name, expected_suite.name, "Name for {}", context);
145 assert_match_option!(
146 expected_suite.duration_milliseconds,
147 common.deref().duration_milliseconds,
148 format!("Duration for {}", context)
149 );
150 assert_match_option!(
151 expected_suite.start_time,
152 common.deref().start_time,
153 format!("Start time for {}", context)
154 );
155
156 let mut tags: Vec<TestTag> = tags.clone().into_owned();
157 tags.sort();
158
159 let mut expected_tags = expected_suite.tags.clone();
160 expected_tags.sort();
161
162 assert_eq!(tags, expected_tags);
163
164 let filtered_artifacts: HashMap<PathBuf, ArtifactMetadata> = common
171 .deref()
172 .artifact_dir
173 .artifacts
174 .clone()
175 .into_iter()
176 .filter(|(_, value)| value.artifact_type != ArtifactType::Debug.into())
177 .collect();
178
179 assert_artifacts(
180 root,
181 &common.deref().artifact_dir.root,
182 &filtered_artifacts,
183 &expected_suite.artifacts,
184 context,
185 );
186
187 assert_eq!(cases.len(), expected_suite.cases.len());
188 for case in cases.iter() {
189 let expected_case = expected_suite.cases.get(&case.common.deref().name);
190 assert!(
191 expected_case.is_some(),
192 "Found unexpected case {} in {}",
193 case.common.deref().name,
194 context
195 );
196 assert_case_result(root, case, expected_case.unwrap(), expected_suite);
197 }
198}
199
200fn assert_case_result(
201 root: &Path,
202 actual_case: &TestCaseResult<'_>,
203 expected_case: &ExpectedTestCase,
204 parent_suite: &ExpectedSuite,
205) {
206 let context = EntityContext::Case(parent_suite, expected_case);
207 assert_eq!(actual_case.common.deref().name, expected_case.name, "Name for {}", context);
208 assert_eq!(
209 actual_case.common.deref().outcome,
210 expected_case.outcome,
211 "Outcome for {}",
212 context
213 );
214 assert_match_option!(
215 expected_case.duration_milliseconds,
216 actual_case.common.deref().duration_milliseconds,
217 format!("Duration for {}", context)
218 );
219 assert_match_option!(
220 expected_case.start_time,
221 actual_case.common.deref().start_time,
222 format!("Start time for {}", context)
223 );
224 assert_artifacts(
225 root,
226 &actual_case.common.deref().artifact_dir.root,
227 &actual_case.common.deref().artifact_dir.artifacts,
228 &expected_case.artifacts,
229 context,
230 );
231}
232
233fn assert_artifacts(
234 root: &Path,
235 artifact_dir: &Path,
236 actual_artifacts: &HashMap<PathBuf, ArtifactMetadata>,
237 expected_artifacts: &ArtifactMetadataToAssertionMap,
238 entity_context: EntityContext<'_>,
239) {
240 if expected_artifacts.is_empty() {
247 return;
248 }
249
250 let actual_artifacts_by_metadata: HashMap<ArtifactMetadata, PathBuf> =
251 actual_artifacts.iter().map(|(key, value)| (value.clone(), key.clone())).collect();
252 assert_eq!(
254 actual_artifacts_by_metadata.len(),
255 actual_artifacts.len(),
256 "Artifacts for {} do not have unique metadata. Actual artifacts: {:?}",
257 entity_context,
258 actual_artifacts
259 );
260
261 let expected_metadata: HashSet<_> = expected_artifacts.keys().collect();
262 let actual_metadata: HashSet<_> = actual_artifacts_by_metadata.keys().collect();
263
264 assert_eq!(
265 expected_metadata, actual_metadata,
266 "Artifacts for {} do not have matching metadata.",
267 entity_context,
268 );
269
270 for (expected_metadata, expected_artifact) in expected_artifacts.iter() {
271 let actual_filepath =
272 artifact_dir.join(actual_artifacts_by_metadata.get(expected_metadata).unwrap());
273 match expected_artifact {
274 ExpectedArtifact::File { name, assertion_fn } => {
275 assert_file(
276 &root.join(&actual_filepath),
277 name,
278 assertion_fn,
279 ArtifactContext { entity: &entity_context, metadata: expected_metadata },
280 );
281 }
282 ExpectedArtifact::Directory { files, name } => {
283 match name {
284 None => (),
285 Some(name) => assert_eq!(
286 name.as_str(),
287 actual_filepath.file_name().unwrap().to_str().unwrap(),
288 "Expected filename {} for artifact matching {:?} but got {}",
289 name,
290 expected_metadata,
291 actual_filepath.file_name().unwrap().to_str().unwrap()
292 ),
293 }
294 let actual_entries: HashSet<_> = std::fs::read_dir(root.join(&actual_filepath))
295 .expect("Failed to read directory artifact path")
296 .map(|entry| match entry {
297 Ok(dir_entry) if dir_entry.file_type().unwrap().is_file() => {
298 dir_entry.file_name().to_str().unwrap().to_string()
299 }
300 Ok(_) => panic!("Directory artifact with subdirectories unsupported"),
302 Err(e) => panic!("Error reading directory artifact: {:?}", e),
303 })
304 .collect();
305 let expected_entries: HashSet<_> =
306 files.iter().map(|(name, _)| name.to_string()).collect();
307 assert_eq!(
308 actual_entries, expected_entries,
309 "Expected files {:?} in directory artifact, got {:?}",
310 &expected_entries, &actual_entries
311 );
312 for (name, assertion) in files {
313 assert_file(
314 &root.join(&actual_filepath).join(name),
315 &None,
316 assertion,
317 ArtifactContext { entity: &entity_context, metadata: expected_metadata },
318 );
319 }
320 }
321 }
322 }
323}
324
325fn assert_file(
326 file_path: &Path,
327 name: &Option<String>,
328 assertion_fn: &Box<dyn Fn(&str)>,
329 artifact_context: ArtifactContext<'_, '_>,
330) {
331 match name {
332 None => (),
333 Some(name) => assert_eq!(
334 name.as_str(),
335 file_path.file_name().unwrap().to_str().unwrap(),
336 "Got incorrect filename while checking file for artifact {}",
337 artifact_context
338 ),
339 }
340 let actual_contents = std::fs::read_to_string(&file_path);
341 (assertion_fn)(&actual_contents.unwrap());
342}
343
344enum ExpectedArtifact {
346 File {
348 name: Option<String>,
350 assertion_fn: Box<dyn Fn(&str)>,
352 },
353 Directory {
355 files: Vec<(String, Box<dyn Fn(&str)>)>,
359 name: Option<String>,
361 },
362}
363
364pub struct ExpectedDirectory {
366 files: Vec<(String, Box<dyn Fn(&str)>)>,
367}
368
369impl ExpectedDirectory {
370 pub fn new() -> Self {
372 Self { files: vec![] }
373 }
374
375 pub fn with_file(self, name: impl AsRef<str>, contents: impl AsRef<str>) -> Self {
377 let owned_expected = contents.as_ref().to_string();
378 let owned_name = name.as_ref().to_string();
379 self.with_matching_file(name, move |actual| {
380 assert_eq!(
381 &owned_expected, actual,
382 "Mismatch in contents of file {}. Expected: '{}', actual:'{}'",
383 owned_name, &owned_expected, actual
384 )
385 })
386 }
387
388 pub fn with_matching_file(
389 mut self,
390 name: impl AsRef<str>,
391 matcher: impl 'static + Fn(&str),
392 ) -> Self {
393 self.files.push((name.as_ref().to_string(), Box::new(matcher)));
394 self
395 }
396}
397
398pub struct ExpectedTestRun {
401 artifacts: ArtifactMetadataToAssertionMap,
402 outcome: MaybeUnknown<Outcome>,
403 start_time: MatchOption<u64>,
404 duration_milliseconds: MatchOption<u64>,
405 suites: Vec<ExpectedSuite>,
406}
407
408pub struct ExpectedSuite {
411 artifacts: ArtifactMetadataToAssertionMap,
412 name: String,
413 outcome: MaybeUnknown<Outcome>,
414 cases: HashMap<String, ExpectedTestCase>,
415 start_time: MatchOption<u64>,
416 duration_milliseconds: MatchOption<u64>,
417 tags: Vec<TestTag>,
418}
419
420pub struct ExpectedTestCase {
423 artifacts: ArtifactMetadataToAssertionMap,
424 name: String,
425 outcome: MaybeUnknown<Outcome>,
426 start_time: MatchOption<u64>,
427 duration_milliseconds: MatchOption<u64>,
428}
429
430macro_rules! common_impl {
431 {} => {
432 pub fn with_artifact<S, T, U>(
437 self, metadata: U, name: Option<S>, contents: T
438 ) -> Self
439 where
440 S: AsRef<str>,
441 T: AsRef<str>,
442 U: Into<ArtifactMetadata>
443 {
444 let owned_expected = contents.as_ref().to_string();
445 let metadata = metadata.into();
446 let metadata_clone = metadata.clone();
447 self.with_matching_artifact(metadata, name, move |actual| {
448 assert_eq!(
449 &owned_expected, actual,
450 "Mismatch in artifact with metadata {:?}. Expected: '{}', actual:'{}'",
451 metadata_clone, &owned_expected, actual
452 )
453 })
454 }
455
456 pub fn with_matching_artifact<S, F, U>(
461 mut self,
462 metadata: U,
463 name: Option<S>,
464 matcher: F,
465 ) -> Self
466 where
467 S: AsRef<str>,
468 F: 'static + Fn(&str),
469 U: Into<ArtifactMetadata>
470 {
471 self.artifacts.insert(
472 metadata.into(),
473 ExpectedArtifact::File {
474 name: name.map(|s| s.as_ref().to_string()),
475 assertion_fn: Box::new(matcher),
476 }
477 );
478 self
479 }
480
481 pub fn with_directory_artifact<S, U>(
483 mut self,
484 metadata: U,
485 name: Option<S>,
486 directory: ExpectedDirectory,
487 ) -> Self
488 where
489 S: AsRef<str>,
490 U: Into<ArtifactMetadata>
491 {
492 self.artifacts.insert(
493 metadata.into(),
494 ExpectedArtifact::Directory {
495 name: name.map(|s| s.as_ref().to_string()),
496 files: directory.files,
497 }
498 );
499 self
500 }
501
502 pub fn with_start_time(mut self, millis: u64) -> Self {
504 self.start_time = MatchOption::Specified(millis);
505 self
506 }
507
508 pub fn with_run_duration(mut self, millis: u64) -> Self {
510 self.duration_milliseconds = MatchOption::Specified(millis);
511 self
512 }
513
514 pub fn with_any_start_time(mut self) -> Self {
516 self.start_time = MatchOption::Any;
517 self
518 }
519
520 pub fn with_any_run_duration(mut self) -> Self {
522 self.duration_milliseconds = MatchOption::Any;
523 self
524 }
525
526 pub fn with_no_start_time(mut self) -> Self {
528 self.start_time = MatchOption::None;
529 self
530 }
531
532 pub fn with_no_run_duration(mut self) -> Self {
534 self.duration_milliseconds = MatchOption::None;
535 self
536 }
537 };
538}
539
540impl ExpectedTestRun {
541 pub fn new(outcome: Outcome) -> Self {
543 Self {
544 artifacts: ArtifactMetadataToAssertionMap::new(),
545 outcome: outcome.into(),
546 start_time: MatchOption::AnyOrNone,
547 duration_milliseconds: MatchOption::AnyOrNone,
548 suites: vec![],
549 }
550 }
551
552 pub fn with_suite(mut self, suite: ExpectedSuite) -> Self {
553 self.suites.push(suite);
554 self
555 }
556
557 common_impl! {}
558}
559
560impl ExpectedSuite {
561 pub fn new<S: AsRef<str>>(name: S, outcome: Outcome) -> Self {
563 Self {
564 artifacts: ArtifactMetadataToAssertionMap::new(),
565 name: name.as_ref().to_string(),
566 outcome: outcome.into(),
567 cases: HashMap::new(),
568 start_time: MatchOption::AnyOrNone,
569 duration_milliseconds: MatchOption::AnyOrNone,
570 tags: vec![],
571 }
572 }
573
574 pub fn with_case(mut self, case: ExpectedTestCase) -> Self {
576 self.cases.insert(case.name.clone(), case);
577 self
578 }
579
580 pub fn with_tag(mut self, tag: TestTag) -> Self {
582 self.tags.push(tag);
583 self
584 }
585
586 common_impl! {}
587}
588
589impl ExpectedTestCase {
590 pub fn new<S: AsRef<str>>(name: S, outcome: Outcome) -> Self {
592 Self {
593 artifacts: ArtifactMetadataToAssertionMap::new(),
594 name: name.as_ref().to_string(),
595 outcome: outcome.into(),
596 start_time: MatchOption::AnyOrNone,
597 duration_milliseconds: MatchOption::AnyOrNone,
598 }
599 }
600
601 common_impl! {}
602}
603
604#[cfg(test)]
605mod test {
606 use super::*;
607 use crate::{ArtifactType, CommonResult, OutputDirectoryBuilder, SchemaVersion, RUN_NAME};
608 use std::borrow::Cow;
609 use std::io::Write;
610
611 fn test_with_directory<F: Fn(OutputDirectoryBuilder)>(_test_name: &str, test_fn: F) {
612 for version in SchemaVersion::all_variants() {
613 let dir = tempfile::TempDir::new().unwrap();
614 let directory_builder =
615 OutputDirectoryBuilder::new(dir.path(), version).expect("Create directory builder");
616 test_fn(directory_builder);
617 }
618 }
619
620 #[fixture::fixture(test_with_directory)]
621 #[test]
622 fn assert_run_result_check_outcome_only(output_dir: OutputDirectoryBuilder) {
623 let actual = TestRunResult {
624 common: Cow::Owned(CommonResult {
625 name: RUN_NAME.to_string(),
626 artifact_dir: output_dir.new_artifact_dir().expect("new artifact dir"),
627 outcome: Outcome::Passed.into(),
628 start_time: Some(64),
629 duration_milliseconds: Some(128),
630 }),
631 suites: vec![],
632 };
633
634 output_dir.save_summary(&actual).expect("save summary");
635 assert_run_result(
636 output_dir.path(),
637 &ExpectedTestRun::new(Outcome::Passed).with_any_start_time().with_any_run_duration(),
638 );
639 }
640
641 #[fixture::fixture(test_with_directory)]
642 #[test]
643 fn assert_run_result_check_exact_timing(output_dir: OutputDirectoryBuilder) {
644 let actual = TestRunResult {
645 common: Cow::Owned(CommonResult {
646 name: RUN_NAME.to_string(),
647 artifact_dir: output_dir.new_artifact_dir().expect("new artifact dir"),
648 outcome: Outcome::Passed.into(),
649 start_time: Some(64),
650 duration_milliseconds: Some(128),
651 }),
652 suites: vec![],
653 };
654
655 output_dir.save_summary(&actual).expect("save summary");
656 assert_run_result(
657 output_dir.path(),
658 &ExpectedTestRun::new(Outcome::Passed).with_start_time(64).with_run_duration(128),
659 );
660 }
661
662 #[fixture::fixture(test_with_directory)]
663 #[test]
664 fn assert_run_result_check_timing_unspecified(output_dir: OutputDirectoryBuilder) {
665 let actual = TestRunResult {
666 common: Cow::Owned(CommonResult {
667 name: RUN_NAME.to_string(),
668 artifact_dir: output_dir.new_artifact_dir().expect("new artifact dir"),
669 outcome: Outcome::Passed.into(),
670 start_time: None,
671 duration_milliseconds: None,
672 }),
673 suites: vec![],
674 };
675
676 output_dir.save_summary(&actual).expect("save summary");
677 assert_run_result(
678 output_dir.path(),
679 &ExpectedTestRun::new(Outcome::Passed).with_no_start_time().with_no_run_duration(),
680 );
681 }
682
683 #[fixture::fixture(test_with_directory)]
684 #[test]
685 fn assert_run_result_single_artifact_unspecified_name(output_dir: OutputDirectoryBuilder) {
686 let mut artifact_dir = output_dir.new_artifact_dir().expect("new artifact dir");
687 let mut artifact =
688 artifact_dir.new_artifact(ArtifactType::Syslog, "b.txt").expect("create artifact");
689 write!(artifact, "hello").expect("write to artifact");
690 drop(artifact);
691
692 let actual = TestRunResult {
693 common: Cow::Owned(CommonResult {
694 name: RUN_NAME.to_string(),
695 artifact_dir,
696 outcome: Outcome::Passed.into(),
697 start_time: None,
698 duration_milliseconds: None,
699 }),
700 suites: vec![],
701 };
702
703 output_dir.save_summary(&actual).expect("save summary");
704 assert_run_result(
705 output_dir.path(),
706 &ExpectedTestRun::new(Outcome::Passed).with_artifact(
707 ArtifactType::Syslog,
708 Option::<&str>::None,
709 "hello",
710 ),
711 );
712 }
713
714 #[fixture::fixture(test_with_directory)]
715 #[test]
716 fn assert_run_result_single_artifact_specified_name(output_dir: OutputDirectoryBuilder) {
717 let mut artifact_dir = output_dir.new_artifact_dir().expect("new artifact dir");
718 let mut artifact =
719 artifact_dir.new_artifact(ArtifactType::Syslog, "b.txt").expect("create artifact");
720 write!(artifact, "hello").expect("write to artifact");
721 drop(artifact);
722
723 let actual = TestRunResult {
724 common: Cow::Owned(CommonResult {
725 name: RUN_NAME.to_string(),
726 artifact_dir,
727 outcome: Outcome::Passed.into(),
728 start_time: None,
729 duration_milliseconds: None,
730 }),
731 suites: vec![],
732 };
733
734 output_dir.save_summary(&actual).expect("save summary");
735 assert_run_result(
736 output_dir.path(),
737 &ExpectedTestRun::new(Outcome::Passed).with_artifact(
738 ArtifactType::Syslog,
739 "b.txt".into(),
740 "hello",
741 ),
742 );
743 }
744
745 #[fixture::fixture(test_with_directory)]
746 #[test]
747 #[should_panic(expected = "Outcome for TEST RUN")]
748 fn assert_run_outcome_mismatch(output_dir: OutputDirectoryBuilder) {
749 let actual = TestRunResult {
750 common: Cow::Owned(CommonResult {
751 name: RUN_NAME.to_string(),
752 artifact_dir: output_dir.new_artifact_dir().expect("new artifact dir"),
753 outcome: Outcome::Failed.into(),
754 start_time: None,
755 duration_milliseconds: None,
756 }),
757 suites: vec![],
758 };
759
760 output_dir.save_summary(&actual).expect("save summary");
761 assert_run_result(output_dir.path(), &ExpectedTestRun::new(Outcome::Passed));
762 }
763
764 #[fixture::fixture(test_with_directory)]
765 #[test]
766 #[should_panic(expected = "Start time for TEST RUN")]
767 fn assert_run_start_time_mismatch(output_dir: OutputDirectoryBuilder) {
768 let actual = TestRunResult {
769 common: Cow::Owned(CommonResult {
770 name: RUN_NAME.to_string(),
771 artifact_dir: output_dir.new_artifact_dir().expect("new artifact dir"),
772 outcome: Outcome::Failed.into(),
773 start_time: Some(64),
774 duration_milliseconds: None,
775 }),
776 suites: vec![],
777 };
778
779 output_dir.save_summary(&actual).expect("save summary");
780 assert_run_result(
781 output_dir.path(),
782 &ExpectedTestRun::new(Outcome::Passed).with_start_time(23),
783 );
784 }
785
786 #[fixture::fixture(test_with_directory)]
787 #[test]
788 #[should_panic(expected = "Run duration for TEST RUN")]
789 fn assert_run_duration_mismatch(output_dir: OutputDirectoryBuilder) {
790 let actual = TestRunResult {
791 common: Cow::Owned(CommonResult {
792 name: RUN_NAME.to_string(),
793 artifact_dir: output_dir.new_artifact_dir().expect("new artifact dir"),
794 outcome: Outcome::Failed.into(),
795 start_time: None,
796 duration_milliseconds: None,
797 }),
798 suites: vec![],
799 };
800
801 output_dir.save_summary(&actual).expect("save summary");
802 assert_run_result(
803 output_dir.path(),
804 &ExpectedTestRun::new(Outcome::Passed).with_run_duration(23),
805 );
806 }
807
808 #[fixture::fixture(test_with_directory)]
809 #[test]
810 #[should_panic]
811 fn assert_run_artifact_mismatch(output_dir: OutputDirectoryBuilder) {
812 let mut artifact_dir = output_dir.new_artifact_dir().expect("new artifact dir");
813 let mut artifact =
814 artifact_dir.new_artifact(ArtifactType::Syslog, "missing").expect("create artifact");
815 write!(artifact, "hello").expect("write to artifact");
816 drop(artifact);
817
818 let actual = TestRunResult {
819 common: Cow::Owned(CommonResult {
820 name: RUN_NAME.to_string(),
821 artifact_dir,
822 outcome: Outcome::Failed.into(),
823 start_time: None,
824 duration_milliseconds: None,
825 }),
826 suites: vec![],
827 };
828
829 output_dir.save_summary(&actual).expect("save summary");
830 assert_run_result(
831 output_dir.path(),
832 &ExpectedTestRun::new(Outcome::Failed).with_artifact(
833 ArtifactType::Stderr,
834 "stderr.txt".into(),
835 "",
836 ),
837 );
838 }
839
840 fn passing_run_with_single_suite<'a>(
841 output_dir: &OutputDirectoryBuilder,
842 suite: SuiteResult<'a>,
843 ) -> TestRunResult<'a> {
844 TestRunResult {
845 common: Cow::Owned(CommonResult {
846 name: RUN_NAME.to_string(),
847 artifact_dir: output_dir.new_artifact_dir().expect("new artifact dir"),
848 outcome: Outcome::Passed.into(),
849 start_time: Some(64),
850 duration_milliseconds: Some(128),
851 }),
852 suites: vec![suite],
853 }
854 }
855
856 #[fixture::fixture(test_with_directory)]
857 #[test]
858 fn assert_run_result_with_suite(output_dir: OutputDirectoryBuilder) {
859 let actual = passing_run_with_single_suite(
860 &output_dir,
861 SuiteResult {
862 common: Cow::Owned(CommonResult {
863 name: "suite".to_string(),
864 artifact_dir: output_dir.new_artifact_dir().expect("new artifact dir"),
865 outcome: Outcome::Passed.into(),
866 start_time: Some(64),
867 duration_milliseconds: Some(128),
868 }),
869 cases: vec![],
870 tags: Cow::Owned(vec![]),
871 },
872 );
873
874 output_dir.save_summary(&actual).expect("save summary");
875 assert_run_result(
876 output_dir.path(),
877 &ExpectedTestRun::new(Outcome::Passed)
878 .with_any_start_time()
879 .with_any_run_duration()
880 .with_suite(
881 ExpectedSuite::new("suite", Outcome::Passed)
882 .with_any_start_time()
883 .with_any_run_duration(),
884 ),
885 );
886 }
887
888 #[fixture::fixture(test_with_directory)]
889 #[test]
890 fn assert_run_result_with_suite_exact_times(output_dir: OutputDirectoryBuilder) {
891 let actual = passing_run_with_single_suite(
892 &output_dir,
893 SuiteResult {
894 common: Cow::Owned(CommonResult {
895 name: "suite".to_string(),
896 artifact_dir: output_dir.new_artifact_dir().expect("new artifact dir"),
897 outcome: Outcome::Passed.into(),
898 start_time: Some(64),
899 duration_milliseconds: Some(128),
900 }),
901 cases: vec![],
902 tags: Cow::Owned(vec![]),
903 },
904 );
905
906 output_dir.save_summary(&actual).expect("save summary");
907 assert_run_result(
908 output_dir.path(),
909 &ExpectedTestRun::new(Outcome::Passed)
910 .with_any_start_time()
911 .with_any_run_duration()
912 .with_suite(
913 ExpectedSuite::new("suite", Outcome::Passed)
914 .with_start_time(64)
915 .with_run_duration(128),
916 ),
917 );
918 }
919
920 #[fixture::fixture(test_with_directory)]
921 #[test]
922 fn assert_run_result_with_suite_no_times(output_dir: OutputDirectoryBuilder) {
923 let actual = passing_run_with_single_suite(
924 &output_dir,
925 SuiteResult {
926 common: Cow::Owned(CommonResult {
927 name: "suite".to_string(),
928 artifact_dir: output_dir.new_artifact_dir().expect("new artifact dir"),
929 outcome: Outcome::Passed.into(),
930 start_time: None,
931 duration_milliseconds: None,
932 }),
933 cases: vec![],
934 tags: Cow::Owned(vec![]),
935 },
936 );
937
938 output_dir.save_summary(&actual).expect("save summary");
939 assert_run_result(
940 output_dir.path(),
941 &ExpectedTestRun::new(Outcome::Passed)
942 .with_any_start_time()
943 .with_any_run_duration()
944 .with_suite(
945 ExpectedSuite::new("suite", Outcome::Passed)
946 .with_no_start_time()
947 .with_no_run_duration(),
948 ),
949 );
950 }
951
952 #[fixture::fixture(test_with_directory)]
953 #[test]
954 fn assert_run_result_suite_with_artifact(output_dir: OutputDirectoryBuilder) {
955 let mut artifact_dir = output_dir.new_artifact_dir().expect("new artifact dir");
956 let mut artifact =
957 artifact_dir.new_artifact(ArtifactType::Syslog, "b.txt").expect("create artifact");
958 write!(artifact, "hello").expect("write to artifact");
959 drop(artifact);
960
961 let actual = passing_run_with_single_suite(
962 &output_dir,
963 SuiteResult {
964 common: Cow::Owned(CommonResult {
965 name: "suite".to_string(),
966 artifact_dir,
967 outcome: Outcome::Passed.into(),
968 start_time: None,
969 duration_milliseconds: None,
970 }),
971 cases: vec![],
972 tags: Cow::Owned(vec![]),
973 },
974 );
975
976 output_dir.save_summary(&actual).expect("save summary");
977 assert_run_result(
978 output_dir.path(),
979 &ExpectedTestRun::new(Outcome::Passed)
980 .with_any_start_time()
981 .with_any_run_duration()
982 .with_suite(ExpectedSuite::new("suite", Outcome::Passed).with_artifact(
983 ArtifactType::Syslog,
984 "b.txt".into(),
985 "hello",
986 )),
987 );
988 }
989
990 #[fixture::fixture(test_with_directory)]
991 #[test]
992 fn assert_run_result_suite_with_case(output_dir: OutputDirectoryBuilder) {
993 let actual = passing_run_with_single_suite(
994 &output_dir,
995 SuiteResult {
996 common: Cow::Owned(CommonResult {
997 name: "suite".to_string(),
998 artifact_dir: output_dir.new_artifact_dir().expect("new artifact dir"),
999 outcome: Outcome::Passed.into(),
1000 start_time: None,
1001 duration_milliseconds: None,
1002 }),
1003 cases: vec![TestCaseResult {
1004 common: Cow::Owned(CommonResult {
1005 name: "case".to_string(),
1006 artifact_dir: output_dir.new_artifact_dir().expect("new artifact dir"),
1007 outcome: Outcome::Passed.into(),
1008 start_time: None,
1009 duration_milliseconds: None,
1010 }),
1011 }],
1012 tags: Cow::Owned(vec![]),
1013 },
1014 );
1015
1016 output_dir.save_summary(&actual).expect("save summary");
1017 assert_run_result(
1018 output_dir.path(),
1019 &ExpectedTestRun::new(Outcome::Passed)
1020 .with_any_start_time()
1021 .with_any_run_duration()
1022 .with_suite(
1023 ExpectedSuite::new("suite", Outcome::Passed).with_case(
1024 ExpectedTestCase::new("case", Outcome::Passed)
1025 .with_no_run_duration()
1026 .with_no_start_time(),
1027 ),
1028 ),
1029 );
1030 }
1031
1032 #[fixture::fixture(test_with_directory)]
1033 #[test]
1034 fn assert_run_result_suite_with_tags(output_dir: OutputDirectoryBuilder) {
1035 let actual = passing_run_with_single_suite(
1036 &output_dir,
1037 SuiteResult {
1038 common: Cow::Owned(CommonResult {
1039 name: "suite".to_string(),
1040 artifact_dir: output_dir.new_artifact_dir().expect("new artifact dir"),
1041 outcome: Outcome::Passed.into(),
1042 start_time: None,
1043 duration_milliseconds: None,
1044 }),
1045 cases: vec![],
1046 tags: Cow::Owned(vec![
1047 TestTag { key: "os".to_string(), value: "fuchsia".to_string() },
1048 TestTag { key: "cpu".to_string(), value: "arm64".to_string() },
1049 ]),
1050 },
1051 );
1052
1053 output_dir.save_summary(&actual).expect("save summary");
1054 assert_run_result(
1055 output_dir.path(),
1056 &ExpectedTestRun::new(Outcome::Passed)
1057 .with_any_start_time()
1058 .with_any_run_duration()
1059 .with_suite(
1060 ExpectedSuite::new("suite", Outcome::Passed)
1061 .with_tag(TestTag { key: "cpu".to_string(), value: "arm64".to_string() })
1062 .with_tag(TestTag { key: "os".to_string(), value: "fuchsia".to_string() }),
1063 ),
1064 );
1065 }
1066
1067 #[fixture::fixture(test_with_directory)]
1068 #[test]
1069 #[should_panic(expected = "Outcome for SUITE suite")]
1070 fn assert_suite_outcome_mismatch(output_dir: OutputDirectoryBuilder) {
1071 let actual = passing_run_with_single_suite(
1072 &output_dir,
1073 SuiteResult {
1074 common: Cow::Owned(CommonResult {
1075 name: "suite".to_string(),
1076 artifact_dir: output_dir.new_artifact_dir().expect("new artifact dir"),
1077 outcome: Outcome::Failed.into(),
1078 start_time: None,
1079 duration_milliseconds: None,
1080 }),
1081 cases: vec![],
1082 tags: Cow::Owned(vec![]),
1083 },
1084 );
1085
1086 output_dir.save_summary(&actual).expect("save summary");
1087 assert_run_result(
1088 output_dir.path(),
1089 &ExpectedTestRun::new(Outcome::Passed)
1090 .with_any_start_time()
1091 .with_any_run_duration()
1092 .with_suite(ExpectedSuite::new("suite", Outcome::Passed)),
1093 );
1094 }
1095
1096 #[fixture::fixture(test_with_directory)]
1097 #[test]
1098 #[should_panic(expected = "Start time for SUITE suite")]
1099 fn assert_suite_start_time_mismatch(output_dir: OutputDirectoryBuilder) {
1100 let actual = passing_run_with_single_suite(
1101 &output_dir,
1102 SuiteResult {
1103 common: Cow::Owned(CommonResult {
1104 name: "suite".to_string(),
1105 artifact_dir: output_dir.new_artifact_dir().expect("new artifact dir"),
1106 outcome: Outcome::Passed.into(),
1107 start_time: None,
1108 duration_milliseconds: Some(128),
1109 }),
1110 cases: vec![],
1111 tags: Cow::Owned(vec![]),
1112 },
1113 );
1114
1115 output_dir.save_summary(&actual).expect("save summary");
1116 assert_run_result(
1117 output_dir.path(),
1118 &ExpectedTestRun::new(Outcome::Passed)
1119 .with_any_start_time()
1120 .with_any_run_duration()
1121 .with_suite(ExpectedSuite::new("suite", Outcome::Passed).with_any_start_time()),
1122 );
1123 }
1124
1125 #[fixture::fixture(test_with_directory)]
1126 #[test]
1127 #[should_panic(expected = "Duration for SUITE suite")]
1128 fn assert_suite_duration_mismatch(output_dir: OutputDirectoryBuilder) {
1129 let actual = passing_run_with_single_suite(
1130 &output_dir,
1131 SuiteResult {
1132 common: Cow::Owned(CommonResult {
1133 name: "suite".to_string(),
1134 artifact_dir: output_dir.new_artifact_dir().expect("new artifact dir"),
1135 outcome: Outcome::Passed.into(),
1136 start_time: None,
1137 duration_milliseconds: Some(128),
1138 }),
1139 cases: vec![],
1140 tags: Cow::Owned(vec![]),
1141 },
1142 );
1143
1144 output_dir.save_summary(&actual).expect("save summary");
1145 assert_run_result(
1146 output_dir.path(),
1147 &ExpectedTestRun::new(Outcome::Passed)
1148 .with_any_start_time()
1149 .with_any_run_duration()
1150 .with_suite(ExpectedSuite::new("suite", Outcome::Passed).with_run_duration(32)),
1151 );
1152 }
1153
1154 #[fixture::fixture(test_with_directory)]
1155 #[test]
1156 #[should_panic]
1157 fn assert_suite_artifact_mismatch(output_dir: OutputDirectoryBuilder) {
1158 let actual = passing_run_with_single_suite(
1159 &output_dir,
1160 SuiteResult {
1161 common: Cow::Owned(CommonResult {
1162 name: "suite".to_string(),
1163 artifact_dir: output_dir.new_artifact_dir().expect("new artifact dir"),
1164 outcome: Outcome::Passed.into(),
1165 start_time: None,
1166 duration_milliseconds: Some(128),
1167 }),
1168 cases: vec![],
1169 tags: Cow::Owned(vec![]),
1170 },
1171 );
1172
1173 output_dir.save_summary(&actual).expect("save summary");
1174 assert_run_result(
1175 output_dir.path(),
1176 &ExpectedTestRun::new(Outcome::Passed)
1177 .with_any_start_time()
1178 .with_any_run_duration()
1179 .with_suite(ExpectedSuite::new("suite", Outcome::Passed).with_artifact(
1180 ArtifactType::Stderr,
1181 Option::<&str>::None,
1182 "missing contents",
1183 )),
1184 );
1185 }
1186
1187 #[fixture::fixture(test_with_directory)]
1188 #[test]
1189 #[should_panic(expected = "Found unexpected case")]
1190 fn assert_suite_case_mismatch(output_dir: OutputDirectoryBuilder) {
1191 let actual = passing_run_with_single_suite(
1192 &output_dir,
1193 SuiteResult {
1194 common: Cow::Owned(CommonResult {
1195 name: "suite".to_string(),
1196 artifact_dir: output_dir.new_artifact_dir().expect("new artifact dir"),
1197 outcome: Outcome::Failed.into(),
1198 start_time: None,
1199 duration_milliseconds: None,
1200 }),
1201 cases: vec![TestCaseResult {
1202 common: Cow::Owned(CommonResult {
1203 name: "case".to_string(),
1204 artifact_dir: output_dir.new_artifact_dir().expect("new artifact dir"),
1205 outcome: Outcome::Passed.into(),
1206 start_time: None,
1207 duration_milliseconds: None,
1208 }),
1209 }],
1210 tags: Cow::Owned(vec![]),
1211 },
1212 );
1213
1214 output_dir.save_summary(&actual).expect("save summary");
1215 assert_run_result(
1216 output_dir.path(),
1217 &ExpectedTestRun::new(Outcome::Passed).with_any_start_time().with_suite(
1218 ExpectedSuite::new("suite", Outcome::Failed)
1219 .with_case(ExpectedTestCase::new("wrong name", Outcome::Passed)),
1220 ),
1221 );
1222 }
1223
1224 #[fixture::fixture(test_with_directory)]
1225 #[test]
1226 fn assert_artifacts_empty(output_dir: OutputDirectoryBuilder) {
1227 let actual = TestRunResult {
1228 common: Cow::Owned(CommonResult {
1229 name: RUN_NAME.to_string(),
1230 artifact_dir: output_dir.new_artifact_dir().expect("new artifact dir"),
1231 outcome: Outcome::Passed.into(),
1232 start_time: None,
1233 duration_milliseconds: None,
1234 }),
1235 suites: vec![],
1236 };
1237
1238 output_dir.save_summary(&actual).expect("save summary");
1239 assert_run_result(output_dir.path(), &ExpectedTestRun::new(Outcome::Passed));
1240 }
1241
1242 #[fixture::fixture(test_with_directory)]
1243 #[test]
1244 fn assert_artifacts_exact_content(output_dir: OutputDirectoryBuilder) {
1245 let mut artifact_dir = output_dir.new_artifact_dir().expect("new artifact dir");
1246 let mut artifact =
1247 artifact_dir.new_artifact(ArtifactType::Stderr, "b.txt").expect("new artifact");
1248 write!(artifact, "hello").expect("write to artifact");
1249 let actual = TestRunResult {
1250 common: Cow::Owned(CommonResult {
1251 name: RUN_NAME.to_string(),
1252 artifact_dir,
1253 outcome: Outcome::Passed.into(),
1254 start_time: None,
1255 duration_milliseconds: None,
1256 }),
1257 suites: vec![],
1258 };
1259
1260 output_dir.save_summary(&actual).expect("save summary");
1261 assert_run_result(
1262 output_dir.path(),
1263 &ExpectedTestRun::new(Outcome::Passed).with_artifact(
1264 ArtifactType::Stderr,
1265 Option::<&str>::None,
1266 "hello",
1267 ),
1268 );
1269 }
1270
1271 #[fixture::fixture(test_with_directory)]
1272 #[test]
1273 fn assert_artifacts_exact_content_exact_name(output_dir: OutputDirectoryBuilder) {
1274 let mut artifact_dir = output_dir.new_artifact_dir().expect("new artifact dir");
1275 let mut artifact =
1276 artifact_dir.new_artifact(ArtifactType::Stderr, "b.txt").expect("new artifact");
1277 write!(artifact, "hello").expect("write to artifact");
1278 let actual = TestRunResult {
1279 common: Cow::Owned(CommonResult {
1280 name: RUN_NAME.to_string(),
1281 artifact_dir,
1282 outcome: Outcome::Passed.into(),
1283 start_time: None,
1284 duration_milliseconds: None,
1285 }),
1286 suites: vec![],
1287 };
1288
1289 output_dir.save_summary(&actual).expect("save summary");
1290 assert_run_result(
1291 output_dir.path(),
1292 &ExpectedTestRun::new(Outcome::Passed).with_artifact(
1293 ArtifactType::Stderr,
1294 Some("b.txt"),
1295 "hello",
1296 ),
1297 );
1298 }
1299
1300 #[fixture::fixture(test_with_directory)]
1301 #[test]
1302 fn assert_artifacts_matching_content(output_dir: OutputDirectoryBuilder) {
1303 let mut artifact_dir = output_dir.new_artifact_dir().expect("new artifact dir");
1304 let mut artifact =
1305 artifact_dir.new_artifact(ArtifactType::Stderr, "b.txt").expect("new artifact");
1306 write!(artifact, "hello").expect("write to artifact");
1307 let actual = TestRunResult {
1308 common: Cow::Owned(CommonResult {
1309 name: RUN_NAME.to_string(),
1310 artifact_dir,
1311 outcome: Outcome::Passed.into(),
1312 start_time: None,
1313 duration_milliseconds: None,
1314 }),
1315 suites: vec![],
1316 };
1317
1318 output_dir.save_summary(&actual).expect("save summary");
1319 assert_run_result(
1320 output_dir.path(),
1321 &ExpectedTestRun::new(Outcome::Passed).with_matching_artifact(
1322 ArtifactType::Stderr,
1323 Some("b.txt"),
1324 |content| assert_eq!(content, "hello"),
1325 ),
1326 );
1327 }
1328
1329 #[fixture::fixture(test_with_directory)]
1330 #[test]
1331 fn assert_artifacts_moniker_specified(output_dir: OutputDirectoryBuilder) {
1332 let mut artifact_dir = output_dir.new_artifact_dir().expect("new artifact dir");
1333 let mut artifact = artifact_dir
1334 .new_artifact(
1335 ArtifactMetadata {
1336 artifact_type: ArtifactType::Syslog.into(),
1337 component_moniker: Some("moniker".into()),
1338 },
1339 "b.txt",
1340 )
1341 .expect("new artifact");
1342 write!(artifact, "hello").expect("write to artifact");
1343 let actual = TestRunResult {
1344 common: Cow::Owned(CommonResult {
1345 name: RUN_NAME.to_string(),
1346 artifact_dir,
1347 outcome: Outcome::Passed.into(),
1348 start_time: None,
1349 duration_milliseconds: None,
1350 }),
1351 suites: vec![],
1352 };
1353
1354 output_dir.save_summary(&actual).expect("save summary");
1355 assert_run_result(
1356 output_dir.path(),
1357 &ExpectedTestRun::new(Outcome::Passed).with_artifact(
1358 ArtifactMetadata {
1359 artifact_type: ArtifactType::Syslog.into(),
1360 component_moniker: Some("moniker".into()),
1361 },
1362 Some("b.txt"),
1363 "hello",
1364 ),
1365 );
1366 }
1367
1368 #[fixture::fixture(test_with_directory)]
1369 #[test]
1370 fn assert_artifacts_directory_artifact(output_dir: OutputDirectoryBuilder) {
1371 let mut artifact_dir = output_dir.new_artifact_dir().expect("new artifact dir");
1372 let dir_artifact =
1373 artifact_dir.new_directory_artifact(ArtifactType::Custom, "b").expect("new artifact");
1374 std::fs::write(dir_artifact.join("c.txt"), "hello c").unwrap();
1375 std::fs::write(dir_artifact.join("d.txt"), "hello d").unwrap();
1376 let actual = TestRunResult {
1377 common: Cow::Owned(CommonResult {
1378 name: RUN_NAME.to_string(),
1379 artifact_dir,
1380 outcome: Outcome::Passed.into(),
1381 start_time: None,
1382 duration_milliseconds: None,
1383 }),
1384 suites: vec![],
1385 };
1386
1387 output_dir.save_summary(&actual).expect("save summary");
1388 assert_run_result(
1389 output_dir.path(),
1390 &ExpectedTestRun::new(Outcome::Passed).with_directory_artifact(
1391 ArtifactType::Custom,
1392 Some("b"),
1393 ExpectedDirectory::new()
1394 .with_file("c.txt", "hello c")
1395 .with_matching_file("d.txt", |contents| assert_eq!(contents, "hello d")),
1396 ),
1397 );
1398 }
1399
1400 #[fixture::fixture(test_with_directory)]
1401 #[test]
1402 #[should_panic(expected = "Artifacts for TEST RUN")]
1403 fn assert_artifacts_missing(output_dir: OutputDirectoryBuilder) {
1404 let artifact_dir = output_dir.new_artifact_dir().expect("new artifact dir");
1405 let actual = TestRunResult {
1406 common: Cow::Owned(CommonResult {
1407 name: RUN_NAME.to_string(),
1408 artifact_dir,
1409 outcome: Outcome::Passed.into(),
1410 start_time: None,
1411 duration_milliseconds: None,
1412 }),
1413 suites: vec![],
1414 };
1415
1416 output_dir.save_summary(&actual).expect("save summary");
1417 assert_run_result(
1418 output_dir.path(),
1419 &ExpectedTestRun::new(Outcome::Passed).with_artifact(
1420 ArtifactType::Syslog,
1421 Some("missing"),
1422 "missing contents",
1423 ),
1424 );
1425 }
1426
1427 #[fixture::fixture(test_with_directory)]
1428 #[test]
1429 #[should_panic(expected = "Artifacts for TEST RUN")]
1430 fn assert_artifacts_extra_artifact(output_dir: OutputDirectoryBuilder) {
1431 let mut artifact_dir = output_dir.new_artifact_dir().expect("new artifact dir");
1432 let mut file_b =
1433 artifact_dir.new_artifact(ArtifactType::Stderr, "b.txt").expect("create artifact");
1434 write!(file_b, "hello").unwrap();
1435 let mut file_c =
1436 artifact_dir.new_artifact(ArtifactType::Stdout, "c.txt").expect("create artifact");
1437 write!(file_c, "hello").unwrap();
1438 drop(file_b);
1439 drop(file_c);
1440 let actual = TestRunResult {
1441 common: Cow::Owned(CommonResult {
1442 name: RUN_NAME.to_string(),
1443 artifact_dir,
1444 outcome: Outcome::Passed.into(),
1445 start_time: None,
1446 duration_milliseconds: None,
1447 }),
1448 suites: vec![],
1449 };
1450
1451 output_dir.save_summary(&actual).expect("save summary");
1452 assert_run_result(
1453 output_dir.path(),
1454 &ExpectedTestRun::new(Outcome::Passed).with_artifact(
1455 ArtifactType::Stderr,
1456 "c.txt".into(),
1457 "hello",
1458 ),
1459 );
1460 }
1461
1462 #[fixture::fixture(test_with_directory)]
1463 #[test]
1464 #[should_panic]
1465 fn assert_artifacts_content_not_equal(output_dir: OutputDirectoryBuilder) {
1466 let mut artifact_dir = output_dir.new_artifact_dir().expect("new artifact dir");
1467 let mut file_b =
1468 artifact_dir.new_artifact(ArtifactType::Stderr, "b.txt").expect("create artifact");
1469 write!(file_b, "wrong content").unwrap();
1470 drop(file_b);
1471 let actual = TestRunResult {
1472 common: Cow::Owned(CommonResult {
1473 name: RUN_NAME.to_string(),
1474 artifact_dir,
1475 outcome: Outcome::Passed.into(),
1476 start_time: None,
1477 duration_milliseconds: None,
1478 }),
1479 suites: vec![],
1480 };
1481
1482 output_dir.save_summary(&actual).expect("save summary");
1483 assert_run_result(
1484 output_dir.path(),
1485 &ExpectedTestRun::new(Outcome::Passed).with_artifact(
1486 ArtifactType::Syslog,
1487 Option::<&str>::None,
1488 "expected content",
1489 ),
1490 );
1491 }
1492
1493 #[fixture::fixture(test_with_directory)]
1494 #[test]
1495 #[should_panic]
1496 fn assert_artifacts_content_does_not_match(output_dir: OutputDirectoryBuilder) {
1497 let mut artifact_dir = output_dir.new_artifact_dir().expect("new artifact dir");
1498 let mut file_b =
1499 artifact_dir.new_artifact(ArtifactType::Stderr, "b.txt").expect("create artifact");
1500 write!(file_b, "wrong content").unwrap();
1501 drop(file_b);
1502 let actual = TestRunResult {
1503 common: Cow::Owned(CommonResult {
1504 name: RUN_NAME.to_string(),
1505 artifact_dir,
1506 outcome: Outcome::Passed.into(),
1507 start_time: None,
1508 duration_milliseconds: None,
1509 }),
1510 suites: vec![],
1511 };
1512
1513 output_dir.save_summary(&actual).expect("save summary");
1514 assert_run_result(
1515 output_dir.path(),
1516 &ExpectedTestRun::new(Outcome::Passed).with_matching_artifact(
1517 ArtifactType::Syslog,
1518 Option::<&str>::None,
1519 |content| assert_eq!(content, "expected content"),
1520 ),
1521 );
1522 }
1523
1524 #[fixture::fixture(test_with_directory)]
1525 #[test]
1526 #[should_panic]
1527 fn assert_artifacts_directory_mismatch(output_dir: OutputDirectoryBuilder) {
1528 let mut artifact_dir = output_dir.new_artifact_dir().expect("new artifact dir");
1529 let dir_artifact =
1530 artifact_dir.new_directory_artifact(ArtifactType::Custom, "b").expect("new artifact");
1531 std::fs::write(dir_artifact.join("c.txt"), "unexpected file").unwrap();
1532 let actual = TestRunResult {
1533 common: Cow::Owned(CommonResult {
1534 name: RUN_NAME.to_string(),
1535 artifact_dir,
1536 outcome: Outcome::Passed.into(),
1537 start_time: None,
1538 duration_milliseconds: None,
1539 }),
1540 suites: vec![],
1541 };
1542
1543 output_dir.save_summary(&actual).expect("save summary");
1544 assert_run_result(
1545 output_dir.path(),
1546 &ExpectedTestRun::new(Outcome::Passed).with_directory_artifact(
1547 ArtifactType::Custom,
1548 Option::<&str>::None,
1549 ExpectedDirectory::new(),
1550 ),
1551 );
1552 }
1553
1554 #[fixture::fixture(test_with_directory)]
1555 #[test]
1556 fn assert_artifacts_not_checked_if_unspecified(output_dir: OutputDirectoryBuilder) {
1557 let mut artifact_dir = output_dir.new_artifact_dir().expect("new artifact dir");
1558 let mut file_c =
1559 artifact_dir.new_artifact(ArtifactType::Stderr, "c.txt").expect("create artifact");
1560 write!(file_c, "unexpected file").unwrap();
1561 drop(file_c);
1562 let actual = TestRunResult {
1563 common: Cow::Owned(CommonResult {
1564 name: RUN_NAME.to_string(),
1565 artifact_dir,
1566 outcome: Outcome::Passed.into(),
1567 start_time: None,
1568 duration_milliseconds: None,
1569 }),
1570 suites: vec![],
1571 };
1572
1573 output_dir.save_summary(&actual).expect("save summary");
1574 assert_run_result(output_dir.path(), &ExpectedTestRun::new(Outcome::Passed));
1575 }
1576}