From ebc239733c3a508f08f5539a862d3029d45c753f Mon Sep 17 00:00:00 2001 From: edouard Date: Mon, 5 Sep 2022 12:19:00 +0200 Subject: [PATCH] Add sandbox and daemon mock to utils mod --- gui/src/lib.rs | 1 + gui/src/utils/mock.rs | 93 ++++++++++++++++++++++++++++++++++++++++ gui/src/utils/mod.rs | 5 +++ gui/src/utils/sandbox.rs | 51 ++++++++++++++++++++++ 4 files changed, 150 insertions(+) create mode 100644 gui/src/utils/mock.rs create mode 100644 gui/src/utils/mod.rs create mode 100644 gui/src/utils/sandbox.rs diff --git a/gui/src/lib.rs b/gui/src/lib.rs index 36ca6d86..883f95ce 100644 --- a/gui/src/lib.rs +++ b/gui/src/lib.rs @@ -3,3 +3,4 @@ pub mod daemon; pub mod installer; pub mod loader; pub mod ui; +pub mod utils; diff --git a/gui/src/utils/mock.rs b/gui/src/utils/mock.rs new file mode 100644 index 00000000..05e3c01d --- /dev/null +++ b/gui/src/utils/mock.rs @@ -0,0 +1,93 @@ +use crate::daemon::{client::Client, DaemonError}; +use minisafe::config::Config; +use serde::{de::DeserializeOwned, Serialize}; +use serde_json::{json, Value}; +use std::fmt::Debug; +use std::sync::{ + mpsc::{channel, Receiver, Sender}, + Mutex, +}; +use std::thread; + +#[derive(Debug)] +pub struct DaemonClient { + transport: Mutex<(Sender, Receiver>)>, +} + +impl Client for DaemonClient { + type Error = DaemonError; + fn request( + &self, + method: &str, + params: Option, + ) -> Result { + let req = json!({"method": method, "params": params}); + let connection = self.transport.lock().expect("Failed to unlock"); + connection + .0 + .send(req) + .expect("Mock client failed to send request"); + connection + .1 + .recv() + .expect("Mock client failed to receive response") + .map(|value| serde_json::from_value(value).unwrap()) + } +} + +pub struct Daemon { + requests: Vec<(Option, Result)>, +} + +impl Daemon { + pub fn new(requests: Vec<(Option, Result)>) -> Self { + Self { requests } + } + + pub fn run(self) -> DaemonClient { + let (client_sender, daemon_receiver) = channel(); + let (daemon_sender, client_receiver) = channel(); + + thread::spawn(move || { + let mut requests = self.requests.into_iter(); + while let Ok(msg) = daemon_receiver.recv() { + let request = requests + .next() + .expect("Mock Daemon must have all requests mocked in the right order"); + if let Some(body) = request.0 { + assert_eq!(body, msg); + } + daemon_sender + .send(request.1) + .expect("Mock daemon failed to send response") + } + // close the daemon -> client channel after + // the client -> daemon channel is closed. + // (client -> daemon channel is closed when DaemonClient is dropped) + drop(daemon_sender); + // Readable with `cargo test -- --nocapture` + println!("The daemon has stopped!"); + }); + + DaemonClient { + transport: Mutex::new((client_sender, client_receiver)), + } + } +} + +pub fn fake_daemon_config() -> Config { + toml::from_str( +r#" +data_dir = "/home/edouard/code/revault/demo/minisafe/datadir" +main_descriptor = "wsh(or_d(pk(tpubDCbK3Ysvk8HjcF6mPyrgMu3KgLiaaP19RjKpNezd8GrbAbNg6v5BtWLaCt8FNm6QkLseopKLf5MNYQFtochDTKHdfgG6iqJ8cqnLNAwtXuP/*),and_v(v:pkh(tpubDDtb2WPYwEWw2WWDV7reLV348iJHw2HmhzvPysKKrJw3hYmvrd4jasyoioVPdKGQqjyaBMEvTn1HvHWDSVqQ6amyyxRZ5YjpPBBGjJ8yu8S/*),older(100))))#459t6xxr" + +[bitcoin_config] +network = "regtest" +poll_interval_secs = 30 + +[bitcoind_config] +addr = "127.0.0.1:9001" +cookie_path = "/home/edouard/code/revault/demo/minisafe/regtest/bcdir1/regtest/.cookie" +"# + ).unwrap() +} diff --git a/gui/src/utils/mod.rs b/gui/src/utils/mod.rs new file mode 100644 index 00000000..084f16d9 --- /dev/null +++ b/gui/src/utils/mod.rs @@ -0,0 +1,5 @@ +#[cfg(test)] +pub mod sandbox; + +#[cfg(test)] +pub mod mock; diff --git a/gui/src/utils/sandbox.rs b/gui/src/utils/sandbox.rs new file mode 100644 index 00000000..4c8defd2 --- /dev/null +++ b/gui/src/utils/sandbox.rs @@ -0,0 +1,51 @@ +use std::sync::Arc; + +use iced_native::command::Action; + +use crate::{ + app::{cache::Cache, message::Message, state::State}, + daemon::Daemon, +}; + +pub struct Sandbox { + state: S, +} + +impl Sandbox { + pub fn new(state: S) -> Self { + return Self { state }; + } + + pub fn state(&self) -> &S { + &self.state + } + + pub async fn update( + mut self, + daemon: Arc, + cache: &Cache, + message: Message, + ) -> Self { + let cmd = self.state.update(daemon.clone(), cache, message); + for action in cmd.actions() { + if let Action::Future(f) = action { + let msg = f.await; + let _cmd = self.state.update(daemon.clone(), cache, msg); + } + } + + self + } + + pub async fn load(mut self, daemon: Arc, cache: &Cache) -> Self { + let cmd = self.state.load(daemon.clone()); + for action in cmd.actions() { + if let Action::Future(f) = action { + let msg = f.await; + self = self.update(daemon.clone(), cache, msg).await; + } + } + + self + } +}