blob: 068e565a8e01cd32ec721719be76425eecf58fc6 [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, InternalCapability},
channel,
config::{CapabilityAllowlistKey, CapabilityAllowlistSource},
framework::REALM_SERVICE,
model::{
error::ModelError,
events::{event::EventMode, registry::EventSubscription},
hooks::{Event, EventPayload, EventType, Hook, HooksRegistration},
resolver::ResolverError,
rights,
routing::{self, RoutingError},
testing::{
routing_test_helpers::capability_util::connect_to_svc_in_namespace,
routing_test_helpers::*, test_helpers::*,
},
},
},
anyhow::Error,
async_trait::async_trait,
cm_rust::*,
fidl::endpoints::ServerEnd,
fidl_fidl_examples_echo::{self as echo},
fidl_fuchsia_component_runner as fcrunner,
fidl_fuchsia_io::{MODE_TYPE_SERVICE, OPEN_RIGHT_READABLE},
fidl_fuchsia_sys2 as fsys, fuchsia_async as fasync,
fuchsia_zircon::{self as zx, Status},
futures::{join, lock::Mutex, StreamExt, TryStreamExt},
log::*,
maplit::hashmap,
matches::assert_matches,
moniker::{AbsoluteMoniker, ExtendedMoniker},
std::{
collections::HashSet,
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: &mut zx::Channel,
) -> Result<(), ModelError> {
let server_end = channel::take_channel(server_end);
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::Task::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);
}
})
.detach();
Ok(())
}
}
#[async_trait]
impl Hook for MockRealmCapabilityHost {
async fn on(self: Arc<Self>, event: &Event) -> Result<(), ModelError> {
if let Ok(EventPayload::CapabilityRouted {
source: CapabilitySource::Framework { capability, scope_moniker },
capability_provider,
}) = &event.result
{
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 InternalCapability,
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.
if capability.matches_protocol(&REALM_SERVICE) {
Ok(Some(Box::new(MockRealmCapabilityProvider::new(
scope_moniker.clone(),
self.clone(),
)) as Box<dyn CapabilityProvider>))
} else {
Ok(capability_provider)
}
}
}
let components = vec![
("a", ComponentDeclBuilder::new().add_lazy_child("b").build()),
(
"b",
ComponentDeclBuilder::new()
.use_(UseDecl::Protocol(UseProtocolDecl {
source: UseSource::Framework,
source_name: "fuchsia.sys2.Realm".into(),
target_path: CapabilityPath::try_from("/svc/fuchsia.sys2.Realm").unwrap(),
}))
.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()
.directory(DirectoryDeclBuilder::new("foo_data").build())
.protocol(ProtocolDeclBuilder::new("foo_svc").build())
.protocol(ProtocolDeclBuilder::new("file").path("/svc/file").build())
.offer(OfferDecl::Directory(OfferDirectoryDecl {
source: OfferDirectorySource::Self_,
source_name: "foo_data".into(),
target_name: "bar_data".into(),
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_name: "foo_svc".into(),
target_name: "bar_svc".into(),
target: OfferTarget::Child("b".to_string()),
dependency_type: DependencyType::Strong,
}))
.offer(OfferDecl::Protocol(OfferProtocolDecl {
source: OfferServiceSource::Self_,
source_name: "file".into(),
target_name: "device".into(),
target: OfferTarget::Child("b".to_string()),
dependency_type: DependencyType::Strong,
}))
.add_lazy_child("b")
.build(),
),
(
"b",
ComponentDeclBuilder::new()
.use_(UseDecl::Directory(UseDirectoryDecl {
source: UseSource::Parent,
source_name: "bar_data".into(),
target_path: CapabilityPath::try_from("/data/hippo").unwrap(),
rights: *rights::READ_RIGHTS,
subdir: None,
}))
.use_(UseDecl::Protocol(UseProtocolDecl {
source: UseSource::Parent,
source_name: "bar_svc".into(),
target_path: CapabilityPath::try_from("/svc/hippo").unwrap(),
}))
.use_(UseDecl::Protocol(UseProtocolDecl {
source: UseSource::Parent,
source_name: "device".into(),
target_path: CapabilityPath::try_from("/svc/device").unwrap(),
}))
.build(),
),
];
let test = RoutingTest::new("a", components).await;
test.check_use(vec!["b:0"].into(), CheckUse::default_directory(ExpectedResult::Ok)).await;
test.check_use(
vec!["b:0"].into(),
CheckUse::Protocol { path: default_service_capability(), expected_res: ExpectedResult::Ok },
)
.await;
test.check_open_file(vec!["b:0"].into(), "/svc/device".try_into().unwrap()).await;
}
/// a
/// \
/// b
///
/// a: offers service /svc/foo from self as /svc/bar
/// b: uses service /svc/bar as /svc/hippo
///
/// This test verifies that the parent, if subscribed to the CapabilityRequested event will receive
/// if when the child connects to /svc/hippo.
#[fuchsia_async::run_singlethreaded(test)]
async fn capability_requested_event_at_parent() {
let components = vec![
(
"a",
ComponentDeclBuilder::new()
.protocol(ProtocolDeclBuilder::new("foo_svc").build())
.offer(OfferDecl::Protocol(OfferProtocolDecl {
source: OfferServiceSource::Self_,
source_name: "foo_svc".into(),
target_name: "bar_svc".into(),
target: OfferTarget::Child("b".to_string()),
dependency_type: DependencyType::Strong,
}))
.use_(UseDecl::Protocol(UseProtocolDecl {
source: UseSource::Parent,
source_name: "fuchsia.sys2.EventSource".try_into().unwrap(),
target_path: "/svc/fuchsia.sys2.EventSource".try_into().unwrap(),
}))
.use_(UseDecl::Event(UseEventDecl {
source: UseSource::Framework,
source_name: "capability_requested".into(),
target_name: "capability_requested".into(),
filter: Some(hashmap!{"name".to_string() => DictionaryValue::Str("foo_svc".to_string())}),
mode: cm_rust::EventMode::Async,
}))
.add_lazy_child("b")
.build(),
),
(
"b",
ComponentDeclBuilder::new()
.use_(UseDecl::Protocol(UseProtocolDecl {
source: UseSource::Parent,
source_name: "bar_svc".into(),
target_path: CapabilityPath::try_from("/svc/hippo").unwrap(),
}))
.build(),
),
];
let test = RoutingTest::new("a", components).await;
let namespace_root = test.bind_and_get_namespace(AbsoluteMoniker::root()).await;
let mut event_stream = capability_util::subscribe_to_events(
&namespace_root,
&CapabilityPath::try_from("/svc/fuchsia.sys2.EventSource").unwrap(),
vec![EventSubscription::new("capability_requested".into(), EventMode::Async)],
)
.await
.unwrap();
let namespace_b = test.bind_and_get_namespace(vec!["b:0"].into()).await;
let _echo_proxy = connect_to_svc_in_namespace::<echo::EchoMarker>(
&namespace_b,
&"/svc/hippo".try_into().unwrap(),
)
.await;
let event = match event_stream.next().await {
Some(Ok(fsys::EventStreamRequest::OnEvent { event, .. })) => event,
_ => panic!("Event not found"),
};
// 'b' is the target and 'a' is receiving the event so the relative moniker
// is './b:0'.
assert_matches!(&event,
fsys::Event {
header: Some(fsys::EventHeader {
moniker: Some(moniker), .. }), ..
} if *moniker == "./b:0".to_string() );
assert_matches!(&event,
fsys::Event {
header: Some(fsys::EventHeader {
component_url: Some(component_url), .. }), ..
} if *component_url == "test:///b".to_string() );
assert_matches!(&event,
fsys::Event {
event_result: Some(
fsys::EventResult::Payload(
fsys::EventPayload::CapabilityRequested(
fsys::CapabilityRequestedPayload { name: Some(name), .. }))), ..}
if *name == "foo_svc".to_string()
);
}
/// 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()
.directory(DirectoryDeclBuilder::new("foo_data").build())
.protocol(ProtocolDeclBuilder::new("foo_svc").build())
.offer(OfferDecl::Directory(OfferDirectoryDecl {
source: OfferDirectorySource::Self_,
source_name: "foo_data".into(),
target_name: "bar_data".into(),
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_name: "foo_svc".into(),
target_name: "bar_svc".into(),
target: OfferTarget::Child("b".to_string()),
dependency_type: DependencyType::Strong,
}))
.add_lazy_child("b")
.build(),
),
(
"b",
ComponentDeclBuilder::new()
.offer(OfferDecl::Directory(OfferDirectoryDecl {
source: OfferDirectorySource::Parent,
source_name: "bar_data".into(),
target_name: "baz_data".into(),
target: OfferTarget::Child("c".to_string()),
rights: Some(*rights::READ_RIGHTS),
subdir: None,
dependency_type: DependencyType::Strong,
}))
.offer(OfferDecl::Protocol(OfferProtocolDecl {
source: OfferServiceSource::Parent,
source_name: "bar_svc".into(),
target_name: "baz_svc".into(),
target: OfferTarget::Child("c".to_string()),
dependency_type: DependencyType::Strong,
}))
.add_lazy_child("c")
.build(),
),
(
"c",
ComponentDeclBuilder::new()
.use_(UseDecl::Directory(UseDirectoryDecl {
source: UseSource::Parent,
source_name: "baz_data".into(),
target_path: CapabilityPath::try_from("/data/hippo").unwrap(),
rights: *rights::READ_RIGHTS,
subdir: None,
}))
.use_(UseDecl::Protocol(UseProtocolDecl {
source: UseSource::Parent,
source_name: "baz_svc".into(),
target_path: CapabilityPath::try_from("/svc/hippo").unwrap(),
}))
.build(),
),
];
let test = RoutingTest::new("a", components).await;
test.check_use(vec!["b:0", "c:0"].into(), CheckUse::default_directory(ExpectedResult::Ok))
.await;
test.check_use(
vec!["b:0", "c:0"].into(),
CheckUse::Protocol { path: default_service_capability(), expected_res: ExpectedResult::Ok },
)
.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::Parent,
source_name: "builtin.Echo".into(),
target_name: "builtin.Echo".into(),
target: OfferTarget::Child("b".to_string()),
dependency_type: DependencyType::Strong,
}))
.add_lazy_child("b")
.build(),
),
(
"b",
ComponentDeclBuilder::new()
.offer(OfferDecl::Protocol(OfferProtocolDecl {
source: OfferServiceSource::Parent,
source_name: "builtin.Echo".into(),
target_name: "builtin.Echo".into(),
target: OfferTarget::Child("c".to_string()),
dependency_type: DependencyType::Strong,
}))
.add_lazy_child("c")
.build(),
),
(
"c",
ComponentDeclBuilder::new()
.use_(UseDecl::Protocol(UseProtocolDecl {
source: UseSource::Parent,
source_name: "builtin.Echo".into(),
target_path: CapabilityPath::try_from("/svc/hippo").unwrap(),
}))
.build(),
),
];
let test = RoutingTest::new("a", components).await;
test.check_use(
vec!["b:0", "c:0"].into(),
CheckUse::Protocol { path: default_service_capability(), expected_res: ExpectedResult::Ok },
)
.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").build()),
(
"b",
ComponentDeclBuilder::new()
.offer(OfferDecl::Directory(OfferDirectoryDecl {
source: OfferDirectorySource::Child("d".to_string()),
source_name: "bar_data".into(),
target_name: "foobar_data".into(),
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_name: "bar_svc".into(),
target_name: "foobar_svc".into(),
target: OfferTarget::Child("c".to_string()),
dependency_type: DependencyType::Strong,
}))
.add_lazy_child("c")
.add_lazy_child("d")
.build(),
),
(
"c",
ComponentDeclBuilder::new()
.use_(UseDecl::Directory(UseDirectoryDecl {
source: UseSource::Parent,
source_name: "foobar_data".into(),
target_path: CapabilityPath::try_from("/data/hippo").unwrap(),
rights: *rights::READ_RIGHTS,
subdir: None,
}))
.use_(UseDecl::Protocol(UseProtocolDecl {
source: UseSource::Parent,
source_name: "foobar_svc".into(),
target_path: CapabilityPath::try_from("/svc/hippo").unwrap(),
}))
.build(),
),
(
"d",
ComponentDeclBuilder::new()
.directory(DirectoryDeclBuilder::new("foo_data").build())
.protocol(ProtocolDeclBuilder::new("foo_svc").build())
.expose(ExposeDecl::Directory(ExposeDirectoryDecl {
source: ExposeSource::Self_,
source_name: "foo_data".into(),
target_name: "bar_data".into(),
target: ExposeTarget::Parent,
rights: Some(*rights::READ_RIGHTS),
subdir: None,
}))
.expose(ExposeDecl::Protocol(ExposeProtocolDecl {
source: ExposeSource::Self_,
source_name: "foo_svc".into(),
target_name: "bar_svc".into(),
target: ExposeTarget::Parent,
}))
.build(),
),
];
let test = RoutingTest::new("a", components).await;
test.check_use(vec!["b:0", "c:0"].into(), CheckUse::default_directory(ExpectedResult::Ok))
.await;
test.check_use(
vec!["b:0", "c:0"].into(),
CheckUse::Protocol { path: default_service_capability(), expected_res: ExpectedResult::Ok },
)
.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_name: "bar_data".into(),
target_name: "baz_data".into(),
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_name: "bar_svc".into(),
target_name: "baz_svc".into(),
target: OfferTarget::Child("c".to_string()),
dependency_type: DependencyType::Strong,
}))
.add_lazy_child("b")
.add_lazy_child("c")
.build(),
),
(
"b",
ComponentDeclBuilder::new()
.directory(DirectoryDeclBuilder::new("foo_data").build())
.protocol(ProtocolDeclBuilder::new("foo_svc").build())
.expose(ExposeDecl::Directory(ExposeDirectoryDecl {
source: ExposeSource::Self_,
source_name: "foo_data".into(),
target_name: "bar_data".into(),
target: ExposeTarget::Parent,
rights: Some(*rights::READ_RIGHTS),
subdir: None,
}))
.expose(ExposeDecl::Protocol(ExposeProtocolDecl {
source: ExposeSource::Self_,
source_name: "foo_svc".into(),
target_name: "bar_svc".into(),
target: ExposeTarget::Parent,
}))
.build(),
),
(
"c",
ComponentDeclBuilder::new()
.use_(UseDecl::Directory(UseDirectoryDecl {
source: UseSource::Parent,
source_name: "baz_data".into(),
target_path: CapabilityPath::try_from("/data/hippo").unwrap(),
rights: *rights::READ_RIGHTS,
subdir: None,
}))
.use_(UseDecl::Protocol(UseProtocolDecl {
source: UseSource::Parent,
source_name: "baz_svc".into(),
target_path: CapabilityPath::try_from("/svc/hippo").unwrap(),
}))
.build(),
),
];
let test = RoutingTest::new("a", components).await;
test.check_use(vec!["c:0"].into(), CheckUse::default_directory(ExpectedResult::Ok)).await;
test.check_use(
vec!["c:0"].into(),
CheckUse::Protocol { path: default_service_capability(), expected_res: ExpectedResult::Ok },
)
.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_name: "baz_data".into(),
target_name: "foobar_data".into(),
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_name: "baz_svc".into(),
target_name: "foobar_svc".into(),
target: OfferTarget::Child("c".to_string()),
dependency_type: DependencyType::Strong,
}))
.add_lazy_child("b")
.add_lazy_child("c")
.build(),
),
(
"b",
ComponentDeclBuilder::new()
.expose(ExposeDecl::Directory(ExposeDirectoryDecl {
source: ExposeSource::Child("d".to_string()),
source_name: "bar_data".into(),
target_name: "baz_data".into(),
target: ExposeTarget::Parent,
rights: Some(*rights::READ_RIGHTS),
subdir: None,
}))
.expose(ExposeDecl::Protocol(ExposeProtocolDecl {
source: ExposeSource::Child("d".to_string()),
source_name: "bar_svc".into(),
target_name: "baz_svc".into(),
target: ExposeTarget::Parent,
}))
.add_lazy_child("d")
.build(),
),
(
"c",
ComponentDeclBuilder::new()
.use_(UseDecl::Directory(UseDirectoryDecl {
source: UseSource::Parent,
source_name: "foobar_data".into(),
target_path: CapabilityPath::try_from("/data/hippo").unwrap(),
rights: *rights::READ_RIGHTS,
subdir: None,
}))
.use_(UseDecl::Protocol(UseProtocolDecl {
source: UseSource::Parent,
source_name: "foobar_svc".into(),
target_path: CapabilityPath::try_from("/svc/hippo").unwrap(),
}))
.build(),
),
(
"d",
ComponentDeclBuilder::new()
.directory(DirectoryDeclBuilder::new("foo_data").build())
.protocol(ProtocolDeclBuilder::new("foo_svc").build())
.expose(ExposeDecl::Directory(ExposeDirectoryDecl {
source: ExposeSource::Self_,
source_name: "foo_data".into(),
target_name: "bar_data".into(),
target: ExposeTarget::Parent,
rights: Some(*rights::READ_RIGHTS),
subdir: None,
}))
.expose(ExposeDecl::Protocol(ExposeProtocolDecl {
source: ExposeSource::Self_,
source_name: "foo_svc".into(),
target_name: "bar_svc".into(),
target: ExposeTarget::Parent,
}))
.build(),
),
];
let test = RoutingTest::new("a", components).await;
test.check_use(vec!["c:0"].into(), CheckUse::default_directory(ExpectedResult::Ok)).await;
test.check_use(
vec!["c:0"].into(),
CheckUse::Protocol { path: default_service_capability(), expected_res: ExpectedResult::Ok },
)
.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()
.protocol(ProtocolDeclBuilder::new("foo_svc").build())
.offer(OfferDecl::Protocol(OfferProtocolDecl {
source: OfferServiceSource::Self_,
source_name: "foo_svc".into(),
target_name: "foo_from_a_svc".into(),
target: OfferTarget::Child("b".to_string()),
dependency_type: DependencyType::Strong,
}))
.offer(OfferDecl::Directory(OfferDirectoryDecl {
source: OfferDirectorySource::Child("b".to_string()),
source_name: "foo_from_d_data".into(),
target_name: "foo_from_d_data".into(),
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")
.build(),
),
(
"b",
ComponentDeclBuilder::new_empty_component()
.offer(OfferDecl::Directory(OfferDirectoryDecl {
source: OfferDirectorySource::Child("d".to_string()),
source_name: "foo_from_d_data".into(),
target_name: "foo_from_d_data".into(),
target: OfferTarget::Child("e".to_string()),
rights: Some(*rights::READ_RIGHTS),
subdir: None,
dependency_type: DependencyType::Strong,
}))
.offer(OfferDecl::Protocol(OfferProtocolDecl {
source: OfferServiceSource::Parent,
source_name: "foo_from_a_svc".into(),
target_name: "foo_from_a_svc".into(),
target: OfferTarget::Child("e".to_string()),
dependency_type: DependencyType::Strong,
}))
.expose(ExposeDecl::Directory(ExposeDirectoryDecl {
source: ExposeSource::Child("d".to_string()),
source_name: "foo_from_d_data".into(),
target_name: "foo_from_d_data".into(),
target: ExposeTarget::Parent,
rights: Some(*rights::READ_RIGHTS),
subdir: None,
}))
.add_lazy_child("d")
.add_lazy_child("e")
.build(),
),
(
"c",
ComponentDeclBuilder::new_empty_component()
.offer(OfferDecl::Directory(OfferDirectoryDecl {
source: OfferDirectorySource::Parent,
source_name: "foo_from_d_data".into(),
target_name: "foo_from_d_data".into(),
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_name: "foo_from_h_svc".into(),
target_name: "foo_from_h_svc".into(),
target: OfferTarget::Child("f".to_string()),
dependency_type: DependencyType::Strong,
}))
.add_lazy_child("f")
.add_lazy_child("g")
.build(),
),
(
"d",
ComponentDeclBuilder::new()
.directory(DirectoryDeclBuilder::new("foo_data").build())
.expose(ExposeDecl::Directory(ExposeDirectoryDecl {
source: ExposeSource::Self_,
source_name: "foo_data".into(),
target_name: "foo_from_d_data".into(),
target: ExposeTarget::Parent,
rights: Some(*rights::READ_RIGHTS),
subdir: None,
}))
.build(),
),
(
"e",
ComponentDeclBuilder::new()
.use_(UseDecl::Directory(UseDirectoryDecl {
source: UseSource::Parent,
source_name: "foo_from_d_data".into(),
target_path: CapabilityPath::try_from("/data/hippo").unwrap(),
rights: *rights::READ_RIGHTS,
subdir: None,
}))
.use_(UseDecl::Protocol(UseProtocolDecl {
source: UseSource::Parent,
source_name: "foo_from_a_svc".into(),
target_path: CapabilityPath::try_from("/svc/hippo").unwrap(),
}))
.build(),
),
(
"f",
ComponentDeclBuilder::new()
.use_(UseDecl::Directory(UseDirectoryDecl {
source: UseSource::Parent,
source_name: "foo_from_d_data".into(),
target_path: CapabilityPath::try_from("/data/hippo").unwrap(),
rights: *rights::READ_RIGHTS,
subdir: None,
}))
.use_(UseDecl::Protocol(UseProtocolDecl {
source: UseSource::Parent,
source_name: "foo_from_h_svc".into(),
target_path: CapabilityPath::try_from("/svc/hippo").unwrap(),
}))
.build(),
),
(
"g",
ComponentDeclBuilder::new_empty_component()
.expose(ExposeDecl::Protocol(ExposeProtocolDecl {
source: ExposeSource::Child("h".to_string()),
source_name: "foo_from_h_svc".into(),
target_name: "foo_from_h_svc".into(),
target: ExposeTarget::Parent,
}))
.add_lazy_child("h")
.build(),
),
(
"h",
ComponentDeclBuilder::new()
.protocol(ProtocolDeclBuilder::new("foo_svc").build())
.expose(ExposeDecl::Protocol(ExposeProtocolDecl {
source: ExposeSource::Self_,
source_name: "foo_svc".into(),
target_name: "foo_from_h_svc".into(),
target: ExposeTarget::Parent,
}))
.build(),
),
];
let test = RoutingTest::new("a", components).await;
test.check_use(vec!["b:0", "e:0"].into(), CheckUse::default_directory(ExpectedResult::Ok))
.await;
test.check_use(
vec!["b:0", "e:0"].into(),
CheckUse::Protocol { path: default_service_capability(), expected_res: ExpectedResult::Ok },
)
.await;
test.check_use(vec!["c:0", "f:0"].into(), CheckUse::default_directory(ExpectedResult::Ok))
.await;
test.check_use(
vec!["c:0", "f:0"].into(),
CheckUse::Protocol { path: default_service_capability(), expected_res: ExpectedResult::Ok },
)
.await;
}
/// component manager's namespace
/// |
/// a
///
/// a: uses directory /use_from_cm_namespace/data/foo as foo_data
/// a: uses service /use_from_cm_namespace/svc/foo as foo_svc
#[fuchsia_async::run_singlethreaded(test)]
async fn use_from_component_manager_namespace() {
let components = vec![(
"a",
ComponentDeclBuilder::new()
.use_(UseDecl::Directory(UseDirectoryDecl {
source: UseSource::Parent,
source_name: "foo_data".into(),
target_path: CapabilityPath::try_from("/data/hippo").unwrap(),
rights: *rights::READ_RIGHTS,
subdir: None,
}))
.use_(UseDecl::Protocol(UseProtocolDecl {
source: UseSource::Parent,
source_name: "foo_svc".into(),
target_path: CapabilityPath::try_from("/svc/hippo").unwrap(),
}))
.build(),
)];
let namespace_capabilities = vec![
CapabilityDecl::Directory(
DirectoryDeclBuilder::new("foo_data").path("/use_from_cm_namespace/data/foo").build(),
),
CapabilityDecl::Protocol(
ProtocolDeclBuilder::new("foo_svc").path("/use_from_cm_namespace/svc/foo").build(),
),
];
let test = RoutingTestBuilder::new("a", components)
.set_namespace_capabilities(namespace_capabilities)
.build()
.await;
let _ns_dir = ScopedNamespaceDir::new(&test, "/use_from_cm_namespace");
test.check_use(vec![].into(), CheckUse::default_directory(ExpectedResult::Ok)).await;
test.check_use(
vec![].into(),
CheckUse::Protocol { path: default_service_capability(), expected_res: ExpectedResult::Ok },
)
.await;
}
/// component manager's namespace
/// |
/// a
/// \
/// b
///
/// a: offers directory /offer_from_cm_namespace/data/foo from realm as bar_data
/// a: offers service /offer_from_cm_namespace/svc/foo from realm as bar_svc
/// b: uses directory bar_data as /data/hippo
/// b: uses service bar_svc 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::Parent,
source_name: "foo_data".into(),
target_name: "bar_data".into(),
target: OfferTarget::Child("b".to_string()),
rights: Some(*rights::READ_RIGHTS),
subdir: None,
dependency_type: DependencyType::Strong,
}))
.offer(OfferDecl::Protocol(OfferProtocolDecl {
source: OfferServiceSource::Parent,
source_name: "foo_svc".into(),
target_name: "bar_svc".into(),
target: OfferTarget::Child("b".to_string()),
dependency_type: DependencyType::Strong,
}))
.add_lazy_child("b")
.build(),
),
(
"b",
ComponentDeclBuilder::new()
.use_(UseDecl::Directory(UseDirectoryDecl {
source: UseSource::Parent,
source_name: "bar_data".into(),
target_path: CapabilityPath::try_from("/data/hippo").unwrap(),
rights: *rights::READ_RIGHTS,
subdir: None,
}))
.use_(UseDecl::Protocol(UseProtocolDecl {
source: UseSource::Parent,
source_name: "bar_svc".into(),
target_path: CapabilityPath::try_from("/svc/hippo").unwrap(),
}))
.build(),
),
];
let namespace_capabilities = vec![
CapabilityDecl::Directory(
DirectoryDeclBuilder::new("foo_data").path("/offer_from_cm_namespace/data/foo").build(),
),
CapabilityDecl::Protocol(
ProtocolDeclBuilder::new("foo_svc").path("/offer_from_cm_namespace/svc/foo").build(),
),
];
let test = RoutingTestBuilder::new("a", components)
.set_namespace_capabilities(namespace_capabilities)
.build()
.await;
let _ns_dir = ScopedNamespaceDir::new(&test, "/offer_from_cm_namespace");
test.check_use(vec!["b:0"].into(), CheckUse::default_directory(ExpectedResult::Ok)).await;
test.check_use(
vec!["b:0"].into(),
CheckUse::Protocol { path: default_service_capability(), expected_res: ExpectedResult::Ok },
)
.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").build()),
(
"b",
ComponentDeclBuilder::new()
.use_(UseDecl::Directory(UseDirectoryDecl {
source: UseSource::Parent,
source_name: "hippo_data".into(),
target_path: CapabilityPath::try_from("/data/hippo").unwrap(),
rights: *rights::READ_RIGHTS,
subdir: None,
}))
.use_(UseDecl::Protocol(UseProtocolDecl {
source: UseSource::Parent,
source_name: "hippo_svc".into(),
target_path: CapabilityPath::try_from("/svc/hippo").unwrap(),
}))
.build(),
),
];
let test = RoutingTest::new("a", components).await;
test.check_use(
vec!["b:0"].into(),
CheckUse::default_directory(ExpectedResult::Err(zx::Status::UNAVAILABLE)),
)
.await;
test.check_use(
vec!["b:0"].into(),
CheckUse::Protocol {
path: default_service_capability(),
expected_res: ExpectedResult::Err(zx::Status::UNAVAILABLE),
},
)
.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_name: "hippo_data".into(),
source: OfferDirectorySource::Child("b".to_string()),
target_name: "hippo_data".into(),
target: OfferTarget::Child("c".to_string()),
rights: Some(*rights::READ_RIGHTS),
subdir: None,
dependency_type: DependencyType::Strong,
}))
.offer(OfferDecl::Protocol(OfferProtocolDecl {
source_name: "hippo_svc".into(),
source: OfferServiceSource::Child("b".to_string()),
target_name: "hippo_svc".into(),
target: OfferTarget::Child("c".to_string()),
dependency_type: DependencyType::Strong,
}))
.add_lazy_child("b")
.add_lazy_child("c")
.build(),
),
("b", component_decl_with_test_runner()),
(
"c",
ComponentDeclBuilder::new()
.use_(UseDecl::Directory(UseDirectoryDecl {
source: UseSource::Parent,
source_name: "hippo_data".into(),
target_path: CapabilityPath::try_from("/data/hippo").unwrap(),
rights: *rights::READ_RIGHTS,
subdir: None,
}))
.use_(UseDecl::Protocol(UseProtocolDecl {
source: UseSource::Parent,
source_name: "hippo_svc".into(),
target_path: CapabilityPath::try_from("/svc/hippo").unwrap(),
}))
.build(),
),
];
let test = RoutingTest::new("a", components).await;
test.check_use(
vec!["c:0"].into(),
CheckUse::default_directory(ExpectedResult::Err(zx::Status::UNAVAILABLE)),
)
.await;
test.check_use(
vec!["c:0"].into(),
CheckUse::Protocol {
path: default_service_capability(),
expected_res: ExpectedResult::Err(zx::Status::UNAVAILABLE),
},
)
.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").build()),
(
"b",
ComponentDeclBuilder::new_empty_component()
.offer(OfferDecl::Directory(OfferDirectoryDecl {
source_name: "hippo_data".into(),
source: OfferDirectorySource::Parent,
target_name: "hippo_data".into(),
target: OfferTarget::Child("c".to_string()),
rights: Some(*rights::READ_RIGHTS),
subdir: None,
dependency_type: DependencyType::Strong,
}))
.offer(OfferDecl::Protocol(OfferProtocolDecl {
source_name: "hippo_svc".into(),
source: OfferServiceSource::Parent,
target_name: "hippo_svc".into(),
target: OfferTarget::Child("c".to_string()),
dependency_type: DependencyType::Strong,
}))
.add_lazy_child("c")
.build(),
),
(
"c",
ComponentDeclBuilder::new()
.use_(UseDecl::Directory(UseDirectoryDecl {
source: UseSource::Parent,
source_name: "hippo_data".into(),
target_path: CapabilityPath::try_from("/data/hippo").unwrap(),
rights: *rights::READ_RIGHTS,
subdir: None,
}))
.use_(UseDecl::Protocol(UseProtocolDecl {
source: UseSource::Parent,
source_name: "hippo_svc".into(),
target_path: CapabilityPath::try_from("/svc/hippo").unwrap(),
}))
.build(),
),
];
let test = RoutingTest::new("a", components).await;
test.check_use(
vec!["b:0", "c:0"].into(),
CheckUse::default_directory(ExpectedResult::Err(zx::Status::UNAVAILABLE)),
)
.await;
test.check_use(
vec!["b:0", "c:0"].into(),
CheckUse::Protocol {
path: default_service_capability(),
expected_res: ExpectedResult::Err(zx::Status::UNAVAILABLE),
},
)
.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").build()),
(
"b",
ComponentDeclBuilder::new()
.use_(UseDecl::Directory(UseDirectoryDecl {
source: UseSource::Parent,
source_name: "hippo_data".into(),
target_path: CapabilityPath::try_from("/data/hippo").unwrap(),
rights: *rights::READ_RIGHTS,
subdir: None,
}))
.use_(UseDecl::Protocol(UseProtocolDecl {
source: UseSource::Parent,
source_name: "hippo_svc".into(),
target_path: CapabilityPath::try_from("/svc/hippo").unwrap(),
}))
.add_lazy_child("c")
.build(),
),
(
"c",
ComponentDeclBuilder::new()
.directory(DirectoryDeclBuilder::new("hippo_data").build())
.protocol(ProtocolDeclBuilder::new("hippo_svc").build())
.expose(ExposeDecl::Directory(ExposeDirectoryDecl {
source_name: "hippo_data".into(),
source: ExposeSource::Self_,
target_name: "hippo_data".into(),
target: ExposeTarget::Parent,
rights: Some(*rights::READ_RIGHTS),
subdir: None,
}))
.expose(ExposeDecl::Protocol(ExposeProtocolDecl {
source_name: "hippo_svc".into(),
source: ExposeSource::Self_,
target_name: "hippo_svc".into(),
target: ExposeTarget::Parent,
}))
.build(),
),
];
let test = RoutingTest::new("a", components).await;
test.check_use(
vec!["b:0"].into(),
CheckUse::default_directory(ExpectedResult::Err(zx::Status::UNAVAILABLE)),
)
.await;
test.check_use(
vec!["b:0"].into(),
CheckUse::Protocol {
path: default_service_capability(),
expected_res: ExpectedResult::Err(zx::Status::UNAVAILABLE),
},
)
.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_name: "bar_data".into(),
target_name: "baz_data".into(),
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_name: "bar_svc".into(),
target_name: "baz_svc".into(),
target: OfferTarget::Child("c".to_string()),
dependency_type: DependencyType::Strong,
}))
.add_lazy_child("b")
.add_lazy_child("c")
.build(),
),
(
"b",
ComponentDeclBuilder::new()
.directory(DirectoryDeclBuilder::new("foo_data").build())
.protocol(ProtocolDeclBuilder::new("foo_svc").build())
.expose(ExposeDecl::Directory(ExposeDirectoryDecl {
source: ExposeSource::Self_,
source_name: "foo_data".into(),
target_name: "bar_data".into(),
target: ExposeTarget::Framework,
rights: Some(*rights::READ_RIGHTS),
subdir: None,
}))
.expose(ExposeDecl::Protocol(ExposeProtocolDecl {
source: ExposeSource::Self_,
source_name: "foo_svc".into(),
target_name: "bar_svc".into(),
target: ExposeTarget::Framework,
}))
.build(),
),
(
"c",
ComponentDeclBuilder::new()
.use_(UseDecl::Directory(UseDirectoryDecl {
source: UseSource::Parent,
source_name: "baz_data".into(),
target_path: CapabilityPath::try_from("/data/hippo").unwrap(),
rights: *rights::READ_RIGHTS,
subdir: None,
}))
.use_(UseDecl::Protocol(UseProtocolDecl {
source: UseSource::Parent,
source_name: "baz_svc".into(),
target_path: CapabilityPath::try_from("/svc/hippo").unwrap(),
}))
.build(),
),
];
let test = RoutingTest::new("a", components).await;
test.check_use(
vec!["c:0"].into(),
CheckUse::default_directory(ExpectedResult::Err(zx::Status::UNAVAILABLE)),
)
.await;
test.check_use(
vec!["c:0"].into(),
CheckUse::Protocol {
path: default_service_capability(),
expected_res: ExpectedResult::Err(zx::Status::UNAVAILABLE),
},
)
.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()
.directory(DirectoryDeclBuilder::new("hippo_data").build())
.protocol(ProtocolDeclBuilder::new("hippo_svc").build())
.offer(OfferDecl::Directory(OfferDirectoryDecl {
source_name: "hippo_data".into(),
source: OfferDirectorySource::Self_,
target_name: "hippo_data".into(),
target: OfferTarget::Child("b".to_string()),
rights: Some(*rights::READ_RIGHTS),
subdir: None,
dependency_type: DependencyType::Strong,
}))
.offer(OfferDecl::Protocol(OfferProtocolDecl {
source_name: "hippo_svc".into(),
source: OfferServiceSource::Self_,
target_name: "hippo_svc".into(),
target: OfferTarget::Child("b".to_string()),
dependency_type: DependencyType::Strong,
}))
.add_lazy_child("b")
.build(),
),
(
"b",
ComponentDeclBuilder::new()
.use_(UseDecl::Directory(UseDirectoryDecl {
source: UseSource::Parent,
source_name: "hippo_data".into(),
target_path: CapabilityPath::try_from("/data/hippo").unwrap(),
rights: *rights::READ_RIGHTS,
subdir: None,
}))
.use_(UseDecl::Protocol(UseProtocolDecl {
source: UseSource::Parent,
source_name: "hippo_svc".into(),
target_path: CapabilityPath::try_from("/svc/hippo").unwrap(),
}))
.build(),
),
];
let test = RoutingTest::new("a", components).await;
test.check_use(
vec!["b:0"].into(),
CheckUse::default_directory(ExpectedResult::Err(zx::Status::UNAVAILABLE)),
)
.await;
test.check_use(
vec!["b:0"].into(),
CheckUse::Protocol {
path: default_service_capability(),
expected_res: ExpectedResult::Err(zx::Status::UNAVAILABLE),
},
)
.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()
.directory(DirectoryDeclBuilder::new("foo_data").build())
.protocol(ProtocolDeclBuilder::new("foo_svc").build())
.offer(OfferDecl::Directory(OfferDirectoryDecl {
source_name: "foo_data".into(),
source: OfferDirectorySource::Self_,
target_name: "hippo_data".into(),
target: OfferTarget::Child("b".to_string()),
rights: Some(*rights::READ_RIGHTS),
subdir: None,
dependency_type: DependencyType::Strong,
}))
.offer(OfferDecl::Protocol(OfferProtocolDecl {
source_name: "foo_svc".into(),
source: OfferServiceSource::Self_,
target_name: "hippo_svc".into(),
target: OfferTarget::Child("b".to_string()),
dependency_type: DependencyType::Strong,
}))
.add_lazy_child("b")
.build(),
),
(
"b",
ComponentDeclBuilder::new()
.use_(UseDecl::Protocol(UseProtocolDecl {
source: UseSource::Framework,
source_name: "fuchsia.sys2.Realm".into(),
target_path: CapabilityPath::try_from("/svc/fuchsia.sys2.Realm").unwrap(),
}))
.offer(OfferDecl::Directory(OfferDirectoryDecl {
source_name: "hippo_data".into(),
source: OfferDirectorySource::Parent,
target_name: "hippo_data".into(),
target: OfferTarget::Collection("coll".to_string()),
rights: Some(*rights::READ_RIGHTS),
subdir: None,
dependency_type: DependencyType::Strong,
}))
.offer(OfferDecl::Protocol(OfferProtocolDecl {
source_name: "hippo_svc".into(),
source: OfferServiceSource::Parent,
target_name: "hippo_svc".into(),
target: OfferTarget::Collection("coll".to_string()),
dependency_type: DependencyType::Strong,
}))
.add_transient_collection("coll")
.build(),
),
(
"c",
ComponentDeclBuilder::new()
.use_(UseDecl::Directory(UseDirectoryDecl {
source: UseSource::Parent,
source_name: "hippo_data".into(),
target_path: CapabilityPath::try_from("/data/hippo").unwrap(),
rights: *rights::READ_RIGHTS,
subdir: None,
}))
.build(),
),
(
"d",
ComponentDeclBuilder::new()
.use_(UseDecl::Protocol(UseProtocolDecl {
source: UseSource::Parent,
source_name: "hippo_svc".into(),
target_path: CapabilityPath::try_from("/svc/hippo").unwrap(),
}))
.build(),
),
];
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(ExpectedResult::Ok))
.await;
test.check_use(
vec!["b:0", "coll:d:2"].into(),
CheckUse::Protocol { path: default_service_capability(), expected_res: ExpectedResult::Ok },
)
.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()
.directory(DirectoryDeclBuilder::new("foo_data").build())
.protocol(ProtocolDeclBuilder::new("foo_svc").build())
.offer(OfferDecl::Directory(OfferDirectoryDecl {
source_name: "foo_data".into(),
source: OfferDirectorySource::Self_,
target_name: "hippo_data".into(),
target: OfferTarget::Child("b".to_string()),
rights: Some(*rights::READ_RIGHTS),
subdir: None,
dependency_type: DependencyType::Strong,
}))
.offer(OfferDecl::Protocol(OfferProtocolDecl {
source_name: "foo_svc".into(),
source: OfferServiceSource::Self_,
target_name: "hippo_svc".into(),
target: OfferTarget::Child("b".to_string()),
dependency_type: DependencyType::Strong,
}))
.add_lazy_child("b")
.build(),
),
(
"b",
ComponentDeclBuilder::new()
.use_(UseDecl::Protocol(UseProtocolDecl {
source: UseSource::Framework,
source_name: "fuchsia.sys2.Realm".into(),
target_path: CapabilityPath::try_from("/svc/fuchsia.sys2.Realm").unwrap(),
}))
.add_transient_collection("coll")
.build(),
),
(
"c",
ComponentDeclBuilder::new()
.use_(UseDecl::Directory(UseDirectoryDecl {
source: UseSource::Parent,
source_name: "hippo_data".into(),
target_path: CapabilityPath::try_from("/data/hippo").unwrap(),
rights: *rights::READ_RIGHTS,
subdir: None,
}))
.use_(UseDecl::Protocol(UseProtocolDecl {
source: UseSource::Parent,
source_name: "hippo_svc".into(),
target_path: CapabilityPath::try_from("/svc/hippo").unwrap(),
}))
.build(),
),
];
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(ExpectedResult::Err(zx::Status::UNAVAILABLE)),
)
.await;
test.check_use(
vec!["b:0", "coll:c:1"].into(),
CheckUse::Protocol {
path: default_service_capability(),
expected_res: ExpectedResult::Err(zx::Status::UNAVAILABLE),
},
)
.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()
.directory(DirectoryDeclBuilder::new("foo_data").build())
.protocol(ProtocolDeclBuilder::new("foo_svc").build())
.offer(OfferDecl::Directory(OfferDirectoryDecl {
source: OfferDirectorySource::Self_,
source_name: "foo_data".into(),
target_name: "foo_data".into(),
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")
.build(),
),
(
"b",
ComponentDeclBuilder::new()
.offer(OfferDecl::Directory(OfferDirectoryDecl {
source: OfferDirectorySource::Parent,
source_name: "foo_data".into(),
target_name: "foo_data".into(),
target: OfferTarget::Child("c".to_string()),
rights: Some(*rights::READ_RIGHTS),
subdir: Some(PathBuf::from("s3")),
dependency_type: DependencyType::Strong,
}))
.add_lazy_child("c")
.build(),
),
(
"c",
ComponentDeclBuilder::new()
.use_(UseDecl::Directory(UseDirectoryDecl {
source: UseSource::Parent,
source_name: "foo_data".into(),
target_path: CapabilityPath::try_from("/data/hippo").unwrap(),
rights: *rights::READ_RIGHTS,
subdir: Some(PathBuf::from("s4")),
}))
.build(),
),
];
let test = RoutingTest::new("a", components).await;
test.create_static_file(Path::new("foo/s1/s2/s3/s4/inner"), "hello")
.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"),
expected_res: ExpectedResult::Ok,
},
)
.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_name: "foo_data".into(),
target: OfferTarget::Child("c".to_string()),
target_name: "foo_data".into(),
rights: Some(*rights::READ_RIGHTS),
subdir: Some(PathBuf::from("s3")),
dependency_type: DependencyType::Strong,
}))
.add_lazy_child("b")
.add_lazy_child("c")
.build(),
),
(
"b",
ComponentDeclBuilder::new()
.directory(DirectoryDeclBuilder::new("foo_data").build())
.expose(ExposeDecl::Directory(ExposeDirectoryDecl {
source: ExposeSource::Self_,
source_name: "foo_data".into(),
target: ExposeTarget::Parent,
target_name: "foo_data".into(),
rights: Some(*rights::READ_RIGHTS),
subdir: Some(PathBuf::from("s1/s2")),
}))
.build(),
),
(
"c",
ComponentDeclBuilder::new()
.use_(UseDecl::Directory(UseDirectoryDecl {
source: UseSource::Parent,
source_name: "foo_data".into(),
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"), "hello")
.await
.expect("failed to create file");
test.check_use(
vec!["c:0"].into(),
CheckUse::Directory {
path: default_directory_capability(),
file: PathBuf::from("inner"),
expected_res: ExpectedResult::Ok,
},
)
.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_name: "foo_data".into(),
target_name: "hippo_data".into(),
target: ExposeTarget::Parent,
rights: None,
subdir: Some(PathBuf::from("s3")),
}))
.add_lazy_child("b")
.build(),
),
(
"b",
ComponentDeclBuilder::new()
.expose(ExposeDecl::Directory(ExposeDirectoryDecl {
source: ExposeSource::Child("c".to_string()),
source_name: "foo_data".into(),
target_name: "foo_data".into(),
target: ExposeTarget::Parent,
rights: None,
subdir: Some(PathBuf::from("s1/s2")),
}))
.add_lazy_child("c")
.build(),
),
(
"c",
ComponentDeclBuilder::new()
.directory(DirectoryDeclBuilder::new("foo_data").build())
.expose(ExposeDecl::Directory(ExposeDirectoryDecl {
source: ExposeSource::Self_,
source_name: "foo_data".into(),
target_name: "foo_data".into(),
target: ExposeTarget::Parent,
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"), "hello")
.await
.expect("failed to create file");
test.check_use_exposed_dir(
vec![].into(),
CheckUse::Directory {
path: "/hippo_data".try_into().unwrap(),
file: PathBuf::from("inner"),
expected_res: ExpectedResult::Ok,
},
)
.await;
}
/// a
/// \
/// b
///
/// a: declares runner "elf" with service "/svc/runner" from "self".
/// a: registers runner "elf" from self in environment as "hobbit".
/// b: uses runner "hobbit".
#[fuchsia_async::run_singlethreaded(test)]
async fn use_runner_from_parent_environment() {
let components = vec![
(
"a",
ComponentDeclBuilder::new()
.add_child(ChildDeclBuilder::new_lazy_child("b").environment("env").build())
.add_environment(
EnvironmentDeclBuilder::new()
.name("env")
.extends(fsys::EnvironmentExtends::Realm)
.add_runner(RunnerRegistration {
source_name: "elf".into(),
source: RegistrationSource::Self_,
target_name: "hobbit".into(),
})
.build(),
)
.runner(RunnerDecl {
name: "elf".into(),
source: RunnerSource::Self_,
source_path: CapabilityPath::try_from("/svc/runner").unwrap(),
})
.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 "b" exposes a runner service.
.add_outgoing_path("a", 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())
);
}
);
}
/// a
/// \
/// [b]
///
/// a: declares runner "elf" with service "/svc/runner" from "self".
/// a: registers runner "elf" from self in environment as "hobbit".
/// b: instance in collection uses runner "hobbit".
#[fuchsia_async::run_singlethreaded(test)]
async fn use_runner_from_environment_in_collection() {
let components = vec![
(
"a",
ComponentDeclBuilder::new()
.add_collection(
CollectionDeclBuilder::new_transient_collection("coll")
.environment("env")
.build(),
)
.add_environment(
EnvironmentDeclBuilder::new()
.name("env")
.extends(fsys::EnvironmentExtends::Realm)
.add_runner(RunnerRegistration {
source_name: "elf".into(),
source: RegistrationSource::Self_,
target_name: "hobbit".into(),
})
.build(),
)
.use_(UseDecl::Protocol(UseProtocolDecl {
source: UseSource::Framework,
source_name: "fuchsia.sys2.Realm".into(),
target_path: CapabilityPath::try_from("/svc/fuchsia.sys2.Realm").unwrap(),
}))
.runner(RunnerDecl {
name: "elf".into(),
source: RunnerSource::Self_,
source_path: CapabilityPath::try_from("/svc/runner").unwrap(),
})
.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 "a" exposes a runner service.
.add_outgoing_path("a", CapabilityPath::try_from("/svc/runner").unwrap(), runner_service)
.build()
.await;
universe
.create_dynamic_child(
AbsoluteMoniker::root(),
"coll",
ChildDecl {
name: "b".to_string(),
url: "test:///b".to_string(),
startup: fsys::StartupMode::Lazy,
environment: None,
},
)
.await;
join!(
// Bind "coll:b:1". We expect to see a call to our runner service for the new component.
async move {
universe.bind_instance(&vec!["coll:b:1"].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())
);
}
);
}
/// a
/// \
/// b
/// \
/// c
///
/// a: declares runner "elf" as service "/svc/runner" from self.
/// a: offers runner "elf" from self to "b" as "dwarf".
/// b: registers runner "dwarf" from realm in environment as "hobbit".
/// c: uses runner "hobbit".
#[fuchsia_async::run_singlethreaded(test)]
async fn use_runner_from_grandparent_environment() {
let components = vec![
(
"a",
ComponentDeclBuilder::new()
.add_lazy_child("b")
.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()),
}))
.runner(RunnerDecl {
name: "elf".into(),
source: RunnerSource::Self_,
source_path: CapabilityPath::try_from("/svc/runner").unwrap(),
})
.build(),
),
(
"b",
ComponentDeclBuilder::new()
.add_child(ChildDeclBuilder::new_lazy_child("c").environment("env").build())
.add_environment(
EnvironmentDeclBuilder::new()
.name("env")
.extends(fsys::EnvironmentExtends::Realm)
.add_runner(RunnerRegistration {
source_name: "dwarf".into(),
source: RegistrationSource::Parent,
target_name: "hobbit".into(),
})
.build(),
)
.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
/// / \
/// b c
///
/// a: registers runner "dwarf" from "b" in environment as "hobbit".
/// b: exposes runner "elf" as service "/svc/runner" from self as "dwarf".
/// c: uses runner "hobbit".
#[fuchsia_async::run_singlethreaded(test)]
async fn use_runner_from_sibling_environment() {
let components = vec![
(
"a",
ComponentDeclBuilder::new()
.add_lazy_child("b")
.add_child(ChildDeclBuilder::new_lazy_child("c").environment("env").build())
.add_environment(
EnvironmentDeclBuilder::new()
.name("env")
.extends(fsys::EnvironmentExtends::Realm)
.add_runner(RunnerRegistration {
source_name: "dwarf".into(),
source: RegistrationSource::Child("b".into()),
target_name: "hobbit".into(),
})
.build(),
)
.build(),
),
(
"b",
ComponentDeclBuilder::new()
.expose(ExposeDecl::Runner(ExposeRunnerDecl {
source: ExposeSource::Self_,
source_name: CapabilityName("elf".to_string()),
target: ExposeTarget::Parent,
target_name: CapabilityName("dwarf".to_string()),
}))
.runner(RunnerDecl {
name: "elf".into(),
source: RunnerSource::Self_,
source_path: CapabilityPath::try_from("/svc/runner").unwrap(),
})
.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("b", 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!["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
/// \
/// b
/// \
/// c
///
/// a: declares runner "elf" as service "/svc/runner" from self.
/// a: registers runner "elf" from realm in environment as "hobbit".
/// b: creates environment extending from realm.
/// c: uses runner "hobbit".
#[fuchsia_async::run_singlethreaded(test)]
async fn use_runner_from_inherited_environment() {
let components = vec![
(
"a",
ComponentDeclBuilder::new()
.add_child(ChildDeclBuilder::new_lazy_child("b").environment("env").build())
.add_environment(
EnvironmentDeclBuilder::new()
.name("env")
.extends(fsys::EnvironmentExtends::Realm)
.add_runner(RunnerRegistration {
source_name: "elf".into(),
source: RegistrationSource::Self_,
target_name: "hobbit".into(),
})
.build(),
)
.runner(RunnerDecl {
name: "elf".into(),
source: RunnerSource::Self_,
source_path: CapabilityPath::try_from("/svc/runner").unwrap(),
})
.build(),
),
(
"b",
ComponentDeclBuilder::new()
.add_child(ChildDeclBuilder::new_lazy_child("c").environment("env").build())
.add_environment(
EnvironmentDeclBuilder::new()
.name("env")
.extends(fsys::EnvironmentExtends::Realm)
.build(),
)
.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
/// \
/// b
///
/// a: declares runner "elf" with service "/svc/runner" from "self".
/// a: registers runner "elf" from self in environment as "hobbit".
/// b: uses runner "hobbit". Fails because "hobbit" was not in environment.
#[fuchsia_async::run_singlethreaded(test)]
async fn use_runner_from_environment_not_found() {
let components = vec![
(
"a",
ComponentDeclBuilder::new()
.add_child(ChildDeclBuilder::new_lazy_child("b").environment("env").build())
.add_environment(
EnvironmentDeclBuilder::new()
.name("env")
.extends(fsys::EnvironmentExtends::Realm)
.add_runner(RunnerRegistration {
source_name: "elf".into(),
source: RegistrationSource::Self_,
target_name: "dwarf".into(),
})
.build(),
)
.runner(RunnerDecl {
name: "elf".into(),
source: RunnerSource::Self_,
source_path: CapabilityPath::try_from("/svc/runner").unwrap(),
})
.build(),
),
("b", ComponentDeclBuilder::new_empty_component().use_runner("hobbit").build()),
];
// Set up the system.
let (runner_service, _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;
// Bind "b:0". We expect it to fail because routing failed.
//
// The error we get is UseFromRealmNotFound we fall back to searching for an `offer`
// declaration in the parent. Once we remove the fallback, this could be a UseFromEnvironment
// error.
assert_matches!(
universe.bind_instance(&vec!["b:0"].into()).await,
Err(ModelError::RoutingError {
err: RoutingError::UseFromEnvironmentNotFound {
moniker,
capability_type,
capability_id,
}
})
if moniker == AbsoluteMoniker::from(vec!["b:0"]) &&
capability_type == "runner".to_string() &&
capability_id == "hobbit".to_string());
}
// TODO: Write a test for environment that extends from None. Currently, this is not
// straightforward because resolver routing is not implemented yet, which makes it impossible to
// register a new resolver and have it be usable.
#[fuchsia_async::run_singlethreaded(test)]
async fn expose_from_self_and_child() {
let components = vec![
("a", ComponentDeclBuilder::new().add_lazy_child("b").build()),
(
"b",
ComponentDeclBuilder::new()
.expose(ExposeDecl::Directory(ExposeDirectoryDecl {
source: ExposeSource::Child("c".to_string()),
source_name: "hippo_data".into(),
target_name: "hippo_bar_data".into(),
target: ExposeTarget::Parent,
rights: Some(*rights::READ_RIGHTS),
subdir: None,
}))
.expose(ExposeDecl::Protocol(ExposeProtocolDecl {
source: ExposeSource::Child("c".to_string()),
source_name: "hippo_svc".into(),
target_name: "hippo_bar_svc".into(),
target: ExposeTarget::Parent,
}))
.add_lazy_child("c")
.build(),
),
(
"c",
ComponentDeclBuilder::new()
.directory(DirectoryDeclBuilder::new("foo_data").build())
.protocol(ProtocolDeclBuilder::new("foo_svc").build())
.expose(ExposeDecl::Directory(ExposeDirectoryDecl {
source: ExposeSource::Self_,
source_name: "foo_data".into(),
target_name: "hippo_data".into(),
target: ExposeTarget::Parent,
rights: Some(*rights::READ_RIGHTS),
subdir: None,
}))
.expose(ExposeDecl::Protocol(ExposeProtocolDecl {
source: ExposeSource::Self_,
source_name: "foo_svc".into(),
target_name: "hippo_svc".into(),
target: ExposeTarget::Parent,
}))
.build(),
),
];
let test = RoutingTest::new("a", components).await;
test.check_use_exposed_dir(
vec!["b:0"].into(),
CheckUse::Directory {
path: "/hippo_bar_data".try_into().unwrap(),
file: PathBuf::from("hippo"),
expected_res: ExpectedResult::Ok,
},
)
.await;
test.check_use_exposed_dir(
vec!["b:0"].into(),
CheckUse::Protocol {
path: "/hippo_bar_svc".try_into().unwrap(),
expected_res: ExpectedResult::Ok,
},
)
.await;
test.check_use_exposed_dir(
vec!["b:0", "c:0"].into(),
CheckUse::Directory {
path: "/hippo_data".try_into().unwrap(),
file: PathBuf::from("hippo"),
expected_res: ExpectedResult::Ok,
},
)
.await;
test.check_use_exposed_dir(
vec!["b:0", "c:0"].into(),
CheckUse::Protocol {
path: "/hippo_svc".try_into().unwrap(),
expected_res: ExpectedResult::Ok,
},
)
.await;
}
#[fuchsia_async::run_singlethreaded(test)]
async fn use_not_exposed() {
let components = vec![
("a", ComponentDeclBuilder::new().add_lazy_child("b").build()),
("b", ComponentDeclBuilder::new().add_lazy_child("c").build()),
(
"c",
ComponentDeclBuilder::new()
.directory(DirectoryDeclBuilder::new("foo_data").build())
.protocol(ProtocolDeclBuilder::new("foo_svc").build())
.expose(ExposeDecl::Directory(ExposeDirectoryDecl {
source: ExposeSource::Self_,
source_name: "foo_data".into(),
target_name: "hippo_data".into(),
target: ExposeTarget::Parent,
rights: Some(*rights::READ_RIGHTS),
subdir: None,
}))
.expose(ExposeDecl::Protocol(ExposeProtocolDecl {
source: ExposeSource::Self_,
source_name: "foo_svc".into(),
target_name: "hippo_svc".into(),
target: ExposeTarget::Parent,
}))
.build(),
),
];
let test = RoutingTest::new("a", components).await;
// Capability is only exposed from "c", so it only be usable from there.
// When trying to open a capability that's not exposed to realm, there's no node for it in the
// exposed dir, so no routing takes place.
test.check_use_exposed_dir(
vec!["b:0"].into(),
CheckUse::Directory {
path: "/hippo_data".try_into().unwrap(),
file: PathBuf::from("hippo"),
expected_res: ExpectedResult::Err(zx::Status::NOT_FOUND),
},
)
.await;
test.check_use_exposed_dir(
vec!["b:0"].into(),
CheckUse::Protocol {
path: "/hippo_svc".try_into().unwrap(),
expected_res: ExpectedResult::Err(zx::Status::NOT_FOUND),
},
)
.await;
test.check_use_exposed_dir(
vec!["b:0", "c:0"].into(),
CheckUse::Directory {
path: "/hippo_data".try_into().unwrap(),
file: PathBuf::from("hippo"),
expected_res: ExpectedResult::Ok,
},
)
.await;
test.check_use_exposed_dir(
vec!["b:0", "c:0"].into(),
CheckUse::Protocol {
path: "/hippo_svc".try_into().unwrap(),
expected_res: ExpectedResult::Ok,
},
)
.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::Parent,
source_name: "foo_svc".into(),
target_path: CapabilityPath::try_from("/svc/hippo").unwrap(),
});
let components = vec![
(
"a",
ComponentDeclBuilder::new()
.protocol(ProtocolDeclBuilder::new("foo_svc").build())
.use_(UseDecl::Protocol(UseProtocolDecl {
source: UseSource::Framework,
source_name: "fuchsia.sys2.Realm".into(),
target_path: CapabilityPath::try_from("/svc/fuchsia.sys2.Realm").unwrap(),
}))
.offer(OfferDecl::Protocol(OfferProtocolDecl {
source: OfferServiceSource::Self_,
source_name: "foo_svc".into(),
target_name: "foo_svc".into(),
target: OfferTarget::Collection("coll".to_string()),
dependency_type: DependencyType::Strong,
}))
.add_transient_collection("coll")
.build(),
),
(
"b",
ComponentDeclBuilder::new()
.offer(OfferDecl::Protocol(OfferProtocolDecl {
source: OfferServiceSource::Parent,
source_name: "foo_svc".into(),
target_name: "foo_svc".into(),
target: OfferTarget::Child("c".to_string()),
dependency_type: DependencyType::Strong,
}))
.add_lazy_child("c")
.build(),
),
("c", ComponentDeclBuilder::new().use_(use_decl.clone()).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(), expected_res: ExpectedResult::Ok },
)
.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, mut server) = zx::Channel::create().unwrap();
let err = routing::route_use_capability(
OPEN_RIGHT_READABLE,
MODE_TYPE_SERVICE,
"hippo".to_string(),
&use_decl,
&realm_c,
&mut 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::Parent,
source_name: "invalid".into(),
target_path: CapabilityPath::try_from("/svc/valid").unwrap(),
}))
.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(),
expected_res: ExpectedResult::Err(zx::Status::UNAVAILABLE),
},
)
.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_name: "invalid".into(),
source: OfferServiceSource::Parent,
target_name: "valid".into(),
target: OfferTarget::Child("b".to_string()),
dependency_type: DependencyType::Strong,
}))
.add_lazy_child("b")
.build(),
),
(
"b",
ComponentDeclBuilder::new()
.use_(UseDecl::Protocol(UseProtocolDecl {
source: UseSource::Parent,
source_name: "valid".into(),
target_path: CapabilityPath::try_from("/svc/valid").unwrap(),
}))
.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(),
expected_res: ExpectedResult::Err(zx::Status::UNAVAILABLE),
},
)
.await;
}
/// a
/// \
/// b
///
/// b: uses framework events "started", and "capability_requested"
#[fuchsia_async::run_singlethreaded(test)]
async fn use_event_from_framework() {
let components = vec![
(
"a",
ComponentDeclBuilder::new()
.offer(OfferDecl::Protocol(OfferProtocolDecl {
source: OfferServiceSource::Parent,
source_name: "fuchsia.sys2.BlockingEventSource".try_into().unwrap(),
target_name: "fuchsia.sys2.BlockingEventSource".try_into().unwrap(),
target: OfferTarget::Child("b".to_string()),
dependency_type: DependencyType::Strong,
}))
.add_lazy_child("b")
.build(),
),
(
"b",
ComponentDeclBuilder::new()
.use_(UseDecl::Protocol(UseProtocolDecl {
source: UseSource::Parent,
source_name: "fuchsia.sys2.BlockingEventSource".try_into().unwrap(),
target_path: "/svc/fuchsia.sys2.BlockingEventSource".try_into().unwrap(),
}))
.use_(UseDecl::Event(UseEventDecl {
source: UseSource::Framework,
source_name: "capability_requested".into(),
target_name: "capability_requested".into(),
filter: None,
mode: cm_rust::EventMode::Sync,
}))
.use_(UseDecl::Event(UseEventDecl {
source: UseSource::Framework,
source_name: "started".into(),
target_name: "started".into(),
filter: None,
mode: cm_rust::EventMode::Sync,
}))
.use_(UseDecl::Event(UseEventDecl {
source: UseSource::Framework,
source_name: "resolved".into(),
target_name: "resolved".into(),
filter: None,
mode: cm_rust::EventMode::Sync,
}))
.build(),
),
];
let test = RoutingTest::new("a", components).await;
test.check_use(
vec!["b:0"].into(),
CheckUse::Event {
requests: vec![
EventSubscription::new("capability_requested".into(), EventMode::Sync),
EventSubscription::new("started".into(), EventMode::Sync),
],
expected_res: ExpectedResult::Ok,
},
)
.await;
}
/// a
/// \
/// b
///
/// a; attempts to offer event "capability_requested" to b.
#[fuchsia_async::run_singlethreaded(test)]
async fn can_offer_capability_requested_event() {
let components = vec![
(
"a",
ComponentDeclBuilder::new()
.offer(OfferDecl::Protocol(OfferProtocolDecl {
source: OfferServiceSource::Parent,
source_name: "fuchsia.sys2.BlockingEventSource".try_into().unwrap(),
target_name: "fuchsia.sys2.BlockingEventSource".try_into().unwrap(),
target: OfferTarget::Child("b".to_string()),
dependency_type: DependencyType::Strong,
}))
.offer(OfferDecl::Event(OfferEventDecl {
source: OfferEventSource::Framework,
source_name: "capability_requested".into(),
target_name: "capability_requested_on_a".into(),
target: OfferTarget::Child("b".to_string()),
filter: None,
mode: cm_rust::EventMode::Sync,
}))
.add_lazy_child("b")
.build(),
),
(
"b",
ComponentDeclBuilder::new()
.use_(UseDecl::Protocol(UseProtocolDecl {
source: UseSource::Parent,
source_name: "fuchsia.sys2.BlockingEventSource".try_into().unwrap(),
target_path: "/svc/fuchsia.sys2.BlockingEventSource".try_into().unwrap(),
}))
.use_(UseDecl::Event(UseEventDecl {
source: UseSource::Parent,
source_name: "capability_requested_on_a".into(),
target_name: "capability_requested_from_parent".into(),
filter: None,
mode: cm_rust::EventMode::Sync,
}))
.use_(UseDecl::Event(UseEventDecl {
source: UseSource::Framework,
source_name: "resolved".into(),
target_name: "resolved".into(),
filter: None,
mode: cm_rust::EventMode::Sync,
}))
.build(),
),
];
let test = RoutingTest::new("a", components).await;
test.check_use(
vec!["b:0"].into(),
CheckUse::Event {
requests: vec![EventSubscription::new(
"capability_requested_from_parent".into(),
EventMode::Sync,
)],
expected_res: ExpectedResult::Ok,
},
)
.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,
mode: cm_rust::EventMode::Sync,
}))
.offer(OfferDecl::Protocol(OfferProtocolDecl {
source: OfferServiceSource::Parent,
source_name: "fuchsia.sys2.BlockingEventSource".try_into().unwrap(),
target_name: "fuchsia.sys2.BlockingEventSource".try_into().unwrap(),
target: OfferTarget::Child("b".to_string()),
dependency_type: DependencyType::Strong,
}))
.add_lazy_child("b")
.build(),
),
(
"b",
ComponentDeclBuilder::new()
.use_(UseDecl::Protocol(UseProtocolDecl {
source: UseSource::Parent,
source_name: "fuchsia.sys2.BlockingEventSource".try_into().unwrap(),
target_path: "/svc/fuchsia.sys2.BlockingEventSource".try_into().unwrap(),
}))
.use_(UseDecl::Event(UseEventDecl {
source: UseSource::Parent,
source_name: "started_on_a".into(),
target_name: "started".into(),
filter: None,
mode: cm_rust::EventMode::Sync,
}))
.use_(UseDecl::Event(UseEventDecl {
source: UseSource::Framework,
source_name: "resolved".into(),
target_name: "resolved".into(),
filter: None,
mode: cm_rust::EventMode::Sync,
}))
.build(),
),
];
let test = RoutingTest::new("a", components).await;
test.check_use(
vec!["b:0"].into(),
CheckUse::Event {
requests: vec![EventSubscription::new("started".into(), EventMode::Sync)],
expected_res: ExpectedResult::Ok,
},
)
.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,
mode: cm_rust::EventMode::Sync,
}))
.offer(OfferDecl::Protocol(OfferProtocolDecl {
source: OfferServiceSource::Parent,
source_name: "fuchsia.sys2.BlockingEventSource".try_into().unwrap(),
target_name: "fuchsia.sys2.BlockingEventSource".try_into().unwrap(),
target: OfferTarget::Child("b".to_string()),
dependency_type: DependencyType::Strong,
}))
.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,
mode: cm_rust::EventMode::Sync,
}))
.add_lazy_child("b")
.build(),
),
(
"b",
ComponentDeclBuilder::new()
.offer(OfferDecl::Event(OfferEventDecl {
source: OfferEventSource::Parent,
source_name: "started_on_a".into(),
target_name: "started_on_a".into(),
target: OfferTarget::Child("c".to_string()),
filter: None,
mode: cm_rust::EventMode::Sync,
}))
.offer(OfferDecl::Protocol(OfferProtocolDecl {
source: OfferServiceSource::Parent,
source_name: "fuchsia.sys2.BlockingEventSource".try_into().unwrap(),
target_name: "fuchsia.sys2.BlockingEventSource".try_into().unwrap(),
target: OfferTarget::Child("c".to_string()),
dependency_type: DependencyType::Strong,
}))
.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())}),
mode: cm_rust::EventMode::Sync,
}))
.add_lazy_child("c")
.build(),
),
(
"c",
ComponentDeclBuilder::new()
.use_(UseDecl::Protocol(UseProtocolDecl {
source: UseSource::Parent,
source_name: "fuchsia.sys2.BlockingEventSource".try_into().unwrap(),
target_path: "/svc/fuchsia.sys2.BlockingEventSource".try_into().unwrap(),
}))
.use_(UseDecl::Event(UseEventDecl {
source: UseSource::Parent,
source_name: "started_on_a".into(),
target_name: "started".into(),
filter: None,
mode: cm_rust::EventMode::Sync,
}))
.use_(UseDecl::Event(UseEventDecl {
source: UseSource::Parent,
source_name: "destroyed".into(),
target_name: "destroyed".into(),
filter: Some(hashmap!{"path".to_string() => DictionaryValue::Str("/diagnostics".to_string())}),
mode: cm_rust::EventMode::Sync,
}))
.use_(UseDecl::Event(UseEventDecl {
source: UseSource::Parent,
source_name: "stopped_on_a".into(),
target_name: "stopped".into(),
filter: None,
mode: cm_rust::EventMode::Sync,
}))
.use_(UseDecl::Event(UseEventDecl {
source: UseSource::Framework,
source_name: "resolved".into(),
target_name: "resolved".into(),
filter: None,
mode: cm_rust::EventMode::Sync,
}))
.build(),
),
];
let test = RoutingTest::new("a", components).await;
test.check_use(
vec!["b:0", "c:0"].into(),
CheckUse::Event {
requests: vec![
EventSubscription::new("started".into(), EventMode::Sync),
EventSubscription::new("destroyed".into(), EventMode::Sync),
],
expected_res: ExpectedResult::Ok,
},
)
.await;
test.check_use(
vec!["b:0", "c:0"].into(),
CheckUse::Event {
requests: vec![EventSubscription::new("stopped".into(), EventMode::Sync)],
expected_res: ExpectedResult::Err(zx::Status::UNAVAILABLE),
},
)
.await;
}
/// a
/// |
/// b
/// / \
/// c d
///
/// a: offer framework event "capability_ready" with filters "/foo", "/bar", "/baz" to b
/// b: uses realm event "capability_ready" with filters "/foo"
/// b: offers realm event "capabilty_ready" with filters "/foo", "/bar" to c, d
/// c: uses realm event "capability_ready" with filters "/foo", "/bar"
/// d: uses realm event "capability_ready" with filters "/baz" (fails)
#[fuchsia_async::run_singlethreaded(test)]
async fn event_filter_routing() {
let components = vec![
(
"a",
ComponentDeclBuilder::new()
.offer(OfferDecl::Event(OfferEventDecl {
source: OfferEventSource::Framework,
source_name: "capability_ready".into(),
target_name: "capability_ready".into(),
target: OfferTarget::Child("b".to_string()),
filter: Some(hashmap! {
"name".to_string() => DictionaryValue::StrVec(vec![
"foo".to_string(), "bar".to_string(), "baz".to_string()
])
}),
mode: cm_rust::EventMode::Sync,
}))
.offer(OfferDecl::Protocol(OfferProtocolDecl {
source: OfferServiceSource::Parent,
source_name: "fuchsia.sys2.BlockingEventSource".try_into().unwrap(),
target_name: "fuchsia.sys2.BlockingEventSource".try_into().unwrap(),
target: OfferTarget::Child("b".to_string()),
dependency_type: DependencyType::Strong,
}))
.add_lazy_child("b")
.build(),
),
(
"b",
ComponentDeclBuilder::new()
.use_(UseDecl::Protocol(UseProtocolDecl {
source: UseSource::Parent,
source_name: "fuchsia.sys2.BlockingEventSource".try_into().unwrap(),
target_path: "/svc/fuchsia.sys2.BlockingEventSource".try_into().unwrap(),
}))
.use_(UseDecl::Event(UseEventDecl {
source: UseSource::Parent,
source_name: "capability_ready".into(),
target_name: "capability_ready_foo".into(),
filter: Some(hashmap! {
"name".to_string() => DictionaryValue::Str("foo".into()),
}),
mode: cm_rust::EventMode::Sync,
}))
.use_(UseDecl::Event(UseEventDecl {
source: UseSource::Framework,
source_name: "resolved".into(),
target_name: "resolved".into(),
filter: None,
mode: cm_rust::EventMode::Sync,
}))
.offer(OfferDecl::Protocol(OfferProtocolDecl {
source: OfferServiceSource::Parent,
source_name: "fuchsia.sys2.BlockingEventSource".try_into().unwrap(),
target_name: "fuchsia.sys2.BlockingEventSource".try_into().unwrap(),
target: OfferTarget::Child("c".to_string()),
dependency_type: DependencyType::Strong,
}))
.offer(OfferDecl::Protocol(OfferProtocolDecl {
source: OfferServiceSource::Parent,
source_name: "fuchsia.sys2.BlockingEventSource".try_into().unwrap(),
target_name: "fuchsia.sys2.BlockingEventSource".try_into().unwrap(),
target: OfferTarget::Child("d".to_string()),
dependency_type: DependencyType::Strong,
}))
.offer(OfferDecl::Event(OfferEventDecl {
source: OfferEventSource::Parent,
source_name: "capability_ready".into(),
target_name: "capability_ready".into(),
target: OfferTarget::Child("c".to_string()),
filter: Some(hashmap! {
"name".to_string() => DictionaryValue::StrVec(vec![
"foo".to_string(), "bar".to_string()
])
}),
mode: cm_rust::EventMode::Sync,
}))
.offer(OfferDecl::Event(OfferEventDecl {
source: OfferEventSource::Parent,
source_name: "capability_ready".into(),
target_name: "capability_ready".into(),
target: OfferTarget::Child("d".to_string()),
filter: Some(hashmap! {
"name".to_string() => DictionaryValue::StrVec(vec![
"foo".to_string(), "bar".to_string()
])
}),
mode: cm_rust::EventMode::Sync,
}))
.add_lazy_child("c")
.add_lazy_child("d")
.build(),
),
(
"c",
ComponentDeclBuilder::new()
.use_(UseDecl::Protocol(UseProtocolDecl {
source: UseSource::Parent,
source_name: "fuchsia.sys2.BlockingEventSource".try_into().unwrap(),
target_path: "/svc/fuchsia.sys2.BlockingEventSource".try_into().unwrap(),
}))
.use_(UseDecl::Event(UseEventDecl {
source: UseSource::Parent,
source_name: "capability_ready".into(),
target_name: "capability_ready_foo_bar".into(),
filter: Some(hashmap! {
"name".to_string() => DictionaryValue::StrVec(vec![
"foo".to_string(), "bar".to_string()
])
}),
mode: cm_rust::EventMode::Sync,
}))
.use_(UseDecl::Event(UseEventDecl {
source: UseSource::Framework,
source_name: "resolved".into(),
target_name: "resolved".into(),
filter: None,
mode: cm_rust::EventMode::Sync,
}))
.build(),
),
(
"d",
ComponentDeclBuilder::new()
.use_(UseDecl::Protocol(UseProtocolDecl {
source: UseSource::Parent,
source_name: "fuchsia.sys2.BlockingEventSource".try_into().unwrap(),
target_path: "/svc/fuchsia.sys2.BlockingEventSource".try_into().unwrap(),
}))
.use_(UseDecl::Event(UseEventDecl {
source: UseSource::Parent,
source_name: "capability_ready".into(),
target_name: "capability_ready_baz".into(),
filter: Some(hashmap! {
"name".to_string() => DictionaryValue::Str("baz".into()),
}),
mode: cm_rust::EventMode::Sync,
}))
.use_(UseDecl::Event(UseEventDecl {
source: UseSource::Framework,
source_name: "resolved".into(),
target_name: "resolved".into(),
filter: None,
mode: cm_rust::EventMode::Sync,
}))
.build(),
),
];
let test = RoutingTest::new("a", components).await;
test.check_use(
vec!["b:0"].into(),
CheckUse::Event {
requests: vec![EventSubscription::new("capability_ready_foo".into(), EventMode::Sync)],
expected_res: ExpectedResult::Ok,
},
)
.await;
test.check_use(
vec!["b:0", "c:0"].into(),
CheckUse::Event {
requests: vec![EventSubscription::new(
"capability_ready_foo_bar".into(),
EventMode::Sync,
)],
expected_res: ExpectedResult::Ok,
},
)
.await;
test.check_use(
vec!["b:0", "d:0"].into(),
CheckUse::Event {
requests: vec![EventSubscription::new("capability_ready_baz".into(), EventMode::Sync)],
expected_res: ExpectedResult::Err(zx::Status::UNAVAILABLE),
},
)
.await;
}
/// a
/// |
/// b
/// |
/// c
///
/// a: offer framework event "capability_ready" with mode "async".
/// b: offers parent event "capabilty_ready" with mode "sync".
/// c: uses realm event "capability_ready" with mode "sync"
#[fuchsia_async::run_singlethreaded(test)]
async fn event_mode_routing_failure() {
let components = vec![
(
"a",
ComponentDeclBuilder::new()
.offer(OfferDecl::Event(OfferEventDecl {
source: OfferEventSource::Framework,
source_name: "capability_ready".into(),
target_name: "capability_ready".into(),
target: OfferTarget::Child("b".to_string()),
filter: None,
mode: cm_rust::EventMode::Async,
}))
.offer(OfferDecl::Protocol(OfferProtocolDecl {
source: OfferServiceSource::Parent,
source_name: "fuchsia.sys2.BlockingEventSource".try_into().unwrap(),
target_name: "fuchsia.sys2.BlockingEventSource".try_into().unwrap(),
target: OfferTarget::Child("b".to_string()),
dependency_type: DependencyType::Strong,
}))
.add_lazy_child("b")
.build(),
),
(
"b",
ComponentDeclBuilder::new()
.offer(OfferDecl::Event(OfferEventDecl {
source: OfferEventSource::Parent,
source_name: "capability_ready".into(),
target_name: "capability_ready".into(),
target: OfferTarget::Child("c".to_string()),
filter: None,
mode: cm_rust::EventMode::Sync,
}))
.offer(OfferDecl::Protocol(OfferProtocolDecl {
source: OfferServiceSource::Parent,
source_name: "fuchsia.sys2.BlockingEventSource".try_into().unwrap(),
target_name: "fuchsia.sys2.BlockingEventSource".try_into().unwrap(),
target: OfferTarget::Child("c".to_string()),
dependency_type: DependencyType::Strong,
}))
.add_lazy_child("c")
.build(),
),
(
"c",
ComponentDeclBuilder::new()
.use_(UseDecl::Event(UseEventDecl {
source: UseSource::Parent,
source_name: "capability_ready".into(),
target_name: "capability_ready_foo_bar".into(),
filter: None,
mode: cm_rust::EventMode::Sync,
}))
.use_(UseDecl::Protocol(UseProtocolDecl {
source: UseSource::Parent,
source_name: "fuchsia.sys2.BlockingEventSource".try_into().unwrap(),
target_path: "/svc/fuchsia.sys2.BlockingEventSource".try_into().unwrap(),
}))
.use_(UseDecl::Event(UseEventDecl {
source: UseSource::Framework,
source_name: "resolved".into(),
target_name: "resolved".into(),
filter: None,
mode: cm_rust::EventMode::Sync,
}))
.build(),
),
];
let test = RoutingTest::new("a", components).await;
test.check_use(
vec!["b:0", "c:0"].into(),
CheckUse::Event {
requests: vec![EventSubscription::new(
"capability_ready_foo_bar".into(),
EventMode::Sync,
)],
expected_res: ExpectedResult::Err(zx::Status::UNAVAILABLE),
},
)
.await;
}
/// a
/// |
/// b
/// |
/// c
///
/// a: offer framework event "capability_ready" with mode "sync".
/// b: offers parent event "capabilty_ready" with mode "async".
/// c: uses realm event "capability_ready" with mode "async"
#[fuchsia_async::run_singlethreaded(test)]
async fn event_mode_routing_success() {
let components = vec![
(
"a",
ComponentDeclBuilder::new()
.offer(OfferDecl::Event(OfferEventDecl {
source: OfferEventSource::Framework,
source_name: "capability_ready".into(),
target_name: "capability_ready".into(),
target: OfferTarget::Child("b".to_string()),
filter: None,
mode: cm_rust::EventMode::Sync,
}))
.offer(OfferDecl::Protocol(OfferProtocolDecl {
source: OfferServiceSource::Parent,
source_name: "fuchsia.sys2.BlockingEventSource".try_into().unwrap(),
target_name: "fuchsia.sys2.BlockingEventSource".try_into().unwrap(),
target: OfferTarget::Child("b".to_string()),
dependency_type: DependencyType::Strong,
}))
.add_lazy_child("b")
.build(),
),
(
"b",
ComponentDeclBuilder::new()
.offer(OfferDecl::Event(OfferEventDecl {
source: OfferEventSource::Parent,
source_name: "capability_ready".into(),
target_name: "capability_ready".into(),
target: OfferTarget::Child("c".to_string()),
filter: None,
mode: cm_rust::EventMode::Async,
}))
.offer(OfferDecl::Protocol(OfferProtocolDecl {
source: OfferServiceSource::Parent,
source_name: "fuchsia.sys2.BlockingEventSource".try_into().unwrap(),
target_name: "fuchsia.sys2.BlockingEventSource".try_into().unwrap(),
target: OfferTarget::Child("c".to_string()),
dependency_type: DependencyType::Strong,
}))
.add_lazy_child("c")
.build(),
),
(
"c",
ComponentDeclBuilder::new()
.use_(UseDecl::Event(UseEventDecl {
source: UseSource::Parent,
source_name: "capability_ready".into(),
target_name: "capability_ready_foo_bar".into(),
filter: None,
mode: cm_rust::EventMode::Async,
}))
.use_(UseDecl::Protocol(UseProtocolDecl {
source: UseSource::Parent,
source_name: "fuchsia.sys2.BlockingEventSource".try_into().unwrap(),
target_path: "/svc/fuchsia.sys2.BlockingEventSource".try_into().unwrap(),
}))
.use_(UseDecl::Event(UseEventDecl {
source: UseSource::Framework,
source_name: "resolved".into(),
target_name: "resolved".into(),
filter: None,
mode: cm_rust::EventMode::Sync,
}))
.build(),
),
];
let test = RoutingTest::new("a", components).await;
test.check_use(
vec!["b:0", "c:0"].into(),
CheckUse::Event {
requests: vec![EventSubscription::new(
"capability_ready_foo_bar".into(),
EventMode::Async,
)],
expected_res: ExpectedResult::Ok,
},
)
.await;
test.check_use(
vec!["b:0", "c:0"].into(),
CheckUse::Event {
requests: vec![EventSubscription::new(
"capability_ready_foo_bar".into(),
EventMode::Sync,
)],
expected_res: ExpectedResult::Err(zx::Status::UNAVAILABLE),
},
)
.await;
}
/// a
/// / \
/// b c
///
/// a: creates environment "env" and registers resolver "base" from c.
/// b: resolved by resolver "base" through "env".
/// b: exposes resolver "base" from self.
#[fuchsia_async::run_singlethreaded(test)]
async fn use_resolver_from_parent_environment() {
// Note that we do not define a component "b". This will be resolved by our custom resolver.
let components = vec![
(
"a",
ComponentDeclBuilder::new_empty_component()
.add_child(ChildDeclBuilder::new().name("b").url("base://b").environment("env"))
.add_child(ChildDeclBuilder::new_lazy_child("c"))
.add_environment(
EnvironmentDeclBuilder::new()
.name("env")
.extends(fsys::EnvironmentExtends::Realm)
.add_resolver(ResolverRegistration {
resolver: "base".into(),
source: RegistrationSource::Child("c".into()),
scheme: "base".into(),
}),
)
.build(),
),
(
"c",
ComponentDeclBuilder::new()
.expose(ExposeDecl::Resolver(ExposeResolverDecl {
source: ExposeSource::Self_,
source_name: "base".into(),
target: ExposeTarget::Parent,
target_name: "base".into(),
}))
.resolver(ResolverDecl {
name: "base".into(),
source_path: "/svc/fuchsia.sys2.ComponentResolver".parse().unwrap(),
})
.build(),
),
];
// Set up the system.
let (resolver_service, mut receiver) =
create_service_directory_entry::<fsys::ComponentResolverMarker>();
let universe = RoutingTestBuilder::new("a", components)
// Component "c" exposes a resolver service.
.add_outgoing_path(
"c",
CapabilityPath::try_from("/svc/fuchsia.sys2.ComponentResolver").unwrap(),
resolver_service,
)
.build()
.await;
join!(
// Bind "b:0". We expect to see a call to our resolver service for the new component.
async move {
universe
.bind_instance(&vec!["b:0"].into())
.await
.expect("failed to bind to instance b:0");
},
// Wait for a request, and resolve it.
async {
while let Some(fsys::ComponentResolverRequest::Resolve { component_url, responder }) =
receiver.next().await
{
assert_eq!(component_url, "base://b");
responder
.send(
Status::OK.into_raw(),
fsys::Component {
resolved_url: Some("test://b".into()),
decl: Some(default_component_decl().native_into_fidl()),
package: None,
..fsys::Component::EMPTY
},
)
.expect("failed to send resolve response");
}
}
);
}
/// a
/// \
/// b
/// \
/// c
/// a: creates environment "env" and registers resolver "base" from self.
/// b: has environment "env".
/// c: is resolved by resolver from grandarent.
#[fuchsia_async::run_singlethreaded(test)]
async fn use_resolver_from_grandparent_environment() {
// Note that we do not define a component "c". This will be resolved by our custom resolver.
let components = vec![
(
"a",
ComponentDeclBuilder::new()
.add_child(ChildDeclBuilder::new_lazy_child("b").environment("env"))
.add_environment(
EnvironmentDeclBuilder::new()
.name("env")
.extends(fsys::EnvironmentExtends::Realm)
.add_resolver(ResolverRegistration {
resolver: "base".into(),
source: RegistrationSource::Self_,
scheme: "base".into(),
}),
)
.resolver(ResolverDecl {
name: "base".into(),
source_path: "/svc/fuchsia.sys2.ComponentResolver".parse().unwrap(),
})
.build(),
),
(
"b",
ComponentDeclBuilder::new_empty_component()
.add_child(ChildDeclBuilder::new().name("c").url("base://c"))
.build(),
),
];
// Set up the system.
let (resolver_service, mut receiver) =
create_service_directory_entry::<fsys::ComponentResolverMarker>();
let universe = RoutingTestBuilder::new("a", components)
// Component "c" exposes a resolver service.
.add_outgoing_path(
"a",
CapabilityPath::try_from("/svc/fuchsia.sys2.ComponentResolver").unwrap(),
resolver_service,
)
.build()
.await;
join!(
// Bind "c:0". We expect to see a call to our resolver service for the new component.
async move {
universe
.bind_instance(&vec!["b:0", "c:0"].into())
.await
.expect("failed to bind to instance c:0");
},
// Wait for a request, and resolve it.
async {
while let Some(fsys::ComponentResolverRequest::Resolve { component_url, responder }) =
receiver.next().await
{
assert_eq!(component_url, "base://c");
responder
.send(
Status::OK.into_raw(),
fsys::Component {
resolved_url: Some("test://c".into()),
decl: Some(default_component_decl().native_into_fidl()),
package: None,
..fsys::Component::EMPTY
},
)
.expect("failed to send resolve response");
}
}
);
}
/// a
/// / \
/// b c
/// a: creates environment "env" and registers resolver "base" from self.
/// b: has environment "env".
/// c: does NOT have environment "env".
#[fuchsia_async::run_singlethreaded(test)]
async fn resolver_is_not_available() {
// Note that we do not define a component "b" or "c". This will be resolved by our custom resolver.
let components = vec![(
"a",
ComponentDeclBuilder::new()
.add_child(ChildDeclBuilder::new().name("b").url("base://b").environment("env"))
.add_child(ChildDeclBuilder::new().name("c").url("base://c"))
.add_environment(
EnvironmentDeclBuilder::new()
.name("env")
.extends(fsys::EnvironmentExtends::Realm)
.add_resolver(ResolverRegistration {
resolver: "base".into(),
source: RegistrationSource::Self_,
scheme: "base".into(),
}),
)
.resolver(ResolverDecl {
name: "base".into(),
source_path: "/svc/fuchsia.sys2.ComponentResolver".parse().unwrap(),
})
.build(),
)];
// Set up the system.
let (resolver_service, mut receiver) =
create_service_directory_entry::<fsys::ComponentResolverMarker>();
let universe = RoutingTestBuilder::new("a", components)
// Component "c" exposes a resolver service.
.add_outgoing_path(
"a",
CapabilityPath::try_from("/svc/fuchsia.sys2.ComponentResolver").unwrap(),
resolver_service,
)
.build()
.await;
join!(
// Bind "c:0". We expect to see a failure that the scheme is not registered.
async move {
assert_matches!(
universe.bind_instance(&vec!["c:0"].into()).await,
Err(ModelError::ResolverError { err: ResolverError::SchemeNotRegistered })
);
},
// Wait for a request, and resolve it.
async {
while let Some(fsys::ComponentResolverRequest::Resolve { component_url, responder }) =
receiver.next().await
{
assert_eq!(component_url, "base://b");
responder
.send(
Status::OK.into_raw(),
fsys::Component {
resolved_url: Some("test://b".into()),
decl: Some(default_component_decl().native_into_fidl()),
package: None,
..fsys::Component::EMPTY
},
)
.expect("failed to send resolve response");
}
}
);
}
/// a
/// /
/// b
/// a: creates environment "env" and registers resolver "base" from self.
/// b: has environment "env".
#[fuchsia_async::run_singlethreaded(test)]
async fn resolver_component_decl_is_validated() {
// Note that we do not define a component "b". This will be resolved by our custom resolver.
let components = vec![(
"a",
ComponentDeclBuilder::new()
.add_child(ChildDeclBuilder::new().name("b").url("base://b").environment("env"))
.add_environment(
EnvironmentDeclBuilder::new()
.name("env")
.extends(fsys::EnvironmentExtends::Realm)
.add_resolver(ResolverRegistration {
resolver: "base".into(),
source: RegistrationSource::Self_,
scheme: "base".into(),
}),
)
.resolver(ResolverDecl {
name: "base".into(),
source_path: "/svc/fuchsia.sys2.ComponentResolver".parse().unwrap(),
})
.build(),
)];
// Set up the system.
let (resolver_service, mut receiver) =
create_service_directory_entry::<fsys::ComponentResolverMarker>();
let universe = RoutingTestBuilder::new("a", components)
// Component "a" exposes a resolver service.
.add_outgoing_path(
"a",
CapabilityPath::try_from("/svc/fuchsia.sys2.ComponentResolver").unwrap(),
resolver_service,
)
.build()
.await;
join!(
// Bind "b:0". We expect to see a ResolverError.
async move {
assert_matches!(
universe.bind_instance(&vec!["b:0"].into()).await,
Err(ModelError::ResolverError { err: ResolverError::ManifestInvalid { .. } })
);
},
// Wait for a request, and resolve it.
async {
while let Some(fsys::ComponentResolverRequest::Resolve { component_url, responder }) =
receiver.next().await
{
assert_eq!(component_url, "base://b");
responder
.send(
Status::OK.into_raw(),
fsys::Component {
resolved_url: Some("test://b".into()),
decl: Some(fsys::ComponentDecl {
exposes: Some(vec![fsys::ExposeDecl::Protocol(
fsys::ExposeProtocolDecl {
source: Some(fsys::Ref::Self_(fsys::SelfRef {})),
..fsys::ExposeProtocolDecl::EMPTY
},
)]),
..fsys::ComponentDecl::EMPTY
}),
package: None,
..fsys::Component::EMPTY
},
)
.expect("failed to send resolve response");
}
}
);
}
/// a
/// \
/// b
///
/// b: uses service /svc/hippo as /svc/hippo.
/// a: provides b with the service but policy prevents it.
#[fuchsia_async::run_singlethreaded(test)]
async fn use_protocol_denied_by_capability_policy() {
let components = vec![
(
"a",
ComponentDeclBuilder::new()
.protocol(ProtocolDeclBuilder::new("hippo_svc").build())
.offer(OfferDecl::Protocol(OfferProtocolDecl {
source: OfferServiceSource::Self_,
source_name: "hippo_svc".into(),
target_name: "hippo_svc".into(),
target: OfferTarget::Child("b".to_string()),
dependency_type: DependencyType::Strong,
}))
.add_lazy_child("b")
.build(),
),
(
"b",
ComponentDeclBuilder::new()
.use_(UseDecl::Protocol(UseProtocolDecl {
source: UseSource::Parent,
source_name: "hippo_svc".into(),
target_path: CapabilityPath::try_from("/svc/hippo").unwrap(),
}))
.build(),
),
];
let test = RoutingTestBuilder::new("a", components)
.add_capability_policy(
CapabilityAllowlistKey {
source_moniker: ExtendedMoniker::ComponentInstance(AbsoluteMoniker::root()),
source_name: CapabilityName::from("hippo_svc"),
source: CapabilityAllowlistSource::Self_,
capability: CapabilityTypeName::Protocol,
},
HashSet::new(),
)
.build()
.await;
test.check_use(
vec!["b:0"].into(),
CheckUse::Protocol {
path: default_service_capability(),
expected_res: ExpectedResult::Err(zx::Status::ACCESS_DENIED),
},
)
.await;
}
/// a
/// \
/// b
///
/// b: uses directory /data/foo as /data/bar.
/// a: provides b with the directory but policy prevents it.
#[fuchsia_async::run_singlethreaded(test)]
async fn use_directory_with_alias_denied_by_capability_policy() {
let components = vec![
(
"a",
ComponentDeclBuilder::new()
.directory(DirectoryDeclBuilder::new("foo_data").build())
.offer(OfferDecl::Directory(OfferDirectoryDecl {
source: OfferDirectorySource::Self_,
source_name: "foo_data".into(),
target_name: "bar_data".into(),
target: OfferTarget::Child("b".to_string()),
rights: Some(*rights::READ_RIGHTS),
subdir: None,
dependency_type: DependencyType::Strong,
}))
.add_lazy_child("b")
.build(),
),
(
"b",
ComponentDeclBuilder::new()
.use_(UseDecl::Directory(UseDirectoryDecl {
source: UseSource::Parent,
source_name: "bar_data".into(),
target_path: CapabilityPath::try_from("/data/hippo").unwrap(),
rights: *rights::READ_RIGHTS,
subdir: None,
}))
.build(),
),
];
let test = RoutingTestBuilder::new("a", components)
.add_capability_policy(
CapabilityAllowlistKey {
source_moniker: ExtendedMoniker::ComponentInstance(AbsoluteMoniker::root()),
source_name: CapabilityName::from("foo_data"),
source: CapabilityAllowlistSource::Self_,
capability: CapabilityTypeName::Directory,
},
HashSet::new(),
)
.build()
.await;
test.check_use(
vec!["b:0"].into(),
CheckUse::default_directory(ExpectedResult::Err(zx::Status::ACCESS_DENIED)),
)
.await;
}
/// a
/// \
/// b
/// \
/// c
/// c: uses service /svc/hippo as /svc/hippo.
/// b: uses service /svc/hippo as /svc/hippo.
/// a: provides b with the service policy allows it.
/// b: provides c with the service policy does not allow it.
#[fuchsia_async::run_singlethreaded(test)]
async fn use_protocol_partial_chain_allowed_by_capability_policy() {
let components = vec![
(
"a",
ComponentDeclBuilder::new()
.protocol(ProtocolDeclBuilder::new("hippo_svc").build())
.offer(OfferDecl::Protocol(OfferProtocolDecl {
source: OfferServiceSource::Self_,
source_name: "hippo_svc".into(),
target_name: "hippo_svc".into(),
target: OfferTarget::Child("b".to_string()),
dependency_type: DependencyType::Strong,
}))
.add_lazy_child("b")
.build(),
),
(
"b",
ComponentDeclBuilder::new()
.offer(OfferDecl::Protocol(OfferProtocolDecl {
source: OfferServiceSource::Parent,
source_name: "hippo_svc".into(),
target_name: "hippo_svc".into(),
target: OfferTarget::Child("c".to_string()),
dependency_type: DependencyType::Strong,
}))
.use_(UseDecl::Protocol(UseProtocolDecl {
source: UseSource::Parent,
source_name: "hippo_svc".into(),
target_path: CapabilityPath::try_from("/svc/hippo").unwrap(),
}))
.add_lazy_child("c")
.build(),
),
(
"c",
ComponentDeclBuilder::new()
.use_(UseDecl::Protocol(UseProtocolDecl {
source: UseSource::Parent,
source_name: "hippo_svc".into(),
target_path: CapabilityPath::try_from("/svc/hippo").unwrap(),
}))
.build(),
),
];
let mut allowlist = HashSet::new();
allowlist.insert(AbsoluteMoniker::from(vec!["b:0"]));
let test = RoutingTestBuilder::new("a", components)
.add_capability_policy(
CapabilityAllowlistKey {
source_moniker: ExtendedMoniker::ComponentInstance(AbsoluteMoniker::root()),
source_name: CapabilityName::from("hippo_svc"),
source: CapabilityAllowlistSource::Self_,
capability: CapabilityTypeName::Protocol,
},
allowlist,
)
.build()
.await;
test.check_use(
vec!["b:0"].into(),
CheckUse::Protocol { path: default_service_capability(), expected_res: ExpectedResult::Ok },
)
.await;
test.check_use(
vec!["b:0", "c:0"].into(),
CheckUse::Protocol {
path: default_service_capability(),
expected_res: ExpectedResult::Err(zx::Status::ACCESS_DENIED),
},
)
.await;
}
/// a
/// \
/// b
/// / \
/// c d
/// b: provides d with the service policy allows denies it.
/// b: provides c with the service policy allows it.
/// c: uses service /svc/hippo as /svc/hippo.
/// Tests component provided caps in the middle of a path
#[fuchsia_async::run_singlethreaded(test)]
async fn use_protocol_component_provided_capability_policy() {
let components = vec![
(
"a",
ComponentDeclBuilder::new()
.protocol(ProtocolDeclBuilder::new("hippo_svc").build())
.offer(OfferDecl::Protocol(OfferProtocolDecl {
source: OfferServiceSource::Self_,
source_name: "hippo_svc".into(),
target_name: "hippo_svc".into(),
target: OfferTarget::Child("b".to_string()),
dependency_type: DependencyType::Strong,
}))
.add_lazy_child("b")
.build(),
),
(
"b",
ComponentDeclBuilder::new()
.offer(OfferDecl::Protocol(OfferProtocolDecl {
source: OfferServiceSource::Parent,
source_name: "hippo_svc".into(),
target_name: "hippo_svc".into(),
target: OfferTarget::Child("c".to_string()),
dependency_type: DependencyType::Strong,
}))
.offer(OfferDecl::Protocol(OfferProtocolDecl {
source: OfferServiceSource::Parent,
source_name: "hippo_svc".into(),
target_name: "hippo_svc".into(),
target: OfferTarget::Child("d".to_string()),
dependency_type: DependencyType::Strong,
}))
.add_lazy_child("c")
.add_lazy_child("d")
.build(),
),
(
"c",
ComponentDeclBuilder::new()
.use_(UseDecl::Protocol(UseProtocolDecl {
source: UseSource::Parent,
source_name: "hippo_svc".into(),
target_path: CapabilityPath::try_from("/svc/hippo").unwrap(),
}))
.build(),
),
(
"d",
ComponentDeclBuilder::new()
.use_(UseDecl::Protocol(UseProtocolDecl {
source: UseSource::Parent,
source_name: "hippo_svc".into(),
target_path: CapabilityPath::try_from("/svc/hippo").unwrap(),
}))
.build(),
),
];
let mut allowlist = HashSet::new();
allowlist.insert(AbsoluteMoniker::from(vec!["b:0"]));
allowlist.insert(AbsoluteMoniker::from(vec!["b:0", "c:0"]));
let test = RoutingTestBuilder::new("a", components)
.add_capability_policy(
CapabilityAllowlistKey {
source_moniker: ExtendedMoniker::ComponentInstance(AbsoluteMoniker::root()),
source_name: CapabilityName::from("hippo_svc"),
source: CapabilityAllowlistSource::Self_,
capability: CapabilityTypeName::Protocol,
},
allowlist,
)
.build()
.await;
test.check_use(
vec!["b:0", "c:0"].into(),
CheckUse::Protocol { path: default_service_capability(), expected_res: ExpectedResult::Ok },
)
.await;
test.check_use(
vec!["b:0", "d:0"].into(),
CheckUse::Protocol {
path: default_service_capability(),
expected_res: ExpectedResult::Err(zx::Status::ACCESS_DENIED),
},
)
.await;
}
/// a
/// \
/// b
///
/// b: uses framework events "started", and "capability_requested".
/// Capability policy denies the route from being allowed for started but
/// not for capability_requested.
#[fuchsia_async::run_singlethreaded(test)]
async fn use_event_from_framework_denied_by_capabiilty_policy() {
let components = vec![
(
"a",
ComponentDeclBuilder::new()
.offer(OfferDecl::Protocol(OfferProtocolDecl {
source: OfferServiceSource::Parent,
source_name: "fuchsia.sys2.BlockingEventSource".try_into().unwrap(),
target_name: "fuchsia.sys2.BlockingEventSource".try_into().unwrap(),
target: OfferTarget::Child("b".to_string()),
dependency_type: DependencyType::Strong,
}))
.add_lazy_child("b")
.build(),
),
(
"b",
ComponentDeclBuilder::new()
.use_(UseDecl::Protocol(UseProtocolDecl {
source: UseSource::Parent,
source_name: "fuchsia.sys2.BlockingEventSource".try_into().unwrap(),
target_path: "/svc/fuchsia.sys2.BlockingEventSource".try_into().unwrap(),
}))
.use_(UseDecl::Event(UseEventDecl {
source: UseSource::Framework,
source_name: "capability_requested".into(),
target_name: "capability_requested".into(),
filter: None,
mode: cm_rust::EventMode::Async,
}))
.use_(UseDecl::Event(UseEventDecl {
source: UseSource::Framework,
source_name: "started".into(),
target_name: "started".into(),
filter: None,
mode: cm_rust::EventMode::Async,
}))
.use_(UseDecl::Event(UseEventDecl {
source: UseSource::Framework,
source_name: "resolved".into(),
target_name: "resolved".into(),
filter: None,
mode: cm_rust::EventMode::Sync,
}))
.build(),
),
];
let mut allowlist = HashSet::new();
allowlist.insert(AbsoluteMoniker::from(vec!["b:0"]));
let test = RoutingTestBuilder::new("a", components)
.add_capability_policy(
CapabilityAllowlistKey {
source_moniker: ExtendedMoniker::ComponentInstance(AbsoluteMoniker::from(vec![
"b:0",
])),
source_name: CapabilityName::from("started"),
source: CapabilityAllowlistSource::Framework,
capability: CapabilityTypeName::Event,
},
HashSet::new(),
)
.add_capability_policy(
CapabilityAllowlistKey {
source_moniker: ExtendedMoniker::ComponentInstance(AbsoluteMoniker::from(vec![
"b:0",
])),
source_name: CapabilityName::from("capability_requested"),
source: CapabilityAllowlistSource::Framework,
capability: CapabilityTypeName::Event,
},
allowlist,
)
.build()
.await;
test.check_use(
vec!["b:0"].into(),
CheckUse::Event {
requests: vec![EventSubscription::new("capability_requested".into(), EventMode::Async)],
expected_res: ExpectedResult::Ok,
},
)
.await;
test.check_use(
vec!["b:0"].into(),
CheckUse::Event {
requests: vec![EventSubscription::new("started".into(), EventMode::Async)],
expected_res: ExpectedResult::Err(zx::Status::ACCESS_DENIED),
},
)
.await
}
/// component manager's namespace
/// |
/// a
///
/// a: uses service /use_from_cm_namespace/svc/foo as foo_svc
#[fuchsia_async::run_singlethreaded(test)]
async fn use_from_component_manager_namespace_denied_by_policy() {
let components = vec![(
"a",
ComponentDeclBuilder::new()
.use_(UseDecl::Protocol(UseProtocolDecl {
source: UseSource::Parent,
source_name: "foo_svc".into(),
target_path: CapabilityPath::try_from("/svc/hippo").unwrap(),
}))
.build(),
)];
let namespace_capabilities = vec![CapabilityDecl::Protocol(
ProtocolDeclBuilder::new("foo_svc").path("/use_from_cm_namespace/svc/foo").build(),
)];
let test = RoutingTestBuilder::new("a", components)
.set_namespace_capabilities(namespace_capabilities)
.add_capability_policy(
CapabilityAllowlistKey {
source_moniker: ExtendedMoniker::ComponentManager,
source_name: CapabilityName::from("foo_svc"),
source: CapabilityAllowlistSource::Self_,
capability: CapabilityTypeName::Protocol,
},
HashSet::new(),
)
.build()
.await;
let _ns_dir = ScopedNamespaceDir::new(&test, "/use_from_cm_namespace");
test.check_use(
vec![].into(),
CheckUse::Protocol {
path: default_service_capability(),
expected_res: ExpectedResult::Err(zx::Status::ACCESS_DENIED),
},
)
.await;
}