1use crate::cancel::{Cancelled, NamedFutureExt, OrCancel};
6use crate::connector::SuiteRunnerConnector;
7use crate::diagnostics::{self, LogDisplayConfiguration};
8use crate::outcome::{ExtendedOutcome, Outcome, RunTestSuiteError};
9use crate::output::{self, RunReporter, Timestamp};
10use crate::params::{RunParams, TestParams, TimeoutBehavior};
11use crate::running_suite::{RunningSuite, WaitForStartArgs, run_suite_and_collect_logs};
12use diagnostics_data::LogTextDisplayOptions;
13use flex_client::ProxyHasDomain;
14use flex_fuchsia_test_manager::{self as ftest_manager, SuiteRunnerProxy};
15use futures::prelude::*;
16use log::warn;
17use std::io::Write;
18use std::path::PathBuf;
19
20struct RunState<'a> {
21 run_params: &'a RunParams,
22 final_outcome: Option<Outcome>,
23 failed_suites: u32,
24 timeout_occurred: bool,
25 cancel_occurred: bool,
26 internal_error_occurred: bool,
27 setup_succeeded: Option<bool>,
28 teardown_succeeded: Option<bool>,
29}
30
31impl<'a> RunState<'a> {
32 fn new(run_params: &'a RunParams) -> Self {
33 Self {
34 run_params,
35 final_outcome: None,
36 failed_suites: 0,
37 timeout_occurred: false,
38 cancel_occurred: false,
39 internal_error_occurred: false,
40 setup_succeeded: None,
41 teardown_succeeded: None,
42 }
43 }
44
45 fn cancel_run(&mut self, final_outcome: Outcome) {
46 self.final_outcome = Some(final_outcome);
47 self.cancel_occurred = true;
48 }
49
50 fn record_next_outcome(&mut self, next_outcome: ExtendedOutcome) {
51 self.setup_succeeded = next_outcome.setup_succeeded;
52 self.teardown_succeeded = next_outcome.teardown_succeeded;
53 let next_outcome = next_outcome.outcome;
54 if next_outcome != Outcome::Passed {
55 self.failed_suites += 1;
56 }
57 match &next_outcome {
58 Outcome::Timedout => self.timeout_occurred = true,
59 Outcome::Cancelled => self.cancel_occurred = true,
60 Outcome::Error { origin } if origin.is_internal_error() => {
61 self.internal_error_occurred = true;
62 }
63 Outcome::Passed
64 | Outcome::Failed
65 | Outcome::Inconclusive
66 | Outcome::DidNotFinish
67 | Outcome::Error { .. } => (),
68 }
69
70 self.final_outcome = match (self.final_outcome.take(), next_outcome) {
71 (None, first_outcome) => Some(first_outcome),
72 (Some(outcome), Outcome::Passed) => Some(outcome),
73 (Some(_), failing_outcome) => Some(failing_outcome),
74 };
75 }
76
77 fn should_stop_run(&mut self) -> bool {
78 let stop_due_to_timeout = self.run_params.timeout_behavior
79 == TimeoutBehavior::TerminateRemaining
80 && self.timeout_occurred;
81 let stop_due_to_failures = match self.run_params.stop_after_failures.as_ref() {
82 Some(threshold) => self.failed_suites >= threshold.get(),
83 None => false,
84 };
85 stop_due_to_timeout
86 || stop_due_to_failures
87 || self.cancel_occurred
88 || self.internal_error_occurred
89 }
90
91 fn final_outcome(self) -> ExtendedOutcome {
92 ExtendedOutcome {
93 outcome: self.final_outcome.unwrap_or(Outcome::Passed),
94 setup_succeeded: self.setup_succeeded,
95 teardown_succeeded: self.teardown_succeeded,
96 }
97 }
98}
99
100async fn run_test_chunk<'a, F: 'a + Future<Output = ()> + Unpin>(
104 runner_proxy: SuiteRunnerProxy,
105 test_params: TestParams,
106 run_state: &'a mut RunState<'_>,
107 run_params: &'a RunParams,
108 run_reporter: &'a RunReporter,
109 cancel_fut: F,
110) -> Result<(), RunTestSuiteError> {
111 let timeout = test_params
112 .timeout_seconds
113 .map(|seconds| std::time::Duration::from_secs(seconds.get() as u64));
114
115 let mut combined_log_interest = run_params.min_severity_logs.clone();
118 combined_log_interest.extend(test_params.min_severity_logs.iter().cloned());
119
120 let mut run_options = flex_fuchsia_test_manager::RunSuiteOptions {
121 max_concurrent_test_case_runs: test_params.parallel,
122 arguments: Some(test_params.test_args),
123 timeout: timeout.map(|duration| duration.as_nanos() as i64),
124 run_disabled_tests: Some(test_params.also_run_disabled_tests),
125 test_case_filters: test_params.test_filters,
126 break_on_failure: Some(test_params.break_on_failure),
127 logs_iterator_type: Some(
128 run_params.log_protocol.unwrap_or_else(diagnostics::get_logs_iterator_type),
129 ),
130 log_interest: Some(combined_log_interest),
131 no_exception_channel: Some(test_params.no_exception_channel),
132 ..Default::default()
133 };
134 let suite = run_reporter.new_suite(&test_params.test_url)?;
135 suite.set_tags(test_params.tags);
136 if let Some(realm) = test_params.realm.as_ref() {
137 run_options.realm_options = Some(flex_fuchsia_test_manager::RealmOptions {
138 realm: Some(realm.get_realm_client()?),
139 offers: Some(realm.offers()),
140 test_collection: Some(realm.collection().to_string()),
141 ..Default::default()
142 });
143 }
144 let (suite_controller, suite_server_end) =
145 runner_proxy.domain().create_proxy::<ftest_manager::SuiteControllerMarker>();
146 let suite_start_fut = RunningSuite::wait_for_start(WaitForStartArgs {
147 proxy: suite_controller,
148 max_severity_logs: test_params.max_severity_logs,
149 timeout,
150 timeout_grace: std::time::Duration::from_secs(run_params.timeout_grace_seconds as u64),
151 max_pipelined: None,
152 no_cases_equals_success: test_params.no_cases_equals_success,
153 });
154
155 runner_proxy.run(&test_params.test_url, run_options, suite_server_end)?;
156
157 let cancel_fut = cancel_fut.shared();
158
159 let handle_suite_fut = async move {
160 'block: {
162 let suite_stop_fut = cancel_fut.clone().map(|_| Outcome::Cancelled);
163
164 let running_suite =
165 match suite_start_fut.named("suite_start").or_cancelled(suite_stop_fut).await {
166 Ok(running_suite) => running_suite,
167 Err(Cancelled(final_outcome)) => {
168 run_state.cancel_run(final_outcome);
169 break 'block;
170 }
171 };
172
173 let log_display = LogDisplayConfiguration {
174 interest: run_params.min_severity_logs.clone(),
175 text_options: LogTextDisplayOptions {
176 show_full_moniker: run_params.show_full_moniker,
177 ..Default::default()
178 },
179 };
180
181 let result =
182 run_suite_and_collect_logs(running_suite, &suite, log_display, cancel_fut.clone())
183 .await;
184 let suite_outcome = result.unwrap_or_else(|err| ExtendedOutcome {
188 outcome: Outcome::error(err),
189 setup_succeeded: Some(true),
190 teardown_succeeded: Some(false),
191 });
192 suite.finished()?;
194 run_state.record_next_outcome(suite_outcome);
195 if run_state.should_stop_run() {
196 break 'block;
197 }
198 }
199 Result::<_, RunTestSuiteError>::Ok(())
200 };
201
202 handle_suite_fut.boxed_local().await.map_err(|e| e.into())
203}
204
205async fn run_tests<'a, F: 'a + Future<Output = ()> + Unpin>(
206 connector: impl SuiteRunnerConnector,
207 test_params: TestParams,
208 run_params: RunParams,
209 run_reporter: &'a RunReporter,
210 cancel_fut: F,
211) -> Result<ExtendedOutcome, RunTestSuiteError> {
212 let mut run_state = RunState::new(&run_params);
213 let cancel_fut = cancel_fut.shared();
214 match run_state.should_stop_run() {
215 true => {
216 let suite_reporter = run_reporter.new_suite(&test_params.test_url)?;
219 suite_reporter.set_tags(test_params.tags);
220 suite_reporter.finished()?;
221 }
222 false => {
223 let runner_proxy = connector.connect().await?;
224 run_test_chunk(
225 runner_proxy,
226 test_params,
227 &mut run_state,
228 &run_params,
229 run_reporter,
230 cancel_fut.clone(),
231 )
232 .await?;
233 }
234 }
235
236 Ok(run_state.final_outcome())
237}
238
239pub async fn run_test_and_get_outcome<F>(
250 connector: impl SuiteRunnerConnector,
251 test_params: TestParams,
252 run_params: RunParams,
253 run_reporter: RunReporter,
254 cancel_fut: F,
255) -> ExtendedOutcome
256where
257 F: Future<Output = ()>,
258{
259 match run_reporter.started(Timestamp::Unknown) {
260 Ok(()) => (),
261 Err(e) => {
262 return ExtendedOutcome {
263 outcome: Outcome::error(e),
264 setup_succeeded: None,
265 teardown_succeeded: None,
266 };
267 }
268 }
269 let test_outcome = match run_tests(
270 connector,
271 test_params,
272 run_params,
273 &run_reporter,
274 cancel_fut.boxed_local(),
275 )
276 .await
277 {
278 Ok(s) => s,
279 Err(e) => {
280 return ExtendedOutcome {
281 outcome: Outcome::error(e),
282 setup_succeeded: None,
283 teardown_succeeded: None,
284 };
285 }
286 };
287
288 let report_result =
289 match run_reporter.stopped(&test_outcome.outcome.clone().into(), Timestamp::Unknown) {
290 Ok(()) => run_reporter.finished(),
291 Err(e) => Err(e),
292 };
293 if let Err(e) = report_result {
294 warn!("Failed to record results: {:?}", e);
295 }
296
297 test_outcome
298}
299
300pub struct DirectoryReporterOptions {
301 pub root_path: PathBuf,
303}
304
305pub fn create_reporter<W: 'static + Write + Send + Sync>(
307 filter_ansi: bool,
308 dir: Option<DirectoryReporterOptions>,
309 writer: W,
310) -> Result<output::RunReporter, anyhow::Error> {
311 let stdout_reporter = output::ShellReporter::new(writer);
312 let dir_reporter = dir
313 .map(|dir| {
314 output::DirectoryWithStdoutReporter::new(dir.root_path, output::SchemaVersion::V1)
315 })
316 .transpose()?;
317 let reporter = match (dir_reporter, filter_ansi) {
318 (Some(dir_reporter), false) => output::RunReporter::new(output::MultiplexedReporter::new(
319 stdout_reporter,
320 dir_reporter,
321 )),
322 (Some(dir_reporter), true) => output::RunReporter::new_ansi_filtered(
323 output::MultiplexedReporter::new(stdout_reporter, dir_reporter),
324 ),
325 (None, false) => output::RunReporter::new(stdout_reporter),
326 (None, true) => output::RunReporter::new_ansi_filtered(stdout_reporter),
327 };
328 Ok(reporter)
329}
330
331#[cfg(test)]
332mod test {
333 use super::*;
334 use crate::connector::SingleRunConnector;
335 use crate::output::{EntityId, InMemoryReporter};
336 use assert_matches::assert_matches;
337 use fidl::endpoints::{Proxy, create_proxy_and_stream};
338 use flex_fuchsia_test_manager as ftest_manager;
339 use futures::future::join;
340 use futures::stream::futures_unordered::FuturesUnordered;
341 use maplit::hashmap;
342 use std::collections::HashMap;
343 #[cfg(target_os = "fuchsia")]
344 use {
345 flex_fuchsia_io as fio, futures::future::join3, vfs::file::vmo::read_only,
346 vfs::pseudo_directory, zx,
347 };
348
349 async fn fake_running_all_suites(
352 mut stream: ftest_manager::SuiteRunnerRequestStream,
353 mut suite_events: HashMap<&str, Vec<ftest_manager::Event>>,
354 ) {
355 let mut suite_streams = vec![];
356
357 if let Ok(Some(req)) = stream.try_next().await {
358 match req {
359 ftest_manager::SuiteRunnerRequest::Run { test_suite_url, controller, .. } => {
360 let events = suite_events
361 .remove(test_suite_url.as_str())
362 .expect("Got a request for an unexpected test URL");
363 suite_streams.push((controller.into_stream(), events));
364 }
365 ftest_manager::SuiteRunnerRequest::_UnknownMethod { ordinal, .. } => {
366 panic!("Not expecting unknown request: {}", ordinal)
367 }
368 }
369 }
370 assert!(suite_events.is_empty(), "Expected AddSuite to be called for all specified suites");
371
372 let mut suite_streams = suite_streams
374 .into_iter()
375 .map(|(mut stream, events)| {
376 async move {
377 let mut maybe_events = Some(events);
378 while let Ok(Some(req)) = stream.try_next().await {
379 match req {
380 ftest_manager::SuiteControllerRequest::WatchEvents {
381 responder,
382 ..
383 } => {
384 let send_events = maybe_events.take().unwrap_or(vec![]);
385 let _ = responder.send(Ok(send_events));
386 }
387 _ => {
388 }
390 }
391 }
392 }
393 })
394 .collect::<FuturesUnordered<_>>();
395
396 async move { while let Some(_) = suite_streams.next().await {} }.await;
397 }
398
399 struct ParamsForRunTests {
400 runner_proxy: ftest_manager::SuiteRunnerProxy,
401 test_params: TestParams,
402 run_reporter: RunReporter,
403 }
404
405 fn create_empty_suite_events() -> Vec<ftest_manager::Event> {
406 vec![
407 ftest_manager::Event {
408 timestamp: Some(1000),
409 details: Some(ftest_manager::EventDetails::SuiteStarted(
410 ftest_manager::SuiteStartedEventDetails { ..Default::default() },
411 )),
412 ..Default::default()
413 },
414 ftest_manager::Event {
415 timestamp: Some(2000),
416 details: Some(ftest_manager::EventDetails::SuiteStopped(
417 ftest_manager::SuiteStoppedEventDetails {
418 result: Some(ftest_manager::SuiteResult::Finished),
419 ..Default::default()
420 },
421 )),
422 ..Default::default()
423 },
424 ]
425 }
426
427 async fn call_run_tests(params: ParamsForRunTests) -> ExtendedOutcome {
428 run_test_and_get_outcome(
429 SingleRunConnector::new(params.runner_proxy),
430 params.test_params,
431 RunParams {
432 timeout_behavior: TimeoutBehavior::Continue,
433 timeout_grace_seconds: 0,
434 stop_after_failures: None,
435 accumulate_debug_data: false,
436 log_protocol: None,
437 min_severity_logs: vec![],
438 show_full_moniker: false,
439 },
440 params.run_reporter,
441 futures::future::pending(),
442 )
443 .await
444 }
445
446 #[fuchsia::test]
447 async fn single_run_no_events() {
448 let (runner_proxy, suite_runner_stream) =
449 create_proxy_and_stream::<ftest_manager::SuiteRunnerMarker>();
450
451 let reporter = InMemoryReporter::new();
452 let run_reporter = RunReporter::new(reporter.clone());
453 let run_fut = call_run_tests(ParamsForRunTests {
454 runner_proxy,
455 test_params: TestParams {
456 test_url: "fuchsia-pkg://fuchsia.com/nothing#meta/nothing.cm".to_string(),
457 ..TestParams::default()
458 },
459 run_reporter,
460 });
461 let fake_fut = fake_running_all_suites(
462 suite_runner_stream,
463 hashmap! {
464 "fuchsia-pkg://fuchsia.com/nothing#meta/nothing.cm" => create_empty_suite_events()
465 },
466 );
467
468 assert_eq!(
469 join(run_fut, fake_fut).await.0,
470 ExtendedOutcome {
471 outcome: Outcome::Passed,
472 setup_succeeded: Some(true),
473 teardown_succeeded: Some(true),
474 }
475 );
476
477 let reports = reporter.get_reports();
478 assert_eq!(2usize, reports.len());
479 assert!(reports[0].report.artifacts.is_empty());
480 assert!(reports[0].report.directories.is_empty());
481 assert!(reports[1].report.artifacts.is_empty());
482 assert!(reports[1].report.directories.is_empty());
483 }
484
485 #[cfg(target_os = "fuchsia")]
486 #[fuchsia::test]
487 async fn single_run_custom_directory() {
488 let (runner_proxy, suite_runner_stream) =
489 create_proxy_and_stream::<ftest_manager::SuiteRunnerMarker>();
490
491 let reporter = InMemoryReporter::new();
492 let run_reporter = RunReporter::new(reporter.clone());
493 let run_fut = call_run_tests(ParamsForRunTests {
494 runner_proxy,
495 test_params: TestParams {
496 test_url: "fuchsia-pkg://fuchsia.com/nothing#meta/nothing.cm".to_string(),
497 ..TestParams::default()
498 },
499 run_reporter,
500 });
501
502 let dir = pseudo_directory! {
503 "test_file.txt" => read_only("Hello, World!"),
504 };
505
506 let directory_proxy = vfs::directory::serve(
507 dir,
508 vfs::execution_scope::ExecutionScope::new(),
509 fio::PERM_READABLE | fio::PERM_WRITABLE,
510 );
511
512 let directory_client =
513 fidl::endpoints::ClientEnd::new(directory_proxy.into_channel().unwrap().into());
514
515 let (_pair_1, pair_2) = zx::EventPair::create();
516
517 let events = vec![
518 ftest_manager::Event {
519 timestamp: Some(1000),
520 details: Some(ftest_manager::EventDetails::SuiteStarted(
521 ftest_manager::SuiteStartedEventDetails { ..Default::default() },
522 )),
523 ..Default::default()
524 },
525 ftest_manager::Event {
526 details: Some(ftest_manager::EventDetails::SuiteArtifactGenerated(
527 ftest_manager::SuiteArtifactGeneratedEventDetails {
528 artifact: Some(ftest_manager::Artifact::Custom(
529 ftest_manager::CustomArtifact {
530 directory_and_token: Some(ftest_manager::DirectoryAndToken {
531 directory: directory_client,
532 token: pair_2,
533 }),
534 ..Default::default()
535 },
536 )),
537 ..Default::default()
538 },
539 )),
540 ..Default::default()
541 },
542 ftest_manager::Event {
543 timestamp: Some(2000),
544 details: Some(ftest_manager::EventDetails::SuiteStopped(
545 ftest_manager::SuiteStoppedEventDetails {
546 result: Some(ftest_manager::SuiteResult::Finished),
547 ..Default::default()
548 },
549 )),
550 ..Default::default()
551 },
552 ];
553
554 let fake_fut = fake_running_all_suites(
555 suite_runner_stream,
556 hashmap! {
557 "fuchsia-pkg://fuchsia.com/nothing#meta/nothing.cm" => events
558 },
559 );
560
561 assert_eq!(
562 join(run_fut, fake_fut).await.0,
563 ExtendedOutcome {
564 outcome: Outcome::Passed,
565 setup_succeeded: Some(true),
566 teardown_succeeded: Some(true),
567 },
568 );
569
570 let reports = reporter.get_reports();
571 assert_eq!(2usize, reports.len());
572 let run = reports.iter().find(|e| e.id == EntityId::Suite).expect("find run report");
573 assert_eq!(1usize, run.report.directories.len());
574 let dir = &run.report.directories[0];
575 let files = dir.1.files.lock();
576 assert_eq!(1usize, files.len());
577 let (name, file) = &files[0];
578 assert_eq!(name.to_string_lossy(), "test_file.txt".to_string());
579 assert_eq!(file.get_contents(), b"Hello, World!");
580 }
581
582 #[fuchsia::test]
583 async fn record_output_after_internal_error() {
584 let (runner_proxy, suite_runner_stream) =
585 create_proxy_and_stream::<ftest_manager::SuiteRunnerMarker>();
586
587 let reporter = InMemoryReporter::new();
588 let run_reporter = RunReporter::new(reporter.clone());
589 let run_fut = call_run_tests(ParamsForRunTests {
590 runner_proxy,
591 test_params: TestParams {
592 test_url: "fuchsia-pkg://fuchsia.com/invalid#meta/invalid.cm".to_string(),
593 ..TestParams::default()
594 },
595 run_reporter,
596 });
597
598 let fake_fut = fake_running_all_suites(
599 suite_runner_stream,
600 hashmap! {
601 "fuchsia-pkg://fuchsia.com/invalid#meta/invalid.cm" => vec![
603 ftest_manager::Event {
604 timestamp: Some(1000),
605 details: Some(ftest_manager::EventDetails::SuiteStarted(ftest_manager::SuiteStartedEventDetails {..Default::default()})),
606 ..Default::default()
607 },
608 ftest_manager::Event {
609 timestamp: Some(2000),
610 details: Some(ftest_manager::EventDetails::SuiteStopped(ftest_manager::SuiteStoppedEventDetails {
611 result: Some(ftest_manager::SuiteResult::InternalError),
612 ..Default::default()
613 },
614 )),
615 ..Default::default()
616 },
617 ],
618 },
619 );
620
621 assert_matches!(
622 join(run_fut, fake_fut).await.0,
623 ExtendedOutcome {
624 outcome: Outcome::Error { .. },
625 setup_succeeded: Some(true),
626 teardown_succeeded: Some(true)
627 }
628 );
629
630 let reports = reporter.get_reports();
631 assert_eq!(2usize, reports.len());
632 let invalid_suite = reports
633 .iter()
634 .find(|e| e.report.name == "fuchsia-pkg://fuchsia.com/invalid#meta/invalid.cm")
635 .expect("find run report");
636 assert_eq!(invalid_suite.report.outcome, Some(output::ReportedOutcome::Error));
637 assert!(invalid_suite.report.is_finished);
638
639 let run = reports.iter().find(|e| e.id == EntityId::TestRun).expect("find run report");
641 assert_eq!(run.report.outcome, Some(output::ReportedOutcome::Error));
642 assert!(run.report.is_finished);
643 assert!(run.report.started_time.is_some());
644 }
645
646 #[fuchsia::test]
647 async fn record_output_after_launch_error() {
648 let (runner_proxy, mut suite_runner_stream) =
649 create_proxy_and_stream::<ftest_manager::SuiteRunnerMarker>();
650
651 let reporter = InMemoryReporter::new();
652 let run_reporter = RunReporter::new(reporter.clone());
653 let run_fut = call_run_tests(ParamsForRunTests {
654 runner_proxy,
655 test_params: TestParams {
656 test_url: "fuchsia-pkg://fuchsia.com/invalid#meta/invalid.cm".to_string(),
657 ..TestParams::default()
658 },
659 run_reporter,
660 });
661
662 let fake_fut = async move {
663 if let Ok(Some(ftest_manager::SuiteRunnerRequest::Run { controller, .. })) =
664 suite_runner_stream.try_next().await
665 {
666 let mut stream = controller.into_stream();
667 while let Ok(Some(req)) = stream.try_next().await {
668 if let ftest_manager::SuiteControllerRequest::WatchEvents {
669 responder, ..
670 } = req
671 {
672 let _ =
673 responder.send(Err(ftest_manager::LaunchError::InstanceCannotResolve));
674 }
675 }
676 }
677 };
678
679 assert_matches!(
680 join(run_fut, fake_fut).await.0,
681 ExtendedOutcome {
682 outcome: Outcome::Error { .. },
683 setup_succeeded: Some(false),
684 teardown_succeeded: None
685 }
686 );
687 }
688
689 #[cfg(target_os = "fuchsia")]
690 #[fuchsia::test]
691 async fn single_run_debug_data() {
692 let (runner_proxy, suite_runner_stream) =
693 create_proxy_and_stream::<ftest_manager::SuiteRunnerMarker>();
694
695 let reporter = InMemoryReporter::new();
696 let run_reporter = RunReporter::new(reporter.clone());
697 let run_fut = call_run_tests(ParamsForRunTests {
698 runner_proxy,
699 test_params: TestParams {
700 test_url: "fuchsia-pkg://fuchsia.com/nothing#meta/nothing.cm".to_string(),
701 ..TestParams::default()
702 },
703 run_reporter,
704 });
705
706 let (debug_client, debug_service) =
707 fidl::endpoints::create_endpoints::<ftest_manager::DebugDataIteratorMarker>();
708 let debug_data_fut = async move {
709 let (client, server) = zx::Socket::create_stream();
710 let mut compressor = zstd::bulk::Compressor::new(0).unwrap();
711 let bytes = compressor.compress(b"Not a real profile").unwrap();
712 let _ = server.write(bytes.as_slice()).unwrap();
713 let mut service = debug_service.into_stream();
714 let mut data = vec![ftest_manager::DebugData {
715 name: Some("test_file.profraw".to_string()),
716 socket: Some(client.into()),
717 ..Default::default()
718 }];
719 drop(server);
720 while let Ok(Some(request)) = service.try_next().await {
721 match request {
722 ftest_manager::DebugDataIteratorRequest::GetNext { .. } => {
723 panic!("Not Implemented");
724 }
725 ftest_manager::DebugDataIteratorRequest::GetNextCompressed {
726 responder,
727 ..
728 } => {
729 let _ = responder.send(std::mem::take(&mut data));
730 }
731 }
732 }
733 };
734
735 let events = vec![
736 ftest_manager::Event {
737 timestamp: Some(1000),
738 details: Some(ftest_manager::EventDetails::SuiteStarted(
739 ftest_manager::SuiteStartedEventDetails { ..Default::default() },
740 )),
741 ..Default::default()
742 },
743 ftest_manager::Event {
744 details: Some(ftest_manager::EventDetails::SuiteArtifactGenerated(
745 ftest_manager::SuiteArtifactGeneratedEventDetails {
746 artifact: Some(ftest_manager::Artifact::DebugData(debug_client)),
747 ..Default::default()
748 },
749 )),
750 ..Default::default()
751 },
752 ftest_manager::Event {
753 timestamp: Some(2000),
754 details: Some(ftest_manager::EventDetails::SuiteStopped(
755 ftest_manager::SuiteStoppedEventDetails {
756 result: Some(ftest_manager::SuiteResult::Finished),
757 ..Default::default()
758 },
759 )),
760 ..Default::default()
761 },
762 ];
763
764 let fake_fut = fake_running_all_suites(
765 suite_runner_stream,
766 hashmap! {
767 "fuchsia-pkg://fuchsia.com/nothing#meta/nothing.cm" => events,
768 },
769 );
770
771 assert_eq!(
772 join3(run_fut, debug_data_fut, fake_fut).await.0,
773 ExtendedOutcome {
774 outcome: Outcome::Passed,
775 setup_succeeded: Some(true),
776 teardown_succeeded: Some(true),
777 }
778 );
779
780 let reports = reporter.get_reports();
781 assert_eq!(2usize, reports.len());
782 let run = reports.iter().find(|e| e.id == EntityId::Suite).expect("find run report");
783 assert_eq!(1usize, run.report.directories.len());
784 let dir = &run.report.directories[0];
785 let files = dir.1.files.lock();
786 assert_eq!(1usize, files.len());
787 let (name, file) = &files[0];
788 assert_eq!(name.to_string_lossy(), "test_file.profraw".to_string());
789 assert_eq!(file.get_contents(), b"Not a real profile");
790 }
791}