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::{enumerate_test_cases, RunningSuite};
10use crate::self_diagnostics::RootDiagnosticNode;
11use crate::test_suite::{Suite, SuiteRealm, TestRunBuilder};
12use crate::{constants, debug_data_server, facet};
13use fidl::endpoints::ControlHandle;
14use fidl::Error;
15use fidl_fuchsia_component_resolution::ResolverProxy;
16use fidl_fuchsia_pkg::PackageResolverProxy;
17use fidl_fuchsia_test_manager as ftest_manager;
18use fidl_fuchsia_test_manager::{QueryEnumerateInRealmResponder, QueryEnumerateResponder};
19use ftest_manager::LaunchError;
20use fuchsia_async::{self as fasync};
21use futures::prelude::*;
22use log::warn;
23use std::sync::Arc;
24
25/// Start `RunBuilder` server and serve it over `stream`.
26pub async fn run_test_manager_run_builder_server(
27    mut stream: ftest_manager::RunBuilderRequestStream,
28    resolver: Arc<ResolverProxy>,
29    pkg_resolver: Arc<PackageResolverProxy>,
30    above_root_capabilities_for_test: Arc<AboveRootCapabilitiesForTest>,
31    root_diagnostics: &RootDiagnosticNode,
32) -> Result<(), TestManagerError> {
33    let mut builder = TestRunBuilder { suites: vec![] };
34    let mut scheduling_options: Option<ftest_manager::SchedulingOptions> = None;
35    while let Some(event) = stream.try_next().await.map_err(TestManagerError::Stream)? {
36        match event {
37            ftest_manager::RunBuilderRequest::AddSuite {
38                test_url,
39                options,
40                controller,
41                control_handle: _,
42            } => {
43                let controller = controller.into_stream();
44
45                builder.suites.push(Suite {
46                    realm: None,
47                    test_url,
48                    options,
49                    controller,
50                    resolver: resolver.clone(),
51                    pkg_resolver: pkg_resolver.clone(),
52                    above_root_capabilities_for_test: above_root_capabilities_for_test.clone(),
53                    facets: facet::ResolveStatus::Unresolved,
54                });
55            }
56            ftest_manager::RunBuilderRequest::AddSuiteInRealm {
57                realm,
58                offers,
59                test_collection,
60                test_url,
61                options,
62                controller,
63                control_handle,
64            } => {
65                let realm_proxy = realm.into_proxy();
66                let controller = controller.into_stream();
67                let offers = match map_offers(offers) {
68                    Ok(offers) => offers,
69                    Err(e) => {
70                        warn!("Cannot add suite {}, invalid offers. error: {}", test_url, e);
71                        control_handle.shutdown_with_epitaph(zx::Status::INVALID_ARGS);
72                        break;
73                    }
74                };
75
76                builder.suites.push(Suite {
77                    realm: SuiteRealm { realm_proxy, offers, test_collection }.into(),
78                    test_url,
79                    options,
80                    controller,
81                    resolver: resolver.clone(),
82                    pkg_resolver: pkg_resolver.clone(),
83                    above_root_capabilities_for_test: above_root_capabilities_for_test.clone(),
84                    facets: facet::ResolveStatus::Unresolved,
85                });
86            }
87            ftest_manager::RunBuilderRequest::WithSchedulingOptions { options, .. } => {
88                scheduling_options = Some(options);
89            }
90            ftest_manager::RunBuilderRequest::Build { controller, control_handle: _ } => {
91                let controller = controller.into_stream();
92
93                let persist_diagnostics =
94                    match scheduling_options.as_ref().map(|options| options.max_parallel_suites) {
95                        Some(Some(_)) => true,
96                        Some(None) | None => false,
97                    };
98                let diagnostics = match persist_diagnostics {
99                    true => root_diagnostics.persistent_child(),
100                    false => root_diagnostics.child(),
101                };
102
103                builder.run(controller, diagnostics, scheduling_options).await;
104                // clients should reconnect to run new tests.
105                break;
106            }
107            ftest_manager::RunBuilderRequest::_UnknownMethod {
108                ordinal, control_handle, ..
109            } => {
110                warn!("Unknown run builder request received: {}, closing connection", ordinal);
111                control_handle.shutdown_with_epitaph(zx::Status::NOT_SUPPORTED);
112                break;
113            }
114        }
115    }
116    Ok(())
117}
118
119enum QueryResponder {
120    Enumerate(QueryEnumerateResponder),
121    EnumerateInRealm(QueryEnumerateInRealmResponder),
122}
123
124impl QueryResponder {
125    fn send(self, result: Result<(), LaunchError>) -> Result<(), fidl::Error> {
126        match self {
127            QueryResponder::Enumerate(responder) => responder.send(result),
128            QueryResponder::EnumerateInRealm(responder) => responder.send(result),
129        }
130    }
131}
132
133/// Start `Query` server and serve it over `stream`.
134pub async fn run_test_manager_query_server(
135    mut stream: ftest_manager::QueryRequestStream,
136    resolver: Arc<ResolverProxy>,
137    pkg_resolver: Arc<PackageResolverProxy>,
138    above_root_capabilities_for_test: Arc<AboveRootCapabilitiesForTest>,
139    root_diagnostics: &RootDiagnosticNode,
140) -> Result<(), TestManagerError> {
141    while let Some(event) = stream.try_next().await.map_err(TestManagerError::Stream)? {
142        let (test_url, iterator, realm, responder) = match event {
143            ftest_manager::QueryRequest::Enumerate { test_url, iterator, responder } => {
144                (test_url, iterator, None, QueryResponder::Enumerate(responder))
145            }
146            ftest_manager::QueryRequest::EnumerateInRealm {
147                test_url,
148                realm,
149                offers,
150                test_collection,
151                iterator,
152                responder,
153            } => {
154                let realm_proxy = realm.into_proxy();
155                let offers = match map_offers(offers) {
156                    Ok(offers) => offers,
157                    Err(e) => {
158                        warn!("Cannot add suite {}, invalid offers. error: {}", test_url, e);
159                        responder.send(Err(LaunchError::InvalidArgs)).ok();
160                        break;
161                    }
162                };
163                (
164                    test_url,
165                    iterator,
166                    SuiteRealm { realm_proxy, offers, test_collection }.into(),
167                    QueryResponder::EnumerateInRealm(responder),
168                )
169            }
170
171            ftest_manager::QueryRequest::_UnknownMethod { ordinal, control_handle, .. } => {
172                warn!("Unknown query request received: {}, closing connection", ordinal);
173                control_handle.shutdown_with_epitaph(zx::Status::NOT_SUPPORTED);
174                break;
175            }
176        };
177        let mut iterator = iterator.into_stream();
178        let (_processor, sender) = DebugDataProcessor::new(DebugDataDirectory::Isolated {
179            parent: constants::ISOLATED_TMP,
180        });
181        let diagnostics = root_diagnostics.child();
182        let launch_fut =
183            facet::get_suite_facets(test_url.clone(), resolver.clone()).and_then(|facets| {
184                RunningSuite::launch(
185                    &test_url,
186                    facets,
187                    resolver.clone(),
188                    pkg_resolver.clone(),
189                    above_root_capabilities_for_test.clone(),
190                    sender,
191                    &diagnostics,
192                    &realm,
193                    false, // use_debug_agent
194                )
195            });
196        match launch_fut.await {
197            Ok(suite_instance) => {
198                let suite = match suite_instance.connect_to_suite() {
199                    Ok(proxy) => proxy,
200                    Err(e) => {
201                        responder.send(Err(e.into())).ok();
202                        continue;
203                    }
204                };
205                let enumeration_result = enumerate_test_cases(&suite, None).await;
206                let t = fasync::Task::spawn(suite_instance.destroy(root_diagnostics.child()));
207                match enumeration_result {
208                    Ok(invocations) => {
209                        const NAMES_CHUNK: usize = 50;
210                        let mut names = Vec::with_capacity(invocations.len());
211                        if let Ok(_) = invocations.into_iter().try_for_each(|i| match i.name {
212                            Some(name) => {
213                                names.push(name);
214                                Ok(())
215                            }
216                            None => {
217                                warn!("no name for a invocation in {}", test_url);
218                                Err(())
219                            }
220                        }) {
221                            responder.send(Ok(())).ok();
222                            let mut names = names.chunks(NAMES_CHUNK);
223                            while let Ok(Some(request)) = iterator.try_next().await {
224                                match request {
225                                    ftest_manager::CaseIteratorRequest::GetNext { responder } => {
226                                        match names.next() {
227                                            Some(names) => {
228                                                responder
229                                                    .send(
230                                                        &names
231                                                            .into_iter()
232                                                            .map(|s| ftest_manager::Case {
233                                                                name: Some(s.into()),
234                                                                ..Default::default()
235                                                            })
236                                                            .collect::<Vec<_>>(),
237                                                    )
238                                                    .ok();
239                                            }
240                                            None => {
241                                                responder.send(&[]).ok();
242                                            }
243                                        }
244                                    }
245                                }
246                            }
247                        } else {
248                            responder.send(Err(LaunchError::CaseEnumeration)).ok();
249                        }
250                    }
251                    Err(err) => {
252                        warn!(err:?; "cannot enumerate tests for {}", test_url);
253                        responder.send(Err(LaunchError::CaseEnumeration)).ok();
254                    }
255                }
256                if let Err(err) = t.await {
257                    warn!(err:?; "Error destroying test realm for {}", test_url);
258                }
259            }
260            Err(e) => {
261                responder.send(Err(e.into())).ok();
262            }
263        }
264    }
265    Ok(())
266}
267
268pub async fn serve_early_boot_profiles(
269    mut stream: ftest_manager::EarlyBootProfileRequestStream,
270) -> Result<(), TestManagerError> {
271    while let Some(req) = stream.try_next().await.map_err(TestManagerError::Stream)? {
272        match req {
273            ftest_manager::EarlyBootProfileRequest::RegisterWatcher {
274                iterator,
275                control_handle,
276            } => {
277                let iterator = iterator.into_stream();
278                if let Err(e) = debug_data_server::send_kernel_debug_data(iterator).await {
279                    warn!("Err serving kernel profiles: {}", e);
280                    control_handle.shutdown_with_epitaph(zx::Status::INTERNAL);
281                    break;
282                }
283            }
284            ftest_manager::EarlyBootProfileRequest::_UnknownMethod {
285                ordinal,
286                control_handle,
287                ..
288            } => {
289                warn!("Unknown EarlyBootProfile request received: {}, closing connection", ordinal);
290                control_handle.shutdown_with_epitaph(zx::Status::NOT_SUPPORTED);
291                break;
292            }
293        }
294    }
295    Ok(())
296}
297
298/// Start `TestCaseEnumerator` server and serve it over `stream`.
299pub async fn run_test_manager_test_case_enumerator_server(
300    mut stream: ftest_manager::TestCaseEnumeratorRequestStream,
301    resolver: Arc<ResolverProxy>,
302    pkg_resolver: Arc<PackageResolverProxy>,
303    above_root_capabilities_for_test: Arc<AboveRootCapabilitiesForTest>,
304    root_diagnostics: &RootDiagnosticNode,
305) -> Result<(), TestManagerError> {
306    while let Some(req) = stream.try_next().await.map_err(TestManagerError::Stream)? {
307        match req {
308            ftest_manager::TestCaseEnumeratorRequest::Enumerate {
309                test_suite_url,
310                options,
311                iterator,
312                responder,
313            } => {
314                let realm = if let Some(realm_options) = options.realm_options {
315                    let realm_proxy = match realm_options
316                        .realm
317                        .map(|r| Ok(r.into_proxy()))
318                        .unwrap_or(Err(Error::NotNullable))
319                    {
320                        Ok(r) => r,
321                        Err(e) => {
322                            warn!(
323                                "Cannot enumerate test cases {}, invalid realm. Closing connection. error: {}",
324                                test_suite_url, e
325                            );
326                            responder.send(Err(LaunchError::InvalidArgs)).ok();
327                            break;
328                        }
329                    };
330                    let offers = match realm_options
331                        .offers
332                        .map(map_offers)
333                        .unwrap_or(Err(Error::NotNullable.into()))
334                    {
335                        Ok(offers) => offers,
336                        Err(e) => {
337                            warn!(
338                                "Cannot enumerate test cases {}, invalid offers. error: {}",
339                                test_suite_url, e
340                            );
341                            responder.send(Err(LaunchError::InvalidArgs)).ok();
342                            break;
343                        }
344                    };
345                    let test_collection = match realm_options.test_collection {
346                        Some(test_collection) => test_collection,
347                        None => {
348                            warn!(
349                                "Cannot enumerate test cases {}, missing test collection.",
350                                test_suite_url
351                            );
352                            responder.send(Err(LaunchError::InvalidArgs)).ok();
353                            break;
354                        }
355                    };
356                    Some(SuiteRealm { realm_proxy, offers, test_collection })
357                } else {
358                    None
359                };
360
361                let iterator = iterator.into_stream();
362                let (_processor, sender) = DebugDataProcessor::new(DebugDataDirectory::Isolated {
363                    parent: constants::ISOLATED_TMP,
364                });
365                let diagnostics = root_diagnostics.child();
366                let launch_fut = facet::get_suite_facets(test_suite_url.clone(), resolver.clone())
367                    .and_then(|facets| {
368                        RunningSuite::launch(
369                            &test_suite_url,
370                            facets,
371                            resolver.clone(),
372                            pkg_resolver.clone(),
373                            above_root_capabilities_for_test.clone(),
374                            sender,
375                            &diagnostics,
376                            &realm,
377                            false, // use_debug_agent
378                        )
379                    });
380                match launch_fut.await {
381                    Ok(suite_instance) => {
382                        let suite = match suite_instance.connect_to_suite() {
383                            Ok(proxy) => proxy,
384                            Err(e) => {
385                                responder.send(Err(e.into())).ok();
386                                continue;
387                            }
388                        };
389                        let enumeration_result = enumerate_test_cases(&suite, None).await;
390                        let t = fasync::Task::spawn(suite_instance.destroy(diagnostics));
391                        match enumeration_result {
392                            Ok(invocations) => {
393                                if let Ok(names) = invocations
394                                    .into_iter()
395                                    .map(|i| i.name.ok_or(()))
396                                    .collect::<Result<Vec<String>, ()>>()
397                                {
398                                    responder.send(Ok(())).ok();
399                                    drain_test_case_names(iterator, names).await;
400                                } else {
401                                    responder.send(Err(LaunchError::CaseEnumeration)).ok();
402                                }
403                            }
404                            Err(err) => {
405                                warn!(err:?; "cannot enumerate tests for {}", test_suite_url);
406                                responder.send(Err(LaunchError::CaseEnumeration)).ok();
407                            }
408                        }
409                        if let Err(err) = t.await {
410                            warn!(err:?; "Error destroying test realm for {}", test_suite_url);
411                        }
412                    }
413                    Err(e) => {
414                        responder.send(Err(e.into())).ok();
415                    }
416                }
417            }
418
419            ftest_manager::TestCaseEnumeratorRequest::_UnknownMethod {
420                ordinal,
421                control_handle,
422                ..
423            } => {
424                warn!("Unknown query request received: {}, closing connection", ordinal);
425                control_handle.shutdown_with_epitaph(zx::Status::NOT_SUPPORTED);
426                break;
427            }
428        };
429    }
430    Ok(())
431}
432
433async fn drain_test_case_names(
434    mut iterator: ftest_manager::TestCaseIteratorRequestStream,
435    names: Vec<String>,
436) {
437    const NAMES_CHUNK: usize = 50;
438    let mut names = names.chunks(NAMES_CHUNK);
439    while let Ok(Some(request)) = iterator.try_next().await {
440        match request {
441            ftest_manager::TestCaseIteratorRequest::GetNext { responder } => match names.next() {
442                Some(names) => {
443                    responder
444                        .send(
445                            &names
446                                .into_iter()
447                                .map(|s| ftest_manager::TestCase {
448                                    name: Some(s.into()),
449                                    ..Default::default()
450                                })
451                                .collect::<Vec<_>>(),
452                        )
453                        .ok();
454                }
455                None => {
456                    responder.send(&[]).ok();
457                }
458            },
459        }
460    }
461}
462
463/// Start `SuiteRunner` server and serve it over `stream`.
464pub async fn run_test_manager_suite_runner_server(
465    mut stream: ftest_manager::SuiteRunnerRequestStream,
466    resolver: Arc<ResolverProxy>,
467    pkg_resolver: Arc<PackageResolverProxy>,
468    above_root_capabilities_for_test: Arc<AboveRootCapabilitiesForTest>,
469    root_diagnostics: &RootDiagnosticNode,
470) -> Result<(), TestManagerError> {
471    while let Some(req) = stream.try_next().await.map_err(TestManagerError::Stream)? {
472        match req {
473            ftest_manager::SuiteRunnerRequest::Run {
474                test_suite_url,
475                options,
476                controller,
477                control_handle,
478            } => {
479                let realm = if let Some(realm_options) = options.realm_options {
480                    let realm_proxy = match realm_options
481                        .realm
482                        .map(|r| Ok(r.into_proxy()))
483                        .unwrap_or(Err(Error::NotNullable))
484                    {
485                        Ok(r) => r,
486                        Err(e) => {
487                            warn!(
488                                "Cannot add suite {}, invalid realm. Closing connection. error: {}",
489                                test_suite_url, e
490                            );
491                            control_handle.shutdown_with_epitaph(zx::Status::INVALID_ARGS);
492                            break;
493                        }
494                    };
495                    let offers = match realm_options
496                        .offers
497                        .map(map_offers)
498                        .unwrap_or(Err(Error::NotNullable.into()))
499                    {
500                        Ok(offers) => offers,
501                        Err(e) => {
502                            warn!(
503                                "Cannot add suite {}, invalid offers. error: {}",
504                                test_suite_url, e
505                            );
506                            control_handle.shutdown_with_epitaph(zx::Status::INVALID_ARGS);
507                            break;
508                        }
509                    };
510                    let test_collection = match realm_options.test_collection {
511                        Some(test_collection) => test_collection,
512                        None => {
513                            warn!("Cannot add suite {}, missing test collection.", test_suite_url);
514                            control_handle.shutdown_with_epitaph(zx::Status::INVALID_ARGS);
515                            break;
516                        }
517                    };
518                    Some(SuiteRealm { realm_proxy, offers, test_collection })
519                } else {
520                    None
521                };
522
523                let controller = controller.into_stream();
524
525                let suite = Suite {
526                    realm: realm.into(),
527                    test_url: test_suite_url,
528                    options: ftest_manager::RunOptions {
529                        run_disabled_tests: options.run_disabled_tests,
530                        parallel: options.max_concurrent_test_case_runs,
531                        arguments: options.arguments,
532                        timeout: options.timeout,
533                        case_filters_to_run: options.test_case_filters,
534                        log_iterator: options.logs_iterator_type.map(convert),
535                        log_interest: options.log_interest,
536                        ..Default::default()
537                    },
538                    controller,
539                    resolver: resolver.clone(),
540                    pkg_resolver: pkg_resolver.clone(),
541                    above_root_capabilities_for_test: above_root_capabilities_for_test.clone(),
542                    facets: facet::ResolveStatus::Unresolved,
543                };
544
545                let diagnostics = root_diagnostics.child();
546
547                suite.run(diagnostics, options.accumulate_debug_data.unwrap_or(false)).await;
548            }
549
550            ftest_manager::SuiteRunnerRequest::_UnknownMethod {
551                ordinal, control_handle, ..
552            } => {
553                warn!("Unknown run builder request received: {}, closing connection", ordinal);
554                control_handle.shutdown_with_epitaph(zx::Status::NOT_SUPPORTED);
555                break;
556            }
557        }
558    }
559    Ok(())
560}
561
562fn convert(item: ftest_manager::LogsIteratorType) -> ftest_manager::LogsIteratorOption {
563    match item {
564        ftest_manager::LogsIteratorType::Batch => ftest_manager::LogsIteratorOption::BatchIterator,
565        ftest_manager::LogsIteratorType::Socket => {
566            ftest_manager::LogsIteratorOption::SocketBatchIterator
567        }
568        _ => todo!(),
569    }
570}