summaryrefslogtreecommitdiffhomepage
path: root/test/test-runner/src
diff options
context:
space:
mode:
authorMarkus Pettersson <markus.pettersson@mullvad.net>2023-11-17 15:37:13 +0100
committerMarkus Pettersson <markus.pettersson@mullvad.net>2023-12-06 14:37:06 +0100
commit1b930b893fa3fcb06f5fee4affbfb62887d0e68a (patch)
tree21b0836c5b5730f844950a2764aa652832f13849 /test/test-runner/src
parentac3e222d031b0f599561c4c30504de5cd3f871a2 (diff)
downloadmullvadvpn-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/src')
-rw-r--r--test/test-runner/src/app.rs58
-rw-r--r--test/test-runner/src/main.rs44
-rw-r--r--test/test-runner/src/net.rs4
-rw-r--r--test/test-runner/src/sys.rs103
4 files changed, 158 insertions, 51 deletions
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(())
}