use std::io;
use std::path::{Path, PathBuf};
use std::process::Command;
type Result = std::result::Result<(), Box<dyn std::error::Error>>;
#[cfg(windows)]
const ERROR_PRIVILEGE_NOT_HELD: i32 = 1314;
enum Symlink {
Created(PathBuf),
#[allow(dead_code)]
Privilege,
}
fn compile_restart_test_binary() -> io::Result<PathBuf> {
let project = PathBuf::from("tests").join("restart");
let mut cargo = Command::new("cargo");
cargo.arg("build");
cargo.arg("--manifest-path");
cargo.arg(project.join("Cargo.toml"));
if cfg!(feature = "process-relaunch-dangerous-allow-symlink-macos") {
cargo.args([
"--features",
"tauri/process-relaunch-dangerous-allow-symlink-macos",
]);
}
let status = cargo.status()?;
if !status.success() {
return Err(io::Error::new(
io::ErrorKind::Other,
"Unable to compile restart test cargo project inside restart integration test",
));
}
let profile = if cfg!(debug_assertions) {
"debug"
} else {
"release"
};
let bin = if cfg!(windows) {
"restart.exe"
} else {
"restart"
};
Ok(project.join("target").join(profile).join(bin))
}
fn symlink_runner(create_symlinks: impl Fn(&Path) -> io::Result<Symlink>) -> Result {
let compiled_binary = compile_restart_test_binary()?;
let temp = tempfile::TempDir::new()?;
let bin = temp.path().canonicalize()?.join("restart.exe");
std::fs::copy(compiled_binary, &bin)?;
if let Symlink::Created(link) = create_symlinks(&bin)? {
let mut cmd = Command::new(link);
cmd.arg("restart");
let output = cmd.output()?;
drop(temp);
if output.status.success() {
let stdout = String::from_utf8(output.stdout)?;
assert_eq!(stdout, format!("{bin}\n{bin}\n", bin = bin.display()));
} else if cfg!(all(
target_os = "macos",
not(feature = "process-relaunch-dangerous-allow-symlink-macos")
)) {
let stderr = String::from_utf8(output.stderr)?;
assert!(stderr.contains(
"StartingBinary found current_exe() that contains a symlink on a non-allowed platform"
));
} else {
panic!("restart integration test runner failed for unknown reason");
}
}
Ok(())
}
fn create_symlink(original: &Path, link: PathBuf) -> io::Result<Symlink> {
#[cfg(unix)]
return std::os::unix::fs::symlink(original, &link).map(|()| Symlink::Created(link));
#[cfg(windows)]
return match std::os::windows::fs::symlink_file(original, &link) {
Ok(()) => Ok(Symlink::Created(link)),
Err(e) => match e.raw_os_error() {
Some(ERROR_PRIVILEGE_NOT_HELD) => Ok(Symlink::Privilege),
_ => Err(e),
},
};
}
#[test]
fn restart_symlinks() -> Result {
symlink_runner(|bin| {
let mut link = bin.to_owned();
link.set_file_name("symlink");
link.set_extension("exe");
create_symlink(bin, link)
})?;
symlink_runner(|bin| {
let mut link1 = bin.to_owned();
link1.set_file_name("symlink1");
link1.set_extension("exe");
create_symlink(bin, link1.clone())?;
let mut link2 = bin.to_owned();
link2.set_file_name("symlink2");
link2.set_extension("exe");
create_symlink(&link1, link2)
})
}