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