1use anyhow::{Context as _, Error};
8use fidl_fuchsia_io as fio;
9use fidl_fuchsia_test_manager::{self as ftest_manager, EventDetailsUnknown, SuiteControllerProxy};
10use fuchsia_async as fasync;
11use futures::channel::mpsc;
12use futures::prelude::*;
13use linked_hash_map::LinkedHashMap;
14use log::*;
15use moniker::ExtendedMoniker;
16use std::collections::HashMap;
17use std::sync::Arc;
18use test_diagnostics::zstd_compress::Decoder;
19use test_diagnostics::{LogStream, collect_and_send_string_output, collect_string_from_socket};
20
21pub fn default_run_suite_options() -> ftest_manager::RunSuiteOptions {
22 ftest_manager::RunSuiteOptions { run_disabled_tests: Some(false), ..Default::default() }
23}
24
25#[derive(Debug, Eq, PartialEq)]
26pub struct AttributedLog {
27 pub log: String,
28 pub moniker: ExtendedMoniker,
29}
30
31pub async fn collect_suite_events_with_watch(
32 suite_instance: SuiteRunInstance,
33 filter_debug_data: bool,
34 compressed_debug_data: bool,
35) -> Result<(Vec<RunEvent>, Vec<AttributedLog>), Error> {
36 let (sender, mut recv) = mpsc::channel(1);
37 let execution_task = fasync::Task::spawn(async move {
38 suite_instance
39 .collect_events_with_watch(sender, filter_debug_data, compressed_debug_data)
40 .await
41 });
42 let mut events = vec![];
43 let mut log_tasks = vec![];
44 while let Some(event) = recv.next().await {
45 match event.payload {
46 SuiteEventPayload::RunEvent(RunEvent::CaseStdout { name, mut stdout_message }) => {
47 if stdout_message.ends_with("\n") {
48 stdout_message.truncate(stdout_message.len() - 1)
49 }
50 let logs = stdout_message.split("\n");
51 for log in logs {
52 if log.contains("Note: Randomizing tests' orders with a seed of") {
55 continue;
56 }
57 events.push(RunEvent::case_stdout(name.clone(), log.to_string()));
58 }
59 }
60 SuiteEventPayload::RunEvent(RunEvent::CaseStderr { name, mut stderr_message }) => {
61 if stderr_message.ends_with("\n") {
62 stderr_message.truncate(stderr_message.len() - 1)
63 }
64 let logs = stderr_message.split("\n");
65 for log in logs {
66 events.push(RunEvent::case_stderr(name.clone(), log.to_string()));
67 }
68 }
69 SuiteEventPayload::RunEvent(e) => events.push(e),
70 SuiteEventPayload::SuiteLog { log_stream } => {
71 let t = fasync::Task::spawn(log_stream.collect::<Vec<_>>());
72 log_tasks.push(t);
73 }
74 SuiteEventPayload::TestCaseLog { .. } => {
75 panic!("not supported yet!")
76 }
77 SuiteEventPayload::DebugData { filename, socket } => {
78 events.push(RunEvent::DebugData { filename, socket })
79 }
80 }
81 }
82 execution_task.await.context("test execution failed")?;
83
84 let mut collected_logs = vec![];
85 for t in log_tasks {
86 let logs = t.await;
87 for log_result in logs {
88 let log = log_result?;
89 collected_logs
90 .push(AttributedLog { log: log.msg().unwrap().to_string(), moniker: log.moniker });
91 }
92 }
93
94 Ok((events, collected_logs))
95}
96
97pub async fn collect_string_from_socket_helper(
99 socket: fidl::Socket,
100 compressed_debug_data: bool,
101) -> Result<String, anyhow::Error> {
102 if !compressed_debug_data {
103 return collect_string_from_socket(socket).await;
104 }
105 let mut async_socket = fidl::AsyncSocket::from_socket(socket);
106 let mut buf = vec![0u8; 1024 * 32];
107
108 let (mut decoder, mut receiver) = Decoder::new();
109 let task: fasync::Task<Result<(), anyhow::Error>> = fasync::Task::spawn(async move {
110 loop {
111 let l = async_socket.read(&mut buf).await?;
112 match l {
113 0 => {
114 decoder.finish().await?;
115 break;
116 }
117 _ => {
118 decoder.decompress(&buf[..l]).await?;
119 }
120 }
121 }
122 Ok(())
123 });
124
125 let mut decompressed_data = Vec::new();
126 while let Some(chunk) = receiver.next().await {
127 decompressed_data.extend_from_slice(&chunk);
128 }
129 task.await?;
130 Ok(String::from_utf8_lossy(decompressed_data.as_slice()).into())
131}
132pub struct SuiteRunner {
134 proxy: ftest_manager::SuiteRunnerProxy,
135}
136
137impl SuiteRunner {
138 pub fn new(proxy: ftest_manager::SuiteRunnerProxy) -> Self {
140 Self { proxy }
141 }
142
143 pub fn take_proxy(self) -> ftest_manager::SuiteRunnerProxy {
144 self.proxy
145 }
146
147 pub fn start_suite_run(
149 &self,
150 test_url: &str,
151 options: ftest_manager::RunSuiteOptions,
152 ) -> Result<SuiteRunInstance, Error> {
153 let (controller_proxy, controller) = fidl::endpoints::create_proxy();
154 self.proxy.run(test_url, options, controller).context("Error starting tests")?;
155
156 return Ok(SuiteRunInstance { controller_proxy: controller_proxy.into() });
157 }
158}
159
160pub struct SuiteEvent {
162 pub timestamp: Option<i64>,
163 pub payload: SuiteEventPayload,
164}
165
166impl SuiteEvent {
167 pub fn debug_data<S: Into<String>>(
169 timestamp: Option<i64>,
170 filename: S,
171 socket: fidl::Socket,
172 ) -> Self {
173 Self {
174 timestamp,
175 payload: SuiteEventPayload::DebugData { filename: filename.into(), socket },
176 }
177 }
178
179 pub fn case_found(timestamp: Option<i64>, name: String) -> Self {
180 SuiteEvent { timestamp, payload: SuiteEventPayload::RunEvent(RunEvent::case_found(name)) }
181 }
182
183 pub fn case_started(timestamp: Option<i64>, name: String) -> Self {
184 SuiteEvent { timestamp, payload: SuiteEventPayload::RunEvent(RunEvent::case_started(name)) }
185 }
186
187 pub fn case_stdout<N, L>(timestamp: Option<i64>, name: N, stdout_message: L) -> Self
188 where
189 N: Into<String>,
190 L: Into<String>,
191 {
192 SuiteEvent {
193 timestamp,
194 payload: SuiteEventPayload::RunEvent(RunEvent::case_stdout(
195 name.into(),
196 stdout_message.into(),
197 )),
198 }
199 }
200
201 pub fn case_stderr<N, L>(timestamp: Option<i64>, name: N, stderr_message: L) -> Self
202 where
203 N: Into<String>,
204 L: Into<String>,
205 {
206 SuiteEvent {
207 timestamp,
208 payload: SuiteEventPayload::RunEvent(RunEvent::case_stderr(
209 name.into(),
210 stderr_message.into(),
211 )),
212 }
213 }
214
215 pub fn case_stopped(
216 timestamp: Option<i64>,
217 name: String,
218 status: ftest_manager::TestCaseResult,
219 ) -> Self {
220 SuiteEvent {
221 timestamp,
222 payload: SuiteEventPayload::RunEvent(RunEvent::case_stopped(name, status)),
223 }
224 }
225
226 pub fn case_finished(timestamp: Option<i64>, name: String) -> Self {
227 SuiteEvent {
228 timestamp,
229 payload: SuiteEventPayload::RunEvent(RunEvent::case_finished(name)),
230 }
231 }
232
233 pub fn suite_stopped(timestamp: Option<i64>, status: ftest_manager::SuiteResult) -> Self {
234 SuiteEvent {
235 timestamp,
236 payload: SuiteEventPayload::RunEvent(RunEvent::suite_stopped(status)),
237 }
238 }
239
240 pub fn suite_custom(
241 timestamp: Option<i64>,
242 component: String,
243 filename: String,
244 contents: String,
245 ) -> Self {
246 SuiteEvent {
247 timestamp,
248 payload: SuiteEventPayload::RunEvent(RunEvent::suite_custom(
249 component, filename, contents,
250 )),
251 }
252 }
253
254 pub fn suite_log(timestamp: Option<i64>, log_stream: LogStream) -> Self {
255 SuiteEvent { timestamp, payload: SuiteEventPayload::SuiteLog { log_stream } }
256 }
257
258 pub fn test_case_log(timestamp: Option<i64>, name: String, log_stream: LogStream) -> Self {
259 SuiteEvent { timestamp, payload: SuiteEventPayload::TestCaseLog { name, log_stream } }
260 }
261}
262
263pub enum SuiteEventPayload {
264 SuiteLog {
266 log_stream: LogStream,
267 },
268
269 TestCaseLog {
271 name: String,
272 log_stream: LogStream,
273 },
274
275 RunEvent(RunEvent),
277
278 DebugData {
280 filename: String,
281 socket: fidl::Socket,
282 },
283}
284
285#[derive(PartialEq, Debug, Eq, Hash, Ord, PartialOrd)]
286pub enum RunEvent {
287 CaseFound { name: String },
288 CaseStarted { name: String },
289 CaseStdout { name: String, stdout_message: String },
290 CaseStderr { name: String, stderr_message: String },
291 CaseStopped { name: String, status: ftest_manager::TestCaseResult },
292 CaseFinished { name: String },
293 SuiteStarted,
294 SuiteCustom { component: String, filename: String, contents: String },
295 SuiteStopped { status: ftest_manager::SuiteResult },
296 DebugData { filename: String, socket: fidl::Socket },
297}
298
299impl RunEvent {
300 pub fn case_found<S>(name: S) -> Self
301 where
302 S: Into<String>,
303 {
304 Self::CaseFound { name: name.into() }
305 }
306
307 pub fn case_started<S>(name: S) -> Self
308 where
309 S: Into<String>,
310 {
311 Self::CaseStarted { name: name.into() }
312 }
313
314 pub fn case_stdout<S, L>(name: S, stdout_message: L) -> Self
315 where
316 S: Into<String>,
317 L: Into<String>,
318 {
319 Self::CaseStdout { name: name.into(), stdout_message: stdout_message.into() }
320 }
321
322 pub fn case_stderr<S, L>(name: S, stderr_message: L) -> Self
323 where
324 S: Into<String>,
325 L: Into<String>,
326 {
327 Self::CaseStderr { name: name.into(), stderr_message: stderr_message.into() }
328 }
329
330 pub fn case_stopped<S>(name: S, status: ftest_manager::TestCaseResult) -> Self
331 where
332 S: Into<String>,
333 {
334 Self::CaseStopped { name: name.into(), status }
335 }
336
337 pub fn case_finished<S>(name: S) -> Self
338 where
339 S: Into<String>,
340 {
341 Self::CaseFinished { name: name.into() }
342 }
343
344 pub fn suite_started() -> Self {
345 Self::SuiteStarted
346 }
347
348 pub fn suite_custom<T, U, V>(component: T, filename: U, contents: V) -> Self
349 where
350 T: Into<String>,
351 U: Into<String>,
352 V: Into<String>,
353 {
354 Self::SuiteCustom {
355 component: component.into(),
356 filename: filename.into(),
357 contents: contents.into(),
358 }
359 }
360
361 pub fn suite_stopped(status: ftest_manager::SuiteResult) -> Self {
362 Self::SuiteStopped { status }
363 }
364
365 pub fn debug_data<S>(filename: S, socket: fidl::Socket) -> Self
366 where
367 S: Into<String>,
368 {
369 Self::DebugData { filename: filename.into(), socket }
370 }
371
372 pub fn test_case_name(&self) -> Option<&String> {
374 match self {
375 RunEvent::CaseFound { name }
376 | RunEvent::CaseStarted { name }
377 | RunEvent::CaseStdout { name, .. }
378 | RunEvent::CaseStderr { name, .. }
379 | RunEvent::CaseStopped { name, .. }
380 | RunEvent::CaseFinished { name } => Some(name),
381 RunEvent::SuiteStarted
382 | RunEvent::SuiteStopped { .. }
383 | RunEvent::SuiteCustom { .. }
384 | RunEvent::DebugData { .. } => None,
385 }
386 }
387
388 pub fn owned_test_case_name(&self) -> Option<String> {
390 self.test_case_name().map(String::from)
391 }
392}
393
394#[derive(Default, Debug, Eq, PartialEq)]
397pub struct GroupedRunEvents {
398 pub non_artifact_events: Vec<RunEvent>,
400 pub stdout_events: Vec<RunEvent>,
402 pub stderr_events: Vec<RunEvent>,
404}
405
406pub trait GroupRunEventByTestCase: Iterator<Item = RunEvent> + Sized {
408 fn group_by_test_case_ordered(self) -> LinkedHashMap<Option<String>, GroupedRunEvents> {
423 let mut map = LinkedHashMap::new();
424 for run_event in self {
425 match run_event {
426 RunEvent::CaseStderr { .. } => map
427 .entry(run_event.owned_test_case_name())
428 .or_insert_with(|| GroupedRunEvents::default())
429 .stderr_events
430 .push(run_event),
431
432 RunEvent::CaseStdout { .. } => map
433 .entry(run_event.owned_test_case_name())
434 .or_insert_with(|| GroupedRunEvents::default())
435 .stdout_events
436 .push(run_event),
437
438 _ => map
439 .entry(run_event.owned_test_case_name())
440 .or_insert_with(|| GroupedRunEvents::default())
441 .non_artifact_events
442 .push(run_event),
443 }
444 }
445 map
446 }
447
448 fn group_by_test_case_unordered(self) -> HashMap<Option<String>, GroupedRunEvents> {
451 let mut map = HashMap::new();
452 for run_event in self {
453 match run_event {
454 RunEvent::CaseStderr { .. } => map
455 .entry(run_event.owned_test_case_name())
456 .or_insert_with(|| GroupedRunEvents::default())
457 .stderr_events
458 .push(run_event),
459
460 RunEvent::CaseStdout { .. } => map
461 .entry(run_event.owned_test_case_name())
462 .or_insert_with(|| GroupedRunEvents::default())
463 .stdout_events
464 .push(run_event),
465
466 _ => map
467 .entry(run_event.owned_test_case_name())
468 .or_insert_with(|| GroupedRunEvents::default())
469 .non_artifact_events
470 .push(run_event),
471 }
472 }
473 map
474 }
475
476 fn group(self) -> GroupedRunEvents {
478 let mut events = GroupedRunEvents::default();
479 for run_event in self {
480 match run_event {
481 RunEvent::CaseStderr { .. } => events.stderr_events.push(run_event),
482
483 RunEvent::CaseStdout { .. } => events.stdout_events.push(run_event),
484
485 _ => events.non_artifact_events.push(run_event),
486 }
487 }
488 events
489 }
490}
491
492impl<T> GroupRunEventByTestCase for T where T: Iterator<Item = RunEvent> + Sized {}
493
494#[derive(Default)]
495struct FidlSuiteEventProcessor {
496 case_map: HashMap<u32, String>,
497 std_output_map: HashMap<u32, Vec<fasync::Task<Result<(), Error>>>>,
498}
499
500impl FidlSuiteEventProcessor {
501 fn new() -> Self {
502 FidlSuiteEventProcessor::default()
503 }
504
505 fn get_test_case_name(&self, identifier: u32) -> String {
506 self.case_map
507 .get(&identifier)
508 .unwrap_or_else(|| panic!("invalid test case identifier: {:?}", identifier))
509 .clone()
510 }
511
512 async fn process_event(
513 &mut self,
514 event: ftest_manager::Event,
515 mut sender: mpsc::Sender<SuiteEvent>,
516 filter_debug_data: bool,
517 compressed_debug_data: bool,
518 ) -> Result<(), Error> {
519 let timestamp = event.timestamp;
520 let e = match event.details.expect("Details cannot be null, please file bug.") {
521 ftest_manager::EventDetails::TestCaseFound(cf) => {
522 let test_case_name =
523 cf.test_case_name.expect("test_case_name must be specified, please file bug.");
524 self.case_map.insert(
525 cf.test_case_id.expect("test_case_id must be specified, please file bug."),
526 test_case_name.clone(),
527 );
528 SuiteEvent::case_found(timestamp, test_case_name).into()
529 }
530 ftest_manager::EventDetails::TestCaseStarted(cs) => {
531 let test_case_name = self.get_test_case_name(
532 cs.test_case_id.expect("test_case_id must be specified, please file bug."),
533 );
534 SuiteEvent::case_started(timestamp, test_case_name).into()
535 }
536 ftest_manager::EventDetails::TestCaseStopped(cs) => {
537 let test_case_name = self.get_test_case_name(
538 cs.test_case_id.expect("test_case_id must be specified, please file bug."),
539 );
540 if let Some(outputs) = self.std_output_map.remove(
541 &cs.test_case_id.expect("test_case_id must be specified, please file bug."),
542 ) {
543 for s in outputs {
544 s.await.context(format!(
545 "error collecting stdout/stderr of {}",
546 test_case_name
547 ))?;
548 }
549 }
550 SuiteEvent::case_stopped(
551 timestamp,
552 test_case_name,
553 cs.result.expect("result must be specified, please file bug."),
554 )
555 .into()
556 }
557 ftest_manager::EventDetails::TestCaseFinished(cf) => {
558 let test_case_name = self.get_test_case_name(
559 cf.test_case_id.expect("test_case_id must be specified, please file bug."),
560 );
561 SuiteEvent::case_finished(timestamp, test_case_name).into()
562 }
563 ftest_manager::EventDetails::TestCaseArtifactGenerated(ca) => {
564 let name = self.get_test_case_name(
565 ca.test_case_id.expect("test_case_id must be specified, please file bug."),
566 );
567 match ca.artifact.expect("artifact must be specified, please file bug.") {
568 ftest_manager::Artifact::Stdout(stdout) => {
569 let (s, mut r) = mpsc::channel(1024);
570 let stdout_task =
571 fasync::Task::spawn(collect_and_send_string_output(stdout, s));
572 let mut sender_clone = sender.clone();
573 let send_stdout_task = fasync::Task::spawn(async move {
574 while let Some(msg) = r.next().await {
575 sender_clone
576 .send(SuiteEvent::case_stdout(None, &name, msg))
577 .await
578 .context(format!("cannot send logs for {}", name))?;
579 }
580 Ok(())
581 });
582 match self.std_output_map.get_mut(
583 &ca.test_case_id
584 .expect("test_case_id must be specified, please file bug."),
585 ) {
586 Some(v) => {
587 v.push(stdout_task);
588 v.push(send_stdout_task);
589 }
590 None => {
591 self.std_output_map.insert(
592 ca.test_case_id
593 .expect("test_case_id must be specified, please file bug."),
594 vec![stdout_task, send_stdout_task],
595 );
596 }
597 }
598 None
599 }
600 ftest_manager::Artifact::Stderr(stderr) => {
601 let (s, mut r) = mpsc::channel(1024);
602 let stderr_task =
603 fasync::Task::spawn(collect_and_send_string_output(stderr, s));
604 let mut sender_clone = sender.clone();
605 let send_stderr_task = fasync::Task::spawn(async move {
606 while let Some(msg) = r.next().await {
607 sender_clone
608 .send(SuiteEvent::case_stderr(None, &name, msg))
609 .await
610 .context(format!("cannot send logs for {}", name))?;
611 }
612 Ok(())
613 });
614 match self.std_output_map.get_mut(
615 &ca.test_case_id
616 .expect("test_case_id must be specified, please file bug."),
617 ) {
618 Some(v) => {
619 v.push(stderr_task);
620 v.push(send_stderr_task);
621 }
622 None => {
623 self.std_output_map.insert(
624 ca.test_case_id
625 .expect("test_case_id must be specified, please file bug."),
626 vec![stderr_task, send_stderr_task],
627 );
628 }
629 }
630 None
631 }
632 ftest_manager::Artifact::Log(log) => match LogStream::from_syslog(log) {
633 Ok(log_stream) => {
634 SuiteEvent::test_case_log(timestamp, name, log_stream).into()
635 }
636 Err(e) => {
637 warn!("Cannot collect logs for test suite: {:?}", e);
638 None
639 }
640 },
641 _ => {
642 panic!("not supported")
643 }
644 }
645 }
646 ftest_manager::EventDetails::SuiteArtifactGenerated(sa) => {
647 match sa.artifact.expect("artifact must be specified, please file bug.") {
648 ftest_manager::Artifact::Stdout(_) => {
649 panic!("not supported")
650 }
651 ftest_manager::Artifact::Stderr(_) => {
652 panic!("not supported")
653 }
654 ftest_manager::Artifact::Log(log) => match LogStream::from_syslog(log) {
655 Ok(log_stream) => SuiteEvent::suite_log(timestamp, log_stream).into(),
656 Err(e) => {
657 warn!("Cannot collect logs for test suite: {:?}", e);
658 None
659 }
660 },
661 ftest_manager::Artifact::Custom(custom_artifact) => {
662 let ftest_manager::DirectoryAndToken { directory, token } =
663 custom_artifact.directory_and_token.unwrap();
664 let component_moniker = custom_artifact.component_moniker.unwrap();
665 let mut sender_clone = sender.clone();
666 fasync::Task::spawn(async move {
667 let directory = directory.into_proxy();
668 let entries: Vec<_> =
669 fuchsia_fs::directory::readdir_recursive(&directory, None)
670 .try_collect()
671 .await
672 .expect("read custom artifact directory");
673 for entry in entries.into_iter() {
674 let file = fuchsia_fs::directory::open_file_async(
675 &directory,
676 &entry.name,
677 fio::PERM_READABLE,
678 )
679 .unwrap();
680 let contents =
681 fuchsia_fs::file::read_to_string(&file).await.unwrap();
682 sender_clone
683 .send(SuiteEvent::suite_custom(
684 timestamp,
685 component_moniker.clone(),
686 entry.name,
687 contents,
688 ))
689 .await
690 .unwrap();
691 }
692 drop(token);
695 })
696 .detach();
697 None
698 }
699 ftest_manager::Artifact::DebugData(iterator) => {
700 if !filter_debug_data {
701 let mut sender_clone = sender.clone();
702 let proxy = iterator.into_proxy();
703 fasync::Task::spawn(async move {
704 loop {
705 let data = match compressed_debug_data {
706 true => proxy.get_next_compressed().await.unwrap(),
707 false => proxy.get_next().await.unwrap(),
708 };
709 if data.is_empty() {
710 break;
711 }
712 for data_file in data {
713 let socket =
714 data_file.socket.expect("File cannot be empty");
715 sender_clone
716 .send(SuiteEvent::debug_data(
717 timestamp,
718 data_file.name.expect("Name cannot be empty"),
719 socket,
720 ))
721 .await
722 .unwrap();
723 }
724 }
725 })
726 .detach();
727 }
728 None
729 }
730 _ => {
731 panic!("not supported")
732 }
733 }
734 }
735 ftest_manager::EventDetails::SuiteStarted(_started) => SuiteEvent {
736 timestamp,
737 payload: SuiteEventPayload::RunEvent(RunEvent::SuiteStarted),
738 }
739 .into(),
740 ftest_manager::EventDetails::SuiteStopped(stopped) => SuiteEvent {
741 timestamp,
742 payload: SuiteEventPayload::RunEvent(RunEvent::SuiteStopped {
743 status: stopped.result.expect("result must be specified, please file bug."),
744 }),
745 }
746 .into(),
747 EventDetailsUnknown!() => panic!("Unrecognized EventDetails"),
748 };
749 if let Some(item) = e {
750 sender.send(item).await.context("Cannot send event")?;
751 }
752 Ok(())
753 }
754}
755
756#[derive(Debug, thiserror::Error, Eq, PartialEq, Copy, Clone)]
757pub enum SuiteLaunchError {
758 #[error("Cannot enumerate tests")]
759 CaseEnumeration,
760
761 #[error("Cannot resolve test url")]
762 InstanceCannotResolve,
763
764 #[error("Invalid arguments passed")]
765 InvalidArgs,
766
767 #[error("Cannot connect to test suite")]
768 FailedToConnectToTestSuite,
769
770 #[error("resource unavailable")]
771 ResourceUnavailable,
772
773 #[error("Some internal error occurred. Please file bug")]
774 InternalError,
775
776 #[error("No test cases matched the provided filters")]
777 NoMatchingCases,
778}
779
780impl From<ftest_manager::LaunchError> for SuiteLaunchError {
781 fn from(error: ftest_manager::LaunchError) -> Self {
782 match error {
783 ftest_manager::LaunchError::ResourceUnavailable => {
784 SuiteLaunchError::ResourceUnavailable
785 }
786 ftest_manager::LaunchError::InstanceCannotResolve => {
787 SuiteLaunchError::InstanceCannotResolve
788 }
789 ftest_manager::LaunchError::InvalidArgs => SuiteLaunchError::InvalidArgs,
790 ftest_manager::LaunchError::FailedToConnectToTestSuite => {
791 SuiteLaunchError::FailedToConnectToTestSuite
792 }
793 ftest_manager::LaunchError::CaseEnumeration => SuiteLaunchError::CaseEnumeration,
794 ftest_manager::LaunchError::InternalError => SuiteLaunchError::InternalError,
795 ftest_manager::LaunchError::NoMatchingCases => SuiteLaunchError::NoMatchingCases,
796 ftest_manager::LaunchErrorUnknown!() => panic!("Encountered unknown launch error"),
797 }
798 }
799}
800
801pub struct SuiteRunInstance {
803 controller_proxy: Arc<SuiteControllerProxy>,
804}
805
806impl SuiteRunInstance {
807 pub fn controller(&self) -> Arc<SuiteControllerProxy> {
808 self.controller_proxy.clone()
809 }
810
811 pub async fn collect_events_with_watch(
812 &self,
813 sender: mpsc::Sender<SuiteEvent>,
814 filter_debug_data: bool,
815 compressed_debug_data: bool,
816 ) -> Result<(), Error> {
817 let controller_proxy = self.controller_proxy.clone();
818 let mut processor = FidlSuiteEventProcessor::new();
819 loop {
820 match controller_proxy.watch_events().await? {
821 Err(e) => return Err(SuiteLaunchError::from(e).into()),
822 Ok(events) => {
823 if events.len() == 0 {
824 break;
825 }
826 for event in events {
827 if let Err(e) = processor
828 .process_event(
829 event,
830 sender.clone(),
831 filter_debug_data,
832 compressed_debug_data,
833 )
834 .await
835 {
836 warn!("error running test suite: {:?}", e);
837 let _ = controller_proxy.kill();
838 return Ok(());
839 }
840 }
841 }
842 }
843 }
844 Ok(())
845 }
846}