run_test_suite_lib/output/
mod.rs1use fidl_fuchsia_test_manager as ftest_manager;
6use std::borrow::Borrow;
7use std::io::{Error, Write};
8use std::marker::PhantomData;
9use std::path::Path;
10use test_list::TestTag;
11
12mod directory;
13mod directory_with_stdout;
14mod line;
15mod memory;
16mod mux;
17mod noop;
18mod shell;
19
20pub use directory::{DirectoryReporter, SchemaVersion};
21pub use directory_with_stdout::DirectoryWithStdoutReporter;
22use line::AnsiFilterReporter;
23pub use memory::{InMemoryArtifact, InMemoryDirectoryWriter, InMemoryReporter};
24pub use mux::MultiplexedReporter;
25pub use noop::NoopReporter;
26pub use shell::{ShellReporter, ShellWriterView};
27
28pub(crate) type DynArtifact = dyn 'static + Write + Send + Sync;
29pub(crate) type DynDirectoryArtifact = dyn 'static + DirectoryWrite + Send + Sync;
30pub(crate) type DynReporter = dyn 'static + Reporter + Send + Sync;
31
32pub struct EntityReporter<E, T: Borrow<DynReporter>> {
33 reporter: T,
34 entity: EntityId,
35 _entity_type: std::marker::PhantomData<E>,
36}
37
38pub type RunReporter = EntityReporter<(), Box<DynReporter>>;
48pub type SuiteReporter<'a> = EntityReporter<(), &'a DynReporter>;
51
52pub type CaseReporter<'a> = EntityReporter<CaseId, &'a DynReporter>;
55
56impl<E, T: Borrow<DynReporter>> EntityReporter<E, T> {
57 pub fn new_artifact(&self, artifact_type: &ArtifactType) -> Result<Box<DynArtifact>, Error> {
59 self.reporter.borrow().new_artifact(&self.entity, artifact_type)
60 }
61
62 pub fn new_directory_artifact(
64 &self,
65 artifact_type: &DirectoryArtifactType,
66 component_moniker: Option<String>,
67 ) -> Result<Box<DynDirectoryArtifact>, Error> {
68 self.reporter.borrow().new_directory_artifact(
69 &self.entity,
70 artifact_type,
71 component_moniker,
72 )
73 }
74
75 pub fn started(&self, timestamp: Timestamp) -> Result<(), Error> {
77 self.reporter.borrow().entity_started(&self.entity, timestamp)
78 }
79
80 pub fn stopped(&self, outcome: &ReportedOutcome, timestamp: Timestamp) -> Result<(), Error> {
82 self.reporter.borrow().entity_stopped(&self.entity, outcome, timestamp)
83 }
84 pub fn finished(self) -> Result<(), Error> {
86 self.reporter.borrow().entity_finished(&self.entity)
87 }
88}
89
90impl RunReporter {
91 pub fn new_ansi_filtered<R: 'static + Reporter + Send + Sync>(reporter: R) -> Self {
94 Self::new(AnsiFilterReporter::new(reporter))
95 }
96
97 pub fn new<R: 'static + Reporter + Send + Sync>(reporter: R) -> Self {
99 let reporter = Box::new(reporter);
100 Self { reporter, entity: EntityId::TestRun, _entity_type: PhantomData }
101 }
102
103 pub fn set_expected_suites(&self, expected_suites: u32) {
105 self.reporter.set_entity_info(
106 &self.entity,
107 &EntityInfo { expected_children: Some(expected_suites), ..EntityInfo::default() },
108 )
109 }
110
111 pub fn new_suite(&self, url: &str) -> Result<SuiteReporter<'_>, Error> {
113 let entity = EntityId::Suite;
114 self.reporter.new_entity(&entity, url)?;
115 Ok(SuiteReporter { reporter: self.reporter.borrow(), entity, _entity_type: PhantomData })
116 }
117}
118
119impl<'a> SuiteReporter<'a> {
120 pub fn new_case(&self, name: &str, case_id: &CaseId) -> Result<CaseReporter<'_>, Error> {
122 let entity = EntityId::Case { case: *case_id };
123 self.reporter.new_entity(&entity, name)?;
124 Ok(CaseReporter { reporter: self.reporter, entity, _entity_type: PhantomData })
125 }
126
127 pub fn set_tags(&self, tags: Vec<TestTag>) {
129 self.reporter.set_entity_info(
130 &self.entity,
131 &EntityInfo { tags: Some(tags), ..EntityInfo::default() },
132 );
133 }
134}
135
136#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
138pub enum ArtifactType {
139 Stdout,
140 Stderr,
141 Syslog,
142 RestrictedLog,
143}
144
145#[derive(Clone, Copy, Debug)]
147pub enum DirectoryArtifactType {
148 Custom,
150 Debug,
152}
153
154#[derive(Clone, Copy, PartialEq, Debug)]
156pub enum ReportedOutcome {
157 Passed,
158 Failed,
159 Inconclusive,
160 Timedout,
161 Error,
162 Skipped,
163 Cancelled,
164 DidNotFinish,
165}
166
167impl std::fmt::Display for ReportedOutcome {
168 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
169 let repr = match self {
170 Self::Passed => "PASSED",
171 Self::Failed => "FAILED",
172 Self::Inconclusive => "INCONCLUSIVE",
173 Self::Timedout => "TIMED_OUT",
174 Self::Error => "ERROR",
175 Self::Skipped => "SKIPPED",
176 Self::Cancelled => "CANCELLED",
177 Self::DidNotFinish => "DID_NOT_FINISH",
178 };
179 write!(f, "{}", repr)
180 }
181}
182
183impl From<ftest_manager::TestCaseResult> for ReportedOutcome {
184 fn from(status: ftest_manager::TestCaseResult) -> Self {
185 match status {
186 ftest_manager::TestCaseResult::Passed => Self::Passed,
187 ftest_manager::TestCaseResult::Failed => Self::Failed,
188 ftest_manager::TestCaseResult::TimedOut => Self::Timedout,
189 ftest_manager::TestCaseResult::Skipped => Self::Skipped,
190 ftest_manager::TestCaseResult::Error => Self::DidNotFinish,
192 ftest_manager::TestCaseResultUnknown!() => {
193 panic!("unrecognized case status");
194 }
195 }
196 }
197}
198
199impl From<crate::Outcome> for ReportedOutcome {
200 fn from(outcome: crate::Outcome) -> Self {
201 match outcome {
202 crate::Outcome::Passed => Self::Passed,
203 crate::Outcome::Failed => Self::Failed,
204 crate::Outcome::Inconclusive => Self::Inconclusive,
205 crate::Outcome::Cancelled => Self::Cancelled,
206 crate::Outcome::DidNotFinish => Self::DidNotFinish,
207 crate::Outcome::Timedout => Self::Timedout,
208 crate::Outcome::Error { .. } => Self::Error,
209 }
210 }
211}
212
213impl Into<test_output_directory::ArtifactType> for ArtifactType {
214 fn into(self) -> test_output_directory::ArtifactType {
215 match self {
216 Self::Stdout => test_output_directory::ArtifactType::Stdout,
217 Self::Stderr => test_output_directory::ArtifactType::Stderr,
218 Self::Syslog => test_output_directory::ArtifactType::Syslog,
219 Self::RestrictedLog => test_output_directory::ArtifactType::RestrictedLog,
220 }
221 }
222}
223
224impl Into<test_output_directory::ArtifactType> for DirectoryArtifactType {
225 fn into(self) -> test_output_directory::ArtifactType {
226 match self {
227 Self::Custom => test_output_directory::ArtifactType::Custom,
228 Self::Debug => test_output_directory::ArtifactType::Debug,
229 }
230 }
231}
232
233#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
234pub struct CaseId(pub u32);
235
236#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
237pub enum EntityId {
238 TestRun,
239 Suite,
240 Case { case: CaseId },
241}
242
243#[derive(Default)]
244pub struct EntityInfo {
245 pub expected_children: Option<u32>,
246 pub tags: Option<Vec<TestTag>>,
247}
248
249pub trait Reporter: Send + Sync {
256 fn new_entity(&self, entity: &EntityId, name: &str) -> Result<(), Error>;
258
259 fn set_entity_info(&self, entity: &EntityId, info: &EntityInfo);
261
262 fn entity_started(&self, entity: &EntityId, timestamp: Timestamp) -> Result<(), Error>;
264
265 fn entity_stopped(
267 &self,
268 entity: &EntityId,
269 outcome: &ReportedOutcome,
270 timestamp: Timestamp,
271 ) -> Result<(), Error>;
272
273 fn entity_finished(&self, entity: &EntityId) -> Result<(), Error>;
278
279 fn new_artifact(
281 &self,
282 entity: &EntityId,
283 artifact_type: &ArtifactType,
284 ) -> Result<Box<DynArtifact>, Error>;
285
286 fn new_directory_artifact(
288 &self,
289 entity: &EntityId,
290 artifact_type: &DirectoryArtifactType,
291 component_moniker: Option<String>,
292 ) -> Result<Box<DynDirectoryArtifact>, Error>;
293}
294
295pub trait DirectoryWrite {
297 fn new_file(&self, path: &Path) -> Result<Box<DynArtifact>, Error>;
300}
301
302#[derive(Clone, Copy)]
305pub struct ZxTime(i64);
306
307impl ZxTime {
308 pub const fn from_nanos(nanos: i64) -> Self {
309 ZxTime(nanos)
310 }
311
312 pub fn checked_sub(&self, rhs: Self) -> Option<std::time::Duration> {
313 let nanos = self.0 - rhs.0;
314 if nanos < 0 {
315 None
316 } else {
317 Some(std::time::Duration::from_nanos(nanos as u64))
318 }
319 }
320}
321
322#[derive(Clone, Copy)]
323pub enum Timestamp {
324 Unknown,
325 Given(ZxTime),
326}
327
328impl Timestamp {
329 pub fn from_nanos(nanos: Option<i64>) -> Self {
330 match nanos {
331 None => Self::Unknown,
332 Some(n) => Self::Given(ZxTime::from_nanos(n)),
333 }
334 }
335}