1use crate::output::noop::NoopDirectoryWriter;
6use crate::output::{
7 ArtifactType, DirectoryArtifactType, DynArtifact, DynDirectoryArtifact, EntityId, EntityInfo,
8 ReportedOutcome, Reporter, Timestamp,
9};
10use fuchsia_async as fasync;
11use fuchsia_sync::Mutex;
12use log::error;
13use std::collections::HashMap;
14use std::io::{Error, Write};
15use std::sync::Arc;
16use std::sync::atomic::AtomicU32;
17use std::time::Duration;
18
19#[cfg(feature = "fdomain")]
20use test_diagnostics_fdomain as test_diagnostics;
21
22mod writer;
23pub use writer::ShellWriterView;
24use writer::{ShellWriterHandle, ShellWriterHandleInner};
25
26const EXCESSIVE_DURATION: Duration = Duration::from_secs(60);
28const STDIO_BUFFERING_DURATION: Duration = Duration::from_secs(5);
30const STDIO_BUFFER_SIZE: usize = 4096;
32
33pub struct ShellReporter<W: 'static + Write + Send + Sync> {
37 inner: Arc<Mutex<ShellWriterHandleInner<W>>>,
39 entity_state_map: Mutex<HashMap<EntityId, EntityState>>,
41 completed_suites: AtomicU32,
43 expected_suites: Mutex<Option<u32>>,
45 stdio_buffer_size: usize,
47 stdio_buffer_duration: Duration,
49}
50
51struct EntityState {
53 name: String,
54 excessive_duration_task: Option<fasync::Task<()>>,
55 children: Vec<EntityId>,
56 restricted_logs: Option<ShellWriterView<Vec<u8>>>,
57 run_state: EntityRunningState,
58}
59
60enum EntityRunningState {
61 NotRunning,
62 Started,
63 Finished(ReportedOutcome),
64}
65
66impl EntityState {
67 fn new<S: Into<String>>(name: S) -> Self {
68 Self {
69 name: name.into(),
70 excessive_duration_task: None,
71 children: vec![],
72 restricted_logs: None,
73 run_state: EntityRunningState::NotRunning,
74 }
75 }
76
77 fn name(&self) -> &str {
78 &self.name
79 }
80}
81
82impl ShellReporter<Vec<u8>> {
83 pub fn new_expose_writer_for_test() -> (Self, ShellWriterView<Vec<u8>>) {
84 let inner = Arc::new(Mutex::new(ShellWriterHandleInner::new(vec![])));
85 let mut entity_state_map = HashMap::new();
86 entity_state_map.insert(EntityId::TestRun, EntityState::new("TEST RUN"));
87 (
88 Self {
89 inner: inner.clone(),
90 entity_state_map: Mutex::new(entity_state_map),
91 completed_suites: AtomicU32::new(0),
92 expected_suites: Mutex::new(None),
93 stdio_buffer_duration: Duration::ZERO,
95 stdio_buffer_size: 0,
96 },
97 ShellWriterView::new(inner),
98 )
99 }
100}
101
102impl<W: 'static + Write + Send + Sync> ShellReporter<W> {
103 pub fn new(inner: W) -> Self {
104 let inner = Arc::new(Mutex::new(ShellWriterHandleInner::new(inner)));
105 let mut entity_state_map = HashMap::new();
106 entity_state_map.insert(EntityId::TestRun, EntityState::new("TEST RUN"));
107 Self {
108 inner,
109 entity_state_map: Mutex::new(entity_state_map),
110 completed_suites: AtomicU32::new(0),
111 expected_suites: Mutex::new(None),
112 stdio_buffer_duration: STDIO_BUFFERING_DURATION,
113 stdio_buffer_size: STDIO_BUFFER_SIZE,
114 }
115 }
116
117 fn new_writer_handle(&self, prefix: Option<String>) -> ShellWriterHandle<W> {
118 ShellWriterHandle::new_handle(Arc::clone(&self.inner), prefix)
119 }
120}
121
122impl<W: 'static + Write + Send + Sync> Reporter for ShellReporter<W> {
123 fn new_entity(&self, entity: &EntityId, name: &str) -> Result<(), Error> {
124 let mut map = self.entity_state_map.lock();
125 map.insert(entity.clone(), EntityState::new(name));
126 if let EntityId::Case { .. } = entity {
127 map.get_mut(&EntityId::Suite).unwrap().children.push(entity.clone());
128 }
129 Ok(())
130 }
131
132 fn set_entity_info(&self, entity: &EntityId, info: &EntityInfo) {
133 match (entity, info.expected_children) {
134 (EntityId::TestRun, Some(children)) => {
135 self.expected_suites.lock().replace(children);
136 }
137 (_, _) => (),
138 }
139 }
140
141 fn entity_started(&self, entity: &EntityId, _: Timestamp) -> Result<(), Error> {
142 let mut writer = self.new_writer_handle(None);
143 let mut entity_map_lock = self.entity_state_map.lock();
144 let entity_entry = entity_map_lock.get_mut(entity).unwrap();
145 entity_entry.run_state = EntityRunningState::Started;
146 let name = entity_entry.name().to_string();
147 match entity {
148 EntityId::TestRun => (),
149 EntityId::Suite => writeln!(writer, "Running test '{}'", name)?,
150 EntityId::Case { .. } => {
151 writeln!(writer, "[RUNNING]\t{}", name)?;
152 entity_entry.excessive_duration_task = Some(fasync::Task::spawn(async move {
153 fasync::Timer::new(EXCESSIVE_DURATION).await;
154 writeln!(
155 writer,
156 "[duration - {}]:\tStill running after {:?} seconds",
157 name,
158 EXCESSIVE_DURATION.as_secs()
159 )
160 .unwrap_or_else(|e| error!("Failed to write: {:?}", e));
161 }));
162 }
163 }
164 Ok(())
165 }
166
167 fn entity_stopped(
168 &self,
169 entity: &EntityId,
170 outcome: &ReportedOutcome,
171 _: Timestamp,
172 ) -> Result<(), Error> {
173 let mut writer = self.new_writer_handle(None);
174 let mut entity_map_lock = self.entity_state_map.lock();
175 entity_map_lock.get_mut(entity).unwrap().run_state = EntityRunningState::Finished(*outcome);
176 let entity_entry = entity_map_lock.get_mut(entity).unwrap();
177 let name = entity_entry.name().to_string();
178
179 let _ = entity_entry.excessive_duration_task.take();
181 match entity {
182 EntityId::TestRun => (),
183 EntityId::Suite => (),
184 EntityId::Case { .. } => {
185 if *outcome != ReportedOutcome::Error {
187 writeln!(writer, "[{}]\t{}", outcome, name)?;
188 }
189 }
190 }
191 Ok(())
192 }
193
194 fn entity_finished(&self, entity: &EntityId) -> Result<(), Error> {
195 let mut writer = self.new_writer_handle(None);
196 let entity_map_lock = self.entity_state_map.lock();
197 let entity_entry = entity_map_lock.get(entity).unwrap();
198 let name = entity_entry.name().to_string();
199 let outcome = match &entity_entry.run_state {
200 EntityRunningState::Finished(outcome) => *outcome,
201 _ => ReportedOutcome::Inconclusive,
202 };
203 let children: Vec<_> = entity_entry.children.iter().cloned().collect();
204 match entity {
205 EntityId::TestRun => (),
206 EntityId::Suite => {
207 if matches!(entity_entry.run_state, EntityRunningState::NotRunning) {
208 return Ok(());
210 }
211
212 let cases: Vec<_> =
213 children.iter().map(|child| entity_map_lock.get(child).unwrap()).collect();
214 let executed: Vec<_> = cases
215 .iter()
216 .filter(|case| match &case.run_state {
217 EntityRunningState::Started => true,
218 EntityRunningState::Finished(ReportedOutcome::Skipped) => false,
219 EntityRunningState::Finished(_) => true,
220 EntityRunningState::NotRunning => false,
221 })
222 .collect();
223 let mut failed: Vec<_> = cases
224 .iter()
225 .filter(|case| {
226 matches!(
227 &case.run_state,
228 EntityRunningState::Finished(
229 ReportedOutcome::Failed | ReportedOutcome::Timedout
230 )
231 )
232 })
233 .map(|case| case.name())
234 .collect();
235 failed.sort();
236 let mut not_finished: Vec<_> = cases
237 .iter()
238 .filter(|case| {
239 matches!(
240 &case.run_state,
241 EntityRunningState::Started
242 | EntityRunningState::Finished(ReportedOutcome::Error)
243 )
244 })
245 .map(|case| case.name())
246 .collect();
247 not_finished.sort();
248 let num_passed = cases
249 .iter()
250 .filter(|case| {
251 matches!(
252 &case.run_state,
253 EntityRunningState::Finished(ReportedOutcome::Passed)
254 )
255 })
256 .count();
257 let num_skipped = cases
258 .iter()
259 .filter(|case| {
260 matches!(
261 &case.run_state,
262 EntityRunningState::Finished(ReportedOutcome::Skipped)
263 )
264 })
265 .count();
266
267 let suite_number =
268 self.completed_suites.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
269 match &*self.expected_suites.lock() {
270 Some(total_suites) if *total_suites > 1 => writeln!(
271 writer,
272 "\nTest suite count {}/{}",
273 suite_number + 1,
274 total_suites,
275 )?,
276 Some(_) | None => (),
277 }
278 writeln!(writer)?;
279 if !failed.is_empty() {
280 writeln!(writer, "Failed tests: {}", failed.join(", "))?;
281 }
282 if !not_finished.is_empty() {
283 writeln!(writer, "\nThe following test(s) never completed:")?;
284 for t in not_finished {
285 writeln!(writer, "{}", t)?;
286 }
287 }
288 match num_skipped {
289 0 => writeln!(
290 writer,
291 "{} out of {} tests passed...",
292 num_passed,
293 executed.len()
294 )?,
295 skipped => writeln!(
296 writer,
297 "{} out of {} attempted tests passed, {} tests skipped...",
298 num_passed,
299 executed.len(),
300 skipped,
301 )?,
302 }
303 if let Some(restricted_logs) = &entity_entry.restricted_logs {
304 writeln!(writer, "\nTest {} produced unexpected high-severity logs:", &name)?;
305 writeln!(writer, "----------------xxxxx----------------")?;
306 writer.write_all(restricted_logs.lock().as_slice())?;
307 writeln!(writer, "\n----------------xxxxx----------------")?;
308 writeln!(
309 writer,
310 "Failing this test. See: https://fuchsia.dev/fuchsia-src/development/diagnostics/test_and_logs#restricting_log_severity\n"
311 )?;
312 }
313 match outcome {
314 ReportedOutcome::Cancelled => {
315 writeln!(writer, "{} was cancelled before completion.", &name)?
316 }
317 ReportedOutcome::DidNotFinish => {
318 writeln!(writer, "{} did not complete successfully.", &name)?
319 }
320 other => writeln!(writer, "{} completed with result: {}", &name, other)?,
321 }
322 }
323 EntityId::Case { .. } => (),
324 }
325 Ok(())
326 }
327
328 fn new_artifact(
329 &self,
330 entity: &EntityId,
331 artifact_type: &ArtifactType,
332 ) -> Result<Box<DynArtifact>, Error> {
333 let mut lock = self.entity_state_map.lock();
334 let entity = lock.get_mut(entity).unwrap();
335 let name = entity.name();
336
337 Ok(match artifact_type {
338 ArtifactType::Stdout => Box::new(test_diagnostics::StdoutBuffer::new(
339 self.stdio_buffer_duration,
340 self.new_writer_handle(Some(format!("[stdout - {}]\n", name))),
341 self.stdio_buffer_size,
342 )),
343 ArtifactType::Stderr => Box::new(test_diagnostics::StdoutBuffer::new(
344 self.stdio_buffer_duration,
345 self.new_writer_handle(Some(format!("[stderr - {}]\n", name))),
346 self.stdio_buffer_size,
347 )),
348 ArtifactType::Syslog => Box::new(self.new_writer_handle(None)),
349 ArtifactType::RestrictedLog => {
350 let log_buffer = Arc::new(Mutex::new(ShellWriterHandleInner::new(vec![])));
352 entity.restricted_logs = Some(ShellWriterView::new(log_buffer.clone()));
353 Box::new(ShellWriterHandle::new_handle(log_buffer, None))
354 }
355 })
356 }
357
358 fn new_directory_artifact(
359 &self,
360 _entity: &EntityId,
361 _artifact_type: &DirectoryArtifactType,
362 _component_moniker: Option<String>,
363 ) -> Result<Box<DynDirectoryArtifact>, Error> {
364 Ok(Box::new(NoopDirectoryWriter))
366 }
367}
368
369#[cfg(test)]
370mod test {
371 use super::*;
372 use crate::output::{CaseId, RunReporter};
373
374 #[fuchsia::test]
375 async fn report_case_events() {
376 let (shell_reporter, output) = ShellReporter::new_expose_writer_for_test();
377 let run_reporter = RunReporter::new(shell_reporter);
378 let suite_reporter = run_reporter.new_suite("test-suite").expect("create suite");
379 suite_reporter.started(Timestamp::Unknown).expect("case started");
380 let mut expected = "Running test 'test-suite'\n".to_string();
381 assert_eq!(String::from_utf8(output.lock().clone()).unwrap(), expected,);
382
383 let case_1_reporter = suite_reporter.new_case("case-1", &CaseId(0)).expect("create case");
384 let case_2_reporter = suite_reporter.new_case("case-2", &CaseId(1)).expect("create case");
385
386 case_1_reporter.started(Timestamp::Unknown).expect("case started");
387 expected.push_str("[RUNNING]\tcase-1\n");
388 assert_eq!(String::from_utf8(output.lock().clone()).unwrap(), expected,);
389
390 case_2_reporter.started(Timestamp::Unknown).expect("case started");
391 expected.push_str("[RUNNING]\tcase-2\n");
392 assert_eq!(String::from_utf8(output.lock().clone()).unwrap(), expected,);
393
394 case_1_reporter.stopped(&ReportedOutcome::Passed, Timestamp::Unknown).expect("stop case");
395 expected.push_str("[PASSED]\tcase-1\n");
396 assert_eq!(String::from_utf8(output.lock().clone()).unwrap(), expected,);
397
398 case_2_reporter.stopped(&ReportedOutcome::Failed, Timestamp::Unknown).expect("stop case");
399 expected.push_str("[FAILED]\tcase-2\n");
400 assert_eq!(String::from_utf8(output.lock().clone()).unwrap(), expected,);
401
402 case_1_reporter.finished().expect("finish case");
403 case_2_reporter.finished().expect("finish case");
404 suite_reporter.stopped(&ReportedOutcome::Failed, Timestamp::Unknown).expect("stop suite");
405 suite_reporter.finished().expect("finish suite");
406
407 expected.push_str("\n");
408 expected.push_str("Failed tests: case-2\n");
409 expected.push_str("1 out of 2 tests passed...\n");
410 expected.push_str("test-suite completed with result: FAILED\n");
411 assert_eq!(String::from_utf8(output.lock().clone()).unwrap(), expected,);
412 }
413
414 #[fuchsia::test]
415 async fn report_multiple_suites() {
416 const NUM_SUITES: u32 = 5;
417 let (shell_reporter, output) = ShellReporter::new_expose_writer_for_test();
418 let run_reporter = RunReporter::new(shell_reporter);
419 run_reporter.set_expected_suites(NUM_SUITES);
420 run_reporter.started(Timestamp::Unknown).expect("run started");
421 let mut expected = "".to_string();
422
423 for suite_number in 0..4 {
424 let suite_name = format!("test-suite-{:?}", suite_number);
425 let suite_reporter = run_reporter.new_suite(&suite_name).expect("create suite");
426 suite_reporter.started(Timestamp::Unknown).expect("case started");
427 expected.push_str(&format!("Running test '{}'\n", &suite_name));
428 assert_eq!(String::from_utf8(output.lock().clone()).unwrap(), expected,);
429
430 let case_reporter = suite_reporter.new_case("case-1", &CaseId(0)).expect("create case");
431 case_reporter.started(Timestamp::Unknown).expect("case started");
432 expected.push_str("[RUNNING]\tcase-1\n");
433 assert_eq!(String::from_utf8(output.lock().clone()).unwrap(), expected,);
434 case_reporter.stopped(&ReportedOutcome::Passed, Timestamp::Unknown).expect("stop case");
435 expected.push_str("[PASSED]\tcase-1\n");
436 assert_eq!(String::from_utf8(output.lock().clone()).unwrap(), expected,);
437
438 case_reporter.finished().expect("finish case");
439 suite_reporter
440 .stopped(&ReportedOutcome::Passed, Timestamp::Unknown)
441 .expect("stop suite");
442 suite_reporter.finished().expect("finish suite");
443
444 expected.push_str("\n");
445 expected.push_str(&format!(
446 "Test suite count {:?}/{:?}\n\n",
447 suite_number + 1,
448 NUM_SUITES
449 ));
450 expected.push_str("1 out of 1 tests passed...\n");
451 expected.push_str(&format!("{} completed with result: PASSED\n", suite_name));
452 assert_eq!(String::from_utf8(output.lock().clone()).unwrap(), expected,);
453 }
454 }
455
456 #[fuchsia::test]
457 async fn report_case_skipped() {
458 let (shell_reporter, output) = ShellReporter::new_expose_writer_for_test();
459 let run_reporter = RunReporter::new(shell_reporter);
460 let suite_reporter = run_reporter.new_suite("test-suite").expect("create suite");
461 suite_reporter.started(Timestamp::Unknown).expect("case started");
462 let mut expected = "Running test 'test-suite'\n".to_string();
463 assert_eq!(String::from_utf8(output.lock().clone()).unwrap(), expected,);
464
465 let case_1_reporter = suite_reporter.new_case("case-1", &CaseId(0)).expect("create case");
466 let case_2_reporter = suite_reporter.new_case("case-2", &CaseId(1)).expect("create case");
467
468 case_1_reporter.started(Timestamp::Unknown).expect("case started");
469 expected.push_str("[RUNNING]\tcase-1\n");
470 assert_eq!(String::from_utf8(output.lock().clone()).unwrap(), expected,);
471
472 case_1_reporter.stopped(&ReportedOutcome::Passed, Timestamp::Unknown).expect("stop case");
473 expected.push_str("[PASSED]\tcase-1\n");
474 assert_eq!(String::from_utf8(output.lock().clone()).unwrap(), expected,);
475
476 case_2_reporter.stopped(&ReportedOutcome::Skipped, Timestamp::Unknown).expect("stop case");
477 expected.push_str("[SKIPPED]\tcase-2\n");
478 assert_eq!(String::from_utf8(output.lock().clone()).unwrap(), expected,);
479
480 case_1_reporter.finished().expect("finish case");
481 case_2_reporter.finished().expect("finish case");
482 suite_reporter.stopped(&ReportedOutcome::Passed, Timestamp::Unknown).expect("stop suite");
483 suite_reporter.finished().expect("finish suite");
484
485 expected.push_str("\n");
486 expected.push_str("1 out of 1 attempted tests passed, 1 tests skipped...\n");
487 expected.push_str("test-suite completed with result: PASSED\n");
488 assert_eq!(String::from_utf8(output.lock().clone()).unwrap(), expected,);
489 }
490
491 #[fuchsia::test]
492 async fn syslog_artifacts() {
493 let (shell_reporter, output) = ShellReporter::new_expose_writer_for_test();
494 let run_reporter = RunReporter::new(shell_reporter);
495 let suite_reporter = run_reporter.new_suite("test-suite").expect("create suite");
496 suite_reporter.started(Timestamp::Unknown).expect("case started");
497 let mut syslog_writer =
498 suite_reporter.new_artifact(&ArtifactType::Syslog).expect("create syslog");
499
500 writeln!(syslog_writer, "[log] test syslog").expect("write to syslog");
501 let mut expected = "Running test 'test-suite'\n".to_string();
502 expected.push_str("[log] test syslog\n");
503 assert_eq!(String::from_utf8(output.lock().clone()).unwrap(), expected);
504
505 suite_reporter.stopped(&ReportedOutcome::Passed, Timestamp::Unknown).expect("stop suite");
506 writeln!(syslog_writer, "[log] more test syslog").expect("write to syslog");
507 expected.push_str("[log] more test syslog\n");
508 assert_eq!(String::from_utf8(output.lock().clone()).unwrap(), expected);
509
510 suite_reporter.finished().expect("finish suite");
511 expected.push_str("\n");
512 expected.push_str("0 out of 0 tests passed...\n");
513 expected.push_str("test-suite completed with result: PASSED\n");
514 assert_eq!(String::from_utf8(output.lock().clone()).unwrap(), expected,);
515 }
516
517 #[fuchsia::test]
518 async fn report_retricted_logs() {
519 let (shell_reporter, output) = ShellReporter::new_expose_writer_for_test();
520 let run_reporter = RunReporter::new(shell_reporter);
521 let suite_reporter = run_reporter.new_suite("test-suite").expect("create suite");
522 suite_reporter.started(Timestamp::Unknown).expect("case started");
523
524 let case_reporter = suite_reporter.new_case("case-0", &CaseId(0)).expect("create case");
525 case_reporter.started(Timestamp::Unknown).expect("case started");
526 case_reporter.stopped(&ReportedOutcome::Passed, Timestamp::Unknown).expect("stop case");
527 case_reporter.finished().expect("finish case");
528
529 let mut expected = "Running test 'test-suite'\n".to_string();
530 expected.push_str("[RUNNING]\tcase-0\n");
531 expected.push_str("[PASSED]\tcase-0\n");
532 assert_eq!(String::from_utf8(output.lock().clone()).unwrap(), expected,);
533
534 suite_reporter.stopped(&ReportedOutcome::Failed, Timestamp::Unknown).expect("stop suite");
535 let mut restricted_log = suite_reporter
536 .new_artifact(&ArtifactType::RestrictedLog)
537 .expect("create restricted log");
538 write!(restricted_log, "suite restricted log").expect("write to restricted log");
539 drop(restricted_log);
540
541 suite_reporter.finished().expect("finish suite");
542 expected.push_str("\n");
543 expected.push_str("1 out of 1 tests passed...\n");
544 expected.push_str("\nTest test-suite produced unexpected high-severity logs:\n");
545 expected.push_str("----------------xxxxx----------------\n");
546 expected.push_str("suite restricted log\n\n");
547 expected.push_str("----------------xxxxx----------------\n");
548 expected.push_str("Failing this test. See: https://fuchsia.dev/fuchsia-src/development/diagnostics/test_and_logs#restricting_log_severity\n");
549 expected.push_str("\ntest-suite completed with result: FAILED\n");
550 assert_eq!(String::from_utf8(output.lock().clone()).unwrap(), expected,);
551 }
552
553 #[fuchsia::test]
554 async fn stdout_artifacts() {
555 let (shell_reporter, output) = ShellReporter::new_expose_writer_for_test();
556 let run_reporter = RunReporter::new(shell_reporter);
557 let suite_reporter = run_reporter.new_suite("test-suite").expect("create suite");
558 suite_reporter.started(Timestamp::Unknown).expect("case started");
559
560 let case_0_reporter = suite_reporter.new_case("case-0", &CaseId(0)).expect("create case");
561 let case_1_reporter = suite_reporter.new_case("case-1", &CaseId(1)).expect("create case");
562 case_0_reporter.started(Timestamp::Unknown).expect("start case");
563 case_1_reporter.started(Timestamp::Unknown).expect("start case");
564 let mut expected = "Running test 'test-suite'\n".to_string();
565 expected.push_str("[RUNNING]\tcase-0\n[RUNNING]\tcase-1\n");
566 assert_eq!(String::from_utf8(output.lock().clone()).unwrap(), expected,);
567
568 let mut case_0_stdout =
569 case_0_reporter.new_artifact(&ArtifactType::Stdout).expect("create artifact");
570 let mut case_1_stdout =
571 case_1_reporter.new_artifact(&ArtifactType::Stdout).expect("create artifact");
572
573 writeln!(case_0_stdout, "stdout from case 0").expect("write to stdout");
574 writeln!(case_1_stdout, "stdout from case 1").expect("write to stdout");
575
576 expected.push_str("[stdout - case-0]\nstdout from case 0\n");
577 expected.push_str("[stdout - case-1]\nstdout from case 1\n");
578 assert_eq!(String::from_utf8(output.lock().clone()).unwrap(), expected,);
579
580 case_0_reporter.stopped(&ReportedOutcome::Passed, Timestamp::Unknown).expect("stop case");
581 case_1_reporter.stopped(&ReportedOutcome::Passed, Timestamp::Unknown).expect("stop case");
582 expected.push_str("[PASSED]\tcase-0\n");
583 expected.push_str("[PASSED]\tcase-1\n");
584 assert_eq!(String::from_utf8(output.lock().clone()).unwrap(), expected,);
585
586 case_0_reporter.finished().expect("finish case");
587 case_1_reporter.finished().expect("finish case");
588 suite_reporter.stopped(&ReportedOutcome::Passed, Timestamp::Unknown).expect("stop suite");
589
590 suite_reporter.finished().expect("finish suite");
591 expected.push_str("\n");
592 expected.push_str("2 out of 2 tests passed...\n");
593 expected.push_str("test-suite completed with result: PASSED\n");
594 assert_eq!(String::from_utf8(output.lock().clone()).unwrap(), expected,);
595 }
596
597 #[fuchsia::test]
598 async fn report_unfinished() {
599 let (shell_reporter, output) = ShellReporter::new_expose_writer_for_test();
600 let run_reporter = RunReporter::new(shell_reporter);
601 let suite_reporter = run_reporter.new_suite("test-suite").expect("create suite");
602 suite_reporter.started(Timestamp::Unknown).expect("suite started");
603
604 let case_reporter = suite_reporter.new_case("case-0", &CaseId(0)).expect("create case");
605 case_reporter.started(Timestamp::Unknown).expect("case started");
606 case_reporter.stopped(&ReportedOutcome::Passed, Timestamp::Unknown).expect("stop case");
607 case_reporter.finished().expect("finish case");
608 let mut expected = "Running test 'test-suite'\n".to_string();
609 expected.push_str("[RUNNING]\tcase-0\n[PASSED]\tcase-0\n");
610 assert_eq!(String::from_utf8(output.lock().clone()).unwrap(), expected,);
611
612 let unfinished_case_1 = suite_reporter.new_case("case-1", &CaseId(1)).expect("create case");
614 unfinished_case_1.started(Timestamp::Unknown).expect("case started");
615 unfinished_case_1.stopped(&ReportedOutcome::Error, Timestamp::Unknown).expect("stop case");
616 unfinished_case_1.finished().expect("finish case");
617 expected.push_str("[RUNNING]\tcase-1\n");
618 assert_eq!(String::from_utf8(output.lock().clone()).unwrap(), expected,);
619
620 let unfinished_case_2 = suite_reporter.new_case("case-2", &CaseId(2)).expect("create case");
622 unfinished_case_2.started(Timestamp::Unknown).expect("case started");
623 unfinished_case_2.finished().expect("finish case");
624 expected.push_str("[RUNNING]\tcase-2\n");
625 assert_eq!(String::from_utf8(output.lock().clone()).unwrap(), expected,);
626
627 suite_reporter.stopped(&ReportedOutcome::Failed, Timestamp::Unknown).expect("stop suite");
628 suite_reporter.finished().expect("finish suite");
629 expected.push_str("\n");
630 expected.push_str("\nThe following test(s) never completed:\n");
631 expected.push_str("case-1\n");
632 expected.push_str("case-2\n");
633 expected.push_str("1 out of 3 tests passed...\n");
634 expected.push_str("test-suite completed with result: FAILED\n");
635 assert_eq!(String::from_utf8(output.lock().clone()).unwrap(), expected,);
636 }
637
638 #[fuchsia::test]
639 async fn report_cancelled_suite() {
640 let (shell_reporter, output) = ShellReporter::new_expose_writer_for_test();
641 let run_reporter = RunReporter::new(shell_reporter);
642 let suite_reporter = run_reporter.new_suite("test-suite").expect("create suite");
643 suite_reporter.started(Timestamp::Unknown).expect("suite started");
644
645 let case_reporter = suite_reporter.new_case("case", &CaseId(0)).expect("create new case");
646 case_reporter.started(Timestamp::Unknown).expect("case started");
647 case_reporter.finished().expect("case finished");
648 suite_reporter
649 .stopped(&ReportedOutcome::Cancelled, Timestamp::Unknown)
650 .expect("stop suite");
651 suite_reporter.finished().expect("case finished");
652
653 let mut expected = "Running test 'test-suite'\n".to_string();
654 expected.push_str("[RUNNING]\tcase\n");
655 expected.push_str("\n");
656 expected.push_str("\nThe following test(s) never completed:\n");
657 expected.push_str("case\n");
658 expected.push_str("0 out of 1 tests passed...\n");
659 expected.push_str("test-suite was cancelled before completion.\n");
660 assert_eq!(String::from_utf8(output.lock().clone()).unwrap(), expected,);
661 }
662
663 #[fuchsia::test]
664 async fn report_suite_did_not_finish() {
665 let (shell_reporter, output) = ShellReporter::new_expose_writer_for_test();
666 let run_reporter = RunReporter::new(shell_reporter);
667 let suite_reporter = run_reporter.new_suite("test-suite").expect("create suite");
668 suite_reporter.started(Timestamp::Unknown).expect("suite started");
669
670 let case_reporter = suite_reporter.new_case("case", &CaseId(0)).expect("create new case");
671 case_reporter.started(Timestamp::Unknown).expect("case started");
672 case_reporter.finished().expect("case finished");
673 suite_reporter
674 .stopped(&ReportedOutcome::DidNotFinish, Timestamp::Unknown)
675 .expect("stop suite");
676 suite_reporter.finished().expect("case finished");
677
678 let mut expected = "Running test 'test-suite'\n".to_string();
679 expected.push_str("[RUNNING]\tcase\n");
680 expected.push_str("\n");
681 expected.push_str("\nThe following test(s) never completed:\n");
682 expected.push_str("case\n");
683 expected.push_str("0 out of 1 tests passed...\n");
684 expected.push_str("test-suite did not complete successfully.\n");
685 assert_eq!(String::from_utf8(output.lock().clone()).unwrap(), expected,);
686 }
687}