blob: 6682a1f021df4c9c7896c4fbdd353aa214e33770 [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 component_events::matcher::*;
use component_events::sequence::{EventSequence, Ordering};
use fidl_fuchsia_io as fio;
use fuchsia_component::server::ServiceFs;
use fuchsia_component_test::*;
use futures::future::BoxFuture;
use futures::{FutureExt, StreamExt};
use log::*;
use vfs::file::vmo::read_only;
use vfs::pseudo_directory;
// This value must be kept consistent with the value in maintainer.rs
const EXPECTED_BACKSTOP_TIME_SEC_STR: &str = "1589910459";
fn mock_boot_handles(
handles: LocalComponentHandles,
) -> BoxFuture<'static, Result<(), anyhow::Error>> {
// 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(EXPECTED_BACKSTOP_TIME_SEC_STR),
},
},
};
async move {
let mut fs = ServiceFs::new();
fs.add_remote("boot", vfs::directory::serve_read_only(dir));
fs.serve_connection(handles.outgoing_dir).expect("serve mock ServiceFs");
fs.collect::<()>().await;
Ok(())
}
.boxed()
}
#[fuchsia::test(logging_minimum_severity = "warn")]
async fn builtin_time_service_and_clock_routed() {
// Define the realm inside component manager.
let builder = RealmBuilder::new().await.unwrap();
let realm =
builder.add_child("realm", "#meta/realm.cm", ChildOptions::new().eager()).await.unwrap();
builder
.add_route(
Route::new()
.capability(Capability::protocol_by_name("fuchsia.logger.LogSink"))
.capability(Capability::protocol_by_name("fuchsia.process.Launcher"))
.from(Ref::parent())
.to(&realm),
)
.await
.unwrap();
builder
.add_route(
Route::new()
.capability(Capability::protocol_by_name("fuchsia.time.Maintenance"))
.from(Ref::parent())
.to(&realm),
)
.await
.unwrap();
let component_manager_realm =
builder.with_nested_component_manager("#meta/component_manager.cm").await.unwrap();
// Define a mock component that serves the `/boot` directory to component manager
let mock_boot = component_manager_realm
.add_local_child("mock_boot", mock_boot_handles, ChildOptions::new())
.await
.unwrap();
component_manager_realm
.add_route(
Route::new()
.capability(Capability::directory("boot").path("/boot").rights(fio::R_STAR_DIR))
.from(&mock_boot)
.to(Ref::self_()),
)
.await
.unwrap();
let instance = component_manager_realm.build().await.unwrap();
let proxy = instance.root.connect_to_protocol_at_exposed_dir().unwrap();
let event_stream = component_events::events::EventStream::new(proxy);
// Unblock the component_manager.
debug!("starting component tree");
instance.start_component_tree().await.unwrap();
// Wait for both components to exit cleanly.
// The child components do several assertions on UTC time properties.
// If any assertion fails, the component will fail with non-zero exit code.
EventSequence::new()
.has_subset(
vec![
EventMatcher::ok()
.stop(Some(ExitStatusMatcher::Clean))
.moniker("./realm/time_client"),
EventMatcher::ok()
.stop(Some(ExitStatusMatcher::Clean))
.moniker("./realm/maintainer"),
],
Ordering::Unordered,
)
.expect(event_stream)
.await
.unwrap();
}