summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--mullvad-tests/src/bin/mock_openvpn.rs35
-rw-r--r--mullvad-tests/src/lib.rs119
-rw-r--r--mullvad-tests/tests/connection.rs27
3 files changed, 119 insertions, 62 deletions
diff --git a/mullvad-tests/src/bin/mock_openvpn.rs b/mullvad-tests/src/bin/mock_openvpn.rs
index ac7e2c2b39..cbc5b6caf6 100644
--- a/mullvad-tests/src/bin/mock_openvpn.rs
+++ b/mullvad-tests/src/bin/mock_openvpn.rs
@@ -1,30 +1,32 @@
-extern crate notify;
+extern crate mullvad_tests;
use std::env;
use std::fs::{self, File};
use std::io::{self, Read, Write};
-use std::path::{Path, PathBuf};
+use std::path::PathBuf;
use std::sync::mpsc;
use std::thread;
+use std::time::Duration;
-use notify::{raw_watcher, RawEvent, RecursiveMode, Watcher};
+use mullvad_tests::{watch_event, PathWatcher};
+
+const MAX_EVENT_TIME: Duration = Duration::from_secs(60);
fn main() {
let (file, path) = create_args_file();
- let path_to_wait_for = path;
- let path_to_remove = path_to_wait_for.clone();
let (finished_tx, finished_rx) = mpsc::channel();
+ let watcher = PathWatcher::watch(&path).expect("Failed to watch file for events");
write_command_line(file);
wait_thread(wait_for_stdin_to_be_closed, finished_tx.clone());
wait_thread(
- move || wait_for_file_to_be_deleted(path_to_wait_for),
+ move || wait_for_file_to_be_deleted(watcher, MAX_EVENT_TIME),
finished_tx,
);
let _ = finished_rx.recv();
- let _ = fs::remove_file(path_to_remove);
+ let _ = fs::remove_file(path);
}
fn create_args_file() -> (File, PathBuf) {
@@ -61,19 +63,8 @@ fn wait_for_stdin_to_be_closed() {
let _ignore_bytes = io::stdin().bytes().last();
}
-fn wait_for_file_to_be_deleted<P: AsRef<Path>>(file: P) {
- let file = file.as_ref();
- let (tx, rx) = mpsc::channel();
-
- if let Ok(mut watcher) = raw_watcher(tx) {
- if watcher.watch(&file, RecursiveMode::NonRecursive).is_ok() {
- for event in rx {
- if let RawEvent { op: Ok(op), .. } = event {
- if op.contains(notify::op::REMOVE) {
- break;
- }
- }
- }
- }
- }
+fn wait_for_file_to_be_deleted(mut watcher: PathWatcher, timeout: Duration) {
+ let _ignore_event = watcher
+ .set_timeout(timeout)
+ .find(|&event| event == watch_event::REMOVE);
}
diff --git a/mullvad-tests/src/lib.rs b/mullvad-tests/src/lib.rs
index 9bc4755827..534bf11003 100644
--- a/mullvad-tests/src/lib.rs
+++ b/mullvad-tests/src/lib.rs
@@ -23,7 +23,7 @@ use std::thread;
use std::time::{Duration, Instant};
use mullvad_ipc_client::DaemonRpcClient;
-use notify::{op, RawEvent, RecursiveMode, Watcher};
+use notify::{RawEvent, RecommendedWatcher, RecursiveMode, Watcher};
use openvpn_plugin::types::OpenVpnPluginEvent;
use os_pipe::{pipe, PipeReader};
use talpid_ipc::WsIpcClient;
@@ -32,6 +32,8 @@ use tempfile::TempDir;
use self::mock_openvpn::MOCK_OPENVPN_ARGS_FILE;
use self::platform_specific::*;
+pub use self::notify::op::{self as watch_event, Op as WatchEvent};
+
type Result<T> = ::std::result::Result<T, String>;
#[cfg(unix)]
@@ -53,44 +55,97 @@ mod platform_specific {
pub const TALPID_OPENVPN_PLUGIN_FILE: &str = "talpid_openvpn_plugin.dll";
}
-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");
+pub struct PathWatcher {
+ events: mpsc::Receiver<RawEvent>,
+ path: PathBuf,
+ timeout: Duration,
+ _watcher: RecommendedWatcher,
+}
+
+impl PathWatcher {
+ pub fn watch<P: AsRef<Path>>(file_path: P) -> Result<Self> {
+ let file_path = file_path.as_ref();
+ let parent_dir = file_path
+ .parent()
+ .ok_or_else(|| "Missing file parent directory")?;
+
+ let absolute_parent_dir = parent_dir
+ .canonicalize()
+ .map_err(|_| "Failed to get absolute path to watch")?;
+ let file_name = file_path
+ .file_name()
+ .ok_or_else(|| "Missing file name of file path to watch")?;
+ let absolute_file_path = absolute_parent_dir.join(file_name);
- 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).map_err(|_| {
+ format!(
+ "Failed to create watcher of file system events to watch {}",
+ file_path.display()
+ )
+ })?;
- 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::Recursive)
+ .map_err(|_| {
+ format!(
+ "Failed to start watching for file system events from {}",
+ file_path.display()
+ )
+ })?;
+
+ Ok(PathWatcher {
+ events: rx,
+ path: absolute_file_path,
+ timeout: Duration::from_secs(5),
+ _watcher: watcher,
+ })
+ }
- watcher
- .watch(absolute_parent_dir, RecursiveMode::NonRecursive)
- .expect("Failed to listen for file system events on directory");
+ pub fn set_timeout(&mut self, timeout: Duration) -> &mut Self {
+ self.timeout = timeout;
+ self
+ }
- if !file_path.exists() {
- while let Some(wait_time) = remaining_time {
- let event = rx.recv_timeout(wait_time);
+ pub fn assert_create_write_close_sequence(&mut self) {
+ assert_eq!(self.next(), Some(watch_event::CREATE));
+ assert_eq!(self.next(), Some(watch_event::WRITE));
- if let Ok(RawEvent {
- path: Some(path),
- op: Ok(op),
- ..
- }) = event
- {
- if op.contains(op::CLOSE_WRITE) && path == absolute_file_path {
+ loop {
+ match self.next() {
+ Some(watch_event::WRITE) => continue,
+ event => {
+ assert_eq!(event, Some(watch_event::CLOSE_WRITE));
break;
}
}
+ }
+ }
+}
- remaining_time = timeout.checked_sub(start.elapsed());
+impl Iterator for PathWatcher {
+ type Item = WatchEvent;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ let start = Instant::now();
+
+ while let Some(remaining_time) = self.timeout.checked_sub(start.elapsed()) {
+ match self.events.recv_timeout(remaining_time) {
+ Ok(RawEvent {
+ path: Some(path),
+ op: Ok(op),
+ ..
+ }) => if path == self.path {
+ return Some(op);
+ } else {
+ continue;
+ },
+ Ok(_) => continue,
+ Err(_) => return None,
+ }
}
+
+ None
}
}
@@ -232,7 +287,11 @@ impl DaemonRunner {
pub fn rpc_client(&mut self) -> Result<DaemonRpcClient> {
if !self.rpc_address_file.exists() {
- wait_for_file_write_finish(&self.rpc_address_file, Duration::from_secs(10));
+ let _ = PathWatcher::watch(&self.rpc_address_file).map(|mut events| {
+ events
+ .set_timeout(Duration::from_secs(10))
+ .find(|&event| event == watch_event::CLOSE_WRITE)
+ });
}
DaemonRpcClient::with_insecure_rpc_address_file(&self.rpc_address_file)
diff --git a/mullvad-tests/tests/connection.rs b/mullvad-tests/tests/connection.rs
index 91a110fd8a..7ae5efca21 100644
--- a/mullvad-tests/tests/connection.rs
+++ b/mullvad-tests/tests/connection.rs
@@ -10,7 +10,7 @@ use std::sync::mpsc;
use std::time::Duration;
use mullvad_tests::mock_openvpn::search_openvpn_args;
-use mullvad_tests::{wait_for_file_write_finish, DaemonRunner, MockOpenVpnPluginRpcClient};
+use mullvad_tests::{watch_event, DaemonRunner, MockOpenVpnPluginRpcClient, PathWatcher};
use mullvad_types::states::{DaemonState, SecurityState, TargetState};
#[cfg(target_os = "linux")]
@@ -44,15 +44,14 @@ fn spawns_openvpn() {
let mut daemon = DaemonRunner::spawn();
let mut rpc_client = daemon.rpc_client().unwrap();
let openvpn_args_file = daemon.mock_openvpn_args_file();
+ let mut openvpn_args_file_events = PathWatcher::watch(&openvpn_args_file).unwrap();
assert!(!openvpn_args_file.exists());
rpc_client.set_account(Some("123456".to_owned())).unwrap();
rpc_client.connect().unwrap();
- wait_for_file_write_finish(&openvpn_args_file, Duration::from_secs(5));
-
- assert!(openvpn_args_file.exists());
+ openvpn_args_file_events.assert_create_write_close_sequence();
}
#[test]
@@ -60,20 +59,22 @@ fn respawns_openvpn_if_it_crashes() {
let mut daemon = DaemonRunner::spawn();
let mut rpc_client = daemon.rpc_client().unwrap();
let openvpn_args_file = daemon.mock_openvpn_args_file();
+ let mut openvpn_args_file_events = PathWatcher::watch(&openvpn_args_file).unwrap();
+
+ openvpn_args_file_events.set_timeout(Duration::from_secs(10));
assert!(!openvpn_args_file.exists());
rpc_client.set_account(Some("123456".to_owned())).unwrap();
rpc_client.connect().unwrap();
- wait_for_file_write_finish(&openvpn_args_file, Duration::from_secs(5));
+ openvpn_args_file_events.assert_create_write_close_sequence();
// Stop OpenVPN by removing the mock OpenVPN arguments file
fs::remove_file(&openvpn_args_file).expect("Failed to remove the mock OpenVPN arguments file");
+ assert_eq!(openvpn_args_file_events.next(), Some(watch_event::REMOVE));
- wait_for_file_write_finish(&openvpn_args_file, Duration::from_secs(5));
-
- assert!(openvpn_args_file.exists());
+ openvpn_args_file_events.assert_create_write_close_sequence();
}
#[test]
@@ -189,12 +190,14 @@ fn returns_to_connecting_state() {
let mut daemon = DaemonRunner::spawn();
let mut rpc_client = daemon.rpc_client().unwrap();
let openvpn_args_file = daemon.mock_openvpn_args_file();
+ let mut openvpn_args_file_events = PathWatcher::watch(&openvpn_args_file).unwrap();
let state_events = rpc_client.new_state_subscribe().unwrap();
rpc_client.set_account(Some("123456".to_owned())).unwrap();
rpc_client.connect().unwrap();
assert_state_event(&state_events, CONNECTING_STATE);
+ openvpn_args_file_events.assert_create_write_close_sequence();
let mut mock_plugin_client = create_mock_openvpn_plugin_client(openvpn_args_file);
@@ -206,7 +209,8 @@ fn returns_to_connecting_state() {
mock_plugin_client.route_predown().unwrap();
// Wait for new OpenVPN instance
- wait_for_file_write_finish(&openvpn_args_file, Duration::from_secs(5));
+ assert_eq!(openvpn_args_file_events.next(), Some(watch_event::REMOVE));
+ openvpn_args_file_events.assert_create_write_close_sequence();
assert_state_event(&state_events, CONNECTING_STATE);
assert_eq!(rpc_client.get_state().unwrap(), CONNECTING_STATE);
@@ -265,7 +269,10 @@ fn create_mock_openvpn_plugin_client<P: AsRef<Path>>(
fn get_plugin_arguments<P: AsRef<Path>>(openvpn_args_file_path: P) -> (String, String) {
let args_file_path = openvpn_args_file_path.as_ref();
- wait_for_file_write_finish(&args_file_path, Duration::from_secs(5));
+ if !args_file_path.exists() {
+ let _wait_for_args_file = PathWatcher::watch(&args_file_path)
+ .map(|mut events| events.find(|&event| event == watch_event::CLOSE_WRITE));
+ }
let mut arguments = search_openvpn_args(&args_file_path, OPENVPN_PLUGIN_NAME).skip(1);