| // 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 { |
| anyhow::{format_err, Error}, |
| fidl_fidl_examples_routing_echo as fecho, fidl_fuchsia_data as fdata, fuchsia_async as fasync, |
| fuchsia_component::server as fserver, |
| fuchsia_component_test::{builder::*, mock, Moniker}, |
| futures::{channel::oneshot, lock::Mutex, StreamExt, TryStreamExt}, |
| std::sync::Arc, |
| }; |
| |
| const V1_ECHO_CLIENT_URL: &'static str = |
| "fuchsia-pkg://fuchsia.com/fuchsia-component-test-tests#meta/echo_client.cmx"; |
| const V2_ECHO_CLIENT_ABSOLUTE_URL: &'static str = |
| "fuchsia-pkg://fuchsia.com/fuchsia-component-test-tests#meta/echo_client.cm"; |
| const V2_ECHO_CLIENT_RELATIVE_URL: &'static str = "#meta/echo_client.cm"; |
| |
| const V1_ECHO_SERVER_URL: &'static str = |
| "fuchsia-pkg://fuchsia.com/fuchsia-component-test-tests#meta/echo_server.cmx"; |
| const V2_ECHO_SERVER_ABSOLUTE_URL: &'static str = |
| "fuchsia-pkg://fuchsia.com/fuchsia-component-test-tests#meta/echo_server.cm"; |
| const V2_ECHO_SERVER_RELATIVE_URL: &'static str = "#meta/echo_server.cm"; |
| |
| const DEFAULT_ECHO_STR: &'static str = "Hippos rule!"; |
| |
| #[fasync::run_singlethreaded(test)] |
| async fn protocol_with_uncle_test() -> Result<(), Error> { |
| let (send_echo_server_called, receive_echo_server_called) = oneshot::channel(); |
| let sender = Arc::new(Mutex::new(Some(send_echo_server_called))); |
| |
| let mut builder = RealmBuilder::new().await?; |
| builder |
| .add_component( |
| "echo-server", |
| ComponentSource::mock(move |mock_handles: mock::MockHandles| { |
| Box::pin(echo_server_mock(DEFAULT_ECHO_STR, sender.clone(), mock_handles)) |
| }), |
| ) |
| .await? |
| .add_eager_component( |
| "parent/echo-client", |
| ComponentSource::url(V2_ECHO_CLIENT_ABSOLUTE_URL), |
| ) |
| .await? |
| .add_route(CapabilityRoute { |
| capability: Capability::protocol("fidl.examples.routing.echo.Echo"), |
| source: RouteEndpoint::component("echo-server"), |
| targets: vec![RouteEndpoint::component("parent/echo-client")], |
| })? |
| .add_route(CapabilityRoute { |
| capability: Capability::protocol("fuchsia.logger.LogSink"), |
| source: RouteEndpoint::AboveRoot, |
| targets: vec![ |
| RouteEndpoint::component("echo-server"), |
| RouteEndpoint::component("parent/echo-client"), |
| ], |
| })?; |
| let _child_instance = builder.build().create().await?; |
| |
| receive_echo_server_called.await?; |
| Ok(()) |
| } |
| |
| #[fasync::run_singlethreaded(test)] |
| async fn protocol_with_siblings_test() -> Result<(), Error> { |
| let (send_echo_server_called, receive_echo_server_called) = oneshot::channel(); |
| let sender = Arc::new(Mutex::new(Some(send_echo_server_called))); |
| |
| let mut builder = RealmBuilder::new().await?; |
| builder |
| .add_eager_component("echo-client", ComponentSource::url(V2_ECHO_CLIENT_ABSOLUTE_URL)) |
| .await? |
| .add_component( |
| "echo-server", |
| ComponentSource::mock(move |mock_handles: mock::MockHandles| { |
| Box::pin(echo_server_mock(DEFAULT_ECHO_STR, sender.clone(), mock_handles)) |
| }), |
| ) |
| .await? |
| .add_route(CapabilityRoute { |
| capability: Capability::protocol("fidl.examples.routing.echo.Echo"), |
| source: RouteEndpoint::component("echo-server"), |
| targets: vec![RouteEndpoint::component("echo-client")], |
| })? |
| .add_route(CapabilityRoute { |
| capability: Capability::protocol("fuchsia.logger.LogSink"), |
| source: RouteEndpoint::AboveRoot, |
| targets: vec![ |
| RouteEndpoint::component("echo-client"), |
| RouteEndpoint::component("echo-server"), |
| ], |
| })?; |
| let _child_instance = builder.build().create().await?; |
| |
| receive_echo_server_called.await?; |
| Ok(()) |
| } |
| |
| #[fasync::run_singlethreaded(test)] |
| async fn protocol_with_cousins_test() -> Result<(), Error> { |
| let (send_echo_server_called, receive_echo_server_called) = oneshot::channel(); |
| let sender = Arc::new(Mutex::new(Some(send_echo_server_called))); |
| |
| let mut builder = RealmBuilder::new().await?; |
| builder |
| .add_eager_component( |
| "parent-1/echo-client", |
| ComponentSource::url(V2_ECHO_CLIENT_ABSOLUTE_URL), |
| ) |
| .await? |
| .add_component( |
| "parent-2/echo-server", |
| ComponentSource::mock(move |mock_handles: mock::MockHandles| { |
| Box::pin(echo_server_mock(DEFAULT_ECHO_STR, sender.clone(), mock_handles)) |
| }), |
| ) |
| .await? |
| .add_route(CapabilityRoute { |
| capability: Capability::protocol("fidl.examples.routing.echo.Echo"), |
| source: RouteEndpoint::component("parent-2/echo-server"), |
| targets: vec![RouteEndpoint::component("parent-1/echo-client")], |
| })? |
| .add_route(CapabilityRoute { |
| capability: Capability::protocol("fuchsia.logger.LogSink"), |
| source: RouteEndpoint::AboveRoot, |
| targets: vec![ |
| RouteEndpoint::component("parent-1/echo-client"), |
| RouteEndpoint::component("parent-2/echo-server"), |
| ], |
| })?; |
| let _child_instance = builder.build().create().await?; |
| |
| receive_echo_server_called.await?; |
| Ok(()) |
| } |
| |
| #[fasync::run_singlethreaded(test)] |
| async fn mock_component_with_a_child() -> Result<(), Error> { |
| let (send_echo_server_called, receive_echo_server_called) = oneshot::channel(); |
| let sender = Arc::new(Mutex::new(Some(send_echo_server_called))); |
| |
| let mut builder = RealmBuilder::new().await?; |
| builder |
| .add_component( |
| "echo-server", |
| ComponentSource::mock(move |mock_handles: mock::MockHandles| { |
| Box::pin(echo_server_mock(DEFAULT_ECHO_STR, sender.clone(), mock_handles)) |
| }), |
| ) |
| .await? |
| .add_eager_component( |
| "echo-server/echo-client", |
| ComponentSource::url(V2_ECHO_CLIENT_ABSOLUTE_URL), |
| ) |
| .await? |
| .add_route(CapabilityRoute { |
| capability: Capability::protocol("fidl.examples.routing.echo.Echo"), |
| source: RouteEndpoint::component("echo-server"), |
| targets: vec![RouteEndpoint::component("echo-server/echo-client")], |
| })? |
| .add_route(CapabilityRoute { |
| capability: Capability::protocol("fuchsia.logger.LogSink"), |
| source: RouteEndpoint::AboveRoot, |
| targets: vec![ |
| RouteEndpoint::component("echo-server"), |
| RouteEndpoint::component("echo-server/echo-client"), |
| ], |
| })?; |
| let _child_instance = builder.build().create().await?; |
| |
| receive_echo_server_called.await?; |
| Ok(()) |
| } |
| |
| #[fasync::run_singlethreaded(test)] |
| async fn relative_echo_realm() -> Result<(), Error> { |
| let mut builder = RealmBuilder::new().await?; |
| builder |
| .add_component(Moniker::root(), ComponentSource::url("#meta/echo_realm.cm")) |
| .await? |
| // This route will result in the imported echo_realm exposing this protocol, whereas before |
| // it only offered it to echo_client |
| .add_route(CapabilityRoute { |
| capability: Capability::protocol("fidl.examples.routing.echo.Echo"), |
| source: RouteEndpoint::component("echo_server"), |
| targets: vec![RouteEndpoint::above_root()], |
| })?; |
| let realm_instance = builder.build().create().await?; |
| |
| let echo_proxy = |
| realm_instance.root.connect_to_protocol_at_exposed_dir::<fecho::EchoMarker>()?; |
| assert_eq!(Some("hello".to_string()), echo_proxy.echo_string(Some("hello")).await?); |
| |
| Ok(()) |
| } |
| |
| #[fasync::run_singlethreaded(test)] |
| async fn altered_echo_client_args() -> Result<(), Error> { |
| let (send_echo_server_called, receive_echo_server_called) = oneshot::channel(); |
| let sender = Arc::new(Mutex::new(Some(send_echo_server_called))); |
| |
| let mut builder = RealmBuilder::new().await?; |
| builder |
| .add_component(Moniker::root(), ComponentSource::url("#meta/echo_realm.cm")) |
| .await? |
| .override_component( |
| "echo_server", |
| ComponentSource::mock(move |mock_handles: mock::MockHandles| { |
| Box::pin(echo_server_mock("Whales rule!", sender.clone(), mock_handles)) |
| }), |
| ) |
| .await? |
| // echo_realm already has the offer we need, but we still need to add this route so that |
| // the proper exposes are added to our mock component |
| .add_route(CapabilityRoute { |
| capability: Capability::protocol("fidl.examples.routing.echo.Echo"), |
| source: RouteEndpoint::component("echo_server"), |
| targets: vec![RouteEndpoint::component("echo_client")], |
| })?; |
| |
| // Change the program.args section of the manifest, to alter the string it will try to echo |
| let mut realm = builder.build(); |
| let mut echo_client_decl = realm.get_decl(&"echo_client".into()).await?; |
| for entry in echo_client_decl.program.as_mut().unwrap().info.entries.as_mut().unwrap() { |
| if entry.key.as_str() == "args" { |
| entry.value = Some(Box::new(fdata::DictionaryValue::StrVec(vec![ |
| "Whales".to_string(), |
| "rule!".to_string(), |
| ]))); |
| } |
| } |
| realm.set_component(&"echo_client".into(), echo_client_decl).await?; |
| let _realm_instance = realm.create().await?; |
| |
| receive_echo_server_called.await?; |
| |
| Ok(()) |
| } |
| |
| #[fasync::run_singlethreaded(test)] |
| async fn echo_clients() -> Result<(), Error> { |
| // This test runs a series of echo clients from different sources against a mock echo server, |
| // confirming that each client successfully connects to the server. |
| |
| let (send_echo_client_results, receive_echo_client_results) = oneshot::channel(); |
| let sender = Arc::new(Mutex::new(Some(send_echo_client_results))); |
| let client_sources = vec![ |
| ComponentSource::legacy_url(V1_ECHO_CLIENT_URL), |
| ComponentSource::url(V2_ECHO_CLIENT_ABSOLUTE_URL), |
| ComponentSource::url(V2_ECHO_CLIENT_RELATIVE_URL), |
| ComponentSource::mock(move |h| Box::pin(echo_client_mock(sender.clone(), h))), |
| ]; |
| |
| for client_source in client_sources { |
| let (send_echo_server_called, receive_echo_server_called) = oneshot::channel(); |
| let sender = Arc::new(Mutex::new(Some(send_echo_server_called))); |
| |
| let mut builder = RealmBuilder::new().await?; |
| builder |
| .add_component( |
| "echo-server", |
| ComponentSource::mock(move |h| { |
| Box::pin(echo_server_mock(DEFAULT_ECHO_STR, sender.clone(), h)) |
| }), |
| ) |
| .await? |
| .add_eager_component("echo-client", client_source) |
| .await? |
| .add_route(CapabilityRoute { |
| capability: Capability::protocol("fidl.examples.routing.echo.Echo"), |
| source: RouteEndpoint::component("echo-server"), |
| targets: vec![RouteEndpoint::component("echo-client")], |
| })? |
| .add_route(CapabilityRoute { |
| capability: Capability::protocol("fuchsia.logger.LogSink"), |
| source: RouteEndpoint::AboveRoot, |
| targets: vec![ |
| RouteEndpoint::component("echo-server"), |
| RouteEndpoint::component("echo-client"), |
| ], |
| })?; |
| |
| let _child_instance = builder.build().create().await?; |
| |
| receive_echo_server_called.await?; |
| } |
| |
| receive_echo_client_results.await?; |
| Ok(()) |
| } |
| |
| #[fasync::run_singlethreaded(test)] |
| async fn echo_servers() -> Result<(), Error> { |
| // This test runs a series of echo servers from different sources against a mock echo client, |
| // confirming that the client can successfully connect to and use each server. |
| |
| let (send_echo_server_called, receive_echo_server_called) = oneshot::channel(); |
| let sender = Arc::new(Mutex::new(Some(send_echo_server_called))); |
| |
| let server_sources = vec![ |
| ComponentSource::legacy_url(V1_ECHO_SERVER_URL), |
| ComponentSource::url(V2_ECHO_SERVER_ABSOLUTE_URL), |
| ComponentSource::url(V2_ECHO_SERVER_RELATIVE_URL), |
| ComponentSource::mock(move |h| { |
| Box::pin(echo_server_mock(DEFAULT_ECHO_STR, sender.clone(), h)) |
| }), |
| ]; |
| |
| for server_source in server_sources { |
| let (send_echo_client_results, receive_echo_client_results) = oneshot::channel(); |
| let sender = Arc::new(Mutex::new(Some(send_echo_client_results))); |
| |
| let mut builder = RealmBuilder::new().await?; |
| builder |
| .add_component("echo-server", server_source) |
| .await? |
| .add_eager_component( |
| "echo-client", |
| ComponentSource::mock(move |h| Box::pin(echo_client_mock(sender.clone(), h))), |
| ) |
| .await? |
| .add_route(CapabilityRoute { |
| capability: Capability::protocol("fidl.examples.routing.echo.Echo"), |
| source: RouteEndpoint::component("echo-server"), |
| targets: vec![RouteEndpoint::component("echo-client")], |
| })? |
| .add_route(CapabilityRoute { |
| capability: Capability::protocol("fuchsia.logger.LogSink"), |
| source: RouteEndpoint::AboveRoot, |
| targets: vec![ |
| RouteEndpoint::component("echo-server"), |
| RouteEndpoint::component("echo-client"), |
| ], |
| })?; |
| |
| let _child_instance = builder.build().create().await?; |
| |
| receive_echo_client_results.await?; |
| } |
| |
| receive_echo_server_called.await?; |
| Ok(()) |
| } |
| |
| // A mock echo server implementation, that will crash if it doesn't receive anything other than the |
| // contents of `expected_echo_str`. It takes and sends a message over `send_echo_server_called` |
| // once it receives one echo request. |
| async fn echo_server_mock( |
| expected_echo_string: &'static str, |
| send_echo_server_called: Arc<Mutex<Option<oneshot::Sender<()>>>>, |
| mock_handles: mock::MockHandles, |
| ) -> Result<(), Error> { |
| let mut fs = fserver::ServiceFs::new(); |
| let mut tasks = vec![]; |
| fs.dir("svc").add_fidl_service(move |mut stream: fecho::EchoRequestStream| { |
| let send_echo_server_called = send_echo_server_called.clone(); |
| tasks.push(fasync::Task::local(async move { |
| while let Some(fecho::EchoRequest::EchoString { value, responder }) = |
| stream.try_next().await.expect("failed to serve echo service") |
| { |
| assert_eq!(Some(expected_echo_string.to_string()), value); |
| responder.send(value.as_ref().map(|s| &**s)).expect("failed to send echo response"); |
| send_echo_server_called |
| .lock() |
| .await |
| .take() |
| .unwrap() |
| .send(()) |
| .expect("failed to send results"); |
| } |
| })); |
| }); |
| fs.serve_connection(mock_handles.outgoing_dir.into_channel())?; |
| fs.collect::<()>().await; |
| Ok(()) |
| } |
| |
| async fn echo_client_mock( |
| send_echo_client_results: Arc<Mutex<Option<oneshot::Sender<()>>>>, |
| mock_handles: mock::MockHandles, |
| ) -> Result<(), Error> { |
| let echo = mock_handles.connect_to_service::<fecho::EchoMarker>()?; |
| let out = echo.echo_string(Some(DEFAULT_ECHO_STR)).await?; |
| send_echo_client_results.lock().await.take().unwrap().send(()).expect("failed to send results"); |
| if Some(DEFAULT_ECHO_STR.to_string()) != out { |
| return Err(format_err!("unexpected echo result: {:?}", out)); |
| } |
| Ok(()) |
| } |