fuchsia_fuzzctl/
manager.rs

1// Copyright 2022 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use anyhow::{bail, Context as _, Result};
6use fidl::endpoints::{create_proxy, ProtocolMarker};
7use url::Url;
8use {fidl_fuchsia_fuzzer as fuzz, zx_status as zx};
9
10/// Represents the FIDL connection from the `ffx fuzz` plugin to the `fuzz-manager` component on a
11/// target device.
12pub struct Manager {
13    proxy: fuzz::ManagerProxy,
14}
15
16impl Manager {
17    /// Creates a new `Manager`.
18    ///
19    /// The created object maintains a FIDL `proxy` to the `fuzz-manager` component on a target
20    /// device. Any output produced by this object will be written using the given `writer`.
21    pub fn new(proxy: fuzz::ManagerProxy) -> Self {
22        Self { proxy }
23    }
24
25    /// Requests that the `fuzz-manager` connect to a fuzzer instance.
26    ///
27    /// This will create and connect a `fuchsia.fuzzer.Controller` to the fuzzer on the target
28    /// device given by the `url`. Any artifacts produced by the fuzzer will be saved to the
29    /// `artifact_dir`, and outputs such as logs can optionally be saved to the `output_dir`.
30    ///
31    /// Returns an object representing the connected fuzzer, or an error.
32    pub async fn connect(&self, url: &Url) -> Result<fuzz::ControllerProxy> {
33        let (proxy, server_end) = create_proxy::<fuzz::ControllerMarker>();
34        let result = self
35            .proxy
36            .connect(url.as_str(), server_end)
37            .await
38            .context("fuchsia.fuzzer/Manager.Connect")?;
39        if let Err(e) = result {
40            bail!("fuchsia.fuzzer/Manager.Connect returned ZX_ERR_{}", zx::Status::from_raw(e));
41        }
42        Ok(proxy)
43    }
44
45    /// Returns a socket that provides the given type of fuzzer output.
46    pub async fn get_output(&self, url: &Url, output: fuzz::TestOutput) -> Result<fidl::Socket> {
47        let (rx, tx) = fidl::Socket::create_stream();
48        let result = self
49            .proxy
50            .get_output(url.as_str(), output, tx)
51            .await
52            .context("failed to get output")?;
53        if let Err(e) = result {
54            bail!("fuchsia.fuzzer/Manager.GetOutput returned ZX_ERR_{}", zx::Status::from_raw(e));
55        }
56        Ok(rx)
57    }
58
59    /// Requests that the `fuzz-manager` stop a running fuzzer instance.
60    ///
61    /// As a result of this call, the fuzzer component will cease an ongoing workflow and exit.
62    ///
63    /// Returns whether a fuzzer was stopped.
64    pub async fn stop(&self, url: &Url) -> Result<bool> {
65        let result = self.proxy.stop(url.as_str()).await.context(fidl_name("Stop"))?;
66        match result {
67            Ok(()) => Ok(true),
68            Err(e) if e == zx::Status::NOT_FOUND.into_raw() => Ok(false),
69            Err(e) => {
70                bail!("fuchsia.fuzzer/Manager.Stop returned ZX_ERR_{}", zx::Status::from_raw(e))
71            }
72        }
73    }
74}
75
76fn fidl_name(method: &str) -> String {
77    format!("{}/{}", fuzz::ManagerMarker::DEBUG_NAME, method)
78}
79
80#[cfg(test)]
81mod tests {
82    use super::Manager;
83    use anyhow::Result;
84    use fidl::endpoints::create_proxy;
85    use fidl_fuchsia_fuzzer as fuzz;
86    use fuchsia_fuzzctl_test::{create_task, serve_manager, Test, TEST_URL};
87    use url::Url;
88
89    #[fuchsia::test]
90    async fn test_connect() -> Result<()> {
91        let test = Test::try_new()?;
92        let (proxy, server_end) = create_proxy::<fuzz::ManagerMarker>();
93        let _task = create_task(serve_manager(server_end, test.clone()), test.writer());
94        let manager = Manager::new(proxy);
95
96        let url = Url::parse(TEST_URL)?;
97        manager.connect(&url).await?;
98
99        let actual = test.url().borrow().as_ref().map(|url| url.to_string());
100        let expected = Some(url.to_string());
101        assert_eq!(actual, expected);
102        Ok(())
103    }
104
105    #[fuchsia::test]
106    async fn test_stop() -> Result<()> {
107        let test = Test::try_new()?;
108        let (proxy, server_end) = create_proxy::<fuzz::ManagerMarker>();
109        let _task = create_task(serve_manager(server_end, test.clone()), test.writer());
110        let manager = Manager::new(proxy);
111
112        let url = Url::parse(TEST_URL)?;
113        manager.stop(&url).await?;
114
115        let actual = test.url().borrow().as_ref().map(|url| url.to_string());
116        let expected = Some(url.to_string());
117        assert_eq!(actual, expected);
118        Ok(())
119    }
120}