692 lines
17 KiB
Rust
692 lines
17 KiB
Rust
//! Tests of the interactive console installer
|
|
|
|
use std::env::consts::EXE_SUFFIX;
|
|
use std::io::Write;
|
|
use std::process::Stdio;
|
|
|
|
use rustup::test::{Assert, CliTestContext, Config, SanitizedOutput, Scenario, this_host_tuple};
|
|
#[cfg(windows)]
|
|
use rustup::test::{RegistryGuard, USER_PATH};
|
|
use rustup::utils::raw;
|
|
|
|
fn run_input(config: &Config, args: &[&str], input: &str) -> Assert {
|
|
run_input_with_env(config, args, input, &[])
|
|
}
|
|
|
|
fn run_input_with_env(config: &Config, args: &[&str], input: &str, env: &[(&str, &str)]) -> Assert {
|
|
let mut cmd = config.cmd(args[0], &args[1..]);
|
|
config.env(&mut cmd);
|
|
|
|
for (key, value) in env.iter() {
|
|
cmd.env(key, value);
|
|
}
|
|
|
|
cmd.stdin(Stdio::piped());
|
|
cmd.stdout(Stdio::piped());
|
|
cmd.stderr(Stdio::piped());
|
|
let mut child = cmd.spawn().unwrap();
|
|
|
|
child
|
|
.stdin
|
|
.as_mut()
|
|
.unwrap()
|
|
.write_all(input.as_bytes())
|
|
.unwrap();
|
|
let out = child.wait_with_output().unwrap();
|
|
|
|
Assert::new(SanitizedOutput {
|
|
status: out.status.code(),
|
|
stdout: String::from_utf8(out.stdout).unwrap(),
|
|
stderr: String::from_utf8(out.stderr).unwrap(),
|
|
})
|
|
}
|
|
|
|
#[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");
|
|
run_input(&cx.config, &["rustup-init"], "\n\n").is_ok();
|
|
}
|
|
|
|
// 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.
|
|
#[tokio::test]
|
|
async fn smoke_case_install_no_modify_path() {
|
|
let cx = CliTestContext::new(Scenario::SimpleV2).await;
|
|
// 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
|
|
run_input(&cx.config, &["rustup-init", "--no-modify-path"], "\n\n")
|
|
.with_stdout(snapbox::str![[r#"
|
|
...
|
|
This path needs to be in your PATH environment variable,
|
|
but will not be added automatically.
|
|
|
|
You can uninstall at any time with rustup self uninstall and
|
|
these changes will be reverted.
|
|
|
|
Current installation options:
|
|
|
|
|
|
default host triple: [HOST_TUPLE]
|
|
default toolchain: stable (default)
|
|
profile: default
|
|
modify PATH variable: no
|
|
|
|
1) Proceed with standard installation (default - just press enter)
|
|
2) Customize installation
|
|
3) Cancel installation
|
|
>
|
|
|
|
stable-[HOST_TUPLE] installed - 1.1.0 (hash-stable-1.1.0)
|
|
|
|
|
|
Rust is installed now. Great!
|
|
...
|
|
"#]])
|
|
.is_ok();
|
|
if cfg!(unix) {
|
|
assert!(!cx.config.homedir.join(".profile").exists());
|
|
assert!(cx.config.cargodir.join("env").exists());
|
|
}
|
|
}
|
|
|
|
#[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();
|
|
|
|
run_input(&cx.config, &["rustup-init"], "\n\n")
|
|
.is_ok()
|
|
.without_stdout("This path needs to be in your PATH environment variable");
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn blank_lines_around_stderr_log_output_update() {
|
|
let cx = CliTestContext::new(Scenario::SimpleV2).await;
|
|
cx.config
|
|
.expect(["rustup-init", "-y", "--no-modify-path"])
|
|
.await
|
|
.is_ok();
|
|
run_input(
|
|
&cx.config,
|
|
&[
|
|
"rustup-init",
|
|
"--no-update-default-toolchain",
|
|
"--no-modify-path",
|
|
],
|
|
"\n\n",
|
|
)
|
|
.with_stdout(snapbox::str![[r#"
|
|
...
|
|
3) Cancel installation
|
|
>
|
|
|
|
|
|
Rust is installed now. Great!
|
|
...
|
|
"#]]);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn installer_shows_default_host_triple() {
|
|
let cx = CliTestContext::new(Scenario::SimpleV2).await;
|
|
run_input(&cx.config, &["rustup-init", "--no-modify-path"], "2\n").with_stdout(snapbox::str![
|
|
[r#"
|
|
...
|
|
Default host triple? [[HOST_TUPLE]]
|
|
...
|
|
"#]
|
|
]);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn installer_shows_default_toolchain_as_stable() {
|
|
let cx = CliTestContext::new(Scenario::SimpleV2).await;
|
|
run_input(&cx.config, &["rustup-init", "--no-modify-path"], "2\n\n").with_stdout(
|
|
snapbox::str![[r#"
|
|
...
|
|
Default toolchain? (stable/beta/nightly/none) [stable]
|
|
...
|
|
"#]],
|
|
);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn installer_shows_default_toolchain_when_set_in_args() {
|
|
let cx = CliTestContext::new(Scenario::SimpleV2).await;
|
|
run_input(
|
|
&cx.config,
|
|
&[
|
|
"rustup-init",
|
|
"--no-modify-path",
|
|
"--default-toolchain=nightly",
|
|
],
|
|
"2\n\n",
|
|
)
|
|
.with_stdout(snapbox::str![[r#"
|
|
...
|
|
Default toolchain? (stable/beta/nightly/none) [nightly]
|
|
...
|
|
"#]]);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn installer_shows_default_profile() {
|
|
let cx = CliTestContext::new(Scenario::SimpleV2).await;
|
|
run_input(&cx.config, &["rustup-init", "--no-modify-path"], "2\n\n\n").with_stdout(
|
|
snapbox::str![[r#"
|
|
...
|
|
Profile (which tools and data to install)? (minimal/default/complete) [default]
|
|
...
|
|
"#]],
|
|
);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn installer_shows_default_profile_when_set_in_args() {
|
|
let cx = CliTestContext::new(Scenario::SimpleV2).await;
|
|
run_input(
|
|
&cx.config,
|
|
&["rustup-init", "--no-modify-path", "--profile=minimal"],
|
|
"2\n\n\n",
|
|
)
|
|
.with_stdout(snapbox::str![[r#"
|
|
...
|
|
Profile (which tools and data to install)? (minimal/default/complete) [minimal]
|
|
...
|
|
"#]]);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn installer_shows_default_for_modify_path() {
|
|
let cx = CliTestContext::new(Scenario::SimpleV2).await;
|
|
run_input(&cx.config, &["rustup-init"], "2\n\n\n\n").with_stdout(snapbox::str![[r#"
|
|
...
|
|
Modify PATH variable? (Y/n)
|
|
...
|
|
"#]]);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn installer_shows_default_for_modify_path_when_set_with_args() {
|
|
let cx = CliTestContext::new(Scenario::SimpleV2).await;
|
|
run_input(
|
|
&cx.config,
|
|
&["rustup-init", "--no-modify-path"],
|
|
"2\n\n\n\n",
|
|
)
|
|
.with_stdout(snapbox::str![[r#"
|
|
...
|
|
Modify PATH variable? (y/N)
|
|
...
|
|
"#]]);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn user_says_nope() {
|
|
let cx = CliTestContext::new(Scenario::SimpleV2).await;
|
|
run_input(&cx.config, &["rustup-init", "--no-modify-path"], "n\n\n").is_ok();
|
|
assert!(!cx.config.cargodir.join("bin").exists());
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn with_no_toolchain() {
|
|
let cx = CliTestContext::new(Scenario::SimpleV2).await;
|
|
run_input(
|
|
&cx.config,
|
|
&[
|
|
"rustup-init",
|
|
"--no-modify-path",
|
|
"--default-toolchain=none",
|
|
],
|
|
"\n\n",
|
|
)
|
|
.is_ok();
|
|
|
|
cx.config
|
|
.expect(["rustup", "show"])
|
|
.await
|
|
.with_stdout(snapbox::str![[r#"
|
|
...
|
|
no active toolchain
|
|
...
|
|
"#]])
|
|
.is_ok();
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn with_no_toolchain_doesnt_hang() {
|
|
let cx = CliTestContext::new(Scenario::SimpleV2).await;
|
|
run_input(
|
|
&cx.config,
|
|
&[
|
|
"rustup-init",
|
|
"--no-modify-path",
|
|
"--default-toolchain=none",
|
|
],
|
|
"\n\n",
|
|
)
|
|
.is_ok();
|
|
|
|
cx.config.expect(["rustup", "check"]).await.is_ok();
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn with_no_toolchain_doesnt_hang_with_concurrent_downloads_override() {
|
|
let cx = CliTestContext::new(Scenario::SimpleV2).await;
|
|
run_input(
|
|
&cx.config,
|
|
&[
|
|
"rustup-init",
|
|
"--no-modify-path",
|
|
"--default-toolchain=none",
|
|
],
|
|
"\n\n",
|
|
)
|
|
.is_ok();
|
|
|
|
cx.config
|
|
.expect_with_env(["rustup", "check"], [("RUSTUP_CONCURRENT_DOWNLOADS", "2")])
|
|
.await
|
|
.is_ok();
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn with_non_default_toolchain_still_prompts() {
|
|
let cx = CliTestContext::new(Scenario::SimpleV2).await;
|
|
run_input(
|
|
&cx.config,
|
|
&[
|
|
"rustup-init",
|
|
"--no-modify-path",
|
|
"--default-toolchain=nightly",
|
|
],
|
|
"\n\n",
|
|
)
|
|
.is_ok();
|
|
|
|
cx.config
|
|
.expect(["rustup", "show"])
|
|
.await
|
|
.with_stdout(snapbox::str![[r#"
|
|
...
|
|
installed toolchains
|
|
--------------------
|
|
nightly-[HOST_TUPLE] (active, default)
|
|
...
|
|
"#]])
|
|
.is_ok();
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn with_non_release_channel_non_default_toolchain() {
|
|
let cx = CliTestContext::new(Scenario::SimpleV2).await;
|
|
run_input(
|
|
&cx.config,
|
|
&[
|
|
"rustup-init",
|
|
"--no-modify-path",
|
|
"--default-toolchain=nightly-2015-01-02",
|
|
],
|
|
"\n\n",
|
|
)
|
|
.is_ok();
|
|
|
|
cx.config
|
|
.expect(["rustup", "show"])
|
|
.await
|
|
.with_stdout(snapbox::str![[r#"
|
|
...
|
|
installed toolchains
|
|
--------------------
|
|
nightly-2015-01-02-[HOST_TUPLE] (active, default)
|
|
...
|
|
"#]])
|
|
.is_ok();
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn set_nightly_toolchain() {
|
|
let cx = CliTestContext::new(Scenario::SimpleV2).await;
|
|
run_input(
|
|
&cx.config,
|
|
&["rustup-init", "--no-modify-path"],
|
|
"2\n\nnightly\n\n\n\n\n",
|
|
)
|
|
.is_ok();
|
|
|
|
cx.config
|
|
.expect(["rustup", "show"])
|
|
.await
|
|
.with_stdout(snapbox::str![[r#"
|
|
...
|
|
installed toolchains
|
|
--------------------
|
|
nightly-[HOST_TUPLE] (active, default)
|
|
...
|
|
"#]])
|
|
.is_ok();
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn set_no_modify_path() {
|
|
let cx = CliTestContext::new(Scenario::SimpleV2).await;
|
|
run_input(
|
|
&cx.config,
|
|
&["rustup-init", "--no-modify-path"],
|
|
"2\n\n\n\nno\n\n\n",
|
|
)
|
|
.is_ok();
|
|
|
|
if cfg!(unix) {
|
|
assert!(!cx.config.homedir.join(".profile").exists());
|
|
}
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn set_nightly_toolchain_and_unset() {
|
|
let cx = CliTestContext::new(Scenario::SimpleV2).await;
|
|
run_input(
|
|
&cx.config,
|
|
&["rustup-init", "--no-modify-path"],
|
|
"2\n\nnightly\n\n\n2\n\nbeta\n\n\n\n\n",
|
|
)
|
|
.is_ok();
|
|
|
|
cx.config
|
|
.expect(["rustup", "show"])
|
|
.await
|
|
.with_stdout(snapbox::str![[r#"
|
|
...
|
|
installed toolchains
|
|
--------------------
|
|
beta-[HOST_TUPLE] (active, default)
|
|
...
|
|
"#]])
|
|
.is_ok();
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn user_says_nope_after_advanced_install() {
|
|
let cx = CliTestContext::new(Scenario::SimpleV2).await;
|
|
run_input(
|
|
&cx.config,
|
|
&["rustup-init", "--no-modify-path"],
|
|
"2\n\n\n\n\nn\n\n\n",
|
|
)
|
|
.is_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);
|
|
|
|
let cx = CliTestContext::new(Scenario::SimpleV2).await;
|
|
cx.config.expect(&args).await.is_ok();
|
|
cx.config
|
|
.expect(["rustup", "component", "list"])
|
|
.await
|
|
.with_stdout(snapbox::str![[r#"
|
|
...
|
|
rust-src (installed)
|
|
...
|
|
"#]])
|
|
.is_ok();
|
|
cx.config
|
|
.expect(["rustup", "component", "list"])
|
|
.await
|
|
.with_stdout(snapbox::str![[r#"
|
|
...
|
|
rust-analysis-[HOST_TUPLE] (installed)
|
|
...
|
|
"#]])
|
|
.is_ok();
|
|
}
|
|
|
|
go(&["-c", "rust-src", "-c", "rust-analysis"]).await;
|
|
go(&["-c", "rust-src,rust-analysis"]).await;
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn install_forces_and_skips_rls() {
|
|
let cx = CliTestContext::new(Scenario::UnavailableRls).await;
|
|
cx.config.set_current_dist_date("2015-01-01");
|
|
|
|
run_input(
|
|
&cx.config,
|
|
&[
|
|
"rustup-init",
|
|
"--profile",
|
|
"complete",
|
|
"--default-toolchain",
|
|
"nightly",
|
|
"--no-modify-path",
|
|
],
|
|
"\n\n",
|
|
)
|
|
.is_ok()
|
|
.with_stderr(snapbox::str![[r#"
|
|
...
|
|
warn: skipping unavailable component rls
|
|
...
|
|
"#]]);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_warn_if_complete_profile_is_used() {
|
|
let cx = CliTestContext::new(Scenario::SimpleV2).await;
|
|
cx.config
|
|
.expect([
|
|
"rustup-init",
|
|
"-y",
|
|
"--profile",
|
|
"complete",
|
|
"--no-modify-path",
|
|
])
|
|
.await
|
|
.with_stderr(snapbox::str![[r#"
|
|
...
|
|
warn: downloading with complete profile isn't recommended unless you are a developer of the rust language
|
|
...
|
|
"#]])
|
|
.is_ok();
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn installing_when_already_installed_updates_toolchain() {
|
|
let cx = CliTestContext::new(Scenario::SimpleV2).await;
|
|
cx.config
|
|
.expect(["rustup-init", "-y", "--no-modify-path"])
|
|
.await
|
|
.is_ok();
|
|
run_input(&cx.config, &["rustup-init", "--no-modify-path"], "\n\n").with_stdout(snapbox::str![
|
|
[r#"
|
|
...
|
|
[..]stable-[HOST_TUPLE] 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()
|
|
.unwrap();
|
|
// Create fake executable
|
|
let fake_exe = temp_dir.path().join(format!("{}{}", "rustc", EXE_SUFFIX));
|
|
raw::append_file(&fake_exe, "").unwrap();
|
|
let temp_dir_path = temp_dir.path().to_str().unwrap();
|
|
|
|
let cx = CliTestContext::new(Scenario::SimpleV2).await;
|
|
cx.config
|
|
.expect_with_env(
|
|
["rustup-init", "--no-modify-path"],
|
|
[
|
|
("RUSTUP_INIT_SKIP_PATH_CHECK", "no"),
|
|
("PATH", temp_dir_path),
|
|
],
|
|
)
|
|
.await
|
|
.is_err()
|
|
.with_stderr(snapbox::str![[r#"
|
|
...
|
|
warn: It looks like you have an existing installation of Rust at:
|
|
...
|
|
warn: 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()
|
|
.unwrap();
|
|
// Create fake executable
|
|
let fake_exe = temp_dir.path().join(format!("{}{}", "cargo", EXE_SUFFIX));
|
|
raw::append_file(&fake_exe, "").unwrap();
|
|
let temp_dir_path = temp_dir.path().to_str().unwrap();
|
|
|
|
let cx = CliTestContext::new(Scenario::SimpleV2).await;
|
|
cx.config
|
|
.expect_with_env(
|
|
["rustup-init", "--no-modify-path"],
|
|
[
|
|
("RUSTUP_INIT_SKIP_PATH_CHECK", "no"),
|
|
("PATH", temp_dir_path),
|
|
],
|
|
)
|
|
.await
|
|
.is_err()
|
|
.with_stderr(snapbox::str![[r#"
|
|
...
|
|
warn: It looks like you have an existing installation of Rust at:
|
|
...
|
|
warn: 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()
|
|
.unwrap();
|
|
// Create fake executable
|
|
let fake_exe = temp_dir.path().join(format!("{}{}", "rustc", EXE_SUFFIX));
|
|
raw::append_file(&fake_exe, "").unwrap();
|
|
let temp_dir_path = temp_dir.path().to_str().unwrap();
|
|
|
|
let cx = CliTestContext::new(Scenario::SimpleV2).await;
|
|
cx.config
|
|
.expect_with_env(
|
|
["rustup-init", "-y", "--no-modify-path"],
|
|
[
|
|
("RUSTUP_INIT_SKIP_PATH_CHECK", "no"),
|
|
("PATH", temp_dir_path),
|
|
],
|
|
)
|
|
.await
|
|
.is_ok();
|
|
}
|
|
|
|
// Issue 2547
|
|
#[tokio::test]
|
|
async fn install_non_installable_toolchain() {
|
|
let cx = CliTestContext::new(Scenario::Unavailable).await;
|
|
cx.config
|
|
.expect([
|
|
"rustup-init",
|
|
"-y",
|
|
"--no-modify-path",
|
|
"--default-toolchain",
|
|
"nightly",
|
|
])
|
|
.await
|
|
.with_stderr(snapbox::str![[r#"
|
|
...
|
|
error: toolchain 'nightly-[HOST_TUPLE]' is not installable
|
|
...
|
|
"#]])
|
|
.is_err();
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn install_warns_about_existing_settings_file() {
|
|
let temp_dir = tempfile::Builder::new()
|
|
.prefix("fakehome")
|
|
.tempdir()
|
|
.unwrap();
|
|
// Create `settings.toml`
|
|
let settings_file = temp_dir.path().join("settings.toml");
|
|
raw::write_file(
|
|
&settings_file,
|
|
&format!(
|
|
r#"default_toolchain = "{}"
|
|
profile = "default"
|
|
version = "12""#,
|
|
this_host_tuple()
|
|
),
|
|
)
|
|
.unwrap();
|
|
let temp_dir_path = temp_dir.path().to_str().unwrap();
|
|
|
|
let cx = CliTestContext::new(Scenario::SimpleV2).await;
|
|
cx.config
|
|
.expect_with_env(
|
|
["rustup-init", "-y", "--no-modify-path"],
|
|
[
|
|
("RUSTUP_INIT_SKIP_PATH_CHECK", "no"),
|
|
("RUSTUP_HOME", temp_dir_path),
|
|
],
|
|
)
|
|
.await
|
|
.is_ok()
|
|
.with_stderr(snapbox::str![[r#"
|
|
...
|
|
warn: it looks like you have an existing rustup settings file at:
|
|
...
|
|
"#]]);
|
|
}
|
|
|
|
#[cfg(unix)]
|
|
#[tokio::test]
|
|
async fn install_warns_if_default_linker_missing() {
|
|
// Fake PATH pointing to an empty directory so `cc` cannot be found
|
|
let temp_dir = tempfile::Builder::new()
|
|
.prefix("emptybin")
|
|
.tempdir()
|
|
.unwrap();
|
|
let temp_dir_path = temp_dir.path().to_str().unwrap();
|
|
|
|
let cx = CliTestContext::new(Scenario::SimpleV2).await;
|
|
cx.config
|
|
.expect_with_env(
|
|
["rustup-init", "-y", "--no-modify-path"],
|
|
[("PATH", temp_dir_path)], // override PATH to hide system `cc`
|
|
)
|
|
.await
|
|
.is_ok()
|
|
.with_stderr(snapbox::str![[r#"
|
|
...
|
|
warn: no default linker ([CC_TOOL]) was found in your PATH
|
|
warn: many Rust crates require a system C toolchain to build
|
|
...
|
|
"#]]);
|
|
}
|