blob: 43f5d3dbcbb75097293135b6fb36c7e4a101332c [file] [log] [blame]
// Copyright 2021 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use {
anyhow::{format_err, Context, Error},
component_events::{
events::{Discovered, Event, EventSource, EventSubscription, Started},
matcher::EventMatcher,
sequence::*,
},
fidl::endpoints::ServiceMarker,
fidl_fuchsia_examples_services as fexamples, fidl_fuchsia_sys2 as fsys2,
fuchsia_component::client,
fuchsia_component_test::ScopedInstance,
tracing::*,
};
/// Name of the collection that contains branch components.
const BRANCHES_COLLECTION: &str = "branches";
/// Component URL of the branch component.
const BRANCH_COMPONENT_URL: &str = "#meta/service-routing-branch.cm";
/// Name of the collection in the branch component that contains BankAccount service providers.
const ACCOUNT_PROVIDERS_COLLECTION: &str = "account_providers";
/// Name of the provider-a.cm child component in the branch.
const PROVIDER_A_NAME: &str = "a";
/// Name of the provider-b.cm child component in the branch.
const PROVIDER_B_NAME: &str = "b";
/// Path to the LifecycleController protocol in the hub.
const LIFECYCLE_CONTROLLER_HUB_PATH: &str = "/hub/debug/fuchsia.sys2.LifecycleController";
#[fuchsia::test]
async fn list_instances_test() {
let branch = start_branch().await.expect("failed to start branch component");
start_provider(&branch, PROVIDER_A_NAME).await.expect("failed to start provider a");
start_provider(&branch, PROVIDER_B_NAME).await.expect("failed to start provider b");
// List the instances in the BankAccount service.
let service_dir = io_util::directory::open_directory(
branch.get_exposed_dir(),
fexamples::BankAccountMarker::SERVICE_NAME,
io_util::OPEN_RIGHT_READABLE,
)
.await
.expect("failed to open service dir");
let instances = files_async::readdir(&service_dir)
.await
.expect("failed to read entries from service dir")
.into_iter()
.map(|dirent| dirent.name);
assert_eq!(2, instances.len());
}
#[fuchsia::test]
async fn connect_to_instances_test() {
let branch = start_branch().await.expect("failed to start branch component");
start_provider(&branch, PROVIDER_A_NAME).await.expect("failed to start provider a");
start_provider(&branch, PROVIDER_B_NAME).await.expect("failed to start provider b");
// List the instances in the BankAccount service.
let service_dir = io_util::directory::open_directory(
branch.get_exposed_dir(),
fexamples::BankAccountMarker::SERVICE_NAME,
io_util::OPEN_RIGHT_READABLE,
)
.await
.expect("failed to open service dir");
let instances = files_async::readdir(&service_dir)
.await
.expect("failed to read entries from service dir")
.into_iter()
.map(|dirent| dirent.name);
// Connect to every instance and ensure the protocols are functional.
for instance in instances {
let proxy = client::connect_to_service_instance_at_dir::<fexamples::BankAccountMarker>(
branch.get_exposed_dir(),
&instance,
)
.expect("failed to connect to service instance");
let read_only_account = proxy.read_only().expect("read_only protocol");
let owner = read_only_account.get_owner().await.expect("failed to get owner");
let initial_balance = read_only_account.get_balance().await.expect("failed to get_balance");
info!("retrieved account for owner '{}' with balance ${}", &owner, &initial_balance);
let read_write_account = proxy.read_write().expect("read_write protocol");
assert_eq!(read_write_account.get_owner().await.expect("failed to get_owner"), owner);
assert_eq!(
read_write_account.get_balance().await.expect("failed to get_balance"),
initial_balance
);
}
}
/// Starts a branch child component.
async fn start_branch() -> Result<ScopedInstance, Error> {
let event_source = EventSource::new()?;
let event_stream = event_source
.subscribe(vec![EventSubscription::new(vec![Discovered::NAME])])
.await
.context("failed to subscribe to EventSource")?;
let branch =
ScopedInstance::new(BRANCHES_COLLECTION.to_string(), BRANCH_COMPONENT_URL.to_string())
.await
.context("failed to create branch component instance")?;
branch.start_with_binder_sync().await?;
// Wait for the providers to be discovered (created) to ensure that
// subsequent calls to `start_provider` can start them.
EventSequence::new()
.has_subset(
vec![
EventMatcher::ok().r#type(Discovered::TYPE).moniker(format!(
"./{}:{}/{}:{}",
BRANCHES_COLLECTION,
branch.child_name(),
ACCOUNT_PROVIDERS_COLLECTION,
PROVIDER_A_NAME,
)),
EventMatcher::ok().r#type(Discovered::TYPE).moniker(format!(
"./{}:{}/{}:{}",
BRANCHES_COLLECTION,
branch.child_name(),
ACCOUNT_PROVIDERS_COLLECTION,
PROVIDER_B_NAME,
)),
],
Ordering::Unordered,
)
.expect(event_stream)
.await
.context("event sequence did not match expected")?;
Ok(branch)
}
/// Starts the provider with the name `child_name` in the branch component.
async fn start_provider(branch: &ScopedInstance, child_name: &str) -> Result<(), Error> {
let lifecycle_controller_proxy = client::connect_to_protocol_at_path::<
fsys2::LifecycleControllerMarker,
>(LIFECYCLE_CONTROLLER_HUB_PATH)
.context("failed to connect to LifecycleController")?;
let event_source = EventSource::new()?;
let event_stream = event_source
.subscribe(vec![EventSubscription::new(vec![Started::NAME])])
.await
.context("failed to subscribe to EventSource")?;
let provider_moniker = format!(
"./{}:{}/{}:{}",
BRANCHES_COLLECTION,
branch.child_name(),
ACCOUNT_PROVIDERS_COLLECTION,
child_name,
);
// Start the provider child.
lifecycle_controller_proxy
.start(&provider_moniker)
.await?
.map_err(|err| format_err!("failed to start provider component: {:?}", err))?;
// Wait for the provider to start.
EventSequence::new()
.has_subset(
vec![EventMatcher::ok().r#type(Started::TYPE).moniker(provider_moniker)],
Ordering::Unordered,
)
.expect(event_stream)
.await
.context("event sequence did not match expected")?;
Ok(())
}