| // 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(()) |
| } |