test_runners_elf_lib/
test_server.rs

1// Copyright 2020 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::launcher::ComponentLauncher;
6use async_trait::async_trait;
7use fidl_fuchsia_test::{
8    self as ftest, Invocation, Result_ as TestResult, RunListenerProxy, Status,
9};
10use fuchsia_async as fasync;
11use futures::future::{abortable, AbortHandle};
12use futures::prelude::*;
13use futures::TryStreamExt;
14use log::{debug, error};
15use std::sync::{Arc, Weak};
16use test_runners_lib::cases::TestCaseInfo;
17use test_runners_lib::elf::{Component, EnumeratedTestCases, KernelError, SuiteServer};
18use test_runners_lib::errors::*;
19use test_runners_lib::logs::SocketLogWriter;
20
21/// Implements `fuchsia.test.Suite` and runs provided test.
22#[derive(Default)]
23pub struct TestServer<T: ComponentLauncher> {
24    pub launcher: T,
25}
26
27static PARALLEL_DEFAULT: u16 = 1;
28
29#[async_trait]
30impl<T: 'static> SuiteServer for TestServer<T>
31where
32    T: ComponentLauncher,
33{
34    /// Launches test process and gets test list out. Returns list of tests names in the format
35    /// defined by gtests, i.e FOO.Bar.
36    /// It only runs enumeration logic once, caches and returns the same result back on subsequent
37    /// calls.
38    async fn enumerate_tests(
39        &self,
40        _test_component: Arc<Component>,
41    ) -> Result<EnumeratedTestCases, EnumerationError> {
42        Ok(Arc::new(vec![TestCaseInfo { name: "main".to_string(), enabled: true }]))
43    }
44
45    async fn run_tests(
46        &self,
47        invocations: Vec<Invocation>,
48        run_options: ftest::RunOptions,
49        component: Arc<Component>,
50        run_listener: &RunListenerProxy,
51    ) -> Result<(), RunTestError> {
52        let num_parallel =
53            Self::get_parallel_count(run_options.parallel.unwrap_or(PARALLEL_DEFAULT));
54
55        let invocations = stream::iter(invocations);
56        invocations
57            .map(Ok)
58            .try_for_each_concurrent(num_parallel, |invocation| {
59                self.run_test(invocation, &run_options, component.clone(), run_listener)
60            })
61            .await
62    }
63
64    /// Run this server.
65    fn run(
66        self,
67        weak_component: Weak<Component>,
68        test_url: &str,
69        stream: ftest::SuiteRequestStream,
70    ) -> AbortHandle {
71        let test_url = test_url.to_owned();
72        let (fut, test_suite_abortable_handle) =
73            abortable(self.serve_test_suite(stream, weak_component));
74
75        fasync::Task::local(async move {
76            match fut.await {
77                Ok(result) => {
78                    if let Err(e) = result {
79                        error!("server failed for test {}: {:?}", test_url, e);
80                    }
81                }
82                Err(e) => debug!("server aborted for test {}: {:?}", test_url, e),
83            }
84            debug!("Done running server for {}.", test_url);
85        })
86        .detach();
87        test_suite_abortable_handle
88    }
89}
90
91impl<T: 'static> TestServer<T>
92where
93    T: ComponentLauncher,
94{
95    pub fn new(launcher_: T) -> Self {
96        Self { launcher: launcher_ }
97    }
98
99    pub fn validate_args(_args: &Vec<String>) -> Result<(), ArgumentError> {
100        // Unopinionated about args,
101        // they're passed through to the test program unfiltered
102        Ok(())
103    }
104
105    async fn run_test<'a>(
106        &'a self,
107        invocation: Invocation,
108        run_options: &ftest::RunOptions,
109        component: Arc<Component>,
110        run_listener: &RunListenerProxy,
111    ) -> Result<(), RunTestError> {
112        if "main" != *invocation.name.as_ref().ok_or(RunTestError::TestCaseName)? {
113            // "main" is the only valid test case name
114            return Ok(());
115        }
116
117        let (test_stdout, stdout_client) = zx::Socket::create_stream();
118        let (test_stderr, stderr_client) = zx::Socket::create_stream();
119        let (case_listener_proxy, listener) =
120            fidl::endpoints::create_proxy::<fidl_fuchsia_test::CaseListenerMarker>();
121        run_listener
122            .on_test_case_started(
123                &invocation,
124                ftest::StdHandles {
125                    out: Some(stdout_client),
126                    err: Some(stderr_client),
127                    ..Default::default()
128                },
129                listener,
130            )
131            .map_err(RunTestError::SendStart)?;
132        let mut test_stdout = SocketLogWriter::new(fasync::Socket::from_socket(test_stdout));
133        let mut test_stderr = SocketLogWriter::new(fasync::Socket::from_socket(test_stderr));
134
135        let mut args = component.args.clone();
136        if let Some(user_args) = &run_options.arguments {
137            args.extend(user_args.clone());
138        }
139
140        // Launch test program
141        let (process, _job, stdout_logger, stderr_logger) =
142            self.launcher.launch_process(&component, args).await?;
143
144        // Drain stdout
145        let stdout_fut = stdout_logger.buffer_and_drain(&mut test_stdout);
146        // Drain stderr
147        let stderr_fut = stderr_logger.buffer_and_drain(&mut test_stderr);
148        let (ret1, ret2) = futures::future::join(stdout_fut, stderr_fut).await;
149        ret1?;
150        ret2?;
151
152        // Wait for test to return
153        fasync::OnSignals::new(&process, zx::Signals::PROCESS_TERMINATED)
154            .await
155            .map_err(KernelError::ProcessExit)
156            .unwrap();
157        let process_info = process.info().map_err(RunTestError::ProcessInfo)?;
158
159        // Map return value of zero to Passed, non-zero to Failed
160        let status = match process_info.return_code {
161            0 => Status::Passed,
162            _ => Status::Failed,
163        };
164        case_listener_proxy
165            .finished(&TestResult { status: Some(status), ..Default::default() })
166            .map_err(RunTestError::SendFinish)?;
167        Ok(())
168    }
169}