diff options
| author | Markus Pettersson <markus.pettersson@mullvad.net> | 2023-11-17 15:37:13 +0100 |
|---|---|---|
| committer | Markus Pettersson <markus.pettersson@mullvad.net> | 2023-12-06 14:37:06 +0100 |
| commit | 1b930b893fa3fcb06f5fee4affbfb62887d0e68a (patch) | |
| tree | 21b0836c5b5730f844950a2764aa652832f13849 /test/test-runner | |
| parent | ac3e222d031b0f599561c4c30504de5cd3f871a2 (diff) | |
| download | mullvadvpn-1b930b893fa3fcb06f5fee4affbfb62887d0e68a.tar.xz mullvadvpn-1b930b893fa3fcb06f5fee4affbfb62887d0e68a.zip | |
Implement RPC for reading & writing to app cache file
- Implement RPC for writing to a file in a test runner / guest VM.
- Implement RPC for getting app cache directory
- Implement RPC for restarting the app in a test runner / guest vm
- Implement RPC for starting the app in a test runner / guest vm
- Implement RPC for stopping the app in a test runner / guest vm
- Implement `find_cache_traces` on Window & macOS
Diffstat (limited to 'test/test-runner')
| -rw-r--r-- | test/test-runner/Cargo.toml | 2 | ||||
| -rw-r--r-- | test/test-runner/src/app.rs | 58 | ||||
| -rw-r--r-- | test/test-runner/src/main.rs | 44 | ||||
| -rw-r--r-- | test/test-runner/src/net.rs | 4 | ||||
| -rw-r--r-- | test/test-runner/src/sys.rs | 103 |
5 files changed, 159 insertions, 52 deletions
diff --git a/test/test-runner/Cargo.toml b/test/test-runner/Cargo.toml index 2c431ffa49..64461d76aa 100644 --- a/test/test-runner/Cargo.toml +++ b/test/test-runner/Cargo.toml @@ -18,7 +18,7 @@ serde_json = { workspace = true } tokio-serde = { workspace = true } libc = "0.2" -chrono = { workspace = true } +chrono = { workspace = true, features = ["serde"] } test-rpc = { path = "../test-rpc" } mullvad-paths = { path = "../../mullvad-paths" } diff --git a/test/test-runner/src/app.rs b/test/test-runner/src/app.rs index 43aca23abb..f4e1fc3c53 100644 --- a/test/test-runner/src/app.rs +++ b/test/test-runner/src/app.rs @@ -1,5 +1,5 @@ use chrono::{DateTime, Utc}; -use std::path::Path; +use std::path::{Path, PathBuf}; use test_rpc::{AppTrace, Error}; @@ -14,21 +14,18 @@ pub fn find_traces() -> Result<Vec<AppTrace>, Error> { Error::Syscall })?; - let mut traces = vec![ + let caches = find_cache_traces()?; + let traces = vec![ Path::new(r"C:\Program Files\Mullvad VPN"), // NOTE: This only works as of `499c06decda37dc639e5f` in the Mullvad app. // Older builds have no way of silently fully uninstalling the app. Path::new(r"C:\ProgramData\Mullvad VPN"), // NOTE: Works as of `4116ebc` (Mullvad app). &settings_dir, + &caches, ]; - filter_non_existent_paths(&mut traces)?; - - Ok(traces - .into_iter() - .map(|path| AppTrace::Path(path.to_path_buf())) - .collect()) + Ok(existing_paths(&traces)) } #[cfg(target_os = "linux")] @@ -36,10 +33,11 @@ pub fn find_traces() -> Result<Vec<AppTrace>, Error> { // TODO: Check GUI data // TODO: Check temp data - let mut traces = vec![ + let caches = find_cache_traces()?; + let traces = vec![ Path::new(r"/etc/mullvad-vpn/"), Path::new(r"/var/log/mullvad-vpn/"), - Path::new(r"/var/cache/mullvad-vpn/"), + &caches, Path::new(r"/opt/Mullvad VPN/"), // management interface socket Path::new(r"/var/run/mullvad-vpn"), @@ -55,12 +53,11 @@ pub fn find_traces() -> Result<Vec<AppTrace>, Error> { Path::new(r"/usr/share/fish/vendor_completions.d/mullvad.fish"), ]; - filter_non_existent_paths(&mut traces)?; + Ok(existing_paths(&traces)) +} - Ok(traces - .into_iter() - .map(|path| AppTrace::Path(path.to_path_buf())) - .collect()) +pub fn find_cache_traces() -> Result<PathBuf, Error> { + mullvad_paths::get_cache_dir().map_err(|error| Error::FileSystem(error.to_string())) } #[cfg(target_os = "macos")] @@ -68,10 +65,11 @@ pub fn find_traces() -> Result<Vec<AppTrace>, Error> { // TODO: Check GUI data // TODO: Check temp data - let mut traces = vec![ + let caches = find_cache_traces()?; + let traces = vec![ Path::new(r"/Applications/Mullvad VPN.app/"), Path::new(r"/var/log/mullvad-vpn/"), - Path::new(r"/Library/Caches/mullvad-vpn/"), + &caches, // management interface socket Path::new(r"/var/run/mullvad-vpn"), // launch daemon @@ -84,26 +82,16 @@ pub fn find_traces() -> Result<Vec<AppTrace>, Error> { Path::new(r"/usr/local/share/fish/vendor_completions.d/mullvad.fish"), ]; - filter_non_existent_paths(&mut traces)?; - - Ok(traces - .into_iter() - .map(|path| AppTrace::Path(path.to_path_buf())) - .collect()) + Ok(existing_paths(&traces)) } -fn filter_non_existent_paths(paths: &mut Vec<&Path>) -> Result<(), Error> { - for i in (0..paths.len()).rev() { - let path_exists = paths[i].try_exists().map_err(|error| { - log::error!("Failed to check whether path exists: {error}"); - Error::Syscall - })?; - if !path_exists { - paths.swap_remove(i); - continue; - } - } - Ok(()) +/// Find all present app traces on the test runner. +fn existing_paths(paths: &[&Path]) -> Vec<AppTrace> { + paths + .iter() + .filter(|&path| path.try_exists().is_ok_and(|exists| exists)) + .map(|path| AppTrace::Path(path.to_path_buf())) + .collect() } pub async fn make_device_json_old() -> Result<(), Error> { diff --git a/test/test-runner/src/main.rs b/test/test-runner/src/main.rs index ebf0d1e474..259ccdbab6 100644 --- a/test/test-runner/src/main.rs +++ b/test/test-runner/src/main.rs @@ -3,7 +3,7 @@ use logging::LOGGER; use std::{ collections::{BTreeMap, HashMap}, net::{IpAddr, SocketAddr}, - path::Path, + path::{Path, PathBuf}, }; use tarpc::context; @@ -114,6 +114,13 @@ impl Service for TestServer { app::find_traces() } + async fn get_mullvad_app_cache_dir( + self, + _: context::Context, + ) -> Result<PathBuf, test_rpc::Error> { + app::find_cache_traces() + } + async fn send_tcp( self, _: context::Context, @@ -140,7 +147,7 @@ impl Service for TestServer { interface: Option<String>, destination: IpAddr, ) -> Result<(), test_rpc::Error> { - net::send_ping(interface.as_ref().map(String::as_str), destination).await + net::send_ping(interface.as_deref(), destination).await } async fn geoip_lookup( @@ -219,6 +226,20 @@ impl Service for TestServer { logging::get_mullvad_app_logs().await } + async fn restart_app(self, _: context::Context) -> Result<(), test_rpc::Error> { + sys::restart_app().await + } + + /// Stop the Mullvad VPN application. + async fn stop_app(self, _: context::Context) -> Result<(), test_rpc::Error> { + sys::stop_app().await + } + + /// Start the Mullvad VPN application. + async fn start_app(self, _: context::Context) -> Result<(), test_rpc::Error> { + sys::start_app().await + } + async fn set_daemon_log_level( self, _: context::Context, @@ -248,6 +269,25 @@ impl Service for TestServer { Ok(()) } + /// Write a slice as the entire contents of a file. + /// + /// See the documention of [`tokio::fs::write`] for details of the behavior. + async fn write_file( + self, + _: context::Context, + dest: PathBuf, + bytes: Vec<u8>, + ) -> Result<(), test_rpc::Error> { + tokio::fs::write(&dest, bytes).await.map_err(|error| { + log::error!( + "Failed to write to \"{dest}\": {error}", + dest = dest.display() + ); + test_rpc::Error::Syscall + })?; + Ok(()) + } + async fn reboot(self, _: context::Context) -> Result<(), test_rpc::Error> { sys::reboot() } diff --git a/test/test-runner/src/net.rs b/test/test-runner/src/net.rs index f40aece4c9..a4a7a2db47 100644 --- a/test/test-runner/src/net.rs +++ b/test/test-runner/src/net.rs @@ -35,7 +35,7 @@ pub async fn send_tcp( }; #[cfg(target_os = "macos")] - sock.bind_device_by_index(Some(interface_index)) + sock.bind_device_by_index_v4(Some(interface_index)) .map_err(|error| { log::error!("Failed to set IP_BOUND_IF on socket: {error}"); test_rpc::Error::SendTcp @@ -102,7 +102,7 @@ pub async fn send_udp( }; #[cfg(target_os = "macos")] - sock.bind_device_by_index(Some(interface_index)) + sock.bind_device_by_index_v4(Some(interface_index)) .map_err(|error| { log::error!("Failed to set IP_BOUND_IF on socket: {error}"); test_rpc::Error::SendUdp diff --git a/test/test-runner/src/sys.rs b/test/test-runner/src/sys.rs index 93d148a2b5..cc89205425 100644 --- a/test/test-runner/src/sys.rs +++ b/test/test-runner/src/sys.rs @@ -193,16 +193,102 @@ ExecStart=/usr/bin/mullvad-daemon --disable-stdout-timestamps {verbosity}"# .await .map_err(|e| test_rpc::Error::Service(e.to_string()))?; + restart_app().await?; + Ok(()) +} + +/// Restart the Mullvad VPN application. +/// +/// This function waits for the app to successfully start again. +#[cfg(target_os = "linux")] +pub async fn restart_app() -> Result<(), test_rpc::Error> { tokio::process::Command::new("systemctl") .args(["restart", "mullvad-daemon"]) .status() .await .map_err(|e| test_rpc::Error::Service(e.to_string()))?; - wait_for_service_state(ServiceState::Running).await?; Ok(()) } +/// Stop the Mullvad VPN application. +/// +/// This function waits for the app to successfully shut down. +#[cfg(target_os = "linux")] +pub async fn stop_app() -> Result<(), test_rpc::Error> { + set_mullvad_daemon_service_state(false).await +} + +/// Start the Mullvad VPN application. +/// +/// This function waits for the app to successfully start again. +#[cfg(target_os = "linux")] +pub async fn start_app() -> Result<(), test_rpc::Error> { + set_mullvad_daemon_service_state(true).await +} + +/// Restart the Mullvad VPN application. +/// +/// This function waits for the app to successfully start again. +#[cfg(target_os = "windows")] +pub async fn restart_app() -> Result<(), test_rpc::Error> { + stop_app().await?; + start_app().await?; + Ok(()) +} + +/// Stop the Mullvad VPN application. +/// +/// This function waits for the app to successfully shut down. +#[cfg(target_os = "windows")] +pub async fn stop_app() -> Result<(), test_rpc::Error> { + let _ = tokio::process::Command::new("net") + .args(["stop", "mullvadvpn"]) + .status() + .await + .map_err(|e| test_rpc::Error::Service(e.to_string()))?; + Ok(()) +} + +/// Start the Mullvad VPN application. +/// +/// This function waits for the app to successfully start again. +#[cfg(target_os = "windows")] +pub async fn start_app() -> Result<(), test_rpc::Error> { + let _ = tokio::process::Command::new("net") + .args(["start", "mullvadvpn"]) + .status() + .await + .map_err(|e| test_rpc::Error::Service(e.to_string()))?; + Ok(()) +} + +/// Restart the Mullvad VPN application. +/// +/// This function waits for the app to successfully start again. +#[cfg(target_os = "macos")] +pub async fn restart_app() -> Result<(), test_rpc::Error> { + stop_app().await?; + start_app().await?; + Ok(()) +} + +/// Stop the Mullvad VPN application. +/// +/// This function waits for the app to successfully shut down. +#[cfg(target_os = "macos")] +pub async fn stop_app() -> Result<(), test_rpc::Error> { + set_mullvad_daemon_service_state(false).await +} + +/// Start the Mullvad VPN application. +/// +/// This function waits for the app to successfully start again. +#[cfg(target_os = "macos")] +pub async fn start_app() -> Result<(), test_rpc::Error> { + set_mullvad_daemon_service_state(true).await +} + #[cfg(target_os = "windows")] pub async fn set_daemon_log_level(verbosity_level: Verbosity) -> Result<(), test_rpc::Error> { log::debug!("Setting log level"); @@ -226,6 +312,7 @@ pub async fn set_daemon_log_level(verbosity_level: Verbosity) -> Result<(), test .map_err(|e| test_rpc::Error::Service(e.to_string()))?; // Stop the service + // TODO: Extract to separate function. service .stop() .map_err(|e| test_rpc::Error::Service(e.to_string()))?; @@ -266,6 +353,7 @@ pub async fn set_daemon_log_level(verbosity_level: Verbosity) -> Result<(), test .map_err(|e| test_rpc::Error::Service(e.to_string()))?; // Start the service + // TODO: Extract to separate function. service .start::<String>(&[]) .map_err(|e| test_rpc::Error::Service(e.to_string()))?; @@ -341,17 +429,8 @@ pub async fn set_daemon_environment(env: HashMap<String, String>) -> Result<(), } // Restart service - tokio::process::Command::new("net") - .args(["stop", "mullvadvpn"]) - .status() - .await - .map_err(|e| test_rpc::Error::Service(e.to_string()))?; - - tokio::process::Command::new("net") - .args(["start", "mullvadvpn"]) - .status() - .await - .map_err(|e| test_rpc::Error::Service(e.to_string()))?; + stop_app().await?; + start_app().await?; Ok(()) } |
