cargo_fixture/
client_fixture.rs

1use serde::Serialize;
2
3use crate::{
4    rpc_socket::{ConnectionType, Request, RpcSocket},
5    Error, Result,
6};
7
8/// An RPC client used from fixture code.
9///
10/// An instance is created using [`FixtureClient::connect()`].
11pub struct FixtureClient {
12    socket: RpcSocket,
13}
14
15impl FixtureClient {
16    /// Connect to the parent `cargo fixture` process.
17    pub async fn connect() -> Result<Self> {
18        RpcSocket::connect(ConnectionType::Fixture)
19            .await
20            .map(|socket| Self { socket })
21    }
22
23    /// Request that an environment variable be set for `cargo test`.
24    pub async fn set_env_var(
25        &mut self,
26        name: impl Into<String>,
27        value: impl Into<String>,
28    ) -> Result<()> {
29        let name = name.into();
30        let value = value.into();
31
32        if name.is_empty() || name.contains('=') || name.contains('\0') || value.contains('\0') {
33            return Err(Error::InvalidSetEnv);
34        }
35
36        let req = Request::SetEnv { name, value };
37        self.socket.call(req).await?.as_ok()
38    }
39
40    /// Request that multiple environment variables be set for `cargo test`.
41    pub async fn set_env_vars(
42        &mut self,
43        vars: impl IntoIterator<Item = (impl Into<String>, impl Into<String>)>,
44    ) -> Result<()> {
45        // TODO: batch this when bumping RPC version
46        for (name, value) in vars {
47            self.set_env_var(name, value).await?;
48        }
49
50        Ok(())
51    }
52
53    /// Set additional CLI arguments to be passed to `cargo test`.
54    ///
55    /// No that these are arguments intended for the `cargo test` command itself, to pass arguments to the test binary,
56    /// such as `--nocapture` or similar, use [`set_extra_test_binary_args()`][FixtureClient::set_extra_test_binary_args].
57    pub async fn set_extra_cargo_test_args(
58        &mut self,
59        args: impl IntoIterator<Item = impl Into<String>>,
60    ) -> Result<()> {
61        let req = Request::SetExtraTestArgs {
62            args: args.into_iter().map(Into::into).collect(),
63        };
64        self.socket.call(req).await?.as_ok()
65    }
66
67    /// Set additional CLI arguments to be passed to the test binary.
68    ///
69    /// When using CLI, these are usually passed via cargo using the `--` syntax, i.e. `cargo test -- args`...
70    pub async fn set_extra_test_binary_args(
71        &mut self,
72        args: impl IntoIterator<Item = impl Into<String>>,
73    ) -> Result<()> {
74        let req = Request::SetExtraHarnessArgs {
75            args: args.into_iter().map(Into::into).collect(),
76        };
77        self.socket.call(req).await?.as_ok()
78    }
79
80    /// Set a value in `cargo fixture`'s in-memory K-V storage.
81    ///
82    /// The value can be any serde-serializable value. After set, it can be retrieved by the test code.
83    ///
84    /// The K-V store internally uses JSON representation.
85    pub async fn set_value(&mut self, key: impl Into<String>, value: impl Serialize) -> Result<()> {
86        let value = serde_json::to_value(value)?;
87        let req = Request::SetKeyValue {
88            key: key.into(),
89            value,
90        };
91        self.socket.call(req).await?.as_ok()
92    }
93
94    /// Replace the testing program to be executed to a custom one, along with arguments (if any).
95    ///
96    /// This will make `cargo fixture` run the provided program instead of the usual `cargo test` invocation.
97    pub async fn set_exec(
98        &mut self,
99        exec: impl IntoIterator<Item = impl Into<String>>,
100    ) -> Result<()> {
101        let req = Request::SetExec {
102            exec: exec.into_iter().map(Into::into).collect::<Vec<_>>(),
103        };
104        self.socket.call(req).await?.as_ok()
105    }
106
107    /// Signal to `cargo fixture` that the fixture is ready, starting the test run.
108    ///
109    /// This will by default run `cargo test` and return back a `bool` success status,
110    /// once the test run is complete. Note that it may take an arbitrarily long time.
111    pub async fn ready(&mut self) -> Result<bool> {
112        self.socket.call(Request::Ready).await?.as_tests_finished()
113    }
114}