blob: 24dbdbaf866e73714db8b1e6a94b03dccb319868 [file] [log] [blame]
// Copyright 2019 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 {
crate::{
capability::{CapabilityProvider, CapabilitySource, FrameworkCapability},
framework::REALM_SERVICE,
model::{
error::ModelError,
events::source_factory::EVENT_SOURCE_SYNC_SERVICE_PATH,
hooks::{Event, EventPayload, EventType, Hook, HooksRegistration},
moniker::AbsoluteMoniker,
rights, routing,
testing::{routing_test_helpers::*, test_helpers::*},
},
},
anyhow::Error,
async_trait::async_trait,
cm_rust::*,
fidl::endpoints::ServerEnd,
fidl_fuchsia_component_runner as fcrunner,
fidl_fuchsia_io::{MODE_TYPE_SERVICE, OPEN_RIGHT_READABLE},
fidl_fuchsia_io2 as fio2, fidl_fuchsia_sys2 as fsys, fuchsia_async as fasync,
fuchsia_zircon as zx,
futures::{join, lock::Mutex, TryStreamExt},
log::*,
maplit::hashmap,
std::{
convert::{TryFrom, TryInto},
path::{Path, PathBuf},
sync::{Arc, Weak},
},
};
/// a
/// \
/// b
///
/// b: uses framework service /svc/fuchsia.sys2.Realm
#[fuchsia_async::run_singlethreaded(test)]
async fn use_framework_service() {
pub struct MockRealmCapabilityProvider {
scope_moniker: AbsoluteMoniker,
host: MockRealmCapabilityHost,
}
impl MockRealmCapabilityProvider {
pub fn new(scope_moniker: AbsoluteMoniker, host: MockRealmCapabilityHost) -> Self {
Self { scope_moniker, host }
}
}
#[async_trait]
impl CapabilityProvider for MockRealmCapabilityProvider {
async fn open(
self: Box<Self>,
_flags: u32,
_open_mode: u32,
_relative_path: PathBuf,
server_end: zx::Channel,
) -> Result<(), ModelError> {
let stream = ServerEnd::<fsys::RealmMarker>::new(server_end)
.into_stream()
.expect("could not convert channel into stream");
let scope_moniker = self.scope_moniker.clone();
let host = self.host.clone();
fasync::spawn(async move {
if let Err(e) = host.serve(scope_moniker, stream).await {
// TODO: Set an epitaph to indicate this was an unexpected error.
warn!("serve_realm failed: {}", e);
}
});
Ok(())
}
}
#[async_trait]
impl Hook for MockRealmCapabilityHost {
async fn on(self: Arc<Self>, event: &Event) -> Result<(), ModelError> {
if let EventPayload::CapabilityRouted {
source:
CapabilitySource::Framework { capability, scope_moniker: Some(scope_moniker) },
capability_provider,
} = &event.payload
{
let mut capability_provider = capability_provider.lock().await;
*capability_provider = self
.on_scoped_framework_capability_routed_async(
scope_moniker.clone(),
&capability,
capability_provider.take(),
)
.await?;
}
Ok(())
}
}
#[derive(Clone)]
pub struct MockRealmCapabilityHost {
/// List of calls to `BindChild` with component's relative moniker.
bind_calls: Arc<Mutex<Vec<String>>>,
}
impl MockRealmCapabilityHost {
pub fn new() -> Self {
Self { bind_calls: Arc::new(Mutex::new(vec![])) }
}
pub fn bind_calls(&self) -> Arc<Mutex<Vec<String>>> {
self.bind_calls.clone()
}
async fn serve(
&self,
scope_moniker: AbsoluteMoniker,
mut stream: fsys::RealmRequestStream,
) -> Result<(), Error> {
while let Some(request) = stream.try_next().await? {
match request {
fsys::RealmRequest::BindChild { responder, .. } => {
self.bind_calls.lock().await.push(
scope_moniker
.path()
.last()
.expect("did not expect root component")
.name()
.to_string(),
);
responder.send(&mut Ok(()))?;
}
_ => {}
}
}
Ok(())
}
pub async fn on_scoped_framework_capability_routed_async<'a>(
&'a self,
scope_moniker: AbsoluteMoniker,
capability: &'a FrameworkCapability,
capability_provider: Option<Box<dyn CapabilityProvider>>,
) -> Result<Option<Box<dyn CapabilityProvider>>, ModelError> {
// If some other capability has already been installed, then there's nothing to
// do here.
match capability {
FrameworkCapability::Protocol(capability_path)
if *capability_path == *REALM_SERVICE =>
{
return Ok(Some(Box::new(MockRealmCapabilityProvider::new(
scope_moniker.clone(),
self.clone(),
)) as Box<dyn CapabilityProvider>));
}
_ => return Ok(capability_provider),
}
}
}
let components = vec![
(
"a",
ComponentDeclBuilder::new()
.add_lazy_child("b")
.offer_runner_to_children(TEST_RUNNER_NAME)
.build(),
),
(
"b",
ComponentDeclBuilder::new()
.use_(UseDecl::Protocol(UseProtocolDecl {
source: UseSource::Framework,
source_path: CapabilityPath::try_from("/svc/fuchsia.sys2.Realm").unwrap(),
target_path: CapabilityPath::try_from("/svc/fuchsia.sys2.Realm").unwrap(),
}))
.offer_runner_to_children(TEST_RUNNER_NAME)
.build(),
),
];
let test = RoutingTest::new("a", components).await;
// RoutingTest installs the real RealmCapabilityHost. Installing the
// MockRealmCapabilityHost here overrides the previously installed one.
let realm_service_host = Arc::new(MockRealmCapabilityHost::new());
test.model
.root_realm
.hooks
.install(vec![HooksRegistration::new(
"MockRealmCapabilityHost",
vec![EventType::CapabilityRouted],
Arc::downgrade(&realm_service_host) as Weak<dyn Hook>,
)])
.await;
test.check_use_realm(vec!["b:0"].into(), realm_service_host.bind_calls()).await;
}
/// a
/// \
/// b
///
/// a: offers directory /data/foo from self as /data/bar
/// a: offers service /svc/foo from self as /svc/bar
/// a: offers service /svc/file from self as /svc/device
/// b: uses directory /data/bar as /data/hippo
/// b: uses service /svc/bar as /svc/hippo
/// b: uses service /svc/device
///
/// The test related to `/svc/file` is used to verify that services that require
/// extended flags, like `OPEN_FLAG_DESCRIBE`, work correctly. This often
/// happens for fuchsia.hardware protocols that compose fuchsia.io protocols,
/// and expect that `fdio_open` should operate correctly.
#[fuchsia_async::run_singlethreaded(test)]
async fn use_from_parent() {
let components = vec![
(
"a",
ComponentDeclBuilder::new()
.offer(OfferDecl::Directory(OfferDirectoryDecl {
source: OfferDirectorySource::Self_,
source_path: CapabilityPath::try_from("/data/foo").unwrap(),
target_path: CapabilityPath::try_from("/data/bar").unwrap(),
target: OfferTarget::Child("b".to_string()),
rights: Some(*rights::READ_RIGHTS),
subdir: None,
dependency_type: DependencyType::Strong,
}))
.offer(OfferDecl::Protocol(OfferProtocolDecl {
source: OfferServiceSource::Self_,
source_path: CapabilityPath::try_from("/svc/foo").unwrap(),
target_path: CapabilityPath::try_from("/svc/bar").unwrap(),
target: OfferTarget::Child("b".to_string()),
dependency_type: DependencyType::Strong,
}))
.offer(OfferDecl::Protocol(OfferProtocolDecl {
source: OfferServiceSource::Self_,
source_path: CapabilityPath::try_from("/svc/file").unwrap(),
target_path: CapabilityPath::try_from("/svc/device").unwrap(),
target: OfferTarget::Child("b".to_string()),
dependency_type: DependencyType::Strong,
}))
.add_lazy_child("b")
.offer_runner_to_children(TEST_RUNNER_NAME)
.build(),
),
(
"b",
ComponentDeclBuilder::new()
.use_(UseDecl::Directory(UseDirectoryDecl {
source: UseSource::Realm,
source_path: CapabilityPath::try_from("/data/bar").unwrap(),
target_path: CapabilityPath::try_from("/data/hippo").unwrap(),
rights: *rights::READ_RIGHTS,
subdir: None,
}))
.use_(UseDecl::Protocol(UseProtocolDecl {
source: UseSource::Realm,
source_path: CapabilityPath::try_from("/svc/bar").unwrap(),
target_path: CapabilityPath::try_from("/svc/hippo").unwrap(),
}))
.use_(UseDecl::Protocol(UseProtocolDecl {
source: UseSource::Realm,
source_path: CapabilityPath::try_from("/svc/device").unwrap(),
target_path: CapabilityPath::try_from("/svc/device").unwrap(),
}))
.offer_runner_to_children(TEST_RUNNER_NAME)
.build(),
),
];
let test = RoutingTest::new("a", components).await;
test.check_use(vec!["b:0"].into(), CheckUse::default_directory(true)).await;
test.check_use(
vec!["b:0"].into(),
CheckUse::Protocol { path: default_service_capability(), should_succeed: true },
)
.await;
test.check_open_file(vec!["b:0"].into(), "/svc/device".try_into().unwrap()).await
}
/// a
/// \
/// b
/// \
/// c
///
/// a: offers directory /data/foo from self as /data/bar
/// a: offers service /svc/foo from self as /svc/bar
/// b: offers directory /data/bar from realm as /data/baz
/// b: offers service /svc/bar from realm as /svc/baz
/// c: uses /data/baz as /data/hippo
/// c: uses /svc/baz as /svc/hippo
#[fuchsia_async::run_singlethreaded(test)]
async fn use_from_grandparent() {
let components = vec![
(
"a",
ComponentDeclBuilder::new()
.offer(OfferDecl::Directory(OfferDirectoryDecl {
source: OfferDirectorySource::Self_,
source_path: CapabilityPath::try_from("/data/foo").unwrap(),
target_path: CapabilityPath::try_from("/data/bar").unwrap(),
target: OfferTarget::Child("b".to_string()),
rights: Some(*rights::READ_RIGHTS),
subdir: None,
dependency_type: DependencyType::Strong,
}))
.offer(OfferDecl::Protocol(OfferProtocolDecl {
source: OfferServiceSource::Self_,
source_path: CapabilityPath::try_from("/svc/foo").unwrap(),
target_path: CapabilityPath::try_from("/svc/bar").unwrap(),
target: OfferTarget::Child("b".to_string()),
dependency_type: DependencyType::Strong,
}))
.add_lazy_child("b")
.offer_runner_to_children(TEST_RUNNER_NAME)
.build(),
),
(
"b",
ComponentDeclBuilder::new()
.offer(OfferDecl::Directory(OfferDirectoryDecl {
source: OfferDirectorySource::Realm,
source_path: CapabilityPath::try_from("/data/bar").unwrap(),
target_path: CapabilityPath::try_from("/data/baz").unwrap(),
target: OfferTarget::Child("c".to_string()),
rights: Some(*rights::READ_RIGHTS),
subdir: None,
dependency_type: DependencyType::Strong,
}))
.offer(OfferDecl::Protocol(OfferProtocolDecl {
source: OfferServiceSource::Realm,
source_path: CapabilityPath::try_from("/svc/bar").unwrap(),
target_path: CapabilityPath::try_from("/svc/baz").unwrap(),
target: OfferTarget::Child("c".to_string()),
dependency_type: DependencyType::Strong,
}))
.add_lazy_child("c")
.offer_runner_to_children(TEST_RUNNER_NAME)
.build(),
),
(
"c",
ComponentDeclBuilder::new()
.use_(UseDecl::Directory(UseDirectoryDecl {
source: UseSource::Realm,
source_path: CapabilityPath::try_from("/data/baz").unwrap(),
target_path: CapabilityPath::try_from("/data/hippo").unwrap(),
rights: *rights::READ_RIGHTS,
subdir: None,
}))
.use_(UseDecl::Protocol(UseProtocolDecl {
source: UseSource::Realm,
source_path: CapabilityPath::try_from("/svc/baz").unwrap(),
target_path: CapabilityPath::try_from("/svc/hippo").unwrap(),
}))
.offer_runner_to_children(TEST_RUNNER_NAME)
.build(),
),
];
let test = RoutingTest::new("a", components).await;
test.check_use(vec!["b:0", "c:0"].into(), CheckUse::default_directory(true)).await;
test.check_use(
vec!["b:0", "c:0"].into(),
CheckUse::Protocol { path: default_service_capability(), should_succeed: true },
)
.await;
}
/// a
/// \
/// b
/// \
/// c
///
/// a: offers service /svc/builtin.Echo from realm
/// b: offers service /svc/builtin.Echo from realm
/// c: uses /svc/builtin.Echo as /svc/hippo
#[fuchsia_async::run_singlethreaded(test)]
async fn use_builtin_from_grandparent() {
let components = vec![
(
"a",
ComponentDeclBuilder::new()
.offer(OfferDecl::Protocol(OfferProtocolDecl {
source: OfferServiceSource::Realm,
source_path: CapabilityPath::try_from("/svc/builtin.Echo").unwrap(),
target_path: CapabilityPath::try_from("/svc/builtin.Echo").unwrap(),
target: OfferTarget::Child("b".to_string()),
dependency_type: DependencyType::Strong,
}))
.add_lazy_child("b")
.offer_runner_to_children(TEST_RUNNER_NAME)
.build(),
),
(
"b",
ComponentDeclBuilder::new()
.offer(OfferDecl::Protocol(OfferProtocolDecl {
source: OfferServiceSource::Realm,
source_path: CapabilityPath::try_from("/svc/builtin.Echo").unwrap(),
target_path: CapabilityPath::try_from("/svc/builtin.Echo").unwrap(),
target: OfferTarget::Child("c".to_string()),
dependency_type: DependencyType::Strong,
}))
.add_lazy_child("c")
.offer_runner_to_children(TEST_RUNNER_NAME)
.build(),
),
(
"c",
ComponentDeclBuilder::new()
.use_(UseDecl::Protocol(UseProtocolDecl {
source: UseSource::Realm,
source_path: CapabilityPath::try_from("/svc/builtin.Echo").unwrap(),
target_path: CapabilityPath::try_from("/svc/hippo").unwrap(),
}))
.offer_runner_to_children(TEST_RUNNER_NAME)
.build(),
),
];
let test = RoutingTest::new("a", components).await;
test.check_use(
vec!["b:0", "c:0"].into(),
CheckUse::Protocol { path: default_service_capability(), should_succeed: true },
)
.await;
}
/// a
/// /
/// b
/// / \
/// d c
///
/// d: exposes directory /data/foo from self as /data/bar
/// b: offers directory /data/bar from d as /data/foobar to c
/// c: uses /data/foobar as /data/hippo
#[fuchsia_async::run_singlethreaded(test)]
async fn use_from_sibling_no_root() {
let components = vec![
(
"a",
ComponentDeclBuilder::new()
.add_lazy_child("b")
.offer_runner_to_children(TEST_RUNNER_NAME)
.build(),
),
(
"b",
ComponentDeclBuilder::new()
.offer(OfferDecl::Directory(OfferDirectoryDecl {
source: OfferDirectorySource::Child("d".to_string()),
source_path: CapabilityPath::try_from("/data/bar").unwrap(),
target_path: CapabilityPath::try_from("/data/foobar").unwrap(),
target: OfferTarget::Child("c".to_string()),
rights: Some(*rights::READ_RIGHTS),
subdir: None,
dependency_type: DependencyType::Strong,
}))
.offer(OfferDecl::Protocol(OfferProtocolDecl {
source: OfferServiceSource::Child("d".to_string()),
source_path: CapabilityPath::try_from("/svc/bar").unwrap(),
target_path: CapabilityPath::try_from("/svc/foobar").unwrap(),
target: OfferTarget::Child("c".to_string()),
dependency_type: DependencyType::Strong,
}))
.add_lazy_child("c")
.add_lazy_child("d")
.offer_runner_to_children(TEST_RUNNER_NAME)
.build(),
),
(
"c",
ComponentDeclBuilder::new()
.use_(UseDecl::Directory(UseDirectoryDecl {
source: UseSource::Realm,
source_path: CapabilityPath::try_from("/data/foobar").unwrap(),
target_path: CapabilityPath::try_from("/data/hippo").unwrap(),
rights: *rights::READ_RIGHTS,
subdir: None,
}))
.use_(UseDecl::Protocol(UseProtocolDecl {
source: UseSource::Realm,
source_path: CapabilityPath::try_from("/svc/foobar").unwrap(),
target_path: CapabilityPath::try_from("/svc/hippo").unwrap(),
}))
.offer_runner_to_children(TEST_RUNNER_NAME)
.build(),
),
(
"d",
ComponentDeclBuilder::new()
.expose(ExposeDecl::Directory(ExposeDirectoryDecl {
source: ExposeSource::Self_,
source_path: CapabilityPath::try_from("/data/foo").unwrap(),
target_path: CapabilityPath::try_from("/data/bar").unwrap(),
target: ExposeTarget::Realm,
rights: Some(*rights::READ_RIGHTS),
subdir: None,
}))
.expose(ExposeDecl::Protocol(ExposeProtocolDecl {
source: ExposeSource::Self_,
source_path: CapabilityPath::try_from("/svc/foo").unwrap(),
target_path: CapabilityPath::try_from("/svc/bar").unwrap(),
target: ExposeTarget::Realm,
}))
.offer_runner_to_children(TEST_RUNNER_NAME)
.build(),
),
];
let test = RoutingTest::new("a", components).await;
test.check_use(vec!["b:0", "c:0"].into(), CheckUse::default_directory(true)).await;
test.check_use(
vec!["b:0", "c:0"].into(),
CheckUse::Protocol { path: default_service_capability(), should_succeed: true },
)
.await;
}
/// a
/// / \
/// b c
///
/// b: exposes directory /data/foo from self as /data/bar
/// a: offers directory /data/bar from b as /data/baz to c
/// c: uses /data/baz as /data/hippo
#[fuchsia_async::run_singlethreaded(test)]
async fn use_from_sibling_root() {
let components = vec![
(
"a",
ComponentDeclBuilder::new()
.offer(OfferDecl::Directory(OfferDirectoryDecl {
source: OfferDirectorySource::Child("b".to_string()),
source_path: CapabilityPath::try_from("/data/bar").unwrap(),
target_path: CapabilityPath::try_from("/data/baz").unwrap(),
target: OfferTarget::Child("c".to_string()),
rights: Some(*rights::READ_RIGHTS),
subdir: None,
dependency_type: DependencyType::Strong,
}))
.offer(OfferDecl::Protocol(OfferProtocolDecl {
source: OfferServiceSource::Child("b".to_string()),
source_path: CapabilityPath::try_from("/svc/bar").unwrap(),
target_path: CapabilityPath::try_from("/svc/baz").unwrap(),
target: OfferTarget::Child("c".to_string()),
dependency_type: DependencyType::Strong,
}))
.add_lazy_child("b")
.add_lazy_child("c")
.offer_runner_to_children(TEST_RUNNER_NAME)
.build(),
),
(
"b",
ComponentDeclBuilder::new()
.expose(ExposeDecl::Directory(ExposeDirectoryDecl {
source: ExposeSource::Self_,
source_path: CapabilityPath::try_from("/data/foo").unwrap(),
target_path: CapabilityPath::try_from("/data/bar").unwrap(),
target: ExposeTarget::Realm,
rights: Some(*rights::READ_RIGHTS),
subdir: None,
}))
.expose(ExposeDecl::Protocol(ExposeProtocolDecl {
source: ExposeSource::Self_,
source_path: CapabilityPath::try_from("/svc/foo").unwrap(),
target_path: CapabilityPath::try_from("/svc/bar").unwrap(),
target: ExposeTarget::Realm,
}))
.offer_runner_to_children(TEST_RUNNER_NAME)
.build(),
),
(
"c",
ComponentDeclBuilder::new()
.use_(UseDecl::Directory(UseDirectoryDecl {
source: UseSource::Realm,
source_path: CapabilityPath::try_from("/data/baz").unwrap(),
target_path: CapabilityPath::try_from("/data/hippo").unwrap(),
rights: *rights::READ_RIGHTS,
subdir: None,
}))
.use_(UseDecl::Protocol(UseProtocolDecl {
source: UseSource::Realm,
source_path: CapabilityPath::try_from("/svc/baz").unwrap(),
target_path: CapabilityPath::try_from("/svc/hippo").unwrap(),
}))
.offer_runner_to_children(TEST_RUNNER_NAME)
.build(),
),
];
let test = RoutingTest::new("a", components).await;
test.check_use(vec!["c:0"].into(), CheckUse::default_directory(true)).await;
test.check_use(
vec!["c:0"].into(),
CheckUse::Protocol { path: default_service_capability(), should_succeed: true },
)
.await;
}
/// a
/// / \
/// b c
/// /
/// d
///
/// d: exposes directory /data/foo from self as /data/bar
/// b: exposes directory /data/bar from d as /data/baz
/// a: offers directory /data/baz from b as /data/foobar to c
/// c: uses /data/foobar as /data/hippo
#[fuchsia_async::run_singlethreaded(test)]
async fn use_from_niece() {
let components = vec![
(
"a",
ComponentDeclBuilder::new()
.offer(OfferDecl::Directory(OfferDirectoryDecl {
source: OfferDirectorySource::Child("b".to_string()),
source_path: CapabilityPath::try_from("/data/baz").unwrap(),
target_path: CapabilityPath::try_from("/data/foobar").unwrap(),
target: OfferTarget::Child("c".to_string()),
rights: Some(*rights::READ_RIGHTS),
subdir: None,
dependency_type: DependencyType::Strong,
}))
.offer(OfferDecl::Protocol(OfferProtocolDecl {
source: OfferServiceSource::Child("b".to_string()),
source_path: CapabilityPath::try_from("/svc/baz").unwrap(),
target_path: CapabilityPath::try_from("/svc/foobar").unwrap(),
target: OfferTarget::Child("c".to_string()),
dependency_type: DependencyType::Strong,
}))
.add_lazy_child("b")
.add_lazy_child("c")
.offer_runner_to_children(TEST_RUNNER_NAME)
.build(),
),
(
"b",
ComponentDeclBuilder::new()
.expose(ExposeDecl::Directory(ExposeDirectoryDecl {
source: ExposeSource::Child("d".to_string()),
source_path: CapabilityPath::try_from("/data/bar").unwrap(),
target_path: CapabilityPath::try_from("/data/baz").unwrap(),
target: ExposeTarget::Realm,
rights: Some(*rights::READ_RIGHTS),
subdir: None,
}))
.expose(ExposeDecl::Protocol(ExposeProtocolDecl {
source: ExposeSource::Child("d".to_string()),
source_path: CapabilityPath::try_from("/svc/bar").unwrap(),
target_path: CapabilityPath::try_from("/svc/baz").unwrap(),
target: ExposeTarget::Realm,
}))
.add_lazy_child("d")
.offer_runner_to_children(TEST_RUNNER_NAME)
.build(),
),
(
"c",
ComponentDeclBuilder::new()
.use_(UseDecl::Directory(UseDirectoryDecl {
source: UseSource::Realm,
source_path: CapabilityPath::try_from("/data/foobar").unwrap(),
target_path: CapabilityPath::try_from("/data/hippo").unwrap(),
rights: *rights::READ_RIGHTS,
subdir: None,
}))
.use_(UseDecl::Protocol(UseProtocolDecl {
source: UseSource::Realm,
source_path: CapabilityPath::try_from("/svc/foobar").unwrap(),
target_path: CapabilityPath::try_from("/svc/hippo").unwrap(),
}))
.offer_runner_to_children(TEST_RUNNER_NAME)
.build(),
),
(
"d",
ComponentDeclBuilder::new()
.expose(ExposeDecl::Directory(ExposeDirectoryDecl {
source: ExposeSource::Self_,
source_path: CapabilityPath::try_from("/data/foo").unwrap(),
target_path: CapabilityPath::try_from("/data/bar").unwrap(),
target: ExposeTarget::Realm,
rights: Some(*rights::READ_RIGHTS),
subdir: None,
}))
.expose(ExposeDecl::Protocol(ExposeProtocolDecl {
source: ExposeSource::Self_,
source_path: CapabilityPath::try_from("/svc/foo").unwrap(),
target_path: CapabilityPath::try_from("/svc/bar").unwrap(),
target: ExposeTarget::Realm,
}))
.offer_runner_to_children(TEST_RUNNER_NAME)
.build(),
),
];
let test = RoutingTest::new("a", components).await;
test.check_use(vec!["c:0"].into(), CheckUse::default_directory(true)).await;
test.check_use(
vec!["c:0"].into(),
CheckUse::Protocol { path: default_service_capability(), should_succeed: true },
)
.await;
}
/// a
/// / \
/// / \
/// b c
/// / \ / \
/// d e f g
/// \
/// h
///
/// a,d,h: hosts /svc/foo and /data/foo
/// e: uses /svc/foo as /svc/hippo from a, uses /data/foo as /data/hippo from d
/// f: uses /data/foo from d as /data/hippo, uses /svc/foo from h as /svc/hippo
#[fuchsia_async::run_singlethreaded(test)]
async fn use_kitchen_sink() {
let components = vec![
(
"a",
ComponentDeclBuilder::new()
.offer(OfferDecl::Protocol(OfferProtocolDecl {
source: OfferServiceSource::Self_,
source_path: CapabilityPath::try_from("/svc/foo").unwrap(),
target_path: CapabilityPath::try_from("/svc/foo_from_a").unwrap(),
target: OfferTarget::Child("b".to_string()),
dependency_type: DependencyType::Strong,
}))
.offer(OfferDecl::Directory(OfferDirectoryDecl {
source: OfferDirectorySource::Child("b".to_string()),
source_path: CapabilityPath::try_from("/data/foo_from_d").unwrap(),
target_path: CapabilityPath::try_from("/data/foo_from_d").unwrap(),
target: OfferTarget::Child("c".to_string()),
rights: Some(*rights::READ_RIGHTS),
subdir: None,
dependency_type: DependencyType::Strong,
}))
.add_lazy_child("b")
.add_lazy_child("c")
.offer_runner_to_children(TEST_RUNNER_NAME)
.build(),
),
(
"b",
ComponentDeclBuilder::new_empty_component()
.offer(OfferDecl::Directory(OfferDirectoryDecl {
source: OfferDirectorySource::Child("d".to_string()),
source_path: CapabilityPath::try_from("/data/foo_from_d").unwrap(),
target_path: CapabilityPath::try_from("/data/foo_from_d").unwrap(),
target: OfferTarget::Child("e".to_string()),
rights: Some(*rights::READ_RIGHTS),
subdir: None,
dependency_type: DependencyType::Strong,
}))
.offer(OfferDecl::Protocol(OfferProtocolDecl {
source: OfferServiceSource::Realm,
source_path: CapabilityPath::try_from("/svc/foo_from_a").unwrap(),
target_path: CapabilityPath::try_from("/svc/foo_from_a").unwrap(),
target: OfferTarget::Child("e".to_string()),
dependency_type: DependencyType::Strong,
}))
.expose(ExposeDecl::Directory(ExposeDirectoryDecl {
source: ExposeSource::Child("d".to_string()),
source_path: CapabilityPath::try_from("/data/foo_from_d").unwrap(),
target_path: CapabilityPath::try_from("/data/foo_from_d").unwrap(),
target: ExposeTarget::Realm,
rights: Some(*rights::READ_RIGHTS),
subdir: None,
}))
.add_lazy_child("d")
.add_lazy_child("e")
.offer_runner_to_children(TEST_RUNNER_NAME)
.build(),
),
(
"c",
ComponentDeclBuilder::new_empty_component()
.offer(OfferDecl::Directory(OfferDirectoryDecl {
source: OfferDirectorySource::Realm,
source_path: CapabilityPath::try_from("/data/foo_from_d").unwrap(),
target_path: CapabilityPath::try_from("/data/foo_from_d").unwrap(),
target: OfferTarget::Child("f".to_string()),
rights: Some(*rights::READ_RIGHTS),
subdir: None,
dependency_type: DependencyType::Strong,
}))
.offer(OfferDecl::Protocol(OfferProtocolDecl {
source: OfferServiceSource::Child("g".to_string()),
source_path: CapabilityPath::try_from("/svc/foo_from_h").unwrap(),
target_path: CapabilityPath::try_from("/svc/foo_from_h").unwrap(),
target: OfferTarget::Child("f".to_string()),
dependency_type: DependencyType::Strong,
}))
.add_lazy_child("f")
.add_lazy_child("g")
.offer_runner_to_children(TEST_RUNNER_NAME)
.build(),
),
(
"d",
ComponentDeclBuilder::new()
.expose(ExposeDecl::Directory(ExposeDirectoryDecl {
source: ExposeSource::Self_,
source_path: CapabilityPath::try_from("/data/foo").unwrap(),
target_path: CapabilityPath::try_from("/data/foo_from_d").unwrap(),
target: ExposeTarget::Realm,
rights: Some(*rights::READ_RIGHTS),
subdir: None,
}))
.offer_runner_to_children(TEST_RUNNER_NAME)
.build(),
),
(
"e",
ComponentDeclBuilder::new()
.use_(UseDecl::Directory(UseDirectoryDecl {
source: UseSource::Realm,
source_path: CapabilityPath::try_from("/data/foo_from_d").unwrap(),
target_path: CapabilityPath::try_from("/data/hippo").unwrap(),
rights: *rights::READ_RIGHTS,
subdir: None,
}))
.use_(UseDecl::Protocol(UseProtocolDecl {
source: UseSource::Realm,
source_path: CapabilityPath::try_from("/svc/foo_from_a").unwrap(),
target_path: CapabilityPath::try_from("/svc/hippo").unwrap(),
}))
.offer_runner_to_children(TEST_RUNNER_NAME)
.build(),
),
(
"f",
ComponentDeclBuilder::new()
.use_(UseDecl::Directory(UseDirectoryDecl {
source: UseSource::Realm,
source_path: CapabilityPath::try_from("/data/foo_from_d").unwrap(),
target_path: CapabilityPath::try_from("/data/hippo").unwrap(),
rights: *rights::READ_RIGHTS,
subdir: None,
}))
.use_(UseDecl::Protocol(UseProtocolDecl {
source: UseSource::Realm,
source_path: CapabilityPath::try_from("/svc/foo_from_h").unwrap(),
target_path: CapabilityPath::try_from("/svc/hippo").unwrap(),
}))
.offer_runner_to_children(TEST_RUNNER_NAME)
.build(),
),
(
"g",
ComponentDeclBuilder::new_empty_component()
.expose(ExposeDecl::Protocol(ExposeProtocolDecl {
source: ExposeSource::Child("h".to_string()),
source_path: CapabilityPath::try_from("/svc/foo_from_h").unwrap(),
target_path: CapabilityPath::try_from("/svc/foo_from_h").unwrap(),
target: ExposeTarget::Realm,
}))
.add_lazy_child("h")
.offer_runner_to_children(TEST_RUNNER_NAME)
.build(),
),
(
"h",
ComponentDeclBuilder::new()
.expose(ExposeDecl::Protocol(ExposeProtocolDecl {
source: ExposeSource::Self_,
source_path: CapabilityPath::try_from("/svc/foo").unwrap(),
target_path: CapabilityPath::try_from("/svc/foo_from_h").unwrap(),
target: ExposeTarget::Realm,
}))
.offer_runner_to_children(TEST_RUNNER_NAME)
.build(),
),
];
let test = RoutingTest::new("a", components).await;
test.check_use(vec!["b:0", "e:0"].into(), CheckUse::default_directory(true)).await;
test.check_use(
vec!["b:0", "e:0"].into(),
CheckUse::Protocol { path: default_service_capability(), should_succeed: true },
)
.await;
test.check_use(vec!["c:0", "f:0"].into(), CheckUse::default_directory(true)).await;
test.check_use(
vec!["c:0", "f:0"].into(),
CheckUse::Protocol { path: default_service_capability(), should_succeed: true },
)
.await;
}
/// component manager's namespace
/// |
/// a
///
/// a: uses directory /use_from_cm_namespace/data/foo as /data/hippo
/// a: uses service /use_from_cm_namespace/svc/foo as /svc/hippo
#[fuchsia_async::run_singlethreaded(test)]
async fn use_from_component_manager_namespace() {
let components = vec![(
"a",
ComponentDeclBuilder::new()
.use_(UseDecl::Directory(UseDirectoryDecl {
source: UseSource::Realm,
source_path: CapabilityPath::try_from("/use_from_cm_namespace/data/foo").unwrap(),
target_path: CapabilityPath::try_from("/data/hippo").unwrap(),
rights: fio2::Operations::Connect,
subdir: None,
}))
.use_(UseDecl::Protocol(UseProtocolDecl {
source: UseSource::Realm,
source_path: CapabilityPath::try_from("/use_from_cm_namespace/svc/foo").unwrap(),
target_path: CapabilityPath::try_from("/svc/hippo").unwrap(),
}))
.offer_runner_to_children(TEST_RUNNER_NAME)
.build(),
)];
let test = RoutingTest::new("a", components).await;
test.install_hippo_dir("/use_from_cm_namespace");
test.check_use(vec![].into(), CheckUse::default_directory(true)).await;
test.check_use(
vec![].into(),
CheckUse::Protocol { path: default_service_capability(), should_succeed: true },
)
.await;
}
/// component manager's namespace
/// |
/// a
/// \
/// b
///
/// a: offers directory /offer_from_cm_namespace/data/foo from realm as /foo
/// a: offers service /offer_from_cm_namespace/svc/foo from realm as /echo/echo
/// b: uses directory /foo as /data/hippo
/// b: uses service /echo/echo as /svc/hippo
#[fuchsia_async::run_singlethreaded(test)]
async fn offer_from_component_manager_namespace() {
let components = vec![
(
"a",
ComponentDeclBuilder::new()
.offer(OfferDecl::Directory(OfferDirectoryDecl {
source: OfferDirectorySource::Realm,
source_path: CapabilityPath::try_from("/offer_from_cm_namespace/data/foo")
.unwrap(),
target_path: CapabilityPath::try_from("/foo").unwrap(),
target: OfferTarget::Child("b".to_string()),
rights: Some(*rights::READ_RIGHTS),
subdir: None,
dependency_type: DependencyType::Strong,
}))
.offer(OfferDecl::Protocol(OfferProtocolDecl {
source: OfferServiceSource::Realm,
source_path: CapabilityPath::try_from("/offer_from_cm_namespace/svc/foo")
.unwrap(),
target_path: CapabilityPath::try_from("/echo/echo").unwrap(),
target: OfferTarget::Child("b".to_string()),
dependency_type: DependencyType::Strong,
}))
.add_lazy_child("b")
.offer_runner_to_children(TEST_RUNNER_NAME)
.build(),
),
(
"b",
ComponentDeclBuilder::new()
.use_(UseDecl::Directory(UseDirectoryDecl {
source: UseSource::Realm,
source_path: CapabilityPath::try_from("/foo").unwrap(),
target_path: CapabilityPath::try_from("/data/hippo").unwrap(),
rights: *rights::READ_RIGHTS,
subdir: None,
}))
.use_(UseDecl::Protocol(UseProtocolDecl {
source: UseSource::Realm,
source_path: CapabilityPath::try_from("/echo/echo").unwrap(),
target_path: CapabilityPath::try_from("/svc/hippo").unwrap(),
}))
.offer_runner_to_children(TEST_RUNNER_NAME)
.build(),
),
];
let test = RoutingTest::new("a", components).await;
test.install_hippo_dir("/offer_from_cm_namespace");
test.check_use(vec!["b:0"].into(), CheckUse::default_directory(true)).await;
test.check_use(
vec!["b:0"].into(),
CheckUse::Protocol { path: default_service_capability(), should_succeed: true },
)
.await;
}
/// a
/// \
/// b
///
/// b: uses directory /data/hippo as /data/hippo, but it's not in its realm
/// b: uses service /svc/hippo as /svc/hippo, but it's not in its realm
#[fuchsia_async::run_singlethreaded(test)]
async fn use_not_offered() {
let components = vec![
(
"a",
ComponentDeclBuilder::new_empty_component()
.add_lazy_child("b")
.offer_runner_to_children(TEST_RUNNER_NAME)
.build(),
),
(
"b",
ComponentDeclBuilder::new()
.use_(UseDecl::Directory(UseDirectoryDecl {
source: UseSource::Realm,
source_path: CapabilityPath::try_from("/data/hippo").unwrap(),
target_path: CapabilityPath::try_from("/data/hippo").unwrap(),
rights: *rights::READ_RIGHTS,
subdir: None,
}))
.use_(UseDecl::Protocol(UseProtocolDecl {
source: UseSource::Realm,
source_path: CapabilityPath::try_from("/svc/hippo").unwrap(),
target_path: CapabilityPath::try_from("/svc/hippo").unwrap(),
}))
.offer_runner_to_children(TEST_RUNNER_NAME)
.build(),
),
];
let test = RoutingTest::new("a", components).await;
test.check_use(vec!["b:0"].into(), CheckUse::default_directory(false)).await;
test.check_use(
vec!["b:0"].into(),
CheckUse::Protocol { path: default_service_capability(), should_succeed: false },
)
.await;
}
/// a
/// / \
/// b c
///
/// a: offers directory /data/hippo from b as /data/hippo, but it's not exposed by b
/// a: offers service /svc/hippo from b as /svc/hippo, but it's not exposed by b
/// c: uses directory /data/hippo as /data/hippo
/// c: uses service /svc/hippo as /svc/hippo
#[fuchsia_async::run_singlethreaded(test)]
async fn use_offer_source_not_exposed() {
let components = vec![
(
"a",
ComponentDeclBuilder::new_empty_component()
.offer(OfferDecl::Directory(OfferDirectoryDecl {
source_path: CapabilityPath::try_from("/data/hippo").unwrap(),
source: OfferDirectorySource::Child("b".to_string()),
target_path: CapabilityPath::try_from("/data/hippo").unwrap(),
target: OfferTarget::Child("c".to_string()),
rights: Some(*rights::READ_RIGHTS),
subdir: None,
dependency_type: DependencyType::Strong,
}))
.offer(OfferDecl::Protocol(OfferProtocolDecl {
source_path: CapabilityPath::try_from("/svc/hippo").unwrap(),
source: OfferServiceSource::Child("b".to_string()),
target_path: CapabilityPath::try_from("/svc/hippo").unwrap(),
target: OfferTarget::Child("c".to_string()),
dependency_type: DependencyType::Strong,
}))
.add_lazy_child("b")
.add_lazy_child("c")
.offer_runner_to_children(TEST_RUNNER_NAME)
.build(),
),
("b", component_decl_with_test_runner()),
(
"c",
ComponentDeclBuilder::new()
.use_(UseDecl::Directory(UseDirectoryDecl {
source: UseSource::Realm,
source_path: CapabilityPath::try_from("/data/hippo").unwrap(),
target_path: CapabilityPath::try_from("/data/hippo").unwrap(),
rights: *rights::READ_RIGHTS,
subdir: None,
}))
.use_(UseDecl::Protocol(UseProtocolDecl {
source: UseSource::Realm,
source_path: CapabilityPath::try_from("/svc/hippo").unwrap(),
target_path: CapabilityPath::try_from("/svc/hippo").unwrap(),
}))
.offer_runner_to_children(TEST_RUNNER_NAME)
.build(),
),
];
let test = RoutingTest::new("a", components).await;
test.check_use(vec!["c:0"].into(), CheckUse::default_directory(false)).await;
test.check_use(
vec!["c:0"].into(),
CheckUse::Protocol { path: default_service_capability(), should_succeed: false },
)
.await;
}
/// a
/// \
/// b
/// \
/// c
///
/// b: offers directory /data/hippo from its realm as /data/hippo, but it's not offered by a
/// b: offers service /svc/hippo from its realm as /svc/hippo, but it's not offfered by a
/// c: uses directory /data/hippo as /data/hippo
/// c: uses service /svc/hippo as /svc/hippo
#[fuchsia_async::run_singlethreaded(test)]
async fn use_offer_source_not_offered() {
let components = vec![
(
"a",
ComponentDeclBuilder::new()
.add_lazy_child("b")
.offer_runner_to_children(TEST_RUNNER_NAME)
.build(),
),
(
"b",
ComponentDeclBuilder::new_empty_component()
.offer(OfferDecl::Directory(OfferDirectoryDecl {
source_path: CapabilityPath::try_from("/data/hippo").unwrap(),
source: OfferDirectorySource::Realm,
target_path: CapabilityPath::try_from("/data/hippo").unwrap(),
target: OfferTarget::Child("c".to_string()),
rights: Some(*rights::READ_RIGHTS),
subdir: None,
dependency_type: DependencyType::Strong,
}))
.offer(OfferDecl::Protocol(OfferProtocolDecl {
source_path: CapabilityPath::try_from("/svc/hippo").unwrap(),
source: OfferServiceSource::Realm,
target_path: CapabilityPath::try_from("/svc/hippo").unwrap(),
target: OfferTarget::Child("c".to_string()),
dependency_type: DependencyType::Strong,
}))
.add_lazy_child("c")
.offer_runner_to_children(TEST_RUNNER_NAME)
.build(),
),
(
"c",
ComponentDeclBuilder::new()
.use_(UseDecl::Directory(UseDirectoryDecl {
source: UseSource::Realm,
source_path: CapabilityPath::try_from("/data/hippo").unwrap(),
target_path: CapabilityPath::try_from("/data/hippo").unwrap(),
rights: *rights::READ_RIGHTS,
subdir: None,
}))
.use_(UseDecl::Protocol(UseProtocolDecl {
source: UseSource::Realm,
source_path: CapabilityPath::try_from("/svc/hippo").unwrap(),
target_path: CapabilityPath::try_from("/svc/hippo").unwrap(),
}))
.offer_runner_to_children(TEST_RUNNER_NAME)
.build(),
),
];
let test = RoutingTest::new("a", components).await;
test.check_use(vec!["b:0", "c:0"].into(), CheckUse::default_directory(false)).await;
test.check_use(
vec!["b:0", "c:0"].into(),
CheckUse::Protocol { path: default_service_capability(), should_succeed: false },
)
.await;
}
/// a
/// \
/// b
/// \
/// c
///
/// b: uses directory /data/hippo as /data/hippo, but it's exposed to it, not offered
/// b: uses service /svc/hippo as /svc/hippo, but it's exposed to it, not offered
/// c: exposes /data/hippo
/// c: exposes /svc/hippo
#[fuchsia_async::run_singlethreaded(test)]
async fn use_from_expose() {
let components = vec![
(
"a",
ComponentDeclBuilder::new_empty_component()
.add_lazy_child("b")
.offer_runner_to_children(TEST_RUNNER_NAME)
.build(),
),
(
"b",
ComponentDeclBuilder::new()
.use_(UseDecl::Directory(UseDirectoryDecl {
source: UseSource::Realm,
source_path: CapabilityPath::try_from("/data/hippo").unwrap(),
target_path: CapabilityPath::try_from("/data/hippo").unwrap(),
rights: *rights::READ_RIGHTS,
subdir: None,
}))
.use_(UseDecl::Protocol(UseProtocolDecl {
source: UseSource::Realm,
source_path: CapabilityPath::try_from("/svc/hippo").unwrap(),
target_path: CapabilityPath::try_from("/svc/hippo").unwrap(),
}))
.add_lazy_child("c")
.offer_runner_to_children(TEST_RUNNER_NAME)
.build(),
),
(
"c",
ComponentDeclBuilder::new()
.expose(ExposeDecl::Directory(ExposeDirectoryDecl {
source_path: CapabilityPath::try_from("/data/hippo").unwrap(),
source: ExposeSource::Self_,
target_path: CapabilityPath::try_from("/data/hippo").unwrap(),
target: ExposeTarget::Realm,
rights: Some(*rights::READ_RIGHTS),
subdir: None,
}))
.expose(ExposeDecl::Protocol(ExposeProtocolDecl {
source_path: CapabilityPath::try_from("/svc/hippo").unwrap(),
source: ExposeSource::Self_,
target_path: CapabilityPath::try_from("/svc/hippo").unwrap(),
target: ExposeTarget::Realm,
}))
.offer_runner_to_children(TEST_RUNNER_NAME)
.build(),
),
];
let test = RoutingTest::new("a", components).await;
test.check_use(vec!["b:0"].into(), CheckUse::default_directory(false)).await;
test.check_use(
vec!["b:0"].into(),
CheckUse::Protocol { path: default_service_capability(), should_succeed: false },
)
.await;
}
/// a
/// / \
/// b c
///
/// b: exposes directory /data/foo from self as /data/bar to framework (NOT realm)
/// a: offers directory /data/bar from b as /data/baz to c, but it is not exposed via realm
/// c: uses /data/baz as /data/hippo
#[fuchsia_async::run_singlethreaded(test)]
async fn use_from_expose_to_framework() {
let components = vec![
(
"a",
ComponentDeclBuilder::new()
.offer(OfferDecl::Directory(OfferDirectoryDecl {
source: OfferDirectorySource::Child("b".to_string()),
source_path: CapabilityPath::try_from("/data/bar").unwrap(),
target_path: CapabilityPath::try_from("/data/baz").unwrap(),
target: OfferTarget::Child("c".to_string()),
rights: Some(*rights::READ_RIGHTS),
subdir: None,
dependency_type: DependencyType::Strong,
}))
.offer(OfferDecl::Protocol(OfferProtocolDecl {
source: OfferServiceSource::Child("b".to_string()),
source_path: CapabilityPath::try_from("/svc/bar").unwrap(),
target_path: CapabilityPath::try_from("/svc/baz").unwrap(),
target: OfferTarget::Child("c".to_string()),
dependency_type: DependencyType::Strong,
}))
.add_lazy_child("b")
.add_lazy_child("c")
.offer_runner_to_children(TEST_RUNNER_NAME)
.build(),
),
(
"b",
ComponentDeclBuilder::new()
.expose(ExposeDecl::Directory(ExposeDirectoryDecl {
source: ExposeSource::Self_,
source_path: CapabilityPath::try_from("/data/foo").unwrap(),
target_path: CapabilityPath::try_from("/data/bar").unwrap(),
target: ExposeTarget::Framework,
rights: Some(*rights::READ_RIGHTS),
subdir: None,
}))
.expose(ExposeDecl::Protocol(ExposeProtocolDecl {
source: ExposeSource::Self_,
source_path: CapabilityPath::try_from("/svc/foo").unwrap(),
target_path: CapabilityPath::try_from("/svc/bar").unwrap(),
target: ExposeTarget::Framework,
}))
.offer_runner_to_children(TEST_RUNNER_NAME)
.build(),
),
(
"c",
ComponentDeclBuilder::new()
.use_(UseDecl::Directory(UseDirectoryDecl {
source: UseSource::Realm,
source_path: CapabilityPath::try_from("/data/baz").unwrap(),
target_path: CapabilityPath::try_from("/data/hippo").unwrap(),
rights: *rights::READ_RIGHTS,
subdir: None,
}))
.use_(UseDecl::Protocol(UseProtocolDecl {
source: UseSource::Realm,
source_path: CapabilityPath::try_from("/svc/baz").unwrap(),
target_path: CapabilityPath::try_from("/svc/hippo").unwrap(),
}))
.offer_runner_to_children(TEST_RUNNER_NAME)
.build(),
),
];
let test = RoutingTest::new("a", components).await;
test.check_use(vec!["c:0"].into(), CheckUse::default_directory(false)).await;
test.check_use(
vec!["c:0"].into(),
CheckUse::Protocol { path: default_service_capability(), should_succeed: false },
)
.await;
}
/// a
/// \
/// b
///
/// a: offers directory /data/hippo to b, but a is not executable
/// a: offers service /svc/hippo to b, but a is not executable
/// b: uses directory /data/hippo as /data/hippo, but it's not in its realm
/// b: uses service /svc/hippo as /svc/hippo, but it's not in its realm
#[fuchsia_async::run_singlethreaded(test)]
async fn offer_from_non_executable() {
let components = vec![
(
"a",
ComponentDeclBuilder::new_empty_component()
.offer(OfferDecl::Directory(OfferDirectoryDecl {
source_path: CapabilityPath::try_from("/data/hippo").unwrap(),
source: OfferDirectorySource::Self_,
target_path: CapabilityPath::try_from("/data/hippo").unwrap(),
target: OfferTarget::Child("b".to_string()),
rights: Some(*rights::READ_RIGHTS),
subdir: None,
dependency_type: DependencyType::Strong,
}))
.offer(OfferDecl::Protocol(OfferProtocolDecl {
source_path: CapabilityPath::try_from("/svc/hippo").unwrap(),
source: OfferServiceSource::Self_,
target_path: CapabilityPath::try_from("/svc/hippo").unwrap(),
target: OfferTarget::Child("b".to_string()),
dependency_type: DependencyType::Strong,
}))
.add_lazy_child("b")
.offer_runner_to_children(TEST_RUNNER_NAME)
.build(),
),
(
"b",
ComponentDeclBuilder::new()
.use_(UseDecl::Directory(UseDirectoryDecl {
source: UseSource::Realm,
source_path: CapabilityPath::try_from("/data/hippo").unwrap(),
target_path: CapabilityPath::try_from("/data/hippo").unwrap(),
rights: *rights::READ_RIGHTS,
subdir: None,
}))
.use_(UseDecl::Protocol(UseProtocolDecl {
source: UseSource::Realm,
source_path: CapabilityPath::try_from("/svc/hippo").unwrap(),
target_path: CapabilityPath::try_from("/svc/hippo").unwrap(),
}))
.offer_runner_to_children(TEST_RUNNER_NAME)
.build(),
),
];
let test = RoutingTest::new("a", components).await;
test.check_use(vec!["b:0"].into(), CheckUse::default_directory(false)).await;
test.check_use(
vec!["b:0"].into(),
CheckUse::Protocol { path: default_service_capability(), should_succeed: false },
)
.await;
}
/// a
/// \
/// b
/// / \
/// [c] [d]
/// a: offers service /svc/hippo to b
/// b: offers service /svc/hippo to collection, creates [c]
/// [c]: instance in collection uses service /svc/hippo
/// [d]: ditto, but with /data/hippo
#[fuchsia_async::run_singlethreaded(test)]
async fn use_in_collection() {
let components = vec![
(
"a",
ComponentDeclBuilder::new()
.offer(OfferDecl::Directory(OfferDirectoryDecl {
source_path: CapabilityPath::try_from("/data/foo").unwrap(),
source: OfferDirectorySource::Self_,
target_path: CapabilityPath::try_from("/data/hippo").unwrap(),
target: OfferTarget::Child("b".to_string()),
rights: Some(*rights::READ_RIGHTS),
subdir: None,
dependency_type: DependencyType::Strong,
}))
.offer(OfferDecl::Protocol(OfferProtocolDecl {
source_path: CapabilityPath::try_from("/svc/foo").unwrap(),
source: OfferServiceSource::Self_,
target_path: CapabilityPath::try_from("/svc/hippo").unwrap(),
target: OfferTarget::Child("b".to_string()),
dependency_type: DependencyType::Strong,
}))
.add_lazy_child("b")
.offer_runner_to_children(TEST_RUNNER_NAME)
.build(),
),
(
"b",
ComponentDeclBuilder::new()
.use_(UseDecl::Protocol(UseProtocolDecl {
source: UseSource::Framework,
source_path: CapabilityPath::try_from("/svc/fuchsia.sys2.Realm").unwrap(),
target_path: CapabilityPath::try_from("/svc/fuchsia.sys2.Realm").unwrap(),
}))
.offer(OfferDecl::Directory(OfferDirectoryDecl {
source_path: CapabilityPath::try_from("/data/hippo").unwrap(),
source: OfferDirectorySource::Realm,
target_path: CapabilityPath::try_from("/data/hippo").unwrap(),
target: OfferTarget::Collection("coll".to_string()),
rights: Some(*rights::READ_RIGHTS),
subdir: None,
dependency_type: DependencyType::Strong,
}))
.offer(OfferDecl::Protocol(OfferProtocolDecl {
source_path: CapabilityPath::try_from("/svc/hippo").unwrap(),
source: OfferServiceSource::Realm,
target_path: CapabilityPath::try_from("/svc/hippo").unwrap(),
target: OfferTarget::Collection("coll".to_string()),
dependency_type: DependencyType::Strong,
}))
.add_collection("coll", fsys::Durability::Transient)
.offer_runner_to_children(TEST_RUNNER_NAME)
.build(),
),
(
"c",
ComponentDeclBuilder::new()
.use_(UseDecl::Directory(UseDirectoryDecl {
source: UseSource::Realm,
source_path: CapabilityPath::try_from("/data/hippo").unwrap(),
target_path: CapabilityPath::try_from("/data/hippo").unwrap(),
rights: *rights::READ_RIGHTS,
subdir: None,
}))
.offer_runner_to_children(TEST_RUNNER_NAME)
.build(),
),
(
"d",
ComponentDeclBuilder::new()
.use_(UseDecl::Protocol(UseProtocolDecl {
source: UseSource::Realm,
source_path: CapabilityPath::try_from("/svc/hippo").unwrap(),
target_path: CapabilityPath::try_from("/svc/hippo").unwrap(),
}))
.offer_runner_to_children(TEST_RUNNER_NAME)
.build(),
),
];
// `RealmCapabilityHost` is needed to create dynamic children.
let test = RoutingTest::new("a", components).await;
test.create_dynamic_child(
vec!["b:0"].into(),
"coll",
ChildDecl {
name: "c".to_string(),
url: "test:///c".to_string(),
startup: fsys::StartupMode::Lazy,
environment: None,
},
)
.await;
test.create_dynamic_child(
vec!["b:0"].into(),
"coll",
ChildDecl {
name: "d".to_string(),
url: "test:///d".to_string(),
startup: fsys::StartupMode::Lazy,
environment: None,
},
)
.await;
test.check_use(vec!["b:0", "coll:c:1"].into(), CheckUse::default_directory(true)).await;
test.check_use(
vec!["b:0", "coll:d:2"].into(),
CheckUse::Protocol { path: default_service_capability(), should_succeed: true },
)
.await;
}
/// a
/// \
/// b
/// \
/// [c]
/// a: offers service /svc/hippo to b
/// b: creates [c]
/// [c]: tries to use /svc/hippo, but can't because service was not offered to its collection
#[fuchsia_async::run_singlethreaded(test)]
async fn use_in_collection_not_offered() {
let components = vec![
(
"a",
ComponentDeclBuilder::new()
.offer(OfferDecl::Directory(OfferDirectoryDecl {
source_path: CapabilityPath::try_from("/data/foo").unwrap(),
source: OfferDirectorySource::Self_,
target_path: CapabilityPath::try_from("/data/hippo").unwrap(),
target: OfferTarget::Child("b".to_string()),
rights: Some(*rights::READ_RIGHTS),
subdir: None,
dependency_type: DependencyType::Strong,
}))
.offer(OfferDecl::Protocol(OfferProtocolDecl {
source_path: CapabilityPath::try_from("/svc/foo").unwrap(),
source: OfferServiceSource::Self_,
target_path: CapabilityPath::try_from("/svc/hippo").unwrap(),
target: OfferTarget::Child("b".to_string()),
dependency_type: DependencyType::Strong,
}))
.add_lazy_child("b")
.offer_runner_to_children(TEST_RUNNER_NAME)
.build(),
),
(
"b",
ComponentDeclBuilder::new()
.use_(UseDecl::Protocol(UseProtocolDecl {
source: UseSource::Framework,
source_path: CapabilityPath::try_from("/svc/fuchsia.sys2.Realm").unwrap(),
target_path: CapabilityPath::try_from("/svc/fuchsia.sys2.Realm").unwrap(),
}))
.add_collection("coll", fsys::Durability::Transient)
.offer_runner_to_children(TEST_RUNNER_NAME)
.build(),
),
(
"c",
ComponentDeclBuilder::new()
.use_(UseDecl::Directory(UseDirectoryDecl {
source: UseSource::Realm,
source_path: CapabilityPath::try_from("/data/hippo").unwrap(),
target_path: CapabilityPath::try_from("/data/hippo").unwrap(),
rights: *rights::READ_RIGHTS,
subdir: None,
}))
.use_(UseDecl::Protocol(UseProtocolDecl {
source: UseSource::Realm,
source_path: CapabilityPath::try_from("/svc/hippo").unwrap(),
target_path: CapabilityPath::try_from("/svc/hippo").unwrap(),
}))
.offer_runner_to_children(TEST_RUNNER_NAME)
.build(),
),
];
// `RealmCapabilityHost` is needed to create dynamic children.
let test = RoutingTest::new("a", components).await;
test.create_dynamic_child(
vec!["b:0"].into(),
"coll",
ChildDecl {
name: "c".to_string(),
url: "test:///c".to_string(),
startup: fsys::StartupMode::Lazy,
environment: None,
},
)
.await;
test.check_use(vec!["b:0", "coll:c:1"].into(), CheckUse::default_directory(false)).await;
test.check_use(
vec!["b:0", "coll:c:1"].into(),
CheckUse::Protocol { path: default_service_capability(), should_succeed: false },
)
.await;
}
/// a
/// \
/// b
/// \
/// c
///
/// a: offers directory /data/foo from self with subdir 's1/s2'
/// b: offers directory /data/foo from realm with subdir 's3'
/// c: uses /data/foo as /data/hippo
#[fuchsia_async::run_singlethreaded(test)]
async fn use_directory_with_subdir_from_grandparent() {
let components = vec![
(
"a",
ComponentDeclBuilder::new()
.offer(OfferDecl::Directory(OfferDirectoryDecl {
source: OfferDirectorySource::Self_,
source_path: CapabilityPath::try_from("/data/foo").unwrap(),
target_path: CapabilityPath::try_from("/data/foo").unwrap(),
target: OfferTarget::Child("b".to_string()),
rights: Some(*rights::READ_RIGHTS),
subdir: Some(PathBuf::from("s1/s2")),
dependency_type: DependencyType::Strong,
}))
.add_lazy_child("b")
.offer_runner_to_children(TEST_RUNNER_NAME)
.build(),
),
(
"b",
ComponentDeclBuilder::new()
.offer(OfferDecl::Directory(OfferDirectoryDecl {
source: OfferDirectorySource::Realm,
source_path: CapabilityPath::try_from("/data/foo").unwrap(),
target_path: CapabilityPath::try_from("/data/foo").unwrap(),
target: OfferTarget::Child("c".to_string()),
rights: Some(*rights::READ_RIGHTS),
subdir: Some(PathBuf::from("s3")),
dependency_type: DependencyType::Strong,
}))
.add_lazy_child("c")
.offer_runner_to_children(TEST_RUNNER_NAME)
.build(),
),
(
"c",
ComponentDeclBuilder::new()
.use_(UseDecl::Directory(UseDirectoryDecl {
source: UseSource::Realm,
source_path: CapabilityPath::try_from("/data/foo").unwrap(),
target_path: CapabilityPath::try_from("/data/hippo").unwrap(),
rights: *rights::READ_RIGHTS,
subdir: Some(PathBuf::from("s4")),
}))
.offer_runner_to_children(TEST_RUNNER_NAME)
.build(),
),
];
let test = RoutingTest::new("a", components).await;
test.create_static_file(Path::new("foo/s1/s2/s3/s4/inner"), "hippo")
.await
.expect("failed to create file");
test.check_use(
vec!["b:0", "c:0"].into(),
CheckUse::Directory {
path: default_directory_capability(),
file: PathBuf::from("inner"),
should_succeed: true,
},
)
.await;
test.check_use(vec!["b:0", "c:0"].into(), CheckUse::default_directory(false)).await;
}
/// a
/// / \
/// b c
///
///
/// b: exposes directory /data/foo from self with subdir 's1/s2'
/// a: offers directory /data/foo from `b` to `c` with subdir 's3'
/// c: uses /data/foo as /data/hippo
#[fuchsia_async::run_singlethreaded(test)]
async fn use_directory_with_subdir_from_sibling() {
let components = vec![
(
"a",
ComponentDeclBuilder::new()
.offer(OfferDecl::Directory(OfferDirectoryDecl {
source: OfferDirectorySource::Child("b".to_string()),
source_path: CapabilityPath::try_from("/data/foo").unwrap(),
target: OfferTarget::Child("c".to_string()),
target_path: CapabilityPath::try_from("/data/foo").unwrap(),
rights: Some(*rights::READ_RIGHTS),
subdir: Some(PathBuf::from("s3")),
dependency_type: DependencyType::Strong,
}))
.add_lazy_child("b")
.add_lazy_child("c")
.offer_runner_to_children(TEST_RUNNER_NAME)
.build(),
),
(
"b",
ComponentDeclBuilder::new()
.expose(ExposeDecl::Directory(ExposeDirectoryDecl {
source: ExposeSource::Self_,
source_path: CapabilityPath::try_from("/data/foo").unwrap(),
target: ExposeTarget::Realm,
target_path: CapabilityPath::try_from("/data/foo").unwrap(),
rights: Some(*rights::READ_RIGHTS),
subdir: Some(PathBuf::from("s1/s2")),
}))
.build(),
),
(
"c",
ComponentDeclBuilder::new()
.use_(UseDecl::Directory(UseDirectoryDecl {
source: UseSource::Realm,
source_path: CapabilityPath::try_from("/data/foo").unwrap(),
target_path: CapabilityPath::try_from("/data/hippo").unwrap(),
rights: *rights::READ_RIGHTS,
subdir: None,
}))
.build(),
),
];
let test = RoutingTest::new("a", components).await;
test.create_static_file(Path::new("foo/s1/s2/s3/inner"), "hippo")
.await
.expect("failed to create file");
test.check_use(
vec!["c:0"].into(),
CheckUse::Directory {
path: default_directory_capability(),
file: PathBuf::from("inner"),
should_succeed: true,
},
)
.await;
test.check_use(vec!["c:0"].into(), CheckUse::default_directory(false)).await;
}
/// a
/// \
/// b
/// \
/// c
///
/// c: exposes /data/foo from self
/// b: exposes /data/foo from `c` with subdir `s1/s2`
/// a: exposes /data/foo from `b` with subdir `s3` as /data/hippo
/// use /data/hippo from a's exposed dir
#[fuchsia_async::run_singlethreaded(test)]
async fn expose_directory_with_subdir() {
let components = vec![
(
"a",
ComponentDeclBuilder::new()
.expose(ExposeDecl::Directory(ExposeDirectoryDecl {
source: ExposeSource::Child("b".to_string()),
source_path: CapabilityPath::try_from("/data/foo").unwrap(),
target_path: CapabilityPath::try_from("/data/hippo").unwrap(),
target: ExposeTarget::Realm,
rights: None,
subdir: Some(PathBuf::from("s3")),
}))
.add_lazy_child("b")
.offer_runner_to_children(TEST_RUNNER_NAME)
.build(),
),
(
"b",
ComponentDeclBuilder::new()
.expose(ExposeDecl::Directory(ExposeDirectoryDecl {
source: ExposeSource::Child("c".to_string()),
source_path: CapabilityPath::try_from("/data/foo").unwrap(),
target_path: CapabilityPath::try_from("/data/foo").unwrap(),
target: ExposeTarget::Realm,
rights: None,
subdir: Some(PathBuf::from("s1/s2")),
}))
.add_lazy_child("c")
.offer_runner_to_children(TEST_RUNNER_NAME)
.build(),
),
(
"c",
ComponentDeclBuilder::new()
.expose(ExposeDecl::Directory(ExposeDirectoryDecl {
source: ExposeSource::Self_,
source_path: CapabilityPath::try_from("/data/foo").unwrap(),
target_path: CapabilityPath::try_from("/data/foo").unwrap(),
target: ExposeTarget::Realm,
rights: Some(*rights::READ_RIGHTS),
subdir: None,
}))
.build(),
),
];
let test = RoutingTest::new("a", components).await;
test.create_static_file(Path::new("foo/s1/s2/s3/inner"), "hippo")
.await
.expect("failed to create file");
test.check_use_exposed_dir(
vec![].into(),
CheckUse::Directory {
path: "/data/hippo".try_into().unwrap(),
file: PathBuf::from("inner"),
should_succeed: true,
},
)
.await;
test.check_use_exposed_dir(vec![].into(), CheckUse::default_directory(false)).await;
}
/// a
/// \
/// b
/// \
/// c
///
/// a: declares runner "elf" as service "/svc/runner" from self.
/// a: offers runner "elf" from self to "b" as "dwarf".
/// b: offers runner "dwarf" from parent to "c" as "hobbit".
/// c: uses runner "hobbit".
#[fuchsia_async::run_singlethreaded(test)]
async fn use_runner_from_grandparent() {
let components = vec![
(
"a",
ComponentDeclBuilder::new()
.offer(OfferDecl::Runner(OfferRunnerDecl {
source: OfferRunnerSource::Self_,
source_name: CapabilityName("elf".to_string()),
target: OfferTarget::Child("b".to_string()),
target_name: CapabilityName("dwarf".to_string()),
}))
.add_lazy_child("b")
.runner(RunnerDecl {
name: "elf".to_string(),
source: RunnerSource::Self_,
source_path: CapabilityPath::try_from("/svc/runner").unwrap(),
})
.offer_runner_to_children(TEST_RUNNER_NAME)
.build(),
),
(
"b",
ComponentDeclBuilder::new()
.offer(OfferDecl::Runner(OfferRunnerDecl {
source: OfferRunnerSource::Realm,
source_name: CapabilityName("dwarf".to_string()),
target: OfferTarget::Child("c".to_string()),
target_name: CapabilityName("hobbit".to_string()),
}))
.add_lazy_child("c")
.build(),
),
("c", ComponentDeclBuilder::new_empty_component().use_runner("hobbit").build()),
];
// Set up the system.
let (runner_service, mut receiver) =
create_service_directory_entry::<fcrunner::ComponentRunnerMarker>();
let universe = RoutingTestBuilder::new("a", components)
// Component "a" exposes a runner service.
.add_outgoing_path("a", CapabilityPath::try_from("/svc/runner").unwrap(), runner_service)
.build()
.await;
join!(
// Bind "c:0". We expect to see a call to our runner service for the new component.
async move {
universe.bind_instance(&vec!["b:0", "c:0"].into()).await.unwrap();
},
// Wait for a request, and ensure it has the correct URL.
async move {
assert_eq!(
wait_for_runner_request(&mut receiver).await.resolved_url,
Some("test:///c_resolved".to_string())
);
}
);
}
/// a
/// / \
/// r b
///
/// r: declares runner "elf" as service "/svc/runner" from self.
/// r: exposes runner "elf" to "a" as "dwarf".
/// a: offers runner "dwarf" from "r" to "b" as "hobbit".
/// b: uses runner "hobbit".
#[fuchsia_async::run_singlethreaded(test)]
async fn use_runner_from_sibling() {
let components = vec![
(
"a",
ComponentDeclBuilder::new()
.offer(OfferDecl::Runner(OfferRunnerDecl {
source: OfferRunnerSource::Child("r".to_string()),
source_name: CapabilityName("dwarf".to_string()),
target: OfferTarget::Child("b".to_string()),
target_name: CapabilityName("hobbit".to_string()),
}))
.add_lazy_child("b")
.add_lazy_child("r")
.offer_runner_to_children(TEST_RUNNER_NAME)
.build(),
),
(
"r",
ComponentDeclBuilder::new()
.expose(ExposeDecl::Runner(ExposeRunnerDecl {
source: ExposeSource::Self_,
source_name: CapabilityName("elf".to_string()),
target: ExposeTarget::Realm,
target_name: CapabilityName("dwarf".to_string()),
}))
.runner(RunnerDecl {
name: "elf".to_string(),
source: RunnerSource::Self_,
source_path: CapabilityPath::try_from("/svc/runner").unwrap(),
})
.offer_runner_to_children(TEST_RUNNER_NAME)
.build(),
),
("b", ComponentDeclBuilder::new_empty_component().use_runner("hobbit").build()),
];
// Set up the system.
let (runner_service, mut receiver) =
create_service_directory_entry::<fcrunner::ComponentRunnerMarker>();
let universe = RoutingTestBuilder::new("a", components)
// Component "r" exposes a runner service.
.add_outgoing_path("r", CapabilityPath::try_from("/svc/runner").unwrap(), runner_service)
.build()
.await;
join!(
// Bind "b:0". We expect to see a call to our runner service for the new component.
async move {
universe.bind_instance(&vec!["b:0"].into()).await.unwrap();
},
// Wait for a request, and ensure it has the correct URL.
async move {
assert_eq!(
wait_for_runner_request(&mut receiver).await.resolved_url,
Some("test:///b_resolved".to_string())
);
}
);
}
#[fuchsia_async::run_singlethreaded(test)]
async fn expose_from_self_and_child() {
let components = vec![
(
"a",
ComponentDeclBuilder::new()
.add_lazy_child("b")
.offer_runner_to_children(TEST_RUNNER_NAME)
.build(),
),
(
"b",
ComponentDeclBuilder::new()
.expose(ExposeDecl::Directory(ExposeDirectoryDecl {
source: ExposeSource::Child("c".to_string()),
source_path: CapabilityPath::try_from("/data/hippo").unwrap(),
target_path: CapabilityPath::try_from("/data/bar/hippo").unwrap(),
target: ExposeTarget::Realm,
rights: Some(*rights::READ_RIGHTS),
subdir: None,
}))
.expose(ExposeDecl::Protocol(ExposeProtocolDecl {
source: ExposeSource::Child("c".to_string()),
source_path: CapabilityPath::try_from("/svc/hippo").unwrap(),
target_path: CapabilityPath::try_from("/svc/bar/hippo").unwrap(),
target: ExposeTarget::Realm,
}))
.add_lazy_child("c")
.offer_runner_to_children(TEST_RUNNER_NAME)
.build(),
),
(
"c",
ComponentDeclBuilder::new()
.expose(ExposeDecl::Directory(ExposeDirectoryDecl {
source: ExposeSource::Self_,
source_path: CapabilityPath::try_from("/data/foo").unwrap(),
target_path: CapabilityPath::try_from("/data/hippo").unwrap(),
target: ExposeTarget::Realm,
rights: Some(*rights::READ_RIGHTS),
subdir: None,
}))
.expose(ExposeDecl::Protocol(ExposeProtocolDecl {
source: ExposeSource::Self_,
source_path: CapabilityPath::try_from("/svc/foo").unwrap(),
target_path: CapabilityPath::try_from("/svc/hippo").unwrap(),
target: ExposeTarget::Realm,
}))
.offer_runner_to_children(TEST_RUNNER_NAME)
.build(),
),
];
let test = RoutingTest::new("a", components).await;
test.check_use_exposed_dir(
vec!["b:0"].into(),
CheckUse::Directory {
path: "/data/bar/hippo".try_into().unwrap(),
file: PathBuf::from("hippo"),
should_succeed: true,
},
)
.await;
test.check_use_exposed_dir(
vec!["b:0"].into(),
CheckUse::Protocol { path: "/svc/bar/hippo".try_into().unwrap(), should_succeed: true },
)
.await;
test.check_use_exposed_dir(vec!["b:0", "c:0"].into(), CheckUse::default_directory(true)).await;
test.check_use_exposed_dir(
vec!["b:0", "c:0"].into(),
CheckUse::Protocol { path: default_service_capability(), should_succeed: true },
)
.await;
}
#[fuchsia_async::run_singlethreaded(test)]
async fn use_not_exposed() {
let components = vec![
(
"a",
ComponentDeclBuilder::new()
.add_lazy_child("b")
.offer_runner_to_children(TEST_RUNNER_NAME)
.build(),
),
(
"b",
ComponentDeclBuilder::new()
.add_lazy_child("c")
.offer_runner_to_children(TEST_RUNNER_NAME)
.build(),
),
(
"c",
ComponentDeclBuilder::new()
.expose(ExposeDecl::Directory(ExposeDirectoryDecl {
source: ExposeSource::Self_,
source_path: CapabilityPath::try_from("/data/foo").unwrap(),
target_path: CapabilityPath::try_from("/data/hippo").unwrap(),
target: ExposeTarget::Realm,
rights: Some(*rights::READ_RIGHTS),
subdir: None,
}))
.expose(ExposeDecl::Protocol(ExposeProtocolDecl {
source: ExposeSource::Self_,
source_path: CapabilityPath::try_from("/svc/foo").unwrap(),
target_path: CapabilityPath::try_from("/svc/hippo").unwrap(),
target: ExposeTarget::Realm,
}))
.offer_runner_to_children(TEST_RUNNER_NAME)
.build(),
),
];
let test = RoutingTest::new("a", components).await;
// Capability is only exposed from "c", so it only be usable from there.
test.check_use_exposed_dir(vec!["b:0"].into(), CheckUse::default_directory(false)).await;
test.check_use_exposed_dir(
vec!["b:0"].into(),
CheckUse::Protocol { path: default_service_capability(), should_succeed: false },
)
.await;
test.check_use_exposed_dir(vec!["b:0", "c:0"].into(), CheckUse::default_directory(true)).await;
test.check_use_exposed_dir(
vec!["b:0", "c:0"].into(),
CheckUse::Protocol { path: default_service_capability(), should_succeed: true },
)
.await;
}
/// a
/// \
/// [b]
/// \
/// c
///
/// a: offers service /svc/foo from self
/// [b]: offers service /svc/foo to c
/// [b]: is destroyed
/// c: uses service /svc/foo, which should fail
#[fuchsia_async::run_singlethreaded(test)]
async fn use_with_destroyed_parent() {
let use_decl = UseDecl::Protocol(UseProtocolDecl {
source: UseSource::Realm,
source_path: CapabilityPath::try_from("/svc/foo").unwrap(),
target_path: CapabilityPath::try_from("/svc/hippo").unwrap(),
});
let components = vec![
(
"a",
ComponentDeclBuilder::new()
.use_(UseDecl::Protocol(UseProtocolDecl {
source: UseSource::Framework,
source_path: CapabilityPath::try_from("/svc/fuchsia.sys2.Realm").unwrap(),
target_path: CapabilityPath::try_from("/svc/fuchsia.sys2.Realm").unwrap(),
}))
.offer(OfferDecl::Protocol(OfferProtocolDecl {
source: OfferServiceSource::Self_,
source_path: CapabilityPath::try_from("/svc/foo").unwrap(),
target_path: CapabilityPath::try_from("/svc/foo").unwrap(),
target: OfferTarget::Collection("coll".to_string()),
dependency_type: DependencyType::Strong,
}))
.add_collection("coll", fsys::Durability::Transient)
.offer_runner_to_children(TEST_RUNNER_NAME)
.build(),
),
(
"b",
ComponentDeclBuilder::new()
.offer(OfferDecl::Protocol(OfferProtocolDecl {
source: OfferServiceSource::Realm,
source_path: CapabilityPath::try_from("/svc/foo").unwrap(),
target_path: CapabilityPath::try_from("/svc/foo").unwrap(),
target: OfferTarget::Child("c".to_string()),
dependency_type: DependencyType::Strong,
}))
.add_lazy_child("c")
.offer_runner_to_children(TEST_RUNNER_NAME)
.build(),
),
(
"c",
ComponentDeclBuilder::new()
.use_(use_decl.clone())
.offer_runner_to_children(TEST_RUNNER_NAME)
.build(),
),
];
let test = RoutingTest::new("a", components).await;
test.create_dynamic_child(
vec![].into(),
"coll",
ChildDecl {
name: "b".to_string(),
url: "test:///b".to_string(),
startup: fsys::StartupMode::Lazy,
environment: None,
},
)
.await;
// Confirm we can use service from "c".
test.check_use(
vec!["coll:b:1", "c:0"].into(),
CheckUse::Protocol { path: default_service_capability(), should_succeed: true },
)
.await;
// Destroy "b", but preserve a reference to "c" so we can route from it below.
let moniker = vec!["coll:b:1", "c:0"].into();
let realm_c = test.model.look_up_realm(&moniker).await.expect("failed to look up realm b");
test.destroy_dynamic_child(vec![].into(), "coll", "b").await;
// Now attempt to route the service from "c". Should fail because "b" does not exist so we
// cannot follow it.
let (_client, server) = zx::Channel::create().unwrap();
let err = routing::route_use_capability(
OPEN_RIGHT_READABLE,
MODE_TYPE_SERVICE,
"hippo".to_string(),
&use_decl,
&realm_c,
server,
)
.await
.expect_err("routing unexpectedly succeeded");
assert_eq!(
format!("{:?}", err),
format!("{:?}", ModelError::instance_not_found(vec!["coll:b:1"].into()))
);
}
/// (cm)
/// |
/// a
///
/// a: uses an invalid service from the component manager.
#[fuchsia_async::run_singlethreaded(test)]
async fn invalid_use_from_component_manager() {
let components = vec![(
"a",
ComponentDeclBuilder::new()
.use_(UseDecl::Protocol(UseProtocolDecl {
source: UseSource::Realm,
source_path: CapabilityPath::try_from("/invalid").unwrap(),
target_path: CapabilityPath::try_from("/svc/valid").unwrap(),
}))
.offer_runner_to_children(TEST_RUNNER_NAME)
.build(),
)];
// Try and use the service. We expect a failure.
let universe = RoutingTest::new("a", components).await;
universe
.check_use(
vec![].into(),
CheckUse::Protocol {
path: CapabilityPath::try_from("/svc/valid").unwrap(),
should_succeed: false,
},
)
.await;
}
/// (cm)
/// |
/// a
/// |
/// b
///
/// a: offers an invalid service from the component manager to "b".
/// b: attempts to use the service
#[fuchsia_async::run_singlethreaded(test)]
async fn invalid_offer_from_component_manager() {
let components = vec![
(
"a",
ComponentDeclBuilder::new()
.offer(OfferDecl::Protocol(OfferProtocolDecl {
source_path: CapabilityPath::try_from("/invalid").unwrap(),
source: OfferServiceSource::Realm,
target_path: CapabilityPath::try_from("/svc/valid").unwrap(),
target: OfferTarget::Child("b".to_string()),
dependency_type: DependencyType::Strong,
}))
.add_lazy_child("b")
.offer_runner_to_children(TEST_RUNNER_NAME)
.build(),
),
(
"b",
ComponentDeclBuilder::new()
.use_(UseDecl::Protocol(UseProtocolDecl {
source: UseSource::Realm,
source_path: CapabilityPath::try_from("/svc/valid").unwrap(),
target_path: CapabilityPath::try_from("/svc/valid").unwrap(),
}))
.offer_runner_to_children(TEST_RUNNER_NAME)
.build(),
),
];
// Try and use the service. We expect a failure.
let universe = RoutingTest::new("a", components).await;
universe
.check_use(
vec!["b:0"].into(),
CheckUse::Protocol {
path: CapabilityPath::try_from("/svc/valid").unwrap(),
should_succeed: false,
},
)
.await;
}
/// a
/// \
/// b
///
/// b: uses framework event "started"
#[fuchsia_async::run_singlethreaded(test)]
async fn use_event_from_framework() {
let components = vec![
(
"a",
ComponentDeclBuilder::new()
.add_lazy_child("b")
.offer_runner_to_children(TEST_RUNNER_NAME)
.build(),
),
(
"b",
ComponentDeclBuilder::new()
.use_(UseDecl::Protocol(UseProtocolDecl {
source: UseSource::Framework,
source_path: EVENT_SOURCE_SYNC_SERVICE_PATH.clone(),
target_path: EVENT_SOURCE_SYNC_SERVICE_PATH.clone(),
}))
.use_(UseDecl::Event(UseEventDecl {
source: UseSource::Framework,
source_name: "started".into(),
target_name: "started".into(),
filter: None,
}))
.build(),
),
];
let test = RoutingTest::new("a", components).await;
test.check_use(
vec!["b:0"].into(),
CheckUse::Event { names: vec!["started".into()], should_be_allowed: true },
)
.await;
}
/// a
/// \
/// b
///
/// a: uses framework event "started" and offers to b as "started_on_a"
/// b: uses framework event "started_on_a" as "started"
#[fuchsia_async::run_singlethreaded(test)]
async fn use_event_from_parent() {
let components = vec![
(
"a",
ComponentDeclBuilder::new()
.offer(OfferDecl::Event(OfferEventDecl {
source: OfferEventSource::Framework,
source_name: "started".into(),
target_name: "started_on_a".into(),
target: OfferTarget::Child("b".to_string()),
filter: None,
}))
.add_lazy_child("b")
.offer_runner_to_children(TEST_RUNNER_NAME)
.build(),
),
(
"b",
ComponentDeclBuilder::new()
.use_(UseDecl::Protocol(UseProtocolDecl {
source: UseSource::Framework,
source_path: EVENT_SOURCE_SYNC_SERVICE_PATH.clone(),
target_path: EVENT_SOURCE_SYNC_SERVICE_PATH.clone(),
}))
.use_(UseDecl::Event(UseEventDecl {
source: UseSource::Realm,
source_name: "started_on_a".into(),
target_name: "started".into(),
filter: None,
}))
.build(),
),
];
let test = RoutingTest::new("a", components).await;
test.check_use(
vec!["b:0"].into(),
CheckUse::Event { names: vec!["started".into()], should_be_allowed: true },
)
.await;
}
/// a
/// \
/// b
/// \
/// c
///
/// a: uses framework event "started" and offers to b as "started_on_a"
/// a: uses framework event "stopped" and offers to b as "stopped_on_a"
/// b: offers realm event "started_on_a" to c
/// b: offers realm event "destroyed" from framework
/// c: uses realm event "started_on_a"
/// c: uses realm event "destroyed"
/// c: uses realm event "stopped_on_a" but fails to do so
#[fuchsia_async::run_singlethreaded(test)]
async fn use_event_from_grandparent() {
let components = vec![
(
"a",
ComponentDeclBuilder::new()
.offer(OfferDecl::Event(OfferEventDecl {
source: OfferEventSource::Framework,
source_name: "started".into(),
target_name: "started_on_a".into(),
target: OfferTarget::Child("b".to_string()),
filter: None,
}))
.offer(OfferDecl::Event(OfferEventDecl {
source: OfferEventSource::Framework,
source_name: "stopped".into(),
target_name: "stopped_on_b".into(),
target: OfferTarget::Child("b".to_string()),
filter: None,
}))
.add_lazy_child("b")
.offer_runner_to_children(TEST_RUNNER_NAME)
.build(),
),
(
"b",
ComponentDeclBuilder::new()
.offer(OfferDecl::Event(OfferEventDecl {
source: OfferEventSource::Realm,
source_name: "started_on_a".into(),
target_name: "started_on_a".into(),
target: OfferTarget::Child("c".to_string()),
filter: None,
}))
.offer(OfferDecl::Event(OfferEventDecl {
source: OfferEventSource::Framework,
source_name: "destroyed".into(),
target_name: "destroyed".into(),
target: OfferTarget::Child("c".to_string()),
filter: Some(hashmap!{"path".to_string() => DictionaryValue::Str("/diagnostics".to_string())}),
}))
.add_lazy_child("c")
.offer_runner_to_children(TEST_RUNNER_NAME)
.build(),
),
(
"c",
ComponentDeclBuilder::new()
.use_(UseDecl::Protocol(UseProtocolDecl {
source: UseSource::Framework,
source_path: EVENT_SOURCE_SYNC_SERVICE_PATH.clone(),
target_path: EVENT_SOURCE_SYNC_SERVICE_PATH.clone(),
}))
.use_(UseDecl::Event(UseEventDecl {
source: UseSource::Realm,
source_name: "started_on_a".into(),
target_name: "started".into(),
filter: None,
}))
.use_(UseDecl::Event(UseEventDecl {
source: UseSource::Realm,
source_name: "destroyed".into(),
target_name: "destroyed".into(),
filter: Some(hashmap!{"path".to_string() => DictionaryValue::Str("/diagnostics".to_string())}),
}))
.use_(UseDecl::Event(UseEventDecl {
source: UseSource::Realm,
source_name: "stopped_on_a".into(),
target_name: "stopped".into(),
filter: None,
}))
.build(),
),
];
let test = RoutingTest::new("a", components).await;
test.check_use(
vec!["b:0", "c:0"].into(),
CheckUse::Event {
names: vec!["started".into(), "destroyed".into()],
should_be_allowed: true,
},
)
.await;
test.check_use(
vec!["b:0", "c:0"].into(),
CheckUse::Event { names: vec!["stopped".into()], should_be_allowed: false },
)
.await;
}