run_test_suite_lib/output/
directory_with_stdout.rs1use crate::output::directory::{DirectoryReporter, SchemaVersion};
6use crate::output::mux::{MultiplexedDirectoryWriter, MultiplexedWriter};
7use crate::output::shell::ShellReporter;
8use crate::output::{
9 ArtifactType, DirectoryArtifactType, DynArtifact, DynDirectoryArtifact, EntityId, EntityInfo,
10 ReportedOutcome, Reporter, Timestamp,
11};
12use fuchsia_sync::Mutex;
13use std::fs::File;
14use std::io::{BufWriter, Error};
15use std::path::PathBuf;
16
17pub struct DirectoryWithStdoutReporter {
22 directory_reporter: DirectoryReporter,
23 shell_reporter: Mutex<Option<ShellReporter<BufWriter<File>>>>,
26}
27
28impl DirectoryWithStdoutReporter {
29 pub fn new(root: PathBuf, version: SchemaVersion) -> Result<Self, Error> {
30 Ok(Self {
31 directory_reporter: DirectoryReporter::new(root, version)?,
32 shell_reporter: Mutex::<Option<ShellReporter<BufWriter<File>>>>::new(None),
33 })
34 }
35}
36
37impl Reporter for DirectoryWithStdoutReporter {
38 fn new_entity(&self, entity: &EntityId, name: &str) -> Result<(), Error> {
39 self.directory_reporter.new_entity(entity, name)?;
40
41 match entity {
42 EntityId::Suite => {
43 let human_readable_artifact = self.directory_reporter.add_report(entity)?;
44 let shell_reporter = ShellReporter::new(human_readable_artifact);
45 shell_reporter.new_entity(entity, name)?;
46 let _ = self.shell_reporter.lock().insert(shell_reporter);
47 Ok(())
48 }
49 EntityId::Case { .. } => {
50 self.shell_reporter.lock().as_ref().unwrap().new_entity(entity, name)
51 }
52 EntityId::TestRun => Ok(()),
53 }
54 }
55
56 fn set_entity_info(&self, entity: &EntityId, info: &EntityInfo) {
57 self.directory_reporter.set_entity_info(entity, info);
58
59 match entity {
60 EntityId::Suite | EntityId::Case { .. } => {
61 self.shell_reporter.lock().as_ref().unwrap().set_entity_info(entity, info);
62 }
63 _ => (),
64 };
65 }
66
67 fn entity_started(&self, entity: &EntityId, timestamp: Timestamp) -> Result<(), Error> {
68 self.directory_reporter.entity_started(entity, timestamp)?;
69
70 match entity {
71 EntityId::Suite => {
72 let reporter_guard = self.shell_reporter.lock();
73 let _ = (*reporter_guard)
76 .as_ref()
77 .unwrap()
78 .entity_started(&EntityId::TestRun, timestamp);
79 (*reporter_guard).as_ref().unwrap().entity_started(entity, timestamp)
80 }
81 EntityId::Case { .. } => {
82 self.shell_reporter.lock().as_ref().unwrap().entity_started(entity, timestamp)
83 }
84 EntityId::TestRun => Ok(()),
85 }
86 }
87
88 fn entity_stopped(
89 &self,
90 entity: &EntityId,
91 outcome: &ReportedOutcome,
92 timestamp: Timestamp,
93 ) -> Result<(), Error> {
94 self.directory_reporter.entity_stopped(entity, outcome, timestamp)?;
95
96 match entity {
97 EntityId::Suite => {
98 let reporter_guard = self.shell_reporter.lock();
99 (*reporter_guard).as_ref().unwrap().entity_stopped(entity, outcome, timestamp)?;
102 (*reporter_guard).as_ref().unwrap().entity_stopped(
103 &EntityId::TestRun,
104 outcome,
105 timestamp,
106 )
107 }
108 EntityId::Case { .. } => self
109 .shell_reporter
110 .lock()
111 .as_ref()
112 .unwrap()
113 .entity_stopped(entity, outcome, timestamp),
114 EntityId::TestRun => Ok(()),
115 }
116 }
117
118 fn entity_finished(&self, entity: &EntityId) -> Result<(), Error> {
119 self.directory_reporter.entity_finished(entity)?;
120
121 match entity {
122 EntityId::Suite => {
123 let reporter_guard = self.shell_reporter.lock();
124 (*reporter_guard).as_ref().unwrap().entity_finished(entity)?;
127 (*reporter_guard).as_ref().unwrap().entity_finished(&EntityId::TestRun)
128 }
129 EntityId::Case { .. } => {
130 self.shell_reporter.lock().as_ref().unwrap().entity_finished(entity)
131 }
132 EntityId::TestRun => Ok(()),
133 }
134 }
135
136 fn new_artifact(
137 &self,
138 entity: &EntityId,
139 artifact_type: &ArtifactType,
140 ) -> Result<Box<DynArtifact>, Error> {
141 let shell_reporter_artifact = match entity {
142 EntityId::Suite | EntityId::Case { .. } => Some(
143 self.shell_reporter.lock().as_ref().unwrap().new_artifact(entity, artifact_type)?,
144 ),
145 EntityId::TestRun => None,
146 };
147 let directory_reporter_artifact =
148 self.directory_reporter.new_artifact(entity, artifact_type)?;
149 match shell_reporter_artifact {
150 Some(artifact) => {
151 Ok(Box::new(MultiplexedWriter::new(artifact, directory_reporter_artifact)))
152 }
153 None => Ok(directory_reporter_artifact),
154 }
155 }
156
157 fn new_directory_artifact(
158 &self,
159 entity: &EntityId,
160 artifact_type: &DirectoryArtifactType,
161 component_moniker: Option<String>,
162 ) -> Result<Box<DynDirectoryArtifact>, Error> {
163 let component_moniker_clone = component_moniker.clone();
164 let shell_reporter_artifact = match entity {
165 EntityId::Suite | EntityId::Case { .. } => {
166 Some(self.shell_reporter.lock().as_ref().unwrap().new_directory_artifact(
167 entity,
168 artifact_type,
169 component_moniker_clone,
170 )?)
171 }
172 EntityId::TestRun => None,
173 };
174 let directory_reporter_artifact = self.directory_reporter.new_directory_artifact(
175 entity,
176 artifact_type,
177 component_moniker,
178 )?;
179 match shell_reporter_artifact {
180 Some(artifact) => {
181 Ok(Box::new(MultiplexedDirectoryWriter::new(artifact, directory_reporter_artifact)))
182 }
183 None => Ok(directory_reporter_artifact),
184 }
185 }
186}
187
188#[cfg(test)]
189mod test {
190 use super::*;
191 use crate::output::{CaseId, RunReporter};
192 use tempfile::tempdir;
193 use test_output_directory as directory;
194 use test_output_directory::testing::{
195 assert_run_result, ExpectedSuite, ExpectedTestCase, ExpectedTestRun,
196 };
197
198 #[fuchsia::test]
204 async fn directory_with_stdout() {
205 let dir = tempdir().expect("create temp directory");
206 let run_reporter = RunReporter::new(
207 DirectoryWithStdoutReporter::new(dir.path().to_path_buf(), SchemaVersion::V1).unwrap(),
208 );
209
210 let suite_reporter = run_reporter.new_suite(&format!("test-suite")).expect("create suite");
211 suite_reporter.started(Timestamp::Unknown).expect("start suite");
212 let case_reporter =
213 suite_reporter.new_case("test-case", &CaseId(0)).expect("create test case");
214 case_reporter.started(Timestamp::Unknown).expect("start case");
215 let mut case_stdout =
216 case_reporter.new_artifact(&ArtifactType::Stdout).expect("create stdout");
217 writeln!(case_stdout, "Stdout for test case").expect("write to stdout");
218 case_stdout.flush().expect("flush stdout");
219 case_reporter.stopped(&ReportedOutcome::Passed, Timestamp::Unknown).expect("stop case");
220 case_reporter.finished().expect("finish case");
221 suite_reporter.stopped(&ReportedOutcome::Passed, Timestamp::Unknown).expect("stop suite");
222 suite_reporter.finished().expect("finish suite");
223
224 run_reporter.stopped(&ReportedOutcome::Passed, Timestamp::Unknown).expect("stop run");
225 run_reporter.finished().expect("finish run");
226
227 let mut expected_test = ExpectedTestRun::new(directory::Outcome::Passed);
228
229 let expected_report = format!(
230 "Running test 'test-suite'\n\
231 [RUNNING]\ttest-case\n\
232 [stdout - test-case]\n\
233 Stdout for test case\n\
234 [PASSED]\ttest-case\n\
235 \n\
236 1 out of 1 tests passed...\n\
237 test-suite completed with result: PASSED\n",
238 );
239 let suite = ExpectedSuite::new(format!("test-suite"), directory::Outcome::Passed)
240 .with_artifact(directory::ArtifactType::Report, "report.txt".into(), &expected_report)
241 .with_case(
242 ExpectedTestCase::new("test-case", directory::Outcome::Passed).with_artifact(
243 directory::ArtifactType::Stdout,
244 "stdout.txt".into(),
245 "Stdout for test case\n",
246 ),
247 );
248 expected_test = expected_test.with_suite(suite);
249
250 assert_run_result(dir.path(), &expected_test);
251 }
252}