Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Port tests to CliTestContext #3893

Merged
merged 19 commits into from
Jun 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/cli/self_update.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,6 @@ use crate::{

#[cfg(unix)]
mod shell;
#[cfg(feature = "test")]
pub(crate) mod test;

#[cfg(unix)]
mod unix;
Expand All @@ -85,6 +83,8 @@ mod windows;
pub use windows::complete_windows_uninstall;
#[cfg(windows)]
use windows::{delete_rustup_and_cargo_home, do_add_to_path, do_remove_from_path};
#[cfg(all(windows, feature = "test"))]
pub use windows::{get_path, RegistryGuard, RegistryValueId, USER_PATH};
#[cfg(windows)]
pub(crate) use windows::{run_update, self_replace};

Expand Down
73 changes: 0 additions & 73 deletions src/cli/self_update/test.rs

This file was deleted.

230 changes: 149 additions & 81 deletions src/cli/self_update/windows.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
use std::env::{consts::EXE_SUFFIX, split_paths};
use std::ffi::{OsStr, OsString};
use std::fmt;
use std::io::Write;
use std::io::{self, Write};
use std::os::windows::ffi::{OsStrExt, OsStringExt};
use std::path::Path;
use std::process::Command;
use std::sync::{Arc, Mutex};
use std::sync::{Arc, LockResult, Mutex, MutexGuard};

use anyhow::{anyhow, Context, Result};
use tracing::{info, warn};
Expand All @@ -20,6 +20,7 @@ use crate::utils::utils;
use crate::utils::Notification;

use winreg::enums::{RegType, HKEY_CURRENT_USER, KEY_READ, KEY_WRITE};
use winreg::types::{FromRegValue, ToRegValue};
use winreg::{RegKey, RegValue};

pub(crate) fn ensure_prompt(process: &Process) -> Result<()> {
Expand Down Expand Up @@ -807,16 +808,85 @@ pub(crate) fn delete_rustup_and_cargo_home(process: &Process) -> Result<()> {
Ok(())
}

#[cfg(test)]
mod tests {
use std::ffi::OsString;
use std::os::windows::ffi::OsStrExt;
#[cfg(any(test, feature = "test"))]
pub fn get_path() -> io::Result<Option<RegValue>> {
USER_PATH.get()
}

#[cfg(any(test, feature = "test"))]
pub struct RegistryGuard<'a> {
_locked: LockResult<MutexGuard<'a, ()>>,
id: &'static RegistryValueId,
prev: Option<RegValue>,
}

#[cfg(any(test, feature = "test"))]
impl<'a> RegistryGuard<'a> {
pub fn new(id: &'static RegistryValueId) -> io::Result<Self> {
Ok(Self {
_locked: REGISTRY_LOCK.lock(),
id,
prev: id.get()?,
})
}
}

#[cfg(any(test, feature = "test"))]
impl<'a> Drop for RegistryGuard<'a> {
fn drop(&mut self) {
self.id.set(self.prev.as_ref()).unwrap();
}
}

#[cfg(any(test, feature = "test"))]
static REGISTRY_LOCK: Mutex<()> = Mutex::new(());

use winreg::enums::{RegType, HKEY_CURRENT_USER, KEY_READ, KEY_WRITE};
use winreg::{RegKey, RegValue};
#[cfg(any(test, feature = "test"))]
pub const USER_PATH: RegistryValueId = RegistryValueId {
sub_key: "Environment",
value_name: "PATH",
};

#[cfg(any(test, feature = "test"))]
pub struct RegistryValueId {
pub sub_key: &'static str,
pub value_name: &'static str,
}

#[cfg(any(test, feature = "test"))]
impl RegistryValueId {
pub fn get_value<T: FromRegValue>(&self) -> io::Result<Option<T>> {
self.get()?.map(|v| T::from_reg_value(&v)).transpose()
}

fn get(&self) -> io::Result<Option<RegValue>> {
let sub_key = RegKey::predef(HKEY_CURRENT_USER)
.open_subkey_with_flags(self.sub_key, KEY_READ | KEY_WRITE)?;
match sub_key.get_raw_value(self.value_name) {
Ok(val) => Ok(Some(val)),
Err(ref e) if e.kind() == io::ErrorKind::NotFound => Ok(None),
Err(e) => Err(e),
}
}

pub fn set_value(&self, new: Option<impl ToRegValue>) -> io::Result<()> {
self.set(new.map(|s| s.to_reg_value()).as_ref())
}

fn set(&self, new: Option<&RegValue>) -> io::Result<()> {
let sub_key = RegKey::predef(HKEY_CURRENT_USER)
.open_subkey_with_flags(self.sub_key, KEY_READ | KEY_WRITE)?;
match new {
Some(new) => sub_key.set_raw_value(self.value_name, new),
None => sub_key.delete_value(self.value_name),
}
}
}

#[cfg(test)]
mod tests {
use super::*;
use crate::currentprocess::TestProcess;
use crate::test::with_saved_path;

fn wide(str: &str) -> Vec<u16> {
OsString::from(str).encode_wide().collect()
Expand Down Expand Up @@ -856,60 +926,58 @@ mod tests {
#[test]
fn windows_path_regkey_type() {
// per issue #261, setting PATH should use REG_EXPAND_SZ.
with_saved_path(&mut || {
let root = RegKey::predef(HKEY_CURRENT_USER);
let environment = root
.open_subkey_with_flags("Environment", KEY_READ | KEY_WRITE)
.unwrap();
environment.delete_value("PATH").unwrap();

{
// Can't compare the Results as Eq isn't derived; thanks error-chain.
#![allow(clippy::unit_cmp)]
assert_eq!((), super::_apply_new_path(Some(wide("foo"))).unwrap());
}
let root = RegKey::predef(HKEY_CURRENT_USER);
let environment = root
.open_subkey_with_flags("Environment", KEY_READ | KEY_WRITE)
.unwrap();
let path = environment.get_raw_value("PATH").unwrap();
assert_eq!(path.vtype, RegType::REG_EXPAND_SZ);
assert_eq!(super::to_winreg_bytes(wide("foo")), &path.bytes[..]);
});
let _guard = RegistryGuard::new(&USER_PATH);
let root = RegKey::predef(HKEY_CURRENT_USER);
let environment = root
.open_subkey_with_flags("Environment", KEY_READ | KEY_WRITE)
.unwrap();
environment.delete_value("PATH").unwrap();

{
// Can't compare the Results as Eq isn't derived; thanks error-chain.
#![allow(clippy::unit_cmp)]
assert_eq!((), super::_apply_new_path(Some(wide("foo"))).unwrap());
}
let root = RegKey::predef(HKEY_CURRENT_USER);
let environment = root
.open_subkey_with_flags("Environment", KEY_READ | KEY_WRITE)
.unwrap();
let path = environment.get_raw_value("PATH").unwrap();
assert_eq!(path.vtype, RegType::REG_EXPAND_SZ);
assert_eq!(super::to_winreg_bytes(wide("foo")), &path.bytes[..]);
}

#[test]
fn windows_path_delete_key_when_empty() {
use std::io;
// during uninstall the PATH key may end up empty; if so we should
// delete it.
with_saved_path(&mut || {
let root = RegKey::predef(HKEY_CURRENT_USER);
let environment = root
.open_subkey_with_flags("Environment", KEY_READ | KEY_WRITE)
.unwrap();
environment
.set_raw_value(
"PATH",
&RegValue {
bytes: super::to_winreg_bytes(wide("foo")),
vtype: RegType::REG_EXPAND_SZ,
},
)
.unwrap();

{
// Can't compare the Results as Eq isn't derived; thanks error-chain.
#![allow(clippy::unit_cmp)]
assert_eq!((), super::_apply_new_path(Some(Vec::new())).unwrap());
}
let reg_value = environment.get_raw_value("PATH");
match reg_value {
Ok(_) => panic!("key not deleted"),
Err(ref e) if e.kind() == io::ErrorKind::NotFound => {}
Err(ref e) => panic!("error {e}"),
}
});
let _guard = RegistryGuard::new(&USER_PATH);
let root = RegKey::predef(HKEY_CURRENT_USER);
let environment = root
.open_subkey_with_flags("Environment", KEY_READ | KEY_WRITE)
.unwrap();
environment
.set_raw_value(
"PATH",
&RegValue {
bytes: super::to_winreg_bytes(wide("foo")),
vtype: RegType::REG_EXPAND_SZ,
},
)
.unwrap();

{
// Can't compare the Results as Eq isn't derived; thanks error-chain.
#![allow(clippy::unit_cmp)]
assert_eq!((), super::_apply_new_path(Some(Vec::new())).unwrap());
}
let reg_value = environment.get_raw_value("PATH");
match reg_value {
Ok(_) => panic!("key not deleted"),
Err(ref e) if e.kind() == io::ErrorKind::NotFound => {}
Err(ref e) => panic!("error {e}"),
}
}

#[test]
Expand All @@ -921,22 +989,23 @@ mod tests {
.cloned()
.collect(),
);
with_saved_path(&mut || {
let root = RegKey::predef(HKEY_CURRENT_USER);
let environment = root
.open_subkey_with_flags("Environment", KEY_READ | KEY_WRITE)
.unwrap();
let reg_value = RegValue {
bytes: vec![0x12, 0x34],
vtype: RegType::REG_BINARY,
};
environment.set_raw_value("PATH", &reg_value).unwrap();
// Ok(None) signals no change to the PATH setting layer
assert_eq!(
None,
super::_with_path_cargo_home_bin(|_, _| panic!("called"), &tp.process).unwrap()
);
});

let _guard = RegistryGuard::new(&USER_PATH);
let root = RegKey::predef(HKEY_CURRENT_USER);
let environment = root
.open_subkey_with_flags("Environment", KEY_READ | KEY_WRITE)
.unwrap();
let reg_value = RegValue {
bytes: vec![0x12, 0x34],
vtype: RegType::REG_BINARY,
};
environment.set_raw_value("PATH", &reg_value).unwrap();
// Ok(None) signals no change to the PATH setting layer
assert_eq!(
None,
super::_with_path_cargo_home_bin(|_, _| panic!("called"), &tp.process).unwrap()
);

assert_eq!(
r"warn: the registry key HKEY_CURRENT_USER\Environment\PATH is not a string. Not modifying the PATH variable
",
Expand All @@ -947,15 +1016,14 @@ mod tests {
#[test]
fn windows_treat_missing_path_as_empty() {
// during install the PATH key may be missing; treat it as empty
with_saved_path(&mut || {
let root = RegKey::predef(HKEY_CURRENT_USER);
let environment = root
.open_subkey_with_flags("Environment", KEY_READ | KEY_WRITE)
.unwrap();
environment.delete_value("PATH").unwrap();

assert_eq!(Some(Vec::new()), super::get_windows_path_var().unwrap());
});
let _guard = RegistryGuard::new(&USER_PATH);
let root = RegKey::predef(HKEY_CURRENT_USER);
let environment = root
.open_subkey_with_flags("Environment", KEY_READ | KEY_WRITE)
.unwrap();
environment.delete_value("PATH").unwrap();

assert_eq!(Some(Vec::new()), super::get_windows_path_var().unwrap());
}

#[test]
Expand Down
Loading
Loading