blob: dbc117183d41e1c131433fa46af20cabf7ef9638 [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use anyhow::{anyhow, Context, Error};
use async_trait::async_trait;
use fidl::endpoints::ServerEnd;
use fidl_componentmanager_test as ftest;
use fidl_fuchsia_io as fio;
use fuchsia_async as fasync;
use fuchsia_zircon as zx;
use futures::{channel::oneshot, lock::Mutex, prelude::*};
use log::*;
use std::sync::Arc;
use test_util::assert_geq;
use test_utils_lib::{injectors::*, matcher::EventMatcher, opaque_test::OpaqueTestBuilder};
use vfs::{
directory::entry::DirectoryEntry, execution_scope::ExecutionScope, file::pcb::read_only_static,
pseudo_directory,
};
/// Offset the maintainer component uses from backstop to set the UTC time.
const TEST_OFFSET: zx::Duration = zx::Duration::from_minutes(2);
#[fasync::run_singlethreaded(test)]
async fn builtin_time_service_and_clock_routed() -> Result<(), Error> {
fuchsia_syslog::init().unwrap();
fuchsia_syslog::set_severity(fuchsia_syslog::levels::WARN);
let expected_backstop_time = zx::Time::from_nanos(1589910459000000000);
let expected_backstop_time_sec_str = "1589910459";
// Construct a pseudo-directory to mock the component manager's configured
// backstop time.
let dir = pseudo_directory! {
"config" => pseudo_directory! {
"build_info" => pseudo_directory! {
// The backstop time is stored in seconds.
"minimum_utc_stamp" => read_only_static(expected_backstop_time_sec_str),
},
},
};
let (client, server) = zx::Channel::create().expect("failed to create channel pair");
let scope = ExecutionScope::new();
dir.open(
scope,
fio::OPEN_FLAG_DIRECTORY | fio::OPEN_RIGHT_READABLE,
0,
vfs::path::Path::empty(),
ServerEnd::new(server),
);
// Start a component_manager as a v1 component, with the extra `--maintain-utc-clock` flag.
debug!("starting component_manager");
let test = OpaqueTestBuilder::new("fuchsia-pkg://fuchsia.com/utc-time-tests#meta/realm.cm")
.component_manager_url(
"fuchsia-pkg://fuchsia.com/utc-time-tests#meta/component_manager.cmx",
)
.config("/pkg/data/cm_config")
.add_dir_handle("/boot", client.into())
.build()
.await
.expect("failed to start the OpaqueTest");
let event_source = test
.connect_to_event_source()
.await
.expect("failed to connect to the BlockingEventSource protocol");
// Inject TestOutcomeCapabilities which the components launched by component manager request
// and use to report back the test outcome.
// The capabilities can only be used once, so multiple components requesting this will panic.
debug!("injecting capabilities");
let (maintain_capability, maintain_fut) = test_outcome_report();
maintain_capability.inject(&event_source, EventMatcher::ok().moniker("/maintainer:*")).await;
let (client_capability, client_fut) = test_outcome_report();
client_capability.inject(&event_source, EventMatcher::ok().moniker("/time_client:*")).await;
// Unblock the component_manager.
debug!("starting component tree");
event_source.start_component_tree().await;
// Await the test result.
debug!("waiting for test outcome");
let maintain_result = maintain_fut.await??;
let client_result = client_fut.await??;
// Check that times reported by the maintainer and client agree.
assert_eq!(expected_backstop_time.into_nanos(), maintain_result.backstop);
assert_eq!(expected_backstop_time.into_nanos(), client_result.backstop);
let expected_current_time = expected_backstop_time + TEST_OFFSET;
assert_geq!(expected_current_time.into_nanos(), maintain_result.current_time);
assert_geq!(client_result.current_time, maintain_result.current_time);
Ok(())
}
type TestResult = Result<ftest::SuccessOutcome, Error>;
/// The TestOutcomeCapability that can be injected in order for a component under test
/// to report back test outcomes.
pub struct TestOutcomeCapability {
sender: Mutex<Option<oneshot::Sender<TestResult>>>,
}
/// Create a TestOutcomeCapability and Receiver pair.
fn test_outcome_report() -> (Arc<TestOutcomeCapability>, oneshot::Receiver<TestResult>) {
let (sender, receiver) = oneshot::channel::<TestResult>();
(Arc::new(TestOutcomeCapability { sender: Mutex::new(Some(sender)) }), receiver)
}
#[async_trait]
impl ProtocolInjector for TestOutcomeCapability {
type Marker = ftest::TestOutcomeReportMarker;
async fn serve(
self: Arc<Self>,
mut request_stream: ftest::TestOutcomeReportRequestStream,
) -> Result<(), Error> {
let sender = match self.sender.lock().await.take() {
Some(sender) => sender,
None => panic!("TestOutcomeCapability already reported"),
};
sender
.send(match request_stream.next().await {
Some(outcome) => outcome.map_err(|e| e.into()).and_then(
|ftest::TestOutcomeReportRequest::Report { outcome, responder }| {
responder.send().context("failed to send response")?;
match outcome {
ftest::TestOutcome::Success(success) => Ok(success),
ftest::TestOutcome::Failed(ftest::FailedOutcome { message }) => {
Err(anyhow!("test failed: {}", &message))
}
}
},
),
None => Err(anyhow!("channel finished without a response")),
})
.expect("failed to send outcome on channel");
Ok(())
}
}