diff --git a/src/cli/self_update.rs b/src/cli/self_update.rs index 62139983c5..6fd3b5565d 100644 --- a/src/cli/self_update.rs +++ b/src/cli/self_update.rs @@ -69,8 +69,6 @@ use crate::{ #[cfg(unix)] mod shell; -#[cfg(feature = "test")] -pub(crate) mod test; #[cfg(unix)] mod unix; @@ -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}; diff --git a/src/cli/self_update/test.rs b/src/cli/self_update/test.rs deleted file mode 100644 index cf22644d77..0000000000 --- a/src/cli/self_update/test.rs +++ /dev/null @@ -1,73 +0,0 @@ -//! Support for functional tests. - -use std::{io, sync::Mutex}; - -#[cfg(windows)] -use winreg::{ - enums::{HKEY_CURRENT_USER, KEY_READ, KEY_WRITE}, - RegKey, RegValue, -}; - -/// Support testing of code that mutates global state -pub fn with_saved_global_state( - getter: impl Fn() -> io::Result, - setter: impl Fn(S), - f: &mut dyn FnMut(), -) { - // Lock protects concurrent mutation of registry - static LOCK: Mutex<()> = Mutex::new(()); - let _g = LOCK.lock(); - - // Save and restore the global state here to keep from trashing things. - let saved_state = - getter().expect("Error getting global state: Better abort to avoid trashing it"); - let _g = scopeguard::guard(saved_state, setter); - - f(); -} - -#[cfg(windows)] -pub fn with_saved_path(f: &mut dyn FnMut()) { - with_saved_reg_value(&RegKey::predef(HKEY_CURRENT_USER), "Environment", "PATH", f) -} - -#[cfg(unix)] -pub fn with_saved_path(f: &mut dyn FnMut()) { - f() -} - -#[cfg(windows)] -pub fn get_path() -> io::Result> { - get_reg_value(&RegKey::predef(HKEY_CURRENT_USER), "Environment", "PATH") -} - -#[cfg(windows)] -pub fn with_saved_reg_value(root: &RegKey, subkey: &str, name: &str, f: &mut dyn FnMut()) { - with_saved_global_state( - || get_reg_value(root, subkey, name), - |p| restore_reg_value(root, subkey, name, p), - f, - ) -} - -#[cfg(windows)] -fn get_reg_value(root: &RegKey, subkey: &str, name: &str) -> io::Result> { - let subkey = root.open_subkey_with_flags(subkey, KEY_READ | KEY_WRITE)?; - match subkey.get_raw_value(name) { - Ok(val) => Ok(Some(val)), - Err(ref e) if e.kind() == io::ErrorKind::NotFound => Ok(None), - Err(e) => Err(e), - } -} - -#[cfg(windows)] -fn restore_reg_value(root: &RegKey, subkey: &str, name: &str, p: Option) { - let subkey = root - .open_subkey_with_flags(subkey, KEY_READ | KEY_WRITE) - .unwrap(); - if let Some(p) = p.as_ref() { - subkey.set_raw_value(name, p).unwrap(); - } else { - let _ = subkey.delete_value(name); - } -} diff --git a/src/cli/self_update/windows.rs b/src/cli/self_update/windows.rs index 1b1e19593c..e7f271b932 100644 --- a/src/cli/self_update/windows.rs +++ b/src/cli/self_update/windows.rs @@ -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}; @@ -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<()> { @@ -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> { + USER_PATH.get() +} + +#[cfg(any(test, feature = "test"))] +pub struct RegistryGuard<'a> { + _locked: LockResult>, + id: &'static RegistryValueId, + prev: Option, +} + +#[cfg(any(test, feature = "test"))] +impl<'a> RegistryGuard<'a> { + pub fn new(id: &'static RegistryValueId) -> io::Result { + 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(&self) -> io::Result> { + self.get()?.map(|v| T::from_reg_value(&v)).transpose() + } + + fn get(&self) -> io::Result> { + 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) -> 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 { OsString::from(str).encode_wide().collect() @@ -856,26 +926,25 @@ 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] @@ -883,33 +952,32 @@ mod tests { 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] @@ -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", ®_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", ®_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 ", @@ -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] diff --git a/src/env_var.rs b/src/env_var.rs index ce3a9eb046..d5d84c03f6 100644 --- a/src/env_var.rs +++ b/src/env_var.rs @@ -47,8 +47,10 @@ mod tests { use std::ffi::{OsStr, OsString}; use super::*; + #[cfg(windows)] + use crate::cli::self_update::{RegistryGuard, USER_PATH}; use crate::currentprocess::TestProcess; - use crate::test::{with_saved_path, Env}; + use crate::test::Env; #[test] fn prepend_unique_path() { @@ -58,43 +60,43 @@ mod tests { env::join_paths(["/home/a/.cargo/bin", "/home/b/.cargo/bin"].iter()).unwrap(), ); let tp = TestProcess::with_vars(vars); - with_saved_path(&mut || { - let mut path_entries = vec![]; - let mut cmd = Command::new("test"); + #[cfg(windows)] + let _path_guard = RegistryGuard::new(&USER_PATH).unwrap(); + let mut path_entries = vec![]; + let mut cmd = Command::new("test"); - let a = OsString::from("/home/a/.cargo/bin"); - let path_a = PathBuf::from(a); - path_entries.push(path_a); + let a = OsString::from("/home/a/.cargo/bin"); + let path_a = PathBuf::from(a); + path_entries.push(path_a); - let _a = OsString::from("/home/a/.cargo/bin"); - let _path_a = PathBuf::from(_a); - path_entries.push(_path_a); + let _a = OsString::from("/home/a/.cargo/bin"); + let _path_a = PathBuf::from(_a); + path_entries.push(_path_a); - let z = OsString::from("/home/z/.cargo/bin"); - let path_z = PathBuf::from(z); - path_entries.push(path_z); + let z = OsString::from("/home/z/.cargo/bin"); + let path_z = PathBuf::from(z); + path_entries.push(path_z); - prepend_path("PATH", path_entries, &mut cmd, &tp.process); - let envs: Vec<_> = cmd.get_envs().collect(); + prepend_path("PATH", path_entries, &mut cmd, &tp.process); + let envs: Vec<_> = cmd.get_envs().collect(); - assert_eq!( - envs, - &[( - OsStr::new("PATH"), - Some( - env::join_paths( - [ - "/home/z/.cargo/bin", - "/home/a/.cargo/bin", - "/home/b/.cargo/bin" - ] - .iter() - ) - .unwrap() - .as_os_str() + assert_eq!( + envs, + &[( + OsStr::new("PATH"), + Some( + env::join_paths( + [ + "/home/z/.cargo/bin", + "/home/a/.cargo/bin", + "/home/b/.cargo/bin" + ] + .iter() ) - ),] - ); - }); + .unwrap() + .as_os_str() + ) + ),] + ); } } diff --git a/src/test.rs b/src/test.rs index 8fd91dff65..24e11442b9 100644 --- a/src/test.rs +++ b/src/test.rs @@ -15,12 +15,11 @@ use std::process::Command; #[cfg(test)] use anyhow::Result; -pub use crate::cli::self_update::test::{with_saved_global_state, with_saved_path}; use crate::currentprocess::TestProcess; use crate::dist::TargetTriple; #[cfg(windows)] -pub use crate::cli::self_update::test::{get_path, with_saved_reg_value}; +pub use crate::cli::self_update::{get_path, RegistryGuard, RegistryValueId, USER_PATH}; // Things that can have environment variables applied to them. pub trait Env { diff --git a/src/test/mock/clitools.rs b/src/test/mock/clitools.rs index 15a3986de5..3ce3232674 100644 --- a/src/test/mock/clitools.rs +++ b/src/test/mock/clitools.rs @@ -8,6 +8,8 @@ use std::{ fmt::Debug, fs, io::{self, Write}, + mem, + ops::{Deref, DerefMut}, path::{Path, PathBuf}, process::Command, sync::{Arc, RwLock, RwLockWriteGuard}, @@ -16,7 +18,7 @@ use std::{ use enum_map::{enum_map, Enum, EnumMap}; use once_cell::sync::Lazy; -use tokio::runtime::Builder; +use tempfile::TempDir; use url::Url; use crate::cli::rustup_mode; @@ -186,7 +188,7 @@ impl ConstState { } /// State a test can interact and mutate -pub fn setup_test_state(test_dist_dir: tempfile::TempDir) -> (tempfile::TempDir, Config) { +pub async fn setup_test_state(test_dist_dir: tempfile::TempDir) -> (tempfile::TempDir, Config) { // Unset env variables that will break our testing env::remove_var("RUSTUP_UPDATE_ROOT"); env::remove_var("RUSTUP_TOOLCHAIN"); @@ -291,10 +293,14 @@ pub fn setup_test_state(test_dist_dir: tempfile::TempDir) -> (tempfile::TempDir, // Make sure the host triple matches the build triple. Otherwise testing a 32-bit build of // rustup on a 64-bit machine will fail, because the tests do not have the host detection // functionality built in. - config.run("rustup", ["set", "default-host", &this_host_triple()], &[]); + config + .run("rustup", ["set", "default-host", &this_host_triple()], &[]) + .await; // Set the auto update mode to disable, as most tests do not want to update rustup itself during the test. - config.run("rustup", ["set", "auto-self-update", "disable"], &[]); + config + .run("rustup", ["set", "auto-self-update", "disable"], &[]) + .await; // Create some custom toolchains create_custom_toolchains(&config.customdir); @@ -302,56 +308,95 @@ pub fn setup_test_state(test_dist_dir: tempfile::TempDir) -> (tempfile::TempDir, (test_dir, config) } -/// Run this to create the test environment containing rustup, and -/// a mock dist server. -pub fn test(s: Scenario, f: &dyn Fn(&mut Config)) { - // Things we might cache or what not +pub struct SelfUpdateTestContext { + pub config: Config, + _test_dir: TempDir, + self_dist_tmp: TempDir, +} + +impl SelfUpdateTestContext { + pub async fn new(version: &str) -> Self { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + + // Create a mock self-update server + let self_dist_tmp = tempfile::Builder::new() + .prefix("self_dist") + .tempdir_in(&cx.config.test_root_dir) + .unwrap(); + let self_dist = self_dist_tmp.path(); - // Mutable dist server - working toward elimination - let test_dist_dir = crate::test::test_dist_dir().unwrap(); - create_mock_dist_server(test_dist_dir.path(), s); + let root_url = create_local_update_server(self_dist, &cx.config.exedir, version); + cx.config.rustup_update_root = Some(root_url); + + let trip = this_host_triple(); + let dist_dir = self_dist.join(format!("archive/{version}/{trip}")); + let dist_exe = dist_dir.join(format!("rustup-init{EXE_SUFFIX}")); + let dist_tmp = dist_dir.join("rustup-init-tmp"); + + // Modify the exe so it hashes different + // 1) move out of the way the file + fs::rename(&dist_exe, &dist_tmp).unwrap(); + // 2) copy it + fs::copy(dist_tmp, &dist_exe).unwrap(); + // modify it + let mut dest_file = fs::OpenOptions::new() + .append(true) + .create(true) + .open(dist_exe) + .unwrap(); + writeln!(dest_file).unwrap(); - // Things that are just about the test itself - let (_test_dir, mut config) = setup_test_state(test_dist_dir); - // Pulled out of setup_test_state for clarity: the long term intent is to - // not have this at all. - if s != Scenario::None { - config.distdir = Some(config.test_dist_dir.path().to_path_buf()); + Self { + config: cx.config, + _test_dir: cx._test_dir, + self_dist_tmp, + } } - // Run the test - f(&mut config); + pub fn path(&self) -> &Path { + self.self_dist_tmp.path() + } } -fn create_local_update_server(self_dist: &Path, exedir: &Path, version: &str) -> String { - let trip = this_host_triple(); - let dist_dir = self_dist.join(format!("archive/{version}/{trip}")); - let dist_exe = dist_dir.join(format!("rustup-init{EXE_SUFFIX}")); - let rustup_bin = exedir.join(format!("rustup-init{EXE_SUFFIX}")); +pub struct CliTestContext { + pub config: Config, + _test_dir: TempDir, +} - fs::create_dir_all(dist_dir).unwrap(); - output_release_file(self_dist, "1", version); - // TODO: should this hardlink since the modify-codepath presumes it has to - // link break? - fs::copy(rustup_bin, dist_exe).unwrap(); +impl CliTestContext { + pub async fn new(scenario: Scenario) -> Self { + // Things we might cache or what not - let root_url = format!("file://{}", self_dist.display()); - root_url -} + // Mutable dist server - working toward elimination + let test_dist_dir = crate::test::test_dist_dir().unwrap(); + create_mock_dist_server(test_dist_dir.path(), scenario); -pub fn self_update_setup(f: &dyn Fn(&mut Config, &Path), version: &str) { - test(Scenario::SimpleV2, &|config| { - // Create a mock self-update server + // Things that are just about the test itself + let (_test_dir, mut config) = setup_test_state(test_dist_dir).await; + // Pulled out of setup_test_state for clarity: the long term intent is to + // not have this at all. + if scenario != Scenario::None { + config.distdir = Some(config.test_dist_dir.path().to_path_buf()); + } + Self { config, _test_dir } + } + + /// Move the dist server to the specified scenario and restore it + /// afterwards. + pub fn with_dist_dir(&mut self, scenario: Scenario) -> DistDirGuard<'_> { + self.config.distdir = Some(CONST_TEST_STATE.dist_server_for(scenario).unwrap()); + DistDirGuard { inner: self } + } + + pub fn with_update_server(&mut self, version: &str) -> UpdateServerGuard { let self_dist_tmp = tempfile::Builder::new() .prefix("self_dist") - .tempdir_in(&config.test_root_dir) + .tempdir() .unwrap(); let self_dist = self_dist_tmp.path(); - let root_url = create_local_update_server(self_dist, &config.exedir, version); - config.rustup_update_root = Some(root_url); - + let root_url = create_local_update_server(self_dist, &self.config.exedir, version); let trip = this_host_triple(); let dist_dir = self_dist.join(format!("archive/{version}/{trip}")); let dist_exe = dist_dir.join(format!("rustup-init{EXE_SUFFIX}")); @@ -370,39 +415,88 @@ pub fn self_update_setup(f: &dyn Fn(&mut Config, &Path), version: &str) { .unwrap(); writeln!(dest_file).unwrap(); - f(config, self_dist); - }); + self.config.rustup_update_root = Some(root_url); + UpdateServerGuard { + _self_dist: self_dist_tmp, + } + } + + pub fn change_dir(&mut self, path: &Path) -> WorkDirGuard<'_> { + let prev = self.config.workdir.replace(path.to_owned()); + WorkDirGuard { inner: self, prev } + } } -pub fn with_update_server(config: &mut Config, version: &str, f: &dyn Fn(&mut Config)) { - let self_dist_tmp = tempfile::Builder::new() - .prefix("self_dist") - .tempdir() - .unwrap(); - let self_dist = self_dist_tmp.path(); +#[must_use] +pub struct UpdateServerGuard { + _self_dist: TempDir, +} + +#[must_use] +pub struct WorkDirGuard<'a> { + inner: &'a mut CliTestContext, + prev: PathBuf, +} - let root_url = create_local_update_server(self_dist, &config.exedir, version); +impl Deref for WorkDirGuard<'_> { + type Target = CliTestContext; + + fn deref(&self) -> &Self::Target { + &*self.inner + } +} + +impl DerefMut for WorkDirGuard<'_> { + fn deref_mut(&mut self) -> &mut Self::Target { + self.inner + } +} + +impl Drop for WorkDirGuard<'_> { + fn drop(&mut self) { + self.inner.config.workdir.replace(mem::take(&mut self.prev)); + } +} + +#[must_use] +pub struct DistDirGuard<'a> { + inner: &'a mut CliTestContext, +} + +impl Deref for DistDirGuard<'_> { + type Target = CliTestContext; + + fn deref(&self) -> &Self::Target { + &*self.inner + } +} + +impl DerefMut for DistDirGuard<'_> { + fn deref_mut(&mut self) -> &mut Self::Target { + self.inner + } +} + +impl Drop for DistDirGuard<'_> { + fn drop(&mut self) { + self.inner.config.distdir = None; + } +} + +fn create_local_update_server(self_dist: &Path, exedir: &Path, version: &str) -> String { let trip = this_host_triple(); let dist_dir = self_dist.join(format!("archive/{version}/{trip}")); let dist_exe = dist_dir.join(format!("rustup-init{EXE_SUFFIX}")); - let dist_tmp = dist_dir.join("rustup-init-tmp"); - - // Modify the exe so it hashes different - // 1) move out of the way the file - fs::rename(&dist_exe, &dist_tmp).unwrap(); - // 2) copy it - fs::copy(dist_tmp, &dist_exe).unwrap(); - // modify it - let mut dest_file = fs::OpenOptions::new() - .append(true) - .create(true) - .open(dist_exe) - .unwrap(); - writeln!(dest_file).unwrap(); - - config.rustup_update_root = Some(root_url); - f(config); - config.rustup_update_root = None; + let rustup_bin = exedir.join(format!("rustup-init{EXE_SUFFIX}")); + + fs::create_dir_all(dist_dir).unwrap(); + output_release_file(self_dist, "1", version); + // TODO: should this hardlink since the modify-codepath presumes it has to + // link break? + fs::copy(rustup_bin, dist_exe).unwrap(); + + let root_url = format!("file://{}", self_dist.display()); + root_url } pub fn output_release_file(dist_dir: &Path, schema: &str, version: &str) { @@ -421,12 +515,6 @@ impl Config { self.workdir.borrow().clone() } - pub fn change_dir(&mut self, path: &Path, f: &dyn Fn(&mut Config)) { - let prev = self.workdir.replace(path.to_owned()); - f(self); - *self.workdir.borrow_mut() = prev; - } - pub fn create_rustup_sh_metadata(&self) { let rustup_dir = self.homedir.join(".rustup"); fs::create_dir_all(&rustup_dir).unwrap(); @@ -434,15 +522,6 @@ impl Config { raw::write_file(&version_file, "").unwrap(); } - /// Move the dist server to the specified scenario and restore it - /// afterwards. - pub fn with_scenario(&mut self, s: Scenario, f: &dyn Fn(&mut Config)) { - let dist_path = CONST_TEST_STATE.dist_server_for(s).unwrap(); - self.const_dist_dir = Some(dist_path); - f(self); - self.const_dist_dir = None; - } - pub fn cmd(&self, name: &str, args: I) -> Command where I: IntoIterator, @@ -514,9 +593,8 @@ impl Config { } /// Expect an ok status - #[track_caller] - pub fn expect_ok(&mut self, args: &[&str]) { - let out = self.run(args[0], &args[1..], &[]); + pub async fn expect_ok(&mut self, args: &[&str]) { + let out = self.run(args[0], &args[1..], &[]).await; if !out.ok { print_command(args, &out); println!("expected.ok: true"); @@ -525,9 +603,8 @@ impl Config { } /// Expect an err status and a string in stderr - #[track_caller] - pub fn expect_err(&self, args: &[&str], expected: &str) { - let out = self.run(args[0], &args[1..], &[]); + pub async fn expect_err(&self, args: &[&str], expected: &str) { + let out = self.run(args[0], &args[1..], &[]).await; if out.ok || !out.stderr.contains(expected) { print_command(args, &out); println!("expected.ok: false"); @@ -537,9 +614,8 @@ impl Config { } /// Expect an ok status and a string in stdout - #[track_caller] - pub fn expect_stdout_ok(&self, args: &[&str], expected: &str) { - let out = self.run(args[0], &args[1..], &[]); + pub async fn expect_stdout_ok(&self, args: &[&str], expected: &str) { + let out = self.run(args[0], &args[1..], &[]).await; if !out.ok || !out.stdout.contains(expected) { print_command(args, &out); println!("expected.ok: true"); @@ -548,9 +624,8 @@ impl Config { } } - #[track_caller] - pub fn expect_not_stdout_ok(&self, args: &[&str], expected: &str) { - let out = self.run(args[0], &args[1..], &[]); + pub async fn expect_not_stdout_ok(&self, args: &[&str], expected: &str) { + let out = self.run(args[0], &args[1..], &[]).await; if !out.ok || out.stdout.contains(expected) { print_command(args, &out); println!("expected.ok: true"); @@ -559,9 +634,8 @@ impl Config { } } - #[track_caller] - pub fn expect_not_stderr_ok(&self, args: &[&str], expected: &str) { - let out = self.run(args[0], &args[1..], &[]); + pub async fn expect_not_stderr_ok(&self, args: &[&str], expected: &str) { + let out = self.run(args[0], &args[1..], &[]).await; if !out.ok || out.stderr.contains(expected) { print_command(args, &out); println!("expected.ok: false"); @@ -570,9 +644,8 @@ impl Config { } } - #[track_caller] - pub fn expect_not_stderr_err(&self, args: &[&str], expected: &str) { - let out = self.run(args[0], &args[1..], &[]); + pub async fn expect_not_stderr_err(&self, args: &[&str], expected: &str) { + let out = self.run(args[0], &args[1..], &[]).await; if out.ok || out.stderr.contains(expected) { print_command(args, &out); println!("expected.ok: false"); @@ -582,9 +655,8 @@ impl Config { } /// Expect an ok status and a string in stderr - #[track_caller] - pub fn expect_stderr_ok(&self, args: &[&str], expected: &str) { - let out = self.run(args[0], &args[1..], &[]); + pub async fn expect_stderr_ok(&self, args: &[&str], expected: &str) { + let out = self.run(args[0], &args[1..], &[]).await; if !out.ok || !out.stderr.contains(expected) { print_command(args, &out); println!("expected.ok: true"); @@ -594,9 +666,8 @@ impl Config { } /// Expect an exact strings on stdout/stderr with an ok status code - #[track_caller] - pub fn expect_ok_ex(&mut self, args: &[&str], stdout: &str, stderr: &str) { - let out = self.run(args[0], &args[1..], &[]); + pub async fn expect_ok_ex(&mut self, args: &[&str], stdout: &str, stderr: &str) { + let out = self.run(args[0], &args[1..], &[]).await; if !out.ok || out.stdout != stdout || out.stderr != stderr { print_command(args, &out); println!("expected.ok: true"); @@ -609,9 +680,8 @@ impl Config { } /// Expect an exact strings on stdout/stderr with an error status code - #[track_caller] - pub fn expect_err_ex(&self, args: &[&str], stdout: &str, stderr: &str) { - let out = self.run(args[0], &args[1..], &[]); + pub async fn expect_err_ex(&self, args: &[&str], stdout: &str, stderr: &str) { + let out = self.run(args[0], &args[1..], &[]).await; if out.ok || out.stdout != stdout || out.stderr != stderr { print_command(args, &out); println!("expected.ok: false"); @@ -629,9 +699,8 @@ impl Config { } } - #[track_caller] - pub fn expect_ok_contains(&self, args: &[&str], stdout: &str, stderr: &str) { - let out = self.run(args[0], &args[1..], &[]); + pub async fn expect_ok_contains(&self, args: &[&str], stdout: &str, stderr: &str) { + let out = self.run(args[0], &args[1..], &[]).await; if !out.ok || !out.stdout.contains(stdout) || !out.stderr.contains(stderr) { print_command(args, &out); println!("expected.ok: true"); @@ -641,10 +710,9 @@ impl Config { } } - #[track_caller] - pub fn expect_ok_eq(&self, args1: &[&str], args2: &[&str]) { - let out1 = self.run(args1[0], &args1[1..], &[]); - let out2 = self.run(args2[0], &args2[1..], &[]); + pub async fn expect_ok_eq(&self, args1: &[&str], args2: &[&str]) { + let out1 = self.run(args1[0], &args1[1..], &[]).await; + let out2 = self.run(args2[0], &args2[1..], &[]).await; if !out1.ok || !out2.ok || out1.stdout != out2.stdout || out1.stderr != out2.stderr { print_command(args1, &out1); println!("expected.ok: true"); @@ -654,9 +722,8 @@ impl Config { } } - #[track_caller] - pub fn expect_component_executable(&self, cmd: &str) { - let out1 = self.run(cmd, ["--version"], &[]); + pub async fn expect_component_executable(&self, cmd: &str) { + let out1 = self.run(cmd, ["--version"], &[]).await; if !out1.ok { print_command(&[cmd, "--version"], &out1); println!("expected.ok: true"); @@ -664,9 +731,8 @@ impl Config { } } - #[track_caller] - pub fn expect_component_not_executable(&self, cmd: &str) { - let out1 = self.run(cmd, ["--version"], &[]); + pub async fn expect_component_not_executable(&self, cmd: &str) { + let out1 = self.run(cmd, ["--version"], &[]).await; if out1.ok { print_command(&[cmd, "--version"], &out1); println!("expected.ok: false"); @@ -674,7 +740,7 @@ impl Config { } } - pub fn run(&self, name: &str, args: I, env: &[(&str, &str)]) -> SanitizedOutput + pub async fn run(&self, name: &str, args: I, env: &[(&str, &str)]) -> SanitizedOutput where I: IntoIterator + Clone + Debug, A: AsRef, @@ -682,7 +748,7 @@ impl Config { let inprocess = allow_inprocess(name, args.clone()); let start = Instant::now(); let out = if inprocess { - self.run_inprocess(name, args.clone(), env) + self.run_inprocess(name, args.clone(), env).await } else { self.run_subprocess(name, args.clone(), env) }; @@ -704,7 +770,12 @@ impl Config { } #[cfg_attr(feature = "otel", tracing::instrument(skip_all))] - pub(crate) fn run_inprocess(&self, name: &str, args: I, env: &[(&str, &str)]) -> Output + pub(crate) async fn run_inprocess( + &self, + name: &str, + args: I, + env: &[(&str, &str)], + ) -> Output where I: IntoIterator, A: AsRef, @@ -725,31 +796,22 @@ impl Config { .into_boxed_str(), ); } - let mut builder = Builder::new_multi_thread(); - builder - .enable_all() - .worker_threads(2) - .max_blocking_threads(2); - let rt = builder.build().unwrap(); - rt.block_on(async { - let tp = - currentprocess::TestProcess::new(&*self.workdir.borrow(), &arg_strings, vars, ""); - let process_res = - rustup_mode::main(tp.process.current_dir().unwrap(), &tp.process).await; - // convert Err's into an ec - let ec = match process_res { - Ok(process_res) => process_res, - Err(e) => { - crate::cli::common::report_error(&e, &tp.process); - utils::ExitCode(1) - } - }; - Output { - status: Some(ec.0), - stderr: tp.stderr(), - stdout: tp.stdout(), + + let tp = currentprocess::TestProcess::new(&*self.workdir.borrow(), &arg_strings, vars, ""); + let process_res = rustup_mode::main(tp.process.current_dir().unwrap(), &tp.process).await; + // convert Err's into an ec + let ec = match process_res { + Ok(process_res) => process_res, + Err(e) => { + crate::cli::common::report_error(&e, &tp.process); + utils::ExitCode(1) } - }) + }; + Output { + status: Some(ec.0), + stderr: tp.stderr(), + stdout: tp.stdout(), + } } #[track_caller] diff --git a/tests/suite/cli_exact.rs b/tests/suite/cli_exact.rs index 7b2302d258..99735136df 100644 --- a/tests/suite/cli_exact.rs +++ b/tests/suite/cli_exact.rs @@ -3,29 +3,24 @@ use rustup::for_host; use rustup::test::{ - mock::clitools::{self, set_current_dist_date, with_update_server, Config, Scenario}, + mock::clitools::{self, set_current_dist_date, CliTestContext, Scenario}, this_host_triple, }; -/// Start a test with Scenario::None -fn test(f: &dyn Fn(&mut Config)) { - clitools::test(Scenario::None, f); -} - -#[test] -fn update_once() { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - config.expect_ok_ex( - &["rustup", "update", "nightly"], - for_host!( - r" +#[tokio::test] +async fn update_once() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config + .expect_ok_ex( + &["rustup", "update", "nightly"], + for_host!( + r" nightly-{0} installed - 1.3.0 (hash-nightly-2) " - ), - for_host!( - r"info: syncing channel updates for 'nightly-{0}' + ), + for_host!( + r"info: syncing channel updates for 'nightly-{0}' info: latest update on 2015-01-02, rust version 1.3.0 (hash-nightly-2) info: downloading component 'cargo' info: downloading component 'rust-docs' @@ -37,36 +32,39 @@ info: installing component 'rust-std' info: installing component 'rustc' info: default toolchain set to 'nightly-{0}' " - ), - ); - }) - }); + ), + ) + .await; } -#[test] -fn update_once_and_check_self_update() { +#[tokio::test] +async fn update_once_and_check_self_update() { let test_version = "2.0.0"; - test(&|config| { - with_update_server(config, test_version, &|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - config.expect_ok(&["rustup-init", "-y", "--no-modify-path"]); - config.expect_ok(&["rustup", "set", "auto-self-update", "check-only"]); - let current_version = env!("CARGO_PKG_VERSION"); - - config.expect_ok_ex( - &["rustup", "update", "nightly"], - &format!( - r" + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + let _dist_guard = cx.with_update_server(test_version); + cx.config + .expect_ok(&["rustup-init", "-y", "--no-modify-path"]) + .await; + cx.config + .expect_ok(&["rustup", "set", "auto-self-update", "check-only"]) + .await; + let current_version = env!("CARGO_PKG_VERSION"); + + cx.config + .expect_ok_ex( + &["rustup", "update", "nightly"], + &format!( + r" nightly-{} installed - 1.3.0 (hash-nightly-2) rustup - Update available : {} -> {} ", - &this_host_triple(), - current_version, - test_version - ), - for_host!( - r"info: syncing channel updates for 'nightly-{0}' + &this_host_triple(), + current_version, + test_version + ), + for_host!( + r"info: syncing channel updates for 'nightly-{0}' info: latest update on 2015-01-02, rust version 1.3.0 (hash-nightly-2) info: downloading component 'cargo' info: downloading component 'rust-docs' @@ -77,32 +75,33 @@ info: installing component 'rust-docs' info: installing component 'rust-std' info: installing component 'rustc' " - ), - ); - }) - }) - }) + ), + ) + .await; } -#[test] -fn update_once_and_self_update() { +#[tokio::test] +async fn update_once_and_self_update() { let test_version = "2.0.0"; - - test(&|config| { - with_update_server(config, test_version, &|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - config.expect_ok(&["rustup-init", "-y", "--no-modify-path"]); - config.expect_ok(&["rustup", "set", "auto-self-update", "enable"]); - config.expect_ok_ex( - &["rustup", "update", "nightly"], - for_host!( - r" + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + let _dist_guard = cx.with_update_server(test_version); + cx.config + .expect_ok(&["rustup-init", "-y", "--no-modify-path"]) + .await; + cx.config + .expect_ok(&["rustup", "set", "auto-self-update", "enable"]) + .await; + cx.config + .expect_ok_ex( + &["rustup", "update", "nightly"], + for_host!( + r" nightly-{0} installed - 1.3.0 (hash-nightly-2) " - ), - for_host!( - r"info: syncing channel updates for 'nightly-{0}' + ), + for_host!( + r"info: syncing channel updates for 'nightly-{0}' info: latest update on 2015-01-02, rust version 1.3.0 (hash-nightly-2) info: downloading component 'cargo' info: downloading component 'rust-docs' @@ -115,133 +114,135 @@ info: installing component 'rustc' info: checking for self-update info: downloading self-update " - ), - ); - }) - }) - }) + ), + ) + .await; } -#[test] -fn update_again() { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - config.expect_ok(&["rustup", "update", "nightly"]); - config.expect_ok(&["rustup", "upgrade", "nightly"]); - config.expect_ok_ex( - &["rustup", "update", "nightly"], - for_host!( - r" +#[tokio::test] +async fn update_again() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "update", "nightly"]).await; + cx.config.expect_ok(&["rustup", "upgrade", "nightly"]).await; + cx.config + .expect_ok_ex( + &["rustup", "update", "nightly"], + for_host!( + r" nightly-{0} unchanged - 1.3.0 (hash-nightly-2) " - ), - for_host!( - r"info: syncing channel updates for 'nightly-{0}' + ), + for_host!( + r"info: syncing channel updates for 'nightly-{0}' " - ), - ); - config.expect_ok_ex( - &["rustup", "upgrade", "nightly"], - for_host!( - r" + ), + ) + .await; + cx.config + .expect_ok_ex( + &["rustup", "upgrade", "nightly"], + for_host!( + r" nightly-{0} unchanged - 1.3.0 (hash-nightly-2) " - ), - for_host!( - r"info: syncing channel updates for 'nightly-{0}' + ), + for_host!( + r"info: syncing channel updates for 'nightly-{0}' " - ), - ); - }) - }); + ), + ) + .await; } -#[test] -fn check_updates_none() { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - config.expect_ok(&["rustup", "toolchain", "add", "stable", "beta", "nightly"]); - config.expect_stdout_ok( - &["rustup", "check"], - for_host!( - r"stable-{0} - Up to date : 1.1.0 (hash-stable-1.1.0) +#[tokio::test] +async fn check_updates_none() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config + .expect_ok(&["rustup", "toolchain", "add", "stable", "beta", "nightly"]) + .await; + cx.config + .expect_stdout_ok( + &["rustup", "check"], + for_host!( + r"stable-{0} - Up to date : 1.1.0 (hash-stable-1.1.0) beta-{0} - Up to date : 1.2.0 (hash-beta-1.2.0) nightly-{0} - Up to date : 1.3.0 (hash-nightly-2) " - ), - ); - }) - }) + ), + ) + .await; } -#[test] -fn check_updates_some() { - test(&|config| { - config.with_scenario(Scenario::ArchivesV2_2015_01_01, &|config| { - config.expect_ok(&["rustup", "toolchain", "add", "stable", "beta", "nightly"]); - }); - config.with_scenario(Scenario::SimpleV2, &|config| { - config.expect_stdout_ok( - &["rustup", "check"], - for_host!( - r"stable-{0} - Update available : 1.0.0 (hash-stable-1.0.0) -> 1.1.0 (hash-stable-1.1.0) +#[tokio::test] +async fn check_updates_some() { + let mut cx = CliTestContext::new(Scenario::None).await; + + { + let mut cx = cx.with_dist_dir(Scenario::ArchivesV2_2015_01_01); + cx.config + .expect_ok(&["rustup", "toolchain", "add", "stable", "beta", "nightly"]) + .await; + } + + let cx = cx.with_dist_dir(Scenario::SimpleV2); + cx.config.expect_stdout_ok( + &["rustup", "check"], + for_host!( + r"stable-{0} - Update available : 1.0.0 (hash-stable-1.0.0) -> 1.1.0 (hash-stable-1.1.0) beta-{0} - Update available : 1.1.0 (hash-beta-1.1.0) -> 1.2.0 (hash-beta-1.2.0) nightly-{0} - Update available : 1.2.0 (hash-nightly-1) -> 1.3.0 (hash-nightly-2) " - ), - ); - }) - }) + ), + ).await; } -#[test] -fn check_updates_self() { +#[tokio::test] +async fn check_updates_self() { let test_version = "2.0.0"; + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + let _dist_guard = cx.with_update_server(test_version); + let current_version = env!("CARGO_PKG_VERSION"); - test(&|config| { - with_update_server(config, test_version, &|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - let current_version = env!("CARGO_PKG_VERSION"); - - config.expect_stdout_ok( - &["rustup", "check"], - &format!( - r"rustup - Update available : {current_version} -> {test_version} + cx.config + .expect_stdout_ok( + &["rustup", "check"], + &format!( + r"rustup - Update available : {current_version} -> {test_version} " - ), - ); - }) - }) - }) + ), + ) + .await; } -#[test] -fn check_updates_self_no_change() { +#[tokio::test] +async fn check_updates_self_no_change() { let current_version = env!("CARGO_PKG_VERSION"); - - test(&|config| { - with_update_server(config, current_version, &|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - config.expect_stdout_ok( - &["rustup", "check"], - &format!( - r"rustup - Up to date : {current_version} + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + let _dist_guard = cx.with_update_server(current_version); + cx.config + .expect_stdout_ok( + &["rustup", "check"], + &format!( + r"rustup - Up to date : {current_version} " - ), - ); - }) - }) - }) + ), + ) + .await; } -#[test] -fn check_updates_with_update() { - test(&|config| { - config.with_scenario(Scenario::ArchivesV2_2015_01_01, &|config| { - config.expect_ok(&["rustup", "toolchain", "add", "stable", "beta", "nightly"]); - config.expect_stdout_ok( +#[tokio::test] +async fn check_updates_with_update() { + let mut cx = CliTestContext::new(Scenario::None).await; + + { + let mut cx = cx.with_dist_dir(Scenario::ArchivesV2_2015_01_01); + cx.config + .expect_ok(&["rustup", "toolchain", "add", "stable", "beta", "nightly"]) + .await; + cx.config + .expect_stdout_ok( &["rustup", "check"], for_host!( r"stable-{0} - Up to date : 1.0.0 (hash-stable-1.0.0) @@ -249,46 +250,46 @@ beta-{0} - Up to date : 1.1.0 (hash-beta-1.1.0) nightly-{0} - Up to date : 1.2.0 (hash-nightly-1) " ), - ); - }); - config.with_scenario(Scenario::SimpleV2, &|config | { - config.expect_stdout_ok( - &["rustup", "check"], - for_host!( - r"stable-{0} - Update available : 1.0.0 (hash-stable-1.0.0) -> 1.1.0 (hash-stable-1.1.0) + ) + .await; + } + + let mut cx = cx.with_dist_dir(Scenario::SimpleV2); + cx.config.expect_stdout_ok( + &["rustup", "check"], + for_host!( + r"stable-{0} - Update available : 1.0.0 (hash-stable-1.0.0) -> 1.1.0 (hash-stable-1.1.0) beta-{0} - Update available : 1.1.0 (hash-beta-1.1.0) -> 1.2.0 (hash-beta-1.2.0) nightly-{0} - Update available : 1.2.0 (hash-nightly-1) -> 1.3.0 (hash-nightly-2) " - ), - ); - config.expect_ok(&["rustup", "update", "beta"]); - config.expect_stdout_ok( - &["rustup", "check"], - for_host!( - r"stable-{0} - Update available : 1.0.0 (hash-stable-1.0.0) -> 1.1.0 (hash-stable-1.1.0) + ), + ).await; + cx.config.expect_ok(&["rustup", "update", "beta"]).await; + cx.config.expect_stdout_ok( + &["rustup", "check"], + for_host!( + r"stable-{0} - Update available : 1.0.0 (hash-stable-1.0.0) -> 1.1.0 (hash-stable-1.1.0) beta-{0} - Up to date : 1.2.0 (hash-beta-1.2.0) nightly-{0} - Update available : 1.2.0 (hash-nightly-1) -> 1.3.0 (hash-nightly-2) " - ), - ); - }) - }); + ), + ).await; } -#[test] -fn default() { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - config.expect_ok_ex( - &["rustup", "default", "nightly"], - for_host!( - r" +#[tokio::test] +async fn default() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config + .expect_ok_ex( + &["rustup", "default", "nightly"], + for_host!( + r" nightly-{0} installed - 1.3.0 (hash-nightly-2) " - ), - for_host!( - r"info: syncing channel updates for 'nightly-{0}' + ), + for_host!( + r"info: syncing channel updates for 'nightly-{0}' info: latest update on 2015-01-02, rust version 1.3.0 (hash-nightly-2) info: downloading component 'cargo' info: downloading component 'rust-docs' @@ -300,419 +301,435 @@ info: installing component 'rust-std' info: installing component 'rustc' info: default toolchain set to 'nightly-{0}' " - ), - ); - }) - }); + ), + ) + .await; } -#[test] -fn override_again() { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - let cwd = config.current_dir(); - config.expect_ok(&["rustup", "override", "add", "nightly"]); - config.expect_ok_ex( - &["rustup", "override", "add", "nightly"], - "", - &format!( - r"info: override toolchain for '{}' set to 'nightly-{1}' +#[tokio::test] +async fn override_again() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + let cwd = cx.config.current_dir(); + cx.config + .expect_ok(&["rustup", "override", "add", "nightly"]) + .await; + cx.config + .expect_ok_ex( + &["rustup", "override", "add", "nightly"], + "", + &format!( + r"info: override toolchain for '{}' set to 'nightly-{1}' ", - cwd.display(), - &this_host_triple() - ), - ); - }) - }); + cwd.display(), + &this_host_triple() + ), + ) + .await; } -#[test] -fn remove_override() { +#[tokio::test] +async fn remove_override() { for keyword in &["remove", "unset"] { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - let cwd = config.current_dir(); - config.expect_ok(&["rustup", "override", "add", "nightly"]); - config.expect_ok_ex( - &["rustup", "override", keyword], - r"", - &format!("info: override toolchain for '{}' removed\n", cwd.display()), - ); - }) - }); + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + let cwd = cx.config.current_dir(); + cx.config + .expect_ok(&["rustup", "override", "add", "nightly"]) + .await; + cx.config + .expect_ok_ex( + &["rustup", "override", keyword], + r"", + &format!("info: override toolchain for '{}' removed\n", cwd.display()), + ) + .await; } } -#[test] -fn remove_override_none() { +#[tokio::test] +async fn remove_override_none() { for keyword in &["remove", "unset"] { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - let cwd = config.current_dir(); - config.expect_ok_ex( - &["rustup", "override", keyword], - r"", - &format!( - "info: no override toolchain for '{}' + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + let cwd = cx.config.current_dir(); + cx.config + .expect_ok_ex( + &["rustup", "override", keyword], + r"", + &format!( + "info: no override toolchain for '{}' info: you may use `--path ` option to remove override toolchain for a specific path\n", - cwd.display() - ), - ); - }) - }); + cwd.display() + ), + ) + .await; } } -#[test] -fn remove_override_with_path() { +#[tokio::test] +async fn remove_override_with_path() { for keyword in &["remove", "unset"] { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - let dir = tempfile::Builder::new() - .prefix("rustup-test") - .tempdir() - .unwrap(); - config.change_dir(dir.path(), &|config| { - config.expect_ok(&["rustup", "override", "add", "nightly"]); - }); - config.expect_ok_ex( - &[ - "rustup", - "override", - keyword, - "--path", - dir.path().to_str().unwrap(), - ], - r"", - &format!( - "info: override toolchain for '{}' removed\n", - dir.path().display() - ), - ); - }) - }); + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + let dir = tempfile::Builder::new() + .prefix("rustup-test") + .tempdir() + .unwrap(); + + { + let mut cx = cx.change_dir(dir.path()); + cx.config + .expect_ok(&["rustup", "override", "add", "nightly"]) + .await; + } + + cx.config + .expect_ok_ex( + &[ + "rustup", + "override", + keyword, + "--path", + dir.path().to_str().unwrap(), + ], + r"", + &format!( + "info: override toolchain for '{}' removed\n", + dir.path().display() + ), + ) + .await; } } -#[test] -fn remove_override_with_path_deleted() { +#[tokio::test] +async fn remove_override_with_path_deleted() { for keyword in &["remove", "unset"] { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - let path = { - let dir = tempfile::Builder::new() - .prefix("rustup-test") - .tempdir() - .unwrap(); - let path = std::fs::canonicalize(dir.path()).unwrap(); - config.change_dir(&path, &|config| { - config.expect_ok(&["rustup", "override", "add", "nightly"]); - }); - path - }; - config.expect_ok_ex( - &[ - "rustup", - "override", - keyword, - "--path", - path.to_str().unwrap(), - ], - r"", - &format!( - "info: override toolchain for '{}' removed\n", - path.display() - ), - ); - }) - }); + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + let path = { + let dir = tempfile::Builder::new() + .prefix("rustup-test") + .tempdir() + .unwrap(); + let path = std::fs::canonicalize(dir.path()).unwrap(); + let mut cx = cx.change_dir(&path); + cx.config + .expect_ok(&["rustup", "override", "add", "nightly"]) + .await; + path + }; + cx.config + .expect_ok_ex( + &[ + "rustup", + "override", + keyword, + "--path", + path.to_str().unwrap(), + ], + r"", + &format!( + "info: override toolchain for '{}' removed\n", + path.display() + ), + ) + .await; } } -#[test] +#[tokio::test] #[cfg_attr(target_os = "windows", ignore)] // FIXME #1103 -fn remove_override_nonexistent() { +async fn remove_override_nonexistent() { for keyword in &["remove", "unset"] { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - let path = { - let dir = tempfile::Builder::new() - .prefix("rustup-test") - .tempdir() - .unwrap(); - let path = std::fs::canonicalize(dir.path()).unwrap(); - config.change_dir(&path, &|config| { - config.expect_ok(&["rustup", "override", "add", "nightly"]); - }); - path - }; - // FIXME TempDir seems to succumb to difficulties removing dirs on windows - let _ = rustup::utils::raw::remove_dir(&path); - assert!(!path.exists()); - config.expect_ok_ex( - &["rustup", "override", keyword, "--nonexistent"], - r"", - &format!( - "info: override toolchain for '{}' removed\n", - path.display() - ), - ); - }) - }); + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + let path = { + let dir = tempfile::Builder::new() + .prefix("rustup-test") + .tempdir() + .unwrap(); + let path = std::fs::canonicalize(dir.path()).unwrap(); + let mut cx = cx.change_dir(&path); + cx.config + .expect_ok(&["rustup", "override", "add", "nightly"]) + .await; + path + }; + // FIXME TempDir seems to succumb to difficulties removing dirs on windows + let _ = rustup::utils::raw::remove_dir(&path); + assert!(!path.exists()); + cx.config + .expect_ok_ex( + &["rustup", "override", keyword, "--nonexistent"], + r"", + &format!( + "info: override toolchain for '{}' removed\n", + path.display() + ), + ) + .await; } } -#[test] -fn list_overrides() { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - let cwd = std::fs::canonicalize(config.current_dir()).unwrap(); - let mut cwd_formatted = format!("{}", cwd.display()); - - if cfg!(windows) { - cwd_formatted.drain(..4); - } - - let trip = this_host_triple(); - config.expect_ok(&["rustup", "override", "add", "nightly"]); - config.expect_ok_ex( - &["rustup", "override", "list"], - &format!( - "{:<40}\t{:<20}\n", - cwd_formatted, - &format!("nightly-{trip}") - ), - r"", - ); - }) - }); +#[tokio::test] +async fn list_overrides() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + let cwd = std::fs::canonicalize(cx.config.current_dir()).unwrap(); + let mut cwd_formatted = format!("{}", cwd.display()); + + if cfg!(windows) { + cwd_formatted.drain(..4); + } + + let trip = this_host_triple(); + cx.config + .expect_ok(&["rustup", "override", "add", "nightly"]) + .await; + cx.config + .expect_ok_ex( + &["rustup", "override", "list"], + &format!( + "{:<40}\t{:<20}\n", + cwd_formatted, + &format!("nightly-{trip}") + ), + r"", + ) + .await; } -#[test] -fn list_overrides_with_nonexistent() { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - let trip = this_host_triple(); - - let nonexistent_path = { - let dir = tempfile::Builder::new() - .prefix("rustup-test") - .tempdir() - .unwrap(); - config.change_dir(dir.path(), &|config| { - config.expect_ok(&["rustup", "override", "add", "nightly"]); - }); - std::fs::canonicalize(dir.path()).unwrap() - }; - // FIXME TempDir seems to succumb to difficulties removing dirs on windows - let _ = rustup::utils::raw::remove_dir(&nonexistent_path); - assert!(!nonexistent_path.exists()); - let mut path_formatted = format!("{}", nonexistent_path.display()); - - if cfg!(windows) { - path_formatted.drain(..4); - } - - config.expect_ok_ex( - &["rustup", "override", "list"], - &format!( - "{:<40}\t{:<20}\n\n", - path_formatted + " (not a directory)", - &format!("nightly-{trip}") - ), - "info: you may remove overrides for non-existent directories with +#[tokio::test] +async fn list_overrides_with_nonexistent() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + let trip = this_host_triple(); + + let nonexistent_path = { + let dir = tempfile::Builder::new() + .prefix("rustup-test") + .tempdir() + .unwrap(); + let mut cx = cx.change_dir(dir.path()); + cx.config + .expect_ok(&["rustup", "override", "add", "nightly"]) + .await; + std::fs::canonicalize(dir.path()).unwrap() + }; + // FIXME TempDir seems to succumb to difficulties removing dirs on windows + let _ = rustup::utils::raw::remove_dir(&nonexistent_path); + assert!(!nonexistent_path.exists()); + let mut path_formatted = format!("{}", nonexistent_path.display()); + + if cfg!(windows) { + path_formatted.drain(..4); + } + + cx.config + .expect_ok_ex( + &["rustup", "override", "list"], + &format!( + "{:<40}\t{:<20}\n\n", + path_formatted + " (not a directory)", + &format!("nightly-{trip}") + ), + "info: you may remove overrides for non-existent directories with `rustup override unset --nonexistent`\n", - ); - }) - }); + ) + .await; } -#[test] -fn update_no_manifest() { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - config.expect_err_ex( - &["rustup", "update", "nightly-2016-01-01"], - r"", - for_host!( - r"info: syncing channel updates for 'nightly-2016-01-01-{0}' +#[tokio::test] +async fn update_no_manifest() { + let cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config + .expect_err_ex( + &["rustup", "update", "nightly-2016-01-01"], + r"", + for_host!( + r"info: syncing channel updates for 'nightly-2016-01-01-{0}' error: no release found for 'nightly-2016-01-01' " - ), - ); - }) - }); + ), + ) + .await; } // Issue #111 -#[test] -fn update_custom_toolchain() { - test(&|config| { - // installable toolchains require 2 digits in the DD and MM fields, so this is - // treated as a custom toolchain, which can't be used with update. - config.expect_err( +#[tokio::test] +async fn update_custom_toolchain() { + let cx = CliTestContext::new(Scenario::None).await; + // installable toolchains require 2 digits in the DD and MM fields, so this is + // treated as a custom toolchain, which can't be used with update. + cx.config + .expect_err( &["rustup", "update", "nightly-2016-03-1"], "invalid toolchain name: 'nightly-2016-03-1'", - ); - }); + ) + .await; } -#[test] -fn default_custom_not_installed_toolchain() { - test(&|config| { - // installable toolchains require 2 digits in the DD and MM fields, so this is - // treated as a custom toolchain, which isn't installed. - config.expect_err( +#[tokio::test] +async fn default_custom_not_installed_toolchain() { + let cx = CliTestContext::new(Scenario::None).await; + // installable toolchains require 2 digits in the DD and MM fields, so this is + // treated as a custom toolchain, which isn't installed. + cx.config + .expect_err( &["rustup", "default", "nightly-2016-03-1"], "toolchain 'nightly-2016-03-1' is not installed", - ); - }); + ) + .await; } -#[test] -fn default_none() { - test(&|config| { - config.expect_stderr_ok( +#[tokio::test] +async fn default_none() { + let mut cx = CliTestContext::new(Scenario::None).await; + cx.config + .expect_stderr_ok( &["rustup", "default", "none"], "info: default toolchain unset", - ); + ) + .await; - config.expect_ok_ex( + cx.config + .expect_ok_ex( &["rustup", "default"], "no default toolchain is configured\n", "", - ); + ) + .await; - config.expect_err_ex( + cx.config.expect_err_ex( &["rustc", "--version"], "", "error: rustup could not choose a version of rustc to run, because one wasn't specified explicitly, and no default is configured. help: run 'rustup default stable' to download the latest stable release of Rust and set it as your default toolchain. ", - ); - }) + ).await; } -#[test] -fn list_targets() { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - let trip = this_host_triple(); - let mut sorted = [ - format!("{} (installed)", &*trip), - format!("{} (installed)", clitools::CROSS_ARCH1), - clitools::CROSS_ARCH2.to_string(), - ]; - sorted.sort(); - - let expected = format!("{}\n{}\n{}\n", sorted[0], sorted[1], sorted[2]); - - config.expect_ok(&["rustup", "default", "nightly"]); - config.expect_ok(&["rustup", "target", "add", clitools::CROSS_ARCH1]); - config.expect_ok_ex(&["rustup", "target", "list"], &expected, r""); - }) - }); +#[tokio::test] +async fn list_targets() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + let trip = this_host_triple(); + let mut sorted = [ + format!("{} (installed)", &*trip), + format!("{} (installed)", clitools::CROSS_ARCH1), + clitools::CROSS_ARCH2.to_string(), + ]; + sorted.sort(); + + let expected = format!("{}\n{}\n{}\n", sorted[0], sorted[1], sorted[2]); + + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + cx.config + .expect_ok(&["rustup", "target", "add", clitools::CROSS_ARCH1]) + .await; + cx.config + .expect_ok_ex(&["rustup", "target", "list"], &expected, r"") + .await; } -#[test] -fn list_targets_quiet() { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - let trip = this_host_triple(); - let mut sorted = [ - trip, - clitools::CROSS_ARCH1.to_string(), - clitools::CROSS_ARCH2.to_string(), - ]; - sorted.sort(); - - let expected = format!("{}\n{}\n{}\n", sorted[0], sorted[1], sorted[2]); - - config.expect_ok(&["rustup", "default", "nightly"]); - config.expect_ok(&["rustup", "target", "add", clitools::CROSS_ARCH1]); - config.expect_ok_ex(&["rustup", "target", "list", "--quiet"], &expected, r""); - }) - }); +#[tokio::test] +async fn list_targets_quiet() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + let trip = this_host_triple(); + let mut sorted = [ + trip, + clitools::CROSS_ARCH1.to_string(), + clitools::CROSS_ARCH2.to_string(), + ]; + sorted.sort(); + + let expected = format!("{}\n{}\n{}\n", sorted[0], sorted[1], sorted[2]); + + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + cx.config + .expect_ok(&["rustup", "target", "add", clitools::CROSS_ARCH1]) + .await; + cx.config + .expect_ok_ex(&["rustup", "target", "list", "--quiet"], &expected, r"") + .await; } -#[test] -fn list_installed_targets() { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - let trip = this_host_triple(); - let mut sorted = [ - trip, - clitools::CROSS_ARCH1.to_string(), - clitools::CROSS_ARCH2.to_string(), - ]; - sorted.sort(); - - let expected = format!("{}\n{}\n{}\n", sorted[0], sorted[1], sorted[2]); - - config.expect_ok(&["rustup", "default", "nightly"]); - config.expect_ok(&["rustup", "target", "add", clitools::CROSS_ARCH1]); - config.expect_ok(&["rustup", "target", "add", clitools::CROSS_ARCH2]); - config.expect_ok_ex(&["rustup", "target", "list", "--installed"], &expected, r""); - }) - }); +#[tokio::test] +async fn list_installed_targets() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + let trip = this_host_triple(); + let mut sorted = [ + trip, + clitools::CROSS_ARCH1.to_string(), + clitools::CROSS_ARCH2.to_string(), + ]; + sorted.sort(); + + let expected = format!("{}\n{}\n{}\n", sorted[0], sorted[1], sorted[2]); + + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + cx.config + .expect_ok(&["rustup", "target", "add", clitools::CROSS_ARCH1]) + .await; + cx.config + .expect_ok(&["rustup", "target", "add", clitools::CROSS_ARCH2]) + .await; + cx.config + .expect_ok_ex(&["rustup", "target", "list", "--installed"], &expected, r"") + .await; } -#[test] -fn cross_install_indicates_target() { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - config.expect_ok(&["rustup", "default", "nightly"]); - // TODO error 'nightly-x86_64-apple-darwin' is not installed - config.expect_ok_ex( - &["rustup", "target", "add", clitools::CROSS_ARCH1], - r"", - &format!( - r"info: downloading component 'rust-std' for '{0}' +#[tokio::test] +async fn cross_install_indicates_target() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + // TODO error 'nightly-x86_64-apple-darwin' is not installed + cx.config + .expect_ok_ex( + &["rustup", "target", "add", clitools::CROSS_ARCH1], + r"", + &format!( + r"info: downloading component 'rust-std' for '{0}' info: installing component 'rust-std' for '{0}' ", - clitools::CROSS_ARCH1 - ), - ); - }) - }); + clitools::CROSS_ARCH1 + ), + ) + .await; } // issue #927 -#[test] -fn undefined_linked_toolchain() { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - config.expect_err_ex( - &["cargo", "+bogus", "test"], - r"", - "error: toolchain 'bogus' is not installed\n", - ); - }) - }); +#[tokio::test] +async fn undefined_linked_toolchain() { + let cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config + .expect_err_ex( + &["cargo", "+bogus", "test"], + r"", + "error: toolchain 'bogus' is not installed\n", + ) + .await; } -#[test] -fn install_by_version_number() { - test(&|config| { - config.with_scenario(Scenario::ArchivesV2TwoVersions, &|config| { - config.expect_ok(&["rustup", "toolchain", "add", "0.100.99"]); - }) - }) +#[tokio::test] +async fn install_by_version_number() { + let mut cx = CliTestContext::new(Scenario::ArchivesV2TwoVersions).await; + cx.config + .expect_ok(&["rustup", "toolchain", "add", "0.100.99"]) + .await; } // issue #2191 -#[test] -fn install_unreleased_component() { - clitools::test(Scenario::MissingComponentMulti, &|config| { - // Initial channel content is host + rls + multiarch-std - set_current_dist_date(config, "2019-09-12"); - config.expect_ok(&["rustup", "default", "nightly"]); - config.expect_ok(&["rustup", "component", "add", "rls"]); - config.expect_ok(&["rustup", "target", "add", clitools::MULTI_ARCH1]); - - // Next channel variant should have host + rls but not multiarch-std - set_current_dist_date(config, "2019-09-13"); - config.expect_ok_ex( +#[tokio::test] +async fn install_unreleased_component() { + let mut cx = CliTestContext::new(Scenario::MissingComponentMulti).await; + // Initial channel content is host + rls + multiarch-std + set_current_dist_date(&cx.config, "2019-09-12"); + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + cx.config + .expect_ok(&["rustup", "component", "add", "rls"]) + .await; + cx.config + .expect_ok(&["rustup", "target", "add", clitools::MULTI_ARCH1]) + .await; + + // Next channel variant should have host + rls but not multiarch-std + set_current_dist_date(&cx.config, "2019-09-13"); + cx.config + .expect_ok_ex( &["rustup", "update", "nightly"], for_host!( r" @@ -729,11 +746,13 @@ info: syncing channel updates for 'nightly-2019-09-12-{0}' this_host_triple(), clitools::MULTI_ARCH1 ), - ); + ) + .await; - // Next channel variant should have host + multiarch-std but have rls missing - set_current_dist_date(config, "2019-09-14"); - config.expect_ok_ex( + // Next channel variant should have host + multiarch-std but have rls missing + set_current_dist_date(&cx.config, "2019-09-14"); + cx.config + .expect_ok_ex( &["rustup", "update", "nightly"], for_host!( r" @@ -753,6 +772,6 @@ info: syncing channel updates for 'nightly-2019-09-12-{0}' this_host_triple(), clitools::MULTI_ARCH1, ), - ); - }) + ) + .await; } diff --git a/tests/suite/cli_inst_interactive.rs b/tests/suite/cli_inst_interactive.rs index b1fb575d65..07e21a4378 100644 --- a/tests/suite/cli_inst_interactive.rs +++ b/tests/suite/cli_inst_interactive.rs @@ -5,10 +5,13 @@ use std::io::Write; use std::process::Stdio; use rustup::for_host; +use rustup::test::mock::clitools::CliTestContext; use rustup::test::{ mock::clitools::{self, set_current_dist_date, Config, SanitizedOutput, Scenario}, - this_host_triple, with_saved_path, + this_host_triple, }; +#[cfg(windows)] +use rustup::test::{RegistryGuard, USER_PATH}; use rustup::utils::raw; fn run_input(config: &Config, args: &[&str], input: &str) -> SanitizedOutput { @@ -48,33 +51,33 @@ fn run_input_with_env( } } -#[test] -fn update() { - clitools::test(Scenario::SimpleV2, &|config| { - with_saved_path(&mut || { - run_input(config, &["rustup-init"], "\n\n"); - let out = run_input(config, &["rustup-init"], "\n\n"); - assert!(out.ok, "stdout:\n{}\nstderr:\n{}", out.stdout, out.stderr); - }) - }); +#[tokio::test] +async fn update() { + let cx = CliTestContext::new(Scenario::SimpleV2).await; + #[cfg(windows)] + let _path_guard = RegistryGuard::new(&USER_PATH).unwrap(); + + run_input(&cx.config, &["rustup-init"], "\n\n"); + let out = run_input(&cx.config, &["rustup-init"], "\n\n"); + assert!(out.ok, "stdout:\n{}\nstderr:\n{}", out.stdout, out.stderr); } // Testing that the right number of blank lines are printed after the // 'pre-install' message and before the 'post-install' message - overall smoke // test for the install case. -#[test] -fn smoke_case_install_no_modify_path() { - clitools::test(Scenario::SimpleV2, &|config| { - let out = run_input(config, &["rustup-init", "--no-modify-path"], "\n\n"); - assert!(out.ok); - // 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(for_host!( - r" +#[tokio::test] +async fn smoke_case_install_no_modify_path() { + let cx = CliTestContext::new(Scenario::SimpleV2).await; + let out = run_input(&cx.config, &["rustup-init", "--no-modify-path"], "\n\n"); + assert!(out.ok); + // 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(for_host!( + r" This path needs to be in your PATH environment variable, but will not be added automatically. @@ -100,346 +103,350 @@ Current installation options: Rust is installed now. Great! " - )), - "pattern not found in \"\"\"{}\"\"\"", - out.stdout - ); - if cfg!(unix) { - assert!(!config.homedir.join(".profile").exists()); - assert!(config.cargodir.join("env").exists()); - } - }); -} - -#[test] -fn smoke_case_install_with_path_install() { - clitools::test(Scenario::SimpleV2, &|config| { - with_saved_path(&mut || { - let out = run_input(config, &["rustup-init"], "\n\n"); - assert!(out.ok); - assert!(!out - .stdout - .contains("This path needs to be in your PATH environment variable")); - }) - }); -} - -#[test] -fn blank_lines_around_stderr_log_output_update() { - clitools::test(Scenario::SimpleV2, &|config| { - config.expect_ok(&["rustup-init", "-y", "--no-modify-path"]); - let out = run_input( - config, - &[ - "rustup-init", - "--no-update-default-toolchain", - "--no-modify-path", - ], - "\n\n", - ); - println!("-- stdout --\n {}", out.stdout); - println!("-- stderr --\n {}", out.stderr); + )), + "pattern not found in \"\"\"{}\"\"\"", + out.stdout + ); + if cfg!(unix) { + assert!(!cx.config.homedir.join(".profile").exists()); + assert!(cx.config.cargodir.join("env").exists()); + } +} - assert!(out.stdout.contains( - r" +#[tokio::test] +async fn smoke_case_install_with_path_install() { + let cx = CliTestContext::new(Scenario::SimpleV2).await; + #[cfg(windows)] + let _path_guard = RegistryGuard::new(&USER_PATH).unwrap(); + + let out = run_input(&cx.config, &["rustup-init"], "\n\n"); + assert!(out.ok); + assert!(!out + .stdout + .contains("This path needs to be in your PATH environment variable")); +} + +#[tokio::test] +async fn blank_lines_around_stderr_log_output_update() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config + .expect_ok(&["rustup-init", "-y", "--no-modify-path"]) + .await; + let out = run_input( + &cx.config, + &[ + "rustup-init", + "--no-update-default-toolchain", + "--no-modify-path", + ], + "\n\n", + ); + println!("-- stdout --\n {}", out.stdout); + println!("-- stderr --\n {}", out.stderr); + + assert!(out.stdout.contains( + r" 3) Cancel installation > Rust is installed now. Great! " - )); - }); + )); } -#[test] -fn installer_shows_default_host_triple() { - clitools::test(Scenario::SimpleV2, &|config| { - let out = run_input(config, &["rustup-init", "--no-modify-path"], "2\n"); +#[tokio::test] +async fn installer_shows_default_host_triple() { + let cx = CliTestContext::new(Scenario::SimpleV2).await; + let out = run_input(&cx.config, &["rustup-init", "--no-modify-path"], "2\n"); - println!("-- stdout --\n {}", out.stdout); - println!("-- stderr --\n {}", out.stderr); - assert!(out.stdout.contains(for_host!( - r" + println!("-- stdout --\n {}", out.stdout); + println!("-- stderr --\n {}", out.stderr); + assert!(out.stdout.contains(for_host!( + r" Default host triple? [{0}] " - ))); - }); + ))); } -#[test] -fn installer_shows_default_toolchain_as_stable() { - clitools::test(Scenario::SimpleV2, &|config| { - let out = run_input(config, &["rustup-init", "--no-modify-path"], "2\n\n"); +#[tokio::test] +async fn installer_shows_default_toolchain_as_stable() { + let cx = CliTestContext::new(Scenario::SimpleV2).await; + let out = run_input(&cx.config, &["rustup-init", "--no-modify-path"], "2\n\n"); - println!("-- stdout --\n {}", out.stdout); - println!("-- stderr --\n {}", out.stderr); - assert!(out.stdout.contains( - r" + println!("-- stdout --\n {}", out.stdout); + println!("-- stderr --\n {}", out.stderr); + assert!(out.stdout.contains( + r" Default toolchain? (stable/beta/nightly/none) [stable] " - )); - }); + )); } -#[test] -fn installer_shows_default_toolchain_when_set_in_args() { - clitools::test(Scenario::SimpleV2, &|config| { - let out = run_input( - config, - &[ - "rustup-init", - "--no-modify-path", - "--default-toolchain=nightly", - ], - "2\n\n", - ); - - println!("-- stdout --\n {}", out.stdout); - println!("-- stderr --\n {}", out.stderr); - assert!(out.stdout.contains( - r" +#[tokio::test] +async fn installer_shows_default_toolchain_when_set_in_args() { + let cx = CliTestContext::new(Scenario::SimpleV2).await; + let out = run_input( + &cx.config, + &[ + "rustup-init", + "--no-modify-path", + "--default-toolchain=nightly", + ], + "2\n\n", + ); + + println!("-- stdout --\n {}", out.stdout); + println!("-- stderr --\n {}", out.stderr); + assert!(out.stdout.contains( + r" Default toolchain? (stable/beta/nightly/none) [nightly] " - )); - }); + )); } -#[test] -fn installer_shows_default_profile() { - clitools::test(Scenario::SimpleV2, &|config| { - let out = run_input(config, &["rustup-init", "--no-modify-path"], "2\n\n\n"); +#[tokio::test] +async fn installer_shows_default_profile() { + let cx = CliTestContext::new(Scenario::SimpleV2).await; + let out = run_input(&cx.config, &["rustup-init", "--no-modify-path"], "2\n\n\n"); - println!("-- stdout --\n {}", out.stdout); - println!("-- stderr --\n {}", out.stderr); - assert!(out.stdout.contains( - r" + println!("-- stdout --\n {}", out.stdout); + println!("-- stderr --\n {}", out.stderr); + assert!(out.stdout.contains( + r" Profile (which tools and data to install)? (minimal/default/complete) [default] " - )); - }); -} - -#[test] -fn installer_shows_default_profile_when_set_in_args() { - clitools::test(Scenario::SimpleV2, &|config| { - let out = run_input( - config, - &["rustup-init", "--no-modify-path", "--profile=minimal"], - "2\n\n\n", - ); - - println!("-- stdout --\n {}", out.stdout); - println!("-- stderr --\n {}", out.stderr); - assert!(out.stdout.contains( - r" + )); +} + +#[tokio::test] +async fn installer_shows_default_profile_when_set_in_args() { + let cx = CliTestContext::new(Scenario::SimpleV2).await; + let out = run_input( + &cx.config, + &["rustup-init", "--no-modify-path", "--profile=minimal"], + "2\n\n\n", + ); + + println!("-- stdout --\n {}", out.stdout); + println!("-- stderr --\n {}", out.stderr); + assert!(out.stdout.contains( + r" Profile (which tools and data to install)? (minimal/default/complete) [minimal] " - )); - }); + )); } -#[test] -fn installer_shows_default_for_modify_path() { - clitools::test(Scenario::SimpleV2, &|config| { - let out = run_input(config, &["rustup-init"], "2\n\n\n\n"); +#[tokio::test] +async fn installer_shows_default_for_modify_path() { + let cx = CliTestContext::new(Scenario::SimpleV2).await; + let out = run_input(&cx.config, &["rustup-init"], "2\n\n\n\n"); - println!("-- stdout --\n {}", out.stdout); - println!("-- stderr --\n {}", out.stderr); - assert!(out.stdout.contains( - r" + println!("-- stdout --\n {}", out.stdout); + println!("-- stderr --\n {}", out.stderr); + assert!(out.stdout.contains( + r" Modify PATH variable? (Y/n) " - )); - }); -} - -#[test] -fn installer_shows_default_for_modify_path_when_set_with_args() { - clitools::test(Scenario::SimpleV2, &|config| { - let out = run_input(config, &["rustup-init", "--no-modify-path"], "2\n\n\n\n"); - - println!("-- stdout --\n {}", out.stdout); - println!("-- stderr --\n {}", out.stderr); - assert!(out.stdout.contains( - r" + )); +} + +#[tokio::test] +async fn installer_shows_default_for_modify_path_when_set_with_args() { + let cx = CliTestContext::new(Scenario::SimpleV2).await; + let out = run_input( + &cx.config, + &["rustup-init", "--no-modify-path"], + "2\n\n\n\n", + ); + + println!("-- stdout --\n {}", out.stdout); + println!("-- stderr --\n {}", out.stderr); + assert!(out.stdout.contains( + r" Modify PATH variable? (y/N) " - )); - }); + )); } -#[test] -fn user_says_nope() { - clitools::test(Scenario::SimpleV2, &|config| { - let out = run_input(config, &["rustup-init", "--no-modify-path"], "n\n\n"); - assert!(out.ok); - assert!(!config.cargodir.join("bin").exists()); - }); +#[tokio::test] +async fn user_says_nope() { + let cx = CliTestContext::new(Scenario::SimpleV2).await; + let out = run_input(&cx.config, &["rustup-init", "--no-modify-path"], "n\n\n"); + assert!(out.ok); + assert!(!cx.config.cargodir.join("bin").exists()); } -#[test] -fn with_no_toolchain() { - clitools::test(Scenario::SimpleV2, &|config| { - let out = run_input( - config, - &[ - "rustup-init", - "--no-modify-path", - "--default-toolchain=none", - ], - "\n\n", - ); - assert!(out.ok); - - config.expect_stdout_ok(&["rustup", "show"], "no active toolchain"); - }); -} - -#[test] -fn with_non_default_toolchain_still_prompts() { - clitools::test(Scenario::SimpleV2, &|config| { - let out = run_input( - config, - &[ - "rustup-init", - "--no-modify-path", - "--default-toolchain=nightly", - ], - "\n\n", - ); - assert!(out.ok); - - config.expect_stdout_ok(&["rustup", "show"], "nightly"); - }); +#[tokio::test] +async fn with_no_toolchain() { + let cx = CliTestContext::new(Scenario::SimpleV2).await; + let out = run_input( + &cx.config, + &[ + "rustup-init", + "--no-modify-path", + "--default-toolchain=none", + ], + "\n\n", + ); + assert!(out.ok); + + cx.config + .expect_stdout_ok(&["rustup", "show"], "no active toolchain") + .await; +} + +#[tokio::test] +async fn with_non_default_toolchain_still_prompts() { + let cx = CliTestContext::new(Scenario::SimpleV2).await; + let out = run_input( + &cx.config, + &[ + "rustup-init", + "--no-modify-path", + "--default-toolchain=nightly", + ], + "\n\n", + ); + assert!(out.ok); + + cx.config + .expect_stdout_ok(&["rustup", "show"], "nightly") + .await; +} + +#[tokio::test] +async fn with_non_release_channel_non_default_toolchain() { + let cx = CliTestContext::new(Scenario::SimpleV2).await; + let out = run_input( + &cx.config, + &[ + "rustup-init", + "--no-modify-path", + "--default-toolchain=nightly-2015-01-02", + ], + "\n\n", + ); + assert!(out.ok); + + cx.config + .expect_stdout_ok(&["rustup", "show"], "nightly") + .await; + cx.config + .expect_stdout_ok(&["rustup", "show"], "2015-01-02") + .await; +} + +#[tokio::test] +async fn set_nightly_toolchain() { + let cx = CliTestContext::new(Scenario::SimpleV2).await; + let out = run_input( + &cx.config, + &["rustup-init", "--no-modify-path"], + "2\n\nnightly\n\n\n\n\n", + ); + assert!(out.ok); + + cx.config + .expect_stdout_ok(&["rustup", "show"], "nightly") + .await; +} + +#[tokio::test] +async fn set_no_modify_path() { + let cx = CliTestContext::new(Scenario::SimpleV2).await; + let out = run_input( + &cx.config, + &["rustup-init", "--no-modify-path"], + "2\n\n\n\nno\n\n\n", + ); + assert!(out.ok); + + if cfg!(unix) { + assert!(!cx.config.homedir.join(".profile").exists()); + } } -#[test] -fn with_non_release_channel_non_default_toolchain() { - clitools::test(Scenario::SimpleV2, &|config| { - let out = run_input( - config, - &[ - "rustup-init", - "--no-modify-path", - "--default-toolchain=nightly-2015-01-02", - ], - "\n\n", - ); - assert!(out.ok); - - config.expect_stdout_ok(&["rustup", "show"], "nightly"); - config.expect_stdout_ok(&["rustup", "show"], "2015-01-02"); - }); -} - -#[test] -fn set_nightly_toolchain() { - clitools::test(Scenario::SimpleV2, &|config| { - let out = run_input( - config, - &["rustup-init", "--no-modify-path"], - "2\n\nnightly\n\n\n\n\n", - ); - assert!(out.ok); - - config.expect_stdout_ok(&["rustup", "show"], "nightly"); - }); -} - -#[test] -fn set_no_modify_path() { - clitools::test(Scenario::SimpleV2, &|config| { - let out = run_input( - config, - &["rustup-init", "--no-modify-path"], - "2\n\n\n\nno\n\n\n", - ); - assert!(out.ok); - - if cfg!(unix) { - assert!(!config.homedir.join(".profile").exists()); - } - }); -} - -#[test] -fn set_nightly_toolchain_and_unset() { - clitools::test(Scenario::SimpleV2, &|config| { - let out = run_input( - config, - &["rustup-init", "--no-modify-path"], - "2\n\nnightly\n\n\n2\n\nbeta\n\n\n\n\n", - ); - println!("{:?}", out.stderr); - println!("{:?}", out.stdout); - assert!(out.ok); - - config.expect_stdout_ok(&["rustup", "show"], "beta"); - }); -} - -#[test] -fn user_says_nope_after_advanced_install() { - clitools::test(Scenario::SimpleV2, &|config| { - let out = run_input( - config, - &["rustup-init", "--no-modify-path"], - "2\n\n\n\n\nn\n\n\n", - ); - assert!(out.ok); - assert!(!config.cargodir.join("bin").exists()); - }); -} - -#[test] -fn install_with_components() { - fn go(comp_args: &[&str]) { +#[tokio::test] +async fn set_nightly_toolchain_and_unset() { + let cx = CliTestContext::new(Scenario::SimpleV2).await; + let out = run_input( + &cx.config, + &["rustup-init", "--no-modify-path"], + "2\n\nnightly\n\n\n2\n\nbeta\n\n\n\n\n", + ); + println!("{:?}", out.stderr); + println!("{:?}", out.stdout); + assert!(out.ok); + + cx.config + .expect_stdout_ok(&["rustup", "show"], "beta") + .await; +} + +#[tokio::test] +async fn user_says_nope_after_advanced_install() { + let cx = CliTestContext::new(Scenario::SimpleV2).await; + let out = run_input( + &cx.config, + &["rustup-init", "--no-modify-path"], + "2\n\n\n\n\nn\n\n\n", + ); + assert!(out.ok); + assert!(!cx.config.cargodir.join("bin").exists()); +} + +#[tokio::test] +async fn install_with_components() { + async fn go(comp_args: &[&str]) { let mut args = vec!["rustup-init", "-y", "--no-modify-path"]; args.extend_from_slice(comp_args); - clitools::test(Scenario::SimpleV2, &|config| { - config.expect_ok(&args); - config.expect_stdout_ok(&["rustup", "component", "list"], "rust-src (installed)"); - config.expect_stdout_ok( + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&args).await; + cx.config + .expect_stdout_ok(&["rustup", "component", "list"], "rust-src (installed)") + .await; + cx.config + .expect_stdout_ok( &["rustup", "component", "list"], &format!("rust-analysis-{} (installed)", this_host_triple()), - ); - }) + ) + .await; } - go(&["-c", "rust-src", "-c", "rust-analysis"]); - go(&["-c", "rust-src,rust-analysis"]); + go(&["-c", "rust-src", "-c", "rust-analysis"]).await; + go(&["-c", "rust-src,rust-analysis"]).await; } -#[test] -fn install_forces_and_skips_rls() { - clitools::test(Scenario::UnavailableRls, &|config| { - set_current_dist_date(config, "2015-01-01"); +#[tokio::test] +async fn install_forces_and_skips_rls() { + let cx = CliTestContext::new(Scenario::UnavailableRls).await; + set_current_dist_date(&cx.config, "2015-01-01"); - let out = run_input( - config, - &[ - "rustup-init", - "--profile", - "complete", - "--default-toolchain", - "nightly", - "--no-modify-path", - ], - "\n\n", - ); - assert!(out.ok); - assert!(out - .stderr - .contains("warn: Force-skipping unavailable component")); - }); -} - -#[test] -fn test_warn_if_complete_profile_is_used() { - clitools::test(Scenario::SimpleV2, &|config| { - config.expect_stderr_ok( + let out = run_input( + &cx.config, + &[ + "rustup-init", + "--profile", + "complete", + "--default-toolchain", + "nightly", + "--no-modify-path", + ], + "\n\n", + ); + assert!(out.ok); + assert!(out + .stderr + .contains("warn: Force-skipping unavailable component")); +} + +#[tokio::test] +async fn test_warn_if_complete_profile_is_used() { + let cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config + .expect_stderr_ok( &[ "rustup-init", "-y", @@ -448,75 +455,77 @@ fn test_warn_if_complete_profile_is_used() { "--no-modify-path", ], "warn: downloading with complete profile", - ); - }); -} - -#[test] -fn test_prompt_fail_if_rustup_sh_already_installed_reply_nothing() { - clitools::test(Scenario::SimpleV2, &|config| { - config.create_rustup_sh_metadata(); - let out = run_input(config, &["rustup-init", "--no-modify-path"], "\n"); - assert!(!out.ok); - assert!(out - .stderr - .contains("warn: it looks like you have existing rustup.sh metadata")); - assert!(out - .stderr - .contains("error: cannot install while rustup.sh is installed")); - assert!(out.stdout.contains("Continue? (y/N)")); - }) -} - -#[test] -fn test_prompt_fail_if_rustup_sh_already_installed_reply_no() { - clitools::test(Scenario::SimpleV2, &|config| { - config.create_rustup_sh_metadata(); - let out = run_input(config, &["rustup-init", "--no-modify-path"], "no\n"); - assert!(!out.ok); - assert!(out - .stderr - .contains("warn: it looks like you have existing rustup.sh metadata")); - assert!(out - .stderr - .contains("error: cannot install while rustup.sh is installed")); - assert!(out.stdout.contains("Continue? (y/N)")); - }) -} - -#[test] -fn test_prompt_succeed_if_rustup_sh_already_installed_reply_yes() { - clitools::test(Scenario::SimpleV2, &|config| { - config.create_rustup_sh_metadata(); - let out = run_input(config, &["rustup-init", "--no-modify-path"], "yes\n\n\n"); - assert!(out - .stderr - .contains("warn: it looks like you have existing rustup.sh metadata")); - assert!(out - .stderr - .contains("error: cannot install while rustup.sh is installed")); - assert!(out.stdout.contains("Continue? (y/N)")); - assert!(!out - .stdout - .contains("warn: continuing (because the -y flag is set and the error is ignorable)")); - assert!(out.ok); - }) -} - -#[test] -fn installing_when_already_installed_updates_toolchain() { - clitools::test(Scenario::SimpleV2, &|config| { - config.expect_ok(&["rustup-init", "-y", "--no-modify-path"]); - let out = run_input(config, &["rustup-init", "--no-modify-path"], "\n\n"); - println!("stdout:\n{}\n...\n", out.stdout); - assert!(out - .stdout - .contains(for_host!("stable-{} unchanged - 1.1.0 (hash-stable-1.1.0)"))); - }) -} - -#[test] -fn install_stops_if_rustc_exists() { + ) + .await; +} + +#[tokio::test] +async fn test_prompt_fail_if_rustup_sh_already_installed_reply_nothing() { + let cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.create_rustup_sh_metadata(); + let out = run_input(&cx.config, &["rustup-init", "--no-modify-path"], "\n"); + assert!(!out.ok); + assert!(out + .stderr + .contains("warn: it looks like you have existing rustup.sh metadata")); + assert!(out + .stderr + .contains("error: cannot install while rustup.sh is installed")); + assert!(out.stdout.contains("Continue? (y/N)")); +} + +#[tokio::test] +async fn test_prompt_fail_if_rustup_sh_already_installed_reply_no() { + let cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.create_rustup_sh_metadata(); + let out = run_input(&cx.config, &["rustup-init", "--no-modify-path"], "no\n"); + assert!(!out.ok); + assert!(out + .stderr + .contains("warn: it looks like you have existing rustup.sh metadata")); + assert!(out + .stderr + .contains("error: cannot install while rustup.sh is installed")); + assert!(out.stdout.contains("Continue? (y/N)")); +} + +#[tokio::test] +async fn test_prompt_succeed_if_rustup_sh_already_installed_reply_yes() { + let cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.create_rustup_sh_metadata(); + let out = run_input( + &cx.config, + &["rustup-init", "--no-modify-path"], + "yes\n\n\n", + ); + assert!(out + .stderr + .contains("warn: it looks like you have existing rustup.sh metadata")); + assert!(out + .stderr + .contains("error: cannot install while rustup.sh is installed")); + assert!(out.stdout.contains("Continue? (y/N)")); + assert!(!out + .stdout + .contains("warn: continuing (because the -y flag is set and the error is ignorable)")); + assert!(out.ok); +} + +#[tokio::test] +async fn installing_when_already_installed_updates_toolchain() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config + .expect_ok(&["rustup-init", "-y", "--no-modify-path"]) + .await; + let out = run_input(&cx.config, &["rustup-init", "--no-modify-path"], "\n\n"); + println!("stdout:\n{}\n...\n", out.stdout); + assert!(out + .stdout + .contains(for_host!("stable-{} unchanged - 1.1.0 (hash-stable-1.1.0)"))); +} + +#[tokio::test] +async fn install_stops_if_rustc_exists() { let temp_dir = tempfile::Builder::new() .prefix("fakebin") .tempdir() @@ -526,27 +535,29 @@ fn install_stops_if_rustc_exists() { raw::append_file(&fake_exe, "").unwrap(); let temp_dir_path = temp_dir.path().to_str().unwrap(); - clitools::test(Scenario::SimpleV2, &|config| { - let out = config.run( + let cx = CliTestContext::new(Scenario::SimpleV2).await; + let out = cx + .config + .run( "rustup-init", ["--no-modify-path"], &[ ("RUSTUP_INIT_SKIP_PATH_CHECK", "no"), ("PATH", temp_dir_path), ], - ); - assert!(!out.ok); - assert!(out - .stderr - .contains("it looks like you have an existing installation of Rust at:")); - assert!(out - .stderr - .contains("If you are sure that you want both rustup and your already installed Rust")); - }); -} - -#[test] -fn install_stops_if_cargo_exists() { + ) + .await; + assert!(!out.ok); + assert!(out + .stderr + .contains("it looks like you have an existing installation of Rust at:")); + assert!(out + .stderr + .contains("If you are sure that you want both rustup and your already installed Rust")); +} + +#[tokio::test] +async fn install_stops_if_cargo_exists() { let temp_dir = tempfile::Builder::new() .prefix("fakebin") .tempdir() @@ -556,27 +567,29 @@ fn install_stops_if_cargo_exists() { raw::append_file(&fake_exe, "").unwrap(); let temp_dir_path = temp_dir.path().to_str().unwrap(); - clitools::test(Scenario::SimpleV2, &|config| { - let out = config.run( + let cx = CliTestContext::new(Scenario::SimpleV2).await; + let out = cx + .config + .run( "rustup-init", ["--no-modify-path"], &[ ("RUSTUP_INIT_SKIP_PATH_CHECK", "no"), ("PATH", temp_dir_path), ], - ); - assert!(!out.ok); - assert!(out - .stderr - .contains("it looks like you have an existing installation of Rust at:")); - assert!(out - .stderr - .contains("If you are sure that you want both rustup and your already installed Rust")); - }); -} - -#[test] -fn with_no_prompt_install_succeeds_if_rustc_exists() { + ) + .await; + assert!(!out.ok); + assert!(out + .stderr + .contains("it looks like you have an existing installation of Rust at:")); + assert!(out + .stderr + .contains("If you are sure that you want both rustup and your already installed Rust")); +} + +#[tokio::test] +async fn with_no_prompt_install_succeeds_if_rustc_exists() { let temp_dir = tempfile::Builder::new() .prefix("fakebin") .tempdir() @@ -586,24 +599,27 @@ fn with_no_prompt_install_succeeds_if_rustc_exists() { raw::append_file(&fake_exe, "").unwrap(); let temp_dir_path = temp_dir.path().to_str().unwrap(); - clitools::test(Scenario::SimpleV2, &|config| { - let out = config.run( + let cx = CliTestContext::new(Scenario::SimpleV2).await; + let out = cx + .config + .run( "rustup-init", ["-y", "--no-modify-path"], &[ ("RUSTUP_INIT_SKIP_PATH_CHECK", "no"), ("PATH", temp_dir_path), ], - ); - assert!(out.ok); - }); + ) + .await; + assert!(out.ok); } // Issue 2547 -#[test] -fn install_non_installable_toolchain() { - clitools::test(Scenario::Unavailable, &|config| { - config.expect_err( +#[tokio::test] +async fn install_non_installable_toolchain() { + let cx = CliTestContext::new(Scenario::Unavailable).await; + cx.config + .expect_err( &[ "rustup-init", "-y", @@ -612,6 +628,6 @@ fn install_non_installable_toolchain() { "nightly", ], "is not installable", - ); - }) + ) + .await; } diff --git a/tests/suite/cli_misc.rs b/tests/suite/cli_misc.rs index 75a3c17639..5359e6afbd 100644 --- a/tests/suite/cli_misc.rs +++ b/tests/suite/cli_misc.rs @@ -1,105 +1,116 @@ //! Test cases of the rustup command that do not depend on the //! dist server, mostly derived from multirust/test-v2.sh +use std::fs; use std::str; use std::{env::consts::EXE_SUFFIX, path::Path}; use rustup::for_host; use rustup::test::{ - mock::clitools::{self, set_current_dist_date, Config, Scenario}, + mock::clitools::{self, set_current_dist_date, CliTestContext, Config, Scenario}, this_host_triple, }; +use rustup::utils::raw::symlink_dir; use rustup::utils::utils; -pub fn setup(f: &dyn Fn(&mut Config)) { - clitools::test(Scenario::SimpleV2, f); +#[tokio::test] +async fn smoke_test() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "--version"]).await; } -#[test] -fn smoke_test() { - setup(&|config| { - config.expect_ok(&["rustup", "--version"]); - }); +#[tokio::test] +async fn version_mentions_rustc_version_confusion() { + let cx = CliTestContext::new(Scenario::SimpleV2).await; + let out = cx.config.run("rustup", vec!["--version"], &[]).await; + assert!(out.ok); + assert!(out + .stderr + .contains("This is the version for the rustup toolchain manager")); + + let out = cx + .config + .run("rustup", vec!["+nightly", "--version"], &[]) + .await; + assert!(out.ok); + assert!(out + .stderr + .contains("The currently active `rustc` version is `1.3.0")); } -#[test] -fn version_mentions_rustc_version_confusion() { - setup(&|config| { - let out = config.run("rustup", vec!["--version"], &[]); - assert!(out.ok); - assert!(out - .stderr - .contains("This is the version for the rustup toolchain manager")); - - let out = config.run("rustup", vec!["+nightly", "--version"], &[]); - assert!(out.ok); - assert!(out - .stderr - .contains("The currently active `rustc` version is `1.3.0")); - }); +#[tokio::test] +async fn no_colors_in_piped_error_output() { + let cx = CliTestContext::new(Scenario::SimpleV2).await; + let args: Vec<&str> = vec![]; + let out = cx.config.run("rustc", args, &[]).await; + assert!(!out.ok); + assert!(!out.stderr.contains('\x1b')); } -#[test] -fn no_colors_in_piped_error_output() { - setup(&|config| { - let args: Vec<&str> = vec![]; - let out = config.run("rustc", args, &[]); - assert!(!out.ok); - assert!(!out.stderr.contains('\x1b')); - }); +#[tokio::test] +async fn rustc_with_bad_rustup_toolchain_env_var() { + let cx = CliTestContext::new(Scenario::SimpleV2).await; + let args: Vec<&str> = vec![]; + let out = cx + .config + .run("rustc", args, &[("RUSTUP_TOOLCHAIN", "bogus")]) + .await; + assert!(!out.ok); + assert!(out.stderr.contains("toolchain 'bogus' is not installed")); } -#[test] -fn rustc_with_bad_rustup_toolchain_env_var() { - setup(&|config| { - let args: Vec<&str> = vec![]; - let out = config.run("rustc", args, &[("RUSTUP_TOOLCHAIN", "bogus")]); - assert!(!out.ok); - assert!(out.stderr.contains("toolchain 'bogus' is not installed")); - }); -} - -#[test] -fn custom_invalid_names() { - setup(&|config| { - config.expect_err( +#[tokio::test] +async fn custom_invalid_names() { + let cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config + .expect_err( &["rustup", "toolchain", "link", "nightly", "foo"], "invalid custom toolchain name 'nightly'", - ); - config.expect_err( + ) + .await; + cx.config + .expect_err( &["rustup", "toolchain", "link", "beta", "foo"], "invalid custom toolchain name 'beta'", - ); - config.expect_err( + ) + .await; + cx.config + .expect_err( &["rustup", "toolchain", "link", "stable", "foo"], "invalid custom toolchain name 'stable'", - ); - }); + ) + .await; } -#[test] -fn custom_invalid_names_with_archive_dates() { - setup(&|config| { - config.expect_err( +#[tokio::test] +async fn custom_invalid_names_with_archive_dates() { + let cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config + .expect_err( &["rustup", "toolchain", "link", "nightly-2015-01-01", "foo"], "invalid custom toolchain name 'nightly-2015-01-01'", - ); - config.expect_err( + ) + .await; + cx.config + .expect_err( &["rustup", "toolchain", "link", "beta-2015-01-01", "foo"], "invalid custom toolchain name 'beta-2015-01-01'", - ); - config.expect_err( + ) + .await; + cx.config + .expect_err( &["rustup", "toolchain", "link", "stable-2015-01-01", "foo"], "invalid custom toolchain name 'stable-2015-01-01'", - ); - }); + ) + .await; } // Regression test for newline placement -#[test] -fn update_all_no_update_whitespace() { - setup(&|config| { - config.expect_stdout_ok( +#[tokio::test] +async fn update_all_no_update_whitespace() { + let cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config + .expect_stdout_ok( &["rustup", "update", "nightly"], for_host!( r" @@ -107,349 +118,381 @@ fn update_all_no_update_whitespace() { " ), - ); - }); + ) + .await; } // Issue #145 -#[test] -fn update_works_without_term() { - setup(&|config| { - let mut cmd = clitools::cmd(config, "rustup", ["update", "nightly"]); - clitools::env(config, &mut cmd); - cmd.env_remove("TERM"); - - let out = cmd.output().unwrap(); - assert!(out.status.success()); - }); +#[tokio::test] +async fn update_works_without_term() { + let cx = CliTestContext::new(Scenario::SimpleV2).await; + let mut cmd = clitools::cmd(&cx.config, "rustup", ["update", "nightly"]); + clitools::env(&cx.config, &mut cmd); + cmd.env_remove("TERM"); + + let out = cmd.output().unwrap(); + assert!(out.status.success()); } // Issue #1738 -#[test] -fn show_works_with_dumb_term() { - setup(&|config| { - let mut cmd = clitools::cmd(config, "rustup", ["show"]); - clitools::env(config, &mut cmd); - cmd.env("TERM", "dumb"); - assert!(cmd.spawn().unwrap().wait().unwrap().success()); - }); +#[tokio::test] +async fn show_works_with_dumb_term() { + let cx = CliTestContext::new(Scenario::SimpleV2).await; + let mut cmd = clitools::cmd(&cx.config, "rustup", ["show"]); + clitools::env(&cx.config, &mut cmd); + cmd.env("TERM", "dumb"); + assert!(cmd.spawn().unwrap().wait().unwrap().success()); } // Issue #2425 // Exit with error and help output when called without subcommand. -#[test] -fn subcommand_required_for_target() { - setup(&|config| { - let mut cmd = clitools::cmd(config, "rustup", ["target"]); - clitools::env(config, &mut cmd); - let out = cmd.output().unwrap(); - assert!(!out.status.success()); - assert_eq!(out.status.code().unwrap(), 1); - assert!(str::from_utf8(&out.stdout).unwrap().contains("Usage")); - }); +#[tokio::test] +async fn subcommand_required_for_target() { + let cx = CliTestContext::new(Scenario::SimpleV2).await; + let mut cmd = clitools::cmd(&cx.config, "rustup", ["target"]); + clitools::env(&cx.config, &mut cmd); + let out = cmd.output().unwrap(); + assert!(!out.status.success()); + assert_eq!(out.status.code().unwrap(), 1); + assert!(str::from_utf8(&out.stdout).unwrap().contains("Usage")); } // Issue #2425 // Exit with error and help output when called without subcommand. -#[test] -fn subcommand_required_for_toolchain() { - setup(&|config| { - let mut cmd = clitools::cmd(config, "rustup", ["toolchain"]); - clitools::env(config, &mut cmd); - let out = cmd.output().unwrap(); - assert!(!out.status.success()); - assert_eq!(out.status.code().unwrap(), 1); - assert!(str::from_utf8(&out.stdout).unwrap().contains("Usage")); - }); +#[tokio::test] +async fn subcommand_required_for_toolchain() { + let cx = CliTestContext::new(Scenario::SimpleV2).await; + let mut cmd = clitools::cmd(&cx.config, "rustup", ["toolchain"]); + clitools::env(&cx.config, &mut cmd); + let out = cmd.output().unwrap(); + assert!(!out.status.success()); + assert_eq!(out.status.code().unwrap(), 1); + assert!(str::from_utf8(&out.stdout).unwrap().contains("Usage")); } // Issue #2425 // Exit with error and help output when called without subcommand. -#[test] -fn subcommand_required_for_override() { - setup(&|config| { - let mut cmd = clitools::cmd(config, "rustup", ["override"]); - clitools::env(config, &mut cmd); - let out = cmd.output().unwrap(); - assert!(!out.status.success()); - assert_eq!(out.status.code().unwrap(), 1); - assert!(str::from_utf8(&out.stdout).unwrap().contains("Usage")); - }); +#[tokio::test] +async fn subcommand_required_for_override() { + let cx = CliTestContext::new(Scenario::SimpleV2).await; + let mut cmd = clitools::cmd(&cx.config, "rustup", ["override"]); + clitools::env(&cx.config, &mut cmd); + let out = cmd.output().unwrap(); + assert!(!out.status.success()); + assert_eq!(out.status.code().unwrap(), 1); + assert!(str::from_utf8(&out.stdout).unwrap().contains("Usage")); } // Issue #2425 // Exit with error and help output when called without subcommand. -#[test] -fn subcommand_required_for_self() { - setup(&|config| { - let mut cmd = clitools::cmd(config, "rustup", ["self"]); - clitools::env(config, &mut cmd); - let out = cmd.output().unwrap(); - assert!(!out.status.success()); - assert_eq!(out.status.code().unwrap(), 1); - assert!(str::from_utf8(&out.stdout).unwrap().contains("Usage")); - }); +#[tokio::test] +async fn subcommand_required_for_self() { + let cx = CliTestContext::new(Scenario::SimpleV2).await; + let mut cmd = clitools::cmd(&cx.config, "rustup", ["self"]); + clitools::env(&cx.config, &mut cmd); + let out = cmd.output().unwrap(); + assert!(!out.status.success()); + assert_eq!(out.status.code().unwrap(), 1); + assert!(str::from_utf8(&out.stdout).unwrap().contains("Usage")); } -#[test] -fn multi_host_smoke_test() { +#[tokio::test] +async fn multi_host_smoke_test() { // We cannot run this test if the current host triple is equal to the // multi-arch triple, but this should never be the case. Check that just // to be sure. assert_ne!(this_host_triple(), clitools::MULTI_ARCH1); - clitools::test(Scenario::MultiHost, &|config| { - let toolchain = format!("nightly-{}", clitools::MULTI_ARCH1); - config.expect_ok(&["rustup", "default", &toolchain]); - config.expect_stdout_ok(&["rustc", "--version"], "xxxx-nightly-2"); // cross-host mocks have their own versions - }); + let mut cx = CliTestContext::new(Scenario::MultiHost).await; + let toolchain = format!("nightly-{}", clitools::MULTI_ARCH1); + cx.config + .expect_ok(&["rustup", "default", &toolchain]) + .await; + cx.config + .expect_stdout_ok(&["rustc", "--version"], "xxxx-nightly-2") + .await; // cross-host mocks have their own versions } -#[test] -fn custom_toolchain_cargo_fallback_proxy() { - setup(&|config| { - let path = config.customdir.join("custom-1"); +#[tokio::test] +async fn custom_toolchain_cargo_fallback_proxy() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + let path = cx.config.customdir.join("custom-1"); - config.expect_ok(&[ + cx.config + .expect_ok(&[ "rustup", "toolchain", "link", "mytoolchain", &path.to_string_lossy(), - ]); - config.expect_ok(&["rustup", "default", "mytoolchain"]); - - config.expect_ok(&["rustup", "update", "stable"]); - config.expect_stdout_ok(&["cargo", "--version"], "hash-stable-1.1.0"); - - config.expect_ok(&["rustup", "update", "beta"]); - config.expect_stdout_ok(&["cargo", "--version"], "hash-beta-1.2.0"); - - config.expect_ok(&["rustup", "update", "nightly"]); - config.expect_stdout_ok(&["cargo", "--version"], "hash-nightly-2"); - }); + ]) + .await; + cx.config + .expect_ok(&["rustup", "default", "mytoolchain"]) + .await; + + cx.config.expect_ok(&["rustup", "update", "stable"]).await; + cx.config + .expect_stdout_ok(&["cargo", "--version"], "hash-stable-1.1.0") + .await; + + cx.config.expect_ok(&["rustup", "update", "beta"]).await; + cx.config + .expect_stdout_ok(&["cargo", "--version"], "hash-beta-1.2.0") + .await; + + cx.config.expect_ok(&["rustup", "update", "nightly"]).await; + cx.config + .expect_stdout_ok(&["cargo", "--version"], "hash-nightly-2") + .await; } -#[test] -fn custom_toolchain_cargo_fallback_run() { - setup(&|config| { - let path = config.customdir.join("custom-1"); +#[tokio::test] +async fn custom_toolchain_cargo_fallback_run() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + let path = cx.config.customdir.join("custom-1"); - config.expect_ok(&[ + cx.config + .expect_ok(&[ "rustup", "toolchain", "link", "mytoolchain", &path.to_string_lossy(), - ]); - config.expect_ok(&["rustup", "default", "mytoolchain"]); - - config.expect_ok(&["rustup", "update", "stable"]); - config.expect_stdout_ok( + ]) + .await; + cx.config + .expect_ok(&["rustup", "default", "mytoolchain"]) + .await; + + cx.config.expect_ok(&["rustup", "update", "stable"]).await; + cx.config + .expect_stdout_ok( &["rustup", "run", "mytoolchain", "cargo", "--version"], "hash-stable-1.1.0", - ); + ) + .await; - config.expect_ok(&["rustup", "update", "beta"]); - config.expect_stdout_ok( + cx.config.expect_ok(&["rustup", "update", "beta"]).await; + cx.config + .expect_stdout_ok( &["rustup", "run", "mytoolchain", "cargo", "--version"], "hash-beta-1.2.0", - ); + ) + .await; - config.expect_ok(&["rustup", "update", "nightly"]); - config.expect_stdout_ok( + cx.config.expect_ok(&["rustup", "update", "nightly"]).await; + cx.config + .expect_stdout_ok( &["rustup", "run", "mytoolchain", "cargo", "--version"], "hash-nightly-2", - ); - }); + ) + .await; } -#[test] -fn rustup_run_searches_path() { - setup(&|config| { - #[cfg(windows)] - let hello_cmd = &["rustup", "run", "nightly", "cmd", "/C", "echo hello"]; - #[cfg(not(windows))] - let hello_cmd = &["rustup", "run", "nightly", "sh", "-c", "echo hello"]; - - config.expect_ok(&["rustup", "default", "nightly"]); - config.expect_stdout_ok(hello_cmd, "hello"); - }); +#[tokio::test] +async fn rustup_run_searches_path() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + #[cfg(windows)] + let hello_cmd = &["rustup", "run", "nightly", "cmd", "/C", "echo hello"]; + #[cfg(not(windows))] + let hello_cmd = &["rustup", "run", "nightly", "sh", "-c", "echo hello"]; + + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + cx.config.expect_stdout_ok(hello_cmd, "hello").await; } -#[test] -fn rustup_doesnt_prepend_path_unnecessarily() { - setup(&|config| { - config.expect_ok(&["rustup", "default", "nightly"]); - - let expect_stderr_ok_env_first_then = - |config: &Config, - args: &[&str], - env: &[(&str, &str)], - first: &Path, - second: Option<&Path>| { - let out = config.run(args[0], &args[1..], env); - let first_then_second = |list: &str| -> bool { - let mut saw_first = false; - let mut saw_second = false; - for path in std::env::split_paths(list) { - if path == first { - if saw_second { - return false; - } - saw_first = true; - } - if Some(&*path) == second { - if !saw_first { - return false; - } - saw_second = true; - } +#[tokio::test] +async fn rustup_doesnt_prepend_path_unnecessarily() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + + async fn expect_stderr_ok_env_first_then( + config: &Config, + args: &[&str], + env: &[(&str, &str)], + first: &Path, + second: Option<&Path>, + ) { + let out = config.run(args[0], &args[1..], env).await; + let first_then_second = |list: &str| -> bool { + let mut saw_first = false; + let mut saw_second = false; + for path in std::env::split_paths(list) { + if path == first { + if saw_second { + return false; } - true - }; - if !out.ok || !first_then_second(&out.stderr) { - clitools::print_command(args, &out); - println!("expected.ok: true"); - clitools::print_indented( - "expected.stderr.first_then", - &format!("{} comes before {:?}", first.display(), second), - ); - panic!(); + saw_first = true; } - }; - - // For all of these, CARGO_HOME/bin will be auto-prepended. - let cargo_home_bin = config.cargodir.join("bin"); - expect_stderr_ok_env_first_then( - config, - &["cargo", "--echo-path"], - &[], - &cargo_home_bin, - None, - ); - expect_stderr_ok_env_first_then( - config, - &["cargo", "--echo-path"], - &[("PATH", "")], - &cargo_home_bin, - None, - ); - - // Check that CARGO_HOME/bin is prepended to path. - expect_stderr_ok_env_first_then( - config, - &["cargo", "--echo-path"], - &[("PATH", &format!("{}", config.exedir.display()))], - &cargo_home_bin, - Some(&config.exedir), - ); - - // But if CARGO_HOME/bin is already on PATH, it will not be prepended again, - // so exedir will take precedence. - expect_stderr_ok_env_first_then( - config, - &["cargo", "--echo-path"], - &[( - "PATH", - std::env::join_paths([&config.exedir, &cargo_home_bin]) - .unwrap() - .to_str() - .unwrap(), - )], - &config.exedir, - Some(&cargo_home_bin), - ); - }); + if Some(&*path) == second { + if !saw_first { + return false; + } + saw_second = true; + } + } + true + }; + if !out.ok || !first_then_second(&out.stderr) { + clitools::print_command(args, &out); + println!("expected.ok: true"); + clitools::print_indented( + "expected.stderr.first_then", + &format!("{} comes before {:?}", first.display(), second), + ); + panic!(); + } + } + + // For all of these, CARGO_HOME/bin will be auto-prepended. + let cargo_home_bin = cx.config.cargodir.join("bin"); + expect_stderr_ok_env_first_then( + &cx.config, + &["cargo", "--echo-path"], + &[], + &cargo_home_bin, + None, + ) + .await; + expect_stderr_ok_env_first_then( + &cx.config, + &["cargo", "--echo-path"], + &[("PATH", "")], + &cargo_home_bin, + None, + ) + .await; + + // Check that CARGO_HOME/bin is prepended to path. + let config = &mut cx.config; + expect_stderr_ok_env_first_then( + config, + &["cargo", "--echo-path"], + &[("PATH", &format!("{}", config.exedir.display()))], + &cargo_home_bin, + Some(&config.exedir), + ) + .await; + + // But if CARGO_HOME/bin is already on PATH, it will not be prepended again, + // so exedir will take precedence. + expect_stderr_ok_env_first_then( + config, + &["cargo", "--echo-path"], + &[( + "PATH", + std::env::join_paths([&config.exedir, &cargo_home_bin]) + .unwrap() + .to_str() + .unwrap(), + )], + &config.exedir, + Some(&cargo_home_bin), + ) + .await; } -#[test] -fn rustup_failed_path_search() { - setup(&|config| { - use std::env::consts::EXE_SUFFIX; +#[tokio::test] +async fn rustup_failed_path_search() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + use std::env::consts::EXE_SUFFIX; - let rustup_path = config.exedir.join(format!("rustup{EXE_SUFFIX}")); - let tool_path = config.exedir.join(format!("fake_proxy{EXE_SUFFIX}")); - utils::hardlink_file(&rustup_path, &tool_path) - .expect("Failed to create fake proxy for test"); + let rustup_path = cx.config.exedir.join(format!("rustup{EXE_SUFFIX}")); + let tool_path = cx.config.exedir.join(format!("fake_proxy{EXE_SUFFIX}")); + utils::hardlink_file(&rustup_path, &tool_path).expect("Failed to create fake proxy for test"); - config.expect_ok(&[ + cx.config + .expect_ok(&[ "rustup", "toolchain", "link", "custom", - &config.customdir.join("custom-1").to_string_lossy(), - ]); + &cx.config.customdir.join("custom-1").to_string_lossy(), + ]) + .await; - config.expect_ok(&["rustup", "default", "custom"]); + cx.config.expect_ok(&["rustup", "default", "custom"]).await; - let broken = &["rustup", "run", "custom", "fake_proxy"]; - config.expect_err( + let broken = &["rustup", "run", "custom", "fake_proxy"]; + cx.config + .expect_err( broken, "unknown proxy name: 'fake_proxy'; valid proxy names are \ 'rustc', 'rustdoc', 'cargo', 'rust-lldb', 'rust-gdb', 'rust-gdbgui', \ 'rls', 'cargo-clippy', 'clippy-driver', 'cargo-miri', \ 'rust-analyzer', 'rustfmt', 'cargo-fmt'", - ); + ) + .await; - // Hardlink will be automatically cleaned up by test setup code - }); + // Hardlink will be automatically cleaned up by test setup code } -#[test] -fn rustup_failed_path_search_toolchain() { - setup(&|config| { - use std::env::consts::EXE_SUFFIX; +#[tokio::test] +async fn rustup_failed_path_search_toolchain() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + use std::env::consts::EXE_SUFFIX; - let rustup_path = config.exedir.join(format!("rustup{EXE_SUFFIX}")); - let tool_path = config.exedir.join(format!("cargo-miri{EXE_SUFFIX}")); - utils::hardlink_file(&rustup_path, &tool_path) - .expect("Failed to create fake cargo-miri for test"); + let rustup_path = cx.config.exedir.join(format!("rustup{EXE_SUFFIX}")); + let tool_path = cx.config.exedir.join(format!("cargo-miri{EXE_SUFFIX}")); + utils::hardlink_file(&rustup_path, &tool_path) + .expect("Failed to create fake cargo-miri for test"); - config.expect_ok(&[ + cx.config + .expect_ok(&[ "rustup", "toolchain", "link", "custom-1", - &config.customdir.join("custom-1").to_string_lossy(), - ]); + &cx.config.customdir.join("custom-1").to_string_lossy(), + ]) + .await; - config.expect_ok(&[ + cx.config + .expect_ok(&[ "rustup", "toolchain", "link", "custom-2", - &config.customdir.join("custom-2").to_string_lossy(), - ]); + &cx.config.customdir.join("custom-2").to_string_lossy(), + ]) + .await; - config.expect_ok(&["rustup", "default", "custom-2"]); + cx.config + .expect_ok(&["rustup", "default", "custom-2"]) + .await; - let broken = &["rustup", "run", "custom-1", "cargo-miri"]; - config.expect_err(broken, "cannot use `rustup component add`"); + let broken = &["rustup", "run", "custom-1", "cargo-miri"]; + cx.config + .expect_err(broken, "cannot use `rustup component add`") + .await; - let broken = &["rustup", "run", "custom-2", "cargo-miri"]; - config.expect_err(broken, "cannot use `rustup component add`"); + let broken = &["rustup", "run", "custom-2", "cargo-miri"]; + cx.config + .expect_err(broken, "cannot use `rustup component add`") + .await; - // Hardlink will be automatically cleaned up by test setup code - }); + // Hardlink will be automatically cleaned up by test setup code } -#[test] -fn rustup_run_not_installed() { - setup(&|config| { - config.expect_ok(&["rustup", "install", "stable"]); - config.expect_err( +#[tokio::test] +async fn rustup_run_not_installed() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "install", "stable"]).await; + cx.config + .expect_err( &["rustup", "run", "nightly", "rustc", "--version"], for_host!("toolchain 'nightly-{0}' is not installed"), - ); - }); + ) + .await; } -#[test] -fn rustup_run_install() { - setup(&|config| { - config.expect_ok(&["rustup", "install", "stable"]); - config.expect_stderr_ok( +#[tokio::test] +async fn rustup_run_install() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "install", "stable"]).await; + cx.config + .expect_stderr_ok( &[ "rustup", "run", @@ -459,576 +502,681 @@ fn rustup_run_install() { "--version", ], "info: installing component 'rustc'", - ); - }); + ) + .await; } -#[test] -fn toolchains_are_resolved_early() { - setup(&|config| { - config.expect_ok(&["rustup", "default", "nightly"]); +#[tokio::test] +async fn toolchains_are_resolved_early() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; - let full_toolchain = format!("nightly-{}", this_host_triple()); - config.expect_stderr_ok( + let full_toolchain = format!("nightly-{}", this_host_triple()); + cx.config + .expect_stderr_ok( &["rustup", "default", &full_toolchain], &format!("info: using existing install for '{full_toolchain}'"), - ); - }); + ) + .await; } // #190 -#[test] -fn proxies_pass_empty_args() { - setup(&|config| { - config.expect_ok(&["rustup", "default", "nightly"]); - config.expect_ok(&["rustup", "run", "nightly", "rustc", "--empty-arg-test", ""]); - }); +#[tokio::test] +async fn proxies_pass_empty_args() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + cx.config + .expect_ok(&["rustup", "run", "nightly", "rustc", "--empty-arg-test", ""]) + .await; } -#[test] -fn rls_exists_in_toolchain() { - setup(&|config| { - config.expect_ok(&["rustup", "default", "stable"]); - config.expect_ok(&["rustup", "component", "add", "rls"]); +#[tokio::test] +async fn rls_exists_in_toolchain() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "default", "stable"]).await; + cx.config + .expect_ok(&["rustup", "component", "add", "rls"]) + .await; - assert!(config.exedir.join(format!("rls{EXE_SUFFIX}")).exists()); - config.expect_ok(&["rls", "--version"]); - }); + assert!(cx.config.exedir.join(format!("rls{EXE_SUFFIX}")).exists()); + cx.config.expect_ok(&["rls", "--version"]).await; } -#[test] -fn run_rls_when_not_available_in_toolchain() { - clitools::test(Scenario::UnavailableRls, &|config| { - set_current_dist_date(config, "2015-01-01"); - config.expect_ok(&["rustup", "default", "nightly"]); - config.expect_err( - &["rls", "--version"], - &format!( - "the 'rls' component which provides the command 'rls{}' is not available for the 'nightly-{}' toolchain", - EXE_SUFFIX, - this_host_triple(), - ), - ); - - set_current_dist_date(config, "2015-01-02"); - config.expect_ok(&["rustup", "update"]); - config.expect_ok(&["rustup", "component", "add", "rls"]); - - config.expect_ok(&["rls", "--version"]); - }); +#[tokio::test] +async fn run_rls_when_not_available_in_toolchain() { + let mut cx = CliTestContext::new(Scenario::UnavailableRls).await; + set_current_dist_date(&cx.config, "2015-01-01"); + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + cx.config.expect_err( + &["rls", "--version"], + &format!( + "the 'rls' component which provides the command 'rls{}' is not available for the 'nightly-{}' toolchain", + EXE_SUFFIX, + this_host_triple(), + ), + ).await; + + set_current_dist_date(&cx.config, "2015-01-02"); + cx.config.expect_ok(&["rustup", "update"]).await; + cx.config + .expect_ok(&["rustup", "component", "add", "rls"]) + .await; + + cx.config.expect_ok(&["rls", "--version"]).await; } -#[test] -fn run_rls_when_not_installed() { - setup(&|config| { - config.expect_ok(&["rustup", "default", "stable"]); - config.expect_err( - &["rls", "--version"], - &format!( - "'rls{}' is not installed for the toolchain 'stable-{}'.\nTo install, run `rustup component add rls`", - EXE_SUFFIX, - this_host_triple(), - ), - ); - }); +#[tokio::test] +async fn run_rls_when_not_installed() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "default", "stable"]).await; + cx.config.expect_err( + &["rls", "--version"], + &format!( + "'rls{}' is not installed for the toolchain 'stable-{}'.\nTo install, run `rustup component add rls`", + EXE_SUFFIX, + this_host_triple(), + ), + ).await; } -#[test] -fn run_rls_when_not_installed_for_nightly() { - setup(&|config| { - config.expect_ok(&["rustup", "default", "stable"]); - config.expect_ok(&["rustup", "toolchain", "install", "nightly"]); - config.expect_err( - &["rls", "+nightly", "--version"], - &format!( - "'rls{}' is not installed for the toolchain 'nightly-{}'.\nTo install, run `rustup component add --toolchain nightly-{1} rls`", - EXE_SUFFIX, - this_host_triple(), - ), - ); - }); +#[tokio::test] +async fn run_rls_when_not_installed_for_nightly() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "default", "stable"]).await; + cx.config + .expect_ok(&["rustup", "toolchain", "install", "nightly"]) + .await; + cx.config.expect_err( + &["rls", "+nightly", "--version"], + &format!( + "'rls{}' is not installed for the toolchain 'nightly-{}'.\nTo install, run `rustup component add --toolchain nightly-{1} rls`", + EXE_SUFFIX, + this_host_triple(), + ), + ).await; } -#[test] -fn run_rust_lldb_when_not_in_toolchain() { - clitools::test(Scenario::UnavailableRls, &|config| { - set_current_dist_date(config, "2015-01-01"); - config.expect_ok(&["rustup", "default", "nightly"]); - config.expect_err( - &["rust-lldb", "--version"], - &format!( - "the 'rust-lldb{}' binary, normally provided by the 'rustc' component, is not applicable to the 'nightly-{}' toolchain", - EXE_SUFFIX, - this_host_triple(), - ), - ); - }); +#[tokio::test] +async fn run_rust_lldb_when_not_in_toolchain() { + let mut cx = CliTestContext::new(Scenario::UnavailableRls).await; + set_current_dist_date(&cx.config, "2015-01-01"); + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + cx.config.expect_err( + &["rust-lldb", "--version"], + &format!( + "the 'rust-lldb{}' binary, normally provided by the 'rustc' component, is not applicable to the 'nightly-{}' toolchain", + EXE_SUFFIX, + this_host_triple(), + ), + ).await; } -#[test] -fn rename_rls_before() { - clitools::test(Scenario::ArchivesV2, &|config| { - set_current_dist_date(config, "2015-01-01"); - config.expect_ok(&["rustup", "default", "nightly"]); - config.expect_ok(&["rustup", "component", "add", "rls"]); +#[tokio::test] +async fn rename_rls_before() { + let mut cx = CliTestContext::new(Scenario::ArchivesV2).await; + set_current_dist_date(&cx.config, "2015-01-01"); + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + cx.config + .expect_ok(&["rustup", "component", "add", "rls"]) + .await; - set_current_dist_date(config, "2015-01-02"); - config.expect_ok(&["rustup", "update"]); + set_current_dist_date(&cx.config, "2015-01-02"); + cx.config.expect_ok(&["rustup", "update"]).await; - assert!(config.exedir.join(format!("rls{EXE_SUFFIX}")).exists()); - config.expect_ok(&["rls", "--version"]); - }); + assert!(cx.config.exedir.join(format!("rls{EXE_SUFFIX}")).exists()); + cx.config.expect_ok(&["rls", "--version"]).await; } -#[test] -fn rename_rls_after() { - clitools::test(Scenario::ArchivesV2, &|config| { - set_current_dist_date(config, "2015-01-01"); - config.expect_ok(&["rustup", "default", "nightly"]); +#[tokio::test] +async fn rename_rls_after() { + let mut cx = CliTestContext::new(Scenario::ArchivesV2).await; + set_current_dist_date(&cx.config, "2015-01-01"); + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; - set_current_dist_date(config, "2015-01-02"); - config.expect_ok(&["rustup", "update"]); - config.expect_ok(&["rustup", "component", "add", "rls-preview"]); + set_current_dist_date(&cx.config, "2015-01-02"); + cx.config.expect_ok(&["rustup", "update"]).await; + cx.config + .expect_ok(&["rustup", "component", "add", "rls-preview"]) + .await; - assert!(config.exedir.join(format!("rls{EXE_SUFFIX}")).exists()); - config.expect_ok(&["rls", "--version"]); - }); + assert!(cx.config.exedir.join(format!("rls{EXE_SUFFIX}")).exists()); + cx.config.expect_ok(&["rls", "--version"]).await; } -#[test] -fn rename_rls_add_old_name() { - clitools::test(Scenario::ArchivesV2, &|config| { - set_current_dist_date(config, "2015-01-01"); - config.expect_ok(&["rustup", "default", "nightly"]); +#[tokio::test] +async fn rename_rls_add_old_name() { + let mut cx = CliTestContext::new(Scenario::ArchivesV2).await; + set_current_dist_date(&cx.config, "2015-01-01"); + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; - set_current_dist_date(config, "2015-01-02"); - config.expect_ok(&["rustup", "update"]); - config.expect_ok(&["rustup", "component", "add", "rls"]); + set_current_dist_date(&cx.config, "2015-01-02"); + cx.config.expect_ok(&["rustup", "update"]).await; + cx.config + .expect_ok(&["rustup", "component", "add", "rls"]) + .await; - assert!(config.exedir.join(format!("rls{EXE_SUFFIX}")).exists()); - config.expect_ok(&["rls", "--version"]); - }); + assert!(cx.config.exedir.join(format!("rls{EXE_SUFFIX}")).exists()); + cx.config.expect_ok(&["rls", "--version"]).await; } -#[test] -fn rename_rls_list() { - clitools::test(Scenario::ArchivesV2, &|config| { - set_current_dist_date(config, "2015-01-01"); - config.expect_ok(&["rustup", "default", "nightly"]); - - set_current_dist_date(config, "2015-01-02"); - config.expect_ok(&["rustup", "update"]); - config.expect_ok(&["rustup", "component", "add", "rls"]); - - let out = config.run("rustup", ["component", "list"], &[]); - assert!(out.ok); - assert!(out.stdout.contains(&format!("rls-{}", this_host_triple()))); - }); +#[tokio::test] +async fn rename_rls_list() { + let mut cx = CliTestContext::new(Scenario::ArchivesV2).await; + set_current_dist_date(&cx.config, "2015-01-01"); + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + + set_current_dist_date(&cx.config, "2015-01-02"); + cx.config.expect_ok(&["rustup", "update"]).await; + cx.config + .expect_ok(&["rustup", "component", "add", "rls"]) + .await; + + let out = cx.config.run("rustup", ["component", "list"], &[]).await; + assert!(out.ok); + assert!(out.stdout.contains(&format!("rls-{}", this_host_triple()))); } -#[test] -fn rename_rls_preview_list() { - clitools::test(Scenario::ArchivesV2, &|config| { - set_current_dist_date(config, "2015-01-01"); - config.expect_ok(&["rustup", "default", "nightly"]); - - set_current_dist_date(config, "2015-01-02"); - config.expect_ok(&["rustup", "update"]); - config.expect_ok(&["rustup", "component", "add", "rls-preview"]); - - let out = config.run("rustup", ["component", "list"], &[]); - assert!(out.ok); - assert!(out.stdout.contains(&format!("rls-{}", this_host_triple()))); - }); +#[tokio::test] +async fn rename_rls_preview_list() { + let mut cx = CliTestContext::new(Scenario::ArchivesV2).await; + set_current_dist_date(&cx.config, "2015-01-01"); + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + + set_current_dist_date(&cx.config, "2015-01-02"); + cx.config.expect_ok(&["rustup", "update"]).await; + cx.config + .expect_ok(&["rustup", "component", "add", "rls-preview"]) + .await; + + let out = cx.config.run("rustup", ["component", "list"], &[]).await; + assert!(out.ok); + assert!(out.stdout.contains(&format!("rls-{}", this_host_triple()))); } -#[test] -fn rename_rls_remove() { - clitools::test(Scenario::ArchivesV2, &|config| { - set_current_dist_date(config, "2015-01-01"); - config.expect_ok(&["rustup", "default", "nightly"]); - - set_current_dist_date(config, "2015-01-02"); - config.expect_ok(&["rustup", "update"]); - - config.expect_ok(&["rustup", "component", "add", "rls"]); - config.expect_ok(&["rls", "--version"]); - config.expect_ok(&["rustup", "component", "remove", "rls"]); - config.expect_err( +#[tokio::test] +async fn rename_rls_remove() { + let mut cx = CliTestContext::new(Scenario::ArchivesV2).await; + set_current_dist_date(&cx.config, "2015-01-01"); + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + + set_current_dist_date(&cx.config, "2015-01-02"); + cx.config.expect_ok(&["rustup", "update"]).await; + + cx.config + .expect_ok(&["rustup", "component", "add", "rls"]) + .await; + cx.config.expect_ok(&["rls", "--version"]).await; + cx.config + .expect_ok(&["rustup", "component", "remove", "rls"]) + .await; + cx.config + .expect_err( &["rls", "--version"], &format!("'rls{EXE_SUFFIX}' is not installed"), - ); - - config.expect_ok(&["rustup", "component", "add", "rls"]); - config.expect_ok(&["rls", "--version"]); - config.expect_ok(&["rustup", "component", "remove", "rls-preview"]); - config.expect_err( + ) + .await; + + cx.config + .expect_ok(&["rustup", "component", "add", "rls"]) + .await; + cx.config.expect_ok(&["rls", "--version"]).await; + cx.config + .expect_ok(&["rustup", "component", "remove", "rls-preview"]) + .await; + cx.config + .expect_err( &["rls", "--version"], &format!("'rls{EXE_SUFFIX}' is not installed"), - ); - }); + ) + .await; } // issue #3737 /// `~/.rustup/toolchains` is permitted to be a symlink. -#[test] +#[tokio::test] #[cfg(any(unix, windows))] -fn toolchains_symlink() { - use rustup::utils::raw::symlink_dir; - use std::fs; - - clitools::test(Scenario::SimpleV2, &|config| { - let cwd = config.current_dir(); - let test_toolchains = cwd.join("toolchains-test"); - fs::create_dir(&test_toolchains).unwrap(); - symlink_dir(&test_toolchains, &config.rustupdir.join("toolchains")).unwrap(); - - config.expect_ok(&["rustup", "default", "nightly"]); - config.expect_ok_contains(&["rustup", "toolchain", "list"], "nightly", ""); - config.expect_ok_contains(&["rustc", "--version"], "hash-nightly-2", ""); - config.expect_ok(&["rustup", "toolchain", "uninstall", "nightly"]); - config.expect_stdout_ok( +async fn toolchains_symlink() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + let cwd = cx.config.current_dir(); + let test_toolchains = cwd.join("toolchains-test"); + fs::create_dir(&test_toolchains).unwrap(); + symlink_dir(&test_toolchains, &cx.config.rustupdir.join("toolchains")).unwrap(); + + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + cx.config + .expect_ok_contains(&["rustup", "toolchain", "list"], "nightly", "") + .await; + cx.config + .expect_ok_contains(&["rustc", "--version"], "hash-nightly-2", "") + .await; + cx.config + .expect_ok(&["rustup", "toolchain", "uninstall", "nightly"]) + .await; + cx.config + .expect_stdout_ok( &["rustup", "toolchain", "list"], "no installed toolchains\n", - ); - }); + ) + .await; } // issue #3344 /// `~/.rustup/tmp` and `~/.rustup/downloads` are permitted to be symlinks. -#[test] +#[tokio::test] #[cfg(any(unix, windows))] -fn tmp_downloads_symlink() { - use rustup::utils::raw::symlink_dir; - use std::fs; - - clitools::test(Scenario::ArchivesV2, &|config| { - let cwd = config.current_dir(); +async fn tmp_downloads_symlink() { + let mut cx = CliTestContext::new(Scenario::ArchivesV2).await; + let cwd = cx.config.current_dir(); - let test_tmp = cwd.join("tmp-test"); - fs::create_dir(&test_tmp).unwrap(); - symlink_dir(&test_tmp, &config.rustupdir.join("tmp")).unwrap(); + let test_tmp = cwd.join("tmp-test"); + fs::create_dir(&test_tmp).unwrap(); + symlink_dir(&test_tmp, &cx.config.rustupdir.join("tmp")).unwrap(); - let test_downloads = cwd.join("tmp-downloads"); - fs::create_dir(&test_downloads).unwrap(); - symlink_dir(&test_downloads, &config.rustupdir.join("downloads")).unwrap(); + let test_downloads = cwd.join("tmp-downloads"); + fs::create_dir(&test_downloads).unwrap(); + symlink_dir(&test_downloads, &cx.config.rustupdir.join("downloads")).unwrap(); - set_current_dist_date(config, "2015-01-01"); - config.expect_ok(&["rustup", "default", "nightly"]); + set_current_dist_date(&cx.config, "2015-01-01"); + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; - set_current_dist_date(config, "2015-01-02"); - config.expect_ok(&["rustup", "update"]); + set_current_dist_date(&cx.config, "2015-01-02"); + cx.config.expect_ok(&["rustup", "update"]).await; - assert!(config.rustupdir.join("tmp").exists()); - assert!(config.rustupdir.join("downloads").exists()); - }); + assert!(cx.config.rustupdir.join("tmp").exists()); + assert!(cx.config.rustupdir.join("downloads").exists()); } // issue #1169 /// A toolchain that is a stale symlink should be correctly uninstalled. -#[test] +#[tokio::test] #[cfg(any(unix, windows))] -fn toolchain_broken_symlink() { - use rustup::utils::raw::symlink_dir; - use std::fs; - - clitools::test(Scenario::None, &|config| { - // We artificially create a broken symlink toolchain -- but this can also happen "legitimately" - // by having a proper toolchain there, using "toolchain link", and later removing the directory. - fs::create_dir(config.rustupdir.join("toolchains")).unwrap(); - fs::create_dir(config.rustupdir.join("this-directory-does-not-exist")).unwrap(); - symlink_dir( - &config.rustupdir.join("this-directory-does-not-exist"), - &config.rustupdir.join("toolchains").join("test"), - ) - .unwrap(); - fs::remove_dir(config.rustupdir.join("this-directory-does-not-exist")).unwrap(); - - // Make sure this "fake install" actually worked - config.expect_ok_ex(&["rustup", "toolchain", "list"], "test\n", ""); - // Now try to uninstall it. That should work only once. - config.expect_ok_ex( +async fn toolchain_broken_symlink() { + let mut cx = CliTestContext::new(Scenario::None).await; + // We artificially create a broken symlink toolchain -- but this can also happen "legitimately" + // by having a proper toolchain there, using "toolchain link", and later removing the directory. + fs::create_dir(cx.config.rustupdir.join("toolchains")).unwrap(); + fs::create_dir(cx.config.rustupdir.join("this-directory-does-not-exist")).unwrap(); + symlink_dir( + &cx.config.rustupdir.join("this-directory-does-not-exist"), + &cx.config.rustupdir.join("toolchains").join("test"), + ) + .unwrap(); + fs::remove_dir(cx.config.rustupdir.join("this-directory-does-not-exist")).unwrap(); + + // Make sure this "fake install" actually worked + cx.config + .expect_ok_ex(&["rustup", "toolchain", "list"], "test\n", "") + .await; + // Now try to uninstall it. That should work only once. + cx.config + .expect_ok_ex( &["rustup", "toolchain", "uninstall", "test"], "", r"info: uninstalling toolchain 'test' info: toolchain 'test' uninstalled ", - ); - config.expect_stderr_ok( + ) + .await; + cx.config + .expect_stderr_ok( &["rustup", "toolchain", "uninstall", "test"], "no toolchain installed for 'test'", - ); - }); + ) + .await; } // issue #1297 -#[test] -fn update_unavailable_rustc() { - clitools::test(Scenario::Unavailable, &|config| { - set_current_dist_date(config, "2015-01-01"); - config.expect_ok(&["rustup", "default", "nightly"]); - - config.expect_stdout_ok(&["rustc", "--version"], "hash-nightly-1"); - - // latest nightly is unavailable - set_current_dist_date(config, "2015-01-02"); - // update should do nothing - config.expect_ok(&["rustup", "update", "nightly"]); - config.expect_stdout_ok(&["rustc", "--version"], "hash-nightly-1"); - }); +#[tokio::test] +async fn update_unavailable_rustc() { + let mut cx = CliTestContext::new(Scenario::Unavailable).await; + set_current_dist_date(&cx.config, "2015-01-01"); + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + + cx.config + .expect_stdout_ok(&["rustc", "--version"], "hash-nightly-1") + .await; + + // latest nightly is unavailable + set_current_dist_date(&cx.config, "2015-01-02"); + // update should do nothing + cx.config.expect_ok(&["rustup", "update", "nightly"]).await; + cx.config + .expect_stdout_ok(&["rustc", "--version"], "hash-nightly-1") + .await; } // issue 2562 -#[test] -fn install_unavailable_platform() { - clitools::test(Scenario::Unavailable, &|config| { - set_current_dist_date(config, "2015-01-02"); - // explicit attempt to install should fail - config.expect_err( +#[tokio::test] +async fn install_unavailable_platform() { + let cx = CliTestContext::new(Scenario::Unavailable).await; + set_current_dist_date(&cx.config, "2015-01-02"); + // explicit attempt to install should fail + cx.config + .expect_err( &["rustup", "toolchain", "install", "nightly"], "is not installable", - ); - // implicit attempt to install should fail - config.expect_err(&["rustup", "default", "nightly"], "is not installable"); - }); + ) + .await; + // implicit attempt to install should fail + cx.config + .expect_err(&["rustup", "default", "nightly"], "is not installable") + .await; } // issue #1329 -#[test] -fn install_beta_with_tag() { - clitools::test(Scenario::BetaTag, &|config| { - config.expect_ok(&["rustup", "default", "1.78.0-beta"]); - config.expect_stdout_ok(&["rustc", "--version"], "1.78.0-beta"); - - config.expect_ok(&["rustup", "default", "1.79.0-beta.2"]); - config.expect_stdout_ok(&["rustc", "--version"], "1.79.0-beta.2"); - }) +#[tokio::test] +async fn install_beta_with_tag() { + let mut cx = CliTestContext::new(Scenario::BetaTag).await; + cx.config + .expect_ok(&["rustup", "default", "1.78.0-beta"]) + .await; + cx.config + .expect_stdout_ok(&["rustc", "--version"], "1.78.0-beta") + .await; + + cx.config + .expect_ok(&["rustup", "default", "1.79.0-beta.2"]) + .await; + cx.config + .expect_stdout_ok(&["rustc", "--version"], "1.79.0-beta.2") + .await; } -#[test] -fn update_nightly_even_with_incompat() { - clitools::test(Scenario::MissingComponent, &|config| { - set_current_dist_date(config, "2019-09-12"); - config.expect_ok(&["rustup", "default", "nightly"]); - - config.expect_stdout_ok(&["rustc", "--version"], "hash-nightly-1"); - config.expect_ok(&["rustup", "component", "add", "rls"]); - config.expect_component_executable("rls"); - - // latest nightly is now one that does not have RLS - set_current_dist_date(config, "2019-09-14"); - - config.expect_component_executable("rls"); - // update should bring us to latest nightly that does - config.expect_ok(&["rustup", "update", "nightly"]); - config.expect_stdout_ok(&["rustc", "--version"], "hash-nightly-2"); - config.expect_component_executable("rls"); - }); +#[tokio::test] +async fn update_nightly_even_with_incompat() { + let mut cx = CliTestContext::new(Scenario::MissingComponent).await; + set_current_dist_date(&cx.config, "2019-09-12"); + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + + cx.config + .expect_stdout_ok(&["rustc", "--version"], "hash-nightly-1") + .await; + cx.config + .expect_ok(&["rustup", "component", "add", "rls"]) + .await; + cx.config.expect_component_executable("rls").await; + + // latest nightly is now one that does not have RLS + set_current_dist_date(&cx.config, "2019-09-14"); + + cx.config.expect_component_executable("rls").await; + // update should bring us to latest nightly that does + cx.config.expect_ok(&["rustup", "update", "nightly"]).await; + cx.config + .expect_stdout_ok(&["rustc", "--version"], "hash-nightly-2") + .await; + cx.config.expect_component_executable("rls").await; } -#[test] -fn nightly_backtrack_skips_missing() { - clitools::test(Scenario::MissingNightly, &|config| { - set_current_dist_date(config, "2019-09-16"); - config.expect_ok(&["rustup", "default", "nightly"]); - - config.expect_stdout_ok(&["rustc", "--version"], "hash-nightly-1"); - config.expect_ok(&["rustup", "component", "add", "rls"]); - config.expect_component_executable("rls"); - - // rls is missing on latest, nightly is missing on second-to-latest - set_current_dist_date(config, "2019-09-18"); - - // update should not change nightly, and should not error - config.expect_ok(&["rustup", "update", "nightly"]); - config.expect_stdout_ok(&["rustc", "--version"], "hash-nightly-1"); - }); +#[tokio::test] +async fn nightly_backtrack_skips_missing() { + let mut cx = CliTestContext::new(Scenario::MissingNightly).await; + set_current_dist_date(&cx.config, "2019-09-16"); + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + + cx.config + .expect_stdout_ok(&["rustc", "--version"], "hash-nightly-1") + .await; + cx.config + .expect_ok(&["rustup", "component", "add", "rls"]) + .await; + cx.config.expect_component_executable("rls").await; + + // rls is missing on latest, nightly is missing on second-to-latest + set_current_dist_date(&cx.config, "2019-09-18"); + + // update should not change nightly, and should not error + cx.config.expect_ok(&["rustup", "update", "nightly"]).await; + cx.config + .expect_stdout_ok(&["rustc", "--version"], "hash-nightly-1") + .await; } -#[test] -fn completion_rustup() { - setup(&|config| { - config.expect_ok(&["rustup", "completions", "bash", "rustup"]); - }); +#[tokio::test] +async fn completion_rustup() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config + .expect_ok(&["rustup", "completions", "bash", "rustup"]) + .await; } -#[test] -fn completion_cargo() { - setup(&|config| { - config.expect_ok(&["rustup", "completions", "bash", "cargo"]); - }); +#[tokio::test] +async fn completion_cargo() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config + .expect_ok(&["rustup", "completions", "bash", "cargo"]) + .await; } -#[test] -fn completion_default() { - setup(&|config| { - config.expect_ok_eq( +#[tokio::test] +async fn completion_default() { + let cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config + .expect_ok_eq( &["rustup", "completions", "bash"], &["rustup", "completions", "bash", "rustup"], - ); - }); + ) + .await; } -#[test] -fn completion_bad_shell() { - setup(&|config| { - config.expect_err( +#[tokio::test] +async fn completion_bad_shell() { + let cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config + .expect_err( &["rustup", "completions", "fake"], r#"error: invalid value 'fake' for ''"#, - ); - config.expect_err( + ) + .await; + cx.config + .expect_err( &["rustup", "completions", "fake", "cargo"], r#"error: invalid value 'fake' for ''"#, - ); - }); + ) + .await; } -#[test] -fn completion_bad_tool() { - setup(&|config| { - config.expect_err( +#[tokio::test] +async fn completion_bad_tool() { + let cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config + .expect_err( &["rustup", "completions", "bash", "fake"], r#"error: invalid value 'fake' for '[COMMAND]'"#, - ); - }); + ) + .await; } -#[test] -fn completion_cargo_unsupported_shell() { - setup(&|config| { - config.expect_err( +#[tokio::test] +async fn completion_cargo_unsupported_shell() { + let cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config + .expect_err( &["rustup", "completions", "fish", "cargo"], "error: cargo does not currently support completions for ", - ); - }); + ) + .await; } -#[test] -fn add_remove_component() { - setup(&|config| { - config.expect_ok(&["rustup", "default", "nightly"]); - config.expect_component_executable("rustc"); - config.expect_ok(&["rustup", "component", "remove", "rustc"]); - config.expect_component_not_executable("rustc"); - config.expect_ok(&["rustup", "component", "add", "rustc"]); - config.expect_component_executable("rustc"); - }); +#[tokio::test] +async fn add_remove_component() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + cx.config.expect_component_executable("rustc").await; + cx.config + .expect_ok(&["rustup", "component", "remove", "rustc"]) + .await; + cx.config.expect_component_not_executable("rustc").await; + cx.config + .expect_ok(&["rustup", "component", "add", "rustc"]) + .await; + cx.config.expect_component_executable("rustc").await; } -#[test] -fn which() { - setup(&|config| { - let path_1 = config.customdir.join("custom-1"); - let path_1 = path_1.to_string_lossy(); - config.expect_ok(&["rustup", "toolchain", "link", "custom-1", &path_1]); - config.expect_ok(&["rustup", "default", "custom-1"]); - #[cfg(windows)] - config.expect_stdout_ok( +#[tokio::test] +async fn which() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + let path_1 = cx.config.customdir.join("custom-1"); + let path_1 = path_1.to_string_lossy(); + cx.config + .expect_ok(&["rustup", "toolchain", "link", "custom-1", &path_1]) + .await; + cx.config + .expect_ok(&["rustup", "default", "custom-1"]) + .await; + #[cfg(windows)] + cx.config + .expect_stdout_ok( &["rustup", "which", "rustc"], "\\toolchains\\custom-1\\bin\\rustc", - ); - #[cfg(not(windows))] - config.expect_stdout_ok( + ) + .await; + #[cfg(not(windows))] + cx.config + .expect_stdout_ok( &["rustup", "which", "rustc"], "/toolchains/custom-1/bin/rustc", - ); - let path_2 = config.customdir.join("custom-2"); - let path_2 = path_2.to_string_lossy(); - config.expect_ok(&["rustup", "toolchain", "link", "custom-2", &path_2]); - #[cfg(windows)] - config.expect_stdout_ok( + ) + .await; + let path_2 = cx.config.customdir.join("custom-2"); + let path_2 = path_2.to_string_lossy(); + cx.config + .expect_ok(&["rustup", "toolchain", "link", "custom-2", &path_2]) + .await; + #[cfg(windows)] + cx.config + .expect_stdout_ok( &["rustup", "which", "--toolchain=custom-2", "rustc"], "\\toolchains\\custom-2\\bin\\rustc", - ); - #[cfg(not(windows))] - config.expect_stdout_ok( + ) + .await; + #[cfg(not(windows))] + cx.config + .expect_stdout_ok( &["rustup", "which", "--toolchain=custom-2", "rustc"], "/toolchains/custom-2/bin/rustc", - ); - }); + ) + .await; } -#[test] -fn which_asking_uninstalled_toolchain() { - setup(&|config| { - let path_1 = config.customdir.join("custom-1"); - let path_1 = path_1.to_string_lossy(); - config.expect_ok(&["rustup", "toolchain", "link", "custom-1", &path_1]); - config.expect_ok(&["rustup", "default", "custom-1"]); - #[cfg(windows)] - config.expect_stdout_ok( +#[tokio::test] +async fn which_asking_uninstalled_toolchain() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + let path_1 = cx.config.customdir.join("custom-1"); + let path_1 = path_1.to_string_lossy(); + cx.config + .expect_ok(&["rustup", "toolchain", "link", "custom-1", &path_1]) + .await; + cx.config + .expect_ok(&["rustup", "default", "custom-1"]) + .await; + #[cfg(windows)] + cx.config + .expect_stdout_ok( &["rustup", "which", "rustc"], "\\toolchains\\custom-1\\bin\\rustc", - ); - #[cfg(not(windows))] - config.expect_stdout_ok( + ) + .await; + #[cfg(not(windows))] + cx.config + .expect_stdout_ok( &["rustup", "which", "rustc"], "/toolchains/custom-1/bin/rustc", - ); - config.expect_err( + ) + .await; + cx.config + .expect_err( &["rustup", "which", "--toolchain=nightly", "rustc"], for_host!("toolchain 'nightly-{}' is not installed"), - ); - }); + ) + .await; } -#[test] -fn override_by_toolchain_on_the_command_line() { - setup(&|config| { - #[cfg(windows)] - config.expect_stdout_ok( +#[tokio::test] +async fn override_by_toolchain_on_the_command_line() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + #[cfg(windows)] + cx.config + .expect_stdout_ok( &["rustup", "+stable", "which", "rustc"], for_host!("\\toolchains\\stable-{}"), - ); - #[cfg(windows)] - config.expect_stdout_ok(&["rustup", "+stable", "which", "rustc"], "\\bin\\rustc"); - #[cfg(not(windows))] - config.expect_stdout_ok( + ) + .await; + #[cfg(windows)] + cx.config + .expect_stdout_ok(&["rustup", "+stable", "which", "rustc"], "\\bin\\rustc") + .await; + #[cfg(not(windows))] + cx.config + .expect_stdout_ok( &["rustup", "+stable", "which", "rustc"], for_host!("/toolchains/stable-{}"), - ); - #[cfg(not(windows))] - config.expect_stdout_ok(&["rustup", "+stable", "which", "rustc"], "/bin/rustc"); - config.expect_ok(&["rustup", "default", "nightly"]); - #[cfg(windows)] - config.expect_stdout_ok( + ) + .await; + #[cfg(not(windows))] + cx.config + .expect_stdout_ok(&["rustup", "+stable", "which", "rustc"], "/bin/rustc") + .await; + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + #[cfg(windows)] + cx.config + .expect_stdout_ok( &["rustup", "+nightly", "which", "rustc"], for_host!("\\toolchains\\nightly-{}"), - ); - #[cfg(windows)] - config.expect_stdout_ok(&["rustup", "+nightly", "which", "rustc"], "\\bin\\rustc"); - #[cfg(not(windows))] - config.expect_stdout_ok( + ) + .await; + #[cfg(windows)] + cx.config + .expect_stdout_ok(&["rustup", "+nightly", "which", "rustc"], "\\bin\\rustc") + .await; + #[cfg(not(windows))] + cx.config + .expect_stdout_ok( &["rustup", "+nightly", "which", "rustc"], for_host!("/toolchains/nightly-{}"), - ); - #[cfg(not(windows))] - config.expect_stdout_ok(&["rustup", "+nightly", "which", "rustc"], "/bin/rustc"); - config.expect_stdout_ok( + ) + .await; + #[cfg(not(windows))] + cx.config + .expect_stdout_ok(&["rustup", "+nightly", "which", "rustc"], "/bin/rustc") + .await; + cx.config + .expect_stdout_ok( &["rustup", "+nightly", "show"], "active because: overridden by +toolchain on the command line", - ); - config.expect_err( + ) + .await; + cx.config + .expect_err( &["rustup", "+foo", "which", "rustc"], "toolchain 'foo' is not installed", - ); - config.expect_stderr_ok( + ) + .await; + cx.config + .expect_stderr_ok( &["rustup", "+stable", "set", "profile", "minimal"], "profile set to 'minimal'", - ); - config.expect_stdout_ok(&["rustup", "default"], for_host!("nightly-{}")); - }); + ) + .await; + cx.config + .expect_stdout_ok(&["rustup", "default"], for_host!("nightly-{}")) + .await; } -#[test] -fn toolchain_link_then_list_verbose() { - setup(&|config| { - let path_1 = config.customdir.join("custom-1"); - let path_1 = path_1.to_string_lossy(); - config.expect_ok(&["rustup", "toolchain", "link", "custom-1", &path_1]); - #[cfg(windows)] - config.expect_stdout_ok(&["rustup", "toolchain", "list", "-v"], "\\custom-1"); - #[cfg(not(windows))] - config.expect_stdout_ok(&["rustup", "toolchain", "list", "-v"], "/custom-1"); - }); +#[tokio::test] +async fn toolchain_link_then_list_verbose() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + let path_1 = cx.config.customdir.join("custom-1"); + let path_1 = path_1.to_string_lossy(); + cx.config + .expect_ok(&["rustup", "toolchain", "link", "custom-1", &path_1]) + .await; + #[cfg(windows)] + cx.config + .expect_stdout_ok(&["rustup", "toolchain", "list", "-v"], "\\custom-1") + .await; + #[cfg(not(windows))] + cx.config + .expect_stdout_ok(&["rustup", "toolchain", "list", "-v"], "/custom-1") + .await; } diff --git a/tests/suite/cli_paths.rs b/tests/suite/cli_paths.rs index 4bcd20e401..c88a7e6120 100644 --- a/tests/suite/cli_paths.rs +++ b/tests/suite/cli_paths.rs @@ -12,7 +12,7 @@ mod unix { use std::path::PathBuf; use super::INIT_NONE; - use rustup::test::mock::clitools::{self, Scenario}; + use rustup::test::mock::clitools::{self, CliTestContext, Scenario}; use rustup::utils::raw; // Let's write a fake .rc which looks vaguely like a real script. @@ -37,113 +37,108 @@ export PATH="$HOME/apple/bin" format!("source \"{dir}/{sh}\"\n") } - #[test] - fn install_creates_necessary_scripts() { - clitools::test(Scenario::Empty, &|config| { - // Override the test harness so that cargo home looks like - // $HOME/.cargo by removing CARGO_HOME from the environment, - // otherwise the literal path will be written to the file. - - let mut cmd = clitools::cmd(config, "rustup-init", &INIT_NONE[1..]); - let files: Vec = [".cargo/env", ".profile", ".zshenv"] - .iter() - .map(|file| config.homedir.join(file)) - .collect(); - for file in &files { - assert!(!file.exists()); - } - cmd.env_remove("CARGO_HOME"); - cmd.env("SHELL", "zsh"); - assert!(cmd.output().unwrap().status.success()); - let mut rcs = files.iter(); - let env = rcs.next().unwrap(); - let envfile = fs::read_to_string(env).unwrap(); - let (_, envfile_export) = envfile.split_at(envfile.find("export PATH").unwrap_or(0)); - assert_eq!(&envfile_export[..DEFAULT_EXPORT.len()], DEFAULT_EXPORT); - - for rc in rcs { - let expected = source("$HOME/.cargo", POSIX_SH); - let new_profile = fs::read_to_string(rc).unwrap(); - assert_eq!(new_profile, expected); - } - }); + #[tokio::test] + async fn install_creates_necessary_scripts() { + let cx = CliTestContext::new(Scenario::Empty).await; + // Override the test harness so that cargo home looks like + // $HOME/.cargo by removing CARGO_HOME from the environment, + // otherwise the literal path will be written to the file. + + let mut cmd = clitools::cmd(&cx.config, "rustup-init", &INIT_NONE[1..]); + let files: Vec = [".cargo/env", ".profile", ".zshenv"] + .iter() + .map(|file| cx.config.homedir.join(file)) + .collect(); + for file in &files { + assert!(!file.exists()); + } + cmd.env_remove("CARGO_HOME"); + cmd.env("SHELL", "zsh"); + assert!(cmd.output().unwrap().status.success()); + let mut rcs = files.iter(); + let env = rcs.next().unwrap(); + let envfile = fs::read_to_string(env).unwrap(); + let (_, envfile_export) = envfile.split_at(envfile.find("export PATH").unwrap_or(0)); + assert_eq!(&envfile_export[..DEFAULT_EXPORT.len()], DEFAULT_EXPORT); + + for rc in rcs { + let expected = source("$HOME/.cargo", POSIX_SH); + let new_profile = fs::read_to_string(rc).unwrap(); + assert_eq!(new_profile, expected); + } } - #[test] - fn install_updates_bash_rcs() { - clitools::test(Scenario::Empty, &|config| { - let rcs: Vec = [".bashrc", ".bash_profile", ".bash_login", ".profile"] - .iter() - .map(|rc| config.homedir.join(rc)) - .collect(); - for rc in &rcs { - raw::write_file(rc, FAKE_RC).unwrap(); - } - - config.expect_ok(&INIT_NONE); - - let expected = FAKE_RC.to_owned() + &source(config.cargodir.display(), POSIX_SH); - for rc in &rcs { - let new_rc = fs::read_to_string(rc).unwrap(); - assert_eq!(new_rc, expected); - } - }) + #[tokio::test] + async fn install_updates_bash_rcs() { + let mut cx = CliTestContext::new(Scenario::Empty).await; + let rcs: Vec = [".bashrc", ".bash_profile", ".bash_login", ".profile"] + .iter() + .map(|rc| cx.config.homedir.join(rc)) + .collect(); + for rc in &rcs { + raw::write_file(rc, FAKE_RC).unwrap(); + } + + cx.config.expect_ok(&INIT_NONE).await; + + let expected = FAKE_RC.to_owned() + &source(cx.config.cargodir.display(), POSIX_SH); + for rc in &rcs { + let new_rc = fs::read_to_string(rc).unwrap(); + assert_eq!(new_rc, expected); + } } - #[test] - fn install_does_not_create_bash_rcs() { - clitools::test(Scenario::Empty, &|config| { - let rcs: Vec = [".bashrc", ".bash_profile", ".bash_login"] - .iter() - .map(|rc| config.homedir.join(rc)) - .collect(); - let rcs_before = rcs.iter().map(|rc| rc.exists()); - config.expect_ok(&INIT_NONE); - - for (before, after) in rcs_before.zip(rcs.iter().map(|rc| rc.exists())) { - assert!(!before); - assert_eq!(before, after); - } - }); + #[tokio::test] + async fn install_does_not_create_bash_rcs() { + let mut cx = CliTestContext::new(Scenario::Empty).await; + let rcs: Vec = [".bashrc", ".bash_profile", ".bash_login"] + .iter() + .map(|rc| cx.config.homedir.join(rc)) + .collect(); + let rcs_before = rcs.iter().map(|rc| rc.exists()); + cx.config.expect_ok(&INIT_NONE).await; + + for (before, after) in rcs_before.zip(rcs.iter().map(|rc| rc.exists())) { + assert!(!before); + assert_eq!(before, after); + } } // This test should NOT be run as root! - #[test] - fn install_errors_when_rc_cannot_be_updated() { - clitools::test(Scenario::Empty, &|config| { - let rc = config.homedir.join(".profile"); - fs::File::create(&rc).unwrap(); - let mut perms = fs::metadata(&rc).unwrap().permissions(); - perms.set_readonly(true); - fs::set_permissions(&rc, perms).unwrap(); - - config.expect_err(&INIT_NONE, "amend shell"); - }); + #[tokio::test] + async fn install_errors_when_rc_cannot_be_updated() { + let cx = CliTestContext::new(Scenario::Empty).await; + let rc = cx.config.homedir.join(".profile"); + fs::File::create(&rc).unwrap(); + let mut perms = fs::metadata(&rc).unwrap().permissions(); + perms.set_readonly(true); + fs::set_permissions(&rc, perms).unwrap(); + + cx.config.expect_err(&INIT_NONE, "amend shell").await; } - #[test] - fn install_with_zdotdir() { - clitools::test(Scenario::Empty, &|config| { - let zdotdir = tempfile::Builder::new() - .prefix("zdotdir") - .tempdir() - .unwrap(); - let rc = zdotdir.path().join(".zshenv"); - raw::write_file(&rc, FAKE_RC).unwrap(); - - let mut cmd = clitools::cmd(config, "rustup-init", &INIT_NONE[1..]); - cmd.env("SHELL", "zsh"); - cmd.env("ZDOTDIR", zdotdir.path()); - assert!(cmd.output().unwrap().status.success()); - - let new_rc = fs::read_to_string(&rc).unwrap(); - let expected = FAKE_RC.to_owned() + &source(config.cargodir.display(), POSIX_SH); - assert_eq!(new_rc, expected); - }); + #[tokio::test] + async fn install_with_zdotdir() { + let cx = CliTestContext::new(Scenario::Empty).await; + let zdotdir = tempfile::Builder::new() + .prefix("zdotdir") + .tempdir() + .unwrap(); + let rc = zdotdir.path().join(".zshenv"); + raw::write_file(&rc, FAKE_RC).unwrap(); + + let mut cmd = clitools::cmd(&cx.config, "rustup-init", &INIT_NONE[1..]); + cmd.env("SHELL", "zsh"); + cmd.env("ZDOTDIR", zdotdir.path()); + assert!(cmd.output().unwrap().status.success()); + + let new_rc = fs::read_to_string(&rc).unwrap(); + let expected = FAKE_RC.to_owned() + &source(cx.config.cargodir.display(), POSIX_SH); + assert_eq!(new_rc, expected); } - #[test] - fn install_with_zdotdir_from_calling_zsh() { + #[tokio::test] + async fn install_with_zdotdir_from_calling_zsh() { // This test requires that zsh is callable. if std::process::Command::new("zsh") .arg("-c") @@ -153,309 +148,305 @@ export PATH="$HOME/apple/bin" { return; } - clitools::test(Scenario::Empty, &|config| { - let zdotdir = tempfile::Builder::new() - .prefix("zdotdir") - .tempdir() - .unwrap(); - let rc = zdotdir.path().join(".zshenv"); - raw::write_file(&rc, FAKE_RC).unwrap(); - - // If $SHELL doesn't include "zsh", Zsh::zdotdir() will call zsh to obtain $ZDOTDIR. - // ZDOTDIR could be set directly in the environment, but having ~/.zshenv set - // ZDOTDIR is a normal setup, and ensures that the value came from calling zsh. - let home_zshenv = config.homedir.join(".zshenv"); - let export_zdotdir = format!( - "export ZDOTDIR=\"{}\"\n", - zdotdir.path().as_os_str().to_str().unwrap() - ); - raw::write_file(&home_zshenv, &export_zdotdir).unwrap(); - - let mut cmd = clitools::cmd(config, "rustup-init", &INIT_NONE[1..]); - cmd.env("SHELL", "/bin/sh"); - assert!(cmd.output().unwrap().status.success()); - - let new_rc = fs::read_to_string(&rc).unwrap(); - let expected = FAKE_RC.to_owned() + &source(config.cargodir.display(), POSIX_SH); - assert_eq!(new_rc, expected); - }); - } - #[test] - fn install_adds_path_to_rc_just_once() { - clitools::test(Scenario::Empty, &|config| { - let profile = config.homedir.join(".profile"); - raw::write_file(&profile, FAKE_RC).unwrap(); - config.expect_ok(&INIT_NONE); - config.expect_ok(&INIT_NONE); + let cx = CliTestContext::new(Scenario::Empty).await; + let zdotdir = tempfile::Builder::new() + .prefix("zdotdir") + .tempdir() + .unwrap(); + let rc = zdotdir.path().join(".zshenv"); + raw::write_file(&rc, FAKE_RC).unwrap(); + + // If $SHELL doesn't include "zsh", Zsh::zdotdir() will call zsh to obtain $ZDOTDIR. + // ZDOTDIR could be set directly in the environment, but having ~/.zshenv set + // ZDOTDIR is a normal setup, and ensures that the value came from calling zsh. + let home_zshenv = cx.config.homedir.join(".zshenv"); + let export_zdotdir = format!( + "export ZDOTDIR=\"{}\"\n", + zdotdir.path().as_os_str().to_str().unwrap() + ); + raw::write_file(&home_zshenv, &export_zdotdir).unwrap(); + + let mut cmd = clitools::cmd(&cx.config, "rustup-init", &INIT_NONE[1..]); + cmd.env("SHELL", "/bin/sh"); + assert!(cmd.output().unwrap().status.success()); + + let new_rc = fs::read_to_string(&rc).unwrap(); + let expected = FAKE_RC.to_owned() + &source(cx.config.cargodir.display(), POSIX_SH); + assert_eq!(new_rc, expected); + } - let new_profile = fs::read_to_string(&profile).unwrap(); - let expected = FAKE_RC.to_owned() + &source(config.cargodir.display(), POSIX_SH); - assert_eq!(new_profile, expected); - }); + #[tokio::test] + async fn install_adds_path_to_rc_just_once() { + let mut cx = CliTestContext::new(Scenario::Empty).await; + let profile = cx.config.homedir.join(".profile"); + raw::write_file(&profile, FAKE_RC).unwrap(); + cx.config.expect_ok(&INIT_NONE).await; + cx.config.expect_ok(&INIT_NONE).await; + + let new_profile = fs::read_to_string(&profile).unwrap(); + let expected = FAKE_RC.to_owned() + &source(cx.config.cargodir.display(), POSIX_SH); + assert_eq!(new_profile, expected); } - #[test] - fn install_adds_path_to_rc_handling_no_newline() { - clitools::test(Scenario::Empty, &|config| { - let profile = config.homedir.join(".profile"); - let fake_rc_modified = FAKE_RC.strip_suffix('\n').expect("Should end in a newline"); - raw::write_file(&profile, fake_rc_modified).unwrap(); - // Run once to add the configuration - config.expect_ok(&INIT_NONE); - // Run twice to test that the process is idempotent - config.expect_ok(&INIT_NONE); - - let new_profile = fs::read_to_string(&profile).unwrap(); - let expected = FAKE_RC.to_owned() + &source(config.cargodir.display(), POSIX_SH); - assert_eq!(new_profile, expected); - }); + #[tokio::test] + async fn install_adds_path_to_rc_handling_no_newline() { + let mut cx = CliTestContext::new(Scenario::Empty).await; + let profile = cx.config.homedir.join(".profile"); + let fake_rc_modified = FAKE_RC.strip_suffix('\n').expect("Should end in a newline"); + raw::write_file(&profile, fake_rc_modified).unwrap(); + // Run once to add the configuration + cx.config.expect_ok(&INIT_NONE).await; + // Run twice to test that the process is idempotent + cx.config.expect_ok(&INIT_NONE).await; + + let new_profile = fs::read_to_string(&profile).unwrap(); + let expected = FAKE_RC.to_owned() + &source(cx.config.cargodir.display(), POSIX_SH); + assert_eq!(new_profile, expected); } - #[test] - fn install_adds_path_to_multiple_rc_files() { - clitools::test(Scenario::Empty, &|config| { - // Two RC files that are both from the same shell - let bash_profile = config.homedir.join(".bash_profile"); - let bashrc = config.homedir.join(".bashrc"); + #[tokio::test] + async fn install_adds_path_to_multiple_rc_files() { + let mut cx = CliTestContext::new(Scenario::Empty).await; + // Two RC files that are both from the same shell + let bash_profile = cx.config.homedir.join(".bash_profile"); + let bashrc = cx.config.homedir.join(".bashrc"); - let expected = FAKE_RC.to_owned() + &source(config.cargodir.display(), POSIX_SH); + let expected = FAKE_RC.to_owned() + &source(cx.config.cargodir.display(), POSIX_SH); - // The order that the two files are processed isn't known, so test both orders - for [path1, path2] in &[[&bash_profile, &bashrc], [&bashrc, &bash_profile]] { - raw::write_file(path1, &expected).unwrap(); - raw::write_file(path2, FAKE_RC).unwrap(); + // The order that the two files are processed isn't known, so test both orders + for [path1, path2] in &[[&bash_profile, &bashrc], [&bashrc, &bash_profile]] { + raw::write_file(path1, &expected).unwrap(); + raw::write_file(path2, FAKE_RC).unwrap(); - config.expect_ok(&INIT_NONE); + cx.config.expect_ok(&INIT_NONE).await; - let new1 = fs::read_to_string(path1).unwrap(); - assert_eq!(new1, expected); - let new2 = fs::read_to_string(path2).unwrap(); - assert_eq!(new2, expected); - } - }); + let new1 = fs::read_to_string(path1).unwrap(); + assert_eq!(new1, expected); + let new2 = fs::read_to_string(path2).unwrap(); + assert_eq!(new2, expected); + } } - #[test] - fn uninstall_removes_source_from_rcs() { - clitools::test(Scenario::Empty, &|config| { - let rcs: Vec = [ - ".bashrc", - ".bash_profile", - ".bash_login", - ".profile", - ".zshenv", - ] - .iter() - .map(|rc| config.homedir.join(rc)) - .collect(); - - for rc in &rcs { - raw::write_file(rc, FAKE_RC).unwrap(); - } + #[tokio::test] + async fn uninstall_removes_source_from_rcs() { + let mut cx = CliTestContext::new(Scenario::Empty).await; + let rcs: Vec = [ + ".bashrc", + ".bash_profile", + ".bash_login", + ".profile", + ".zshenv", + ] + .iter() + .map(|rc| cx.config.homedir.join(rc)) + .collect(); + + for rc in &rcs { + raw::write_file(rc, FAKE_RC).unwrap(); + } - config.expect_ok(&INIT_NONE); - config.expect_ok(&["rustup", "self", "uninstall", "-y"]); + cx.config.expect_ok(&INIT_NONE).await; + cx.config + .expect_ok(&["rustup", "self", "uninstall", "-y"]) + .await; - for rc in &rcs { - let new_rc = fs::read_to_string(rc).unwrap(); - assert_eq!(new_rc, FAKE_RC); - } - }) + for rc in &rcs { + let new_rc = fs::read_to_string(rc).unwrap(); + assert_eq!(new_rc, FAKE_RC); + } } - #[test] - fn install_adds_sources_while_removing_legacy_paths() { - clitools::test(Scenario::Empty, &|config| { - let zdotdir = tempfile::Builder::new() - .prefix("zdotdir") - .tempdir() - .unwrap(); - let rcs: Vec = [".bash_profile", ".profile"] - .iter() - .map(|rc| config.homedir.join(rc)) - .collect(); - let zprofiles = vec![ - config.homedir.join(".zprofile"), - zdotdir.path().join(".zprofile"), - ]; - let old_rc = - FAKE_RC.to_owned() + DEFAULT_EXPORT + &non_posix_source("$HOME/.cargo", POSIX_SH); - for rc in rcs.iter().chain(zprofiles.iter()) { - raw::write_file(rc, &old_rc).unwrap(); - } - - let mut cmd = clitools::cmd(config, "rustup-init", &INIT_NONE[1..]); - cmd.env("SHELL", "zsh"); - cmd.env("ZDOTDIR", zdotdir.path()); - cmd.env_remove("CARGO_HOME"); - assert!(cmd.output().unwrap().status.success()); - let fixed_rc = FAKE_RC.to_owned() + &source("$HOME/.cargo", POSIX_SH); - for rc in &rcs { - let new_rc = fs::read_to_string(rc).unwrap(); - assert_eq!(new_rc, fixed_rc); - } - for rc in &zprofiles { - let new_rc = fs::read_to_string(rc).unwrap(); - assert_eq!(new_rc, FAKE_RC); - } - }) + #[tokio::test] + async fn install_adds_sources_while_removing_legacy_paths() { + let cx = CliTestContext::new(Scenario::Empty).await; + let zdotdir = tempfile::Builder::new() + .prefix("zdotdir") + .tempdir() + .unwrap(); + let rcs: Vec = [".bash_profile", ".profile"] + .iter() + .map(|rc| cx.config.homedir.join(rc)) + .collect(); + let zprofiles = vec![ + cx.config.homedir.join(".zprofile"), + zdotdir.path().join(".zprofile"), + ]; + let old_rc = + FAKE_RC.to_owned() + DEFAULT_EXPORT + &non_posix_source("$HOME/.cargo", POSIX_SH); + for rc in rcs.iter().chain(zprofiles.iter()) { + raw::write_file(rc, &old_rc).unwrap(); + } + + let mut cmd = clitools::cmd(&cx.config, "rustup-init", &INIT_NONE[1..]); + cmd.env("SHELL", "zsh"); + cmd.env("ZDOTDIR", zdotdir.path()); + cmd.env_remove("CARGO_HOME"); + assert!(cmd.output().unwrap().status.success()); + let fixed_rc = FAKE_RC.to_owned() + &source("$HOME/.cargo", POSIX_SH); + for rc in &rcs { + let new_rc = fs::read_to_string(rc).unwrap(); + assert_eq!(new_rc, fixed_rc); + } + for rc in &zprofiles { + let new_rc = fs::read_to_string(rc).unwrap(); + assert_eq!(new_rc, FAKE_RC); + } } - #[test] - fn uninstall_cleans_up_legacy_paths() { - clitools::test(Scenario::Empty, &|config| { - // Install first, then overwrite. - config.expect_ok(&INIT_NONE); - - let zdotdir = tempfile::Builder::new() - .prefix("zdotdir") - .tempdir() - .unwrap(); - let mut cmd = clitools::cmd(config, "rustup-init", &INIT_NONE[1..]); - cmd.env("SHELL", "zsh"); - cmd.env("ZDOTDIR", zdotdir.path()); - cmd.env_remove("CARGO_HOME"); - assert!(cmd.output().unwrap().status.success()); - let mut rcs: Vec = [".bash_profile", ".profile", ".zprofile"] - .iter() - .map(|rc| config.homedir.join(rc)) - .collect(); - rcs.push(zdotdir.path().join(".zprofile")); - let old_rc = - FAKE_RC.to_owned() + DEFAULT_EXPORT + &non_posix_source("$HOME/.cargo", POSIX_SH); - for rc in &rcs { - raw::write_file(rc, &old_rc).unwrap(); - } - - let mut cmd = clitools::cmd(config, "rustup", ["self", "uninstall", "-y"]); - cmd.env("SHELL", "zsh"); - cmd.env("ZDOTDIR", zdotdir.path()); - cmd.env_remove("CARGO_HOME"); - assert!(cmd.output().unwrap().status.success()); - - for rc in &rcs { - let new_rc = fs::read_to_string(rc).unwrap(); - // It's not ideal, but it's OK, if we leave whitespace. - assert_eq!(new_rc, FAKE_RC); - } - }) + #[tokio::test] + async fn uninstall_cleans_up_legacy_paths() { + let mut cx = CliTestContext::new(Scenario::Empty).await; + // Install first, then overwrite. + cx.config.expect_ok(&INIT_NONE).await; + + let zdotdir = tempfile::Builder::new() + .prefix("zdotdir") + .tempdir() + .unwrap(); + let mut cmd = clitools::cmd(&cx.config, "rustup-init", &INIT_NONE[1..]); + cmd.env("SHELL", "zsh"); + cmd.env("ZDOTDIR", zdotdir.path()); + cmd.env_remove("CARGO_HOME"); + assert!(cmd.output().unwrap().status.success()); + let mut rcs: Vec = [".bash_profile", ".profile", ".zprofile"] + .iter() + .map(|rc| cx.config.homedir.join(rc)) + .collect(); + rcs.push(zdotdir.path().join(".zprofile")); + let old_rc = + FAKE_RC.to_owned() + DEFAULT_EXPORT + &non_posix_source("$HOME/.cargo", POSIX_SH); + for rc in &rcs { + raw::write_file(rc, &old_rc).unwrap(); + } + + let mut cmd = clitools::cmd(&cx.config, "rustup", ["self", "uninstall", "-y"]); + cmd.env("SHELL", "zsh"); + cmd.env("ZDOTDIR", zdotdir.path()); + cmd.env_remove("CARGO_HOME"); + assert!(cmd.output().unwrap().status.success()); + + for rc in &rcs { + let new_rc = fs::read_to_string(rc).unwrap(); + // It's not ideal, but it's OK, if we leave whitespace. + assert_eq!(new_rc, FAKE_RC); + } } // In the default case we want to write $HOME/.cargo/bin as the path, // not the full path. - #[test] - fn when_cargo_home_is_the_default_write_path_specially() { - clitools::test(Scenario::Empty, &|config| { - // Override the test harness so that cargo home looks like - // $HOME/.cargo by removing CARGO_HOME from the environment, - // otherwise the literal path will be written to the file. - - let profile = config.homedir.join(".profile"); - raw::write_file(&profile, FAKE_RC).unwrap(); - let mut cmd = clitools::cmd(config, "rustup-init", &INIT_NONE[1..]); - cmd.env_remove("CARGO_HOME"); - assert!(cmd.output().unwrap().status.success()); - - let new_profile = fs::read_to_string(&profile).unwrap(); - let expected = format!("{FAKE_RC}. \"$HOME/.cargo/env\"\n"); - assert_eq!(new_profile, expected); - - let mut cmd = clitools::cmd(config, "rustup", ["self", "uninstall", "-y"]); - cmd.env_remove("CARGO_HOME"); - assert!(cmd.output().unwrap().status.success()); - - let new_profile = fs::read_to_string(&profile).unwrap(); - assert_eq!(new_profile, FAKE_RC); - }); + #[tokio::test] + async fn when_cargo_home_is_the_default_write_path_specially() { + let cx = CliTestContext::new(Scenario::Empty).await; + // Override the test harness so that cargo home looks like + // $HOME/.cargo by removing CARGO_HOME from the environment, + // otherwise the literal path will be written to the file. + + let profile = cx.config.homedir.join(".profile"); + raw::write_file(&profile, FAKE_RC).unwrap(); + let mut cmd = clitools::cmd(&cx.config, "rustup-init", &INIT_NONE[1..]); + cmd.env_remove("CARGO_HOME"); + assert!(cmd.output().unwrap().status.success()); + + let new_profile = fs::read_to_string(&profile).unwrap(); + let expected = format!("{FAKE_RC}. \"$HOME/.cargo/env\"\n"); + assert_eq!(new_profile, expected); + + let mut cmd = clitools::cmd(&cx.config, "rustup", ["self", "uninstall", "-y"]); + cmd.env_remove("CARGO_HOME"); + assert!(cmd.output().unwrap().status.success()); + + let new_profile = fs::read_to_string(&profile).unwrap(); + assert_eq!(new_profile, FAKE_RC); } - #[test] - fn install_doesnt_modify_path_if_passed_no_modify_path() { - clitools::test(Scenario::Empty, &|config| { - let profile = config.homedir.join(".profile"); - config.expect_ok(&[ + #[tokio::test] + async fn install_doesnt_modify_path_if_passed_no_modify_path() { + let mut cx = CliTestContext::new(Scenario::Empty).await; + let profile = cx.config.homedir.join(".profile"); + cx.config + .expect_ok(&[ "rustup-init", "-y", "--no-modify-path", "--default-toolchain", "none", - ]); - assert!(!profile.exists()); - }); + ]) + .await; + assert!(!profile.exists()); } } #[cfg(windows)] mod windows { use super::INIT_NONE; - use rustup::test::mock::clitools::{self, Scenario}; - use rustup::test::{get_path, with_saved_path}; + use rustup::test::mock::clitools::{CliTestContext, Scenario}; + use rustup::test::{get_path, RegistryGuard, USER_PATH}; - #[test] + #[tokio::test] /// Smoke test for end-to-end code connectivity of the installer path mgmt on windows. - fn install_uninstall_affect_path() { - clitools::test(Scenario::Empty, &|config| { - with_saved_path(&mut || { - let cfg_path = config.cargodir.join("bin").display().to_string(); - let get_path_ = || get_path().unwrap().unwrap().to_string(); - - config.expect_ok(&INIT_NONE); - assert!( - get_path_().contains(cfg_path.trim_matches('"')), - "`{}` not in `{}`", - cfg_path, - get_path_() - ); - - config.expect_ok(&["rustup", "self", "uninstall", "-y"]); - assert!(!get_path_().contains(&cfg_path)); - }) - }); + async fn install_uninstall_affect_path() { + let mut cx = CliTestContext::new(Scenario::Empty).await; + let _guard = RegistryGuard::new(&USER_PATH).unwrap(); + let cfg_path = cx.config.cargodir.join("bin").display().to_string(); + let get_path_ = || get_path().unwrap().unwrap().to_string(); + + cx.config.expect_ok(&INIT_NONE).await; + assert!( + get_path_().contains(cfg_path.trim_matches('"')), + "`{}` not in `{}`", + cfg_path, + get_path_() + ); + + cx.config + .expect_ok(&["rustup", "self", "uninstall", "-y"]) + .await; + assert!(!get_path_().contains(&cfg_path)); } - #[test] + #[tokio::test] /// Smoke test for end-to-end code connectivity of the installer path mgmt on windows. - fn install_uninstall_affect_path_with_non_unicode() { + async fn install_uninstall_affect_path_with_non_unicode() { use std::ffi::OsString; use std::os::windows::ffi::OsStrExt; use winreg::enums::{RegType, HKEY_CURRENT_USER, KEY_READ, KEY_WRITE}; use winreg::{RegKey, RegValue}; - clitools::test(Scenario::Empty, &|config| { - with_saved_path(&mut || { - // Set up a non unicode PATH - let reg_value = RegValue { - bytes: vec![ - 0x00, 0xD8, // leading surrogate - 0x01, 0x01, // bogus trailing surrogate - 0x00, 0x00, // null - ], - vtype: RegType::REG_EXPAND_SZ, - }; - RegKey::predef(HKEY_CURRENT_USER) - .open_subkey_with_flags("Environment", KEY_READ | KEY_WRITE) - .unwrap() - .set_raw_value("PATH", ®_value) - .unwrap(); - - // compute expected path after installation - let expected = RegValue { - bytes: OsString::from(config.cargodir.join("bin")) - .encode_wide() - .flat_map(|v| vec![v as u8, (v >> 8) as u8]) - .chain(vec![b';', 0]) - .chain(reg_value.bytes.iter().copied()) - .collect(), - vtype: RegType::REG_EXPAND_SZ, - }; - - config.expect_ok(&INIT_NONE); - assert_eq!(get_path().unwrap().unwrap(), expected); - - config.expect_ok(&["rustup", "self", "uninstall", "-y"]); - assert_eq!(get_path().unwrap().unwrap(), reg_value); - }) - }); + let mut cx = CliTestContext::new(Scenario::Empty).await; + let _guard = RegistryGuard::new(&USER_PATH).unwrap(); + // Set up a non unicode PATH + let reg_value = RegValue { + bytes: vec![ + 0x00, 0xD8, // leading surrogate + 0x01, 0x01, // bogus trailing surrogate + 0x00, 0x00, // null + ], + vtype: RegType::REG_EXPAND_SZ, + }; + RegKey::predef(HKEY_CURRENT_USER) + .open_subkey_with_flags("Environment", KEY_READ | KEY_WRITE) + .unwrap() + .set_raw_value("PATH", ®_value) + .unwrap(); + + // compute expected path after installation + let expected = RegValue { + bytes: OsString::from(cx.config.cargodir.join("bin")) + .encode_wide() + .flat_map(|v| vec![v as u8, (v >> 8) as u8]) + .chain(vec![b';', 0]) + .chain(reg_value.bytes.iter().copied()) + .collect(), + vtype: RegType::REG_EXPAND_SZ, + }; + + cx.config.expect_ok(&INIT_NONE).await; + assert_eq!(get_path().unwrap().unwrap(), expected); + + cx.config + .expect_ok(&["rustup", "self", "uninstall", "-y"]) + .await; + assert_eq!(get_path().unwrap().unwrap(), reg_value); } } diff --git a/tests/suite/cli_rustup.rs b/tests/suite/cli_rustup.rs index bcc2ee1d4c..6e82ede94c 100644 --- a/tests/suite/cli_rustup.rs +++ b/tests/suite/cli_rustup.rs @@ -5,41 +5,44 @@ use std::path::{PathBuf, MAIN_SEPARATOR}; use std::{env::consts::EXE_SUFFIX, path::Path}; use rustup::for_host; -use rustup::test::this_host_triple; -use rustup::utils::raw; - -use rustup::test::mock::{ - self, - clitools::{self, Config, Scenario}, +use rustup::test::{ + mock::{ + self, + clitools::{self, CliTestContext, Scenario}, + }, + this_host_triple, }; +use rustup::utils::raw; macro_rules! for_host_and_home { - ($config:ident, $s: expr) => { + ($config:expr, $s: expr) => { &format!($s, this_host_triple(), $config.rustupdir) }; } -fn test(f: &dyn Fn(&mut Config)) { - clitools::test(Scenario::None, f); -} +#[tokio::test] +async fn rustup_stable() { + let mut cx = CliTestContext::new(Scenario::None).await; + + { + let mut cx = cx.with_dist_dir(Scenario::ArchivesV2_2015_01_01); + cx.config + .expect_ok(&["rustup", "toolchain", "add", "stable"]) + .await; + } -#[test] -fn rustup_stable() { - test(&|config| { - config.with_scenario(Scenario::ArchivesV2_2015_01_01, &|config| { - config.expect_ok(&["rustup", "toolchain", "add", "stable"]); - }); - config.with_scenario(Scenario::SimpleV2, &|config| { - config.expect_ok_ex( - &["rustup", "update"], - for_host!( - r" + let mut cx = cx.with_dist_dir(Scenario::SimpleV2); + cx.config + .expect_ok_ex( + &["rustup", "update"], + for_host!( + r" stable-{0} updated - 1.1.0 (hash-stable-1.1.0) (from 1.0.0 (hash-stable-1.0.0)) " - ), - for_host!( - r"info: syncing channel updates for 'stable-{0}' + ), + for_host!( + r"info: syncing channel updates for 'stable-{0}' info: latest update on 2015-01-02, rust version 1.1.0 (hash-stable-1.1.0) info: downloading component 'cargo' info: downloading component 'rust-docs' @@ -55,29 +58,34 @@ info: installing component 'rust-std' info: installing component 'rustc' info: cleaning up downloads & tmp directories " - ), - ); - }) - }); -} - -#[test] -fn rustup_stable_quiet() { - test(&|config| { - config.with_scenario(Scenario::ArchivesV2_2015_01_01, &|config| { - config.expect_ok(&["rustup", "--quiet", "update", "stable"]); - }); - config.with_scenario(Scenario::SimpleV2, &|config| { - config.expect_ok_ex( - &["rustup", "--quiet", "update"], - for_host!( - r" + ), + ) + .await; +} + +#[tokio::test] +async fn rustup_stable_quiet() { + let mut cx = CliTestContext::new(Scenario::None).await; + + { + let mut cx = cx.with_dist_dir(Scenario::ArchivesV2_2015_01_01); + cx.config + .expect_ok(&["rustup", "--quiet", "update", "stable"]) + .await; + } + + let mut cx = cx.with_dist_dir(Scenario::SimpleV2); + cx.config + .expect_ok_ex( + &["rustup", "--quiet", "update"], + for_host!( + r" stable-{0} updated - 1.1.0 (hash-stable-1.1.0) (from 1.0.0 (hash-stable-1.0.0)) " - ), - for_host!( - r"info: syncing channel updates for 'stable-{0}' + ), + for_host!( + r"info: syncing channel updates for 'stable-{0}' info: latest update on 2015-01-02, rust version 1.1.0 (hash-stable-1.1.0) info: downloading component 'cargo' info: downloading component 'rust-docs' @@ -93,54 +101,58 @@ info: installing component 'rust-std' info: installing component 'rustc' info: cleaning up downloads & tmp directories " - ), - ); - }) - }); -} - -#[test] -fn rustup_stable_no_change() { - test(&|config| { - config.with_scenario(Scenario::ArchivesV2_2015_01_01, &|config| { - config.expect_ok(&["rustup", "update", "stable"]); - config.expect_ok_ex( - &["rustup", "update"], - for_host!( - r" + ), + ) + .await; +} + +#[tokio::test] +async fn rustup_stable_no_change() { + let mut cx = CliTestContext::new(Scenario::ArchivesV2_2015_01_01).await; + cx.config.expect_ok(&["rustup", "update", "stable"]).await; + cx.config + .expect_ok_ex( + &["rustup", "update"], + for_host!( + r" stable-{0} unchanged - 1.0.0 (hash-stable-1.0.0) " - ), - for_host!( - r"info: syncing channel updates for 'stable-{0}' + ), + for_host!( + r"info: syncing channel updates for 'stable-{0}' info: cleaning up downloads & tmp directories " - ), - ); - }) - }); -} - -#[test] -fn rustup_all_channels() { - test(&|config| { - config.with_scenario(Scenario::ArchivesV2_2015_01_01, &|config| { - config.expect_ok(&["rustup", "toolchain", "add", "stable", "beta", "nightly"]); - }); - config.with_scenario(Scenario::SimpleV2, &|config| { - config.expect_ok_ex( - &["rustup", "update"], - for_host!( - r" + ), + ) + .await; +} + +#[tokio::test] +async fn rustup_all_channels() { + let mut cx = CliTestContext::new(Scenario::None).await; + + { + let mut cx = cx.with_dist_dir(Scenario::ArchivesV2_2015_01_01); + cx.config + .expect_ok(&["rustup", "toolchain", "add", "stable", "beta", "nightly"]) + .await; + } + + let mut cx = cx.with_dist_dir(Scenario::SimpleV2); + cx.config + .expect_ok_ex( + &["rustup", "update"], + for_host!( + r" stable-{0} updated - 1.1.0 (hash-stable-1.1.0) (from 1.0.0 (hash-stable-1.0.0)) beta-{0} updated - 1.2.0 (hash-beta-1.2.0) (from 1.1.0 (hash-beta-1.1.0)) nightly-{0} updated - 1.3.0 (hash-nightly-2) (from 1.2.0 (hash-nightly-1)) " - ), - for_host!( - r"info: syncing channel updates for 'stable-{0}' + ), + for_host!( + r"info: syncing channel updates for 'stable-{0}' info: latest update on 2015-01-02, rust version 1.1.0 (hash-stable-1.1.0) info: downloading component 'cargo' info: downloading component 'rust-docs' @@ -184,32 +196,37 @@ info: installing component 'rust-std' info: installing component 'rustc' info: cleaning up downloads & tmp directories " - ), - ); - }) - }) -} - -#[test] -fn rustup_some_channels_up_to_date() { - test(&|config| { - config.with_scenario(Scenario::ArchivesV2_2015_01_01, &|config| { - config.expect_ok(&["rustup", "toolchain", "add", "stable", "beta", "nightly"]); - }); - config.with_scenario(Scenario::SimpleV2, &|config| { - config.expect_ok(&["rustup", "update", "beta"]); - config.expect_ok_ex( - &["rustup", "update"], - for_host!( - r" + ), + ) + .await; +} + +#[tokio::test] +async fn rustup_some_channels_up_to_date() { + let mut cx = CliTestContext::new(Scenario::None).await; + + { + let mut cx = cx.with_dist_dir(Scenario::ArchivesV2_2015_01_01); + cx.config + .expect_ok(&["rustup", "toolchain", "add", "stable", "beta", "nightly"]) + .await; + } + + let mut cx = cx.with_dist_dir(Scenario::SimpleV2); + cx.config.expect_ok(&["rustup", "update", "beta"]).await; + cx.config + .expect_ok_ex( + &["rustup", "update"], + for_host!( + r" stable-{0} updated - 1.1.0 (hash-stable-1.1.0) (from 1.0.0 (hash-stable-1.0.0)) beta-{0} unchanged - 1.2.0 (hash-beta-1.2.0) nightly-{0} updated - 1.3.0 (hash-nightly-2) (from 1.2.0 (hash-nightly-1)) " - ), - for_host!( - r"info: syncing channel updates for 'stable-{0}' + ), + for_host!( + r"info: syncing channel updates for 'stable-{0}' info: latest update on 2015-01-02, rust version 1.1.0 (hash-stable-1.1.0) info: downloading component 'cargo' info: downloading component 'rust-docs' @@ -240,41 +257,39 @@ info: installing component 'rust-std' info: installing component 'rustc' info: cleaning up downloads & tmp directories " - ), - ); - }) - }) -} - -#[test] -fn rustup_no_channels() { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - config.expect_ok_ex( - &["rustup", "update"], - r"", - r"info: no updatable toolchains installed + ), + ) + .await; +} + +#[tokio::test] +async fn rustup_no_channels() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config + .expect_ok_ex( + &["rustup", "update"], + r"", + r"info: no updatable toolchains installed info: cleaning up downloads & tmp directories ", - ); - }) - }) -} - -#[test] -fn default() { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - config.expect_ok_ex( - &["rustup", "default", "nightly"], - for_host!( - r" + ) + .await; +} + +#[tokio::test] +async fn default() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config + .expect_ok_ex( + &["rustup", "default", "nightly"], + for_host!( + r" nightly-{0} installed - 1.3.0 (hash-nightly-2) " - ), - for_host!( - r"info: syncing channel updates for 'nightly-{0}' + ), + for_host!( + r"info: syncing channel updates for 'nightly-{0}' info: latest update on 2015-01-02, rust version 1.3.0 (hash-nightly-2) info: downloading component 'cargo' info: downloading component 'rust-docs' @@ -286,265 +301,280 @@ info: installing component 'rust-std' info: installing component 'rustc' info: default toolchain set to 'nightly-{0}' " - ), - ); - }) - }); -} - -#[test] -fn default_override() { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - config.expect_ok(&["rustup", "toolchain", "add", "nightly"]); - config.expect_ok(&["rustup", "default", "stable"]); - config.expect_ok(&["rustup", "override", "set", "nightly"]); - config.expect_stderr_ok( - &["rustup", "default", "stable"], - for_host!( - r"info: using existing install for 'stable-{0}' + ), + ) + .await; +} + +#[tokio::test] +async fn default_override() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config + .expect_ok(&["rustup", "toolchain", "add", "nightly"]) + .await; + cx.config.expect_ok(&["rustup", "default", "stable"]).await; + cx.config + .expect_ok(&["rustup", "override", "set", "nightly"]) + .await; + cx.config + .expect_stderr_ok( + &["rustup", "default", "stable"], + for_host!( + r"info: using existing install for 'stable-{0}' info: default toolchain set to 'stable-{0}' info: note that the toolchain 'nightly-{0}' is currently in use (directory override for" - ), - ); - }) - }); -} - -#[test] -fn rustup_zstd() { - test(&|config| { - config.with_scenario(Scenario::ArchivesV2_2015_01_01, &|config| { - config.expect_stderr_ok( - &["rustup", "--verbose", "toolchain", "add", "nightly"], - for_host!(r"dist/2015-01-01/rust-std-nightly-{0}.tar.zst"), - ); - }) - }); -} - -#[test] -fn add_target() { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - let path = format!( - "toolchains/nightly-{}/lib/rustlib/{}/lib/libstd.rlib", - &this_host_triple(), - clitools::CROSS_ARCH1 - ); - config.expect_ok(&["rustup", "default", "nightly"]); - config.expect_ok(&["rustup", "target", "add", clitools::CROSS_ARCH1]); - assert!(config.rustupdir.has(path)); - }) - }); -} - -#[test] -fn remove_target() { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - let path = format!( - "toolchains/nightly-{}/lib/rustlib/{}/lib/libstd.rlib", - &this_host_triple(), - clitools::CROSS_ARCH1 - ); - config.expect_ok(&["rustup", "default", "nightly"]); - config.expect_ok(&["rustup", "target", "add", clitools::CROSS_ARCH1]); - assert!(config.rustupdir.has(&path)); - config.expect_ok(&["rustup", "target", "remove", clitools::CROSS_ARCH1]); - assert!(!config.rustupdir.has(&path)); - }) - }); -} - -#[test] -fn add_remove_multiple_targets() { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - config.expect_ok(&["rustup", "default", "nightly"]); - config.expect_ok(&[ - "rustup", - "target", - "add", - clitools::CROSS_ARCH1, - clitools::CROSS_ARCH2, - ]); - let path = format!( - "toolchains/nightly-{}/lib/rustlib/{}/lib/libstd.rlib", - &this_host_triple(), - clitools::CROSS_ARCH1 - ); - assert!(config.rustupdir.has(path)); - let path = format!( - "toolchains/nightly-{}/lib/rustlib/{}/lib/libstd.rlib", - &this_host_triple(), - clitools::CROSS_ARCH2 - ); - assert!(config.rustupdir.has(path)); - - config.expect_ok(&[ - "rustup", - "target", - "remove", - clitools::CROSS_ARCH1, - clitools::CROSS_ARCH2, - ]); - let path = format!( - "toolchains/nightly-{}/lib/rustlib/{}/lib/libstd.rlib", - &this_host_triple(), - clitools::CROSS_ARCH1 - ); - assert!(!config.rustupdir.has(path)); - let path = format!( - "toolchains/nightly-{}/lib/rustlib/{}/lib/libstd.rlib", - &this_host_triple(), - clitools::CROSS_ARCH2 - ); - assert!(!config.rustupdir.has(path)); - }) - }); -} - -#[test] -fn list_targets() { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - config.expect_ok(&["rustup", "default", "nightly"]); - config.expect_stdout_ok(&["rustup", "target", "list"], clitools::CROSS_ARCH1); - }) - }); -} - -#[test] -fn list_installed_targets() { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - let trip = this_host_triple(); - - config.expect_ok(&["rustup", "default", "nightly"]); - config.expect_stdout_ok(&["rustup", "target", "list", "--installed"], &trip); - }) - }); -} - -#[test] -fn add_target_explicit() { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - let path = format!( - "toolchains/nightly-{}/lib/rustlib/{}/lib/libstd.rlib", - &this_host_triple(), - clitools::CROSS_ARCH1 - ); - config.expect_ok(&["rustup", "toolchain", "add", "nightly"]); - config.expect_ok(&[ - "rustup", - "target", - "add", - "--toolchain", - "nightly", - clitools::CROSS_ARCH1, - ]); - assert!(config.rustupdir.has(path)); - }) - }); -} - -#[test] -fn remove_target_explicit() { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - let path = format!( - "toolchains/nightly-{}/lib/rustlib/{}/lib/libstd.rlib", - &this_host_triple(), - clitools::CROSS_ARCH1 - ); - config.expect_ok(&["rustup", "toolchain", "add", "nightly"]); - config.expect_ok(&[ - "rustup", - "target", - "add", - "--toolchain", - "nightly", - clitools::CROSS_ARCH1, - ]); - assert!(config.rustupdir.has(&path)); - config.expect_ok(&[ - "rustup", - "target", - "remove", - "--toolchain", - "nightly", - clitools::CROSS_ARCH1, - ]); - assert!(!config.rustupdir.has(&path)); - }) - }); -} - -#[test] -fn list_targets_explicit() { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - config.expect_ok(&["rustup", "toolchain", "add", "nightly"]); - config.expect_stdout_ok( - &["rustup", "target", "list", "--toolchain", "nightly"], - clitools::CROSS_ARCH1, - ); - }) - }); -} - -#[test] -fn link() { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - let path = config.customdir.join("custom-1"); - let path = path.to_string_lossy(); - config.expect_ok(&["rustup", "toolchain", "link", "custom", &path]); - config.expect_ok(&["rustup", "default", "custom"]); - config.expect_stdout_ok(&["rustc", "--version"], "hash-c-1"); - config.expect_stdout_ok(&["rustup", "show"], "custom (active, default)"); - config.expect_ok(&["rustup", "update", "nightly"]); - config.expect_ok(&["rustup", "default", "nightly"]); - config.expect_stdout_ok(&["rustup", "show"], "custom"); - }) - }); + ), + ) + .await; +} + +#[tokio::test] +async fn rustup_zstd() { + let cx = CliTestContext::new(Scenario::ArchivesV2_2015_01_01).await; + cx.config + .expect_stderr_ok( + &["rustup", "--verbose", "toolchain", "add", "nightly"], + for_host!(r"dist/2015-01-01/rust-std-nightly-{0}.tar.zst"), + ) + .await; +} + +#[tokio::test] +async fn add_target() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + let path = format!( + "toolchains/nightly-{}/lib/rustlib/{}/lib/libstd.rlib", + &this_host_triple(), + clitools::CROSS_ARCH1 + ); + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + cx.config + .expect_ok(&["rustup", "target", "add", clitools::CROSS_ARCH1]) + .await; + assert!(cx.config.rustupdir.has(path)); +} + +#[tokio::test] +async fn remove_target() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + let path = format!( + "toolchains/nightly-{}/lib/rustlib/{}/lib/libstd.rlib", + &this_host_triple(), + clitools::CROSS_ARCH1 + ); + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + cx.config + .expect_ok(&["rustup", "target", "add", clitools::CROSS_ARCH1]) + .await; + assert!(cx.config.rustupdir.has(&path)); + cx.config + .expect_ok(&["rustup", "target", "remove", clitools::CROSS_ARCH1]) + .await; + assert!(!cx.config.rustupdir.has(&path)); +} + +#[tokio::test] +async fn add_remove_multiple_targets() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + cx.config + .expect_ok(&[ + "rustup", + "target", + "add", + clitools::CROSS_ARCH1, + clitools::CROSS_ARCH2, + ]) + .await; + let path = format!( + "toolchains/nightly-{}/lib/rustlib/{}/lib/libstd.rlib", + &this_host_triple(), + clitools::CROSS_ARCH1 + ); + assert!(cx.config.rustupdir.has(path)); + let path = format!( + "toolchains/nightly-{}/lib/rustlib/{}/lib/libstd.rlib", + &this_host_triple(), + clitools::CROSS_ARCH2 + ); + assert!(cx.config.rustupdir.has(path)); + + cx.config + .expect_ok(&[ + "rustup", + "target", + "remove", + clitools::CROSS_ARCH1, + clitools::CROSS_ARCH2, + ]) + .await; + let path = format!( + "toolchains/nightly-{}/lib/rustlib/{}/lib/libstd.rlib", + &this_host_triple(), + clitools::CROSS_ARCH1 + ); + assert!(!cx.config.rustupdir.has(path)); + let path = format!( + "toolchains/nightly-{}/lib/rustlib/{}/lib/libstd.rlib", + &this_host_triple(), + clitools::CROSS_ARCH2 + ); + assert!(!cx.config.rustupdir.has(path)); +} + +#[tokio::test] +async fn list_targets() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + cx.config + .expect_stdout_ok(&["rustup", "target", "list"], clitools::CROSS_ARCH1) + .await; +} + +#[tokio::test] +async fn list_installed_targets() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + let trip = this_host_triple(); + + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + cx.config + .expect_stdout_ok(&["rustup", "target", "list", "--installed"], &trip) + .await; +} + +#[tokio::test] +async fn add_target_explicit() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + let path = format!( + "toolchains/nightly-{}/lib/rustlib/{}/lib/libstd.rlib", + &this_host_triple(), + clitools::CROSS_ARCH1 + ); + cx.config + .expect_ok(&["rustup", "toolchain", "add", "nightly"]) + .await; + cx.config + .expect_ok(&[ + "rustup", + "target", + "add", + "--toolchain", + "nightly", + clitools::CROSS_ARCH1, + ]) + .await; + assert!(cx.config.rustupdir.has(path)); +} + +#[tokio::test] +async fn remove_target_explicit() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + let path = format!( + "toolchains/nightly-{}/lib/rustlib/{}/lib/libstd.rlib", + &this_host_triple(), + clitools::CROSS_ARCH1 + ); + cx.config + .expect_ok(&["rustup", "toolchain", "add", "nightly"]) + .await; + cx.config + .expect_ok(&[ + "rustup", + "target", + "add", + "--toolchain", + "nightly", + clitools::CROSS_ARCH1, + ]) + .await; + assert!(cx.config.rustupdir.has(&path)); + cx.config + .expect_ok(&[ + "rustup", + "target", + "remove", + "--toolchain", + "nightly", + clitools::CROSS_ARCH1, + ]) + .await; + assert!(!cx.config.rustupdir.has(&path)); +} + +#[tokio::test] +async fn list_targets_explicit() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config + .expect_ok(&["rustup", "toolchain", "add", "nightly"]) + .await; + cx.config + .expect_stdout_ok( + &["rustup", "target", "list", "--toolchain", "nightly"], + clitools::CROSS_ARCH1, + ) + .await; +} + +#[tokio::test] +async fn link() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + let path = cx.config.customdir.join("custom-1"); + let path = path.to_string_lossy(); + cx.config + .expect_ok(&["rustup", "toolchain", "link", "custom", &path]) + .await; + cx.config.expect_ok(&["rustup", "default", "custom"]).await; + cx.config + .expect_stdout_ok(&["rustc", "--version"], "hash-c-1") + .await; + cx.config + .expect_stdout_ok(&["rustup", "show"], "custom (active, default)") + .await; + cx.config.expect_ok(&["rustup", "update", "nightly"]).await; + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + cx.config + .expect_stdout_ok(&["rustup", "show"], "custom") + .await; } // Issue #809. When we call the fallback cargo, when it in turn invokes // "rustc", that rustc should actually be the rustup proxy, not the toolchain rustc. // That way the proxy can pick the correct toolchain. -#[test] -fn fallback_cargo_calls_correct_rustc() { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - // Hm, this is the _only_ test that assumes that toolchain proxies - // exist in CARGO_HOME. Adding that proxy here. - let rustup_path = config.exedir.join(format!("rustup{EXE_SUFFIX}")); - let cargo_bin_path = config.cargodir.join("bin"); - fs::create_dir_all(&cargo_bin_path).unwrap(); - let rustc_path = cargo_bin_path.join(format!("rustc{EXE_SUFFIX}")); - fs::hard_link(rustup_path, &rustc_path).unwrap(); - - // Install a custom toolchain and a nightly toolchain for the cargo fallback - let path = config.customdir.join("custom-1"); - let path = path.to_string_lossy(); - config.expect_ok(&["rustup", "toolchain", "link", "custom", &path]); - config.expect_ok(&["rustup", "default", "custom"]); - config.expect_ok(&["rustup", "update", "nightly"]); - config.expect_stdout_ok(&["rustc", "--version"], "hash-c-1"); - config.expect_stdout_ok(&["cargo", "--version"], "hash-nightly-2"); - - assert!(rustc_path.exists()); - - // Here --call-rustc tells the mock cargo bin to exec `rustc --version`. - // We should be ultimately calling the custom rustc, according to the - // RUSTUP_TOOLCHAIN variable set by the original "cargo" proxy, and - // interpreted by the nested "rustc" proxy. - config.expect_stdout_ok(&["cargo", "--call-rustc"], "hash-c-1"); - }) - }); +#[tokio::test] +async fn fallback_cargo_calls_correct_rustc() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + // Hm, this is the _only_ test that assumes that toolchain proxies + // exist in CARGO_HOME. Adding that proxy here. + let rustup_path = cx.config.exedir.join(format!("rustup{EXE_SUFFIX}")); + let cargo_bin_path = cx.config.cargodir.join("bin"); + fs::create_dir_all(&cargo_bin_path).unwrap(); + let rustc_path = cargo_bin_path.join(format!("rustc{EXE_SUFFIX}")); + fs::hard_link(rustup_path, &rustc_path).unwrap(); + + // Install a custom toolchain and a nightly toolchain for the cargo fallback + let path = cx.config.customdir.join("custom-1"); + let path = path.to_string_lossy(); + cx.config + .expect_ok(&["rustup", "toolchain", "link", "custom", &path]) + .await; + cx.config.expect_ok(&["rustup", "default", "custom"]).await; + cx.config.expect_ok(&["rustup", "update", "nightly"]).await; + cx.config + .expect_stdout_ok(&["rustc", "--version"], "hash-c-1") + .await; + cx.config + .expect_stdout_ok(&["cargo", "--version"], "hash-nightly-2") + .await; + + assert!(rustc_path.exists()); + + // Here --call-rustc tells the mock cargo bin to exec `rustc --version`. + // We should be ultimately calling the custom rustc, according to the + // RUSTUP_TOOLCHAIN variable set by the original "cargo" proxy, and + // interpreted by the nested "rustc" proxy. + cx.config + .expect_stdout_ok(&["cargo", "--call-rustc"], "hash-c-1") + .await; } // Checks that cargo can recursively invoke itself with rustup shorthand (via @@ -565,53 +595,54 @@ fn fallback_cargo_calls_correct_rustc() { // If the toolchain `bin` directory is in PATH, then this test would fail in // step 5 because the `cargo` executable would be the "mock" nightly cargo, // and the first argument would be `+nightly` which would be an error. -#[test] -fn recursive_cargo() { - test(&|config| { - config.with_scenario(Scenario::ArchivesV2, &|config| { - config.expect_ok(&["rustup", "default", "nightly"]); - - // We need an intermediary to run cargo itself. - // The "mock" cargo can't do that because on Windows it will check - // for a `cargo.exe` in the current directory before checking PATH. - // - // The solution here is to copy from the "mock" `cargo.exe` into - // `~/.cargo/bin/cargo-foo`. This is just for convenience to avoid - // needing to build another executable just for this test. - let output = config.run("rustup", ["which", "cargo"], &[]); - let real_mock_cargo = output.stdout.trim(); - let cargo_bin_path = config.cargodir.join("bin"); - let cargo_subcommand = cargo_bin_path.join(format!("cargo-foo{}", EXE_SUFFIX)); - fs::create_dir_all(&cargo_bin_path).unwrap(); - fs::copy(real_mock_cargo, cargo_subcommand).unwrap(); - - config.expect_stdout_ok(&["cargo", "--recursive-cargo-subcommand"], "hash-nightly-2"); - }); - }); -} - -#[test] -fn show_home() { - test(&|config| { - config.expect_ok_ex( +#[tokio::test] +async fn recursive_cargo() { + let mut cx = CliTestContext::new(Scenario::ArchivesV2).await; + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + + // We need an intermediary to run cargo itself. + // The "mock" cargo can't do that because on Windows it will check + // for a `cargo.exe` in the current directory before checking PATH. + // + // The solution here is to copy from the "mock" `cargo.exe` into + // `~/.cargo/bin/cargo-foo`. This is just for convenience to avoid + // needing to build another executable just for this test. + let output = cx.config.run("rustup", ["which", "cargo"], &[]).await; + let real_mock_cargo = output.stdout.trim(); + let cargo_bin_path = cx.config.cargodir.join("bin"); + let cargo_subcommand = cargo_bin_path.join(format!("cargo-foo{}", EXE_SUFFIX)); + fs::create_dir_all(&cargo_bin_path).unwrap(); + fs::copy(real_mock_cargo, cargo_subcommand).unwrap(); + + cx.config + .expect_stdout_ok(&["cargo", "--recursive-cargo-subcommand"], "hash-nightly-2") + .await; +} + +#[tokio::test] +async fn show_home() { + let mut cx = CliTestContext::new(Scenario::None).await; + cx.config + .expect_ok_ex( &["rustup", "show", "home"], &format!( r"{} ", - config.rustupdir + cx.config.rustupdir ), r"", - ); - }); + ) + .await; } -#[test] -fn show_toolchain_none() { - test(&|config| { - config.expect_ok_ex( +#[tokio::test] +async fn show_toolchain_none() { + let mut cx = CliTestContext::new(Scenario::None).await; + cx.config + .expect_ok_ex( &["rustup", "show"], for_host_and_home!( - config, + &cx.config, r"Default host: {0} rustup home: {1} @@ -624,20 +655,20 @@ no active toolchain " ), r"", - ); - }); -} - -#[test] -fn show_toolchain_default() { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - config.expect_ok(&["rustup", "default", "nightly"]); - config.expect_ok_ex( - &["rustup", "show"], - for_host_and_home!( - config, - r"Default host: {0} + ) + .await; +} + +#[tokio::test] +async fn show_toolchain_default() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + cx.config + .expect_ok_ex( + &["rustup", "show"], + for_host_and_home!( + cx.config, + r"Default host: {0} rustup home: {1} installed toolchains @@ -652,68 +683,65 @@ active because: it's the default toolchain installed targets: {0} " - ), - r"", - ); - }) - }); -} - -#[test] -fn show_no_default() { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - config.expect_ok(&["rustup", "install", "nightly"]); - config.expect_ok(&["rustup", "default", "none"]); - config.expect_stdout_ok( - &["rustup", "show"], - for_host!( - "\ + ), + r"", + ) + .await; +} + +#[tokio::test] +async fn show_no_default() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "install", "nightly"]).await; + cx.config.expect_ok(&["rustup", "default", "none"]).await; + cx.config + .expect_stdout_ok( + &["rustup", "show"], + for_host!( + "\ installed toolchains -------------------- nightly-{0} active toolchain " - ), - ); - }) - }); -} - -#[test] -fn show_no_default_active() { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - config.expect_ok(&["rustup", "install", "nightly"]); - config.expect_ok(&["rustup", "default", "none"]); - config.expect_stdout_ok( - &["rustup", "+nightly", "show"], - for_host!( - "\ + ), + ) + .await; +} + +#[tokio::test] +async fn show_no_default_active() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "install", "nightly"]).await; + cx.config.expect_ok(&["rustup", "default", "none"]).await; + cx.config + .expect_stdout_ok( + &["rustup", "+nightly", "show"], + for_host!( + "\ installed toolchains -------------------- nightly-{0} (active) active toolchain " - ), - ); - }) - }); -} - -#[test] -fn show_multiple_toolchains() { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - config.expect_ok(&["rustup", "default", "nightly"]); - config.expect_ok(&["rustup", "update", "stable"]); - config.expect_ok_ex( - &["rustup", "show"], - for_host_and_home!( - config, - r"Default host: {0} + ), + ) + .await; +} + +#[tokio::test] +async fn show_multiple_toolchains() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + cx.config.expect_ok(&["rustup", "update", "stable"]).await; + cx.config + .expect_ok_ex( + &["rustup", "show"], + for_host_and_home!( + cx.config, + r"Default host: {0} rustup home: {1} installed toolchains @@ -729,27 +757,30 @@ active because: it's the default toolchain installed targets: {0} " - ), - r"", - ); - }) - }); -} - -#[test] -fn show_multiple_targets() { - test(&|config| { - config.with_scenario(Scenario::MultiHost, &|config| { - config.expect_ok(&[ - "rustup", - "default", - &format!("nightly-{}", clitools::MULTI_ARCH1), - ]); - config.expect_ok(&["rustup", "target", "add", clitools::CROSS_ARCH2]); - config.expect_ok_ex( - &["rustup", "show"], - &format!( - r"Default host: {2} + ), + r"", + ) + .await; +} + +#[tokio::test] +async fn show_multiple_targets() { + let mut cx = CliTestContext::new(Scenario::MultiHost).await; + cx.config + .expect_ok(&[ + "rustup", + "default", + &format!("nightly-{}", clitools::MULTI_ARCH1), + ]) + .await; + cx.config + .expect_ok(&["rustup", "target", "add", clitools::CROSS_ARCH2]) + .await; + cx.config + .expect_ok_ex( + &["rustup", "show"], + &format!( + r"Default host: {2} rustup home: {3} installed toolchains @@ -765,40 +796,45 @@ installed targets: {1} {0} ", - clitools::MULTI_ARCH1, - clitools::CROSS_ARCH2, - this_host_triple(), - config.rustupdir - ), - r"", - ); - }) - }); -} - -#[test] -fn show_multiple_toolchains_and_targets() { + clitools::MULTI_ARCH1, + clitools::CROSS_ARCH2, + this_host_triple(), + cx.config.rustupdir + ), + r"", + ) + .await; +} + +#[tokio::test] +async fn show_multiple_toolchains_and_targets() { if cfg!(target_os = "linux") && cfg!(target_arch = "x86") { return; } - test(&|config| { - config.with_scenario(Scenario::MultiHost, &|config| { - config.expect_ok(&[ - "rustup", - "default", - &format!("nightly-{}", clitools::MULTI_ARCH1), - ]); - config.expect_ok(&["rustup", "target", "add", clitools::CROSS_ARCH2]); - config.expect_ok(&[ - "rustup", - "update", - &format!("stable-{}", clitools::MULTI_ARCH1), - ]); - config.expect_ok_ex( - &["rustup", "show"], - &format!( - r"Default host: {2} + let mut cx = CliTestContext::new(Scenario::MultiHost).await; + cx.config + .expect_ok(&[ + "rustup", + "default", + &format!("nightly-{}", clitools::MULTI_ARCH1), + ]) + .await; + cx.config + .expect_ok(&["rustup", "target", "add", clitools::CROSS_ARCH2]) + .await; + cx.config + .expect_ok(&[ + "rustup", + "update", + &format!("stable-{}", clitools::MULTI_ARCH1), + ]) + .await; + cx.config + .expect_ok_ex( + &["rustup", "show"], + &format!( + r"Default host: {2} rustup home: {3} installed toolchains @@ -815,170 +851,174 @@ installed targets: {1} {0} ", - clitools::MULTI_ARCH1, - clitools::CROSS_ARCH2, - this_host_triple(), - config.rustupdir - ), - r"", - ); - }) - }); -} - -#[test] -fn list_default_toolchain() { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - config.expect_ok(&["rustup", "default", "nightly"]); - config.expect_ok_ex( - &["rustup", "toolchain", "list"], - for_host!("nightly-{0} (active, default)\n"), - r"", - ); - }) - }); -} - -#[test] -fn list_default_toolchain_quiet() { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - config.expect_ok(&["rustup", "default", "nightly"]); - config.expect_ok_ex( - &["rustup", "toolchain", "list", "--quiet"], - for_host!("nightly-{0}\n"), - r"", - ); - }) - }); -} - -#[test] -fn list_no_default_toolchain() { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - config.expect_ok(&["rustup", "install", "nightly"]); - config.expect_ok(&["rustup", "default", "none"]); - config.expect_ok_ex( - &["rustup", "toolchain", "list"], - for_host!("nightly-{0}\n"), - r"", - ); - }) - }); -} - -#[test] -fn list_no_default_override_toolchain() { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - config.expect_ok(&["rustup", "override", "set", "nightly"]); - config.expect_ok_ex( - &["rustup", "toolchain", "list"], - for_host!("nightly-{0} (active)\n"), - r"", - ); - }) - }); -} - -#[test] -fn list_default_and_override_toolchain() { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - config.expect_ok(&["rustup", "default", "nightly"]); - config.expect_ok(&["rustup", "override", "set", "nightly"]); - config.expect_ok_ex( - &["rustup", "toolchain", "list"], - for_host!("nightly-{0} (active, default)\n"), - r"", - ); - }) - }); -} - -#[test] -fn heal_damaged_toolchain() { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - config.expect_ok(&["rustup", "default", "nightly"]); - config.expect_not_stderr_ok(&["rustup", "which", "rustc"], "syncing channel updates"); - let manifest_path = format!( - "toolchains/nightly-{}/lib/rustlib/multirust-channel-manifest.toml", - this_host_triple() - ); - - let mut rustc_path = config.rustupdir.join( - [ - "toolchains", - &format!("nightly-{}", this_host_triple()), - "bin", - "rustc", - ] - .iter() - .collect::(), - ); - - if cfg!(windows) { - rustc_path.set_extension("exe"); - } - - fs::remove_file(config.rustupdir.join(manifest_path)).unwrap(); - config.expect_ok_ex( - &["rustup", "which", "rustc"], - &format!("{}\n", rustc_path.to_str().unwrap()), - for_host!("info: syncing channel updates for 'nightly-{0}'\n"), - ); - config.expect_ok(&["rustup", "default", "nightly"]); - config.expect_stderr_ok(&["rustup", "which", "rustc"], "syncing channel updates"); - }) - }); -} - -#[test] + clitools::MULTI_ARCH1, + clitools::CROSS_ARCH2, + this_host_triple(), + cx.config.rustupdir + ), + r"", + ) + .await; +} + +#[tokio::test] +async fn list_default_toolchain() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + cx.config + .expect_ok_ex( + &["rustup", "toolchain", "list"], + for_host!("nightly-{0} (active, default)\n"), + r"", + ) + .await; +} + +#[tokio::test] +async fn list_default_toolchain_quiet() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + cx.config + .expect_ok_ex( + &["rustup", "toolchain", "list", "--quiet"], + for_host!("nightly-{0}\n"), + r"", + ) + .await; +} + +#[tokio::test] +async fn list_no_default_toolchain() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "install", "nightly"]).await; + cx.config.expect_ok(&["rustup", "default", "none"]).await; + cx.config + .expect_ok_ex( + &["rustup", "toolchain", "list"], + for_host!("nightly-{0}\n"), + r"", + ) + .await; +} + +#[tokio::test] +async fn list_no_default_override_toolchain() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config + .expect_ok(&["rustup", "override", "set", "nightly"]) + .await; + cx.config + .expect_ok_ex( + &["rustup", "toolchain", "list"], + for_host!("nightly-{0} (active)\n"), + r"", + ) + .await; +} + +#[tokio::test] +async fn list_default_and_override_toolchain() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + cx.config + .expect_ok(&["rustup", "override", "set", "nightly"]) + .await; + cx.config + .expect_ok_ex( + &["rustup", "toolchain", "list"], + for_host!("nightly-{0} (active, default)\n"), + r"", + ) + .await; +} + +#[tokio::test] +async fn heal_damaged_toolchain() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + cx.config + .expect_not_stderr_ok(&["rustup", "which", "rustc"], "syncing channel updates") + .await; + let manifest_path = format!( + "toolchains/nightly-{}/lib/rustlib/multirust-channel-manifest.toml", + this_host_triple() + ); + + let mut rustc_path = cx.config.rustupdir.join( + [ + "toolchains", + &format!("nightly-{}", this_host_triple()), + "bin", + "rustc", + ] + .iter() + .collect::(), + ); + + if cfg!(windows) { + rustc_path.set_extension("exe"); + } + + fs::remove_file(cx.config.rustupdir.join(manifest_path)).unwrap(); + cx.config + .expect_ok_ex( + &["rustup", "which", "rustc"], + &format!("{}\n", rustc_path.to_str().unwrap()), + for_host!("info: syncing channel updates for 'nightly-{0}'\n"), + ) + .await; + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + cx.config + .expect_stderr_ok(&["rustup", "which", "rustc"], "syncing channel updates") + .await; +} + +#[tokio::test] #[ignore = "FIXME: Windows shows UNC paths"] -fn show_toolchain_override() { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - let cwd = config.current_dir(); - config.expect_ok(&["rustup", "override", "add", "nightly"]); - config.expect_ok_ex( - &["rustup", "show"], - &format!( - r"Default host: {0} +async fn show_toolchain_override() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + let cwd = cx.config.current_dir(); + cx.config + .expect_ok(&["rustup", "override", "add", "nightly"]) + .await; + cx.config + .expect_ok_ex( + &["rustup", "show"], + &format!( + r"Default host: {0} rustup home: {1} nightly-{0} (directory override for '{2}') 1.3.0 (hash-nightly-2) ", - this_host_triple(), - config.rustupdir, - cwd.display(), - ), - r"", - ); - }) - }); + this_host_triple(), + cx.config.rustupdir, + cwd.display(), + ), + r"", + ) + .await; } -#[test] +#[tokio::test] #[ignore = "FIXME: Windows shows UNC paths"] -fn show_toolchain_toolchain_file_override() { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - config.expect_ok(&["rustup", "default", "stable"]); - config.expect_ok(&["rustup", "toolchain", "install", "nightly"]); +async fn show_toolchain_toolchain_file_override() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "default", "stable"]).await; + cx.config + .expect_ok(&["rustup", "toolchain", "install", "nightly"]) + .await; - let cwd = config.current_dir(); - let toolchain_file = cwd.join("rust-toolchain"); + let cwd = cx.config.current_dir(); + let toolchain_file = cwd.join("rust-toolchain"); - raw::write_file(&toolchain_file, "nightly").unwrap(); + raw::write_file(&toolchain_file, "nightly").unwrap(); - config.expect_ok_ex( - &["rustup", "show"], - &format!( - r"Default host: {0} + cx.config + .expect_ok_ex( + &["rustup", "show"], + &format!( + r"Default host: {0} rustup home: {1} installed toolchains @@ -994,38 +1034,41 @@ nightly-{0} (overridden by '{2}') 1.3.0 (hash-nightly-2) ", - this_host_triple(), - config.rustupdir, - toolchain_file.display() - ), - r"", - ); - }) - }); + this_host_triple(), + cx.config.rustupdir, + toolchain_file.display() + ), + r"", + ) + .await; } -#[test] +#[tokio::test] #[ignore = "FIXME: Windows shows UNC paths"] -fn show_toolchain_version_nested_file_override() { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - config.expect_ok(&["rustup", "toolchain", "add", "stable", "beta", "nightly"]); - config.expect_ok(&["rustup", "default", "stable"]); - config.expect_ok(&["rustup", "toolchain", "install", "nightly"]); - - let cwd = config.current_dir(); - let toolchain_file = cwd.join("rust-toolchain"); - - raw::write_file(&toolchain_file, "nightly").unwrap(); - - let subdir = cwd.join("foo"); - - fs::create_dir_all(&subdir).unwrap(); - config.change_dir(&subdir, &|config| { - config.expect_ok_ex( - &["rustup", "show"], - &format!( - r"Default host: {0} +async fn show_toolchain_version_nested_file_override() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config + .expect_ok(&["rustup", "toolchain", "add", "stable", "beta", "nightly"]) + .await; + cx.config.expect_ok(&["rustup", "default", "stable"]).await; + cx.config + .expect_ok(&["rustup", "toolchain", "install", "nightly"]) + .await; + + let cwd = cx.config.current_dir(); + let toolchain_file = cwd.join("rust-toolchain"); + + raw::write_file(&toolchain_file, "nightly").unwrap(); + + let subdir = cwd.join("foo"); + + fs::create_dir_all(&subdir).unwrap(); + let mut cx = cx.change_dir(&subdir); + cx.config + .expect_ok_ex( + &["rustup", "show"], + &format!( + r"Default host: {0} installed toolchains -------------------- @@ -1040,100 +1083,108 @@ nightly-{0} (overridden by '{1}') 1.3.0 (hash-nightly-2) ", - this_host_triple(), - toolchain_file.display() - ), - r"", - ); - }); - }) - }); + this_host_triple(), + toolchain_file.display() + ), + r"", + ) + .await; } -#[test] +#[tokio::test] #[ignore = "FIXME: Windows shows UNC paths"] -fn show_toolchain_toolchain_file_override_not_installed() { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - config.expect_ok(&["rustup", "default", "stable"]); - - let cwd = config.current_dir(); - let toolchain_file = cwd.join("rust-toolchain"); - - raw::write_file(&toolchain_file, "nightly").unwrap(); - - // I'm not sure this should really be erroring when the toolchain - // is not installed; just capturing the behavior. - let mut cmd = clitools::cmd(config, "rustup", ["show"]); - clitools::env(config, &mut cmd); - let out = cmd.output().unwrap(); - assert!(!out.status.success()); - let stderr = String::from_utf8(out.stderr).unwrap(); - assert!(stderr.starts_with("error: override toolchain 'nightly' is not installed")); - assert!(stderr.contains(&format!( - "the toolchain file at '{}' specifies an uninstalled toolchain", - toolchain_file.display() - ))); - }) - }); -} - -#[test] -fn show_toolchain_override_not_installed() { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - config.expect_ok(&["rustup", "override", "add", "nightly"]); - config.expect_ok(&["rustup", "toolchain", "remove", "nightly"]); - let out = config.run("rustup", ["show"], &[]); - assert!(!out.ok); - assert!(out - .stderr - .contains("is not installed: the directory override for")); - assert!(!out.stderr.contains("info: installing component 'rustc'")); - }) - }); -} - -#[test] -fn override_set_unset_with_path() { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - let cwd = fs::canonicalize(config.current_dir()).unwrap(); - let mut cwd_str = cwd.to_str().unwrap(); - - if cfg!(windows) { - cwd_str = &cwd_str[4..]; - } - - let emptydir = tempfile::tempdir().unwrap(); - config.change_dir(emptydir.path(), &|config| { - config.expect_ok(&["rustup", "override", "set", "nightly", "--path", cwd_str]); - }); - config.expect_ok_ex( - &["rustup", "override", "list"], - &format!("{}\tnightly-{}\n", cwd_str, this_host_triple()), - r"", - ); - config.change_dir(emptydir.path(), &|config| { - config.expect_ok(&["rustup", "override", "unset", "--path", cwd_str]); - }); - config.expect_ok_ex(&["rustup", "override", "list"], "no overrides\n", r""); - }) - }); -} - -#[test] -fn show_toolchain_env() { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - config.expect_ok(&["rustup", "default", "nightly"]); - let out = config.run("rustup", ["show"], &[("RUSTUP_TOOLCHAIN", "nightly")]); - assert!(out.ok); - assert_eq!( - &out.stdout, - for_host_and_home!( - config, - r"Default host: {0} +async fn show_toolchain_toolchain_file_override_not_installed() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "default", "stable"]).await; + + let cwd = cx.config.current_dir(); + let toolchain_file = cwd.join("rust-toolchain"); + + raw::write_file(&toolchain_file, "nightly").unwrap(); + + // I'm not sure this should really be erroring when the toolchain + // is not installed; just capturing the behavior. + let mut cmd = clitools::cmd(&cx.config, "rustup", ["show"]); + clitools::env(&cx.config, &mut cmd); + let out = cmd.output().unwrap(); + assert!(!out.status.success()); + let stderr = String::from_utf8(out.stderr).unwrap(); + assert!(stderr.starts_with("error: override toolchain 'nightly' is not installed")); + assert!(stderr.contains(&format!( + "the toolchain file at '{}' specifies an uninstalled toolchain", + toolchain_file.display() + ))); +} + +#[tokio::test] +async fn show_toolchain_override_not_installed() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config + .expect_ok(&["rustup", "override", "add", "nightly"]) + .await; + cx.config + .expect_ok(&["rustup", "toolchain", "remove", "nightly"]) + .await; + let out = cx.config.run("rustup", ["show"], &[]).await; + assert!(!out.ok); + assert!(out + .stderr + .contains("is not installed: the directory override for")); + assert!(!out.stderr.contains("info: installing component 'rustc'")); +} + +#[tokio::test] +async fn override_set_unset_with_path() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + let cwd = fs::canonicalize(cx.config.current_dir()).unwrap(); + let mut cwd_str = cwd.to_str().unwrap(); + + if cfg!(windows) { + cwd_str = &cwd_str[4..]; + } + + let emptydir = tempfile::tempdir().unwrap(); + { + let mut cx = cx.change_dir(emptydir.path()); + cx.config + .expect_ok(&["rustup", "override", "set", "nightly", "--path", cwd_str]) + .await; + } + + cx.config + .expect_ok_ex( + &["rustup", "override", "list"], + &format!("{}\tnightly-{}\n", cwd_str, this_host_triple()), + r"", + ) + .await; + + { + let mut cx = cx.change_dir(emptydir.path()); + cx.config + .expect_ok(&["rustup", "override", "unset", "--path", cwd_str]) + .await; + } + + cx.config + .expect_ok_ex(&["rustup", "override", "list"], "no overrides\n", r"") + .await; +} + +#[tokio::test] +async fn show_toolchain_env() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + let out = cx + .config + .run("rustup", ["show"], &[("RUSTUP_TOOLCHAIN", "nightly")]) + .await; + assert!(out.ok); + assert_eq!( + &out.stdout, + for_host_and_home!( + cx.config, + r"Default host: {0} rustup home: {1} installed toolchains @@ -1148,23 +1199,23 @@ active because: overridden by environment variable RUSTUP_TOOLCHAIN installed targets: {0} " - ) - ); - }) - }); + ) + ); } -#[test] -fn show_toolchain_env_not_installed() { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - let out = config.run("rustup", ["show"], &[("RUSTUP_TOOLCHAIN", "nightly")]); +#[tokio::test] +async fn show_toolchain_env_not_installed() { + let cx = CliTestContext::new(Scenario::SimpleV2).await; + let out = cx + .config + .run("rustup", ["show"], &[("RUSTUP_TOOLCHAIN", "nightly")]) + .await; - assert!(!out.ok); + assert!(!out.ok); - let expected_out = for_host_and_home!( - config, - r"Default host: {0} + let expected_out = for_host_and_home!( + cx.config, + r"Default host: {0} rustup home: {1} installed toolchains @@ -1173,47 +1224,51 @@ installed toolchains active toolchain ---------------- " - ); - assert!(&out.stdout == expected_out); - assert!( - out.stderr - == format!( - "error: override toolchain 'nightly-{}' is not installed: \ + ); + assert!(&out.stdout == expected_out); + assert!( + out.stderr + == format!( + "error: override toolchain 'nightly-{}' is not installed: \ the RUSTUP_TOOLCHAIN environment variable specifies an uninstalled toolchain\n", - this_host_triple() - ) - ); - }) - }); -} - -#[test] -fn show_active_toolchain() { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - config.expect_ok(&["rustup", "default", "nightly"]); - config.expect_ok_ex( - &["rustup", "show", "active-toolchain"], - for_host!("nightly-{0}\nactive because: it's the default toolchain\n"), - r"", - ); - }) - }); -} - -#[test] -fn show_with_verbose() { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - config.expect_ok(&["rustup", "default", "nightly"]); - }); - config.with_scenario(Scenario::ArchivesV2_2015_01_01, &|config| { - config.expect_ok(&["rustup", "update", "nightly-2015-01-01"]); - config.expect_ok_ex( - &["rustup", "show", "--verbose"], - for_host_and_home!( - config, - r"Default host: {0} + this_host_triple() + ) + ); +} + +#[tokio::test] +async fn show_active_toolchain() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + cx.config + .expect_ok_ex( + &["rustup", "show", "active-toolchain"], + for_host!("nightly-{0}\nactive because: it's the default toolchain\n"), + r"", + ) + .await; +} + +#[tokio::test] +async fn show_with_verbose() { + let mut cx = CliTestContext::new(Scenario::None).await; + + { + let mut cx = cx.with_dist_dir(Scenario::SimpleV2); + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + } + + let mut cx = cx.with_dist_dir(Scenario::ArchivesV2_2015_01_01); + let config = &mut cx.config; + config + .expect_ok(&["rustup", "update", "nightly-2015-01-01"]) + .await; + config + .expect_ok_ex( + &["rustup", "show", "--verbose"], + for_host_and_home!( + config, + r"Default host: {0} rustup home: {1} installed toolchains @@ -1232,319 +1287,353 @@ active because: it's the default toolchain installed targets: {0} " - ), - r"", - ); - }) - }); -} - -#[test] -fn show_active_toolchain_with_verbose() { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - config.expect_ok(&["rustup", "default", "nightly"]); - config.expect_ok_ex( - &["rustup", "show", "active-toolchain", "--verbose"], - for_host!( - r"nightly-{0} + ), + r"", + ) + .await; +} + +#[tokio::test] +async fn show_active_toolchain_with_verbose() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + cx.config + .expect_ok_ex( + &["rustup", "show", "active-toolchain", "--verbose"], + for_host!( + r"nightly-{0} active because: it's the default toolchain compiler: 1.3.0 (hash-nightly-2) " - ), - r"", - ); - }) - }); -} - -#[test] -fn show_active_toolchain_with_override() { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - config.expect_ok(&["rustup", "default", "stable"]); - config.expect_ok(&["rustup", "default", "nightly"]); - config.expect_ok(&["rustup", "override", "set", "stable"]); - config.expect_stdout_ok( - &["rustup", "show", "active-toolchain"], - for_host!("stable-{0}\nactive because: directory override for"), - ); - }) - }); -} - -#[test] -fn show_active_toolchain_none() { - test(&|config| { - config.expect_ok_ex( + ), + r"", + ) + .await; +} + +#[tokio::test] +async fn show_active_toolchain_with_override() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "default", "stable"]).await; + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + cx.config + .expect_ok(&["rustup", "override", "set", "stable"]) + .await; + cx.config + .expect_stdout_ok( &["rustup", "show", "active-toolchain"], - "There isn't an active toolchain\n", - "", - ); - }); + for_host!("stable-{0}\nactive because: directory override for"), + ) + .await; } -#[test] -fn show_profile() { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - config.expect_ok(&["rustup", "default", "nightly"]); - config.expect_stdout_ok(&["rustup", "show", "profile"], "default"); - - // Check we get the same thing after we add or remove a component. - config.expect_ok(&["rustup", "component", "add", "rust-src"]); - config.expect_stdout_ok(&["rustup", "show", "profile"], "default"); - config.expect_ok(&["rustup", "component", "remove", "rustc"]); - config.expect_stdout_ok(&["rustup", "show", "profile"], "default"); - }) - }); +#[tokio::test] +async fn show_active_toolchain_none() { + let mut cx = CliTestContext::new(Scenario::None).await; + cx.config + .expect_ok_ex( + &["rustup", "show", "active-toolchain"], + "There isn't an active toolchain\n", + "", + ) + .await; +} + +#[tokio::test] +async fn show_profile() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + cx.config + .expect_stdout_ok(&["rustup", "show", "profile"], "default") + .await; + + // Check we get the same thing after we add or remove a component. + cx.config + .expect_ok(&["rustup", "component", "add", "rust-src"]) + .await; + cx.config + .expect_stdout_ok(&["rustup", "show", "profile"], "default") + .await; + cx.config + .expect_ok(&["rustup", "component", "remove", "rustc"]) + .await; + cx.config + .expect_stdout_ok(&["rustup", "show", "profile"], "default") + .await; } // #846 -#[test] -fn set_default_host() { - test(&|config| { - config.expect_ok(&["rustup", "set", "default-host", &this_host_triple()]); - config.expect_stdout_ok(&["rustup", "show"], for_host!("Default host: {0}")); - }); +#[tokio::test] +async fn set_default_host() { + let mut cx = CliTestContext::new(Scenario::None).await; + cx.config + .expect_ok(&["rustup", "set", "default-host", &this_host_triple()]) + .await; + cx.config + .expect_stdout_ok(&["rustup", "show"], for_host!("Default host: {0}")) + .await; } // #846 -#[test] -fn set_default_host_invalid_triple() { - test(&|config| { - config.expect_err( +#[tokio::test] +async fn set_default_host_invalid_triple() { + let cx = CliTestContext::new(Scenario::None).await; + cx.config + .expect_err( &["rustup", "set", "default-host", "foo"], "error: Provided host 'foo' couldn't be converted to partial triple", - ); - }); + ) + .await; } // #745 -#[test] -fn set_default_host_invalid_triple_valid_partial() { - test(&|config| { - config.expect_err( +#[tokio::test] +async fn set_default_host_invalid_triple_valid_partial() { + let cx = CliTestContext::new(Scenario::None).await; + cx.config + .expect_err( &["rustup", "set", "default-host", "x86_64-msvc"], "error: Provided host 'x86_64-msvc' did not specify an operating system", - ); - }); + ) + .await; } // #422 -#[test] -fn update_doesnt_update_non_tracking_channels() { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - config.expect_ok(&["rustup", "default", "nightly"]); - }); - config.with_scenario(Scenario::ArchivesV2_2015_01_01, &|config| { - config.expect_ok(&["rustup", "update", "nightly-2015-01-01"]); - let mut cmd = clitools::cmd(config, "rustup", ["update"]); - clitools::env(config, &mut cmd); - let out = cmd.output().unwrap(); - let stderr = String::from_utf8(out.stderr).unwrap(); - assert!(!stderr.contains(for_host!( - "syncing channel updates for 'nightly-2015-01-01-{}'" - ))); - }) - }); -} - -#[test] -fn toolchain_install_is_like_update() { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - config.expect_ok(&["rustup", "toolchain", "install", "nightly"]); - config.expect_stdout_ok( - &["rustup", "run", "nightly", "rustc", "--version"], - "hash-nightly-2", - ); - }) - }); -} - -#[test] -fn toolchain_install_is_like_update_quiet() { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - config.expect_ok(&["rustup", "--quiet", "toolchain", "install", "nightly"]); - config.expect_stdout_ok( - &["rustup", "run", "nightly", "rustc", "--version"], - "hash-nightly-2", - ); - }) - }); -} - -#[test] -fn toolchain_install_is_like_update_except_that_bare_install_is_an_error() { - test(&|config| { - config.expect_err( +#[tokio::test] +async fn update_doesnt_update_non_tracking_channels() { + let mut cx = CliTestContext::new(Scenario::None).await; + + { + let mut cx = cx.with_dist_dir(Scenario::SimpleV2); + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + } + + let mut cx = cx.with_dist_dir(Scenario::ArchivesV2_2015_01_01); + cx.config + .expect_ok(&["rustup", "update", "nightly-2015-01-01"]) + .await; + let mut cmd = clitools::cmd(&cx.config, "rustup", ["update"]); + clitools::env(&cx.config, &mut cmd); + let out = cmd.output().unwrap(); + let stderr = String::from_utf8(out.stderr).unwrap(); + assert!(!stderr.contains(for_host!( + "syncing channel updates for 'nightly-2015-01-01-{}'" + ))); +} + +#[tokio::test] +async fn toolchain_install_is_like_update() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config + .expect_ok(&["rustup", "toolchain", "install", "nightly"]) + .await; + cx.config + .expect_stdout_ok( + &["rustup", "run", "nightly", "rustc", "--version"], + "hash-nightly-2", + ) + .await; +} + +#[tokio::test] +async fn toolchain_install_is_like_update_quiet() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config + .expect_ok(&["rustup", "--quiet", "toolchain", "install", "nightly"]) + .await; + cx.config + .expect_stdout_ok( + &["rustup", "run", "nightly", "rustc", "--version"], + "hash-nightly-2", + ) + .await; +} + +#[tokio::test] +async fn toolchain_install_is_like_update_except_that_bare_install_is_an_error() { + let cx = CliTestContext::new(Scenario::None).await; + cx.config + .expect_err( &["rustup", "toolchain", "install"], "arguments were not provided", - ); - }); -} - -#[test] -fn toolchain_update_is_like_update() { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - config.expect_ok(&["rustup", "toolchain", "update", "nightly"]); - config.expect_stdout_ok( - &["rustup", "run", "nightly", "rustc", "--version"], - "hash-nightly-2", - ); - }) - }); -} - -#[test] -fn toolchain_uninstall_is_like_uninstall() { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - config.expect_ok(&["rustup", "toolchain", "install", "nightly"]); - }); - config.expect_ok(&["rustup", "default", "none"]); - config.expect_ok(&["rustup", "uninstall", "nightly"]); - config.expect_not_stdout_ok(&["rustup", "show"], for_host!("'nightly-{}'")); - }); -} - -#[test] -fn toolchain_update_is_like_update_except_that_bare_install_is_an_error() { - test(&|config| { - config.expect_err( + ) + .await; +} + +#[tokio::test] +async fn toolchain_update_is_like_update() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config + .expect_ok(&["rustup", "toolchain", "update", "nightly"]) + .await; + cx.config + .expect_stdout_ok( + &["rustup", "run", "nightly", "rustc", "--version"], + "hash-nightly-2", + ) + .await; +} + +#[tokio::test] +async fn toolchain_uninstall_is_like_uninstall() { + let mut cx = CliTestContext::new(Scenario::None).await; + + { + let mut cx = cx.with_dist_dir(Scenario::SimpleV2); + cx.config + .expect_ok(&["rustup", "toolchain", "install", "nightly"]) + .await; + } + + cx.config.expect_ok(&["rustup", "default", "none"]).await; + cx.config + .expect_ok(&["rustup", "uninstall", "nightly"]) + .await; + cx.config + .expect_not_stdout_ok(&["rustup", "show"], for_host!("'nightly-{}'")) + .await; +} + +#[tokio::test] +async fn toolchain_update_is_like_update_except_that_bare_install_is_an_error() { + let cx = CliTestContext::new(Scenario::None).await; + cx.config + .expect_err( &["rustup", "toolchain", "update"], "arguments were not provided", - ); - }); -} - -#[test] -fn proxy_toolchain_shorthand() { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - config.expect_ok(&["rustup", "default", "stable"]); - config.expect_ok(&["rustup", "toolchain", "update", "nightly"]); - config.expect_stdout_ok(&["rustc", "--version"], "hash-stable-1.1.0"); - config.expect_stdout_ok(&["rustc", "+stable", "--version"], "hash-stable-1.1.0"); - config.expect_stdout_ok(&["rustc", "+nightly", "--version"], "hash-nightly-2"); - }) - }); -} - -#[test] -fn add_component() { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - config.expect_ok(&["rustup", "default", "stable"]); - config.expect_ok(&["rustup", "component", "add", "rust-src"]); - let path = format!( - "toolchains/stable-{}/lib/rustlib/src/rust-src/foo.rs", - this_host_triple() - ); - assert!(config.rustupdir.has(path)); - }) - }); -} - -#[test] -fn add_component_by_target_triple() { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - config.expect_ok(&["rustup", "default", "stable"]); - config.expect_ok(&[ + ) + .await; +} + +#[tokio::test] +async fn proxy_toolchain_shorthand() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "default", "stable"]).await; + cx.config + .expect_ok(&["rustup", "toolchain", "update", "nightly"]) + .await; + cx.config + .expect_stdout_ok(&["rustc", "--version"], "hash-stable-1.1.0") + .await; + cx.config + .expect_stdout_ok(&["rustc", "+stable", "--version"], "hash-stable-1.1.0") + .await; + cx.config + .expect_stdout_ok(&["rustc", "+nightly", "--version"], "hash-nightly-2") + .await; +} + +#[tokio::test] +async fn add_component() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "default", "stable"]).await; + cx.config + .expect_ok(&["rustup", "component", "add", "rust-src"]) + .await; + let path = format!( + "toolchains/stable-{}/lib/rustlib/src/rust-src/foo.rs", + this_host_triple() + ); + assert!(cx.config.rustupdir.has(path)); +} + +#[tokio::test] +async fn add_component_by_target_triple() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "default", "stable"]).await; + cx.config + .expect_ok(&[ + "rustup", + "component", + "add", + &format!("rust-std-{}", clitools::CROSS_ARCH1), + ]) + .await; + let path = format!( + "toolchains/stable-{}/lib/rustlib/{}/lib/libstd.rlib", + this_host_triple(), + clitools::CROSS_ARCH1 + ); + assert!(cx.config.rustupdir.has(path)); +} + +#[tokio::test] +async fn fail_invalid_component_name() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "default", "stable"]).await; + cx.config + .expect_err( + &[ "rustup", "component", "add", - &format!("rust-std-{}", clitools::CROSS_ARCH1), - ]); - let path = format!( - "toolchains/stable-{}/lib/rustlib/{}/lib/libstd.rlib", - this_host_triple(), - clitools::CROSS_ARCH1 - ); - assert!(config.rustupdir.has(path)); - }) - }); -} - -#[test] -fn fail_invalid_component_name() { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - config.expect_ok(&["rustup", "default", "stable"]); - config.expect_err( - &[ - "rustup", - "component", - "add", - &format!("dummy-{}", clitools::CROSS_ARCH1), - ], - &format!("error: toolchain 'stable-{}' does not contain component 'dummy-{}' for target '{}'",this_host_triple(), clitools::CROSS_ARCH1, this_host_triple()), - ); - }) - }); -} - -#[test] -fn fail_invalid_component_target() { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - config.expect_ok(&["rustup", "default", "stable"]); - config.expect_err( - &[ - "rustup", - "component", - "add", - "rust-std-invalid-target", - ], - &format!("error: toolchain 'stable-{}' does not contain component 'rust-std-invalid-target' for target '{}'",this_host_triple(), this_host_triple()), - ); - }) - }); -} - -#[test] -fn remove_component() { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - config.expect_ok(&["rustup", "default", "stable"]); - config.expect_ok(&["rustup", "component", "add", "rust-src"]); - let path = PathBuf::from(format!( - "toolchains/stable-{}/lib/rustlib/src/rust-src/foo.rs", - this_host_triple() - )); - assert!(config.rustupdir.has(&path)); - config.expect_ok(&["rustup", "component", "remove", "rust-src"]); - assert!(!config.rustupdir.has(path.parent().unwrap())); - }) - }); -} - -#[test] -fn remove_component_by_target_triple() { + &format!("dummy-{}", clitools::CROSS_ARCH1), + ], + &format!( + "error: toolchain 'stable-{}' does not contain component 'dummy-{}' for target '{}'", + this_host_triple(), + clitools::CROSS_ARCH1, + this_host_triple() + ), + ) + .await; +} + +#[tokio::test] +async fn fail_invalid_component_target() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "default", "stable"]).await; + cx.config.expect_err( + &[ + "rustup", + "component", + "add", + "rust-std-invalid-target", + ], + &format!("error: toolchain 'stable-{}' does not contain component 'rust-std-invalid-target' for target '{}'",this_host_triple(), this_host_triple()), + ).await; +} + +#[tokio::test] +async fn remove_component() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "default", "stable"]).await; + cx.config + .expect_ok(&["rustup", "component", "add", "rust-src"]) + .await; + let path = PathBuf::from(format!( + "toolchains/stable-{}/lib/rustlib/src/rust-src/foo.rs", + this_host_triple() + )); + assert!(cx.config.rustupdir.has(&path)); + cx.config + .expect_ok(&["rustup", "component", "remove", "rust-src"]) + .await; + assert!(!cx.config.rustupdir.has(path.parent().unwrap())); +} + +#[tokio::test] +async fn remove_component_by_target_triple() { let component_with_triple = format!("rust-std-{}", clitools::CROSS_ARCH1); - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - config.expect_ok(&["rustup", "default", "stable"]); - config.expect_ok(&["rustup", "component", "add", &component_with_triple]); - let path = PathBuf::from(format!( - "toolchains/stable-{}/lib/rustlib/{}/lib/libstd.rlib", - this_host_triple(), - clitools::CROSS_ARCH1 - )); - assert!(config.rustupdir.has(&path)); - config.expect_ok(&["rustup", "component", "remove", &component_with_triple]); - assert!(!config.rustupdir.has(path.parent().unwrap())); - }) - }); -} - -#[test] -fn add_remove_multiple_components() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "default", "stable"]).await; + cx.config + .expect_ok(&["rustup", "component", "add", &component_with_triple]) + .await; + let path = PathBuf::from(format!( + "toolchains/stable-{}/lib/rustlib/{}/lib/libstd.rlib", + this_host_triple(), + clitools::CROSS_ARCH1 + )); + assert!(cx.config.rustupdir.has(&path)); + cx.config + .expect_ok(&["rustup", "component", "remove", &component_with_triple]) + .await; + assert!(!cx.config.rustupdir.has(path.parent().unwrap())); +} + +#[tokio::test] +async fn add_remove_multiple_components() { let files = [ "lib/rustlib/src/rust-src/foo.rs".to_owned(), format!("lib/rustlib/{}/analysis/libfoo.json", this_host_triple()), @@ -1554,1070 +1643,1161 @@ fn add_remove_multiple_components() { let component_with_triple1 = format!("rust-std-{}", clitools::CROSS_ARCH1); let component_with_triple2 = format!("rust-std-{}", clitools::CROSS_ARCH2); - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - config.expect_ok(&["rustup", "default", "nightly"]); - config.expect_ok(&[ - "rustup", - "component", - "add", - "rust-src", - "rust-analysis", - &component_with_triple1, - &component_with_triple2, - ]); - for file in &files { - let path = format!("toolchains/nightly-{}/{}", this_host_triple(), file); - assert!(config.rustupdir.has(&path)); - } - config.expect_ok(&[ - "rustup", - "component", - "remove", - "rust-src", - "rust-analysis", - &component_with_triple1, - &component_with_triple2, - ]); - for file in &files { - let path = PathBuf::from(format!( - "toolchains/nightly-{}/{}", - this_host_triple(), - file - )); - assert!(!config.rustupdir.has(path.parent().unwrap())); - } - }) - }); -} - -#[test] -fn file_override() { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - config.expect_ok(&["rustup", "default", "stable"]); - config.expect_ok(&["rustup", "toolchain", "install", "nightly"]); - - config.expect_stdout_ok(&["rustc", "--version"], "hash-stable-1.1.0"); - - let cwd = config.current_dir(); - let toolchain_file = cwd.join("rust-toolchain"); - raw::write_file(&toolchain_file, "nightly").unwrap(); - - config.expect_stdout_ok(&["rustc", "--version"], "hash-nightly-2"); - }) - }); -} - -#[test] -fn env_override_path() { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - config.expect_ok(&["rustup", "default", "stable"]); - config.expect_ok(&["rustup", "toolchain", "install", "nightly"]); - - let toolchain_path = config - .rustupdir - .join("toolchains") - .join(format!("nightly-{}", this_host_triple())); - - let out = config.run( - "rustc", - ["--version"], - &[("RUSTUP_TOOLCHAIN", toolchain_path.to_str().unwrap())], - ); - assert!(out.ok); - assert!(out.stdout.contains("hash-nightly-2")); - }) - }); -} - -#[test] -fn plus_override_relpath_is_not_supported() { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - config.expect_ok(&["rustup", "default", "stable"]); - config.expect_ok(&["rustup", "toolchain", "install", "nightly"]); - - let toolchain_path = Path::new("..") - .join(config.rustupdir.rustupdir.file_name().unwrap()) - .join("toolchains") - .join(format!("nightly-{}", this_host_triple())); - config.expect_err( - &[ - "rustc", - format!("+{}", toolchain_path.to_str().unwrap()).as_str(), - "--version", - ], - "error: relative path toolchain", - ); - }) - }); -} - -#[test] -fn run_with_relpath_is_not_supported() { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - config.expect_ok(&["rustup", "default", "stable"]); - config.expect_ok(&["rustup", "toolchain", "install", "nightly"]); - - let toolchain_path = Path::new("..") - .join(config.rustupdir.rustupdir.file_name().unwrap()) - .join("toolchains") - .join(format!("nightly-{}", this_host_triple())); - config.expect_err( - &[ - "rustup", - "run", - toolchain_path.to_str().unwrap(), - "rustc", - "--version", - ], - "relative path toolchain", - ); - }) - }); -} - -#[test] -fn plus_override_abspath_is_supported() { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - config.expect_ok(&["rustup", "default", "stable"]); - config.expect_ok(&["rustup", "toolchain", "install", "nightly"]); - - let toolchain_path = config - .rustupdir - .join("toolchains") - .join(format!("nightly-{}", this_host_triple())) - .canonicalize() - .unwrap(); - config.expect_ok(&[ + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + cx.config + .expect_ok(&[ + "rustup", + "component", + "add", + "rust-src", + "rust-analysis", + &component_with_triple1, + &component_with_triple2, + ]) + .await; + for file in &files { + let path = format!("toolchains/nightly-{}/{}", this_host_triple(), file); + assert!(cx.config.rustupdir.has(&path)); + } + cx.config + .expect_ok(&[ + "rustup", + "component", + "remove", + "rust-src", + "rust-analysis", + &component_with_triple1, + &component_with_triple2, + ]) + .await; + for file in &files { + let path = PathBuf::from(format!( + "toolchains/nightly-{}/{}", + this_host_triple(), + file + )); + assert!(!cx.config.rustupdir.has(path.parent().unwrap())); + } +} + +#[tokio::test] +async fn file_override() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "default", "stable"]).await; + cx.config + .expect_ok(&["rustup", "toolchain", "install", "nightly"]) + .await; + + cx.config + .expect_stdout_ok(&["rustc", "--version"], "hash-stable-1.1.0") + .await; + + let cwd = cx.config.current_dir(); + let toolchain_file = cwd.join("rust-toolchain"); + raw::write_file(&toolchain_file, "nightly").unwrap(); + + cx.config + .expect_stdout_ok(&["rustc", "--version"], "hash-nightly-2") + .await; +} + +#[tokio::test] +async fn env_override_path() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "default", "stable"]).await; + cx.config + .expect_ok(&["rustup", "toolchain", "install", "nightly"]) + .await; + + let toolchain_path = cx + .config + .rustupdir + .join("toolchains") + .join(format!("nightly-{}", this_host_triple())); + + let out = cx + .config + .run( + "rustc", + ["--version"], + &[("RUSTUP_TOOLCHAIN", toolchain_path.to_str().unwrap())], + ) + .await; + assert!(out.ok); + assert!(out.stdout.contains("hash-nightly-2")); +} + +#[tokio::test] +async fn plus_override_relpath_is_not_supported() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "default", "stable"]).await; + cx.config + .expect_ok(&["rustup", "toolchain", "install", "nightly"]) + .await; + + let toolchain_path = Path::new("..") + .join(cx.config.rustupdir.rustupdir.file_name().unwrap()) + .join("toolchains") + .join(format!("nightly-{}", this_host_triple())); + cx.config + .expect_err( + &[ "rustc", format!("+{}", toolchain_path.to_str().unwrap()).as_str(), "--version", - ]); - }) - }); -} - -#[test] -fn run_with_abspath_is_supported() { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - config.expect_ok(&["rustup", "default", "stable"]); - config.expect_ok(&["rustup", "toolchain", "install", "nightly"]); - - let toolchain_path = config - .rustupdir - .join("toolchains") - .join(format!("nightly-{}", this_host_triple())) - .canonicalize() - .unwrap(); - config.expect_ok(&[ + ], + "error: relative path toolchain", + ) + .await; +} + +#[tokio::test] +async fn run_with_relpath_is_not_supported() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "default", "stable"]).await; + cx.config + .expect_ok(&["rustup", "toolchain", "install", "nightly"]) + .await; + + let toolchain_path = Path::new("..") + .join(cx.config.rustupdir.rustupdir.file_name().unwrap()) + .join("toolchains") + .join(format!("nightly-{}", this_host_triple())); + cx.config + .expect_err( + &[ "rustup", "run", toolchain_path.to_str().unwrap(), "rustc", "--version", - ]); - }) - }); -} - -#[test] -fn file_override_path() { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - config.expect_ok(&["rustup", "default", "stable"]); - config.expect_ok(&["rustup", "toolchain", "install", "nightly"]); - - let toolchain_path = config - .rustupdir - .join("toolchains") - .join(format!("nightly-{}", this_host_triple())); - let toolchain_file = config.current_dir().join("rust-toolchain.toml"); - raw::write_file( - &toolchain_file, - &format!("[toolchain]\npath='{}'", toolchain_path.to_str().unwrap()), - ) - .unwrap(); - - config.expect_stdout_ok(&["rustc", "--version"], "hash-nightly-2"); - - // Check that the toolchain has the right name - config.expect_stdout_ok( - &["rustup", "show", "active-toolchain"], - &format!("nightly-{}", this_host_triple()), - ); - }) - }); -} - -#[test] -fn proxy_override_path() { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - config.expect_ok(&["rustup", "default", "stable"]); - config.expect_ok(&["rustup", "toolchain", "install", "nightly"]); - - let toolchain_path = config - .rustupdir - .join("toolchains") - .join(format!("nightly-{}", this_host_triple())); - let toolchain_file = config.current_dir().join("rust-toolchain.toml"); - raw::write_file( - &toolchain_file, - &format!("[toolchain]\npath='{}'", toolchain_path.to_str().unwrap()), - ) - .unwrap(); - - config.expect_stdout_ok(&["cargo", "--call-rustc"], "hash-nightly-2"); - }) - }); -} - -#[test] -fn file_override_path_relative_not_supported() { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - config.expect_ok(&["rustup", "default", "stable"]); - config.expect_ok(&["rustup", "toolchain", "install", "nightly"]); - - let toolchain_path = config - .rustupdir - .join("toolchains") - .join(format!("nightly-{}", this_host_triple())); - let toolchain_file = config.current_dir().join("rust-toolchain.toml"); - - // Find shared prefix so we can determine a relative path - let mut p1 = toolchain_path.components().peekable(); - let mut p2 = toolchain_file.components().peekable(); - while let (Some(p1p), Some(p2p)) = (p1.peek(), p2.peek()) { - if p1p == p2p { - let _ = p1.next(); - let _ = p2.next(); - } else { - // The two paths diverge here - break; - } - } - let mut relative_path = PathBuf::new(); - // NOTE: We skip 1 since we don't need to .. across the .toml file at the end of the path - for _ in p2.skip(1) { - relative_path.push(".."); - } - for p in p1 { - relative_path.push(p); - } - assert!(relative_path.is_relative()); - - raw::write_file( - &toolchain_file, - &format!("[toolchain]\npath='{}'", relative_path.to_str().unwrap()), - ) - .unwrap(); - - // Change into an ephemeral dir so that we test that the path is relative to the override - let ephemeral = config.current_dir().join("ephemeral"); - fs::create_dir_all(&ephemeral).unwrap(); - config.change_dir(&ephemeral, &|config| { - config.expect_err(&["rustc", "--version"], "relative path toolchain"); - }); - }) - }); -} - -#[test] -fn file_override_path_no_options() { - test(&|config| { - // Make a plausible-looking toolchain - let cwd = config.current_dir(); - let toolchain_path = cwd.join("ephemeral"); - let toolchain_bin = toolchain_path.join("bin"); - fs::create_dir_all(toolchain_bin).unwrap(); - - let toolchain_file = cwd.join("rust-toolchain.toml"); - raw::write_file( - &toolchain_file, - "[toolchain]\npath=\"ephemeral\"\ntargets=[\"dummy\"]", + ], + "relative path toolchain", ) + .await; +} + +#[tokio::test] +async fn plus_override_abspath_is_supported() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "default", "stable"]).await; + cx.config + .expect_ok(&["rustup", "toolchain", "install", "nightly"]) + .await; + + let toolchain_path = cx + .config + .rustupdir + .join("toolchains") + .join(format!("nightly-{}", this_host_triple())) + .canonicalize() .unwrap(); - - config.expect_err( + cx.config + .expect_ok(&[ + "rustc", + format!("+{}", toolchain_path.to_str().unwrap()).as_str(), + "--version", + ]) + .await; +} + +#[tokio::test] +async fn run_with_abspath_is_supported() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "default", "stable"]).await; + cx.config + .expect_ok(&["rustup", "toolchain", "install", "nightly"]) + .await; + + let toolchain_path = cx + .config + .rustupdir + .join("toolchains") + .join(format!("nightly-{}", this_host_triple())) + .canonicalize() + .unwrap(); + cx.config + .expect_ok(&[ + "rustup", + "run", + toolchain_path.to_str().unwrap(), + "rustc", + "--version", + ]) + .await; +} + +#[tokio::test] +async fn file_override_path() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "default", "stable"]).await; + cx.config + .expect_ok(&["rustup", "toolchain", "install", "nightly"]) + .await; + + let toolchain_path = cx + .config + .rustupdir + .join("toolchains") + .join(format!("nightly-{}", this_host_triple())); + let toolchain_file = cx.config.current_dir().join("rust-toolchain.toml"); + raw::write_file( + &toolchain_file, + &format!("[toolchain]\npath='{}'", toolchain_path.to_str().unwrap()), + ) + .unwrap(); + + cx.config + .expect_stdout_ok(&["rustc", "--version"], "hash-nightly-2") + .await; + + // Check that the toolchain has the right name + cx.config + .expect_stdout_ok( + &["rustup", "show", "active-toolchain"], + &format!("nightly-{}", this_host_triple()), + ) + .await; +} + +#[tokio::test] +async fn proxy_override_path() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "default", "stable"]).await; + cx.config + .expect_ok(&["rustup", "toolchain", "install", "nightly"]) + .await; + + let toolchain_path = cx + .config + .rustupdir + .join("toolchains") + .join(format!("nightly-{}", this_host_triple())); + let toolchain_file = cx.config.current_dir().join("rust-toolchain.toml"); + raw::write_file( + &toolchain_file, + &format!("[toolchain]\npath='{}'", toolchain_path.to_str().unwrap()), + ) + .unwrap(); + + cx.config + .expect_stdout_ok(&["cargo", "--call-rustc"], "hash-nightly-2") + .await; +} + +#[tokio::test] +async fn file_override_path_relative_not_supported() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "default", "stable"]).await; + cx.config + .expect_ok(&["rustup", "toolchain", "install", "nightly"]) + .await; + + let toolchain_path = cx + .config + .rustupdir + .join("toolchains") + .join(format!("nightly-{}", this_host_triple())); + let toolchain_file = cx.config.current_dir().join("rust-toolchain.toml"); + + // Find shared prefix so we can determine a relative path + let mut p1 = toolchain_path.components().peekable(); + let mut p2 = toolchain_file.components().peekable(); + while let (Some(p1p), Some(p2p)) = (p1.peek(), p2.peek()) { + if p1p == p2p { + let _ = p1.next(); + let _ = p2.next(); + } else { + // The two paths diverge here + break; + } + } + let mut relative_path = PathBuf::new(); + // NOTE: We skip 1 since we don't need to .. across the .toml file at the end of the path + for _ in p2.skip(1) { + relative_path.push(".."); + } + for p in p1 { + relative_path.push(p); + } + assert!(relative_path.is_relative()); + + raw::write_file( + &toolchain_file, + &format!("[toolchain]\npath='{}'", relative_path.to_str().unwrap()), + ) + .unwrap(); + + // Change into an ephemeral dir so that we test that the path is relative to the override + let ephemeral = cx.config.current_dir().join("ephemeral"); + fs::create_dir_all(&ephemeral).unwrap(); + + let cx = cx.change_dir(&ephemeral); + cx.config + .expect_err(&["rustc", "--version"], "relative path toolchain") + .await; +} + +#[tokio::test] +async fn file_override_path_no_options() { + let cx = CliTestContext::new(Scenario::None).await; + // Make a plausible-looking toolchain + let cwd = cx.config.current_dir(); + let toolchain_path = cwd.join("ephemeral"); + let toolchain_bin = toolchain_path.join("bin"); + fs::create_dir_all(toolchain_bin).unwrap(); + + let toolchain_file = cwd.join("rust-toolchain.toml"); + raw::write_file( + &toolchain_file, + "[toolchain]\npath=\"ephemeral\"\ntargets=[\"dummy\"]", + ) + .unwrap(); + + cx.config + .expect_err( &["rustc", "--version"], "toolchain options are ignored for path toolchain (ephemeral)", - ); - - raw::write_file( - &toolchain_file, - "[toolchain]\npath=\"ephemeral\"\ncomponents=[\"dummy\"]", ) - .unwrap(); + .await; + + raw::write_file( + &toolchain_file, + "[toolchain]\npath=\"ephemeral\"\ncomponents=[\"dummy\"]", + ) + .unwrap(); - config.expect_err( + cx.config + .expect_err( &["rustc", "--version"], "toolchain options are ignored for path toolchain (ephemeral)", - ); - - raw::write_file( - &toolchain_file, - "[toolchain]\npath=\"ephemeral\"\nprofile=\"minimal\"", ) - .unwrap(); + .await; - config.expect_err( + raw::write_file( + &toolchain_file, + "[toolchain]\npath=\"ephemeral\"\nprofile=\"minimal\"", + ) + .unwrap(); + + cx.config + .expect_err( &["rustc", "--version"], "toolchain options are ignored for path toolchain (ephemeral)", - ); - }); + ) + .await; +} + +#[tokio::test] +async fn file_override_path_xor_channel() { + let cx = CliTestContext::new(Scenario::None).await; + // Make a plausible-looking toolchain + let cwd = cx.config.current_dir(); + let toolchain_path = cwd.join("ephemeral"); + let toolchain_bin = toolchain_path.join("bin"); + fs::create_dir_all(toolchain_bin).unwrap(); + + let toolchain_file = cwd.join("rust-toolchain.toml"); + raw::write_file( + &toolchain_file, + "[toolchain]\npath=\"ephemeral\"\nchannel=\"nightly\"", + ) + .unwrap(); + + cx.config + .expect_err( + &["rustc", "--version"], + "cannot specify both channel (nightly) and path (ephemeral) simultaneously", + ) + .await; } -#[test] -fn file_override_path_xor_channel() { - test(&|config| { - // Make a plausible-looking toolchain - let cwd = config.current_dir(); - let toolchain_path = cwd.join("ephemeral"); - let toolchain_bin = toolchain_path.join("bin"); - fs::create_dir_all(toolchain_bin).unwrap(); +#[tokio::test] +async fn file_override_subdir() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "default", "stable"]).await; + cx.config + .expect_ok(&["rustup", "toolchain", "install", "nightly"]) + .await; - let toolchain_file = cwd.join("rust-toolchain.toml"); - raw::write_file( - &toolchain_file, - "[toolchain]\npath=\"ephemeral\"\nchannel=\"nightly\"", - ) - .unwrap(); + cx.config + .expect_stdout_ok(&["rustc", "--version"], "hash-stable-1.1.0") + .await; - config.expect_err( - &["rustc", "--version"], - "cannot specify both channel (nightly) and path (ephemeral) simultaneously", - ); - }); -} - -#[test] -fn file_override_subdir() { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - config.expect_ok(&["rustup", "default", "stable"]); - config.expect_ok(&["rustup", "toolchain", "install", "nightly"]); - - config.expect_stdout_ok(&["rustc", "--version"], "hash-stable-1.1.0"); - - let cwd = config.current_dir(); - let toolchain_file = cwd.join("rust-toolchain"); - raw::write_file(&toolchain_file, "nightly").unwrap(); - - let subdir = cwd.join("subdir"); - fs::create_dir_all(&subdir).unwrap(); - config.change_dir(&subdir, &|config| { - config.expect_stdout_ok(&["rustc", "--version"], "hash-nightly-2"); - }); - }) - }); -} - -#[test] -fn file_override_with_archive() { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - config.expect_ok(&["rustup", "default", "stable"]); - }); - config.with_scenario(Scenario::ArchivesV2_2015_01_01, &|config| { - config.expect_ok(&["rustup", "toolchain", "install", "nightly-2015-01-01"]); - - config.expect_stdout_ok(&["rustc", "--version"], "hash-stable-1.1.0"); - - let cwd = config.current_dir(); - let toolchain_file = cwd.join("rust-toolchain"); - raw::write_file(&toolchain_file, "nightly-2015-01-01").unwrap(); - - config.expect_stdout_ok(&["rustc", "--version"], "hash-nightly-1"); - }) - }); -} - -#[test] -fn file_override_toml_format_select_installed_toolchain() { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - config.expect_ok(&["rustup", "default", "stable"]); - }); - config.with_scenario(Scenario::ArchivesV2_2015_01_01, &|config| { - config.expect_ok(&["rustup", "toolchain", "install", "nightly-2015-01-01"]); - - config.expect_stdout_ok(&["rustc", "--version"], "hash-stable-1.1.0"); - - let cwd = config.current_dir(); - let toolchain_file = cwd.join("rust-toolchain"); - raw::write_file( - &toolchain_file, - r#" + let cwd = cx.config.current_dir(); + let toolchain_file = cwd.join("rust-toolchain"); + raw::write_file(&toolchain_file, "nightly").unwrap(); + + let subdir = cwd.join("subdir"); + fs::create_dir_all(&subdir).unwrap(); + let cx = cx.change_dir(&subdir); + cx.config + .expect_stdout_ok(&["rustc", "--version"], "hash-nightly-2") + .await; +} + +#[tokio::test] +async fn file_override_with_archive() { + let mut cx = CliTestContext::new(Scenario::None).await; + + { + let mut cx = cx.with_dist_dir(Scenario::SimpleV2); + cx.config.expect_ok(&["rustup", "default", "stable"]).await; + } + + let mut cx = cx.with_dist_dir(Scenario::ArchivesV2_2015_01_01); + cx.config + .expect_ok(&["rustup", "toolchain", "install", "nightly-2015-01-01"]) + .await; + + cx.config + .expect_stdout_ok(&["rustc", "--version"], "hash-stable-1.1.0") + .await; + + let cwd = cx.config.current_dir(); + let toolchain_file = cwd.join("rust-toolchain"); + raw::write_file(&toolchain_file, "nightly-2015-01-01").unwrap(); + + cx.config + .expect_stdout_ok(&["rustc", "--version"], "hash-nightly-1") + .await; +} + +#[tokio::test] +async fn file_override_toml_format_select_installed_toolchain() { + let mut cx = CliTestContext::new(Scenario::None).await; + + { + let mut cx = cx.with_dist_dir(Scenario::SimpleV2); + cx.config.expect_ok(&["rustup", "default", "stable"]).await; + } + + let mut cx = cx.with_dist_dir(Scenario::ArchivesV2_2015_01_01); + cx.config + .expect_ok(&["rustup", "toolchain", "install", "nightly-2015-01-01"]) + .await; + + cx.config + .expect_stdout_ok(&["rustc", "--version"], "hash-stable-1.1.0") + .await; + + let cwd = cx.config.current_dir(); + let toolchain_file = cwd.join("rust-toolchain"); + raw::write_file( + &toolchain_file, + r#" [toolchain] channel = "nightly-2015-01-01" "#, - ) - .unwrap(); - - config.expect_stdout_ok(&["rustc", "--version"], "hash-nightly-1"); - }) - }); -} - -#[test] -fn file_override_toml_format_install_both_toolchain_and_components() { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - config.expect_ok(&["rustup", "default", "stable"]); - }); - config.with_scenario(Scenario::ArchivesV2_2015_01_01, &|config| { - config.expect_stdout_ok(&["rustc", "--version"], "hash-stable-1.1.0"); - config.expect_not_stdout_ok(&["rustup", "component", "list"], "rust-src (installed)"); - - let cwd = config.current_dir(); - let toolchain_file = cwd.join("rust-toolchain"); - raw::write_file( - &toolchain_file, - r#" + ) + .unwrap(); + + cx.config + .expect_stdout_ok(&["rustc", "--version"], "hash-nightly-1") + .await; +} + +#[tokio::test] +async fn file_override_toml_format_install_both_toolchain_and_components() { + let mut cx = CliTestContext::new(Scenario::None).await; + + { + let mut cx = cx.with_dist_dir(Scenario::SimpleV2); + cx.config.expect_ok(&["rustup", "default", "stable"]).await; + } + + let cx = cx.with_dist_dir(Scenario::ArchivesV2_2015_01_01); + cx.config + .expect_stdout_ok(&["rustc", "--version"], "hash-stable-1.1.0") + .await; + cx.config + .expect_not_stdout_ok(&["rustup", "component", "list"], "rust-src (installed)") + .await; + + let cwd = cx.config.current_dir(); + let toolchain_file = cwd.join("rust-toolchain"); + raw::write_file( + &toolchain_file, + r#" [toolchain] channel = "nightly-2015-01-01" components = [ "rust-src" ] "#, - ) - .unwrap(); - - config.expect_stdout_ok(&["rustc", "--version"], "hash-nightly-1"); - config.expect_stdout_ok(&["rustup", "component", "list"], "rust-src (installed)"); - }) - }); -} - -#[test] -fn file_override_toml_format_add_missing_components() { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - config.expect_ok(&["rustup", "default", "stable"]); - config.expect_not_stdout_ok(&["rustup", "component", "list"], "rust-src (installed)"); - - let cwd = config.current_dir(); - let toolchain_file = cwd.join("rust-toolchain"); - raw::write_file( - &toolchain_file, - r#" + ) + .unwrap(); + + cx.config + .expect_stdout_ok(&["rustc", "--version"], "hash-nightly-1") + .await; + cx.config + .expect_stdout_ok(&["rustup", "component", "list"], "rust-src (installed)") + .await; +} + +#[tokio::test] +async fn file_override_toml_format_add_missing_components() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "default", "stable"]).await; + cx.config + .expect_not_stdout_ok(&["rustup", "component", "list"], "rust-src (installed)") + .await; + + let cwd = cx.config.current_dir(); + let toolchain_file = cwd.join("rust-toolchain"); + raw::write_file( + &toolchain_file, + r#" [toolchain] components = [ "rust-src" ] "#, - ) - .unwrap(); - - config.expect_stdout_ok(&["rustup", "component", "list"], "rust-src (installed)"); - }) - }); -} - -#[test] -fn file_override_toml_format_add_missing_targets() { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - config.expect_ok(&["rustup", "default", "stable"]); - config.expect_not_stdout_ok( - &["rustup", "component", "list"], - "arm-linux-androideabi (installed)", - ); - - let cwd = config.current_dir(); - let toolchain_file = cwd.join("rust-toolchain"); - raw::write_file( - &toolchain_file, - r#" + ) + .unwrap(); + + cx.config + .expect_stdout_ok(&["rustup", "component", "list"], "rust-src (installed)") + .await; +} + +#[tokio::test] +async fn file_override_toml_format_add_missing_targets() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "default", "stable"]).await; + cx.config + .expect_not_stdout_ok( + &["rustup", "component", "list"], + "arm-linux-androideabi (installed)", + ) + .await; + + let cwd = cx.config.current_dir(); + let toolchain_file = cwd.join("rust-toolchain"); + raw::write_file( + &toolchain_file, + r#" [toolchain] targets = [ "arm-linux-androideabi" ] "#, - ) - .unwrap(); - - config.expect_stdout_ok( - &["rustup", "component", "list"], - "arm-linux-androideabi (installed)", - ); - }) - }); -} - -#[test] -fn file_override_toml_format_skip_invalid_component() { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - config.expect_ok(&["rustup", "default", "stable"]); - - let cwd = config.current_dir(); - let toolchain_file = cwd.join("rust-toolchain"); - raw::write_file( - &toolchain_file, - r#" + ) + .unwrap(); + + cx.config + .expect_stdout_ok( + &["rustup", "component", "list"], + "arm-linux-androideabi (installed)", + ) + .await; +} + +#[tokio::test] +async fn file_override_toml_format_skip_invalid_component() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "default", "stable"]).await; + + let cwd = cx.config.current_dir(); + let toolchain_file = cwd.join("rust-toolchain"); + raw::write_file( + &toolchain_file, + r#" [toolchain] components = [ "rust-bongo" ] "#, - ) - .unwrap(); - - config.expect_stderr_ok( - &["rustc", "--version"], - "warn: Force-skipping unavailable component 'rust-bongo", - ); - }) - }); -} - -#[test] -fn file_override_toml_format_specify_profile() { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - config.expect_ok(&["rustup", "set", "profile", "default"]); - config.expect_stderr_ok( - &["rustup", "default", "stable"], - "downloading component 'rust-docs'", - ); - - let cwd = config.current_dir(); - let toolchain_file = cwd.join("rust-toolchain"); - raw::write_file( - &toolchain_file, - r#" + ) + .unwrap(); + + cx.config + .expect_stderr_ok( + &["rustc", "--version"], + "warn: Force-skipping unavailable component 'rust-bongo", + ) + .await; +} + +#[tokio::test] +async fn file_override_toml_format_specify_profile() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config + .expect_ok(&["rustup", "set", "profile", "default"]) + .await; + cx.config + .expect_stderr_ok( + &["rustup", "default", "stable"], + "downloading component 'rust-docs'", + ) + .await; + + let cwd = cx.config.current_dir(); + let toolchain_file = cwd.join("rust-toolchain"); + raw::write_file( + &toolchain_file, + r#" [toolchain] profile = "minimal" channel = "nightly" "#, - ) - .unwrap(); - config.expect_not_stdout_ok( - &["rustup", "component", "list"], - for_host!("rust-docs-{} (installed)"), - ); - }) - }); + ) + .unwrap(); + cx.config + .expect_not_stdout_ok( + &["rustup", "component", "list"], + for_host!("rust-docs-{} (installed)"), + ) + .await; } -#[test] -fn close_file_override_beats_far_directory_override() { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - config.expect_ok(&["rustup", "default", "stable"]); - config.expect_ok(&["rustup", "toolchain", "install", "beta"]); - config.expect_ok(&["rustup", "toolchain", "install", "nightly"]); +#[tokio::test] +async fn close_file_override_beats_far_directory_override() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "default", "stable"]).await; + cx.config + .expect_ok(&["rustup", "toolchain", "install", "beta"]) + .await; + cx.config + .expect_ok(&["rustup", "toolchain", "install", "nightly"]) + .await; - config.expect_ok(&["rustup", "override", "set", "beta"]); - config.expect_stdout_ok(&["rustc", "--version"], "hash-beta-1.2.0"); + cx.config + .expect_ok(&["rustup", "override", "set", "beta"]) + .await; + cx.config + .expect_stdout_ok(&["rustc", "--version"], "hash-beta-1.2.0") + .await; - let cwd = config.current_dir(); + let cwd = cx.config.current_dir(); - let subdir = cwd.join("subdir"); - fs::create_dir_all(&subdir).unwrap(); + let subdir = cwd.join("subdir"); + fs::create_dir_all(&subdir).unwrap(); - let toolchain_file = subdir.join("rust-toolchain"); - raw::write_file(&toolchain_file, "nightly").unwrap(); + let toolchain_file = subdir.join("rust-toolchain"); + raw::write_file(&toolchain_file, "nightly").unwrap(); - config.change_dir(&subdir, &|config| { - config.expect_stdout_ok(&["rustc", "--version"], "hash-nightly-2"); - }); - }) - }); + let cx = cx.change_dir(&subdir); + cx.config + .expect_stdout_ok(&["rustc", "--version"], "hash-nightly-2") + .await; } -#[test] // Check that toolchain overrides have the correct priority. -fn override_order() { - test(&|config| { - config.with_scenario(Scenario::ArchivesV2, &|config| { - let host = this_host_triple(); - // give each override type a different toolchain - let default_tc = &format!("beta-2015-01-01-{}", host); - let env_tc = &format!("stable-2015-01-01-{}", host); - let dir_tc = &format!("beta-2015-01-02-{}", host); - let file_tc = &format!("stable-2015-01-02-{}", host); - let command_tc = &format!("nightly-2015-01-01-{}", host); - config.expect_ok(&["rustup", "install", default_tc]); - config.expect_ok(&["rustup", "install", env_tc]); - config.expect_ok(&["rustup", "install", dir_tc]); - config.expect_ok(&["rustup", "install", file_tc]); - config.expect_ok(&["rustup", "install", command_tc]); - - // No default - config.expect_ok(&["rustup", "default", "none"]); - config.expect_stdout_ok( - &["rustup", "show", "active-toolchain"], - "There isn't an active toolchain\n", - ); - - // Default - config.expect_ok(&["rustup", "default", default_tc]); - config.expect_stdout_ok(&["rustup", "show", "active-toolchain"], default_tc); - - // file > default - let toolchain_file = config.current_dir().join("rust-toolchain.toml"); - raw::write_file( - &toolchain_file, - &format!("[toolchain]\nchannel='{}'", file_tc), - ) - .unwrap(); - config.expect_stdout_ok(&["rustup", "show", "active-toolchain"], file_tc); - - // dir override > file > default - config.expect_ok(&["rustup", "override", "set", dir_tc]); - config.expect_stdout_ok(&["rustup", "show", "active-toolchain"], dir_tc); - - // env > dir override > file > default - let out = config.run( - "rustup", - ["show", "active-toolchain"], - &[("RUSTUP_TOOLCHAIN", env_tc)], - ); - assert!(out.ok); - assert!(out.stdout.contains(env_tc)); - - // +toolchain > env > dir override > file > default - let out = config.run( - "rustup", - [&format!("+{}", command_tc), "show", "active-toolchain"], - &[("RUSTUP_TOOLCHAIN", env_tc)], - ); - assert!(out.ok); - assert!(out.stdout.contains(command_tc)); - }) - }); -} - -#[test] -fn directory_override_doesnt_need_to_exist_unless_it_is_selected() { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - config.expect_ok(&["rustup", "default", "stable"]); - config.expect_ok(&["rustup", "toolchain", "install", "beta"]); - // not installing nightly - - config.expect_ok(&["rustup", "override", "set", "beta"]); - config.expect_stdout_ok(&["rustc", "--version"], "hash-beta-1.2.0"); - - let cwd = config.current_dir(); - let toolchain_file = cwd.join("rust-toolchain"); - raw::write_file(&toolchain_file, "nightly").unwrap(); - - config.expect_stdout_ok(&["rustc", "--version"], "hash-beta-1.2.0"); - }) - }); -} - -#[test] -fn env_override_beats_file_override() { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - config.expect_ok(&["rustup", "default", "stable"]); - config.expect_ok(&["rustup", "toolchain", "install", "beta"]); - config.expect_ok(&["rustup", "toolchain", "install", "nightly"]); - - let cwd = config.current_dir(); - let toolchain_file = cwd.join("rust-toolchain"); - raw::write_file(&toolchain_file, "nightly").unwrap(); - - let mut cmd = clitools::cmd(config, "rustc", ["--version"]); - clitools::env(config, &mut cmd); - cmd.env("RUSTUP_TOOLCHAIN", "beta"); - - let out = cmd.output().unwrap(); - assert!(String::from_utf8(out.stdout) - .unwrap() - .contains("hash-beta-1.2.0")); - }) - }); -} - -#[test] -fn plus_override_beats_file_override() { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - config.expect_ok(&["rustup", "default", "stable"]); - config.expect_ok(&["rustup", "toolchain", "install", "beta"]); - config.expect_ok(&["rustup", "toolchain", "install", "nightly"]); - - let cwd = config.current_dir(); - let toolchain_file = cwd.join("rust-toolchain"); - raw::write_file(&toolchain_file, "nightly").unwrap(); - - config.expect_stdout_ok(&["rustc", "+beta", "--version"], "hash-beta-1.2.0"); - }) - }); -} - -#[test] -fn file_override_not_installed_custom() { - test(&|config| { - let cwd = config.current_dir(); - let toolchain_file = cwd.join("rust-toolchain"); - raw::write_file(&toolchain_file, "gumbo").unwrap(); - - config.expect_err(&["rustc", "--version"], "custom and not installed"); - }); -} - -#[test] -fn bad_file_override() { - test(&|config| { - let cwd = config.current_dir(); - let toolchain_file = cwd.join("rust-toolchain"); - // invalid name - cannot specify no toolchain in a toolchain file - raw::write_file(&toolchain_file, "none").unwrap(); - - config.expect_err(&["rustc", "--version"], "invalid toolchain name 'none'"); - }); -} - -#[test] -fn valid_override_settings() { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - let cwd = config.current_dir(); - let toolchain_file = cwd.join("rust-toolchain"); - config.expect_ok(&["rustup", "default", "nightly"]); - raw::write_file(&toolchain_file, "nightly").unwrap(); - config.expect_ok(&["rustc", "--version"]); - // Special case: same version as is installed is permitted. - raw::write_file(&toolchain_file, for_host!("nightly-{}")).unwrap(); - config.expect_ok(&["rustc", "--version"]); - let fullpath = config - .rustupdir - .clone() - .join("toolchains") - .join(for_host!("nightly-{}")); - config.expect_ok(&[ - "rustup", - "toolchain", - "link", - "system", - &format!("{}", fullpath.display()), - ]); - raw::write_file(&toolchain_file, "system").unwrap(); - config.expect_ok(&["rustc", "--version"]); - }) - }) -} - -#[test] -fn file_override_with_target_info() { +#[tokio::test] +async fn override_order() { + let mut cx = CliTestContext::new(Scenario::ArchivesV2).await; + let host = this_host_triple(); + // give each override type a different toolchain + let default_tc = &format!("beta-2015-01-01-{}", host); + let env_tc = &format!("stable-2015-01-01-{}", host); + let dir_tc = &format!("beta-2015-01-02-{}", host); + let file_tc = &format!("stable-2015-01-02-{}", host); + let command_tc = &format!("nightly-2015-01-01-{}", host); + cx.config + .expect_ok(&["rustup", "install", default_tc]) + .await; + cx.config.expect_ok(&["rustup", "install", env_tc]).await; + cx.config.expect_ok(&["rustup", "install", dir_tc]).await; + cx.config.expect_ok(&["rustup", "install", file_tc]).await; + cx.config + .expect_ok(&["rustup", "install", command_tc]) + .await; + + // No default + cx.config.expect_ok(&["rustup", "default", "none"]).await; + cx.config + .expect_stdout_ok( + &["rustup", "show", "active-toolchain"], + "There isn't an active toolchain\n", + ) + .await; + + // Default + cx.config + .expect_ok(&["rustup", "default", default_tc]) + .await; + cx.config + .expect_stdout_ok(&["rustup", "show", "active-toolchain"], default_tc) + .await; + + // file > default + let toolchain_file = cx.config.current_dir().join("rust-toolchain.toml"); + raw::write_file( + &toolchain_file, + &format!("[toolchain]\nchannel='{}'", file_tc), + ) + .unwrap(); + cx.config + .expect_stdout_ok(&["rustup", "show", "active-toolchain"], file_tc) + .await; + + // dir override > file > default + cx.config + .expect_ok(&["rustup", "override", "set", dir_tc]) + .await; + cx.config + .expect_stdout_ok(&["rustup", "show", "active-toolchain"], dir_tc) + .await; + + // env > dir override > file > default + let out = cx + .config + .run( + "rustup", + ["show", "active-toolchain"], + &[("RUSTUP_TOOLCHAIN", env_tc)], + ) + .await; + assert!(out.ok); + assert!(out.stdout.contains(env_tc)); + + // +toolchain > env > dir override > file > default + let out = cx + .config + .run( + "rustup", + [&format!("+{}", command_tc), "show", "active-toolchain"], + &[("RUSTUP_TOOLCHAIN", env_tc)], + ) + .await; + assert!(out.ok); + assert!(out.stdout.contains(command_tc)); +} + +#[tokio::test] +async fn directory_override_doesnt_need_to_exist_unless_it_is_selected() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "default", "stable"]).await; + cx.config + .expect_ok(&["rustup", "toolchain", "install", "beta"]) + .await; + // not installing nightly + + cx.config + .expect_ok(&["rustup", "override", "set", "beta"]) + .await; + cx.config + .expect_stdout_ok(&["rustc", "--version"], "hash-beta-1.2.0") + .await; + + let cwd = cx.config.current_dir(); + let toolchain_file = cwd.join("rust-toolchain"); + raw::write_file(&toolchain_file, "nightly").unwrap(); + + cx.config + .expect_stdout_ok(&["rustc", "--version"], "hash-beta-1.2.0") + .await; +} + +#[tokio::test] +async fn env_override_beats_file_override() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "default", "stable"]).await; + cx.config + .expect_ok(&["rustup", "toolchain", "install", "beta"]) + .await; + cx.config + .expect_ok(&["rustup", "toolchain", "install", "nightly"]) + .await; + + let cwd = cx.config.current_dir(); + let toolchain_file = cwd.join("rust-toolchain"); + raw::write_file(&toolchain_file, "nightly").unwrap(); + + let mut cmd = clitools::cmd(&cx.config, "rustc", ["--version"]); + clitools::env(&cx.config, &mut cmd); + cmd.env("RUSTUP_TOOLCHAIN", "beta"); + + let out = cmd.output().unwrap(); + assert!(String::from_utf8(out.stdout) + .unwrap() + .contains("hash-beta-1.2.0")); +} + +#[tokio::test] +async fn plus_override_beats_file_override() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "default", "stable"]).await; + cx.config + .expect_ok(&["rustup", "toolchain", "install", "beta"]) + .await; + cx.config + .expect_ok(&["rustup", "toolchain", "install", "nightly"]) + .await; + + let cwd = cx.config.current_dir(); + let toolchain_file = cwd.join("rust-toolchain"); + raw::write_file(&toolchain_file, "nightly").unwrap(); + + cx.config + .expect_stdout_ok(&["rustc", "+beta", "--version"], "hash-beta-1.2.0") + .await; +} + +#[tokio::test] +async fn file_override_not_installed_custom() { + let cx = CliTestContext::new(Scenario::None).await; + let cwd = cx.config.current_dir(); + let toolchain_file = cwd.join("rust-toolchain"); + raw::write_file(&toolchain_file, "gumbo").unwrap(); + + cx.config + .expect_err(&["rustc", "--version"], "custom and not installed") + .await; +} + +#[tokio::test] +async fn bad_file_override() { + let cx = CliTestContext::new(Scenario::None).await; + let cwd = cx.config.current_dir(); + let toolchain_file = cwd.join("rust-toolchain"); + // invalid name - cannot specify no toolchain in a toolchain file + raw::write_file(&toolchain_file, "none").unwrap(); + + cx.config + .expect_err(&["rustc", "--version"], "invalid toolchain name 'none'") + .await; +} + +#[tokio::test] +async fn valid_override_settings() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + let cwd = cx.config.current_dir(); + let toolchain_file = cwd.join("rust-toolchain"); + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + raw::write_file(&toolchain_file, "nightly").unwrap(); + cx.config.expect_ok(&["rustc", "--version"]).await; + // Special case: same version as is installed is permitted. + raw::write_file(&toolchain_file, for_host!("nightly-{}")).unwrap(); + cx.config.expect_ok(&["rustc", "--version"]).await; + let fullpath = cx + .config + .rustupdir + .clone() + .join("toolchains") + .join(for_host!("nightly-{}")); + cx.config + .expect_ok(&[ + "rustup", + "toolchain", + "link", + "system", + &format!("{}", fullpath.display()), + ]) + .await; + raw::write_file(&toolchain_file, "system").unwrap(); + cx.config.expect_ok(&["rustc", "--version"]).await; +} + +#[tokio::test] +async fn file_override_with_target_info() { // Target info is not portable between machines, so we reject toolchain // files that include it. - test(&|config| { - let cwd = config.current_dir(); - let toolchain_file = cwd.join("rust-toolchain"); - raw::write_file(&toolchain_file, "nightly-x86_64-unknown-linux-gnu").unwrap(); + let cx = CliTestContext::new(Scenario::None).await; + let cwd = cx.config.current_dir(); + let toolchain_file = cwd.join("rust-toolchain"); + raw::write_file(&toolchain_file, "nightly-x86_64-unknown-linux-gnu").unwrap(); - config.expect_err( + cx.config + .expect_err( &["rustc", "--version"], "target triple in channel name 'nightly-x86_64-unknown-linux-gnu'", + ) + .await; +} + +#[tokio::test] +async fn docs_with_path() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "default", "stable"]).await; + cx.config + .expect_ok(&["rustup", "toolchain", "install", "nightly"]) + .await; + + let mut cmd = clitools::cmd(&cx.config, "rustup", ["doc", "--path"]); + clitools::env(&cx.config, &mut cmd); + + let out = cmd.output().unwrap(); + let path = format!("share{MAIN_SEPARATOR}doc{MAIN_SEPARATOR}rust{MAIN_SEPARATOR}html"); + assert!(String::from_utf8(out.stdout).unwrap().contains(&path)); + + cx.config + .expect_stdout_ok( + &["rustup", "doc", "--path", "--toolchain", "nightly"], + "nightly", + ) + .await; +} + +#[tokio::test] +async fn docs_topical_with_path() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "default", "stable"]).await; + cx.config + .expect_ok(&["rustup", "toolchain", "install", "nightly"]) + .await; + + for (topic, path) in mock::topical_doc_data::test_cases() { + let mut cmd = clitools::cmd(&cx.config, "rustup", ["doc", "--path", topic]); + clitools::env(&cx.config, &mut cmd); + + let out = cmd.output().unwrap(); + eprintln!("{:?}", String::from_utf8(out.stderr).unwrap()); + let out_str = String::from_utf8(out.stdout).unwrap(); + assert!( + out_str.contains(&path), + "comparing path\ntopic: '{topic}'\nexpected path: '{path}'\noutput: {out_str}\n\n\n", ); - }); -} - -#[test] -fn docs_with_path() { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - config.expect_ok(&["rustup", "default", "stable"]); - config.expect_ok(&["rustup", "toolchain", "install", "nightly"]); - - let mut cmd = clitools::cmd(config, "rustup", ["doc", "--path"]); - clitools::env(config, &mut cmd); - - let out = cmd.output().unwrap(); - let path = format!("share{MAIN_SEPARATOR}doc{MAIN_SEPARATOR}rust{MAIN_SEPARATOR}html"); - assert!(String::from_utf8(out.stdout).unwrap().contains(&path)); - - config.expect_stdout_ok( - &["rustup", "doc", "--path", "--toolchain", "nightly"], - "nightly", - ); - }) - }); -} - -#[test] -fn docs_topical_with_path() { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - - config.expect_ok(&["rustup", "default", "stable"]); - config.expect_ok(&["rustup", "toolchain", "install", "nightly"]); - - for (topic, path) in mock::topical_doc_data::test_cases() { - let mut cmd = clitools::cmd(config, "rustup", ["doc", "--path", topic]); - clitools::env(config, &mut cmd); - - let out = cmd.output().unwrap(); - eprintln!("{:?}", String::from_utf8(out.stderr).unwrap()); - let out_str = String::from_utf8(out.stdout).unwrap(); - assert!( - out_str.contains(&path), - "comparing path\ntopic: '{topic}'\nexpected path: '{path}'\noutput: {out_str}\n\n\n", - ); - } - }) - }); -} - -#[test] -fn docs_missing() { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - config.expect_ok(&["rustup", "set", "profile", "minimal"]); - config.expect_ok(&["rustup", "default", "nightly"]); - config.expect_err( - &["rustup", "doc"], - "error: unable to view documentation which is not installed", - ); - }) - }); -} - -#[test] -fn docs_custom() { - test(&|config| { - let path = config.customdir.join("custom-1"); - let path = path.to_string_lossy(); - config.expect_ok(&["rustup", "toolchain", "link", "custom", &path]); - config.expect_ok(&["rustup", "default", "custom"]); - config.expect_stdout_ok(&["rustup", "doc", "--path"], "custom"); - }); + } +} + +#[tokio::test] +async fn docs_missing() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config + .expect_ok(&["rustup", "set", "profile", "minimal"]) + .await; + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + cx.config + .expect_err( + &["rustup", "doc"], + "error: unable to view documentation which is not installed", + ) + .await; +} + +#[tokio::test] +async fn docs_custom() { + let mut cx = CliTestContext::new(Scenario::None).await; + let path = cx.config.customdir.join("custom-1"); + let path = path.to_string_lossy(); + cx.config + .expect_ok(&["rustup", "toolchain", "link", "custom", &path]) + .await; + cx.config.expect_ok(&["rustup", "default", "custom"]).await; + cx.config + .expect_stdout_ok(&["rustup", "doc", "--path"], "custom") + .await; } #[cfg(unix)] -#[test] -fn non_utf8_arg() { +#[tokio::test] +async fn non_utf8_arg() { use std::ffi::OsStr; use std::os::unix::ffi::OsStrExt; - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - config.expect_ok(&["rustup", "default", "nightly"]); - let out = config.run( - "rustc", - [ - OsStr::new("--echo-args"), - OsStr::new("echoed non-utf8 arg:"), - OsStr::from_bytes(b"\xc3\x28"), - ], - &[("RUST_BACKTRACE", "1")], - ); - assert!(out.stderr.contains("echoed non-utf8 arg")); - }) - }); + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + let out = cx + .config + .run( + "rustc", + [ + OsStr::new("--echo-args"), + OsStr::new("echoed non-utf8 arg:"), + OsStr::from_bytes(b"\xc3\x28"), + ], + &[("RUST_BACKTRACE", "1")], + ) + .await; + assert!(out.stderr.contains("echoed non-utf8 arg")); } #[cfg(windows)] -#[test] -fn non_utf8_arg() { +#[tokio::test] +async fn non_utf8_arg() { use std::ffi::OsString; use std::os::windows::ffi::OsStringExt; - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - config.expect_ok(&["rustup", "default", "nightly"]); - let out = config.run( - "rustc", - [ - OsString::from("--echo-args".to_string()), - OsString::from("echoed non-utf8 arg:".to_string()), - OsString::from_wide(&[0xd801, 0xd801]), - ], - &[("RUST_BACKTRACE", "1")], - ); - assert!(out.stderr.contains("echoed non-utf8 arg")); - }) - }); + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + let out = cx + .config + .run( + "rustc", + [ + OsString::from("--echo-args".to_string()), + OsString::from("echoed non-utf8 arg:".to_string()), + OsString::from_wide(&[0xd801, 0xd801]), + ], + &[("RUST_BACKTRACE", "1")], + ) + .await; + assert!(out.stderr.contains("echoed non-utf8 arg")); } #[cfg(unix)] -#[test] -fn non_utf8_toolchain() { +#[tokio::test] +async fn non_utf8_toolchain() { use std::ffi::OsStr; use std::os::unix::ffi::OsStrExt; - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - config.expect_ok(&["rustup", "default", "nightly"]); - let out = config.run( - "rustc", - [OsStr::from_bytes(b"+\xc3\x28")], - &[("RUST_BACKTRACE", "1")], - ); - assert!(out.stderr.contains("toolchain '�(' is not installed")); - }) - }); + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + let out = cx + .config + .run( + "rustc", + [OsStr::from_bytes(b"+\xc3\x28")], + &[("RUST_BACKTRACE", "1")], + ) + .await; + assert!(out.stderr.contains("toolchain '�(' is not installed")); } #[cfg(windows)] -#[test] -fn non_utf8_toolchain() { +#[tokio::test] +async fn non_utf8_toolchain() { use std::ffi::OsString; use std::os::windows::ffi::OsStringExt; - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - config.expect_ok(&["rustup", "default", "nightly"]); - let out = config.run( - "rustc", - [OsString::from_wide(&[u16::from(b'+'), 0xd801, 0xd801])], - &[("RUST_BACKTRACE", "1")], - ); - assert!(out.stderr.contains("toolchain '��' is not installed")); - }) - }); -} - -#[test] -fn check_host_goes_away() { - test(&|config| { - config.with_scenario(Scenario::HostGoesMissingBefore, &|config| { - config.expect_ok(&["rustup", "default", "nightly"]); - }); - config.with_scenario(Scenario::HostGoesMissingAfter, &|config| { - config.expect_err( - &["rustup", "update", "nightly"], - for_host!("target '{}' not found in channel"), - ); - }) - }) + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + let out = cx + .config + .run( + "rustc", + [OsString::from_wide(&[u16::from(b'+'), 0xd801, 0xd801])], + &[("RUST_BACKTRACE", "1")], + ) + .await; + assert!(out.stderr.contains("toolchain '��' is not installed")); } -#[cfg(unix)] -#[test] -fn check_unix_settings_fallback() { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - // No default toolchain specified yet - config.expect_stdout_ok(&["rustup", "default"], "no default toolchain is configured"); - - // Default toolchain specified in fallback settings file - let mock_settings_file = config.current_dir().join("mock_fallback_settings.toml"); - raw::write_file( - &mock_settings_file, - for_host!(r"default_toolchain = 'nightly-{0}'"), - ) - .unwrap(); +#[tokio::test] +async fn check_host_goes_away() { + let mut cx = CliTestContext::new(Scenario::None).await; - let mut cmd = clitools::cmd(config, "rustup", ["default"]); - clitools::env(config, &mut cmd); + { + let mut cx = cx.with_dist_dir(Scenario::HostGoesMissingBefore); + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + } - // Override the path to the fallback settings file to be the mock file - cmd.env("RUSTUP_OVERRIDE_UNIX_FALLBACK_SETTINGS", mock_settings_file); + let cx = cx.with_dist_dir(Scenario::HostGoesMissingAfter); + cx.config + .expect_err( + &["rustup", "update", "nightly"], + for_host!("target '{}' not found in channel"), + ) + .await; +} - let out = cmd.output().unwrap(); - assert!(out.status.success()); - let stdout = String::from_utf8(out.stdout).unwrap(); - assert_eq!( - &stdout, - for_host!( - r"nightly-{0} (default) +#[cfg(unix)] +#[tokio::test] +async fn check_unix_settings_fallback() { + let cx = CliTestContext::new(Scenario::SimpleV2).await; + // No default toolchain specified yet + cx.config + .expect_stdout_ok(&["rustup", "default"], "no default toolchain is configured") + .await; + + // Default toolchain specified in fallback settings file + let mock_settings_file = cx.config.current_dir().join("mock_fallback_settings.toml"); + raw::write_file( + &mock_settings_file, + for_host!(r"default_toolchain = 'nightly-{0}'"), + ) + .unwrap(); + + let mut cmd = clitools::cmd(&cx.config, "rustup", ["default"]); + clitools::env(&cx.config, &mut cmd); + + // Override the path to the fallback settings file to be the mock file + cmd.env("RUSTUP_OVERRIDE_UNIX_FALLBACK_SETTINGS", mock_settings_file); + + let out = cmd.output().unwrap(); + assert!(out.status.success()); + let stdout = String::from_utf8(out.stdout).unwrap(); + assert_eq!( + &stdout, + for_host!( + r"nightly-{0} (default) " - ) - ); - }) - }); -} - -#[test] -fn warn_on_unmatch_build() { - test(&|config| { - config.with_scenario(Scenario::MultiHost, &|config| { - let arch = clitools::MULTI_ARCH1; - config.expect_stderr_ok( - &["rustup", "toolchain", "install", &format!("nightly-{arch}")], - &format!( - r"warn: toolchain 'nightly-{arch}' may not be able to run on this system. -warn: If you meant to build software to target that platform, perhaps try `rustup target add {arch}` instead?", - ), - ); - }) - }); + ) + ); } -#[test] -fn dont_warn_on_partial_build() { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - let triple = this_host_triple(); - let arch = triple.split('-').next().unwrap(); - let mut cmd = clitools::cmd( - config, - "rustup", - ["toolchain", "install", &format!("nightly-{arch}")], - ); - clitools::env(config, &mut cmd); - let out = cmd.output().unwrap(); - assert!(out.status.success()); - let stderr = String::from_utf8(out.stderr).unwrap(); - assert!(stderr.contains(&format!( - r"info: syncing channel updates for 'nightly-{triple}'" - ))); - assert!(!stderr.contains(&format!( - r"warn: toolchain 'nightly-{arch}' may not be able to run on this system." - ))); - }) - }) +#[tokio::test] +async fn warn_on_unmatch_build() { + let cx = CliTestContext::new(Scenario::MultiHost).await; + let arch = clitools::MULTI_ARCH1; + cx.config.expect_stderr_ok( + &["rustup", "toolchain", "install", &format!("nightly-{arch}")], + &format!( + r"warn: toolchain 'nightly-{arch}' may not be able to run on this system. +warn: If you meant to build software to target that platform, perhaps try `rustup target add {arch}` instead?", + ), + ).await; +} + +#[tokio::test] +async fn dont_warn_on_partial_build() { + let cx = CliTestContext::new(Scenario::SimpleV2).await; + let triple = this_host_triple(); + let arch = triple.split('-').next().unwrap(); + let mut cmd = clitools::cmd( + &cx.config, + "rustup", + ["toolchain", "install", &format!("nightly-{arch}")], + ); + clitools::env(&cx.config, &mut cmd); + let out = cmd.output().unwrap(); + assert!(out.status.success()); + let stderr = String::from_utf8(out.stderr).unwrap(); + assert!(stderr.contains(&format!( + r"info: syncing channel updates for 'nightly-{triple}'" + ))); + assert!(!stderr.contains(&format!( + r"warn: toolchain 'nightly-{arch}' may not be able to run on this system." + ))); } /// Checks that `rust-toolchain.toml` files are considered -#[test] -fn rust_toolchain_toml() { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - config.expect_err( - &["rustc", "--version"], - "rustup could not choose a version of rustc to run", - ); +#[tokio::test] +async fn rust_toolchain_toml() { + let cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config + .expect_err( + &["rustc", "--version"], + "rustup could not choose a version of rustc to run", + ) + .await; - let cwd = config.current_dir(); - let toolchain_file = cwd.join("rust-toolchain.toml"); - raw::write_file(&toolchain_file, "[toolchain]\nchannel = \"nightly\"").unwrap(); + let cwd = cx.config.current_dir(); + let toolchain_file = cwd.join("rust-toolchain.toml"); + raw::write_file(&toolchain_file, "[toolchain]\nchannel = \"nightly\"").unwrap(); - config.expect_stdout_ok(&["rustc", "--version"], "hash-nightly-2"); - }) - }); + cx.config + .expect_stdout_ok(&["rustc", "--version"], "hash-nightly-2") + .await; } /// Ensures that `rust-toolchain.toml` files (with `.toml` extension) only allow TOML contents -#[test] -fn only_toml_in_rust_toolchain_toml() { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - let cwd = config.current_dir(); - let toolchain_file = cwd.join("rust-toolchain.toml"); - raw::write_file(&toolchain_file, "nightly").unwrap(); +#[tokio::test] +async fn only_toml_in_rust_toolchain_toml() { + let cx = CliTestContext::new(Scenario::SimpleV2).await; + let cwd = cx.config.current_dir(); + let toolchain_file = cwd.join("rust-toolchain.toml"); + raw::write_file(&toolchain_file, "nightly").unwrap(); - config.expect_err(&["rustc", "--version"], "error parsing override file"); - }) - }); + cx.config + .expect_err(&["rustc", "--version"], "error parsing override file") + .await; } /// Checks that a warning occurs if both `rust-toolchain` and `rust-toolchain.toml` files exist -#[test] -fn warn_on_duplicate_rust_toolchain_file() { - test(&|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - let cwd = config.current_dir(); - let toolchain_file_1 = cwd.join("rust-toolchain"); - raw::write_file(&toolchain_file_1, "stable").unwrap(); - let toolchain_file_2 = cwd.join("rust-toolchain.toml"); - raw::write_file(&toolchain_file_2, "[toolchain]").unwrap(); - - config.expect_stderr_ok( - &["rustc", "--version"], - &format!( - "warn: both `{0}` and `{1}` exist. Using `{0}`", - toolchain_file_1.canonicalize().unwrap().display(), - toolchain_file_2.canonicalize().unwrap().display(), - ), - ); - }) - }); +#[tokio::test] +async fn warn_on_duplicate_rust_toolchain_file() { + let cx = CliTestContext::new(Scenario::SimpleV2).await; + let cwd = cx.config.current_dir(); + let toolchain_file_1 = cwd.join("rust-toolchain"); + raw::write_file(&toolchain_file_1, "stable").unwrap(); + let toolchain_file_2 = cwd.join("rust-toolchain.toml"); + raw::write_file(&toolchain_file_2, "[toolchain]").unwrap(); + + cx.config + .expect_stderr_ok( + &["rustc", "--version"], + &format!( + "warn: both `{0}` and `{1}` exist. Using `{0}`", + toolchain_file_1.canonicalize().unwrap().display(), + toolchain_file_2.canonicalize().unwrap().display(), + ), + ) + .await; } diff --git a/tests/suite/cli_self_upd.rs b/tests/suite/cli_self_upd.rs index 94cf2412a8..95e743aabf 100644 --- a/tests/suite/cli_self_upd.rs +++ b/tests/suite/cli_self_upd.rs @@ -14,62 +14,62 @@ use retry::{ }; use rustup::test::{ mock::{ - clitools::{self, output_release_file, self_update_setup, Config, Scenario}, + clitools::{self, output_release_file, CliTestContext, Scenario, SelfUpdateTestContext}, dist::calc_hash, }, - this_host_triple, with_saved_path, + this_host_triple, }; +#[cfg(windows)] +use rustup::test::{RegistryGuard, RegistryValueId, USER_PATH}; use rustup::utils::{raw, utils}; use rustup::{for_host, DUP_TOOLS, TOOLS}; -#[cfg(windows)] -use rustup::test::with_saved_reg_value; - const TEST_VERSION: &str = "1.1.1"; -pub fn update_setup(f: &dyn Fn(&mut Config, &Path)) { - self_update_setup(f, TEST_VERSION) -} - /// Empty dist server, rustup installed with no toolchain -fn setup_empty_installed(f: &dyn Fn(&mut Config)) { - clitools::test(Scenario::Empty, &|config| { - config.expect_ok(&[ +async fn setup_empty_installed() -> CliTestContext { + let mut cx = CliTestContext::new(Scenario::Empty).await; + cx.config + .expect_ok(&[ "rustup-init", "-y", "--no-modify-path", "--default-toolchain", "none", - ]); - f(config); - }) + ]) + .await; + cx } /// SimpleV3 dist server, rustup installed with default toolchain -fn setup_installed(f: &dyn Fn(&mut Config)) { - clitools::test(Scenario::SimpleV2, &|config| { - config.expect_ok(&["rustup-init", "-y", "--no-modify-path"]); - f(config); - }) +async fn setup_installed() -> CliTestContext { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config + .expect_ok(&["rustup-init", "-y", "--no-modify-path"]) + .await; + cx } -#[test] /// This is the primary smoke test testing the full end to end behavior of the /// installation code path: everything that is output, the proxy installation, /// status of the proxies. -fn install_bins_to_cargo_home() { - clitools::test(Scenario::SimpleV2, &|config| { - with_saved_path(&mut || { - config.expect_ok_contains( - &["rustup-init", "-y"], - for_host!( - r" +#[tokio::test] +async fn install_bins_to_cargo_home() { + let cx = CliTestContext::new(Scenario::SimpleV2).await; + #[cfg(windows)] + let _path_guard = RegistryGuard::new(&USER_PATH).unwrap(); + + cx.config + .expect_ok_contains( + &["rustup-init", "-y"], + for_host!( + r" stable-{0} installed - 1.1.0 (hash-stable-1.1.0) " - ), - for_host!( - r"info: syncing channel updates for 'stable-{0}' + ), + for_host!( + r"info: syncing channel updates for 'stable-{0}' info: latest update on 2015-01-02, rust version 1.1.0 (hash-stable-1.1.0) info: downloading component 'cargo' info: downloading component 'rust-docs' @@ -81,200 +81,224 @@ info: installing component 'rust-std' info: installing component 'rustc' info: default toolchain set to 'stable-{0}' " - ), - ); - #[cfg(windows)] - fn check(path: &Path) { - assert!(path.exists()); - } - #[cfg(not(windows))] - fn check(path: &Path) { - fn is_exe(path: &Path) -> bool { - use std::os::unix::fs::MetadataExt; - let mode = path.metadata().unwrap().mode(); - mode & 0o777 == 0o755 - } - assert!(is_exe(path)); - } - - for tool in TOOLS.iter().chain(DUP_TOOLS.iter()) { - let path = &config.cargodir.join(&format!("bin/{tool}{EXE_SUFFIX}")); - check(path); - } - }) - }); -} - -#[test] -fn install_twice() { - clitools::test(Scenario::SimpleV2, &|config| { - with_saved_path(&mut || { - config.expect_ok(&["rustup-init", "-y"]); - config.expect_ok(&["rustup-init", "-y"]); - let rustup = config.cargodir.join(format!("bin/rustup{EXE_SUFFIX}")); - assert!(rustup.exists()); - }) - }); -} - -#[test] + ), + ) + .await; + #[cfg(windows)] + fn check(path: &Path) { + assert!(path.exists()); + } + #[cfg(not(windows))] + fn check(path: &Path) { + fn is_exe(path: &Path) -> bool { + use std::os::unix::fs::MetadataExt; + let mode = path.metadata().unwrap().mode(); + mode & 0o777 == 0o755 + } + assert!(is_exe(path)); + } + + for tool in TOOLS.iter().chain(DUP_TOOLS.iter()) { + let path = &cx.config.cargodir.join(&format!("bin/{tool}{EXE_SUFFIX}")); + check(path); + } +} + +#[tokio::test] +async fn install_twice() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + #[cfg(windows)] + let _path_guard = RegistryGuard::new(&USER_PATH).unwrap(); + + cx.config.expect_ok(&["rustup-init", "-y"]).await; + cx.config.expect_ok(&["rustup-init", "-y"]).await; + let rustup = cx.config.cargodir.join(format!("bin/rustup{EXE_SUFFIX}")); + assert!(rustup.exists()); +} + /// Smoke test for the entire install process when dirs need to be made : /// depending just on unit tests here could miss subtle dependencies being added /// earlier in the code, so a black-box test is needed. -fn install_creates_cargo_home() { - clitools::test(Scenario::Empty, &|config| { - remove_dir_all(&config.cargodir).unwrap(); - config.rustupdir.remove().unwrap(); - config.expect_ok(&[ +#[tokio::test] +async fn install_creates_cargo_home() { + let mut cx = CliTestContext::new(Scenario::Empty).await; + remove_dir_all(&cx.config.cargodir).unwrap(); + cx.config.rustupdir.remove().unwrap(); + cx.config + .expect_ok(&[ "rustup-init", "-y", "--no-modify-path", "--default-toolchain", "none", - ]); - assert!(config.cargodir.exists()); - }); + ]) + .await; + assert!(cx.config.cargodir.exists()); } -#[test] /// Functional test needed here - we need to do the full dance where we start /// with rustup.exe and end up deleting that exe itself. -fn uninstall_deletes_bins() { - setup_empty_installed(&|config| { - // no-modify-path isn't needed here, as the test-dir-path isn't present - // in the registry, so the no-change code path will be triggered. - config.expect_ok(&["rustup", "self", "uninstall", "-y"]); - let rustup = config.cargodir.join(format!("bin/rustup{EXE_SUFFIX}")); - let rustc = config.cargodir.join(format!("bin/rustc{EXE_SUFFIX}")); - let rustdoc = config.cargodir.join(format!("bin/rustdoc{EXE_SUFFIX}")); - let cargo = config.cargodir.join(format!("bin/cargo{EXE_SUFFIX}")); - let rust_lldb = config.cargodir.join(format!("bin/rust-lldb{EXE_SUFFIX}")); - let rust_gdb = config.cargodir.join(format!("bin/rust-gdb{EXE_SUFFIX}")); - let rust_gdbgui = config.cargodir.join(format!("bin/rust-gdbgui{EXE_SUFFIX}")); - assert!(!rustup.exists()); - assert!(!rustc.exists()); - assert!(!rustdoc.exists()); - assert!(!cargo.exists()); - assert!(!rust_lldb.exists()); - assert!(!rust_gdb.exists()); - assert!(!rust_gdbgui.exists()); - }); -} - -#[test] -fn uninstall_works_if_some_bins_dont_exist() { - setup_empty_installed(&|config| { - let rustup = config.cargodir.join(format!("bin/rustup{EXE_SUFFIX}")); - let rustc = config.cargodir.join(format!("bin/rustc{EXE_SUFFIX}")); - let rustdoc = config.cargodir.join(format!("bin/rustdoc{EXE_SUFFIX}")); - let cargo = config.cargodir.join(format!("bin/cargo{EXE_SUFFIX}")); - let rust_lldb = config.cargodir.join(format!("bin/rust-lldb{EXE_SUFFIX}")); - let rust_gdb = config.cargodir.join(format!("bin/rust-gdb{EXE_SUFFIX}")); - let rust_gdbgui = config.cargodir.join(format!("bin/rust-gdbgui{EXE_SUFFIX}")); - - fs::remove_file(&rustc).unwrap(); - fs::remove_file(&cargo).unwrap(); - - config.expect_ok(&["rustup", "self", "uninstall", "-y"]); - - assert!(!rustup.exists()); - assert!(!rustc.exists()); - assert!(!rustdoc.exists()); - assert!(!cargo.exists()); - assert!(!rust_lldb.exists()); - assert!(!rust_gdb.exists()); - assert!(!rust_gdbgui.exists()); - }); -} - -#[test] -fn uninstall_deletes_rustup_home() { - setup_empty_installed(&|config| { - config.expect_ok(&["rustup", "self", "uninstall", "-y"]); - assert!(!config.rustupdir.has(".")); - }); -} - -#[test] -fn uninstall_works_if_rustup_home_doesnt_exist() { - setup_empty_installed(&|config| { - config.rustupdir.remove().unwrap(); - config.expect_ok(&["rustup", "self", "uninstall", "-y"]); - }); -} - -#[test] -fn uninstall_deletes_cargo_home() { - setup_empty_installed(&|config| { - config.expect_ok(&["rustup", "self", "uninstall", "-y"]); - assert!(!config.cargodir.exists()); - }); -} - -#[test] -fn uninstall_fails_if_not_installed() { - setup_empty_installed(&|config| { - let rustup = config.cargodir.join(format!("bin/rustup{EXE_SUFFIX}")); - fs::remove_file(rustup).unwrap(); - config.expect_err( +#[tokio::test] +async fn uninstall_deletes_bins() { + let mut cx = setup_empty_installed().await; + // no-modify-path isn't needed here, as the test-dir-path isn't present + // in the registry, so the no-change code path will be triggered. + cx.config + .expect_ok(&["rustup", "self", "uninstall", "-y"]) + .await; + let rustup = cx.config.cargodir.join(format!("bin/rustup{EXE_SUFFIX}")); + let rustc = cx.config.cargodir.join(format!("bin/rustc{EXE_SUFFIX}")); + let rustdoc = cx.config.cargodir.join(format!("bin/rustdoc{EXE_SUFFIX}")); + let cargo = cx.config.cargodir.join(format!("bin/cargo{EXE_SUFFIX}")); + let rust_lldb = cx + .config + .cargodir + .join(format!("bin/rust-lldb{EXE_SUFFIX}")); + let rust_gdb = cx.config.cargodir.join(format!("bin/rust-gdb{EXE_SUFFIX}")); + let rust_gdbgui = cx + .config + .cargodir + .join(format!("bin/rust-gdbgui{EXE_SUFFIX}")); + assert!(!rustup.exists()); + assert!(!rustc.exists()); + assert!(!rustdoc.exists()); + assert!(!cargo.exists()); + assert!(!rust_lldb.exists()); + assert!(!rust_gdb.exists()); + assert!(!rust_gdbgui.exists()); +} + +#[tokio::test] +async fn uninstall_works_if_some_bins_dont_exist() { + let mut cx = setup_empty_installed().await; + let rustup = cx.config.cargodir.join(format!("bin/rustup{EXE_SUFFIX}")); + let rustc = cx.config.cargodir.join(format!("bin/rustc{EXE_SUFFIX}")); + let rustdoc = cx.config.cargodir.join(format!("bin/rustdoc{EXE_SUFFIX}")); + let cargo = cx.config.cargodir.join(format!("bin/cargo{EXE_SUFFIX}")); + let rust_lldb = cx + .config + .cargodir + .join(format!("bin/rust-lldb{EXE_SUFFIX}")); + let rust_gdb = cx.config.cargodir.join(format!("bin/rust-gdb{EXE_SUFFIX}")); + let rust_gdbgui = cx + .config + .cargodir + .join(format!("bin/rust-gdbgui{EXE_SUFFIX}")); + + fs::remove_file(&rustc).unwrap(); + fs::remove_file(&cargo).unwrap(); + + cx.config + .expect_ok(&["rustup", "self", "uninstall", "-y"]) + .await; + + assert!(!rustup.exists()); + assert!(!rustc.exists()); + assert!(!rustdoc.exists()); + assert!(!cargo.exists()); + assert!(!rust_lldb.exists()); + assert!(!rust_gdb.exists()); + assert!(!rust_gdbgui.exists()); +} + +#[tokio::test] +async fn uninstall_deletes_rustup_home() { + let mut cx = setup_empty_installed().await; + cx.config + .expect_ok(&["rustup", "self", "uninstall", "-y"]) + .await; + assert!(!cx.config.rustupdir.has(".")); +} + +#[tokio::test] +async fn uninstall_works_if_rustup_home_doesnt_exist() { + let mut cx = setup_empty_installed().await; + cx.config.rustupdir.remove().unwrap(); + cx.config + .expect_ok(&["rustup", "self", "uninstall", "-y"]) + .await; +} + +#[tokio::test] +async fn uninstall_deletes_cargo_home() { + let mut cx = setup_empty_installed().await; + cx.config + .expect_ok(&["rustup", "self", "uninstall", "-y"]) + .await; + assert!(!cx.config.cargodir.exists()); +} + +#[tokio::test] +async fn uninstall_fails_if_not_installed() { + let cx = setup_empty_installed().await; + let rustup = cx.config.cargodir.join(format!("bin/rustup{EXE_SUFFIX}")); + fs::remove_file(rustup).unwrap(); + cx.config + .expect_err( &["rustup", "self", "uninstall", "-y"], "rustup is not installed", - ); - }); + ) + .await; } // The other tests here just run rustup from a temp directory. This // does the uninstall by actually invoking the installed binary in // order to test that it can successfully delete itself. -#[test] +#[tokio::test] #[cfg_attr(target_os = "macos", ignore)] // FIXME #1515 -fn uninstall_self_delete_works() { - setup_empty_installed(&|config| { - let rustup = config.cargodir.join(format!("bin/rustup{EXE_SUFFIX}")); - let mut cmd = Command::new(rustup.clone()); - cmd.args(["self", "uninstall", "-y"]); - clitools::env(config, &mut cmd); - let out = cmd.output().unwrap(); - println!("out: {}", String::from_utf8(out.stdout).unwrap()); - println!("err: {}", String::from_utf8(out.stderr).unwrap()); - - assert!(out.status.success()); - assert!(!rustup.exists()); - assert!(!config.cargodir.exists()); - - let rustc = config.cargodir.join(format!("bin/rustc{EXE_SUFFIX}")); - let rustdoc = config.cargodir.join(format!("bin/rustdoc{EXE_SUFFIX}")); - let cargo = config.cargodir.join(format!("bin/cargo{EXE_SUFFIX}")); - let rust_lldb = config.cargodir.join(format!("bin/rust-lldb{EXE_SUFFIX}")); - let rust_gdb = config.cargodir.join(format!("bin/rust-gdb{EXE_SUFFIX}")); - let rust_gdbgui = config.cargodir.join(format!("bin/rust-gdbgui{EXE_SUFFIX}")); - assert!(!rustc.exists()); - assert!(!rustdoc.exists()); - assert!(!cargo.exists()); - assert!(!rust_lldb.exists()); - assert!(!rust_gdb.exists()); - assert!(!rust_gdbgui.exists()); - }); +async fn uninstall_self_delete_works() { + let cx = setup_empty_installed().await; + let rustup = cx.config.cargodir.join(format!("bin/rustup{EXE_SUFFIX}")); + let mut cmd = Command::new(rustup.clone()); + cmd.args(["self", "uninstall", "-y"]); + clitools::env(&cx.config, &mut cmd); + let out = cmd.output().unwrap(); + println!("out: {}", String::from_utf8(out.stdout).unwrap()); + println!("err: {}", String::from_utf8(out.stderr).unwrap()); + + assert!(out.status.success()); + assert!(!rustup.exists()); + assert!(!cx.config.cargodir.exists()); + + let rustc = cx.config.cargodir.join(format!("bin/rustc{EXE_SUFFIX}")); + let rustdoc = cx.config.cargodir.join(format!("bin/rustdoc{EXE_SUFFIX}")); + let cargo = cx.config.cargodir.join(format!("bin/cargo{EXE_SUFFIX}")); + let rust_lldb = cx + .config + .cargodir + .join(format!("bin/rust-lldb{EXE_SUFFIX}")); + let rust_gdb = cx.config.cargodir.join(format!("bin/rust-gdb{EXE_SUFFIX}")); + let rust_gdbgui = cx + .config + .cargodir + .join(format!("bin/rust-gdbgui{EXE_SUFFIX}")); + assert!(!rustc.exists()); + assert!(!rustdoc.exists()); + assert!(!cargo.exists()); + assert!(!rust_lldb.exists()); + assert!(!rust_gdb.exists()); + assert!(!rust_gdbgui.exists()); } // On windows rustup self uninstall temporarily puts a rustup-gc-$randomnumber.exe // file in CONFIG.CARGODIR/.. ; check that it doesn't exist. -#[test] -fn uninstall_doesnt_leave_gc_file() { - setup_empty_installed(&|config| { - config.expect_ok(&["rustup", "self", "uninstall", "-y"]); - let parent = config.cargodir.parent().unwrap(); - - // The gc removal happens after rustup terminates. Typically under - // 100ms, but during the contention of test suites can be substantially - // longer while still succeeding. - - let check = || ensure_empty(parent); - match retry(Fibonacci::from_millis(1).map(jitter).take(23), check) { - Ok(_) => (), - Err(e) => panic!("{e}"), - } - }) +#[tokio::test] +async fn uninstall_doesnt_leave_gc_file() { + let mut cx = setup_empty_installed().await; + cx.config + .expect_ok(&["rustup", "self", "uninstall", "-y"]) + .await; + let parent = cx.config.cargodir.parent().unwrap(); + + // The gc removal happens after rustup terminates. Typically under + // 100ms, but during the contention of test suites can be substantially + // longer while still succeeding. + + let check = || ensure_empty(parent); + match retry(Fibonacci::from_millis(1).map(jitter).take(23), check) { + Ok(_) => (), + Err(e) => panic!("{e}"), + } } fn ensure_empty(dir: &Path) -> Result<(), GcErr> { @@ -292,161 +316,175 @@ fn ensure_empty(dir: &Path) -> Result<(), GcErr> { #[error("garbage remaining: {:?}", .0)] struct GcErr(Vec); -#[test] -fn update_exact() { +#[tokio::test] +async fn update_exact() { let version = env!("CARGO_PKG_VERSION"); let expected_output = "info: checking for self-update info: downloading self-update " .to_string(); - update_setup(&|config, _| { - config.expect_ok(&["rustup-init", "-y", "--no-modify-path"]); - config.expect_ok_ex( + let mut cx = SelfUpdateTestContext::new(TEST_VERSION).await; + cx.config + .expect_ok(&["rustup-init", "-y", "--no-modify-path"]) + .await; + cx.config + .expect_ok_ex( &["rustup", "self", "update"], &format!(" rustup updated - {version} (from {version})\n\n",), &expected_output, - ); - }); + ) + .await; } -#[test] #[cfg(windows)] -fn update_overwrites_programs_display_version() { - let root = &winreg::RegKey::predef(winreg::enums::HKEY_CURRENT_USER); - let key = r"Software\Microsoft\Windows\CurrentVersion\Uninstall\Rustup"; - let name = "DisplayVersion"; - +#[tokio::test] +async fn update_overwrites_programs_display_version() { const PLACEHOLDER_VERSION: &str = "9.999.99"; let version = env!("CARGO_PKG_VERSION"); - update_setup(&|config, _| { - with_saved_reg_value(root, key, name, &mut || { - config.expect_ok(&["rustup-init", "-y", "--no-modify-path"]); - - root.create_subkey(key) - .unwrap() - .0 - .set_value(name, &PLACEHOLDER_VERSION) - .unwrap(); - - config.expect_ok(&["rustup", "self", "update"]); - - assert_eq!( - root.open_subkey(key) - .unwrap() - .get_value::(name) - .unwrap(), - version, - ); - }); - }); -} - -#[test] -fn update_but_not_installed() { - update_setup(&|config, _| { - config.expect_err_ex( + let mut cx = SelfUpdateTestContext::new(TEST_VERSION).await; + let _guard = RegistryGuard::new(&USER_RUSTUP_VERSION).unwrap(); + cx.config + .expect_ok(&["rustup-init", "-y", "--no-modify-path"]) + .await; + + USER_RUSTUP_VERSION + .set_value(Some(PLACEHOLDER_VERSION)) + .unwrap(); + cx.config.expect_ok(&["rustup", "self", "update"]).await; + assert_eq!( + USER_RUSTUP_VERSION.get_value::().unwrap().unwrap(), + version, + ); +} + +#[cfg(windows)] +const USER_RUSTUP_VERSION: RegistryValueId = RegistryValueId { + sub_key: r"Software\Microsoft\Windows\CurrentVersion\Uninstall\Rustup", + value_name: "DisplayVersion", +}; + +#[tokio::test] +async fn update_but_not_installed() { + let cx = SelfUpdateTestContext::new(TEST_VERSION).await; + cx.config + .expect_err_ex( &["rustup", "self", "update"], r"", &format!( r"error: rustup is not installed at '{}' ", - config.cargodir.display() + cx.config.cargodir.display() ), - ); - }); + ) + .await; } -#[test] -fn update_but_delete_existing_updater_first() { - update_setup(&|config, _| { - // The updater is stored in a known location - let setup = config.cargodir.join(format!("bin/rustup-init{EXE_SUFFIX}")); +#[tokio::test] +async fn update_but_delete_existing_updater_first() { + let mut cx = SelfUpdateTestContext::new(TEST_VERSION).await; + // The updater is stored in a known location + let setup = cx + .config + .cargodir + .join(format!("bin/rustup-init{EXE_SUFFIX}")); - config.expect_ok(&["rustup-init", "-y", "--no-modify-path"]); + cx.config + .expect_ok(&["rustup-init", "-y", "--no-modify-path"]) + .await; - // If it happens to already exist for some reason it - // should just be deleted. - raw::write_file(&setup, "").unwrap(); - config.expect_ok(&["rustup", "self", "update"]); + // If it happens to already exist for some reason it + // should just be deleted. + raw::write_file(&setup, "").unwrap(); + cx.config.expect_ok(&["rustup", "self", "update"]).await; - let rustup = config.cargodir.join(format!("bin/rustup{EXE_SUFFIX}")); - assert!(rustup.exists()); - }); + let rustup = cx.config.cargodir.join(format!("bin/rustup{EXE_SUFFIX}")); + assert!(rustup.exists()); } -#[test] -fn update_download_404() { - update_setup(&|config, self_dist| { - config.expect_ok(&["rustup-init", "-y", "--no-modify-path"]); +#[tokio::test] +async fn update_download_404() { + let mut cx = SelfUpdateTestContext::new(TEST_VERSION).await; + cx.config + .expect_ok(&["rustup-init", "-y", "--no-modify-path"]) + .await; - let trip = this_host_triple(); - let dist_dir = self_dist.join(format!("archive/{TEST_VERSION}/{trip}")); - let dist_exe = dist_dir.join(format!("rustup-init{EXE_SUFFIX}")); + let trip = this_host_triple(); + let dist_dir = cx.path().join(format!("archive/{TEST_VERSION}/{trip}")); + let dist_exe = dist_dir.join(format!("rustup-init{EXE_SUFFIX}")); - fs::remove_file(dist_exe).unwrap(); + fs::remove_file(dist_exe).unwrap(); - config.expect_err(&["rustup", "self", "update"], "could not download file"); - }); + cx.config + .expect_err(&["rustup", "self", "update"], "could not download file") + .await; } -#[test] -fn update_bogus_version() { - update_setup(&|config, _| { - config.expect_ok(&["rustup-init", "-y", "--no-modify-path"]); - config.expect_err( - &["rustup", "update", "1.0.0-alpha"], - "invalid value '1.0.0-alpha' for '[TOOLCHAIN]...': invalid toolchain name: '1.0.0-alpha'", - ); - }); +#[tokio::test] +async fn update_bogus_version() { + let mut cx = SelfUpdateTestContext::new(TEST_VERSION).await; + cx.config + .expect_ok(&["rustup-init", "-y", "--no-modify-path"]) + .await; + cx.config.expect_err( + &["rustup", "update", "1.0.0-alpha"], + "invalid value '1.0.0-alpha' for '[TOOLCHAIN]...': invalid toolchain name: '1.0.0-alpha'", + ).await; } // Check that rustup.exe has changed after the update. This // is hard for windows because the running process needs to exit // before the new updater can delete it. -#[test] -fn update_updates_rustup_bin() { - update_setup(&|config, _| { - config.expect_ok(&["rustup-init", "-y", "--no-modify-path"]); +#[tokio::test] +async fn update_updates_rustup_bin() { + let mut cx = SelfUpdateTestContext::new(TEST_VERSION).await; + cx.config + .expect_ok(&["rustup-init", "-y", "--no-modify-path"]) + .await; - let bin = config.cargodir.join(format!("bin/rustup{EXE_SUFFIX}")); - let before_hash = calc_hash(&bin); + let bin = cx.config.cargodir.join(format!("bin/rustup{EXE_SUFFIX}")); + let before_hash = calc_hash(&bin); - // Running the self update command on the installed binary, - // so that the running binary must be replaced. - let mut cmd = Command::new(&bin); - cmd.args(["self", "update"]); - clitools::env(config, &mut cmd); - let out = cmd.output().unwrap(); + // Running the self update command on the installed binary, + // so that the running binary must be replaced. + let mut cmd = Command::new(&bin); + cmd.args(["self", "update"]); + clitools::env(&cx.config, &mut cmd); + let out = cmd.output().unwrap(); - println!("out: {}", String::from_utf8(out.stdout).unwrap()); - println!("err: {}", String::from_utf8(out.stderr).unwrap()); + println!("out: {}", String::from_utf8(out.stdout).unwrap()); + println!("err: {}", String::from_utf8(out.stderr).unwrap()); - assert!(out.status.success()); + assert!(out.status.success()); - let after_hash = calc_hash(&bin); + let after_hash = calc_hash(&bin); - assert_ne!(before_hash, after_hash); - }); + assert_ne!(before_hash, after_hash); } -#[test] -fn update_bad_schema() { - update_setup(&|config, self_dist| { - config.expect_ok(&["rustup-init", "-y", "--no-modify-path"]); - output_release_file(self_dist, "17", "1.1.1"); - config.expect_err(&["rustup", "self", "update"], "unknown variant"); - }); +#[tokio::test] +async fn update_bad_schema() { + let mut cx = SelfUpdateTestContext::new(TEST_VERSION).await; + cx.config + .expect_ok(&["rustup-init", "-y", "--no-modify-path"]) + .await; + output_release_file(cx.path(), "17", "1.1.1"); + cx.config + .expect_err(&["rustup", "self", "update"], "unknown variant") + .await; } -#[test] -fn update_no_change() { +#[tokio::test] +async fn update_no_change() { let version = env!("CARGO_PKG_VERSION"); - update_setup(&|config, self_dist| { - config.expect_ok(&["rustup-init", "-y", "--no-modify-path"]); - output_release_file(self_dist, "1", version); - config.expect_ok_ex( + let mut cx = SelfUpdateTestContext::new(TEST_VERSION).await; + cx.config + .expect_ok(&["rustup-init", "-y", "--no-modify-path"]) + .await; + output_release_file(cx.path(), "1", version); + cx.config + .expect_ok_ex( &["rustup", "self", "update"], &format!( r" rustup unchanged - {version} @@ -455,67 +493,79 @@ fn update_no_change() { ), r"info: checking for self-update ", - ); - }); + ) + .await; } -#[test] -fn rustup_self_updates_trivial() { - update_setup(&|config, _| { - config.expect_ok(&["rustup", "set", "auto-self-update", "enable"]); - config.expect_ok(&["rustup-init", "-y", "--no-modify-path"]); +#[tokio::test] +async fn rustup_self_updates_trivial() { + let mut cx = SelfUpdateTestContext::new(TEST_VERSION).await; + cx.config + .expect_ok(&["rustup", "set", "auto-self-update", "enable"]) + .await; + cx.config + .expect_ok(&["rustup-init", "-y", "--no-modify-path"]) + .await; - let bin = config.cargodir.join(format!("bin/rustup{EXE_SUFFIX}")); - let before_hash = calc_hash(&bin); + let bin = cx.config.cargodir.join(format!("bin/rustup{EXE_SUFFIX}")); + let before_hash = calc_hash(&bin); - config.expect_ok(&["rustup", "update"]); + cx.config.expect_ok(&["rustup", "update"]).await; - let after_hash = calc_hash(&bin); + let after_hash = calc_hash(&bin); - assert_ne!(before_hash, after_hash); - }) + assert_ne!(before_hash, after_hash); } -#[test] -fn rustup_self_updates_with_specified_toolchain() { - update_setup(&|config, _| { - config.expect_ok(&["rustup", "set", "auto-self-update", "enable"]); - config.expect_ok(&["rustup-init", "-y", "--no-modify-path"]); +#[tokio::test] +async fn rustup_self_updates_with_specified_toolchain() { + let mut cx = SelfUpdateTestContext::new(TEST_VERSION).await; + cx.config + .expect_ok(&["rustup", "set", "auto-self-update", "enable"]) + .await; + cx.config + .expect_ok(&["rustup-init", "-y", "--no-modify-path"]) + .await; - let bin = config.cargodir.join(format!("bin/rustup{EXE_SUFFIX}")); - let before_hash = calc_hash(&bin); + let bin = cx.config.cargodir.join(format!("bin/rustup{EXE_SUFFIX}")); + let before_hash = calc_hash(&bin); - config.expect_ok(&["rustup", "update", "stable"]); + cx.config.expect_ok(&["rustup", "update", "stable"]).await; - let after_hash = calc_hash(&bin); + let after_hash = calc_hash(&bin); - assert_ne!(before_hash, after_hash); - }) + assert_ne!(before_hash, after_hash); } -#[test] -fn rustup_no_self_update_with_specified_toolchain() { - update_setup(&|config, _| { - config.expect_ok(&["rustup-init", "-y", "--no-modify-path"]); +#[tokio::test] +async fn rustup_no_self_update_with_specified_toolchain() { + let mut cx = SelfUpdateTestContext::new(TEST_VERSION).await; + cx.config + .expect_ok(&["rustup-init", "-y", "--no-modify-path"]) + .await; - let bin = config.cargodir.join(format!("bin/rustup{EXE_SUFFIX}")); - let before_hash = calc_hash(&bin); + let bin = cx.config.cargodir.join(format!("bin/rustup{EXE_SUFFIX}")); + let before_hash = calc_hash(&bin); - config.expect_ok(&["rustup", "update", "stable"]); + cx.config.expect_ok(&["rustup", "update", "stable"]).await; - let after_hash = calc_hash(&bin); + let after_hash = calc_hash(&bin); - assert_eq!(before_hash, after_hash); - }) + assert_eq!(before_hash, after_hash); } -#[test] -fn rustup_self_update_exact() { - update_setup(&|config, _| { - config.expect_ok(&["rustup", "set", "auto-self-update", "enable"]); - config.expect_ok(&["rustup-init", "-y", "--no-modify-path"]); +#[tokio::test] +async fn rustup_self_update_exact() { + let mut cx = SelfUpdateTestContext::new(TEST_VERSION).await; + cx.config + .expect_ok(&["rustup", "set", "auto-self-update", "enable"]) + .await; + cx.config + .expect_ok(&["rustup-init", "-y", "--no-modify-path"]) + .await; - config.expect_ok_ex( + cx.config + .expect_ok_ex( &["rustup", "update"], for_host!( r" @@ -530,87 +580,106 @@ info: downloading self-update info: cleaning up downloads & tmp directories " ), - ); - }) + ) + .await; } // Because self-delete on windows is hard, rustup-init doesn't // do it. It instead leaves itself installed for cleanup by later // invocations of rustup. -#[test] -fn updater_leaves_itself_for_later_deletion() { - update_setup(&|config, _| { - config.expect_ok(&["rustup-init", "-y", "--no-modify-path"]); - config.expect_ok(&["rustup", "update", "nightly"]); - config.expect_ok(&["rustup", "self", "update"]); - - let setup = config.cargodir.join(format!("bin/rustup-init{EXE_SUFFIX}")); - assert!(setup.exists()); - }); -} - -#[test] -fn updater_is_deleted_after_running_rustup() { - update_setup(&|config, _| { - config.expect_ok(&["rustup-init", "-y", "--no-modify-path"]); - config.expect_ok(&["rustup", "update", "nightly"]); - config.expect_ok(&["rustup", "self", "update"]); - - config.expect_ok(&["rustup", "update", "nightly"]); - - let setup = config.cargodir.join(format!("bin/rustup-init{EXE_SUFFIX}")); - assert!(!setup.exists()); - }); -} - -#[test] -fn updater_is_deleted_after_running_rustc() { - update_setup(&|config, _| { - config.expect_ok(&["rustup-init", "-y", "--no-modify-path"]); - config.expect_ok(&["rustup", "default", "nightly"]); - config.expect_ok(&["rustup", "self", "update"]); - - config.expect_ok(&["rustc", "--version"]); - - let setup = config.cargodir.join(format!("bin/rustup-init{EXE_SUFFIX}")); - assert!(!setup.exists()); - }); -} - -#[test] -fn rustup_still_works_after_update() { - update_setup(&|config, _| { - config.expect_ok(&["rustup-init", "-y", "--no-modify-path"]); - config.expect_ok(&["rustup", "default", "nightly"]); - config.expect_ok(&["rustup", "self", "update"]); - config.expect_stdout_ok(&["rustc", "--version"], "hash-nightly-2"); - config.expect_ok(&["rustup", "default", "beta"]); - config.expect_stdout_ok(&["rustc", "--version"], "hash-beta-1.2.0"); - }); +#[tokio::test] +async fn updater_leaves_itself_for_later_deletion() { + let mut cx = SelfUpdateTestContext::new(TEST_VERSION).await; + cx.config + .expect_ok(&["rustup-init", "-y", "--no-modify-path"]) + .await; + cx.config.expect_ok(&["rustup", "update", "nightly"]).await; + cx.config.expect_ok(&["rustup", "self", "update"]).await; + + let setup = cx + .config + .cargodir + .join(format!("bin/rustup-init{EXE_SUFFIX}")); + assert!(setup.exists()); +} + +#[tokio::test] +async fn updater_is_deleted_after_running_rustup() { + let mut cx = SelfUpdateTestContext::new(TEST_VERSION).await; + cx.config + .expect_ok(&["rustup-init", "-y", "--no-modify-path"]) + .await; + cx.config.expect_ok(&["rustup", "update", "nightly"]).await; + cx.config.expect_ok(&["rustup", "self", "update"]).await; + + cx.config.expect_ok(&["rustup", "update", "nightly"]).await; + + let setup = cx + .config + .cargodir + .join(format!("bin/rustup-init{EXE_SUFFIX}")); + assert!(!setup.exists()); +} + +#[tokio::test] +async fn updater_is_deleted_after_running_rustc() { + let mut cx = SelfUpdateTestContext::new(TEST_VERSION).await; + cx.config + .expect_ok(&["rustup-init", "-y", "--no-modify-path"]) + .await; + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + cx.config.expect_ok(&["rustup", "self", "update"]).await; + + cx.config.expect_ok(&["rustc", "--version"]).await; + + let setup = cx + .config + .cargodir + .join(format!("bin/rustup-init{EXE_SUFFIX}")); + assert!(!setup.exists()); +} + +#[tokio::test] +async fn rustup_still_works_after_update() { + let mut cx = SelfUpdateTestContext::new(TEST_VERSION).await; + cx.config + .expect_ok(&["rustup-init", "-y", "--no-modify-path"]) + .await; + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + cx.config.expect_ok(&["rustup", "self", "update"]).await; + cx.config + .expect_stdout_ok(&["rustc", "--version"], "hash-nightly-2") + .await; + cx.config.expect_ok(&["rustup", "default", "beta"]).await; + cx.config + .expect_stdout_ok(&["rustc", "--version"], "hash-beta-1.2.0") + .await; } // The installer used to be called rustup-setup. For compatibility it // still needs to work in that mode. -#[test] -fn as_rustup_setup() { - clitools::test(Scenario::Empty, &|config| { - let init = config.exedir.join(format!("rustup-init{EXE_SUFFIX}")); - let setup = config.exedir.join(format!("rustup-setup{EXE_SUFFIX}")); - fs::copy(init, setup).unwrap(); - config.expect_ok(&[ +#[tokio::test] +async fn as_rustup_setup() { + let mut cx = CliTestContext::new(Scenario::Empty).await; + let init = cx.config.exedir.join(format!("rustup-init{EXE_SUFFIX}")); + let setup = cx.config.exedir.join(format!("rustup-setup{EXE_SUFFIX}")); + fs::copy(init, setup).unwrap(); + cx.config + .expect_ok(&[ "rustup-setup", "-y", "--no-modify-path", "--default-toolchain", "none", - ]); - }); + ]) + .await; } -#[test] -fn reinstall_exact() { - setup_empty_installed(&|config| { - config.expect_stderr_ok( +#[tokio::test] +async fn reinstall_exact() { + let cx = setup_empty_installed().await; + cx.config + .expect_stderr_ok( &[ "rustup-init", "-y", @@ -618,14 +687,15 @@ fn reinstall_exact() { "--no-modify-path", ], r"info: updating existing rustup installation - leaving toolchains alone", - ); - }); + ) + .await; } -#[test] -fn reinstall_specifying_toolchain() { - setup_installed(&|config| { - config.expect_stdout_ok( +#[tokio::test] +async fn reinstall_specifying_toolchain() { + let cx = setup_installed().await; + cx.config + .expect_stdout_ok( &[ "rustup-init", "-y", @@ -633,15 +703,18 @@ fn reinstall_specifying_toolchain() { "--no-modify-path", ], for_host!(r"stable-{0} unchanged - 1.1.0"), - ); - }); -} - -#[test] -fn reinstall_specifying_component() { - setup_installed(&|config| { - config.expect_ok(&["rustup", "component", "add", "rls"]); - config.expect_stdout_ok( + ) + .await; +} + +#[tokio::test] +async fn reinstall_specifying_component() { + let mut cx = setup_installed().await; + cx.config + .expect_ok(&["rustup", "component", "add", "rls"]) + .await; + cx.config + .expect_stdout_ok( &[ "rustup-init", "-y", @@ -649,14 +722,15 @@ fn reinstall_specifying_component() { "--no-modify-path", ], for_host!(r"stable-{0} unchanged - 1.1.0"), - ); - }); + ) + .await; } -#[test] -fn reinstall_specifying_different_toolchain() { - clitools::test(Scenario::SimpleV2, &|config| { - config.expect_stderr_ok( +#[tokio::test] +async fn reinstall_specifying_different_toolchain() { + let cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config + .expect_stderr_ok( &[ "rustup-init", "-y", @@ -664,66 +738,79 @@ fn reinstall_specifying_different_toolchain() { "--no-modify-path", ], for_host!(r"info: default toolchain set to 'nightly-{0}'"), - ); - }); + ) + .await; } -#[test] -fn install_sets_up_stable_unless_a_different_default_is_requested() { - clitools::test(Scenario::SimpleV2, &|config| { - config.expect_ok(&[ +#[tokio::test] +async fn install_sets_up_stable_unless_a_different_default_is_requested() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config + .expect_ok(&[ "rustup-init", "-y", "--default-toolchain", "nightly", "--no-modify-path", - ]); - config.expect_stdout_ok(&["rustc", "--version"], "hash-nightly-2"); - }); -} - -#[test] -fn install_sets_up_stable_unless_there_is_already_a_default() { - setup_installed(&|config| { - config.expect_ok(&["rustup", "default", "nightly"]); - config.expect_ok(&["rustup", "toolchain", "remove", "stable"]); - config.expect_ok(&["rustup-init", "-y", "--no-modify-path"]); - config.expect_stdout_ok(&["rustc", "--version"], "hash-nightly-2"); - config.expect_err( + ]) + .await; + cx.config + .expect_stdout_ok(&["rustc", "--version"], "hash-nightly-2") + .await; +} + +#[tokio::test] +async fn install_sets_up_stable_unless_there_is_already_a_default() { + let mut cx = setup_installed().await; + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + cx.config + .expect_ok(&["rustup", "toolchain", "remove", "stable"]) + .await; + cx.config + .expect_ok(&["rustup-init", "-y", "--no-modify-path"]) + .await; + cx.config + .expect_stdout_ok(&["rustc", "--version"], "hash-nightly-2") + .await; + cx.config + .expect_err( &["rustup", "run", "stable", "rustc", "--version"], for_host!("toolchain 'stable-{0}' is not installed"), - ); - }); + ) + .await; } -#[test] -fn readline_no_stdin() { - clitools::test(Scenario::SimpleV2, &|config| { - config.expect_err( +#[tokio::test] +async fn readline_no_stdin() { + let cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config + .expect_err( &["rustup-init", "--no-modify-path"], "unable to read from stdin for confirmation", - ); - }); + ) + .await; } -#[test] -fn rustup_init_works_with_weird_names() { +#[tokio::test] +async fn rustup_init_works_with_weird_names() { // Browsers often rename bins to e.g. rustup-init(2).exe. - clitools::test(Scenario::SimpleV2, &|config| { - let old = config.exedir.join(format!("rustup-init{EXE_SUFFIX}")); - let new = config.exedir.join(format!("rustup-init(2){EXE_SUFFIX}")); - fs::rename(old, new).unwrap(); - config.expect_ok(&["rustup-init(2)", "-y", "--no-modify-path"]); - let rustup = config.cargodir.join(format!("bin/rustup{EXE_SUFFIX}")); - assert!(rustup.exists()); - }); -} - -#[test] -fn install_but_rustup_sh_is_installed() { - clitools::test(Scenario::Empty, &|config| { - config.create_rustup_sh_metadata(); - config.expect_stderr_ok( + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + let old = cx.config.exedir.join(format!("rustup-init{EXE_SUFFIX}")); + let new = cx.config.exedir.join(format!("rustup-init(2){EXE_SUFFIX}")); + fs::rename(old, new).unwrap(); + cx.config + .expect_ok(&["rustup-init(2)", "-y", "--no-modify-path"]) + .await; + let rustup = cx.config.cargodir.join(format!("bin/rustup{EXE_SUFFIX}")); + assert!(rustup.exists()); +} + +#[tokio::test] +async fn install_but_rustup_sh_is_installed() { + let cx = CliTestContext::new(Scenario::Empty).await; + cx.config.create_rustup_sh_metadata(); + cx.config + .expect_stderr_ok( &[ "rustup-init", "-y", @@ -732,138 +819,159 @@ fn install_but_rustup_sh_is_installed() { "--no-modify-path", ], "cannot install while rustup.sh is installed", - ); - }); -} - -#[test] -fn test_warn_succeed_if_rustup_sh_already_installed_y_flag() { - clitools::test(Scenario::SimpleV2, &|config| { - config.create_rustup_sh_metadata(); - let out = config.run("rustup-init", ["-y", "--no-modify-path"], &[]); - assert!(out.ok); - assert!(out - .stderr - .contains("warn: it looks like you have existing rustup.sh metadata")); - assert!(out - .stderr - .contains("error: cannot install while rustup.sh is installed")); - assert!(out - .stderr - .contains("warn: continuing (because the -y flag is set and the error is ignorable)")); - assert!(!out.stdout.contains("Continue? (y/N)")); - }) -} - -#[test] -fn test_succeed_if_rustup_sh_already_installed_env_var_set() { - clitools::test(Scenario::SimpleV2, &|config| { - config.create_rustup_sh_metadata(); - let out = config.run( + ) + .await; +} + +#[tokio::test] +async fn test_warn_succeed_if_rustup_sh_already_installed_y_flag() { + let cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.create_rustup_sh_metadata(); + let out = cx + .config + .run("rustup-init", ["-y", "--no-modify-path"], &[]) + .await; + assert!(out.ok); + assert!(out + .stderr + .contains("warn: it looks like you have existing rustup.sh metadata")); + assert!(out + .stderr + .contains("error: cannot install while rustup.sh is installed")); + assert!(out + .stderr + .contains("warn: continuing (because the -y flag is set and the error is ignorable)")); + assert!(!out.stdout.contains("Continue? (y/N)")); +} + +#[tokio::test] +async fn test_succeed_if_rustup_sh_already_installed_env_var_set() { + let cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.create_rustup_sh_metadata(); + let out = cx + .config + .run( "rustup-init", ["-y", "--no-modify-path"], &[("RUSTUP_INIT_SKIP_EXISTENCE_CHECKS", "yes")], - ); - assert!(out.ok); - assert!(!out - .stderr - .contains("warn: it looks like you have existing rustup.sh metadata")); - assert!(!out - .stderr - .contains("error: cannot install while rustup.sh is installed")); - assert!(!out - .stderr - .contains("warn: continuing (because the -y flag is set and the error is ignorable)")); - assert!(!out.stdout.contains("Continue? (y/N)")); - }) -} - -#[test] -fn rls_proxy_set_up_after_install() { - clitools::test(Scenario::None, &|config| { - config.with_scenario(Scenario::SimpleV2, &|config| { - config.expect_ok(&["rustup-init", "-y", "--no-modify-path"]); - }); - config.expect_err( + ) + .await; + assert!(out.ok); + assert!(!out + .stderr + .contains("warn: it looks like you have existing rustup.sh metadata")); + assert!(!out + .stderr + .contains("error: cannot install while rustup.sh is installed")); + assert!(!out + .stderr + .contains("warn: continuing (because the -y flag is set and the error is ignorable)")); + assert!(!out.stdout.contains("Continue? (y/N)")); +} + +#[tokio::test] +async fn rls_proxy_set_up_after_install() { + let mut cx = CliTestContext::new(Scenario::None).await; + + { + let mut cx = cx.with_dist_dir(Scenario::SimpleV2); + cx.config + .expect_ok(&["rustup-init", "-y", "--no-modify-path"]) + .await; + } + + cx.config + .expect_err( &["rls", "--version"], &format!( "'rls{}' is not installed for the toolchain 'stable-{}'", EXE_SUFFIX, this_host_triple(), ), - ); - config.expect_ok(&["rustup", "component", "add", "rls"]); - config.expect_ok(&["rls", "--version"]); - }); -} - -#[test] -fn rls_proxy_set_up_after_update() { - update_setup(&|config, _| { - let rls_path = config.cargodir.join(format!("bin/rls{EXE_SUFFIX}")); - config.expect_ok(&["rustup-init", "-y", "--no-modify-path"]); - fs::remove_file(&rls_path).unwrap(); - config.expect_ok(&["rustup", "self", "update"]); - assert!(rls_path.exists()); - }); -} - -#[test] -fn update_does_not_overwrite_rustfmt() { - update_setup(&|config, self_dist| { - config.expect_ok(&["rustup-init", "-y", "--no-modify-path"]); - let version = env!("CARGO_PKG_VERSION"); - output_release_file(self_dist, "1", version); - - // Since we just did a fresh install rustfmt will exist. Let's emulate - // it not existing in this test though by removing it just after our - // installation. - let rustfmt_path = config.cargodir.join(format!("bin/rustfmt{EXE_SUFFIX}")); - assert!(rustfmt_path.exists()); - fs::remove_file(&rustfmt_path).unwrap(); - raw::write_file(&rustfmt_path, "").unwrap(); - assert_eq!(utils::file_size(&rustfmt_path).unwrap(), 0); - - // Ok, now a self-update should complain about `rustfmt` not looking - // like rustup and the user should take some action. - config.expect_stderr_ok( + ) + .await; + cx.config + .expect_ok(&["rustup", "component", "add", "rls"]) + .await; + cx.config.expect_ok(&["rls", "--version"]).await; +} + +#[tokio::test] +async fn rls_proxy_set_up_after_update() { + let mut cx = SelfUpdateTestContext::new(TEST_VERSION).await; + let rls_path = cx.config.cargodir.join(format!("bin/rls{EXE_SUFFIX}")); + cx.config + .expect_ok(&["rustup-init", "-y", "--no-modify-path"]) + .await; + fs::remove_file(&rls_path).unwrap(); + cx.config.expect_ok(&["rustup", "self", "update"]).await; + assert!(rls_path.exists()); +} + +#[tokio::test] +async fn update_does_not_overwrite_rustfmt() { + let mut cx = SelfUpdateTestContext::new(TEST_VERSION).await; + cx.config + .expect_ok(&["rustup-init", "-y", "--no-modify-path"]) + .await; + let version = env!("CARGO_PKG_VERSION"); + output_release_file(cx.path(), "1", version); + + // Since we just did a fresh install rustfmt will exist. Let's emulate + // it not existing in this test though by removing it just after our + // installation. + let rustfmt_path = cx.config.cargodir.join(format!("bin/rustfmt{EXE_SUFFIX}")); + assert!(rustfmt_path.exists()); + fs::remove_file(&rustfmt_path).unwrap(); + raw::write_file(&rustfmt_path, "").unwrap(); + assert_eq!(utils::file_size(&rustfmt_path).unwrap(), 0); + + // Ok, now a self-update should complain about `rustfmt` not looking + // like rustup and the user should take some action. + cx.config + .expect_stderr_ok( &["rustup", "self", "update"], "`rustfmt` is already installed", - ); - assert!(rustfmt_path.exists()); - assert_eq!(utils::file_size(&rustfmt_path).unwrap(), 0); - - // Now simulate us removing the rustfmt executable and rerunning a self - // update, this should install the rustup shim. Note that we don't run - // `rustup` here but rather the rustup we've actually installed, this'll - // help reproduce bugs related to having that file being opened by the - // current process. - fs::remove_file(&rustfmt_path).unwrap(); - let installed_rustup = config.cargodir.join("bin/rustup"); - config.expect_ok(&[installed_rustup.to_str().unwrap(), "self", "update"]); - assert!(rustfmt_path.exists()); - assert!(utils::file_size(&rustfmt_path).unwrap() > 0); - }); -} - -#[test] -fn update_installs_clippy_cargo_and() { - update_setup(&|config, self_dist| { - config.expect_ok(&["rustup-init", "-y", "--no-modify-path"]); - let version = env!("CARGO_PKG_VERSION"); - output_release_file(self_dist, "1", version); - - let cargo_clippy_path = config - .cargodir - .join(format!("bin/cargo-clippy{EXE_SUFFIX}")); - assert!(cargo_clippy_path.exists()); - }); -} - -#[test] -fn install_with_components_and_targets() { - clitools::test(Scenario::SimpleV2, &|config| { - config.expect_ok(&[ + ) + .await; + assert!(rustfmt_path.exists()); + assert_eq!(utils::file_size(&rustfmt_path).unwrap(), 0); + + // Now simulate us removing the rustfmt executable and rerunning a self + // update, this should install the rustup shim. Note that we don't run + // `rustup` here but rather the rustup we've actually installed, this'll + // help reproduce bugs related to having that file being opened by the + // current process. + fs::remove_file(&rustfmt_path).unwrap(); + let installed_rustup = cx.config.cargodir.join("bin/rustup"); + cx.config + .expect_ok(&[installed_rustup.to_str().unwrap(), "self", "update"]) + .await; + assert!(rustfmt_path.exists()); + assert!(utils::file_size(&rustfmt_path).unwrap() > 0); +} + +#[tokio::test] +async fn update_installs_clippy_cargo_and() { + let mut cx = SelfUpdateTestContext::new(TEST_VERSION).await; + cx.config + .expect_ok(&["rustup-init", "-y", "--no-modify-path"]) + .await; + let version = env!("CARGO_PKG_VERSION"); + output_release_file(cx.path(), "1", version); + + let cargo_clippy_path = cx + .config + .cargodir + .join(format!("bin/cargo-clippy{EXE_SUFFIX}")); + assert!(cargo_clippy_path.exists()); +} + +#[tokio::test] +async fn install_with_components_and_targets() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config + .expect_ok(&[ "rustup-init", "--default-toolchain", "nightly", @@ -873,31 +981,36 @@ fn install_with_components_and_targets() { "-t", clitools::CROSS_ARCH1, "--no-modify-path", - ]); - config.expect_stdout_ok( + ]) + .await; + cx.config + .expect_stdout_ok( &["rustup", "target", "list"], &format!("{} (installed)", clitools::CROSS_ARCH1), - ); - config.expect_stdout_ok( + ) + .await; + cx.config + .expect_stdout_ok( &["rustup", "component", "list"], &format!("rls-{} (installed)", this_host_triple()), - ); - }) + ) + .await; } -#[test] -fn install_minimal_profile() { - clitools::test(Scenario::SimpleV2, &|config| { - config.expect_ok(&[ +#[tokio::test] +async fn install_minimal_profile() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config + .expect_ok(&[ "rustup-init", "-y", "--profile", "minimal", "--no-modify-path", - ]); + ]) + .await; - config.expect_component_executable("rustup"); - config.expect_component_executable("rustc"); - config.expect_component_not_executable("cargo"); - }); + cx.config.expect_component_executable("rustup").await; + cx.config.expect_component_executable("rustc").await; + cx.config.expect_component_not_executable("cargo").await; } diff --git a/tests/suite/cli_v1.rs b/tests/suite/cli_v1.rs index dcc916b302..78eb492545 100644 --- a/tests/suite/cli_v1.rs +++ b/tests/suite/cli_v1.rs @@ -4,359 +4,482 @@ use std::fs; use rustup::for_host; -use rustup::test::mock::clitools::{self, set_current_dist_date, Config, Scenario}; +use rustup::test::mock::clitools::{set_current_dist_date, CliTestContext, Scenario}; -pub fn setup(f: &dyn Fn(&mut Config)) { - clitools::test(Scenario::SimpleV1, f); -} - -#[test] -fn rustc_no_default_toolchain() { - setup(&|config| { - config.expect_err( +#[tokio::test] +async fn rustc_no_default_toolchain() { + let cx = CliTestContext::new(Scenario::SimpleV1).await; + cx.config + .expect_err( &["rustc"], "rustup could not choose a version of rustc to run", - ); - }); + ) + .await; } -#[test] -fn expected_bins_exist() { - setup(&|config| { - config.expect_ok(&["rustup", "default", "nightly"]); - config.expect_stdout_ok(&["rustc", "--version"], "1.3.0"); - }); +#[tokio::test] +async fn expected_bins_exist() { + let mut cx = CliTestContext::new(Scenario::SimpleV1).await; + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + cx.config + .expect_stdout_ok(&["rustc", "--version"], "1.3.0") + .await; } -#[test] -fn install_toolchain_from_channel() { - setup(&|config| { - config.expect_ok(&["rustup", "default", "nightly"]); - config.expect_stdout_ok(&["rustc", "--version"], "hash-nightly-2"); - config.expect_ok(&["rustup", "default", "beta"]); - config.expect_stdout_ok(&["rustc", "--version"], "hash-beta-1.2.0"); - config.expect_ok(&["rustup", "default", "stable"]); - config.expect_stdout_ok(&["rustc", "--version"], "hash-stable-1.1.0"); - }); +#[tokio::test] +async fn install_toolchain_from_channel() { + let mut cx = CliTestContext::new(Scenario::SimpleV1).await; + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + cx.config + .expect_stdout_ok(&["rustc", "--version"], "hash-nightly-2") + .await; + cx.config.expect_ok(&["rustup", "default", "beta"]).await; + cx.config + .expect_stdout_ok(&["rustc", "--version"], "hash-beta-1.2.0") + .await; + cx.config.expect_ok(&["rustup", "default", "stable"]).await; + cx.config + .expect_stdout_ok(&["rustc", "--version"], "hash-stable-1.1.0") + .await; } -#[test] -fn install_toolchain_from_archive() { - clitools::test(Scenario::ArchivesV1, &|config| { - config.expect_ok(&["rustup", "default", "nightly-2015-01-01"]); - config.expect_stdout_ok(&["rustc", "--version"], "hash-nightly-1"); - config.expect_ok(&["rustup", "default", "beta-2015-01-01"]); - config.expect_stdout_ok(&["rustc", "--version"], "hash-beta-1.1.0"); - config.expect_ok(&["rustup", "default", "stable-2015-01-01"]); - config.expect_stdout_ok(&["rustc", "--version"], "hash-stable-1.0.0"); - }); +#[tokio::test] +async fn install_toolchain_from_archive() { + let mut cx = CliTestContext::new(Scenario::ArchivesV1).await; + cx.config + .expect_ok(&["rustup", "default", "nightly-2015-01-01"]) + .await; + cx.config + .expect_stdout_ok(&["rustc", "--version"], "hash-nightly-1") + .await; + cx.config + .expect_ok(&["rustup", "default", "beta-2015-01-01"]) + .await; + cx.config + .expect_stdout_ok(&["rustc", "--version"], "hash-beta-1.1.0") + .await; + cx.config + .expect_ok(&["rustup", "default", "stable-2015-01-01"]) + .await; + cx.config + .expect_stdout_ok(&["rustc", "--version"], "hash-stable-1.0.0") + .await; } -#[test] -fn install_toolchain_from_version() { - setup(&|config| { - config.expect_ok(&["rustup", "default", "1.1.0"]); - config.expect_stdout_ok(&["rustc", "--version"], "hash-stable-1.1.0"); - }); +#[tokio::test] +async fn install_toolchain_from_version() { + let mut cx = CliTestContext::new(Scenario::SimpleV1).await; + cx.config.expect_ok(&["rustup", "default", "1.1.0"]).await; + cx.config + .expect_stdout_ok(&["rustc", "--version"], "hash-stable-1.1.0") + .await; } -#[test] -fn default_existing_toolchain() { - setup(&|config| { - config.expect_ok(&["rustup", "update", "nightly"]); - config.expect_stderr_ok( +#[tokio::test] +async fn default_existing_toolchain() { + let mut cx = CliTestContext::new(Scenario::SimpleV1).await; + cx.config.expect_ok(&["rustup", "update", "nightly"]).await; + cx.config + .expect_stderr_ok( &["rustup", "default", "nightly"], for_host!("using existing install for 'nightly-{0}'"), - ); - }); + ) + .await; } -#[test] -fn update_channel() { - clitools::test(Scenario::ArchivesV1, &|config| { - set_current_dist_date(config, "2015-01-01"); - config.expect_ok(&["rustup", "default", "nightly"]); - config.expect_stdout_ok(&["rustc", "--version"], "hash-nightly-1"); - set_current_dist_date(config, "2015-01-02"); - config.expect_ok(&["rustup", "update", "nightly"]); - config.expect_stdout_ok(&["rustc", "--version"], "hash-nightly-2"); - }); +#[tokio::test] +async fn update_channel() { + let mut cx = CliTestContext::new(Scenario::ArchivesV1).await; + set_current_dist_date(&cx.config, "2015-01-01"); + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + cx.config + .expect_stdout_ok(&["rustc", "--version"], "hash-nightly-1") + .await; + set_current_dist_date(&cx.config, "2015-01-02"); + cx.config.expect_ok(&["rustup", "update", "nightly"]).await; + cx.config + .expect_stdout_ok(&["rustc", "--version"], "hash-nightly-2") + .await; } -#[test] -fn list_toolchains() { - clitools::test(Scenario::ArchivesV1, &|config| { - config.expect_ok(&["rustup", "update", "nightly"]); - config.expect_ok(&["rustup", "update", "beta-2015-01-01"]); - config.expect_stdout_ok(&["rustup", "toolchain", "list"], "nightly"); - config.expect_stdout_ok(&["rustup", "toolchain", "list", "-v"], "(active, default) "); - #[cfg(windows)] - config.expect_stdout_ok( +#[tokio::test] +async fn list_toolchains() { + let mut cx = CliTestContext::new(Scenario::ArchivesV1).await; + cx.config.expect_ok(&["rustup", "update", "nightly"]).await; + cx.config + .expect_ok(&["rustup", "update", "beta-2015-01-01"]) + .await; + cx.config + .expect_stdout_ok(&["rustup", "toolchain", "list"], "nightly") + .await; + cx.config + .expect_stdout_ok(&["rustup", "toolchain", "list", "-v"], "(active, default) ") + .await; + #[cfg(windows)] + cx.config + .expect_stdout_ok( &["rustup", "toolchain", "list", "-v"], for_host!(r"\toolchains\nightly-{}"), - ); - #[cfg(not(windows))] - config.expect_stdout_ok( + ) + .await; + #[cfg(not(windows))] + cx.config + .expect_stdout_ok( &["rustup", "toolchain", "list", "-v"], for_host!("/toolchains/nightly-{}"), - ); - config.expect_stdout_ok(&["rustup", "toolchain", "list"], "beta-2015-01-01"); - #[cfg(windows)] - config.expect_stdout_ok( + ) + .await; + cx.config + .expect_stdout_ok(&["rustup", "toolchain", "list"], "beta-2015-01-01") + .await; + #[cfg(windows)] + cx.config + .expect_stdout_ok( &["rustup", "toolchain", "list", "-v"], r"\toolchains\beta-2015-01-01", - ); - #[cfg(not(windows))] - config.expect_stdout_ok( + ) + .await; + #[cfg(not(windows))] + cx.config + .expect_stdout_ok( &["rustup", "toolchain", "list", "-v"], "/toolchains/beta-2015-01-01", - ); - }); + ) + .await; } -#[test] -fn list_toolchains_with_none() { - setup(&|config| { - config.expect_stdout_ok(&["rustup", "toolchain", "list"], "no installed toolchains"); - }); +#[tokio::test] +async fn list_toolchains_with_none() { + let cx = CliTestContext::new(Scenario::SimpleV1).await; + cx.config + .expect_stdout_ok(&["rustup", "toolchain", "list"], "no installed toolchains") + .await; } -#[test] -fn remove_toolchain() { - setup(&|config| { - config.expect_ok(&["rustup", "update", "nightly"]); - config.expect_ok(&["rustup", "toolchain", "remove", "nightly"]); - config.expect_ok(&["rustup", "toolchain", "list"]); - config.expect_stdout_ok(&["rustup", "toolchain", "list"], "no installed toolchains"); - }); +#[tokio::test] +async fn remove_toolchain() { + let mut cx = CliTestContext::new(Scenario::SimpleV1).await; + cx.config.expect_ok(&["rustup", "update", "nightly"]).await; + cx.config + .expect_ok(&["rustup", "toolchain", "remove", "nightly"]) + .await; + cx.config.expect_ok(&["rustup", "toolchain", "list"]).await; + cx.config + .expect_stdout_ok(&["rustup", "toolchain", "list"], "no installed toolchains") + .await; } -#[test] -fn remove_default_toolchain_autoinstalls() { - setup(&|config| { - config.expect_ok(&["rustup", "default", "nightly"]); - config.expect_ok(&["rustup", "toolchain", "remove", "nightly"]); - config.expect_stderr_ok(&["rustc", "--version"], "info: installing component"); - }); +#[tokio::test] +async fn remove_default_toolchain_autoinstalls() { + let mut cx = CliTestContext::new(Scenario::SimpleV1).await; + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + cx.config + .expect_ok(&["rustup", "toolchain", "remove", "nightly"]) + .await; + cx.config + .expect_stderr_ok(&["rustc", "--version"], "info: installing component") + .await; } -#[test] -fn remove_override_toolchain_err_handling() { - setup(&|config| { - let tempdir = tempfile::Builder::new().prefix("rustup").tempdir().unwrap(); - config.change_dir(tempdir.path(), &|config| { - config.expect_ok(&["rustup", "default", "nightly"]); - config.expect_ok(&["rustup", "override", "add", "beta"]); - config.expect_ok(&["rustup", "toolchain", "remove", "beta"]); - config.expect_stderr_ok(&["rustc", "--version"], "info: installing component"); - }); - }); +#[tokio::test] +async fn remove_override_toolchain_err_handling() { + let mut cx = CliTestContext::new(Scenario::SimpleV1).await; + let tempdir = tempfile::Builder::new().prefix("rustup").tempdir().unwrap(); + let mut cx = cx.change_dir(tempdir.path()); + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + cx.config + .expect_ok(&["rustup", "override", "add", "beta"]) + .await; + cx.config + .expect_ok(&["rustup", "toolchain", "remove", "beta"]) + .await; + cx.config + .expect_stderr_ok(&["rustc", "--version"], "info: installing component") + .await; } -#[test] -fn bad_sha_on_manifest() { - setup(&|config| { - let sha_file = config - .distdir - .as_ref() - .unwrap() - .join("dist/channel-rust-nightly.sha256"); - let sha_str = fs::read_to_string(&sha_file).unwrap(); - let mut sha_bytes = sha_str.into_bytes(); - sha_bytes[..10].clone_from_slice(b"aaaaaaaaaa"); - let sha_str = String::from_utf8(sha_bytes).unwrap(); - rustup::utils::raw::write_file(&sha_file, &sha_str).unwrap(); - config.expect_err(&["rustup", "default", "nightly"], "checksum failed"); - }); +#[tokio::test] +async fn bad_sha_on_manifest() { + let cx = CliTestContext::new(Scenario::SimpleV1).await; + let sha_file = cx + .config + .distdir + .as_ref() + .unwrap() + .join("dist/channel-rust-nightly.sha256"); + let sha_str = fs::read_to_string(&sha_file).unwrap(); + let mut sha_bytes = sha_str.into_bytes(); + sha_bytes[..10].clone_from_slice(b"aaaaaaaaaa"); + let sha_str = String::from_utf8(sha_bytes).unwrap(); + rustup::utils::raw::write_file(&sha_file, &sha_str).unwrap(); + cx.config + .expect_err(&["rustup", "default", "nightly"], "checksum failed") + .await; } -#[test] -fn bad_sha_on_installer() { - setup(&|config| { - let dir = config.distdir.as_ref().unwrap().join("dist"); - for file in fs::read_dir(dir).unwrap() { - let file = file.unwrap(); - let path = file.path(); - let filename = path.to_string_lossy(); - if filename.ends_with(".tar.gz") || filename.ends_with(".tar.xz") { - rustup::utils::raw::write_file(&path, "xxx").unwrap(); - } +#[tokio::test] +async fn bad_sha_on_installer() { + let cx = CliTestContext::new(Scenario::SimpleV1).await; + let dir = cx.config.distdir.as_ref().unwrap().join("dist"); + for file in fs::read_dir(dir).unwrap() { + let file = file.unwrap(); + let path = file.path(); + let filename = path.to_string_lossy(); + if filename.ends_with(".tar.gz") || filename.ends_with(".tar.xz") { + rustup::utils::raw::write_file(&path, "xxx").unwrap(); } - config.expect_err(&["rustup", "default", "nightly"], "checksum failed"); - }); + } + cx.config + .expect_err(&["rustup", "default", "nightly"], "checksum failed") + .await; } -#[test] -fn install_override_toolchain_from_channel() { - setup(&|config| { - config.expect_ok(&["rustup", "override", "add", "nightly"]); - config.expect_stdout_ok(&["rustc", "--version"], "hash-nightly-2"); - config.expect_ok(&["rustup", "override", "add", "beta"]); - config.expect_stdout_ok(&["rustc", "--version"], "hash-beta-1.2.0"); - config.expect_ok(&["rustup", "override", "add", "stable"]); - config.expect_stdout_ok(&["rustc", "--version"], "hash-stable-1.1.0"); - }); +#[tokio::test] +async fn install_override_toolchain_from_channel() { + let mut cx = CliTestContext::new(Scenario::SimpleV1).await; + cx.config + .expect_ok(&["rustup", "override", "add", "nightly"]) + .await; + cx.config + .expect_stdout_ok(&["rustc", "--version"], "hash-nightly-2") + .await; + cx.config + .expect_ok(&["rustup", "override", "add", "beta"]) + .await; + cx.config + .expect_stdout_ok(&["rustc", "--version"], "hash-beta-1.2.0") + .await; + cx.config + .expect_ok(&["rustup", "override", "add", "stable"]) + .await; + cx.config + .expect_stdout_ok(&["rustc", "--version"], "hash-stable-1.1.0") + .await; } -#[test] -fn install_override_toolchain_from_archive() { - clitools::test(Scenario::ArchivesV1, &|config| { - config.expect_ok(&["rustup", "override", "add", "nightly-2015-01-01"]); - config.expect_stdout_ok(&["rustc", "--version"], "hash-nightly-1"); - config.expect_ok(&["rustup", "override", "add", "beta-2015-01-01"]); - config.expect_stdout_ok(&["rustc", "--version"], "hash-beta-1.1.0"); - config.expect_ok(&["rustup", "override", "add", "stable-2015-01-01"]); - config.expect_stdout_ok(&["rustc", "--version"], "hash-stable-1.0.0"); - }); +#[tokio::test] +async fn install_override_toolchain_from_archive() { + let mut cx = CliTestContext::new(Scenario::ArchivesV1).await; + cx.config + .expect_ok(&["rustup", "override", "add", "nightly-2015-01-01"]) + .await; + cx.config + .expect_stdout_ok(&["rustc", "--version"], "hash-nightly-1") + .await; + cx.config + .expect_ok(&["rustup", "override", "add", "beta-2015-01-01"]) + .await; + cx.config + .expect_stdout_ok(&["rustc", "--version"], "hash-beta-1.1.0") + .await; + cx.config + .expect_ok(&["rustup", "override", "add", "stable-2015-01-01"]) + .await; + cx.config + .expect_stdout_ok(&["rustc", "--version"], "hash-stable-1.0.0") + .await; } -#[test] -fn install_override_toolchain_from_version() { - setup(&|config| { - config.expect_ok(&["rustup", "override", "add", "1.1.0"]); - config.expect_stdout_ok(&["rustc", "--version"], "hash-stable-1.1.0"); - }); +#[tokio::test] +async fn install_override_toolchain_from_version() { + let mut cx = CliTestContext::new(Scenario::SimpleV1).await; + cx.config + .expect_ok(&["rustup", "override", "add", "1.1.0"]) + .await; + cx.config + .expect_stdout_ok(&["rustc", "--version"], "hash-stable-1.1.0") + .await; } -#[test] -fn override_overrides_default() { - setup(&|config| { - let tempdir = tempfile::Builder::new().prefix("rustup").tempdir().unwrap(); - config.expect_ok(&["rustup", "default", "nightly"]); - config.change_dir(tempdir.path(), &|config| { - config.expect_ok(&["rustup", "override", "add", "beta"]); - config.expect_stdout_ok(&["rustc", "--version"], "hash-beta-1.2.0"); - }); - }); +#[tokio::test] +async fn override_overrides_default() { + let mut cx = CliTestContext::new(Scenario::SimpleV1).await; + let tempdir = tempfile::Builder::new().prefix("rustup").tempdir().unwrap(); + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + + let mut cx = cx.change_dir(tempdir.path()); + cx.config + .expect_ok(&["rustup", "override", "add", "beta"]) + .await; + cx.config + .expect_stdout_ok(&["rustc", "--version"], "hash-beta-1.2.0") + .await; } -#[test] -fn multiple_overrides() { - setup(&|config| { - let tempdir1 = tempfile::Builder::new().prefix("rustup").tempdir().unwrap(); - let tempdir2 = tempfile::Builder::new().prefix("rustup").tempdir().unwrap(); - - config.expect_ok(&["rustup", "default", "nightly"]); - config.change_dir(tempdir1.path(), &|config| { - config.expect_ok(&["rustup", "override", "add", "beta"]); - }); - config.change_dir(tempdir2.path(), &|config| { - config.expect_ok(&["rustup", "override", "add", "stable"]); - }); - - config.expect_stdout_ok(&["rustc", "--version"], "hash-nightly-2"); - - config.change_dir(tempdir1.path(), &|config| { - config.expect_stdout_ok(&["rustc", "--version"], "hash-beta-1.2.0"); - }); - config.change_dir(tempdir2.path(), &|config| { - config.expect_stdout_ok(&["rustc", "--version"], "hash-stable-1.1.0"); - }); - }); +#[tokio::test] +async fn multiple_overrides() { + let mut cx = CliTestContext::new(Scenario::SimpleV1).await; + let tempdir1 = tempfile::Builder::new().prefix("rustup").tempdir().unwrap(); + let tempdir2 = tempfile::Builder::new().prefix("rustup").tempdir().unwrap(); + + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + { + let mut cx = cx.change_dir(tempdir1.path()); + cx.config + .expect_ok(&["rustup", "override", "add", "beta"]) + .await; + } + + { + let mut cx = cx.change_dir(tempdir2.path()); + cx.config + .expect_ok(&["rustup", "override", "add", "stable"]) + .await; + } + + cx.config + .expect_stdout_ok(&["rustc", "--version"], "hash-nightly-2") + .await; + + { + let cx = cx.change_dir(tempdir1.path()); + cx.config + .expect_stdout_ok(&["rustc", "--version"], "hash-beta-1.2.0") + .await; + } + + { + let cx = cx.change_dir(tempdir2.path()); + cx.config + .expect_stdout_ok(&["rustc", "--version"], "hash-stable-1.1.0") + .await; + } } -#[test] -fn change_override() { - setup(&|config| { - let tempdir = tempfile::Builder::new().prefix("rustup").tempdir().unwrap(); - config.change_dir(tempdir.path(), &|config| { - config.expect_ok(&["rustup", "override", "add", "nightly"]); - config.expect_ok(&["rustup", "override", "add", "beta"]); - config.expect_stdout_ok(&["rustc", "--version"], "hash-beta-1.2.0"); - }); - }); +#[tokio::test] +async fn change_override() { + let mut cx = CliTestContext::new(Scenario::SimpleV1).await; + let tempdir = tempfile::Builder::new().prefix("rustup").tempdir().unwrap(); + let mut cx = cx.change_dir(tempdir.path()); + cx.config + .expect_ok(&["rustup", "override", "add", "nightly"]) + .await; + cx.config + .expect_ok(&["rustup", "override", "add", "beta"]) + .await; + cx.config + .expect_stdout_ok(&["rustc", "--version"], "hash-beta-1.2.0") + .await; } -#[test] -fn remove_override_no_default() { - setup(&|config| { - let tempdir = tempfile::Builder::new().prefix("rustup").tempdir().unwrap(); - config.change_dir(tempdir.path(), &|config| { - config.expect_ok(&["rustup", "override", "add", "nightly"]); - config.expect_ok(&["rustup", "override", "remove"]); - config.expect_err( - &["rustc"], - "rustup could not choose a version of rustc to run", - ); - }); - }); +#[tokio::test] +async fn remove_override_no_default() { + let mut cx = CliTestContext::new(Scenario::SimpleV1).await; + let tempdir = tempfile::Builder::new().prefix("rustup").tempdir().unwrap(); + let mut cx = cx.change_dir(tempdir.path()); + cx.config + .expect_ok(&["rustup", "override", "add", "nightly"]) + .await; + cx.config.expect_ok(&["rustup", "override", "remove"]).await; + cx.config + .expect_err( + &["rustc"], + "rustup could not choose a version of rustc to run", + ) + .await; } -#[test] -fn remove_override_with_default() { - setup(&|config| { - let tempdir = tempfile::Builder::new().prefix("rustup").tempdir().unwrap(); - config.change_dir(tempdir.path(), &|config| { - config.expect_ok(&["rustup", "default", "nightly"]); - config.expect_ok(&["rustup", "override", "add", "beta"]); - config.expect_ok(&["rustup", "override", "remove"]); - config.expect_stdout_ok(&["rustc", "--version"], "hash-nightly-2"); - }); - }); +#[tokio::test] +async fn remove_override_with_default() { + let mut cx = CliTestContext::new(Scenario::SimpleV1).await; + let tempdir = tempfile::Builder::new().prefix("rustup").tempdir().unwrap(); + let mut cx = cx.change_dir(tempdir.path()); + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + cx.config + .expect_ok(&["rustup", "override", "add", "beta"]) + .await; + cx.config.expect_ok(&["rustup", "override", "remove"]).await; + cx.config + .expect_stdout_ok(&["rustc", "--version"], "hash-nightly-2") + .await; } -#[test] -fn remove_override_with_multiple_overrides() { - setup(&|config| { - let tempdir1 = tempfile::Builder::new().prefix("rustup").tempdir().unwrap(); - let tempdir2 = tempfile::Builder::new().prefix("rustup").tempdir().unwrap(); - config.expect_ok(&["rustup", "default", "nightly"]); - config.change_dir(tempdir1.path(), &|config| { - config.expect_ok(&["rustup", "override", "add", "beta"]); - }); - config.change_dir(tempdir2.path(), &|config| { - config.expect_ok(&["rustup", "override", "add", "stable"]); - }); - config.expect_stdout_ok(&["rustc", "--version"], "hash-nightly-2"); - config.change_dir(tempdir1.path(), &|config| { - config.expect_ok(&["rustup", "override", "remove"]); - config.expect_stdout_ok(&["rustc", "--version"], "hash-nightly-2"); - }); - config.change_dir(tempdir2.path(), &|config| { - config.expect_stdout_ok(&["rustc", "--version"], "hash-stable-1.1.0"); - }); - }); +#[tokio::test] +async fn remove_override_with_multiple_overrides() { + let mut cx = CliTestContext::new(Scenario::SimpleV1).await; + let tempdir1 = tempfile::Builder::new().prefix("rustup").tempdir().unwrap(); + let tempdir2 = tempfile::Builder::new().prefix("rustup").tempdir().unwrap(); + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + + { + let mut cx = cx.change_dir(tempdir1.path()); + cx.config + .expect_ok(&["rustup", "override", "add", "beta"]) + .await; + } + + { + let mut cx = cx.change_dir(tempdir2.path()); + cx.config + .expect_ok(&["rustup", "override", "add", "stable"]) + .await; + } + + cx.config + .expect_stdout_ok(&["rustc", "--version"], "hash-nightly-2") + .await; + + { + let mut cx = cx.change_dir(tempdir1.path()); + cx.config.expect_ok(&["rustup", "override", "remove"]).await; + cx.config + .expect_stdout_ok(&["rustc", "--version"], "hash-nightly-2") + .await; + } + + { + let cx = cx.change_dir(tempdir2.path()); + cx.config + .expect_stdout_ok(&["rustc", "--version"], "hash-stable-1.1.0") + .await; + } } -#[test] -fn no_update_on_channel_when_date_has_not_changed() { - setup(&|config| { - config.expect_ok(&["rustup", "update", "nightly"]); - config.expect_stdout_ok(&["rustup", "update", "nightly"], "unchanged"); - }); +#[tokio::test] +async fn no_update_on_channel_when_date_has_not_changed() { + let mut cx = CliTestContext::new(Scenario::SimpleV1).await; + cx.config.expect_ok(&["rustup", "update", "nightly"]).await; + cx.config + .expect_stdout_ok(&["rustup", "update", "nightly"], "unchanged") + .await; } -#[test] -fn update_on_channel_when_date_has_changed() { - clitools::test(Scenario::ArchivesV1, &|config| { - set_current_dist_date(config, "2015-01-01"); - config.expect_ok(&["rustup", "default", "nightly"]); - config.expect_stdout_ok(&["rustc", "--version"], "hash-nightly-1"); - set_current_dist_date(config, "2015-01-02"); - config.expect_ok(&["rustup", "update", "nightly"]); - config.expect_stdout_ok(&["rustc", "--version"], "hash-nightly-2"); - }); +#[tokio::test] +async fn update_on_channel_when_date_has_changed() { + let mut cx = CliTestContext::new(Scenario::ArchivesV1).await; + set_current_dist_date(&cx.config, "2015-01-01"); + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + cx.config + .expect_stdout_ok(&["rustc", "--version"], "hash-nightly-1") + .await; + set_current_dist_date(&cx.config, "2015-01-02"); + cx.config.expect_ok(&["rustup", "update", "nightly"]).await; + cx.config + .expect_stdout_ok(&["rustc", "--version"], "hash-nightly-2") + .await; } -#[test] -fn run_command() { - setup(&|config| { - config.expect_ok(&["rustup", "update", "nightly"]); - config.expect_ok(&["rustup", "default", "beta"]); - config.expect_stdout_ok( +#[tokio::test] +async fn run_command() { + let mut cx = CliTestContext::new(Scenario::SimpleV1).await; + cx.config.expect_ok(&["rustup", "update", "nightly"]).await; + cx.config.expect_ok(&["rustup", "default", "beta"]).await; + cx.config + .expect_stdout_ok( &["rustup", "run", "nightly", "rustc", "--version"], "hash-nightly-2", - ); - }); + ) + .await; } -#[test] -fn remove_toolchain_then_add_again() { +#[tokio::test] +async fn remove_toolchain_then_add_again() { // Issue brson/multirust #53 - setup(&|config| { - config.expect_ok(&["rustup", "default", "beta"]); - config.expect_ok(&["rustup", "toolchain", "remove", "beta"]); - config.expect_ok(&["rustup", "update", "beta"]); - config.expect_ok(&["rustc", "--version"]); - }); + let mut cx = CliTestContext::new(Scenario::SimpleV1).await; + cx.config.expect_ok(&["rustup", "default", "beta"]).await; + cx.config + .expect_ok(&["rustup", "toolchain", "remove", "beta"]) + .await; + cx.config.expect_ok(&["rustup", "update", "beta"]).await; + cx.config.expect_ok(&["rustc", "--version"]).await; } diff --git a/tests/suite/cli_v2.rs b/tests/suite/cli_v2.rs index 8a61ebe47a..57e9424abf 100644 --- a/tests/suite/cli_v2.rs +++ b/tests/suite/cli_v2.rs @@ -6,712 +6,882 @@ use std::io::Write; use rustup::dist::TargetTriple; use rustup::for_host; -use rustup::test::mock::clitools::{self, set_current_dist_date, Config, Scenario}; +use rustup::test::mock::clitools::{self, set_current_dist_date, CliTestContext, Config, Scenario}; use rustup::test::this_host_triple; -pub fn setup(f: &dyn Fn(&mut Config)) { - clitools::test(Scenario::SimpleV2, f); -} - -pub fn setup_complex(f: &dyn Fn(&mut Config)) { - clitools::test(Scenario::UnavailableRls, f); -} - -#[test] -fn rustc_no_default_toolchain() { - setup(&|config| { - config.expect_err( +#[tokio::test] +async fn rustc_no_default_toolchain() { + let cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config + .expect_err( &["rustc"], "rustup could not choose a version of rustc to run", - ); - }); -} - -#[test] -fn expected_bins_exist() { - setup(&|config| { - config.expect_ok(&["rustup", "default", "nightly"]); - config.expect_stdout_ok(&["rustc", "--version"], "1.3.0"); - }); -} - -#[test] -fn install_toolchain_from_channel() { - setup(&|config| { - config.expect_ok(&["rustup", "default", "nightly"]); - config.expect_stdout_ok(&["rustc", "--version"], "hash-nightly-2"); - config.expect_ok(&["rustup", "default", "beta"]); - config.expect_stdout_ok(&["rustc", "--version"], "hash-beta-1.2.0"); - config.expect_ok(&["rustup", "default", "stable"]); - config.expect_stdout_ok(&["rustc", "--version"], "hash-stable-1.1.0"); - }); -} - -#[test] -fn install_toolchain_from_archive() { - clitools::test(Scenario::ArchivesV2, &|config| { - config.expect_ok(&["rustup", "default", "nightly-2015-01-01"]); - config.expect_stdout_ok(&["rustc", "--version"], "hash-nightly-1"); - config.expect_ok(&["rustup", "default", "beta-2015-01-01"]); - config.expect_stdout_ok(&["rustc", "--version"], "hash-beta-1.1.0"); - config.expect_ok(&["rustup", "default", "stable-2015-01-01"]); - config.expect_stdout_ok(&["rustc", "--version"], "hash-stable-1.0.0"); - }); -} - -#[test] -fn install_toolchain_from_version() { - setup(&|config| { - config.expect_ok(&["rustup", "default", "1.1.0"]); - config.expect_stdout_ok(&["rustc", "--version"], "hash-stable-1.1.0"); - }); -} - -#[test] -fn install_with_profile() { - setup_complex(&|config| { - // Start with a config that uses the "complete" profile - set_current_dist_date(config, "2015-01-01"); - config.expect_ok(&["rustup", "set", "profile", "complete"]); - - // Installing with minimal profile should only install rustc - config.expect_ok(&[ + ) + .await; +} + +#[tokio::test] +async fn expected_bins_exist() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + cx.config + .expect_stdout_ok(&["rustc", "--version"], "1.3.0") + .await; +} + +#[tokio::test] +async fn install_toolchain_from_channel() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + cx.config + .expect_stdout_ok(&["rustc", "--version"], "hash-nightly-2") + .await; + cx.config.expect_ok(&["rustup", "default", "beta"]).await; + cx.config + .expect_stdout_ok(&["rustc", "--version"], "hash-beta-1.2.0") + .await; + cx.config.expect_ok(&["rustup", "default", "stable"]).await; + cx.config + .expect_stdout_ok(&["rustc", "--version"], "hash-stable-1.1.0") + .await; +} + +#[tokio::test] +async fn install_toolchain_from_archive() { + let mut cx = CliTestContext::new(Scenario::ArchivesV2).await; + cx.config + .expect_ok(&["rustup", "default", "nightly-2015-01-01"]) + .await; + cx.config + .expect_stdout_ok(&["rustc", "--version"], "hash-nightly-1") + .await; + cx.config + .expect_ok(&["rustup", "default", "beta-2015-01-01"]) + .await; + cx.config + .expect_stdout_ok(&["rustc", "--version"], "hash-beta-1.1.0") + .await; + cx.config + .expect_ok(&["rustup", "default", "stable-2015-01-01"]) + .await; + cx.config + .expect_stdout_ok(&["rustc", "--version"], "hash-stable-1.0.0") + .await; +} + +#[tokio::test] +async fn install_toolchain_from_version() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "default", "1.1.0"]).await; + cx.config + .expect_stdout_ok(&["rustc", "--version"], "hash-stable-1.1.0") + .await; +} + +#[tokio::test] +async fn install_with_profile() { + let mut cx = CliTestContext::new(Scenario::UnavailableRls).await; + // Start with a config that uses the "complete" profile + set_current_dist_date(&cx.config, "2015-01-01"); + cx.config + .expect_ok(&["rustup", "set", "profile", "complete"]) + .await; + + // Installing with minimal profile should only install rustc + cx.config + .expect_ok(&[ "rustup", "toolchain", "install", "--profile", "minimal", "nightly", - ]); - config.expect_ok(&["rustup", "default", "nightly"]); + ]) + .await; + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; - config.expect_component_executable("rustup"); - config.expect_component_executable("rustc"); - config.expect_component_not_executable("cargo"); + cx.config.expect_component_executable("rustup").await; + cx.config.expect_component_executable("rustc").await; + cx.config.expect_component_not_executable("cargo").await; - // After an update, we should _still_ only have the profile-dictated components - set_current_dist_date(config, "2015-01-02"); - config.expect_ok(&["rustup", "update", "nightly"]); + // After an update, we should _still_ only have the profile-dictated components + set_current_dist_date(&cx.config, "2015-01-02"); + cx.config.expect_ok(&["rustup", "update", "nightly"]).await; - config.expect_component_executable("rustup"); - config.expect_component_executable("rustc"); - config.expect_component_not_executable("cargo"); - }); + cx.config.expect_component_executable("rustup").await; + cx.config.expect_component_executable("rustc").await; + cx.config.expect_component_not_executable("cargo").await; } -#[test] -fn default_existing_toolchain() { - setup(&|config| { - config.expect_ok(&["rustup", "update", "nightly"]); - config.expect_stderr_ok( +#[tokio::test] +async fn default_existing_toolchain() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "update", "nightly"]).await; + cx.config + .expect_stderr_ok( &["rustup", "default", "nightly"], for_host!("using existing install for 'nightly-{0}'"), - ); - }); -} - -#[test] -fn update_channel() { - clitools::test(Scenario::ArchivesV2, &|config| { - set_current_dist_date(config, "2015-01-01"); - config.expect_ok(&["rustup", "default", "nightly"]); - config.expect_stdout_ok(&["rustc", "--version"], "hash-nightly-1"); - set_current_dist_date(config, "2015-01-02"); - config.expect_ok(&["rustup", "update", "nightly"]); - config.expect_stdout_ok(&["rustc", "--version"], "hash-nightly-2"); - }); -} - -#[test] -fn list_toolchains() { - clitools::test(Scenario::ArchivesV2, &|config| { - config.expect_ok(&["rustup", "update", "nightly"]); - config.expect_ok(&["rustup", "update", "beta-2015-01-01"]); - config.expect_stdout_ok(&["rustup", "toolchain", "list"], "nightly"); - config.expect_stdout_ok(&["rustup", "toolchain", "list", "-v"], "(active, default) "); - #[cfg(windows)] - config.expect_stdout_ok( + ) + .await; +} + +#[tokio::test] +async fn update_channel() { + let mut cx = CliTestContext::new(Scenario::ArchivesV2).await; + set_current_dist_date(&cx.config, "2015-01-01"); + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + cx.config + .expect_stdout_ok(&["rustc", "--version"], "hash-nightly-1") + .await; + set_current_dist_date(&cx.config, "2015-01-02"); + cx.config.expect_ok(&["rustup", "update", "nightly"]).await; + cx.config + .expect_stdout_ok(&["rustc", "--version"], "hash-nightly-2") + .await; +} + +#[tokio::test] +async fn list_toolchains() { + let mut cx = CliTestContext::new(Scenario::ArchivesV2).await; + cx.config.expect_ok(&["rustup", "update", "nightly"]).await; + cx.config + .expect_ok(&["rustup", "update", "beta-2015-01-01"]) + .await; + cx.config + .expect_stdout_ok(&["rustup", "toolchain", "list"], "nightly") + .await; + cx.config + .expect_stdout_ok(&["rustup", "toolchain", "list", "-v"], "(active, default) ") + .await; + #[cfg(windows)] + cx.config + .expect_stdout_ok( &["rustup", "toolchain", "list", "-v"], for_host!(r"\toolchains\nightly-{}"), - ); - #[cfg(not(windows))] - config.expect_stdout_ok( + ) + .await; + #[cfg(not(windows))] + cx.config + .expect_stdout_ok( &["rustup", "toolchain", "list", "-v"], for_host!("/toolchains/nightly-{}"), - ); - config.expect_stdout_ok(&["rustup", "toolchain", "list"], "beta-2015-01-01"); - #[cfg(windows)] - config.expect_stdout_ok( + ) + .await; + cx.config + .expect_stdout_ok(&["rustup", "toolchain", "list"], "beta-2015-01-01") + .await; + #[cfg(windows)] + cx.config + .expect_stdout_ok( &["rustup", "toolchain", "list", "-v"], r"\toolchains\beta-2015-01-01", - ); - #[cfg(not(windows))] - config.expect_stdout_ok( + ) + .await; + #[cfg(not(windows))] + cx.config + .expect_stdout_ok( &["rustup", "toolchain", "list", "-v"], "/toolchains/beta-2015-01-01", - ); - }); + ) + .await; } -#[test] -fn list_toolchains_with_bogus_file() { +#[tokio::test] +async fn list_toolchains_with_bogus_file() { // #520 - setup(&|config| { - config.expect_ok(&["rustup", "update", "nightly"]); - - let name = "bogus_regular_file.txt"; - let path = config.rustupdir.join("toolchains").join(name); - rustup::utils::utils::write_file(name, &path, "").unwrap(); - config.expect_stdout_ok(&["rustup", "toolchain", "list"], "nightly"); - config.expect_not_stdout_ok(&["rustup", "toolchain", "list"], name); - }); -} - -#[test] -fn list_toolchains_with_none() { - setup(&|config| { - config.expect_stdout_ok(&["rustup", "toolchain", "list"], "no installed toolchains"); - }); -} - -#[test] -fn remove_toolchain() { - setup(&|config| { - config.expect_ok(&["rustup", "update", "nightly"]); - config.expect_ok(&["rustup", "toolchain", "remove", "nightly"]); - config.expect_ok(&["rustup", "toolchain", "list"]); - config.expect_stdout_ok(&["rustup", "toolchain", "list"], "no installed toolchains"); - }); + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "update", "nightly"]).await; + + let name = "bogus_regular_file.txt"; + let path = cx.config.rustupdir.join("toolchains").join(name); + rustup::utils::utils::write_file(name, &path, "").unwrap(); + cx.config + .expect_stdout_ok(&["rustup", "toolchain", "list"], "nightly") + .await; + cx.config + .expect_not_stdout_ok(&["rustup", "toolchain", "list"], name) + .await; +} + +#[tokio::test] +async fn list_toolchains_with_none() { + let cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config + .expect_stdout_ok(&["rustup", "toolchain", "list"], "no installed toolchains") + .await; +} + +#[tokio::test] +async fn remove_toolchain() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "update", "nightly"]).await; + cx.config + .expect_ok(&["rustup", "toolchain", "remove", "nightly"]) + .await; + cx.config.expect_ok(&["rustup", "toolchain", "list"]).await; + cx.config + .expect_stdout_ok(&["rustup", "toolchain", "list"], "no installed toolchains") + .await; } // Issue #2873 -#[test] -fn remove_toolchain_ignore_trailing_slash() { - setup(&|config| { - // custom toolchain name with trailing slash - let path = config.customdir.join("custom-1"); - let path_str = path.to_string_lossy(); - config.expect_ok(&["rustup", "toolchain", "link", "dev", &path_str]); - config.expect_stderr_ok( +#[tokio::test] +async fn remove_toolchain_ignore_trailing_slash() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + // custom toolchain name with trailing slash + let path = cx.config.customdir.join("custom-1"); + let path_str = path.to_string_lossy(); + cx.config + .expect_ok(&["rustup", "toolchain", "link", "dev", &path_str]) + .await; + cx.config + .expect_stderr_ok( &["rustup", "toolchain", "remove", "dev/"], "toolchain 'dev' uninstalled", - ); - // check if custom toolchain directory contents are not removed - let toolchain_dir_is_non_empty = fs::read_dir(&path).unwrap().next().is_some(); - assert!(toolchain_dir_is_non_empty); - // distributable toolchain name with trailing slash - config.expect_ok(&["rustup", "update", "nightly"]); - config.expect_stderr_ok( + ) + .await; + // check if custom toolchain directory contents are not removed + let toolchain_dir_is_non_empty = fs::read_dir(&path).unwrap().next().is_some(); + assert!(toolchain_dir_is_non_empty); + // distributable toolchain name with trailing slash + cx.config.expect_ok(&["rustup", "update", "nightly"]).await; + cx.config + .expect_stderr_ok( &["rustup", "toolchain", "remove", for_host!("nightly-{}/")], for_host!("toolchain 'nightly-{}' uninstalled"), - ); - }); -} - -#[test] -fn add_remove_multiple_toolchains() { - fn go(add: &str, rm: &str) { - setup(&|config| { - let tch1 = "beta"; - let tch2 = "nightly"; - - config.expect_ok(&["rustup", "toolchain", add, tch1, tch2]); - config.expect_ok(&["rustup", "toolchain", "list"]); - config.expect_stdout_ok(&["rustup", "toolchain", "list"], tch1); - config.expect_stdout_ok(&["rustup", "toolchain", "list"], tch2); - - config.expect_ok(&["rustup", "toolchain", rm, tch1, tch2]); - config.expect_ok(&["rustup", "toolchain", "list"]); - config.expect_not_stdout_ok(&["rustup", "toolchain", "list"], tch1); - config.expect_not_stdout_ok(&["rustup", "toolchain", "list"], tch2); - }); + ) + .await; +} + +#[tokio::test] +async fn add_remove_multiple_toolchains() { + async fn go(add: &str, rm: &str) { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + let tch1 = "beta"; + let tch2 = "nightly"; + + cx.config + .expect_ok(&["rustup", "toolchain", add, tch1, tch2]) + .await; + cx.config.expect_ok(&["rustup", "toolchain", "list"]).await; + cx.config + .expect_stdout_ok(&["rustup", "toolchain", "list"], tch1) + .await; + cx.config + .expect_stdout_ok(&["rustup", "toolchain", "list"], tch2) + .await; + + cx.config + .expect_ok(&["rustup", "toolchain", rm, tch1, tch2]) + .await; + cx.config.expect_ok(&["rustup", "toolchain", "list"]).await; + cx.config + .expect_not_stdout_ok(&["rustup", "toolchain", "list"], tch1) + .await; + cx.config + .expect_not_stdout_ok(&["rustup", "toolchain", "list"], tch2) + .await; } for add in &["add", "update", "install"] { for rm in &["remove", "uninstall"] { - go(add, rm); + go(add, rm).await; } } } -#[test] -fn remove_default_toolchain_autoinstalls() { - setup(&|config| { - config.expect_ok(&["rustup", "default", "nightly"]); - config.expect_ok(&["rustup", "toolchain", "remove", "nightly"]); - config.expect_stderr_ok(&["rustc", "--version"], "info: installing component"); - }); -} - -#[test] -fn remove_override_toolchain_err_handling() { - setup(&|config| { - let tempdir = tempfile::Builder::new().prefix("rustup").tempdir().unwrap(); - config.change_dir(tempdir.path(), &|config| { - config.expect_ok(&["rustup", "default", "nightly"]); - config.expect_ok(&["rustup", "override", "add", "beta"]); - config.expect_ok(&["rustup", "toolchain", "remove", "beta"]); - config.expect_stderr_ok(&["rustc", "--version"], "info: installing component"); - }); - }); -} - -#[test] -fn file_override_toolchain_err_handling() { - setup(&|config| { - let cwd = config.current_dir(); - let toolchain_file = cwd.join("rust-toolchain"); - rustup::utils::raw::write_file(&toolchain_file, "beta").unwrap(); - config.expect_stderr_ok(&["rustc", "--version"], "info: installing component"); - }); -} - -#[test] -fn plus_override_toolchain_err_handling() { - setup(&|config| { - config.expect_err( +#[tokio::test] +async fn remove_default_toolchain_autoinstalls() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + cx.config + .expect_ok(&["rustup", "toolchain", "remove", "nightly"]) + .await; + cx.config + .expect_stderr_ok(&["rustc", "--version"], "info: installing component") + .await; +} + +#[tokio::test] +async fn remove_override_toolchain_err_handling() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + let tempdir = tempfile::Builder::new().prefix("rustup").tempdir().unwrap(); + let mut cx = cx.change_dir(tempdir.path()); + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + cx.config + .expect_ok(&["rustup", "override", "add", "beta"]) + .await; + cx.config + .expect_ok(&["rustup", "toolchain", "remove", "beta"]) + .await; + cx.config + .expect_stderr_ok(&["rustc", "--version"], "info: installing component") + .await; +} + +#[tokio::test] +async fn file_override_toolchain_err_handling() { + let cx = CliTestContext::new(Scenario::SimpleV2).await; + let cwd = cx.config.current_dir(); + let toolchain_file = cwd.join("rust-toolchain"); + rustup::utils::raw::write_file(&toolchain_file, "beta").unwrap(); + cx.config + .expect_stderr_ok(&["rustc", "--version"], "info: installing component") + .await; +} + +#[tokio::test] +async fn plus_override_toolchain_err_handling() { + let cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config + .expect_err( &["rustc", "+beta"], for_host!("toolchain 'beta-{0}' is not installed"), - ); - }); + ) + .await; } -#[test] -fn bad_sha_on_manifest() { - setup(&|config| { - // Corrupt the sha - let sha_file = config - .distdir - .as_ref() - .unwrap() - .join("dist/channel-rust-nightly.toml.sha256"); - let sha_str = fs::read_to_string(&sha_file).unwrap(); - let mut sha_bytes = sha_str.into_bytes(); - sha_bytes[..10].clone_from_slice(b"aaaaaaaaaa"); - let sha_str = String::from_utf8(sha_bytes).unwrap(); - rustup::utils::raw::write_file(&sha_file, &sha_str).unwrap(); - // We fail because the sha is bad, but we should emit the special message to that effect. - config.expect_err( +#[tokio::test] +async fn bad_sha_on_manifest() { + let cx = CliTestContext::new(Scenario::SimpleV2).await; + // Corrupt the sha + let sha_file = cx + .config + .distdir + .as_ref() + .unwrap() + .join("dist/channel-rust-nightly.toml.sha256"); + let sha_str = fs::read_to_string(&sha_file).unwrap(); + let mut sha_bytes = sha_str.into_bytes(); + sha_bytes[..10].clone_from_slice(b"aaaaaaaaaa"); + let sha_str = String::from_utf8(sha_bytes).unwrap(); + rustup::utils::raw::write_file(&sha_file, &sha_str).unwrap(); + // We fail because the sha is bad, but we should emit the special message to that effect. + cx.config + .expect_err( &["rustup", "default", "nightly"], "update not yet available", - ); - }); + ) + .await; } -#[test] -fn bad_manifest() { +#[tokio::test] +async fn bad_manifest() { // issue #3851 - setup(&|config| { - // install some toolchain - config.expect_ok(&["rustup", "update", "nightly"]); - - #[cfg(not(target_os = "windows"))] - let path = format!( - "toolchains/nightly-{}/lib/rustlib/multirust-channel-manifest.toml", - this_host_triple(), - ); - - #[cfg(target_os = "windows")] - let path = format!( - r"toolchains\nightly-{}\lib/rustlib\multirust-channel-manifest.toml", - this_host_triple(), - ); - - assert!(config.rustupdir.has(&path)); - let path = config.rustupdir.join(&path); - - // corrupt the manifest file by inserting a NUL byte at some position - let old = fs::read_to_string(&path).unwrap(); - let pattern = "[[pkg.rust.targ"; - let (prefix, suffix) = old.split_once(pattern).unwrap(); - let new = format!("{prefix}{pattern}\u{0}{suffix}"); - fs::write(&path, new).unwrap(); - - // run some commands that try to load the manifest and - // check that the manifest parsing error includes the manifest file path - config.expect_err( + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + // install some toolchain + cx.config.expect_ok(&["rustup", "update", "nightly"]).await; + + #[cfg(not(target_os = "windows"))] + let path = format!( + "toolchains/nightly-{}/lib/rustlib/multirust-channel-manifest.toml", + this_host_triple(), + ); + + #[cfg(target_os = "windows")] + let path = format!( + r"toolchains\nightly-{}\lib/rustlib\multirust-channel-manifest.toml", + this_host_triple(), + ); + + assert!(cx.config.rustupdir.has(&path)); + let path = cx.config.rustupdir.join(&path); + + // corrupt the manifest file by inserting a NUL byte at some position + let old = fs::read_to_string(&path).unwrap(); + let pattern = "[[pkg.rust.targ"; + let (prefix, suffix) = old.split_once(pattern).unwrap(); + let new = format!("{prefix}{pattern}\u{0}{suffix}"); + fs::write(&path, new).unwrap(); + + // run some commands that try to load the manifest and + // check that the manifest parsing error includes the manifest file path + cx.config + .expect_err( &["rustup", "check"], &format!("error: could not parse manifest file: '{}'", path.display()), - ); - config.expect_err( + ) + .await; + cx.config + .expect_err( &["cargo", "--help"], &format!("error: could not parse manifest file: '{}'", path.display()), - ); - }); -} - -#[test] -fn bad_sha_on_installer() { - setup(&|config| { - // Since the v2 sha's are contained in the manifest, corrupt the installer - let dir = config.distdir.as_ref().unwrap().join("dist/2015-01-02"); - for file in fs::read_dir(dir).unwrap() { - let file = file.unwrap(); - let path = file.path(); - let filename = path.to_string_lossy(); - if filename.ends_with(".tar.gz") - || filename.ends_with(".tar.xz") - || filename.ends_with(".tar.zst") - { - rustup::utils::raw::write_file(&path, "xxx").unwrap(); - } + ) + .await; +} + +#[tokio::test] +async fn bad_sha_on_installer() { + let cx = CliTestContext::new(Scenario::SimpleV2).await; + // Since the v2 sha's are contained in the manifest, corrupt the installer + let dir = cx.config.distdir.as_ref().unwrap().join("dist/2015-01-02"); + for file in fs::read_dir(dir).unwrap() { + let file = file.unwrap(); + let path = file.path(); + let filename = path.to_string_lossy(); + if filename.ends_with(".tar.gz") + || filename.ends_with(".tar.xz") + || filename.ends_with(".tar.zst") + { + rustup::utils::raw::write_file(&path, "xxx").unwrap(); } - config.expect_err(&["rustup", "default", "nightly"], "checksum failed"); - }); -} - -#[test] -fn install_override_toolchain_from_channel() { - setup(&|config| { - config.expect_ok(&["rustup", "override", "add", "nightly"]); - config.expect_stdout_ok(&["rustc", "--version"], "hash-nightly-2"); - config.expect_ok(&["rustup", "override", "add", "beta"]); - config.expect_stdout_ok(&["rustc", "--version"], "hash-beta-1.2.0"); - config.expect_ok(&["rustup", "override", "add", "stable"]); - config.expect_stdout_ok(&["rustc", "--version"], "hash-stable-1.1.0"); - }); -} - -#[test] -fn install_override_toolchain_from_archive() { - clitools::test(Scenario::ArchivesV2, &|config| { - config.expect_ok(&["rustup", "override", "add", "nightly-2015-01-01"]); - config.expect_stdout_ok(&["rustc", "--version"], "hash-nightly-1"); - config.expect_ok(&["rustup", "override", "add", "beta-2015-01-01"]); - config.expect_stdout_ok(&["rustc", "--version"], "hash-beta-1.1.0"); - config.expect_ok(&["rustup", "override", "add", "stable-2015-01-01"]); - config.expect_stdout_ok(&["rustc", "--version"], "hash-stable-1.0.0"); - }); -} - -#[test] -fn install_override_toolchain_from_version() { - setup(&|config| { - config.expect_ok(&["rustup", "override", "add", "1.1.0"]); - config.expect_stdout_ok(&["rustc", "--version"], "hash-stable-1.1.0"); - }); -} - -#[test] -fn override_overrides_default() { - setup(&|config| { - let tempdir = tempfile::Builder::new().prefix("rustup").tempdir().unwrap(); - config.expect_ok(&["rustup", "default", "nightly"]); - config.change_dir(tempdir.path(), &|config| { - config.expect_ok(&["rustup", "override", "add", "beta"]); - config.expect_stdout_ok(&["rustc", "--version"], "hash-beta-1.2.0"); - }); - }); -} - -#[test] -fn multiple_overrides() { - setup(&|config| { - let tempdir1 = tempfile::Builder::new().prefix("rustup").tempdir().unwrap(); - let tempdir2 = tempfile::Builder::new().prefix("rustup").tempdir().unwrap(); - - config.expect_ok(&["rustup", "default", "nightly"]); - config.change_dir(tempdir1.path(), &|config| { - config.expect_ok(&["rustup", "override", "add", "beta"]); - }); - config.change_dir(tempdir2.path(), &|config| { - config.expect_ok(&["rustup", "override", "add", "stable"]); - }); - - config.expect_stdout_ok(&["rustc", "--version"], "hash-nightly-2"); - - config.change_dir(tempdir1.path(), &|config| { - config.expect_stdout_ok(&["rustc", "--version"], "hash-beta-1.2.0"); - }); - config.change_dir(tempdir2.path(), &|config| { - config.expect_stdout_ok(&["rustc", "--version"], "hash-stable-1.1.0"); - }); - }); + } + cx.config + .expect_err(&["rustup", "default", "nightly"], "checksum failed") + .await; +} + +#[tokio::test] +async fn install_override_toolchain_from_channel() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config + .expect_ok(&["rustup", "override", "add", "nightly"]) + .await; + cx.config + .expect_stdout_ok(&["rustc", "--version"], "hash-nightly-2") + .await; + cx.config + .expect_ok(&["rustup", "override", "add", "beta"]) + .await; + cx.config + .expect_stdout_ok(&["rustc", "--version"], "hash-beta-1.2.0") + .await; + cx.config + .expect_ok(&["rustup", "override", "add", "stable"]) + .await; + cx.config + .expect_stdout_ok(&["rustc", "--version"], "hash-stable-1.1.0") + .await; +} + +#[tokio::test] +async fn install_override_toolchain_from_archive() { + let mut cx = CliTestContext::new(Scenario::ArchivesV2).await; + cx.config + .expect_ok(&["rustup", "override", "add", "nightly-2015-01-01"]) + .await; + cx.config + .expect_stdout_ok(&["rustc", "--version"], "hash-nightly-1") + .await; + cx.config + .expect_ok(&["rustup", "override", "add", "beta-2015-01-01"]) + .await; + cx.config + .expect_stdout_ok(&["rustc", "--version"], "hash-beta-1.1.0") + .await; + cx.config + .expect_ok(&["rustup", "override", "add", "stable-2015-01-01"]) + .await; + cx.config + .expect_stdout_ok(&["rustc", "--version"], "hash-stable-1.0.0") + .await; +} + +#[tokio::test] +async fn install_override_toolchain_from_version() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config + .expect_ok(&["rustup", "override", "add", "1.1.0"]) + .await; + cx.config + .expect_stdout_ok(&["rustc", "--version"], "hash-stable-1.1.0") + .await; +} + +#[tokio::test] +async fn override_overrides_default() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + let tempdir = tempfile::Builder::new().prefix("rustup").tempdir().unwrap(); + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + + let mut cx = cx.change_dir(tempdir.path()); + cx.config + .expect_ok(&["rustup", "override", "add", "beta"]) + .await; + cx.config + .expect_stdout_ok(&["rustc", "--version"], "hash-beta-1.2.0") + .await; +} + +#[tokio::test] +async fn multiple_overrides() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + let tempdir1 = tempfile::Builder::new().prefix("rustup").tempdir().unwrap(); + let tempdir2 = tempfile::Builder::new().prefix("rustup").tempdir().unwrap(); + + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + + { + let mut cx = cx.change_dir(tempdir1.path()); + cx.config + .expect_ok(&["rustup", "override", "add", "beta"]) + .await; + } + + { + let mut cx = cx.change_dir(tempdir2.path()); + cx.config + .expect_ok(&["rustup", "override", "add", "stable"]) + .await; + } + + cx.config + .expect_stdout_ok(&["rustc", "--version"], "hash-nightly-2") + .await; + + { + let cx = cx.change_dir(tempdir1.path()); + cx.config + .expect_stdout_ok(&["rustc", "--version"], "hash-beta-1.2.0") + .await; + } + + { + let cx = cx.change_dir(tempdir2.path()); + cx.config + .expect_stdout_ok(&["rustc", "--version"], "hash-stable-1.1.0") + .await; + } } // #316 -#[test] #[cfg(windows)] -fn override_windows_root() { - setup(&|config| { - use std::path::{Component, PathBuf}; - - let cwd = config.current_dir(); - let prefix = cwd.components().next().unwrap(); - let prefix = match prefix { - Component::Prefix(p) => p, - _ => panic!(), - }; - - // This value is probably "C:" - // Really sketchy to be messing with C:\ in a test... - let prefix = prefix.as_os_str().to_str().unwrap(); - let prefix = format!("{prefix}\\"); - config.change_dir(&PathBuf::from(&prefix), &|config| { - config.expect_ok(&["rustup", "default", "stable"]); - config.expect_ok(&["rustup", "override", "add", "nightly"]); - config.expect_stdout_ok(&["rustc", "--version"], "hash-nightly-2"); - config.expect_ok(&["rustup", "override", "remove"]); - config.expect_stdout_ok(&["rustc", "--version"], "hash-stable-1.1.0"); - }); - }); -} - -#[test] -fn change_override() { - setup(&|config| { - let tempdir = tempfile::Builder::new().prefix("rustup").tempdir().unwrap(); - config.change_dir(tempdir.path(), &|config| { - config.expect_ok(&["rustup", "override", "add", "nightly"]); - config.expect_ok(&["rustup", "override", "add", "beta"]); - config.expect_stdout_ok(&["rustc", "--version"], "hash-beta-1.2.0"); - }); - }); -} - -#[test] -fn remove_override_no_default() { - setup(&|config| { - let tempdir = tempfile::Builder::new().prefix("rustup").tempdir().unwrap(); - config.change_dir(tempdir.path(), &|config| { - config.expect_ok(&["rustup", "override", "add", "nightly"]); - config.expect_ok(&["rustup", "override", "remove"]); - config.expect_err( - &["rustc"], - "rustup could not choose a version of rustc to run", - ); - }); - }); -} - -#[test] -fn remove_override_with_default() { - setup(&|config| { - let tempdir = tempfile::Builder::new().prefix("rustup").tempdir().unwrap(); - config.change_dir(tempdir.path(), &|config| { - config.expect_ok(&["rustup", "default", "nightly"]); - config.expect_ok(&["rustup", "override", "add", "beta"]); - config.expect_ok(&["rustup", "override", "remove"]); - config.expect_stdout_ok(&["rustc", "--version"], "hash-nightly-2"); - }); - }); -} - -#[test] -fn remove_override_with_multiple_overrides() { - setup(&|config| { - let tempdir1 = tempfile::Builder::new().prefix("rustup").tempdir().unwrap(); - let tempdir2 = tempfile::Builder::new().prefix("rustup").tempdir().unwrap(); - config.expect_ok(&["rustup", "default", "nightly"]); - config.change_dir(tempdir1.path(), &|config| { - config.expect_ok(&["rustup", "override", "add", "beta"]); - }); - config.change_dir(tempdir2.path(), &|config| { - config.expect_ok(&["rustup", "override", "add", "stable"]); - }); - config.expect_stdout_ok(&["rustc", "--version"], "hash-nightly-2"); - config.change_dir(tempdir1.path(), &|config| { - config.expect_ok(&["rustup", "override", "remove"]); - config.expect_stdout_ok(&["rustc", "--version"], "hash-nightly-2"); - }); - config.change_dir(tempdir2.path(), &|config| { - config.expect_stdout_ok(&["rustc", "--version"], "hash-stable-1.1.0"); - }); - }); -} - -#[test] -fn no_update_on_channel_when_date_has_not_changed() { - setup(&|config| { - config.expect_ok(&["rustup", "update", "nightly"]); - config.expect_stdout_ok(&["rustup", "update", "nightly"], "unchanged"); - }); -} - -#[test] -fn update_on_channel_when_date_has_changed() { - clitools::test(Scenario::ArchivesV2, &|config| { - set_current_dist_date(config, "2015-01-01"); - config.expect_ok(&["rustup", "default", "nightly"]); - config.expect_stdout_ok(&["rustc", "--version"], "hash-nightly-1"); - set_current_dist_date(config, "2015-01-02"); - config.expect_ok(&["rustup", "update", "nightly"]); - config.expect_stdout_ok(&["rustc", "--version"], "hash-nightly-2"); - }); -} - -#[test] -fn run_command() { - setup(&|config| { - config.expect_ok(&["rustup", "update", "nightly"]); - config.expect_ok(&["rustup", "default", "beta"]); - config.expect_stdout_ok( +#[tokio::test] +async fn override_windows_root() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + use std::path::{Component, PathBuf}; + + let cwd = cx.config.current_dir(); + let prefix = cwd.components().next().unwrap(); + let prefix = match prefix { + Component::Prefix(p) => p, + _ => panic!(), + }; + + // This value is probably "C:" + // Really sketchy to be messing with C:\ in a test... + let prefix = prefix.as_os_str().to_str().unwrap(); + let prefix = format!("{prefix}\\"); + let mut cx = cx.change_dir(&PathBuf::from(&prefix)); + cx.config.expect_ok(&["rustup", "default", "stable"]).await; + cx.config + .expect_ok(&["rustup", "override", "add", "nightly"]) + .await; + cx.config + .expect_stdout_ok(&["rustc", "--version"], "hash-nightly-2") + .await; + cx.config.expect_ok(&["rustup", "override", "remove"]).await; + cx.config + .expect_stdout_ok(&["rustc", "--version"], "hash-stable-1.1.0") + .await; +} + +#[tokio::test] +async fn change_override() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + let tempdir = tempfile::Builder::new().prefix("rustup").tempdir().unwrap(); + let mut cx = cx.change_dir(tempdir.path()); + cx.config + .expect_ok(&["rustup", "override", "add", "nightly"]) + .await; + cx.config + .expect_ok(&["rustup", "override", "add", "beta"]) + .await; + cx.config + .expect_stdout_ok(&["rustc", "--version"], "hash-beta-1.2.0") + .await; +} + +#[tokio::test] +async fn remove_override_no_default() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + let tempdir = tempfile::Builder::new().prefix("rustup").tempdir().unwrap(); + let mut cx = cx.change_dir(tempdir.path()); + cx.config + .expect_ok(&["rustup", "override", "add", "nightly"]) + .await; + cx.config.expect_ok(&["rustup", "override", "remove"]).await; + cx.config + .expect_err( + &["rustc"], + "rustup could not choose a version of rustc to run", + ) + .await; +} + +#[tokio::test] +async fn remove_override_with_default() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + let tempdir = tempfile::Builder::new().prefix("rustup").tempdir().unwrap(); + let mut cx = cx.change_dir(tempdir.path()); + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + cx.config + .expect_ok(&["rustup", "override", "add", "beta"]) + .await; + cx.config.expect_ok(&["rustup", "override", "remove"]).await; + cx.config + .expect_stdout_ok(&["rustc", "--version"], "hash-nightly-2") + .await; +} + +#[tokio::test] +async fn remove_override_with_multiple_overrides() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + let tempdir1 = tempfile::Builder::new().prefix("rustup").tempdir().unwrap(); + let tempdir2 = tempfile::Builder::new().prefix("rustup").tempdir().unwrap(); + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + + { + let mut cx = cx.change_dir(tempdir1.path()); + cx.config + .expect_ok(&["rustup", "override", "add", "beta"]) + .await; + } + + { + let mut cx = cx.change_dir(tempdir2.path()); + cx.config + .expect_ok(&["rustup", "override", "add", "stable"]) + .await; + } + + cx.config + .expect_stdout_ok(&["rustc", "--version"], "hash-nightly-2") + .await; + + { + let mut cx = cx.change_dir(tempdir1.path()); + cx.config.expect_ok(&["rustup", "override", "remove"]).await; + cx.config + .expect_stdout_ok(&["rustc", "--version"], "hash-nightly-2") + .await; + } + + { + let cx = cx.change_dir(tempdir2.path()); + cx.config + .expect_stdout_ok(&["rustc", "--version"], "hash-stable-1.1.0") + .await; + } +} + +#[tokio::test] +async fn no_update_on_channel_when_date_has_not_changed() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "update", "nightly"]).await; + cx.config + .expect_stdout_ok(&["rustup", "update", "nightly"], "unchanged") + .await; +} + +#[tokio::test] +async fn update_on_channel_when_date_has_changed() { + let mut cx = CliTestContext::new(Scenario::ArchivesV2).await; + set_current_dist_date(&cx.config, "2015-01-01"); + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + cx.config + .expect_stdout_ok(&["rustc", "--version"], "hash-nightly-1") + .await; + set_current_dist_date(&cx.config, "2015-01-02"); + cx.config.expect_ok(&["rustup", "update", "nightly"]).await; + cx.config + .expect_stdout_ok(&["rustc", "--version"], "hash-nightly-2") + .await; +} + +#[tokio::test] +async fn run_command() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "update", "nightly"]).await; + cx.config.expect_ok(&["rustup", "default", "beta"]).await; + cx.config + .expect_stdout_ok( &["rustup", "run", "nightly", "rustc", "--version"], "hash-nightly-2", - ); - }); + ) + .await; } -#[test] -fn remove_toolchain_then_add_again() { +#[tokio::test] +async fn remove_toolchain_then_add_again() { // Issue brson/multirust #53 - setup(&|config| { - config.expect_ok(&["rustup", "default", "beta"]); - config.expect_ok(&["rustup", "toolchain", "remove", "beta"]); - config.expect_ok(&["rustup", "update", "beta"]); - config.expect_ok(&["rustc", "--version"]); - }); -} - -#[test] -fn upgrade_v1_to_v2() { - clitools::test(Scenario::Full, &|config| { - set_current_dist_date(config, "2015-01-01"); - // Delete the v2 manifest so the first day we install from the v1s - fs::remove_file( - config - .distdir - .as_ref() - .unwrap() - .join("dist/channel-rust-nightly.toml.sha256"), - ) - .unwrap(); - config.expect_ok(&["rustup", "default", "nightly"]); - config.expect_stdout_ok(&["rustc", "--version"], "hash-nightly-1"); - set_current_dist_date(config, "2015-01-02"); - config.expect_ok(&["rustup", "update", "nightly"]); - config.expect_stdout_ok(&["rustc", "--version"], "hash-nightly-2"); - }); -} - -#[test] -fn upgrade_v2_to_v1() { - clitools::test(Scenario::Full, &|config| { - set_current_dist_date(config, "2015-01-01"); - config.expect_ok(&["rustup", "default", "nightly"]); - set_current_dist_date(config, "2015-01-02"); - fs::remove_file( - config - .distdir - .as_ref() - .unwrap() - .join("dist/channel-rust-nightly.toml.sha256"), - ) - .unwrap(); - config.expect_err( + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "default", "beta"]).await; + cx.config + .expect_ok(&["rustup", "toolchain", "remove", "beta"]) + .await; + cx.config.expect_ok(&["rustup", "update", "beta"]).await; + cx.config.expect_ok(&["rustc", "--version"]).await; +} + +#[tokio::test] +async fn upgrade_v1_to_v2() { + let mut cx = CliTestContext::new(Scenario::Full).await; + set_current_dist_date(&cx.config, "2015-01-01"); + // Delete the v2 manifest so the first day we install from the v1s + fs::remove_file( + cx.config + .distdir + .as_ref() + .unwrap() + .join("dist/channel-rust-nightly.toml.sha256"), + ) + .unwrap(); + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + cx.config + .expect_stdout_ok(&["rustc", "--version"], "hash-nightly-1") + .await; + set_current_dist_date(&cx.config, "2015-01-02"); + cx.config.expect_ok(&["rustup", "update", "nightly"]).await; + cx.config + .expect_stdout_ok(&["rustc", "--version"], "hash-nightly-2") + .await; +} + +#[tokio::test] +async fn upgrade_v2_to_v1() { + let mut cx = CliTestContext::new(Scenario::Full).await; + set_current_dist_date(&cx.config, "2015-01-01"); + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + set_current_dist_date(&cx.config, "2015-01-02"); + fs::remove_file( + cx.config + .distdir + .as_ref() + .unwrap() + .join("dist/channel-rust-nightly.toml.sha256"), + ) + .unwrap(); + cx.config + .expect_err( &["rustup", "update", "nightly"], "the server unexpectedly provided an obsolete version of the distribution manifest", - ); - }); + ) + .await; } -#[test] -fn list_targets_no_toolchain() { - setup(&|config| { - config.expect_err( +#[tokio::test] +async fn list_targets_no_toolchain() { + let cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config + .expect_err( &["rustup", "target", "list", "--toolchain=nightly"], for_host!("toolchain 'nightly-{0}' is not installed"), - ); - }); + ) + .await; } -#[test] -fn list_targets_v1_toolchain() { - clitools::test(Scenario::SimpleV1, &|config| { - config.expect_ok(&["rustup", "update", "nightly"]); - config.expect_err( +#[tokio::test] +async fn list_targets_v1_toolchain() { + let mut cx = CliTestContext::new(Scenario::SimpleV1).await; + cx.config.expect_ok(&["rustup", "update", "nightly"]).await; + cx.config + .expect_err( &["rustup", "target", "list", "--toolchain=nightly"], for_host!("toolchain 'nightly-{0}' does not support components"), - ); - }); -} - -#[test] -fn list_targets_custom_toolchain() { - setup(&|config| { - let path = config.customdir.join("custom-1"); - let path = path.to_string_lossy(); - config.expect_ok(&["rustup", "toolchain", "link", "default-from-path", &path]); - config.expect_ok(&["rustup", "default", "default-from-path"]); - config.expect_err( + ) + .await; +} + +#[tokio::test] +async fn list_targets_custom_toolchain() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + let path = cx.config.customdir.join("custom-1"); + let path = path.to_string_lossy(); + cx.config + .expect_ok(&["rustup", "toolchain", "link", "default-from-path", &path]) + .await; + cx.config + .expect_ok(&["rustup", "default", "default-from-path"]) + .await; + cx.config + .expect_err( &["rustup", "target", "list"], "toolchain 'default-from-path' does not support components", - ); - }); -} - -#[test] -fn list_targets() { - setup(&|config| { - config.expect_ok(&["rustup", "default", "nightly"]); - config.expect_stdout_ok(&["rustup", "target", "list"], clitools::CROSS_ARCH1); - config.expect_stdout_ok(&["rustup", "target", "list"], clitools::CROSS_ARCH2); - }); -} - -#[test] -fn list_installed_targets() { - setup(&|config| { - let trip = this_host_triple(); - - config.expect_ok(&["rustup", "default", "nightly"]); - config.expect_stdout_ok(&["rustup", "target", "list", "--installed"], &trip); - }); -} - -#[test] -fn add_target1() { - setup(&|config| { - config.expect_ok(&["rustup", "default", "nightly"]); - config.expect_ok(&["rustup", "target", "add", clitools::CROSS_ARCH1]); - let path = format!( - "toolchains/nightly-{}/lib/rustlib/{}/lib/libstd.rlib", - this_host_triple(), - clitools::CROSS_ARCH1 - ); - assert!(config.rustupdir.has(path)); - }); -} - -#[test] -fn add_target2() { - setup(&|config| { - config.expect_ok(&["rustup", "default", "nightly"]); - config.expect_ok(&["rustup", "target", "add", clitools::CROSS_ARCH2]); - let path = format!( - "toolchains/nightly-{}/lib/rustlib/{}/lib/libstd.rlib", - this_host_triple(), - clitools::CROSS_ARCH2 - ); - assert!(config.rustupdir.has(path)); - }); -} - -#[test] -fn add_all_targets() { - setup(&|config| { - config.expect_ok(&["rustup", "default", "nightly"]); - config.expect_ok(&["rustup", "target", "add", "all"]); - let path = format!( - "toolchains/nightly-{}/lib/rustlib/{}/lib/libstd.rlib", - this_host_triple(), - clitools::CROSS_ARCH1 - ); - assert!(config.rustupdir.has(path)); - let path = format!( - "toolchains/nightly-{}/lib/rustlib/{}/lib/libstd.rlib", - this_host_triple(), - clitools::CROSS_ARCH2 - ); - assert!(config.rustupdir.has(path)); - }); -} - -#[test] -fn add_all_targets_fail() { - setup(&|config| { - config.expect_ok(&["rustup", "default", "nightly"]); - config.expect_err( + ) + .await; +} + +#[tokio::test] +async fn list_targets() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + cx.config + .expect_stdout_ok(&["rustup", "target", "list"], clitools::CROSS_ARCH1) + .await; + cx.config + .expect_stdout_ok(&["rustup", "target", "list"], clitools::CROSS_ARCH2) + .await; +} + +#[tokio::test] +async fn list_installed_targets() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + let trip = this_host_triple(); + + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + cx.config + .expect_stdout_ok(&["rustup", "target", "list", "--installed"], &trip) + .await; +} + +#[tokio::test] +async fn add_target1() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + cx.config + .expect_ok(&["rustup", "target", "add", clitools::CROSS_ARCH1]) + .await; + let path = format!( + "toolchains/nightly-{}/lib/rustlib/{}/lib/libstd.rlib", + this_host_triple(), + clitools::CROSS_ARCH1 + ); + assert!(cx.config.rustupdir.has(path)); +} + +#[tokio::test] +async fn add_target2() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + cx.config + .expect_ok(&["rustup", "target", "add", clitools::CROSS_ARCH2]) + .await; + let path = format!( + "toolchains/nightly-{}/lib/rustlib/{}/lib/libstd.rlib", + this_host_triple(), + clitools::CROSS_ARCH2 + ); + assert!(cx.config.rustupdir.has(path)); +} + +#[tokio::test] +async fn add_all_targets() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + cx.config + .expect_ok(&["rustup", "target", "add", "all"]) + .await; + let path = format!( + "toolchains/nightly-{}/lib/rustlib/{}/lib/libstd.rlib", + this_host_triple(), + clitools::CROSS_ARCH1 + ); + assert!(cx.config.rustupdir.has(path)); + let path = format!( + "toolchains/nightly-{}/lib/rustlib/{}/lib/libstd.rlib", + this_host_triple(), + clitools::CROSS_ARCH2 + ); + assert!(cx.config.rustupdir.has(path)); +} + +#[tokio::test] +async fn add_all_targets_fail() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + cx.config + .expect_err( &[ "rustup", "target", @@ -725,57 +895,70 @@ fn add_all_targets_fail() { clitools::CROSS_ARCH1, clitools::CROSS_ARCH2 ), - ); - }); + ) + .await; } -#[test] -fn add_target_by_component_add() { - setup(&|config| { - config.expect_ok(&["rustup", "default", "nightly"]); - config.expect_not_stdout_ok( +#[tokio::test] +async fn add_target_by_component_add() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + cx.config + .expect_not_stdout_ok( &["rustup", "target", "list"], &format!("{} (installed)", clitools::CROSS_ARCH1), - ); - config.expect_ok(&[ + ) + .await; + cx.config + .expect_ok(&[ "rustup", "component", "add", &format!("rust-std-{}", clitools::CROSS_ARCH1), - ]); - config.expect_stdout_ok( + ]) + .await; + cx.config + .expect_stdout_ok( &["rustup", "target", "list"], &format!("{} (installed)", clitools::CROSS_ARCH1), - ); - }) -} - -#[test] -fn remove_target_by_component_remove() { - setup(&|config| { - config.expect_ok(&["rustup", "default", "nightly"]); - config.expect_ok(&["rustup", "target", "add", clitools::CROSS_ARCH1]); - config.expect_stdout_ok( + ) + .await; +} + +#[tokio::test] +async fn remove_target_by_component_remove() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + cx.config + .expect_ok(&["rustup", "target", "add", clitools::CROSS_ARCH1]) + .await; + cx.config + .expect_stdout_ok( &["rustup", "target", "list"], &format!("{} (installed)", clitools::CROSS_ARCH1), - ); - config.expect_ok(&[ + ) + .await; + cx.config + .expect_ok(&[ "rustup", "component", "remove", &format!("rust-std-{}", clitools::CROSS_ARCH1), - ]); - config.expect_not_stdout_ok( + ]) + .await; + cx.config + .expect_not_stdout_ok( &["rustup", "target", "list"], &format!("{} (installed)", clitools::CROSS_ARCH1), - ); - }) + ) + .await; } -#[test] -fn add_target_no_toolchain() { - setup(&|config| { - config.expect_err( +#[tokio::test] +async fn add_target_no_toolchain() { + let cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config + .expect_err( &[ "rustup", "target", @@ -784,27 +967,27 @@ fn add_target_no_toolchain() { "--toolchain=nightly", ], for_host!("toolchain 'nightly-{0}' is not installed"), - ); - }); -} -#[test] -fn add_target_bogus() { - setup(&|config| { - config.expect_ok(&["rustup", "default", "nightly"]); - config.expect_err( - &["rustup", "target", "add", "bogus"], - "does not support target 'bogus'\n\ - note: you can see a list of supported targets with `rustc --print=target-list`\n\ - note: if you are adding support for a new target to rustc itself, see https://rustc-dev-guide.rust-lang.org/building/new-target.html", - ); - }); -} - -#[test] -fn add_target_v1_toolchain() { - clitools::test(Scenario::SimpleV1, &|config| { - config.expect_ok(&["rustup", "update", "nightly"]); - config.expect_err( + ) + .await; +} +#[tokio::test] +async fn add_target_bogus() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + cx.config.expect_err( + &["rustup", "target", "add", "bogus"], + "does not support target 'bogus'\n\ + note: you can see a list of supported targets with `rustc --print=target-list`\n\ + note: if you are adding support for a new target to rustc itself, see https://rustc-dev-guide.rust-lang.org/building/new-target.html", + ).await; +} + +#[tokio::test] +async fn add_target_v1_toolchain() { + let mut cx = CliTestContext::new(Scenario::SimpleV1).await; + cx.config.expect_ok(&["rustup", "update", "nightly"]).await; + cx.config + .expect_err( &[ "rustup", "target", @@ -813,112 +996,127 @@ fn add_target_v1_toolchain() { "--toolchain=nightly", ], for_host!("toolchain 'nightly-{0}' does not support components (v1 manifest)"), - ); - }); -} - -#[test] -fn add_target_custom_toolchain() { - setup(&|config| { - let path = config.customdir.join("custom-1"); - let path = path.to_string_lossy(); - config.expect_ok(&["rustup", "toolchain", "link", "default-from-path", &path]); - config.expect_ok(&["rustup", "default", "default-from-path"]); - config.expect_err( + ) + .await; +} + +#[tokio::test] +async fn add_target_custom_toolchain() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + let path = cx.config.customdir.join("custom-1"); + let path = path.to_string_lossy(); + cx.config + .expect_ok(&["rustup", "toolchain", "link", "default-from-path", &path]) + .await; + cx.config + .expect_ok(&["rustup", "default", "default-from-path"]) + .await; + cx.config + .expect_err( &["rustup", "target", "add", clitools::CROSS_ARCH1], "toolchain 'default-from-path' does not support components", - ); - }); + ) + .await; } -#[test] -fn cannot_add_empty_named_custom_toolchain() { - setup(&|config| { - let path = config.customdir.join("custom-1"); - let path = path.to_string_lossy(); - config.expect_err( +#[tokio::test] +async fn cannot_add_empty_named_custom_toolchain() { + let cx = CliTestContext::new(Scenario::SimpleV2).await; + let path = cx.config.customdir.join("custom-1"); + let path = path.to_string_lossy(); + cx.config + .expect_err( &["rustup", "toolchain", "link", "", &path], "invalid value '' for '': invalid toolchain name ''", - ); - }); -} - -#[test] -fn add_target_again() { - setup(&|config| { - config.expect_ok(&["rustup", "default", "nightly"]); - config.expect_ok(&["rustup", "target", "add", clitools::CROSS_ARCH1]); - config.expect_stderr_ok( + ) + .await; +} + +#[tokio::test] +async fn add_target_again() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + cx.config + .expect_ok(&["rustup", "target", "add", clitools::CROSS_ARCH1]) + .await; + cx.config + .expect_stderr_ok( &["rustup", "target", "add", clitools::CROSS_ARCH1], &format!( "component 'rust-std' for target '{}' is up to date", clitools::CROSS_ARCH1 ), - ); - let path = format!( - "toolchains/nightly-{}/lib/rustlib/{}/lib/libstd.rlib", - this_host_triple(), - clitools::CROSS_ARCH1 - ); - assert!(config.rustupdir.has(path)); - }); -} - -#[test] -fn add_target_host() { - setup(&|config| { - let trip = this_host_triple(); - config.expect_ok(&["rustup", "default", "nightly"]); - config.expect_ok(&["rustup", "target", "add", &trip]); - }); -} - -#[test] -fn remove_target() { - setup(&|config| { - config.expect_ok(&["rustup", "default", "nightly"]); - config.expect_ok(&["rustup", "target", "add", clitools::CROSS_ARCH1]); - config.expect_ok(&["rustup", "target", "remove", clitools::CROSS_ARCH1]); - let path = format!( - "toolchains/nightly-{}/lib/rustlib/{}/lib/libstd.rlib", - this_host_triple(), - clitools::CROSS_ARCH1 - ); - assert!(!config.rustupdir.has(path)); - let path = format!( - "toolchains/nightly-{}/lib/rustlib/{}/lib", - this_host_triple(), - clitools::CROSS_ARCH1 - ); - assert!(!config.rustupdir.has(path)); - let path = format!( - "toolchains/nightly-{}/lib/rustlib/{}", - this_host_triple(), - clitools::CROSS_ARCH1 - ); - assert!(!config.rustupdir.has(path)); - }); -} - -#[test] -fn remove_target_not_installed() { - setup(&|config| { - config.expect_ok(&["rustup", "default", "nightly"]); - config.expect_err( + ) + .await; + let path = format!( + "toolchains/nightly-{}/lib/rustlib/{}/lib/libstd.rlib", + this_host_triple(), + clitools::CROSS_ARCH1 + ); + assert!(cx.config.rustupdir.has(path)); +} + +#[tokio::test] +async fn add_target_host() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + let trip = this_host_triple(); + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + cx.config + .expect_ok(&["rustup", "target", "add", &trip]) + .await; +} + +#[tokio::test] +async fn remove_target() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + cx.config + .expect_ok(&["rustup", "target", "add", clitools::CROSS_ARCH1]) + .await; + cx.config + .expect_ok(&["rustup", "target", "remove", clitools::CROSS_ARCH1]) + .await; + let path = format!( + "toolchains/nightly-{}/lib/rustlib/{}/lib/libstd.rlib", + this_host_triple(), + clitools::CROSS_ARCH1 + ); + assert!(!cx.config.rustupdir.has(path)); + let path = format!( + "toolchains/nightly-{}/lib/rustlib/{}/lib", + this_host_triple(), + clitools::CROSS_ARCH1 + ); + assert!(!cx.config.rustupdir.has(path)); + let path = format!( + "toolchains/nightly-{}/lib/rustlib/{}", + this_host_triple(), + clitools::CROSS_ARCH1 + ); + assert!(!cx.config.rustupdir.has(path)); +} + +#[tokio::test] +async fn remove_target_not_installed() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + cx.config + .expect_err( &["rustup", "target", "remove", clitools::CROSS_ARCH1], &format!( "toolchain 'nightly-{}' does not have target '{}' installed", this_host_triple(), clitools::CROSS_ARCH1 ), - ); - }); + ) + .await; } -#[test] -fn remove_target_no_toolchain() { - setup(&|config| { - config.expect_err( +#[tokio::test] +async fn remove_target_no_toolchain() { + let cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config + .expect_err( &[ "rustup", "target", @@ -927,26 +1125,28 @@ fn remove_target_no_toolchain() { "--toolchain=nightly", ], for_host!("toolchain 'nightly-{0}' is not installed"), - ); - }); + ) + .await; } -#[test] -fn remove_target_bogus() { - setup(&|config| { - config.expect_ok(&["rustup", "default", "nightly"]); - config.expect_err( +#[tokio::test] +async fn remove_target_bogus() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + cx.config + .expect_err( &["rustup", "target", "remove", "bogus"], "does not have target 'bogus' installed", - ); - }); + ) + .await; } -#[test] -fn remove_target_v1_toolchain() { - clitools::test(Scenario::SimpleV1, &|config| { - config.expect_ok(&["rustup", "default", "nightly"]); - config.expect_err( +#[tokio::test] +async fn remove_target_v1_toolchain() { + let mut cx = CliTestContext::new(Scenario::SimpleV1).await; + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + cx.config + .expect_err( &[ "rustup", "target", @@ -955,107 +1155,120 @@ fn remove_target_v1_toolchain() { "--toolchain=nightly", ], for_host!("toolchain 'nightly-{0}' does not support components (v1 manifest)"), - ); - }); -} - -#[test] -fn remove_target_custom_toolchain() { - setup(&|config| { - let path = config.customdir.join("custom-1"); - let path = path.to_string_lossy(); - config.expect_ok(&["rustup", "toolchain", "link", "default-from-path", &path]); - config.expect_ok(&["rustup", "default", "default-from-path"]); - config.expect_err( + ) + .await; +} + +#[tokio::test] +async fn remove_target_custom_toolchain() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + let path = cx.config.customdir.join("custom-1"); + let path = path.to_string_lossy(); + cx.config + .expect_ok(&["rustup", "toolchain", "link", "default-from-path", &path]) + .await; + cx.config + .expect_ok(&["rustup", "default", "default-from-path"]) + .await; + cx.config + .expect_err( &["rustup", "target", "remove", clitools::CROSS_ARCH1], "toolchain 'default-from-path' does not support components", - ); - }); -} - -#[test] -fn remove_target_again() { - setup(&|config| { - config.expect_ok(&["rustup", "default", "nightly"]); - config.expect_ok(&["rustup", "target", "add", clitools::CROSS_ARCH1]); - config.expect_ok(&["rustup", "target", "remove", clitools::CROSS_ARCH1]); - config.expect_err( + ) + .await; +} + +#[tokio::test] +async fn remove_target_again() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + cx.config + .expect_ok(&["rustup", "target", "add", clitools::CROSS_ARCH1]) + .await; + cx.config + .expect_ok(&["rustup", "target", "remove", clitools::CROSS_ARCH1]) + .await; + cx.config + .expect_err( &["rustup", "target", "remove", clitools::CROSS_ARCH1], &format!( "toolchain 'nightly-{}' does not have target '{}' installed", this_host_triple(), clitools::CROSS_ARCH1 ), - ); - }); -} - -#[test] -fn remove_target_host() { - setup(&|config| { - let host = this_host_triple(); - config.expect_ok(&["rustup", "default", "nightly"]); - config.expect_ok(&["rustup", "target", "add", clitools::CROSS_ARCH1]); - config.expect_stderr_ok( - &["rustup", "target", "remove", &host], - "after removing the default host target, proc-macros and build scripts might no longer build", - ); - let path = format!("toolchains/nightly-{host}/lib/rustlib/{host}/lib/libstd.rlib"); - assert!(!config.rustupdir.has(path)); - let path = format!("toolchains/nightly-{host}/lib/rustlib/{host}/lib"); - assert!(!config.rustupdir.has(path)); - let path = format!("toolchains/nightly-{host}/lib/rustlib/{host}"); - assert!(!config.rustupdir.has(path)); - }); -} - -#[test] -fn remove_target_last() { - setup(&|config| { - let host = this_host_triple(); - config.expect_ok(&["rustup", "default", "nightly"]); - config.expect_stderr_ok( + ) + .await; +} + +#[tokio::test] +async fn remove_target_host() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + let host = this_host_triple(); + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + cx.config + .expect_ok(&["rustup", "target", "add", clitools::CROSS_ARCH1]) + .await; + cx.config.expect_stderr_ok( + &["rustup", "target", "remove", &host], + "after removing the default host target, proc-macros and build scripts might no longer build", + ).await; + let path = format!("toolchains/nightly-{host}/lib/rustlib/{host}/lib/libstd.rlib"); + assert!(!cx.config.rustupdir.has(path)); + let path = format!("toolchains/nightly-{host}/lib/rustlib/{host}/lib"); + assert!(!cx.config.rustupdir.has(path)); + let path = format!("toolchains/nightly-{host}/lib/rustlib/{host}"); + assert!(!cx.config.rustupdir.has(path)); +} + +#[tokio::test] +async fn remove_target_last() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + let host = this_host_triple(); + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + cx.config + .expect_stderr_ok( &["rustup", "target", "remove", &host], "after removing the last target, no build targets will be available", - ); - }); + ) + .await; } -#[test] +#[tokio::test] // Issue #304 -fn remove_target_missing_update_hash() { - setup(&|config| { - config.expect_ok(&["rustup", "update", "nightly"]); +async fn remove_target_missing_update_hash() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "update", "nightly"]).await; - let file_name = format!("nightly-{}", this_host_triple()); - fs::remove_file(config.rustupdir.join("update-hashes").join(file_name)).unwrap(); + let file_name = format!("nightly-{}", this_host_triple()); + fs::remove_file(cx.config.rustupdir.join("update-hashes").join(file_name)).unwrap(); - config.expect_ok(&["rustup", "toolchain", "remove", "nightly"]); - }); + cx.config + .expect_ok(&["rustup", "toolchain", "remove", "nightly"]) + .await; } // Issue #1777 -#[test] -fn warn_about_and_remove_stray_hash() { - clitools::test(Scenario::None, &|config| { - let mut hash_path = config.rustupdir.join("update-hashes"); - fs::create_dir_all(&hash_path).expect("Unable to make the update-hashes directory"); - hash_path.push(for_host!("nightly-{}")); - let mut file = fs::File::create(&hash_path).expect("Unable to open update-hash file"); - file.write_all(b"LEGITHASH") - .expect("Unable to write update-hash"); - drop(file); - - config.with_scenario(Scenario::SimpleV2, &|config| { - config.expect_stderr_ok( - &["rustup", "toolchain", "install", "nightly"], - &format!( - "removing stray hash found at '{}' in order to continue", - hash_path.display() - ), - ); - }) - }); +#[tokio::test] +async fn warn_about_and_remove_stray_hash() { + let mut cx = CliTestContext::new(Scenario::None).await; + let mut hash_path = cx.config.rustupdir.join("update-hashes"); + fs::create_dir_all(&hash_path).expect("Unable to make the update-hashes directory"); + hash_path.push(for_host!("nightly-{}")); + let mut file = fs::File::create(&hash_path).expect("Unable to open update-hash file"); + file.write_all(b"LEGITHASH") + .expect("Unable to write update-hash"); + drop(file); + + let cx = cx.with_dist_dir(Scenario::SimpleV2); + cx.config + .expect_stderr_ok( + &["rustup", "toolchain", "install", "nightly"], + &format!( + "removing stray hash found at '{}' in order to continue", + hash_path.display() + ), + ) + .await; } fn make_component_unavailable(config: &Config, name: &str, target: String) { @@ -1084,48 +1297,52 @@ fn make_component_unavailable(config: &Config, name: &str, target: String) { create_hash(&manifest_path, &hash_path); } -#[test] -fn update_unavailable_std() { - setup(&|config| { - make_component_unavailable(config, "rust-std", this_host_triple()); - config.expect_err( - &["rustup", "update", "nightly", ], - for_host!( - "component 'rust-std' for target '{0}' is unavailable for download for channel 'nightly'" - ), - ); - }); -} - -#[test] -fn add_missing_component() { - setup(&|config| { - make_component_unavailable(config, "rls-preview", this_host_triple()); - config.expect_ok(&["rustup", "toolchain", "add", "nightly"]); - config.expect_err( +#[tokio::test] +async fn update_unavailable_std() { + let cx = CliTestContext::new(Scenario::SimpleV2).await; + make_component_unavailable(&cx.config, "rust-std", this_host_triple()); + cx.config.expect_err( + &["rustup", "update", "nightly", ], + for_host!( + "component 'rust-std' for target '{0}' is unavailable for download for channel 'nightly'" + ), + ).await; +} + +#[tokio::test] +async fn add_missing_component() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + make_component_unavailable(&cx.config, "rls-preview", this_host_triple()); + cx.config + .expect_ok(&["rustup", "toolchain", "add", "nightly"]) + .await; + cx.config + .expect_err( &["rustup", "component", "add", "rls-preview"], for_host!( - "component 'rls' for target '{0}' is unavailable for download for channel 'nightly'\n\ + "component 'rls' for target '{0}' is unavailable for download for channel 'nightly'\n\ Sometimes not all components are available in any given nightly." - ), - ); - // Make sure the following pattern does not match, - // thus addressing https://github.com/rust-lang/rustup/issues/3418. - config.expect_not_stderr_err( + ), + ) + .await; + // Make sure the following pattern does not match, + // thus addressing https://github.com/rust-lang/rustup/issues/3418. + cx.config + .expect_not_stderr_err( &["rustup", "component", "add", "rls-preview"], "If you don't need the component, you can remove it with:", - ); - }); + ) + .await; } -#[test] -fn add_missing_component_toolchain() { - setup(&|config| { - make_component_unavailable(config, "rust-std", this_host_triple()); - config.expect_err( - &["rustup", "toolchain", "add", "nightly"], - for_host!( - r"component 'rust-std' for target '{0}' is unavailable for download for channel 'nightly' +#[tokio::test] +async fn add_missing_component_toolchain() { + let cx = CliTestContext::new(Scenario::SimpleV2).await; + make_component_unavailable(&cx.config, "rust-std", this_host_triple()); + cx.config.expect_err( + &["rustup", "toolchain", "add", "nightly"], + for_host!( + r"component 'rust-std' for target '{0}' is unavailable for download for channel 'nightly' Sometimes not all components are available in any given nightly. If you don't need the component, you could try a minimal installation with: rustup toolchain add nightly --profile minimal @@ -1140,85 +1357,108 @@ After determining the correct date, install it with a command such as: Then you can use the toolchain with commands such as: cargo +nightly-2018-12-27 build" - ), - ); - }); + ), + ).await; } -#[test] -fn update_unavailable_force() { - setup(&|config| { - let trip = this_host_triple(); - config.expect_ok(&["rustup", "update", "nightly"]); - config.expect_ok(&[ +#[tokio::test] +async fn update_unavailable_force() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + let trip = this_host_triple(); + cx.config.expect_ok(&["rustup", "update", "nightly"]).await; + cx.config + .expect_ok(&[ "rustup", "component", "add", "rls", "--toolchain", "nightly", - ]); - make_component_unavailable(config, "rls-preview", trip); - config.expect_err( + ]) + .await; + make_component_unavailable(&cx.config, "rls-preview", trip); + cx.config + .expect_err( &["rustup", "update", "nightly"], for_host!( - "component 'rls' for target '{0}' is unavailable for download for channel 'nightly'" - ), - ); - config.expect_ok(&["rustup", "update", "nightly", "--force"]); - }); -} - -#[test] -fn add_component_suggest_best_match() { - setup(&|config| { - config.expect_ok(&["rustup", "default", "nightly"]); - config.expect_err( + "component 'rls' for target '{0}' is unavailable for download for channel 'nightly'" + ), + ) + .await; + cx.config + .expect_ok(&["rustup", "update", "nightly", "--force"]) + .await; +} + +#[tokio::test] +async fn add_component_suggest_best_match() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + cx.config + .expect_err( &["rustup", "component", "add", "rsl"], "did you mean 'rls'?", - ); - config.expect_err( + ) + .await; + cx.config + .expect_err( &["rustup", "component", "add", "rsl-preview"], "did you mean 'rls-preview'?", - ); - config.expect_err( + ) + .await; + cx.config + .expect_err( &["rustup", "component", "add", "rustd"], "did you mean 'rustc'?", - ); - config.expect_not_stderr_err(&["rustup", "component", "add", "potato"], "did you mean"); - }); -} - -#[test] -fn remove_component_suggest_best_match() { - setup(&|config| { - config.expect_ok(&["rustup", "default", "nightly"]); - config.expect_not_stderr_err( + ) + .await; + cx.config + .expect_not_stderr_err(&["rustup", "component", "add", "potato"], "did you mean") + .await; +} + +#[tokio::test] +async fn remove_component_suggest_best_match() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + cx.config + .expect_not_stderr_err( &["rustup", "component", "remove", "rsl"], "did you mean 'rls'?", - ); - config.expect_ok(&["rustup", "component", "add", "rls"]); - config.expect_err( + ) + .await; + cx.config + .expect_ok(&["rustup", "component", "add", "rls"]) + .await; + cx.config + .expect_err( &["rustup", "component", "remove", "rsl"], "did you mean 'rls'?", - ); - config.expect_ok(&["rustup", "component", "add", "rls-preview"]); - config.expect_err( + ) + .await; + cx.config + .expect_ok(&["rustup", "component", "add", "rls-preview"]) + .await; + cx.config + .expect_err( &["rustup", "component", "add", "rsl-preview"], "did you mean 'rls-preview'?", - ); - config.expect_err( + ) + .await; + cx.config + .expect_err( &["rustup", "component", "remove", "rustd"], "did you mean 'rustc'?", - ); - }); + ) + .await; } -#[test] -fn add_target_suggest_best_match() { - setup(&|config| { - config.expect_ok(&["rustup", "default", "nightly"]); - config.expect_err( +#[tokio::test] +async fn add_target_suggest_best_match() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + cx.config + .expect_err( &[ "rustup", "target", @@ -1226,16 +1466,19 @@ fn add_target_suggest_best_match() { &format!("{}a", clitools::CROSS_ARCH1)[..], ], &format!("did you mean '{}'", clitools::CROSS_ARCH1), - ); - config.expect_not_stderr_err(&["rustup", "target", "add", "potato"], "did you mean"); - }); -} - -#[test] -fn remove_target_suggest_best_match() { - setup(&|config| { - config.expect_ok(&["rustup", "default", "nightly"]); - config.expect_not_stderr_err( + ) + .await; + cx.config + .expect_not_stderr_err(&["rustup", "target", "add", "potato"], "did you mean") + .await; +} + +#[tokio::test] +async fn remove_target_suggest_best_match() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + cx.config + .expect_not_stderr_err( &[ "rustup", "target", @@ -1243,9 +1486,13 @@ fn remove_target_suggest_best_match() { &format!("{}a", clitools::CROSS_ARCH1)[..], ], &format!("did you mean '{}'", clitools::CROSS_ARCH1), - ); - config.expect_ok(&["rustup", "target", "add", clitools::CROSS_ARCH1]); - config.expect_err( + ) + .await; + cx.config + .expect_ok(&["rustup", "target", "add", clitools::CROSS_ARCH1]) + .await; + cx.config + .expect_err( &[ "rustup", "target", @@ -1253,73 +1500,86 @@ fn remove_target_suggest_best_match() { &format!("{}a", clitools::CROSS_ARCH1)[..], ], &format!("did you mean '{}'", clitools::CROSS_ARCH1), - ); - }); -} - -#[test] -fn target_list_ignores_unavailable_targets() { - setup(&|config| { - config.expect_ok(&["rustup", "default", "nightly"]); - let target_list = &["rustup", "target", "list"]; - config.expect_stdout_ok(target_list, clitools::CROSS_ARCH1); - make_component_unavailable(config, "rust-std", clitools::CROSS_ARCH1.to_owned()); - config.expect_ok(&["rustup", "update", "nightly", "--force"]); - config.expect_not_stdout_ok(target_list, clitools::CROSS_ARCH1); - }) -} - -#[test] -fn install_with_components() { - fn go(comp_args: &[&str]) { + ) + .await; +} + +#[tokio::test] +async fn target_list_ignores_unavailable_targets() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + let target_list = &["rustup", "target", "list"]; + cx.config + .expect_stdout_ok(target_list, clitools::CROSS_ARCH1) + .await; + make_component_unavailable(&cx.config, "rust-std", clitools::CROSS_ARCH1.to_owned()); + cx.config + .expect_ok(&["rustup", "update", "nightly", "--force"]) + .await; + cx.config + .expect_not_stdout_ok(target_list, clitools::CROSS_ARCH1) + .await; +} + +#[tokio::test] +async fn install_with_components() { + async fn go(comp_args: &[&str]) { let mut args = vec!["rustup", "toolchain", "install", "nightly"]; args.extend_from_slice(comp_args); - setup(&|config| { - config.expect_ok(&args); - config.expect_stdout_ok(&["rustup", "component", "list"], "rust-src (installed)"); - config.expect_stdout_ok( + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&args).await; + cx.config + .expect_stdout_ok(&["rustup", "component", "list"], "rust-src (installed)") + .await; + cx.config + .expect_stdout_ok( &["rustup", "component", "list"], &format!("rust-analysis-{} (installed)", this_host_triple()), - ); - }) + ) + .await; } - go(&["-c", "rust-src", "-c", "rust-analysis"]); - go(&["-c", "rust-src,rust-analysis"]); + go(&["-c", "rust-src", "-c", "rust-analysis"]).await; + go(&["-c", "rust-src,rust-analysis"]).await; } -#[test] -fn install_with_targets() { - fn go(comp_args: &[&str]) { +#[tokio::test] +async fn install_with_targets() { + async fn go(comp_args: &[&str]) { let mut args = vec!["rustup", "toolchain", "install", "nightly"]; args.extend_from_slice(comp_args); - setup(&|config| { - config.expect_ok(&args); - config.expect_stdout_ok( + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&args).await; + cx.config + .expect_stdout_ok( &["rustup", "target", "list"], &format!("{} (installed)", clitools::CROSS_ARCH1), - ); - config.expect_stdout_ok( + ) + .await; + cx.config + .expect_stdout_ok( &["rustup", "target", "list"], &format!("{} (installed)", clitools::CROSS_ARCH2), - ); - }) + ) + .await; } - go(&["-t", clitools::CROSS_ARCH1, "-t", clitools::CROSS_ARCH2]); + go(&["-t", clitools::CROSS_ARCH1, "-t", clitools::CROSS_ARCH2]).await; go(&[ "-t", &format!("{},{}", clitools::CROSS_ARCH1, clitools::CROSS_ARCH2), - ]); + ]) + .await; } -#[test] -fn install_with_component_and_target() { - setup(&|config| { - config.expect_ok(&["rustup", "default", "nightly"]); - config.expect_ok(&[ +#[tokio::test] +async fn install_with_component_and_target() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config.expect_ok(&["rustup", "default", "nightly"]).await; + cx.config + .expect_ok(&[ "rustup", "toolchain", "install", @@ -1328,23 +1588,30 @@ fn install_with_component_and_target() { "rls", "-t", clitools::CROSS_ARCH1, - ]); - config.expect_stdout_ok( + ]) + .await; + cx.config + .expect_stdout_ok( &["rustup", "component", "list"], &format!("rls-{} (installed)", this_host_triple()), - ); - config.expect_stdout_ok( + ) + .await; + cx.config + .expect_stdout_ok( &["rustup", "target", "list"], &format!("{} (installed)", clitools::CROSS_ARCH1), - ); - }) + ) + .await; } -#[test] -fn test_warn_if_complete_profile_is_used() { - setup(&|config| { - config.expect_ok(&["rustup", "set", "auto-self-update", "enable"]); - config.expect_err( +#[tokio::test] +async fn test_warn_if_complete_profile_is_used() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config + .expect_ok(&["rustup", "set", "auto-self-update", "enable"]) + .await; + cx.config + .expect_err( &[ "rustup", "toolchain", @@ -1354,46 +1621,52 @@ fn test_warn_if_complete_profile_is_used() { "stable", ], "warn: downloading with complete profile", - ); - }); + ) + .await; } -#[test] -fn test_complete_profile_skips_missing_when_forced() { - setup_complex(&|config| { - set_current_dist_date(config, "2015-01-01"); +#[tokio::test] +async fn test_complete_profile_skips_missing_when_forced() { + let mut cx = CliTestContext::new(Scenario::UnavailableRls).await; + set_current_dist_date(&cx.config, "2015-01-01"); - config.expect_ok(&["rustup", "set", "profile", "complete"]); - // First try and install without force - config.expect_err( - &[ - "rustup", - "toolchain", - "install", - "nightly", - ], - for_host!("error: component 'rls' for target '{}' is unavailable for download for channel 'nightly'") - ); - // Now try and force - config.expect_stderr_ok( + cx.config + .expect_ok(&["rustup", "set", "profile", "complete"]) + .await; + // First try and install without force + cx.config.expect_err( + &[ + "rustup", + "toolchain", + "install", + "nightly", + ], + for_host!("error: component 'rls' for target '{}' is unavailable for download for channel 'nightly'") + ).await; + // Now try and force + cx.config + .expect_stderr_ok( &["rustup", "toolchain", "install", "--force", "nightly"], for_host!("warn: Force-skipping unavailable component 'rls-{}'"), - ); + ) + .await; - // Ensure that the skipped component (rls) is not installed - config.expect_not_stdout_ok( + // Ensure that the skipped component (rls) is not installed + cx.config + .expect_not_stdout_ok( &["rustup", "component", "list"], for_host!("rls-{} (installed)"), - ); - }) + ) + .await; } -#[test] -fn run_with_install_flag_against_unavailable_component() { - setup(&|config| { - let trip = this_host_triple(); - make_component_unavailable(config, "rust-std", trip); - config.expect_ok_ex( +#[tokio::test] +async fn run_with_install_flag_against_unavailable_component() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + let trip = this_host_triple(); + make_component_unavailable(&cx.config, "rust-std", trip); + cx.config + .expect_ok_ex( &[ "rustup", "run", @@ -1416,31 +1689,40 @@ info: installing component 'rust-docs' info: installing component 'rustc' " ), - ); - }); -} - -#[test] -fn install_allow_downgrade() { - clitools::test(Scenario::MissingComponent, &|config| { - let trip = this_host_triple(); - - // this dist has no rls and there is no newer one - set_current_dist_date(config, "2019-09-14"); - config.expect_ok(&["rustup", "toolchain", "install", "nightly"]); - config.expect_stdout_ok(&["rustc", "--version"], "hash-nightly-3"); - config.expect_component_not_executable("rls"); - - config.expect_err( + ) + .await; +} + +#[tokio::test] +async fn install_allow_downgrade() { + let mut cx = CliTestContext::new(Scenario::MissingComponent).await; + let trip = this_host_triple(); + + // this dist has no rls and there is no newer one + set_current_dist_date(&cx.config, "2019-09-14"); + cx.config + .expect_ok(&["rustup", "toolchain", "install", "nightly"]) + .await; + cx.config + .expect_stdout_ok(&["rustc", "--version"], "hash-nightly-3") + .await; + cx.config.expect_component_not_executable("rls").await; + + cx.config + .expect_err( &["rustup", "toolchain", "install", "nightly", "-c", "rls"], &format!( - "component 'rls' for target '{trip}' is unavailable for download for channel 'nightly'", - ), - ); - config.expect_stdout_ok(&["rustc", "--version"], "hash-nightly-3"); - config.expect_component_not_executable("rls"); - - config.expect_ok(&[ + "component 'rls' for target '{trip}' is unavailable for download for channel 'nightly'", + ), + ) + .await; + cx.config + .expect_stdout_ok(&["rustc", "--version"], "hash-nightly-3") + .await; + cx.config.expect_component_not_executable("rls").await; + + cx.config + .expect_ok(&[ "rustup", "toolchain", "install", @@ -1448,17 +1730,20 @@ fn install_allow_downgrade() { "-c", "rls", "--allow-downgrade", - ]); - config.expect_stdout_ok(&["rustc", "--version"], "hash-nightly-2"); - config.expect_component_executable("rls"); - }); + ]) + .await; + cx.config + .expect_stdout_ok(&["rustc", "--version"], "hash-nightly-2") + .await; + cx.config.expect_component_executable("rls").await; } -#[test] -fn regression_2601() { +#[tokio::test] +async fn regression_2601() { // We're checking that we don't regress per #2601 - setup(&|config| { - config.expect_ok(&[ + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config + .expect_ok(&[ "rustup", "toolchain", "install", @@ -1467,13 +1752,15 @@ fn regression_2601() { "nightly", "--component", "rust-src", - ]); - // The bug exposed in #2601 was that the above would end up installing - // rust-src-$ARCH which would then have to be healed on the following - // command, resulting in a reinstallation. - config.expect_stderr_ok( + ]) + .await; + // The bug exposed in #2601 was that the above would end up installing + // rust-src-$ARCH which would then have to be healed on the following + // command, resulting in a reinstallation. + cx.config + .expect_stderr_ok( &["rustup", "component", "add", "rust-src"], "info: component 'rust-src' is up to date", - ); - }); + ) + .await; } diff --git a/tests/test_bonanza.rs b/tests/test_bonanza.rs index 7a4d0e3b24..f09810a93b 100644 --- a/tests/test_bonanza.rs +++ b/tests/test_bonanza.rs @@ -1 +1,3 @@ +#![recursion_limit = "256"] + mod suite;