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