Skip to content

Commit 0242e95

Browse files
authored
Merge pull request #2412 from rbtcollins/unit-tests
More unit-testification
2 parents a797355 + 63bf29d commit 0242e95

File tree

4 files changed

+99
-170
lines changed

4 files changed

+99
-170
lines changed

Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,6 @@ members = ["download"]
107107
[lib]
108108
name = "rustup"
109109
path = "src/lib.rs"
110-
test = false # no unit tests
111110

112111
[profile.release]
113112
lto = true

src/cli/self_update/windows.rs

Lines changed: 94 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,11 @@ pub fn wait_for_parent() -> Result<()> {
134134

135135
pub fn do_add_to_path(methods: &[PathUpdateMethod]) -> Result<()> {
136136
assert!(methods.len() == 1 && methods[0] == PathUpdateMethod::Windows);
137+
let new_path = _with_path_cargo_home_bin(_add_to_path)?;
138+
_apply_new_path(new_path)
139+
}
137140

141+
fn _apply_new_path(new_path: Option<String>) -> Result<()> {
138142
use std::ptr;
139143
use winapi::shared::minwindef::*;
140144
use winapi::um::winuser::{
@@ -143,39 +147,29 @@ pub fn do_add_to_path(methods: &[PathUpdateMethod]) -> Result<()> {
143147
use winreg::enums::{RegType, HKEY_CURRENT_USER, KEY_READ, KEY_WRITE};
144148
use winreg::{RegKey, RegValue};
145149

146-
let old_path = if let Some(s) = get_windows_path_var()? {
147-
s
148-
} else {
149-
// Non-unicode path
150-
return Ok(());
150+
let new_path = match new_path {
151+
Some(new_path) => new_path,
152+
None => return Ok(()), // No need to set the path
151153
};
152154

153-
let mut new_path = utils::cargo_home()?
154-
.join("bin")
155-
.to_string_lossy()
156-
.into_owned();
157-
if old_path.contains(&new_path) {
158-
return Ok(());
159-
}
160-
161-
if !old_path.is_empty() {
162-
new_path.push_str(";");
163-
new_path.push_str(&old_path);
164-
}
165-
166155
let root = RegKey::predef(HKEY_CURRENT_USER);
167156
let environment = root
168157
.open_subkey_with_flags("Environment", KEY_READ | KEY_WRITE)
169158
.chain_err(|| ErrorKind::PermissionDenied)?;
170159

171-
let reg_value = RegValue {
172-
bytes: utils::string_to_winreg_bytes(&new_path),
173-
vtype: RegType::REG_EXPAND_SZ,
174-
};
175-
176-
environment
177-
.set_raw_value("PATH", &reg_value)
178-
.chain_err(|| ErrorKind::PermissionDenied)?;
160+
if new_path.is_empty() {
161+
environment
162+
.delete_value("PATH")
163+
.chain_err(|| ErrorKind::PermissionDenied)?;
164+
} else {
165+
let reg_value = RegValue {
166+
bytes: utils::string_to_winreg_bytes(&new_path),
167+
vtype: RegType::REG_EXPAND_SZ,
168+
};
169+
environment
170+
.set_raw_value("PATH", &reg_value)
171+
.chain_err(|| ErrorKind::PermissionDenied)?;
172+
}
179173

180174
// Tell other processes to update their environment
181175
unsafe {
@@ -222,8 +216,23 @@ fn get_windows_path_var() -> Result<Option<String>> {
222216
}
223217
}
224218

219+
// Returns None if the existing old_path does not need changing, otherwise
220+
// prepends the path_str to old_path, handling empty old_path appropriately.
221+
fn _add_to_path(old_path: &str, path_str: String) -> Option<String> {
222+
if old_path.is_empty() {
223+
Some(path_str)
224+
} else if old_path.contains(&path_str) {
225+
None
226+
} else {
227+
let mut new_path = path_str.clone();
228+
new_path.push_str(";");
229+
new_path.push_str(&old_path);
230+
Some(new_path)
231+
}
232+
}
233+
225234
// Returns None if the existing old_path does not need changing
226-
fn _remove_from_path(old_path: &str, path_str: &str) -> Option<String> {
235+
fn _remove_from_path(old_path: &str, path_str: String) -> Option<String> {
227236
let idx = old_path.find(&path_str)?;
228237
// If there's a trailing semicolon (likely, since we probably added one
229238
// during install), include that in the substring to remove. We don't search
@@ -244,71 +253,22 @@ fn _remove_from_path(old_path: &str, path_str: &str) -> Option<String> {
244253
Some(new_path)
245254
}
246255

247-
fn _path_without_cargo_home_bin() -> Result<Option<String>> {
248-
let old_path = if let Some(s) = get_windows_path_var()? {
249-
s
250-
} else {
251-
// Non-unicode path
252-
return Ok(None);
253-
};
254-
256+
fn _with_path_cargo_home_bin<F>(f: F) -> Result<Option<String>>
257+
where
258+
F: FnOnce(&str, String) -> Option<String>,
259+
{
260+
let windows_path = get_windows_path_var()?;
255261
let path_str = utils::cargo_home()?
256262
.join("bin")
257263
.to_string_lossy()
258264
.into_owned();
259-
260-
Ok(_remove_from_path(&old_path, &path_str))
265+
Ok(windows_path.and_then(|old_path| f(&old_path, path_str)))
261266
}
262267

263268
pub fn do_remove_from_path(methods: &[PathUpdateMethod]) -> Result<()> {
264269
assert!(methods.len() == 1 && methods[0] == PathUpdateMethod::Windows);
265-
266-
use std::ptr;
267-
use winapi::shared::minwindef::*;
268-
use winapi::um::winuser::{
269-
SendMessageTimeoutA, HWND_BROADCAST, SMTO_ABORTIFHUNG, WM_SETTINGCHANGE,
270-
};
271-
use winreg::enums::{RegType, HKEY_CURRENT_USER, KEY_READ, KEY_WRITE};
272-
use winreg::{RegKey, RegValue};
273-
274-
let new_path = match _path_without_cargo_home_bin()? {
275-
Some(new_path) => new_path,
276-
None => return Ok(()), // No need to set the path
277-
};
278-
279-
let root = RegKey::predef(HKEY_CURRENT_USER);
280-
let environment = root
281-
.open_subkey_with_flags("Environment", KEY_READ | KEY_WRITE)
282-
.chain_err(|| ErrorKind::PermissionDenied)?;
283-
284-
if new_path.is_empty() {
285-
environment
286-
.delete_value("PATH")
287-
.chain_err(|| ErrorKind::PermissionDenied)?;
288-
} else {
289-
let reg_value = RegValue {
290-
bytes: utils::string_to_winreg_bytes(&new_path),
291-
vtype: RegType::REG_EXPAND_SZ,
292-
};
293-
environment
294-
.set_raw_value("PATH", &reg_value)
295-
.chain_err(|| ErrorKind::PermissionDenied)?;
296-
}
297-
298-
// Tell other processes to update their environment
299-
unsafe {
300-
SendMessageTimeoutA(
301-
HWND_BROADCAST,
302-
WM_SETTINGCHANGE,
303-
0 as WPARAM,
304-
"Environment\0".as_ptr() as LPARAM,
305-
SMTO_ABORTIFHUNG,
306-
5000,
307-
ptr::null_mut(),
308-
);
309-
}
310-
311-
Ok(())
270+
let new_path = _with_path_cargo_home_bin(_remove_from_path)?;
271+
_apply_new_path(new_path)
312272
}
313273

314274
pub fn run_update(setup_path: &Path) -> Result<utils::ExitCode> {
@@ -503,9 +463,26 @@ mod tests {
503463
}
504464

505465
#[test]
506-
fn windows_uninstall_doesnt_mess_with_a_non_unicode_path() {
466+
fn windows_install_does_not_add_path_twice() {
467+
assert_eq!(
468+
None,
469+
super::_add_to_path(
470+
r"c:\users\example\.cargo\bin;foo",
471+
r"c:\users\example\.cargo\bin".into()
472+
)
473+
);
474+
}
475+
476+
#[test]
477+
fn windows_doesnt_mess_with_a_non_unicode_path() {
507478
// This writes an error, so we want a sink for it.
508-
let tp = Box::new(currentprocess::TestProcess::default());
479+
let tp = Box::new(currentprocess::TestProcess {
480+
vars: [("HOME".to_string(), "/unused".to_string())]
481+
.iter()
482+
.cloned()
483+
.collect(),
484+
..Default::default()
485+
});
509486
with_registry_edits(&|| {
510487
currentprocess::with(tp.clone(), || {
511488
let root = RegKey::predef(HKEY_CURRENT_USER);
@@ -522,7 +499,10 @@ mod tests {
522499
};
523500
environment.set_raw_value("PATH", &reg_value).unwrap();
524501
// Ok(None) signals no change to the PATH setting layer
525-
assert_eq!(None, super::_path_without_cargo_home_bin().unwrap());
502+
fn panic(_: &str, _: String) -> Option<String> {
503+
panic!("called");
504+
}
505+
assert_eq!(None, super::_with_path_cargo_home_bin(panic).unwrap());
526506
})
527507
});
528508
assert_eq!(
@@ -532,13 +512,38 @@ mod tests {
532512
);
533513
}
534514

515+
#[test]
516+
fn windows_path_regkey_type() {
517+
// per issue #261, setting PATH should use REG_EXPAND_SZ.
518+
let tp = Box::new(currentprocess::TestProcess::default());
519+
with_registry_edits(&|| {
520+
currentprocess::with(tp.clone(), || {
521+
let root = RegKey::predef(HKEY_CURRENT_USER);
522+
let environment = root
523+
.open_subkey_with_flags("Environment", KEY_READ | KEY_WRITE)
524+
.unwrap();
525+
environment.delete_value("PATH").unwrap();
526+
527+
assert_eq!((), super::_apply_new_path(Some("foo".into())).unwrap());
528+
529+
let root = RegKey::predef(HKEY_CURRENT_USER);
530+
let environment = root
531+
.open_subkey_with_flags("Environment", KEY_READ | KEY_WRITE)
532+
.unwrap();
533+
let path = environment.get_raw_value("PATH").unwrap();
534+
assert_eq!(path.vtype, RegType::REG_EXPAND_SZ);
535+
assert_eq!(utils::string_to_winreg_bytes("foo"), &path.bytes[..]);
536+
})
537+
});
538+
}
539+
535540
#[test]
536541
fn windows_uninstall_removes_semicolon_from_path_prefix() {
537542
assert_eq!(
538543
"foo",
539544
super::_remove_from_path(
540545
r"c:\users\example\.cargo\bin;foo",
541-
r"c:\users\example\.cargo\bin"
546+
r"c:\users\example\.cargo\bin".into()
542547
)
543548
.unwrap()
544549
)
@@ -550,7 +555,7 @@ mod tests {
550555
"foo",
551556
super::_remove_from_path(
552557
r"foo;c:\users\example\.cargo\bin",
553-
r"c:\users\example\.cargo\bin"
558+
r"c:\users\example\.cargo\bin".into()
554559
)
555560
.unwrap()
556561
)

src/currentprocess.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -191,11 +191,11 @@ impl ProcessSource for OSProcess {
191191

192192
#[derive(Clone, Debug, Default)]
193193
pub struct TestProcess {
194-
cwd: PathBuf,
195-
args: Vec<String>,
196-
vars: HashMap<String, String>,
197-
id: u64,
198-
stdin: TestStdinInner,
194+
pub cwd: PathBuf,
195+
pub args: Vec<String>,
196+
pub vars: HashMap<String, String>,
197+
pub id: u64,
198+
pub stdin: TestStdinInner,
199199
pub stdout: TestWriterInner,
200200
pub stderr: TestWriterInner,
201201
}

tests/cli-self-upd.rs

Lines changed: 0 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -506,18 +506,6 @@ fn install_adds_path() {
506506
});
507507
}
508508

509-
#[test]
510-
#[cfg(windows)]
511-
fn install_does_not_add_path_twice() {
512-
setup(&|config| {
513-
expect_ok(config, &["rustup-init", "-y"]);
514-
expect_ok(config, &["rustup-init", "-y"]);
515-
516-
let path = config.cargodir.join("bin").to_string_lossy().to_string();
517-
assert_eq!(get_path().unwrap().matches(&path).count(), 1);
518-
});
519-
}
520-
521509
#[test]
522510
#[cfg(windows)]
523511
fn uninstall_removes_path() {
@@ -1027,34 +1015,6 @@ fn rustup_init_works_with_weird_names() {
10271015
});
10281016
}
10291017

1030-
// # 261
1031-
#[test]
1032-
#[cfg(windows)]
1033-
fn doesnt_write_wrong_path_type_to_reg() {
1034-
use winreg::enums::{RegType, HKEY_CURRENT_USER, KEY_READ, KEY_WRITE};
1035-
use winreg::RegKey;
1036-
1037-
setup(&|config| {
1038-
expect_ok(config, &["rustup-init", "-y"]);
1039-
1040-
let root = RegKey::predef(HKEY_CURRENT_USER);
1041-
let environment = root
1042-
.open_subkey_with_flags("Environment", KEY_READ | KEY_WRITE)
1043-
.unwrap();
1044-
let path = environment.get_raw_value("PATH").unwrap();
1045-
assert!(path.vtype == RegType::REG_EXPAND_SZ);
1046-
1047-
expect_ok(config, &["rustup", "self", "uninstall", "-y"]);
1048-
1049-
let root = RegKey::predef(HKEY_CURRENT_USER);
1050-
let environment = root
1051-
.open_subkey_with_flags("Environment", KEY_READ | KEY_WRITE)
1052-
.unwrap();
1053-
let path = environment.get_raw_value("PATH").unwrap();
1054-
assert!(path.vtype == RegType::REG_EXPAND_SZ);
1055-
});
1056-
}
1057-
10581018
// HKCU\Environment\PATH may not exist during install, and it may need to be
10591019
// deleted during uninstall if we remove the last path from it
10601020
#[test]
@@ -1091,41 +1051,6 @@ fn windows_handle_empty_path_registry_key() {
10911051
});
10921052
}
10931053

1094-
#[test]
1095-
#[cfg(windows)]
1096-
fn install_doesnt_mess_with_a_non_unicode_path() {
1097-
use winreg::enums::{RegType, HKEY_CURRENT_USER, KEY_READ, KEY_WRITE};
1098-
use winreg::{RegKey, RegValue};
1099-
1100-
setup(&|config| {
1101-
let root = RegKey::predef(HKEY_CURRENT_USER);
1102-
let environment = root
1103-
.open_subkey_with_flags("Environment", KEY_READ | KEY_WRITE)
1104-
.unwrap();
1105-
1106-
let reg_value = RegValue {
1107-
bytes: vec![
1108-
0x00, 0xD8, // leading surrogate
1109-
0x01, 0x01, // bogus trailing surrogate
1110-
0x00, 0x00,
1111-
], // null
1112-
vtype: RegType::REG_EXPAND_SZ,
1113-
};
1114-
environment.set_raw_value("PATH", &reg_value).unwrap();
1115-
1116-
expect_stderr_ok(config, &["rustup-init", "-y"],
1117-
"the registry key HKEY_CURRENT_USER\\Environment\\PATH does not contain valid Unicode. \
1118-
Not modifying the PATH variable");
1119-
1120-
let root = RegKey::predef(HKEY_CURRENT_USER);
1121-
let environment = root
1122-
.open_subkey_with_flags("Environment", KEY_READ | KEY_WRITE)
1123-
.unwrap();
1124-
let path = environment.get_raw_value("PATH").unwrap();
1125-
assert_eq!(path.bytes, reg_value.bytes);
1126-
});
1127-
}
1128-
11291054
#[test]
11301055
#[ignore] // untestable
11311056
fn install_but_rustup_is_installed() {}

0 commit comments

Comments
 (0)