Skip to main content

test_manager_lib/
test_manager_server.rs

1// Copyright 2022 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::above_root_capabilities::AboveRootCapabilitiesForTest;
6use crate::debug_data_processor::{DebugDataDirectory, DebugDataProcessor};
7use crate::error::TestManagerError;
8use crate::offers::map_offers;
9use crate::running_suite::{RunningSuite, enumerate_test_cases};
10use crate::self_diagnostics::RootDiagnosticNode;
11use crate::test_suite::{Suite, SuiteRealm};
12use crate::{constants, debug_data_server, facet};
13use fidl::Error;
14use fidl_fuchsia_component_resolution::ResolverProxy;
15use fidl_fuchsia_pkg::PackageResolverProxy;
16use fidl_fuchsia_test_manager as ftest_manager;
17use ftest_manager::LaunchError;
18use fuchsia_async::{self as fasync};
19use futures::prelude::*;
20use log::{error, warn};
21use std::sync::Arc;
22
23pub async fn serve_early_boot_profiles(
24    mut stream: ftest_manager::EarlyBootProfileRequestStream,
25) -> Result<(), TestManagerError> {
26    while let Some(req) = stream.try_next().await.map_err(TestManagerError::Stream)? {
27        match req {
28            ftest_manager::EarlyBootProfileRequest::RegisterWatcher {
29                iterator,
30                control_handle,
31            } => {
32                let iterator = iterator.into_stream();
33                if let Err(e) = debug_data_server::send_kernel_debug_data(iterator).await {
34                    warn!("Err serving kernel profiles: {}", e);
35                    control_handle.shutdown_with_epitaph(zx::Status::INTERNAL);
36                    break;
37                }
38            }
39            ftest_manager::EarlyBootProfileRequest::_UnknownMethod {
40                ordinal,
41                control_handle,
42                ..
43            } => {
44                warn!("Unknown EarlyBootProfile request received: {}, closing connection", ordinal);
45                control_handle.shutdown_with_epitaph(zx::Status::NOT_SUPPORTED);
46                break;
47            }
48        }
49    }
50    Ok(())
51}
52
53/// Start `TestCaseEnumerator` server and serve it over `stream`.
54pub async fn run_test_manager_test_case_enumerator_server(
55    mut stream: ftest_manager::TestCaseEnumeratorRequestStream,
56    resolver: Arc<ResolverProxy>,
57    pkg_resolver: Arc<PackageResolverProxy>,
58    above_root_capabilities_for_test: Arc<AboveRootCapabilitiesForTest>,
59    root_diagnostics: &RootDiagnosticNode,
60) -> Result<(), TestManagerError> {
61    while let Some(req) = stream.try_next().await.map_err(TestManagerError::Stream)? {
62        match req {
63            ftest_manager::TestCaseEnumeratorRequest::Enumerate {
64                test_suite_url,
65                options,
66                iterator,
67                responder,
68            } => {
69                let realm = if let Some(realm_options) = options.realm_options {
70                    let realm_proxy = match realm_options
71                        .realm
72                        .map(|r| Ok(r.into_proxy()))
73                        .unwrap_or(Err(Error::NotNullable))
74                    {
75                        Ok(r) => r,
76                        Err(e) => {
77                            warn!(
78                                "Cannot enumerate test cases {}, invalid realm. Closing connection. error: {}",
79                                test_suite_url, e
80                            );
81                            responder.send(Err(LaunchError::InvalidArgs)).ok();
82                            break;
83                        }
84                    };
85                    let offers = match realm_options
86                        .offers
87                        .map(map_offers)
88                        .unwrap_or(Err(Error::NotNullable.into()))
89                    {
90                        Ok(offers) => offers,
91                        Err(e) => {
92                            warn!(
93                                "Cannot enumerate test cases {}, invalid offers. error: {}",
94                                test_suite_url, e
95                            );
96                            responder.send(Err(LaunchError::InvalidArgs)).ok();
97                            break;
98                        }
99                    };
100                    let test_collection = match realm_options.test_collection {
101                        Some(test_collection) => test_collection,
102                        None => {
103                            warn!(
104                                "Cannot enumerate test cases {}, missing test collection.",
105                                test_suite_url
106                            );
107                            responder.send(Err(LaunchError::InvalidArgs)).ok();
108                            break;
109                        }
110                    };
111                    Some(SuiteRealm { realm_proxy, offers, test_collection })
112                } else {
113                    None
114                };
115
116                let iterator = iterator.into_stream();
117                let (_processor, sender) = DebugDataProcessor::new(DebugDataDirectory::Isolated {
118                    parent: constants::ISOLATED_TMP,
119                });
120                let diagnostics = root_diagnostics.child();
121                let launch_fut = facet::get_suite_facets(test_suite_url.clone(), resolver.clone())
122                    .and_then(|facets| {
123                        RunningSuite::launch(
124                            &test_suite_url,
125                            facets,
126                            resolver.clone(),
127                            pkg_resolver.clone(),
128                            above_root_capabilities_for_test.clone(),
129                            sender,
130                            &diagnostics,
131                            &realm,
132                            false, // use_debug_agent
133                        )
134                    });
135                match launch_fut.await {
136                    Ok(suite_instance) => {
137                        let suite = match suite_instance.connect_to_suite() {
138                            Ok(proxy) => proxy,
139                            Err(e) => {
140                                responder.send(Err(e.into())).ok();
141                                continue;
142                            }
143                        };
144                        let enumeration_result = enumerate_test_cases(&suite, None).await;
145                        let t = fasync::Task::spawn(suite_instance.destroy(diagnostics));
146                        match enumeration_result {
147                            Ok(invocations) => {
148                                if let Ok(names) = invocations
149                                    .into_iter()
150                                    .map(|i| i.name.ok_or(()))
151                                    .collect::<Result<Vec<String>, ()>>()
152                                {
153                                    responder.send(Ok(())).ok();
154                                    drain_test_case_names(iterator, names).await;
155                                } else {
156                                    responder.send(Err(LaunchError::CaseEnumeration)).ok();
157                                }
158                            }
159                            Err(err) => {
160                                warn!(err:?; "cannot enumerate tests for {}", test_suite_url);
161                                responder.send(Err(LaunchError::CaseEnumeration)).ok();
162                            }
163                        }
164                        if let Err(err) = t.await {
165                            warn!(err:?; "Error destroying test realm for {}", test_suite_url);
166                        }
167                    }
168                    Err(e) => {
169                        responder.send(Err(e.into())).ok();
170                    }
171                }
172            }
173
174            ftest_manager::TestCaseEnumeratorRequest::_UnknownMethod {
175                ordinal,
176                control_handle,
177                ..
178            } => {
179                warn!("Unknown query request received: {}, closing connection", ordinal);
180                control_handle.shutdown_with_epitaph(zx::Status::NOT_SUPPORTED);
181                break;
182            }
183        };
184    }
185    Ok(())
186}
187
188async fn drain_test_case_names(
189    mut iterator: ftest_manager::TestCaseIteratorRequestStream,
190    names: Vec<String>,
191) {
192    const NAMES_CHUNK: usize = 50;
193    let mut names = names.chunks(NAMES_CHUNK);
194    while let Ok(Some(request)) = iterator.try_next().await {
195        match request {
196            ftest_manager::TestCaseIteratorRequest::GetNext { responder } => match names.next() {
197                Some(names) => {
198                    responder
199                        .send(
200                            &names
201                                .iter()
202                                .map(|s| ftest_manager::TestCase {
203                                    name: Some(s.into()),
204                                    ..Default::default()
205                                })
206                                .collect::<Vec<_>>(),
207                        )
208                        .ok();
209                }
210                None => {
211                    responder.send(&[]).ok();
212                }
213            },
214        }
215    }
216}
217
218/// Start `SuiteRunner` server and serve it over `stream`.
219pub async fn run_test_manager_suite_runner_server(
220    mut stream: ftest_manager::SuiteRunnerRequestStream,
221    resolver: Arc<ResolverProxy>,
222    pkg_resolver: Arc<PackageResolverProxy>,
223    above_root_capabilities_for_test: Arc<AboveRootCapabilitiesForTest>,
224    root_diagnostics: &RootDiagnosticNode,
225) -> Result<(), TestManagerError> {
226    while let Some(req) = stream.try_next().await.map_err(TestManagerError::Stream)? {
227        match req {
228            ftest_manager::SuiteRunnerRequest::Run {
229                test_suite_url,
230                mut options,
231                controller,
232                control_handle,
233            } => {
234                let realm = if let Some(realm_options) = options.realm_options.take() {
235                    let realm_proxy = match realm_options
236                        .realm
237                        .map(|r| Ok(r.into_proxy()))
238                        .unwrap_or(Err(Error::NotNullable))
239                    {
240                        Ok(r) => r,
241                        Err(e) => {
242                            warn!(
243                                "Cannot run suite {}, invalid realm. Closing connection. error: {}",
244                                test_suite_url, e
245                            );
246                            control_handle.shutdown_with_epitaph(zx::Status::INVALID_ARGS);
247                            break;
248                        }
249                    };
250                    let offers = match realm_options
251                        .offers
252                        .map(map_offers)
253                        .unwrap_or(Err(Error::NotNullable.into()))
254                    {
255                        Ok(offers) => offers,
256                        Err(e) => {
257                            warn!(
258                                "Cannot run suite {}, invalid offers. error: {}",
259                                test_suite_url, e
260                            );
261                            control_handle.shutdown_with_epitaph(zx::Status::INVALID_ARGS);
262                            break;
263                        }
264                    };
265                    let test_collection = match realm_options.test_collection {
266                        Some(test_collection) => test_collection,
267                        None => {
268                            warn!("Cannot run suite {}, missing test collection.", test_suite_url);
269                            control_handle.shutdown_with_epitaph(zx::Status::INVALID_ARGS);
270                            break;
271                        }
272                    };
273                    Some(SuiteRealm { realm_proxy, offers, test_collection })
274                } else {
275                    None
276                };
277
278                // Validate log iterator type.
279                match options.logs_iterator_type {
280                    Some(ftest_manager::LogsIteratorType::Batch)
281                    | Some(ftest_manager::LogsIteratorType::Socket)
282                    | None => {
283                        // Accepted
284                    }
285                    Some(t) => {
286                        error!("Unrecognized log iterator type {:?}.", t);
287                        control_handle.shutdown_with_epitaph(zx::Status::INVALID_ARGS);
288                        break;
289                    }
290                }
291
292                let controller = controller.into_stream();
293                let accumulate_debug_data = options.accumulate_debug_data.unwrap_or(false);
294
295                let suite = Suite {
296                    realm: realm.into(),
297                    test_url: test_suite_url,
298                    options,
299                    controller,
300                    resolver: resolver.clone(),
301                    pkg_resolver: pkg_resolver.clone(),
302                    above_root_capabilities_for_test: above_root_capabilities_for_test.clone(),
303                    facets: facet::ResolveStatus::Unresolved,
304                };
305
306                let diagnostics = root_diagnostics.child();
307
308                suite.run(diagnostics, accumulate_debug_data).await;
309            }
310
311            ftest_manager::SuiteRunnerRequest::_UnknownMethod {
312                ordinal, control_handle, ..
313            } => {
314                warn!("Unknown run builder request received: {}, closing connection", ordinal);
315                control_handle.shutdown_with_epitaph(zx::Status::NOT_SUPPORTED);
316                break;
317            }
318        }
319    }
320    Ok(())
321}