restart.rs 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. // Copyright 2019-2021 Tauri Programme within The Commons Conservancy
  2. // SPDX-License-Identifier: Apache-2.0
  3. // SPDX-License-Identifier: MIT
  4. use std::io;
  5. use std::path::{Path, PathBuf};
  6. use std::process::Command;
  7. /// Helper for generic catch-all errors.
  8. type Result = std::result::Result<(), Box<dyn std::error::Error>>;
  9. /// https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes--1300-1699-
  10. #[cfg(windows)]
  11. const ERROR_PRIVILEGE_NOT_HELD: i32 = 1314;
  12. /// Represents a successfully created symlink.
  13. enum Symlink {
  14. /// Path to the created symlink
  15. Created(PathBuf),
  16. /// A symlink that failed due to missing permissions (Windows).
  17. #[allow(dead_code)]
  18. Privilege,
  19. }
  20. /// Compile the nested restart binary so that we have access to it during the test.
  21. ///
  22. /// This is compiled inside of this test so that we always have a literal binary file we can use
  23. /// for some filesystem related tests. Because of how integration tests work, the current working
  24. /// directory should be the same as the manifest of the crate (not workspace) this integration test
  25. /// is a part of.
  26. fn compile_restart_test_binary() -> io::Result<PathBuf> {
  27. let project = PathBuf::from("tests").join("restart");
  28. let mut cargo = Command::new("cargo");
  29. cargo.arg("build");
  30. cargo.arg("--manifest-path");
  31. cargo.arg(project.join("Cargo.toml"));
  32. let status = cargo.status()?;
  33. if !status.success() {
  34. return Err(io::Error::new(
  35. io::ErrorKind::Other,
  36. "Unable to compile restart test cargo project inside restart integration test",
  37. ));
  38. }
  39. let profile = if cfg!(debug_assertions) {
  40. "debug"
  41. } else {
  42. "release"
  43. };
  44. let bin = if cfg!(windows) {
  45. "restart.exe"
  46. } else {
  47. "restart"
  48. };
  49. Ok(project.join("target").join(profile).join(bin))
  50. }
  51. /// Compile the test binary, run it, and compare it with expected output.
  52. ///
  53. /// Failing to create a symlink due to permissions issues is also a success
  54. /// for the purpose of this runner.
  55. fn symlink_runner(create_symlinks: impl Fn(&Path) -> io::Result<Symlink>) -> Result {
  56. let compiled_binary = compile_restart_test_binary()?;
  57. // set up all the temporary file paths
  58. let temp = tempfile::TempDir::new()?;
  59. let bin = temp.path().canonicalize()?.join("restart.exe");
  60. // copy the built restart test binary to our temporary directory
  61. std::fs::copy(compiled_binary, &bin)?;
  62. if let Symlink::Created(link) = create_symlinks(&bin)? {
  63. // run the command from the symlink, so that we can test if restart resolves it correctly
  64. let mut cmd = Command::new(link);
  65. // add the restart parameter so that the invocation will call tauri::api::process::restart
  66. cmd.arg("restart");
  67. // gather the output into a string
  68. let output = String::from_utf8(cmd.output()?.stdout)?;
  69. // run destructors to prevent resource leaking if the assertion fails
  70. drop(temp);
  71. // we expect the output to be the bin path, twice
  72. assert_eq!(output, format!("{bin}\n{bin}\n", bin = bin.display()));
  73. }
  74. Ok(())
  75. }
  76. /// Cross-platform way to create a symlink
  77. ///
  78. /// Symlinks that failed to create due to permissions issues (like on Windows)
  79. /// are also seen as successful for the purpose of this testing suite.
  80. fn create_symlink(original: &Path, link: PathBuf) -> io::Result<Symlink> {
  81. #[cfg(unix)]
  82. return std::os::unix::fs::symlink(original, &link).map(|()| Symlink::Created(link));
  83. #[cfg(windows)]
  84. return match std::os::windows::fs::symlink_file(original, &link) {
  85. Ok(()) => Ok(Symlink::Created(link)),
  86. Err(e) => match e.raw_os_error() {
  87. Some(ERROR_PRIVILEGE_NOT_HELD) => Ok(Symlink::Privilege),
  88. _ => Err(e),
  89. },
  90. };
  91. }
  92. #[test]
  93. fn symlink() -> Result {
  94. symlink_runner(|bin| {
  95. let mut link = bin.to_owned();
  96. link.set_file_name("symlink");
  97. link.set_extension("exe");
  98. create_symlink(bin, link)
  99. })
  100. }
  101. #[test]
  102. fn nested_symlinks() -> Result {
  103. symlink_runner(|bin| {
  104. let mut link1 = bin.to_owned();
  105. link1.set_file_name("symlink1");
  106. link1.set_extension("exe");
  107. create_symlink(bin, link1.clone())?;
  108. let mut link2 = bin.to_owned();
  109. link2.set_file_name("symlink2");
  110. link2.set_extension("exe");
  111. create_symlink(&link1, link2)
  112. })
  113. }