1#![allow(unused_crate_dependencies)]
2use anyhow::{Context, Error, format_err};
7use fidl::endpoints;
8use fidl::endpoints::{ClientEnd, Proxy};
9use fidl_fuchsia_component as fcomponent;
10use fidl_fuchsia_component_decl as fdecl;
11use fidl_fuchsia_component_runner as fcrunner;
12use fidl_fuchsia_io as fio;
13use fidl_fuchsia_test::CaseListenerRequest::Finished;
14use fidl_fuchsia_test::RunListenerRequest::{OnFinished, OnTestCaseStarted};
15use fidl_fuchsia_test::{Invocation, Result_ as TestResult, RunListenerRequestStream};
16use fidl_fuchsia_test_manager as ftest_manager;
17use fuchsia_async as fasync;
18use fuchsia_component::client::{self, connect_to_protocol_at_dir_root};
19use fuchsia_runtime::job_default;
20use futures::channel::mpsc;
21use futures::prelude::*;
22use namespace::{Namespace, NamespaceError};
23use std::collections::HashMap;
24use std::sync::Arc;
25use test_manager_test_lib::RunEvent;
26use test_runners_lib::elf::{BuilderArgs, Component};
27
28#[derive(PartialEq, Debug)]
29pub enum ListenerEvent {
30 StartTest(String),
31 FinishTest(String, TestResult),
32 FinishAllTests,
33}
34
35fn get_ord_index_and_name(event: &ListenerEvent) -> (usize, &str) {
36 match event {
37 ListenerEvent::StartTest(name) => (0, name),
38 ListenerEvent::FinishTest(name, _) => (1, name),
39 ListenerEvent::FinishAllTests => (2, ""),
40 }
41}
42
43impl Ord for ListenerEvent {
45 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
46 let (s_index, s_test_name) = get_ord_index_and_name(self);
47 let (o_index, o_test_name) = get_ord_index_and_name(other);
48 if s_test_name == o_test_name || s_index == 2 || o_index == 2 {
49 return s_index.cmp(&o_index);
50 }
51 return s_test_name.cmp(&o_test_name);
52 }
53}
54
55pub fn assert_event_ord(events: &Vec<ListenerEvent>) {
58 let mut tests = HashMap::new();
59 let mut all_finish = false;
60 for event in events {
61 assert!(!all_finish, "got FinishAllTests event twice: {:#?}", events);
62 match event {
63 ListenerEvent::StartTest(name) => {
64 assert!(
65 !tests.contains_key(&name),
66 "Multiple StartTest for test {}: {:#?}",
67 name,
68 events
69 );
70 tests.insert(name, false);
71 }
72 ListenerEvent::FinishTest(name, _) => {
73 assert!(
74 tests.contains_key(&name),
75 "Got finish before start event for test {}: {:#?}",
76 name,
77 events
78 );
79 assert!(
80 !tests.insert(name, true).unwrap(),
81 "Multiple FinishTest for test {}: {:#?}",
82 name,
83 events
84 );
85 }
86 ListenerEvent::FinishAllTests => {
87 all_finish = true;
88 }
89 }
90 }
91}
92
93impl PartialOrd for ListenerEvent {
94 fn partial_cmp(&self, other: &ListenerEvent) -> Option<core::cmp::Ordering> {
95 Some(self.cmp(other))
96 }
97}
98
99impl Eq for ListenerEvent {}
100
101impl ListenerEvent {
102 pub fn start_test(name: &str) -> ListenerEvent {
103 ListenerEvent::StartTest(name.to_string())
104 }
105 pub fn finish_test(name: &str, test_result: TestResult) -> ListenerEvent {
106 ListenerEvent::FinishTest(name.to_string(), test_result)
107 }
108 pub fn finish_all_test() -> ListenerEvent {
109 ListenerEvent::FinishAllTests
110 }
111}
112
113impl Clone for ListenerEvent {
114 fn clone(&self) -> Self {
115 match self {
116 ListenerEvent::StartTest(name) => ListenerEvent::start_test(name),
117 ListenerEvent::FinishTest(name, test_result) => ListenerEvent::finish_test(
118 name,
119 TestResult { status: test_result.status.clone(), ..Default::default() },
120 ),
121 ListenerEvent::FinishAllTests => ListenerEvent::finish_all_test(),
122 }
123 }
124}
125
126pub async fn collect_listener_event(
128 mut listener: RunListenerRequestStream,
129) -> Result<Vec<ListenerEvent>, Error> {
130 let mut ret = vec![];
131 let mut loggers = vec![];
133 while let Some(result_event) = listener.try_next().await? {
134 match result_event {
135 OnTestCaseStarted { invocation, std_handles, listener, .. } => {
136 let name = invocation.name.unwrap();
137 ret.push(ListenerEvent::StartTest(name.clone()));
138 loggers.push(std_handles);
139 let mut listener = listener.into_stream();
140 #[allow(clippy::never_loop)]
143 while let Some(result) = listener.try_next().await? {
144 match result {
145 Finished { result, .. } => {
146 ret.push(ListenerEvent::FinishTest(name, result));
147 break;
148 }
149 }
150 }
151 }
152 OnFinished { .. } => {
153 ret.push(ListenerEvent::FinishAllTests);
154 break;
155 }
156 }
157 }
158 Ok(ret)
159}
160
161pub fn names_to_invocation(names: Vec<&str>) -> Vec<Invocation> {
163 names
164 .iter()
165 .map(|s| Invocation { name: Some(s.to_string()), tag: None, ..Default::default() })
166 .collect()
167}
168
169pub async fn process_events(
171 suite_instance: test_manager_test_lib::SuiteRunInstance,
172 exclude_empty_logs: bool,
173) -> Result<(Vec<RunEvent>, Vec<String>), Error> {
174 let (sender, mut recv) = mpsc::channel(1);
175 let execution_task = fasync::Task::spawn(async move {
176 suite_instance.collect_events_with_watch(sender, true, true).await
177 });
178 let mut events = vec![];
179 let mut log_tasks = vec![];
180 let mut buffered_stdout = HashMap::new();
181 let mut buffered_stderr = HashMap::new();
182 while let Some(event) = recv.next().await {
183 match event.payload {
184 test_manager_test_lib::SuiteEventPayload::RunEvent(RunEvent::CaseStdout {
185 name,
186 stdout_message,
187 }) => {
188 let strings = line_buffer_std_message(
189 &name,
190 stdout_message,
191 exclude_empty_logs,
192 &mut buffered_stdout,
193 );
194 for s in strings {
195 events.push(RunEvent::case_stdout(name.clone(), s));
196 }
197 }
198 test_manager_test_lib::SuiteEventPayload::RunEvent(RunEvent::CaseStderr {
199 name,
200 stderr_message,
201 }) => {
202 let strings = line_buffer_std_message(
203 &name,
204 stderr_message,
205 exclude_empty_logs,
206 &mut buffered_stderr,
207 );
208 for s in strings {
209 events.push(RunEvent::case_stderr(name.clone(), s));
210 }
211 }
212 test_manager_test_lib::SuiteEventPayload::RunEvent(e) => events.push(e),
213 test_manager_test_lib::SuiteEventPayload::SuiteLog { log_stream } => {
214 let t = fasync::Task::spawn(log_stream.collect::<Vec<_>>());
215 log_tasks.push(t);
216 }
217 test_manager_test_lib::SuiteEventPayload::TestCaseLog { .. } => {
218 panic!("not supported yet!")
219 }
220 test_manager_test_lib::SuiteEventPayload::DebugData { .. } => {
221 panic!("not supported yet!")
222 }
223 }
224 }
225 execution_task.await.context("test execution failed")?;
226
227 for (name, log) in buffered_stdout {
228 events.push(RunEvent::case_stdout(name, log));
229 }
230 for (name, log) in buffered_stderr {
231 events.push(RunEvent::case_stderr(name, log));
232 }
233
234 let mut collected_logs = vec![];
235 for t in log_tasks {
236 let logs = t.await;
237 for log_result in logs {
238 let log = log_result?;
239 collected_logs.push(log.msg().unwrap().to_string());
240 }
241 }
242
243 Ok((events, collected_logs))
244}
245
246fn line_buffer_std_message(
248 name: &str,
249 std_message: String,
250 exclude_empty_logs: bool,
251 buffer: &mut HashMap<String, String>,
252) -> Vec<String> {
253 let mut ret = vec![];
254 let logs = std_message.split("\n");
255 let mut logs = logs.collect::<Vec<&str>>();
256 let mut last_incomplete_line = logs.pop();
258 if std_message.as_bytes().last() == Some(&b'\n') {
259 last_incomplete_line = None;
260 }
261 for log in logs {
262 if exclude_empty_logs && log.len() == 0 {
263 continue;
264 }
265 let mut msg = log.to_owned();
266 if let Some(prev_log) = buffer.remove(name) {
269 msg = format!("{}{}", prev_log, msg);
270 }
271 ret.push(msg);
272 }
273 if let Some(log) = last_incomplete_line {
274 let mut log = log.to_owned();
275 if let Some(prev_log) = buffer.remove(name) {
276 log = format!("{}{}", prev_log, log);
277 }
278 buffer.insert(name.to_string(), log);
279 }
280 ret
281}
282
283pub async fn connect_to_suite_runner() -> Result<ftest_manager::SuiteRunnerProxy, Error> {
285 let realm = client::connect_to_protocol::<fcomponent::RealmMarker>()
286 .context("could not connect to Realm service")?;
287
288 let child_ref = fdecl::ChildRef { name: "test_manager".to_owned(), collection: None };
289 let (dir, server_end) = endpoints::create_proxy::<fio::DirectoryMarker>();
290 realm
291 .open_exposed_dir(&child_ref, server_end)
292 .await
293 .context("open_exposed_dir fidl call failed for test manager")?
294 .map_err(|e| format_err!("failed to create test manager: {:?}", e))?;
295
296 connect_to_protocol_at_dir_root::<ftest_manager::SuiteRunnerMarker>(&dir)
297 .context("failed to open test suite runner service")
298}
299
300fn create_ns_from_current_ns(
301 dir_paths: Vec<(&str, fio::Flags)>,
302) -> Result<Namespace, NamespaceError> {
303 let mut ns = vec![];
304 for (path, permission) in dir_paths {
305 let chan = fuchsia_fs::directory::open_in_namespace(path, permission)
306 .unwrap()
307 .into_channel()
308 .unwrap()
309 .into_zx_channel();
310 let handle = ClientEnd::new(chan);
311
312 ns.push(fcrunner::ComponentNamespaceEntry {
313 path: Some(path.to_string()),
314 directory: Some(handle),
315 ..Default::default()
316 });
317 }
318 Namespace::try_from(ns)
319}
320
321pub async fn test_component(
323 url: &str,
324 name: &str,
325 binary: &str,
326 args: Vec<String>,
327) -> Result<Arc<Component>, Error> {
328 let ns = create_ns_from_current_ns(vec![
329 ("/pkg", fuchsia_fs::PERM_READABLE | fuchsia_fs::PERM_EXECUTABLE),
330 ("/svc", fuchsia_fs::PERM_READABLE | fuchsia_fs::PERM_EXECUTABLE),
332 ])?;
333 let component = Component::create_for_tests(BuilderArgs {
334 url: url.to_string(),
335 name: name.to_string(),
336 binary: binary.to_string(),
337 args,
338 environ: None,
339 ns,
340 job: job_default().duplicate_handle(zx::Rights::SAME_RIGHTS)?,
341 options: zx::ProcessOptions::empty(),
342 config: None,
343 })
344 .await?;
345 Ok(Arc::new(component))
346}
347
348#[cfg(test)]
349mod tests {
350 use super::*;
351 use fidl_fuchsia_test::Status;
352 use maplit::hashmap;
353
354 #[test]
355 fn test_ordering_by_enum() {
356 let expected_events = vec![
357 ListenerEvent::start_test("a"),
358 ListenerEvent::finish_test(
359 "a",
360 TestResult { status: Some(Status::Passed), ..Default::default() },
361 ),
362 ListenerEvent::finish_all_test(),
363 ];
364
365 let mut events = expected_events.clone();
366 events.reverse();
367
368 assert_ne!(events, expected_events);
369 events.sort();
370 assert_eq!(events, expected_events);
371 }
372
373 #[test]
374 fn test_ordering_by_test_name() {
375 let mut events = vec![
376 ListenerEvent::start_test("b"),
377 ListenerEvent::start_test("a"),
378 ListenerEvent::finish_test(
379 "a",
380 TestResult { status: Some(Status::Passed), ..Default::default() },
381 ),
382 ListenerEvent::start_test("c"),
383 ListenerEvent::finish_test(
384 "b",
385 TestResult { status: Some(Status::Passed), ..Default::default() },
386 ),
387 ListenerEvent::finish_test(
388 "c",
389 TestResult { status: Some(Status::Passed), ..Default::default() },
390 ),
391 ListenerEvent::finish_all_test(),
392 ];
393
394 let expected_events = vec![
395 ListenerEvent::start_test("a"),
396 ListenerEvent::finish_test(
397 "a",
398 TestResult { status: Some(Status::Passed), ..Default::default() },
399 ),
400 ListenerEvent::start_test("b"),
401 ListenerEvent::finish_test(
402 "b",
403 TestResult { status: Some(Status::Passed), ..Default::default() },
404 ),
405 ListenerEvent::start_test("c"),
406 ListenerEvent::finish_test(
407 "c",
408 TestResult { status: Some(Status::Passed), ..Default::default() },
409 ),
410 ListenerEvent::finish_all_test(),
411 ];
412 events.sort();
413 assert_eq!(events, expected_events);
414 }
415
416 #[test]
417 fn line_buffer_std_message_incomplete_line() {
418 let mut buf = HashMap::new();
419 buf.insert("test".to_string(), "some_prev_text".to_string());
420 let strings = line_buffer_std_message("test", "a \nb\nc\nd".into(), false, &mut buf);
421 assert_eq!(strings, vec!["some_prev_texta ".to_owned(), "b".to_owned(), "c".to_owned()]);
422 assert_eq!(buf, hashmap! {"test".to_string() => "d".to_string()});
423 }
424
425 #[test]
426 fn line_buffer_std_message_complete_line() {
427 let mut buf = HashMap::new();
428 buf.insert("test".to_string(), "some_prev_text".to_string());
429 let strings = line_buffer_std_message("test", "a \nb\nc\n".into(), false, &mut buf);
430 assert_eq!(strings, vec!["some_prev_texta ".to_owned(), "b".to_owned(), "c".to_owned()]);
431 assert_eq!(buf.len(), 0);
432
433 let strings = line_buffer_std_message("test", "d \ne\nf\n".into(), false, &mut buf);
435 assert_eq!(strings, vec!["d ".to_owned(), "e".to_owned(), "f".to_owned()]);
436 assert_eq!(buf.len(), 0);
437 }
438}