diff --git a/Cargo.lock b/Cargo.lock index 88ef6a30e0..2d74a051da 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -405,6 +405,8 @@ dependencies = [ "tempdir 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", "walkdir 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "winreg 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] diff --git a/src/rustup-cli/common.rs b/src/rustup-cli/common.rs index 179a343218..f32780458d 100644 --- a/src/rustup-cli/common.rs +++ b/src/rustup-cli/common.rs @@ -29,6 +29,61 @@ pub fn confirm(question: &str, default: bool) -> Result { Ok(r) } +pub enum Confirm { + Yes, No, Advanced +} + +pub fn confirm_advanced(default: Confirm) -> Result { + let _ = std::io::stdout().flush(); + let input = try!(read_line()); + + let r = match &*input { + "y" | "Y" | "yes" => Confirm::Yes, + "n" | "N" | "no" => Confirm::No, + "a" | "A" | "advanced" => Confirm::Advanced, + "" => default, + _ => Confirm::No, + }; + + println!(""); + + Ok(r) +} + +pub fn question_str(question: &str, default: &str) -> Result { + println!("{}", question); + let _ = std::io::stdout().flush(); + let input = try!(read_line()); + + println!(""); + + if input.is_empty() { + Ok(default.to_string()) + } else { + Ok(input) + } +} + +pub fn question_bool(question: &str, default: bool) -> Result { + println!("{}", question); + + let _ = std::io::stdout().flush(); + let input = try!(read_line()); + + println!(""); + + if input.is_empty() { + Ok(default) + } else { + match &*input { + "y" | "Y" | "yes" => Ok(true), + "n" | "N" | "no" => Ok(false), + _ => Ok(default) + } + } + +} + pub fn read_line() -> Result { let stdin = std::io::stdin(); let stdin = stdin.lock(); diff --git a/src/rustup-cli/main.rs b/src/rustup-cli/main.rs index bad873bc67..5f8a7f0c6d 100644 --- a/src/rustup-cli/main.rs +++ b/src/rustup-cli/main.rs @@ -96,10 +96,14 @@ fn run_multirust() -> Result<()> { // ~/.multirust/tmp/multirust-$random, and execute it with // `self install` as the arguments. FIXME: Verify this // works. + let opts = self_update::InstallOpts { + default_toolchain: "stable".to_string(), + no_modify_path: false, + }; if cfg!(windows) { - self_update::install(false, false, "stable") + self_update::install(false, false, opts) } else { - self_update::install(true, false, "stable") + self_update::install(true, false, opts) } } Some(_) => { diff --git a/src/rustup-cli/self_update.rs b/src/rustup-cli/self_update.rs index cfb525c177..76aacf1ed8 100644 --- a/src/rustup-cli/self_update.rs +++ b/src/rustup-cli/self_update.rs @@ -30,7 +30,7 @@ //! Deleting the running binary during uninstall is tricky //! and racy on Windows. -use common::{self, confirm}; +use common::{self, Confirm}; use itertools::Itertools; use rustup::{Error, Result, NotifyHandler}; use rustup_dist::dist; @@ -44,6 +44,11 @@ use std::process::{self, Command}; use std::fs; use tempdir::TempDir; +pub struct InstallOpts { + pub default_toolchain: String, + pub no_modify_path: bool, +} + // The big installation messages. These are macros because the first // argument of format! needs to be a literal. @@ -64,14 +69,17 @@ Cargo's bin directory, located at: ", $platform_msg , -" +r#" You can uninstall at any time with `rustup self uninstall` and these changes will be reverted. WARNING: This is an early beta. Expect breakage. -Continue? (Y/n)" +To cancel installation, type "n", or for more options type "a", +then press the Enter key to continue. + +Press the Enter key to install Rust."# )}; } @@ -93,14 +101,23 @@ modifying the HKEY_CURRENT_USER/Environment/PATH registry key." )}; } +macro_rules! pre_install_msg_no_modify_path { + () => { +pre_install_msg_template!( +"This path needs to be in your PATH environment variable, +but will not be added automatically." + )}; +} + macro_rules! post_install_msg_unix { () => { r"Rust is installed now. Great! To get started you need Cargo's bin directory in your `PATH` environment variable. Next time you log in this will be done -automatically. To configure your current shell without logging out -run `source {cargo_home}/env`. +automatically. + +To configure your current shell run `source {cargo_home}/env`. " }; } @@ -112,7 +129,31 @@ r"Rust is installed now. Great! To get started you need Cargo's bin directory in your `PATH` environment variable. Future applications will automatically have the correct environment, but you may need to restart your current shell. -" }; } +" + }; +} + +macro_rules! post_install_msg_unix_no_modify_path { + () => { +r"Rust is installed now. Great! + +To get started you need Cargo's bin directory in your `PATH` +environment variable. + +To configure your current shell run `source {cargo_home}/env`. +" + }; +} + +macro_rules! post_install_msg_win_no_modify_path { + () => { +r"Rust is installed now. Great! + +To get started you need Cargo's bin directory in your `PATH` +environment variable. This has not been done automatically. +" + }; +} macro_rules! pre_uninstall_msg { () => { @@ -148,25 +189,50 @@ fn canonical_cargo_home() -> Result { /// Installing is a simple matter of coping the running binary to /// CARGO_HOME/bin, hardlinking the various Rust tools to it, /// and and adding CARGO_HOME/bin to PATH. -pub fn install(no_prompt: bool, verbose: bool, default: &str) -> Result<()> { +pub fn install(no_prompt: bool, verbose: bool, + opts: InstallOpts) -> Result<()> { + + let selected_opts; if !no_prompt { - let ref msg = try!(pre_install_msg()); - if !try!(confirm(msg, true)) { - info!("aborting installation"); - return Ok(()); + let ref msg = try!(pre_install_msg(opts.no_modify_path)); + println!("{}", msg); + match try!(common::confirm_advanced(Confirm::Yes)) { + Confirm::No => { + info!("aborting installation"); + return Ok(()); + } + Confirm::Yes => { + selected_opts = opts; + } + Confirm::Advanced => { + if let Some(opts) = try!(advanced_install(opts)) { + selected_opts = opts; + } else { + info!("aborting installation"); + return Ok(()); + } + } } + } else { + selected_opts = opts; } let install_res: Result<()> = (|| { try!(cleanup_legacy()); try!(install_bins()); - try!(do_add_to_path(&get_add_path_methods())); - try!(maybe_install_rust(default, verbose)); + if !selected_opts.no_modify_path { + try!(do_add_to_path(&get_add_path_methods())); + } + try!(maybe_install_rust(&selected_opts.default_toolchain, verbose)); if cfg!(unix) { let ref env_file = try!(utils::cargo_home()).join("env"); - let ref env_str = try!(shell_export_string()); + let ref env_str = format!( + "{}\n\ + printf \"PATH environment variable set.\\n\" + printf \"You're ready to Rust!\\n\\n\"", + try!(shell_export_string())); try!(utils::write_file("env", env_file, env_str)); } @@ -182,7 +248,7 @@ pub fn install(no_prompt: bool, verbose: bool, default: &str) -> Result<()> { // window closes. if cfg!(windows) && !no_prompt { println!(""); - println!("Press enter to continue"); + println!("Press the Enter key to continue."); try!(common::read_line()); } @@ -191,12 +257,22 @@ pub fn install(no_prompt: bool, verbose: bool, default: &str) -> Result<()> { // More helpful advice, skip if -y if !no_prompt { - if cfg!(unix) { - let cargo_home = try!(canonical_cargo_home()); - println!(post_install_msg_unix!(), - cargo_home = cargo_home); + if !selected_opts.no_modify_path { + if cfg!(unix) { + let cargo_home = try!(canonical_cargo_home()); + println!(post_install_msg_unix!(), + cargo_home = cargo_home); + } else { + println!(post_install_msg_win!()); + } } else { - println!(post_install_msg_win!()); + if cfg!(unix) { + let cargo_home = try!(canonical_cargo_home()); + println!(post_install_msg_unix_no_modify_path!(), + cargo_home = cargo_home); + } else { + println!(post_install_msg_win_no_modify_path!()); + } } // On windows, where installation happens in a console @@ -204,7 +280,7 @@ pub fn install(no_prompt: bool, verbose: bool, default: &str) -> Result<()> { // the user to press a key to continue. if cfg!(windows) { println!(""); - println!("Press enter to continue"); + println!("Press the Enter key to continue."); try!(common::read_line()); } } @@ -212,30 +288,94 @@ pub fn install(no_prompt: bool, verbose: bool, default: &str) -> Result<()> { Ok(()) } -fn pre_install_msg() -> Result { +fn pre_install_msg(no_modify_path: bool) -> Result { let cargo_home = try!(utils::cargo_home()); let cargo_home_bin = cargo_home.join("bin"); - if cfg!(unix) { - let add_path_methods = get_add_path_methods(); - let rcfiles = add_path_methods.into_iter() - .filter_map(|m| { - if let PathUpdateMethod::RcFile(path) = m { - Some(format!("{}", path.display())) - } else { - None - } - }).collect::>(); - assert!(rcfiles.len() == 1); // Only modifying .profile - Ok(format!(pre_install_msg_unix!(), - cargo_home_bin = cargo_home_bin.display(), - rcfiles = rcfiles[0])) + if !no_modify_path { + if cfg!(unix) { + let add_path_methods = get_add_path_methods(); + let rcfiles = add_path_methods.into_iter() + .filter_map(|m| { + if let PathUpdateMethod::RcFile(path) = m { + Some(format!("{}", path.display())) + } else { + None + } + }).collect::>(); + assert!(rcfiles.len() == 1); // Only modifying .profile + Ok(format!(pre_install_msg_unix!(), + cargo_home_bin = cargo_home_bin.display(), + rcfiles = rcfiles[0])) + } else { + Ok(format!(pre_install_msg_win!(), + cargo_home_bin = cargo_home_bin.display())) + } } else { - Ok(format!(pre_install_msg_win!(), + Ok(format!(pre_install_msg_no_modify_path!(), cargo_home_bin = cargo_home_bin.display())) } } +// Interactive editing of the install options +fn advanced_install(mut opts: InstallOpts) -> Result> { + + fn print_opts(opts: &InstallOpts) { + println!( + r"Selected installation options: + + default toolchain: {} + modify PATH variable: {} +", + opts.default_toolchain, + if !opts.no_modify_path { "yes" } else { "no" } + ); + } + + loop { + + print_opts(&opts); + + println!( + "I'm going to ask you the value of each these installation options.\n\ + You may simply press the Enter key to accept the default."); + + println!(""); + + opts.default_toolchain = try!(common::question_str( + "Default toolchain? (stable/beta/nightly)", + &opts.default_toolchain)); + + opts.no_modify_path = !try!(common::question_bool( + "Modify PATH variable? (y/n)", + !opts.no_modify_path)); + + println!("That's it! We're ready to install Rust."); + println!(""); + + print_opts(&opts); + + println!( +r#"To cancel installation, type "n", or for more options type "a", +then press the Enter key to continue. + +Press the Enter key to install Rust. "#); + + match try!(common::confirm_advanced(Confirm::Yes)) { + Confirm::No => { + info!("aborting installation"); + return Ok(None); + } + Confirm::Yes => { + return Ok(Some(opts)); + } + Confirm::Advanced => { + continue; + } + } + } +} + // Before multirust-rs installed bins to $CARGO_HOME/bin it installed // them to $RUSTUP_HOME/bin. If those bins continue to exist after // upgrade and are on the $PATH, it would cause major confusion. This @@ -294,8 +434,10 @@ fn install_bins() -> Result<()> { fn maybe_install_rust(toolchain_str: &str, verbose: bool) -> Result<()> { let ref cfg = try!(common::set_globals(verbose)); - // If this is a fresh install (there is no default yet) - // then install the requested toolchain and make it the default. + // If there is already an install, then `toolchain_str` may not be + // a toolchain the user actually wants. Don't do anything. FIXME: + // This logic should be part of InstallOpts so that it isn't + // possible to select a toolchain then have it not be installed. if try!(cfg.find_default()).is_none() { let toolchain = try!(cfg.get_toolchain(toolchain_str, false)); let status = try!(toolchain.install_from_dist()); @@ -303,7 +445,7 @@ fn maybe_install_rust(toolchain_str: &str, verbose: bool) -> Result<()> { println!(""); try!(common::show_channel_update(cfg, toolchain_str, Ok(status))); } else { - info!("updating existing installation"); + info!("updating existing rustup installation"); println!(""); } @@ -321,7 +463,7 @@ pub fn uninstall(no_prompt: bool) -> Result<()> { println!(""); let ref msg = format!(pre_uninstall_msg!(), cargo_home = try!(canonical_cargo_home())); - if !try!(confirm(msg, false)) { + if !try!(common::confirm(msg, false)) { info!("aborting uninstallation"); return Ok(()); } diff --git a/src/rustup-cli/setup_mode.rs b/src/rustup-cli/setup_mode.rs index a434352b8e..80fccb352a 100644 --- a/src/rustup-cli/setup_mode.rs +++ b/src/rustup-cli/setup_mode.rs @@ -1,5 +1,5 @@ use std::env; -use self_update; +use self_update::{self, InstallOpts}; use rustup::Result; use clap::{App, Arg}; use common; @@ -27,14 +27,23 @@ pub fn main() -> Result<()> { .long("default-toolchain") .takes_value(true) .possible_values(&["stable", "beta", "nightly"]) - .help("Choose a default toolchain to install")); + .help("Choose a default toolchain to install")) + .arg(Arg::with_name("no-modify-path") + .long("no-modify-path") + .help("Don't configure the PATH environment variable")); let matches = cli.get_matches(); let no_prompt = matches.is_present("no-prompt"); let verbose = matches.is_present("verbose"); - let default = matches.value_of("default-toolchain").unwrap_or("stable"); + let default_toolchain = matches.value_of("default-toolchain").unwrap_or("stable"); + let no_modify_path = matches.is_present("no-modify-path"); - try!(self_update::install(no_prompt, verbose, default)); + let opts = InstallOpts { + default_toolchain: default_toolchain.to_owned(), + no_modify_path: no_modify_path, + }; + + try!(self_update::install(no_prompt, verbose, opts)); Ok(()) } diff --git a/src/rustup-mock/Cargo.toml b/src/rustup-mock/Cargo.toml index 1207211c51..67b9a8f173 100644 --- a/src/rustup-mock/Cargo.toml +++ b/src/rustup-mock/Cargo.toml @@ -20,3 +20,19 @@ openssl = "0.7.2" itertools = "0.4.1" tar = "0.4.0" toml = "0.1.27" + +[target.x86_64-pc-windows-gnu.dependencies] +winapi = "0.2.4" +winreg = "0.3.2" + +[target.x86_64-pc-windows-msvc.dependencies] +winapi = "0.2.4" +winreg = "0.3.2" + +[target.i686-pc-windows-gnu.dependencies] +winapi = "0.2.4" +winreg = "0.3.2" + +[target.i686-pc-windows-msvc.dependencies] +winapi = "0.2.4" +winreg = "0.3.2" diff --git a/src/rustup-mock/src/lib.rs b/src/rustup-mock/src/lib.rs index 1aaf37d8e8..27d8ac0489 100644 --- a/src/rustup-mock/src/lib.rs +++ b/src/rustup-mock/src/lib.rs @@ -12,6 +12,11 @@ extern crate itertools; extern crate tar; extern crate toml; +#[cfg(windows)] +extern crate winapi; +#[cfg(windows)] +extern crate winreg; + pub mod dist; pub mod clitools; @@ -73,3 +78,48 @@ impl MockInstallerBuilder { writeln!(ver, "3").unwrap(); } } + +#[cfg(windows)] +pub fn get_path() -> Option { + use winreg::RegKey; + use winapi::*; + + let root = RegKey::predef(HKEY_CURRENT_USER); + let environment = root.open_subkey_with_flags("Environment", KEY_READ | KEY_WRITE).unwrap(); + + environment.get_value("PATH").ok() +} + +#[cfg(windows)] +pub fn restore_path(p: &Option) { + use winreg::{RegKey, RegValue}; + use winreg::enums::RegType; + use winapi::*; + + let root = RegKey::predef(HKEY_CURRENT_USER); + let environment = root.open_subkey_with_flags("Environment", KEY_READ | KEY_WRITE).unwrap(); + + if let Some(p) = p.as_ref() { + let reg_value = RegValue { + bytes: string_to_winreg_bytes(&p), + vtype: RegType::REG_EXPAND_SZ, + }; + environment.set_raw_value("PATH", ®_value).unwrap(); + } else { + let _ = environment.delete_value("PATH"); + } + + fn string_to_winreg_bytes(s: &str) -> Vec { + use std::ffi::OsString; + use std::os::windows::ffi::OsStrExt; + let v: Vec<_> = OsString::from(format!("{}\x00", s)).encode_wide().collect(); + unsafe { ::std::slice::from_raw_parts(v.as_ptr() as *const u8, v.len() * 2).to_vec() } + } +} + +#[cfg(unix)] +pub fn get_path() -> Option { None } + +#[cfg(unix)] +pub fn restore_path(_: &Option) { } + diff --git a/tests/cli-install-interactive.rs b/tests/cli-install-interactive.rs new file mode 100644 index 0000000000..5d353b2674 --- /dev/null +++ b/tests/cli-install-interactive.rs @@ -0,0 +1,183 @@ +//! Tests of the interactive console installer + +extern crate rustup_mock; +extern crate rustup_utils; +#[macro_use] +extern crate lazy_static; +extern crate scopeguard; + +use std::sync::Mutex; +use std::process::Stdio; +use std::io::Write; +use rustup_mock::clitools::{self, Config, Scenario, + SanitizedOutput, + expect_stdout_ok}; +use rustup_mock::{get_path, restore_path}; + +pub fn setup(f: &Fn(&Config)) { + clitools::setup(Scenario::SimpleV2, &|config| { + // Lock protects environment variables + lazy_static! { + static ref LOCK: Mutex<()> = Mutex::new(()); + } + let _g = LOCK.lock(); + + // An windows these tests mess with the user's PATH. Save + // and restore them here to keep from trashing things. + let saved_path = get_path(); + let _g = scopeguard::guard(saved_path, |p| restore_path(p)); + + f(config); + }); +} + +fn run_input(config: &Config, args: &[&str], input: &str) -> SanitizedOutput { + let mut cmd = clitools::cmd(config, &args[0], &args[1..]); + clitools::env(config, &mut cmd); + + cmd.stdin(Stdio::piped()); + cmd.stdout(Stdio::piped()); + cmd.stderr(Stdio::piped()); + let mut child = cmd.spawn().unwrap(); + + child.stdin.as_mut().unwrap().write_all(input.as_bytes()).unwrap(); + let out = child.wait_with_output().unwrap(); + + SanitizedOutput { + ok: out.status.success(), + stdout: String::from_utf8(out.stdout).unwrap(), + stderr: String::from_utf8(out.stderr).unwrap(), + } +} + +#[test] +fn smoke_test() { + setup(&|config| { + let out = run_input(config, &["rustup-init"], "\n\n"); + assert!(out.ok); + }); +} + +#[test] +fn update() { + setup(&|config| { + run_input(config, &["rustup-init"], "\n\n"); + let out = run_input(config, &["rustup-init"], "\n\n"); + assert!(out.ok); + }); +} + +// Testing that the right number of blank lines are printed after the +// 'pre-install' message and before the 'post-install' messag. +#[test] +fn blank_lines_around_stderr_log_output_install() { + setup(&|config| { + let out = run_input(config, &["rustup-init"], "\n\n"); + + // During an interactive session, after "Press the Enter + // key..." the UI emits a blank line, then there is a blank + // line that comes from the user pressing enter, then log + // output on stderr, then an explicit blank line on stdout + // before printing $toolchain installed + assert!(out.stdout.contains(r" +Press the Enter key to install Rust. + + + stable installed - 1.1.0 (hash-s-2) + +Rust is installed now. Great! +")); + }); +} + +#[test] +fn blank_lines_around_stderr_log_output_update() { + setup(&|config| { + run_input(config, &["rustup-init"], "\n\n"); + let out = run_input(config, &["rustup-init"], "\n\n"); + + assert!(out.stdout.contains(r" +Press the Enter key to install Rust. + + +Rust is installed now. Great! +")); + }); +} + +#[test] +fn user_says_nope() { + setup(&|config| { + let out = run_input(config, &["rustup-init"], "n\n\n"); + assert!(out.ok); + assert!(!config.cargodir.join("bin").exists()); + }); +} + +#[test] +fn with_no_modify_path() { + setup(&|config| { + let out = run_input(config, &["rustup-init", "--no-modify-path"], "\n\n"); + assert!(out.ok); + assert!(out.stdout.contains("This path needs to be in your PATH environment variable")); + + if cfg!(unix) { + assert!(!config.homedir.join(".profile").exists()); + } + }); +} + +#[test] +fn with_non_default_toolchain() { + setup(&|config| { + let out = run_input(config, &["rustup-init", "--default-toolchain=nightly"], "\n\n"); + assert!(out.ok); + + expect_stdout_ok(config, &["rustup", "show"], "nightly"); + }); +} + +#[test] +fn set_nightly_toolchain() { + setup(&|config| { + let out = run_input(config, &["rustup-init"], + "a\nnightly\n\n\n\n"); + assert!(out.ok); + + expect_stdout_ok(config, &["rustup", "show"], "nightly"); + }); +} + +#[test] +fn set_no_modify_path() { + setup(&|config| { + let out = run_input(config, &["rustup-init"], + "a\n\nno\n\n\n"); + assert!(out.ok); + + if cfg!(unix) { + assert!(!config.homedir.join(".profile").exists()); + } + }); +} + +#[test] +fn set_nightly_toolchain_and_unset() { + setup(&|config| { + let out = run_input(config, &["rustup-init"], + "a\nnightly\n\na\nbeta\n\n\n\n"); + assert!(out.ok); + + expect_stdout_ok(config, &["rustup", "show"], "beta"); + }); +} + +#[test] +fn user_says_nope_after_advanced_install() { + setup(&|config| { + let out = run_input(config, &["rustup-init"], + "a\n\n\nn\n\n"); + assert!(out.ok); + assert!(!config.cargodir.join("bin").exists()); + }); +} diff --git a/tests/cli-self-update.rs b/tests/cli-self-update.rs index 4baf297620..fac083868f 100644 --- a/tests/cli-self-update.rs +++ b/tests/cli-self-update.rs @@ -27,6 +27,7 @@ use rustup_mock::clitools::{self, Config, Scenario, #[cfg(windows)] use rustup_mock::clitools::expect_stderr_ok; use rustup_mock::dist::{create_hash, calc_hash}; +use rustup_mock::{get_path, restore_path}; use rustup_utils::raw; macro_rules! for_host { ($s: expr) => (&format!($s, this_host_triple())) } @@ -387,44 +388,6 @@ fn when_cargo_home_is_the_default_write_path_specially() { }); } -#[cfg(windows)] -fn get_path() -> Option { - use winreg::RegKey; - use winapi::*; - - let root = RegKey::predef(HKEY_CURRENT_USER); - let environment = root.open_subkey_with_flags("Environment", KEY_READ | KEY_WRITE).unwrap(); - - environment.get_value("PATH").ok() -} - -#[cfg(windows)] -fn restore_path(p: &Option) { - use winreg::{RegKey, RegValue}; - use winreg::enums::RegType; - use winapi::*; - use rustup_utils::utils; - - let root = RegKey::predef(HKEY_CURRENT_USER); - let environment = root.open_subkey_with_flags("Environment", KEY_READ | KEY_WRITE).unwrap(); - - if let Some(p) = p.as_ref() { - let reg_value = RegValue { - bytes: utils::string_to_winreg_bytes(&p), - vtype: RegType::REG_EXPAND_SZ, - }; - environment.set_raw_value("PATH", ®_value).unwrap(); - } else { - let _ = environment.delete_value("PATH"); - } -} - -#[cfg(unix)] -fn get_path() -> Option { None } - -#[cfg(unix)] -fn restore_path(_: &Option) { } - #[test] #[cfg(windows)] fn install_adds_path() { @@ -460,6 +423,38 @@ fn uninstall_removes_path() { }); } + +#[test] +#[cfg(unix)] +fn install_doesnt_modify_path_if_passed_no_modify_path() { + setup(&|config| { + let ref profile = config.homedir.join(".profile"); + expect_ok(config, &["rustup-init", "-y", "--no-modify-path"]); + assert!(!profile.exists()); + }); +} + +#[test] +#[cfg(windows)] +fn install_doesnt_modify_path_if_passed_no_modify_path() { + use winreg::RegKey; + use winapi::*; + + setup(&|config| { + let root = RegKey::predef(HKEY_CURRENT_USER); + let environment = root.open_subkey_with_flags("Environment", KEY_READ | KEY_WRITE).unwrap(); + let old_path = environment.get_raw_value("PATH").unwrap(); + + expect_ok(config, &["rustup-init", "-y", "--no-modify-path"]); + + let root = RegKey::predef(HKEY_CURRENT_USER); + let environment = root.open_subkey_with_flags("Environment", KEY_READ | KEY_WRITE).unwrap(); + let new_path = environment.get_raw_value("PATH").unwrap(); + + assert!(old_path == new_path); + }); +} + #[test] fn update_exact() { update_setup(&|config, _| { @@ -741,7 +736,7 @@ fn reinstall_exact() { expect_ok_ex(config, &["rustup-init", "-y"], r" ", -r"info: updating existing installation +r"info: updating existing rustup installation " ); }); @@ -760,7 +755,7 @@ fn produces_env_file_on_unix() { assert!(cmd.output().unwrap().status.success()); let ref envfile = config.homedir.join(".cargo/env"); let envfile = raw::read_file(envfile).unwrap(); - assert_eq!(r#"export PATH="$HOME/.cargo/bin:$PATH""#, envfile); + assert!(envfile.contains(r#"export PATH="$HOME/.cargo/bin:$PATH""#)); }); }