blob: 40b243923238b214a6e0d91e2ad0ae8c64ae9f5a [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use {
crate::{
model::testing::mocks::ManagedNamespace,
model::testing::{routing_test_helpers::*, test_helpers::*},
model::{binding::Binder, testing::mocks::FakeBinder},
work_scheduler::WorkScheduler,
},
cm_rust::{
self, CapabilityName, CapabilityPath, DependencyType, ExposeDecl, ExposeProtocolDecl,
ExposeSource, ExposeTarget, OfferDecl, OfferProtocolDecl, OfferServiceSource, OfferTarget,
UseDecl, UseProtocolDecl, UseSource,
},
fidl::endpoints::Proxy,
fidl_fuchsia_io::{MODE_TYPE_SERVICE, OPEN_RIGHT_READABLE},
fidl_fuchsia_sys2 as fsys,
moniker::AbsoluteMoniker,
std::{convert::TryFrom, ops::Deref, path::Path, sync::Arc},
};
struct BindingWorkScheduler {
work_scheduler: Arc<WorkScheduler>,
// Retain `Arc` to keep `Binder` alive throughout test.
_binder: Arc<dyn Binder>,
}
impl BindingWorkScheduler {
async fn new() -> Self {
let binder = FakeBinder::new();
let work_scheduler = WorkScheduler::new(binder.clone()).await;
Self { work_scheduler, _binder: binder }
}
}
// `BindingWorkScheduler` API is `Arc<WorkScheduler>` API.
impl Deref for BindingWorkScheduler {
type Target = Arc<WorkScheduler>;
fn deref(&self) -> &Self::Target {
&self.work_scheduler
}
}
async fn new_work_scheduler() -> BindingWorkScheduler {
BindingWorkScheduler::new().await
}
async fn call_work_scheduler_svc_from_namespace(
namespace: &ManagedNamespace,
should_succeed: bool,
) {
let path: CapabilityPath = "/svc/fuchsia.sys2.WorkScheduler".parse().unwrap();
let dir_proxy = capability_util::take_dir_from_namespace(namespace, &path.dirname).await;
let node_proxy = io_util::open_node(
&dir_proxy,
&Path::new(&path.basename),
OPEN_RIGHT_READABLE,
MODE_TYPE_SERVICE,
)
.expect("failed to open WorkScheduler service");
capability_util::add_dir_to_namespace(namespace, &path.dirname, dir_proxy).await;
let work_scheduler_proxy = fsys::WorkSchedulerProxy::new(node_proxy.into_channel().unwrap());
let req = fsys::WorkRequest {
start: Some(fsys::Start::MonotonicTime(0)),
period: None,
..fsys::WorkRequest::EMPTY
};
let res = work_scheduler_proxy.schedule_work("hippos", req).await;
match should_succeed {
true => assert_eq!(res.expect("failed to use WorkScheduler service"), Ok(())),
false => {
let err = res.expect_err("used WorkScheduler service successfully when it should fail");
assert!(err.is_closed(), "expected channel closed error, got: {:?}", err);
}
}
}
async fn check_use_work_scheduler(
routing_test: &RoutingTest,
moniker: AbsoluteMoniker,
should_succeed: bool,
) {
let component_name =
routing_test.bind_instance_and_wait_start(&moniker).await.expect("bind instance failed");
let component_resolved_url = RoutingTest::resolved_url(&component_name);
let namespace = routing_test.mock_runner.get_namespace(&component_resolved_url).unwrap();
call_work_scheduler_svc_from_namespace(&namespace, should_succeed).await;
}
async fn call_work_scheduler_control_svc_from_namespace(
namespace: &ManagedNamespace,
path: CapabilityPath,
should_succeed: bool,
) {
let dir_proxy = capability_util::take_dir_from_namespace(namespace, &path.dirname).await;
let node_proxy = io_util::open_node(
&dir_proxy,
&Path::new(&path.basename),
OPEN_RIGHT_READABLE,
MODE_TYPE_SERVICE,
)
.expect("failed to open WorkSchedulerControl service");
capability_util::add_dir_to_namespace(namespace, &path.dirname, dir_proxy).await;
let work_scheduler_control_proxy =
fsys::WorkSchedulerControlProxy::new(node_proxy.into_channel().unwrap());
let res = work_scheduler_control_proxy.get_batch_period().await;
match should_succeed {
true => {
res.expect("failed to use WorkSchedulerControl service")
.expect("WorkSchedulerControl.GetBatchPeriod() yielded error");
}
false => {
let err = res
.expect_err("used WorkSchedulerControl service successfully when it should fail");
assert!(err.is_closed(), "expected channel closed error, got: {:?}", err);
}
}
}
async fn check_use_work_scheduler_control(
routing_test: &RoutingTest,
moniker: AbsoluteMoniker,
path: CapabilityPath,
should_succeed: bool,
) {
let component_name =
routing_test.bind_instance_and_wait_start(&moniker).await.expect("bind instance failed");
let component_resolved_url = RoutingTest::resolved_url(&component_name);
let namespace = routing_test.mock_runner.get_namespace(&component_resolved_url).unwrap();
call_work_scheduler_control_svc_from_namespace(&namespace, path.clone(), should_succeed).await;
}
/// a
/// \
/// b
///
/// b: uses framework service /svc/fuchsia.sys2.WorkScheduler while exposing
/// /svc/fuchsia.sys2.Worker to framework
#[fuchsia_async::run_singlethreaded(test)]
async fn use_work_scheduler_with_expose_to_framework() {
let components = vec![
("a", ComponentDeclBuilder::new().add_lazy_child("b").build()),
(
"b",
ComponentDeclBuilder::new()
.protocol(
ProtocolDeclBuilder::new("fuchsia.sys2.Worker")
.path("/svc/fuchsia.sys2.Worker")
.build(),
)
.expose(ExposeDecl::Protocol(ExposeProtocolDecl {
source: ExposeSource::Self_,
source_name: "fuchsia.sys2.Worker".into(),
target_name: "fuchsia.sys2.Worker".into(),
target: ExposeTarget::Framework,
}))
.use_(UseDecl::Protocol(UseProtocolDecl {
source: UseSource::Framework,
source_name: "fuchsia.sys2.WorkScheduler".into(),
target_path: CapabilityPath::try_from("/svc/fuchsia.sys2.WorkScheduler")
.unwrap(),
}))
.build(),
),
];
let work_scheduler = new_work_scheduler().await;
let test =
RoutingTestBuilder::new("a", components).add_hooks(work_scheduler.hooks()).build().await;
check_use_work_scheduler(&test, vec!["b:0"].into(), true).await;
}
/// a
/// \
/// b
///
/// b: uses framework service /svc/fuchsia.sys2.WorkScheduler without exposing
/// /svc/fuchsia.sys2.Worker
#[fuchsia_async::run_singlethreaded(test)]
async fn use_work_scheduler_without_expose() {
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.WorkScheduler".into(),
target_path: CapabilityPath::try_from("/svc/fuchsia.sys2.WorkScheduler")
.unwrap(),
}))
.build(),
),
];
let work_scheduler = new_work_scheduler().await;
let test =
RoutingTestBuilder::new("a", components).add_hooks(work_scheduler.hooks()).build().await;
check_use_work_scheduler(&test, vec!["b:0"].into(), false).await;
}
/// a
/// \
/// b
///
/// b: uses framework service /svc/fuchsia.sys2.WorkScheduler while exposing
/// /svc/fuchsia.sys2.Worker to realm (not framework)
#[fuchsia_async::run_singlethreaded(test)]
async fn use_work_scheduler_with_expose_to_realm() {
let components = vec![
("a", ComponentDeclBuilder::new().add_lazy_child("b").build()),
(
"b",
ComponentDeclBuilder::new()
.protocol(
ProtocolDeclBuilder::new("fuchsia.sys2.Worker")
.path("/svc/fuchsia.sys2.Worker")
.build(),
)
.expose(ExposeDecl::Protocol(ExposeProtocolDecl {
source: ExposeSource::Self_,
source_name: "fuchsia.sys2.Worker".into(),
target_name: "fuchsia.sys2.Worker".into(),
target: ExposeTarget::Parent,
}))
.use_(UseDecl::Protocol(UseProtocolDecl {
source: UseSource::Framework,
source_name: "fuchsia.sys2.WorkScheduler".into(),
target_path: CapabilityPath::try_from("/svc/fuchsia.sys2.WorkScheduler")
.unwrap(),
}))
.build(),
),
];
let work_scheduler = new_work_scheduler().await;
let test =
RoutingTestBuilder::new("a", components).add_hooks(work_scheduler.hooks()).build().await;
check_use_work_scheduler(&test, vec!["b:0"].into(), false).await;
}
/// a
/// \
/// b
///
/// b: uses WorkSchedulerControl offered by by a
#[fuchsia_async::run_singlethreaded(test)]
async fn use_work_scheduler_control_routed() {
let offer_use_name = CapabilityName::from("WorkSchedulerControl");
let use_path = CapabilityPath::try_from("/svc/WorkSchedulerControl").unwrap();
let components = vec![
(
"a",
ComponentDeclBuilder::new()
.offer(OfferDecl::Protocol(OfferProtocolDecl {
source: OfferServiceSource::Parent,
source_name: "fuchsia.sys2.WorkSchedulerControl".into(),
target_name: offer_use_name.clone().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: offer_use_name.clone().into(),
target_path: use_path.clone(),
}))
.build(),
),
];
let work_scheduler = new_work_scheduler().await;
let test =
RoutingTestBuilder::new("a", components).add_hooks(work_scheduler.hooks()).build().await;
check_use_work_scheduler_control(&test, vec!["b:0"].into(), use_path.clone(), true).await;
}
/// a
/// \
/// b
///
/// b: uses framework service /svc/fuchsia.sys2.WorkSchedulerControl from framework (not allowed)
#[fuchsia_async::run_singlethreaded(test)]
async fn use_work_scheduler_control_error() {
let offer_use_name = CapabilityName::from("WorkSchedulerControl");
let use_path = CapabilityPath::try_from("/svc/WorkSchedulerControl").unwrap();
let components = vec![
(
"a",
ComponentDeclBuilder::new()
.offer(OfferDecl::Protocol(OfferProtocolDecl {
source: OfferServiceSource::Parent,
source_name: "fuchsia.sys2.WorkSchedulerControl".into(),
target_name: offer_use_name.clone().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: offer_use_name.clone().into(),
target_path: use_path.clone(),
}))
.build(),
),
];
let work_scheduler = new_work_scheduler().await;
let test =
RoutingTestBuilder::new("a", components).add_hooks(work_scheduler.hooks()).build().await;
check_use_work_scheduler_control(&test, vec!["b:0"].into(), use_path.clone(), false).await;
}