| // 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 fidl_fuchsia_examples_inspect::{ReverserRequest, ReverserRequestStream}; |
| use fuchsia_async as fasync; |
| use fuchsia_inspect::{self as inspect, NumericProperty}; |
| use futures::TryStreamExt; |
| use std::sync::Arc; |
| |
| pub struct ReverserServerFactory { |
| node: inspect::Node, |
| request_count: Arc<inspect::UintProperty>, |
| connection_count: inspect::UintProperty, |
| } |
| |
| impl ReverserServerFactory { |
| pub fn new(node: inspect::Node) -> Self { |
| let request_count = Arc::new(node.create_uint("total_requests", 0)); |
| let connection_count = node.create_uint("connection_count", 0); |
| Self { node, request_count, connection_count } |
| } |
| |
| pub fn spawn_new(&self, stream: ReverserRequestStream) { |
| self.spawn_new_internal(stream, || {}) |
| } |
| |
| fn spawn_new_internal<F>(&self, stream: ReverserRequestStream, callback: F) |
| where |
| F: FnOnce() -> () + 'static, |
| { |
| let node = self.node.create_child(inspect::unique_name("connection")); |
| let metrics = ReverserServerMetrics::new(node, self.request_count.clone()); |
| ReverserServer::new(metrics).spawn(stream, callback); |
| self.connection_count.add(1); |
| } |
| } |
| |
| // The default implementation of ReverserServerMetrics is a no-op on the inspect properties. |
| #[derive(Default)] |
| struct ReverserServerMetrics { |
| global_request_count: Arc<inspect::UintProperty>, |
| request_count: inspect::UintProperty, |
| response_count: inspect::UintProperty, |
| _node: inspect::Node, |
| } |
| |
| impl ReverserServerMetrics { |
| fn new(node: inspect::Node, global_request_count: Arc<inspect::UintProperty>) -> Self { |
| let request_count = node.create_uint("request_count", 0); |
| let response_count = node.create_uint("response_count", 0); |
| let metrics = Self { |
| global_request_count, |
| // Hold to the node until we are done with this connection. |
| _node: node, |
| request_count, |
| response_count, |
| }; |
| metrics |
| } |
| } |
| |
| struct ReverserServer { |
| metrics: ReverserServerMetrics, |
| } |
| |
| impl ReverserServer { |
| fn new(metrics: ReverserServerMetrics) -> Self { |
| Self { metrics } |
| } |
| |
| pub fn spawn<F>(self, mut stream: ReverserRequestStream, test_on_done: F) |
| where |
| F: FnOnce() -> () + 'static, |
| { |
| fasync::Task::local(async move { |
| let _ = &self; |
| while let Some(request) = stream.try_next().await.expect("serve reverser") { |
| self.metrics.request_count.add(1); |
| self.metrics.global_request_count.add(1); |
| |
| let ReverserRequest::Reverse { input, responder } = request; |
| let result = input.chars().rev().collect::<String>(); |
| responder.send(&result).expect("send reverse request response"); |
| |
| self.metrics.response_count.add(1); |
| } |
| test_on_done(); |
| }) |
| .detach(); |
| } |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use super::*; |
| use anyhow::Error; |
| use diagnostics_assertions::assert_data_tree; |
| use fidl_fuchsia_examples_inspect::{ReverserMarker, ReverserProxy}; |
| use futures::channel::oneshot; |
| |
| #[fuchsia::test] |
| async fn test_reverser() -> Result<(), Error> { |
| let inspector = inspect::Inspector::default(); |
| |
| let node = inspector.root().create_child("reverser_service"); |
| let factory = ReverserServerFactory::new(node); |
| |
| let (channel_closed_snd_1, channel_closed_rcv_1) = oneshot::channel::<()>(); |
| let reverser1 = open_reverser(&factory, || { |
| channel_closed_snd_1.send(()).expect("send close event"); |
| })?; |
| |
| let (channel_closed_snd_2, channel_closed_rcv_2) = oneshot::channel::<()>(); |
| let reverser2 = open_reverser(&factory, || { |
| channel_closed_snd_2.send(()).expect("send close event"); |
| })?; |
| |
| let result = reverser1.reverse("hello").await?; |
| assert_eq!(result, "olleh"); |
| |
| let result = reverser1.reverse("world").await?; |
| assert_eq!(result, "dlrow"); |
| |
| let result = reverser2.reverse("another").await?; |
| assert_eq!(result, "rehtona"); |
| |
| assert_data_tree!(inspector, root: { |
| reverser_service: { |
| total_requests: 3u64, |
| connection_count: 2u64, |
| "connection0": { |
| request_count: 2u64, |
| response_count: 2u64, |
| }, |
| "connection1": { |
| request_count: 1u64, |
| response_count: 1u64, |
| }, |
| } |
| }); |
| |
| drop(reverser1); |
| channel_closed_rcv_1.await?; |
| |
| assert_data_tree!(inspector, root: { |
| reverser_service: { |
| total_requests: 3u64, |
| connection_count: 2u64, |
| "connection1": { |
| request_count: 1u64, |
| response_count: 1u64, |
| }, |
| } |
| }); |
| |
| drop(reverser2); |
| channel_closed_rcv_2.await?; |
| |
| Ok(()) |
| } |
| |
| fn open_reverser<F>( |
| factory: &ReverserServerFactory, |
| callback: F, |
| ) -> Result<ReverserProxy, Error> |
| where |
| F: FnOnce() -> () + 'static, |
| { |
| let (proxy, stream) = fidl::endpoints::create_proxy_and_stream::<ReverserMarker>(); |
| factory.spawn_new_internal(stream, callback); |
| Ok(proxy) |
| } |
| } |