diff options
| author | Janito Vaqueiro Ferreira Filho <janito@mullvad.net> | 2018-05-23 05:52:17 -0300 |
|---|---|---|
| committer | Janito Vaqueiro Ferreira Filho <janito@mullvad.net> | 2018-06-20 08:08:31 -0300 |
| commit | fde8ae1f62b44c9c30081d783dba8b810482ca1a (patch) | |
| tree | 93f6510cc06dc4f574c6f99af80907b370fa8fe6 | |
| parent | 77c146075b293fa0095fc54cecaaa5da8ce27f26 (diff) | |
| download | mullvadvpn-fde8ae1f62b44c9c30081d783dba8b810482ca1a.tar.xz mullvadvpn-fde8ae1f62b44c9c30081d783dba8b810482ca1a.zip | |
Move test `common` module to `mullvad-tests` lib.
| -rw-r--r-- | mullvad-tests/Cargo.toml | 6 | ||||
| -rw-r--r-- | mullvad-tests/src/lib.rs | 219 | ||||
| -rw-r--r-- | mullvad-tests/tests/common/mod.rs | 220 | ||||
| -rw-r--r-- | mullvad-tests/tests/startup.rs | 8 |
4 files changed, 223 insertions, 230 deletions
diff --git a/mullvad-tests/Cargo.toml b/mullvad-tests/Cargo.toml index 71dc7b8663..ae57e6cdc7 100644 --- a/mullvad-tests/Cargo.toml +++ b/mullvad-tests/Cargo.toml @@ -6,14 +6,12 @@ description = "Mullvad test specific modules and binaries" license = "GPL-3.0" [dependencies] -notify = "4.0" - -[dev-dependencies] duct = "0.10" mullvad-ipc-client = { path = "../mullvad-ipc-client" } mullvad-paths = { path = "../mullvad-paths" } +notify = "4.0" os_pipe = "0.6" tempfile = "3.0" -[target.'cfg(unix)'.dev-dependencies] +[target.'cfg(unix)'.dependencies] libc = "0.2" diff --git a/mullvad-tests/src/lib.rs b/mullvad-tests/src/lib.rs index 8b13789179..5489c5b23e 100644 --- a/mullvad-tests/src/lib.rs +++ b/mullvad-tests/src/lib.rs @@ -1 +1,220 @@ +#![allow(dead_code)] +#[macro_use] +extern crate duct; +#[cfg(unix)] +extern crate libc; +extern crate mullvad_ipc_client; +extern crate mullvad_paths; +extern crate notify; +extern crate os_pipe; +extern crate tempfile; + +use std::fs; +use std::io::{BufRead, BufReader}; +use std::path::{Path, PathBuf}; +use std::sync::{mpsc, Arc, Mutex}; +use std::thread; +use std::time::{Duration, Instant}; + +use self::mullvad_ipc_client::DaemonRpcClient; +use self::notify::{op, RawEvent, RecursiveMode, Watcher}; +use self::os_pipe::{pipe, PipeReader}; +use self::tempfile::TempDir; + +#[cfg(unix)] +pub static DAEMON_EXECUTABLE_PATH: &str = "../target/debug/mullvad-daemon"; + +#[cfg(not(unix))] +pub static DAEMON_EXECUTABLE_PATH: &str = r"..\target\debug\mullvad-daemon.exe"; + +pub fn wait_for_file_write_finish<P: AsRef<Path>>(file_path: P, timeout: Duration) { + let file_path = file_path.as_ref(); + let parent_dir = file_path.parent().expect("Missing file parent directory"); + + let absolute_parent_dir = parent_dir + .canonicalize() + .expect("Failed to get absolute path to watch"); + let file_name = file_path + .file_name() + .expect("Missing file name of file path to watch"); + let absolute_file_path = absolute_parent_dir.join(file_name); + + let (tx, rx) = mpsc::channel(); + let mut watcher = notify::raw_watcher(tx).expect("Failed to listen for file system events"); + let start = Instant::now(); + let mut remaining_time = Some(timeout); + + watcher + .watch(absolute_parent_dir, RecursiveMode::NonRecursive) + .expect("Failed to listen for file system events on directory"); + + if !file_path.exists() { + while let Some(wait_time) = remaining_time { + let event = rx.recv_timeout(wait_time); + + if let Ok(RawEvent { + path: Some(path), + op: Ok(op), + .. + }) = event + { + if op.contains(op::CLOSE_WRITE) && path == absolute_file_path { + break; + } + } + + remaining_time = timeout.checked_sub(start.elapsed()); + } + } +} + +fn prepare_test_dirs() -> (TempDir, PathBuf) { + let temp_dir = TempDir::new().expect("Failed to create temporary daemon directory"); + let resource_dir = temp_dir.path().join("resource-dir"); + + fs::create_dir(&resource_dir).expect("Failed to create resource directory"); + + prepare_relay_list(resource_dir.join("relays.json")); + + (temp_dir, resource_dir) +} + +fn prepare_relay_list<T: AsRef<Path>>(path: T) { + fs::write( + path, + r#"{ + "countries": [{ + "name": "Mockland", + "code": "fake", + "latitude": -91, + "longitude": 0, + "relays": [{ + "hostname": "fake-mockland", + "ipv4_addr_in": "192.168.0.100", + "ipv4_addr_exit": "192.168.0.101", + "include_in_country": true, + "weight": 100, + "tunnels": { + "openvpn": [ { "port": 10000, "protocol": "udp" } ], + "wireguard": [], + }, + }], + }] + }"#, + ).expect("Failed to create mock relay list file"); +} + +pub struct DaemonRunner { + process: Option<duct::Handle>, + output: Arc<Mutex<BufReader<PipeReader>>>, + _temp_dir: TempDir, +} + +impl DaemonRunner { + pub fn spawn() -> Self { + let (temp_dir, resource_dir) = prepare_test_dirs(); + + let (reader, writer) = pipe().expect("Failed to open pipe to connect to daemon"); + let process = cmd!(DAEMON_EXECUTABLE_PATH, "-v", "--disable-log-to-file") + .dir("..") + .env("MULLVAD_CACHE_DIR", "./") + .env("MULLVAD_RESOURCE_DIR", resource_dir) + .stderr_to_stdout() + .stdout_handle(writer) + .start() + .expect("Failed to start daemon"); + + DaemonRunner { + process: Some(process), + output: Arc::new(Mutex::new(BufReader::new(reader))), + _temp_dir: temp_dir, + } + } + + pub fn assert_output(&mut self, pattern: &'static str, timeout: Duration) { + let (tx, rx) = mpsc::channel(); + let stdout = self.output.clone(); + + thread::spawn(move || { + Self::wait_for_output(stdout, pattern); + tx.send(()).expect("Failed to report search result"); + }); + + rx.recv_timeout(timeout) + .expect(&format!("failed to search for {:?}", pattern)); + } + + fn wait_for_output(output: Arc<Mutex<BufReader<PipeReader>>>, pattern: &str) { + let mut output = output + .lock() + .expect("Another thread panicked while holding a lock to the process output"); + + let mut line = String::new(); + + while !line.contains(pattern) { + line.clear(); + output + .read_line(&mut line) + .expect("Failed to read line from daemon stdout"); + } + } + + pub fn rpc_client(&mut self) -> Result<DaemonRpcClient, String> { + let rpc_file = mullvad_paths::get_rpc_address_path() + .map_err(|error| format!("Failed to build RPC connection file path: {}", error))?; + + if !rpc_file.exists() { + wait_for_file_write_finish(rpc_file, Duration::from_secs(10)); + } + + DaemonRpcClient::without_rpc_file_security_check() + .map_err(|error| format!("Failed to create RPC client: {}", error)) + } + + #[cfg(unix)] + fn request_clean_shutdown(&mut self, process: &mut duct::Handle) -> bool { + use duct::unix::HandleExt; + + process.send_signal(libc::SIGTERM).is_ok() + } + + #[cfg(not(unix))] + fn request_clean_shutdown(&mut self, _: &mut duct::Handle) -> bool { + if let Ok(mut rpc_client) = self.rpc_client() { + rpc_client.shutdown().is_ok() + } else { + false + } + } +} + +impl Drop for DaemonRunner { + fn drop(&mut self) { + if let Some(mut process) = self.process.take() { + if self.request_clean_shutdown(&mut process) { + let process = Arc::new(process); + let wait_handle = process.clone(); + let (finished_tx, finished_rx) = mpsc::channel(); + + thread::spawn(move || finished_tx.send(wait_handle.wait().map(|_| ())).unwrap()); + + let has_finished = finished_rx + .recv_timeout(Duration::from_secs(5)) + .map_err(|_| ()) + .and_then(|result| result.map_err(|_| ())) + .is_ok(); + + if !has_finished { + process.kill().unwrap(); + } + } else { + process.kill().unwrap(); + } + } + + if let Ok(file_path) = mullvad_paths::get_rpc_address_path() { + let _ = fs::remove_file(file_path); + } + } +} diff --git a/mullvad-tests/tests/common/mod.rs b/mullvad-tests/tests/common/mod.rs deleted file mode 100644 index a93d1b1528..0000000000 --- a/mullvad-tests/tests/common/mod.rs +++ /dev/null @@ -1,220 +0,0 @@ -#![allow(dead_code)] - -#[cfg(unix)] -extern crate libc; -extern crate mullvad_ipc_client; -extern crate mullvad_paths; -extern crate notify; -extern crate os_pipe; -extern crate tempfile; - -use std::fs; -use std::io::{BufRead, BufReader}; -use std::path::{Path, PathBuf}; -use std::sync::{mpsc, Arc, Mutex}; -use std::thread; -use std::time::{Duration, Instant}; - -use duct; - -use self::mullvad_ipc_client::DaemonRpcClient; -use self::notify::{op, RawEvent, RecursiveMode, Watcher}; -use self::os_pipe::{pipe, PipeReader}; -use self::tempfile::TempDir; - -#[cfg(unix)] -pub static DAEMON_EXECUTABLE_PATH: &str = "../target/debug/mullvad-daemon"; - -#[cfg(not(unix))] -pub static DAEMON_EXECUTABLE_PATH: &str = r"..\target\debug\mullvad-daemon.exe"; - -pub fn wait_for_file_write_finish<P: AsRef<Path>>(file_path: P, timeout: Duration) { - let file_path = file_path.as_ref(); - let parent_dir = file_path.parent().expect("Missing file parent directory"); - - let absolute_parent_dir = parent_dir - .canonicalize() - .expect("Failed to get absolute path to watch"); - let file_name = file_path - .file_name() - .expect("Missing file name of file path to watch"); - let absolute_file_path = absolute_parent_dir.join(file_name); - - let (tx, rx) = mpsc::channel(); - let mut watcher = notify::raw_watcher(tx).expect("Failed to listen for file system events"); - let start = Instant::now(); - let mut remaining_time = Some(timeout); - - watcher - .watch(absolute_parent_dir, RecursiveMode::NonRecursive) - .expect("Failed to listen for file system events on directory"); - - if !file_path.exists() { - while let Some(wait_time) = remaining_time { - let event = rx.recv_timeout(wait_time); - - if let Ok(RawEvent { - path: Some(path), - op: Ok(op), - .. - }) = event - { - if op.contains(op::CLOSE_WRITE) && path == absolute_file_path { - break; - } - } - - remaining_time = timeout.checked_sub(start.elapsed()); - } - } -} - -fn prepare_test_dirs() -> (TempDir, PathBuf) { - let temp_dir = TempDir::new().expect("Failed to create temporary daemon directory"); - let resource_dir = temp_dir.path().join("resource-dir"); - - fs::create_dir(&resource_dir).expect("Failed to create resource directory"); - - prepare_relay_list(resource_dir.join("relays.json")); - - (temp_dir, resource_dir) -} - -fn prepare_relay_list<T: AsRef<Path>>(path: T) { - fs::write( - path, - r#"{ - "countries": [{ - "name": "Mockland", - "code": "fake", - "latitude": -91, - "longitude": 0, - "relays": [{ - "hostname": "fake-mockland", - "ipv4_addr_in": "192.168.0.100", - "ipv4_addr_exit": "192.168.0.101", - "include_in_country": true, - "weight": 100, - "tunnels": { - "openvpn": [ { "port": 10000, "protocol": "udp" } ], - "wireguard": [], - }, - }], - }] - }"#, - ).expect("Failed to create mock relay list file"); -} - -pub struct DaemonRunner { - process: Option<duct::Handle>, - output: Arc<Mutex<BufReader<PipeReader>>>, - _temp_dir: TempDir, -} - -impl DaemonRunner { - pub fn spawn() -> Self { - let (temp_dir, resource_dir) = prepare_test_dirs(); - - let (reader, writer) = pipe().expect("Failed to open pipe to connect to daemon"); - let process = cmd!(DAEMON_EXECUTABLE_PATH, "-v", "--disable-log-to-file") - .dir("..") - .env("MULLVAD_CACHE_DIR", "./") - .env("MULLVAD_RESOURCE_DIR", resource_dir) - .stderr_to_stdout() - .stdout_handle(writer) - .start() - .expect("Failed to start daemon"); - - DaemonRunner { - process: Some(process), - output: Arc::new(Mutex::new(BufReader::new(reader))), - _temp_dir: temp_dir, - } - } - - pub fn assert_output(&mut self, pattern: &'static str, timeout: Duration) { - let (tx, rx) = mpsc::channel(); - let stdout = self.output.clone(); - - thread::spawn(move || { - Self::wait_for_output(stdout, pattern); - tx.send(()).expect("Failed to report search result"); - }); - - rx.recv_timeout(timeout) - .expect(&format!("failed to search for {:?}", pattern)); - } - - fn wait_for_output(output: Arc<Mutex<BufReader<PipeReader>>>, pattern: &str) { - let mut output = output - .lock() - .expect("Another thread panicked while holding a lock to the process output"); - - let mut line = String::new(); - - while !line.contains(pattern) { - line.clear(); - output - .read_line(&mut line) - .expect("Failed to read line from daemon stdout"); - } - } - - pub fn rpc_client(&mut self) -> Result<DaemonRpcClient, String> { - let rpc_file = mullvad_paths::get_rpc_address_path() - .map_err(|error| format!("Failed to build RPC connection file path: {}", error))?; - - if !rpc_file.exists() { - wait_for_file_write_finish(rpc_file, Duration::from_secs(10)); - } - - DaemonRpcClient::without_rpc_file_security_check() - .map_err(|error| format!("Failed to create RPC client: {}", error)) - } - - #[cfg(unix)] - fn request_clean_shutdown(&mut self, process: &mut duct::Handle) -> bool { - use duct::unix::HandleExt; - - process.send_signal(libc::SIGTERM).is_ok() - } - - #[cfg(not(unix))] - fn request_clean_shutdown(&mut self, _: &mut duct::Handle) -> bool { - if let Ok(mut rpc_client) = self.rpc_client() { - rpc_client.shutdown().is_ok() - } else { - false - } - } -} - -impl Drop for DaemonRunner { - fn drop(&mut self) { - if let Some(mut process) = self.process.take() { - if self.request_clean_shutdown(&mut process) { - let process = Arc::new(process); - let wait_handle = process.clone(); - let (finished_tx, finished_rx) = mpsc::channel(); - - thread::spawn(move || finished_tx.send(wait_handle.wait().map(|_| ())).unwrap()); - - let has_finished = finished_rx - .recv_timeout(Duration::from_secs(5)) - .map_err(|_| ()) - .and_then(|result| result.map_err(|_| ())) - .is_ok(); - - if !has_finished { - process.kill().unwrap(); - } - } else { - process.kill().unwrap(); - } - } - - if let Ok(file_path) = mullvad_paths::get_rpc_address_path() { - let _ = fs::remove_file(file_path); - } - } -} diff --git a/mullvad-tests/tests/startup.rs b/mullvad-tests/tests/startup.rs index f9bb5d04f6..b14b7f2070 100644 --- a/mullvad-tests/tests/startup.rs +++ b/mullvad-tests/tests/startup.rs @@ -1,15 +1,11 @@ -#[macro_use] -extern crate duct; -extern crate mullvad_ipc_client; extern crate mullvad_paths; - -mod common; +extern crate mullvad_tests; use std::fs::{self, Metadata}; use std::io; use std::time::Duration; -use common::DaemonRunner; +use mullvad_tests::DaemonRunner; use platform_specific::*; |
