1749 lines
46 KiB
Rust
1749 lines
46 KiB
Rust
//! 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 itertools::Itertools;
|
|
use rustup::test::Assert;
|
|
use rustup::test::{CliTestContext, MULTI_ARCH1, Scenario, this_host_tuple};
|
|
use rustup::utils;
|
|
use rustup::utils::raw::symlink_dir;
|
|
|
|
#[tokio::test]
|
|
async fn smoke_test() {
|
|
let cx = CliTestContext::new(Scenario::SimpleV2).await;
|
|
cx.config.expect(["rustup", "--version"]).await.is_ok();
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn version_mentions_rustc_version_confusion() {
|
|
let cx = CliTestContext::new(Scenario::SimpleV2).await;
|
|
|
|
cx.config
|
|
.expect(["rustup", "--version"])
|
|
.await
|
|
.with_stderr(snapbox::str![[r#"
|
|
...
|
|
info: This is the version for the rustup toolchain manager, not the rustc compiler.
|
|
...
|
|
"#]])
|
|
.is_ok();
|
|
|
|
cx.config
|
|
.expect(["rustup", "toolchain", "install", "nightly"])
|
|
.await
|
|
.is_ok();
|
|
|
|
cx.config
|
|
.expect(["rustup", "+nightly", "--version"])
|
|
.await
|
|
.with_stderr(snapbox::str![[r#"
|
|
...
|
|
info: the currently active `rustc` version is `1.3.0 (hash-nightly-2)`
|
|
...
|
|
"#]])
|
|
.is_ok();
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn no_colors_in_piped_error_output() {
|
|
let cx = CliTestContext::new(Scenario::SimpleV2).await;
|
|
cx.config
|
|
.expect(["rustc"])
|
|
.await
|
|
.is_err()
|
|
.without_stderr("\u{1b}");
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn rustc_with_bad_rustup_toolchain_env_var() {
|
|
let cx = CliTestContext::new(Scenario::SimpleV2).await;
|
|
cx.config
|
|
.expect_with_env(["rustc"], [("RUSTUP_TOOLCHAIN", "bogus")])
|
|
.await
|
|
.with_stderr(snapbox::str![[r#"
|
|
error: override toolchain 'bogus' is not installed[..]
|
|
|
|
"#]])
|
|
.is_err();
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn custom_invalid_names() {
|
|
let cx = CliTestContext::new(Scenario::SimpleV2).await;
|
|
cx.config
|
|
.expect(["rustup", "toolchain", "link", "nightly", "foo"])
|
|
.await
|
|
.with_stderr(snapbox::str![[r#"
|
|
...
|
|
error:[..] invalid custom toolchain name 'nightly'
|
|
...
|
|
"#]])
|
|
.is_err();
|
|
cx.config
|
|
.expect(["rustup", "toolchain", "link", "beta", "foo"])
|
|
.await
|
|
.with_stderr(snapbox::str![[r#"
|
|
...
|
|
error:[..] invalid custom toolchain name 'beta'
|
|
...
|
|
"#]])
|
|
.is_err();
|
|
cx.config
|
|
.expect(["rustup", "toolchain", "link", "stable", "foo"])
|
|
.await
|
|
.with_stderr(snapbox::str![[r#"
|
|
...
|
|
error:[..] invalid custom toolchain name 'stable'
|
|
...
|
|
"#]])
|
|
.is_err();
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn custom_invalid_names_with_archive_dates() {
|
|
let cx = CliTestContext::new(Scenario::SimpleV2).await;
|
|
cx.config
|
|
.expect(["rustup", "toolchain", "link", "nightly-2015-01-01", "foo"])
|
|
.await
|
|
.with_stderr(snapbox::str![[r#"
|
|
...
|
|
error:[..] invalid custom toolchain name 'nightly-2015-01-01'
|
|
...
|
|
"#]])
|
|
.is_err();
|
|
cx.config
|
|
.expect(["rustup", "toolchain", "link", "beta-2015-01-01", "foo"])
|
|
.await
|
|
.with_stderr(snapbox::str![[r#"
|
|
...
|
|
error:[..] invalid custom toolchain name 'beta-2015-01-01'
|
|
...
|
|
"#]])
|
|
.is_err();
|
|
cx.config
|
|
.expect(["rustup", "toolchain", "link", "stable-2015-01-01", "foo"])
|
|
.await
|
|
.with_stderr(snapbox::str![[r#"
|
|
...
|
|
error:[..] invalid custom toolchain name 'stable-2015-01-01'
|
|
...
|
|
"#]])
|
|
.is_err();
|
|
}
|
|
|
|
// Regression test for newline placement
|
|
#[tokio::test]
|
|
async fn update_all_no_update_whitespace() {
|
|
let cx = CliTestContext::new(Scenario::SimpleV2).await;
|
|
cx.config
|
|
.expect(["rustup", "update", "nightly"])
|
|
.await
|
|
.is_ok()
|
|
.with_stdout(snapbox::str![[r#"
|
|
|
|
nightly-[HOST_TUPLE] installed - 1.3.0 (hash-nightly-2)
|
|
|
|
|
|
"#]]);
|
|
}
|
|
|
|
// Issue #145
|
|
#[tokio::test]
|
|
async fn update_works_without_term() {
|
|
let cx = CliTestContext::new(Scenario::SimpleV2).await;
|
|
let mut cmd = cx.config.cmd("rustup", ["update", "nightly"]);
|
|
cx.config.env(&mut cmd);
|
|
cmd.env_remove("TERM");
|
|
|
|
let out = cmd.output().unwrap();
|
|
assert!(out.status.success());
|
|
}
|
|
|
|
// Issue #1738
|
|
#[tokio::test]
|
|
async fn show_works_with_dumb_term() {
|
|
let cx = CliTestContext::new(Scenario::SimpleV2).await;
|
|
let mut cmd = cx.config.cmd("rustup", ["show"]);
|
|
cx.config.env(&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.
|
|
#[tokio::test]
|
|
async fn subcommand_required_for_target() {
|
|
let cx = CliTestContext::new(Scenario::SimpleV2).await;
|
|
let mut cmd = cx.config.cmd("rustup", ["target"]);
|
|
cx.config.env(&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.
|
|
#[tokio::test]
|
|
async fn subcommand_required_for_toolchain() {
|
|
let cx = CliTestContext::new(Scenario::SimpleV2).await;
|
|
let mut cmd = cx.config.cmd("rustup", ["toolchain"]);
|
|
cx.config.env(&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.
|
|
#[tokio::test]
|
|
async fn subcommand_required_for_override() {
|
|
let cx = CliTestContext::new(Scenario::SimpleV2).await;
|
|
let mut cmd = cx.config.cmd("rustup", ["override"]);
|
|
cx.config.env(&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.
|
|
#[tokio::test]
|
|
async fn subcommand_required_for_self() {
|
|
let cx = CliTestContext::new(Scenario::SimpleV2).await;
|
|
let mut cmd = cx.config.cmd("rustup", ["self"]);
|
|
cx.config.env(&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 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_tuple(), MULTI_ARCH1);
|
|
|
|
let cx = CliTestContext::new(Scenario::MultiHost).await;
|
|
let toolchain = format!("nightly-{MULTI_ARCH1}");
|
|
cx.config
|
|
.expect(["rustup", "default", &toolchain, "--force-non-host"])
|
|
.await
|
|
.is_ok();
|
|
cx.config
|
|
.expect(["rustc", "--version"])
|
|
.await
|
|
.with_stdout(snapbox::str![[r#"
|
|
1.3.0 (xxxx-nightly-2)
|
|
|
|
"#]])
|
|
.is_ok(); // cross-host mocks have their own versions
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn custom_toolchain_cargo_fallback_proxy() {
|
|
let cx = CliTestContext::new(Scenario::SimpleV2).await;
|
|
let path = cx.config.customdir.join("custom-1");
|
|
|
|
cx.config
|
|
.expect([
|
|
"rustup",
|
|
"toolchain",
|
|
"link",
|
|
"mytoolchain",
|
|
&path.to_string_lossy(),
|
|
])
|
|
.await
|
|
.is_ok();
|
|
cx.config
|
|
.expect(["rustup", "default", "mytoolchain"])
|
|
.await
|
|
.is_ok();
|
|
|
|
cx.config
|
|
.expect(["rustup", "update", "stable"])
|
|
.await
|
|
.is_ok();
|
|
cx.config
|
|
.expect(["cargo", "--version"])
|
|
.await
|
|
.with_stdout(snapbox::str![[r#"
|
|
1.1.0 (hash-stable-1.1.0)
|
|
|
|
"#]])
|
|
.is_ok();
|
|
|
|
cx.config.expect(["rustup", "update", "beta"]).await.is_ok();
|
|
cx.config
|
|
.expect(["cargo", "--version"])
|
|
.await
|
|
.with_stdout(snapbox::str![[r#"
|
|
1.2.0 (hash-beta-1.2.0)
|
|
|
|
"#]])
|
|
.is_ok();
|
|
|
|
cx.config
|
|
.expect(["rustup", "update", "nightly"])
|
|
.await
|
|
.is_ok();
|
|
cx.config
|
|
.expect(["cargo", "--version"])
|
|
.await
|
|
.with_stdout(snapbox::str![[r#"
|
|
1.3.0 (hash-nightly-2)
|
|
|
|
"#]])
|
|
.is_ok();
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn custom_toolchain_cargo_fallback_run() {
|
|
let cx = CliTestContext::new(Scenario::SimpleV2).await;
|
|
let path = cx.config.customdir.join("custom-1");
|
|
|
|
cx.config
|
|
.expect([
|
|
"rustup",
|
|
"toolchain",
|
|
"link",
|
|
"mytoolchain",
|
|
&path.to_string_lossy(),
|
|
])
|
|
.await
|
|
.is_ok();
|
|
cx.config
|
|
.expect(["rustup", "default", "mytoolchain"])
|
|
.await
|
|
.is_ok();
|
|
|
|
cx.config
|
|
.expect(["rustup", "update", "stable"])
|
|
.await
|
|
.is_ok();
|
|
cx.config
|
|
.expect(["rustup", "run", "mytoolchain", "cargo", "--version"])
|
|
.await
|
|
.with_stdout(snapbox::str![[r#"
|
|
1.1.0 (hash-stable-1.1.0)
|
|
|
|
"#]])
|
|
.is_ok();
|
|
|
|
cx.config.expect(["rustup", "update", "beta"]).await.is_ok();
|
|
cx.config
|
|
.expect(["rustup", "run", "mytoolchain", "cargo", "--version"])
|
|
.await
|
|
.with_stdout(snapbox::str![[r#"
|
|
1.2.0 (hash-beta-1.2.0)
|
|
|
|
"#]])
|
|
.is_ok();
|
|
|
|
cx.config
|
|
.expect(["rustup", "update", "nightly"])
|
|
.await
|
|
.is_ok();
|
|
cx.config
|
|
.expect(["rustup", "run", "mytoolchain", "cargo", "--version"])
|
|
.await
|
|
.with_stdout(snapbox::str![[r#"
|
|
1.3.0 (hash-nightly-2)
|
|
|
|
"#]])
|
|
.is_ok();
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn rustup_run_searches_path() {
|
|
let 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(["rustup", "default", "nightly"])
|
|
.await
|
|
.is_ok();
|
|
cx.config
|
|
.expect(hello_cmd)
|
|
.await
|
|
.with_stdout(snapbox::str![[r#"
|
|
hello
|
|
|
|
"#]])
|
|
.is_ok();
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn rustup_doesnt_prepend_path_unnecessarily() {
|
|
let cx = CliTestContext::new(Scenario::SimpleV2).await;
|
|
cx.config
|
|
.expect(["rustup", "default", "nightly"])
|
|
.await
|
|
.is_ok();
|
|
|
|
let assert_ok_with_paths = |assert: &Assert, data| {
|
|
assert.is_ok();
|
|
let stderr = std::env::split_paths(&assert.output.stderr)
|
|
.format_with("\n", |p, f| f(&p.display()))
|
|
.to_string();
|
|
let stderr = assert.redact(&stderr);
|
|
snapbox::assert_data_eq!(stderr, data);
|
|
};
|
|
|
|
// For all of these, CARGO_HOME/bin will be auto-prepended.
|
|
let cargo_home_bin = cx.config.cargodir.join("bin");
|
|
assert_ok_with_paths(
|
|
cx.config
|
|
.expect(["cargo", "--echo-path"])
|
|
.await
|
|
.extend_redactions([("[CARGO_HOME_BIN]", &cargo_home_bin)]),
|
|
snapbox::str![[r#"
|
|
[CARGO_HOME_BIN]
|
|
...
|
|
"#]],
|
|
);
|
|
|
|
assert_ok_with_paths(
|
|
cx.config
|
|
.expect_with_env(["cargo", "--echo-path"], [("PATH", "")])
|
|
.await
|
|
.extend_redactions([("[CARGO_HOME_BIN]", &cargo_home_bin)]),
|
|
snapbox::str![[r#"
|
|
[CARGO_HOME_BIN]
|
|
...
|
|
"#]],
|
|
);
|
|
|
|
// Check that CARGO_HOME/bin is prepended to path.
|
|
assert_ok_with_paths(
|
|
cx.config
|
|
.expect_with_env(
|
|
["cargo", "--echo-path"],
|
|
[("PATH", &*cx.config.exedir.display().to_string())],
|
|
)
|
|
.await
|
|
.extend_redactions([
|
|
("[CARGO_HOME_BIN]", &cargo_home_bin),
|
|
("[EXEDIR]", &cx.config.exedir),
|
|
]),
|
|
snapbox::str![[r#"
|
|
[CARGO_HOME_BIN]
|
|
[EXEDIR]
|
|
...
|
|
"#]],
|
|
);
|
|
|
|
// But if CARGO_HOME/bin is already on PATH, it will not be prepended again,
|
|
// so exedir will take precedence.
|
|
assert_ok_with_paths(
|
|
cx.config
|
|
.expect_with_env(
|
|
["cargo", "--echo-path"],
|
|
[(
|
|
"PATH",
|
|
std::env::join_paths([&cx.config.exedir, &cargo_home_bin])
|
|
.unwrap()
|
|
.to_str()
|
|
.unwrap(),
|
|
)],
|
|
)
|
|
.await
|
|
.extend_redactions([
|
|
("[CARGO_HOME_BIN]", &cargo_home_bin),
|
|
("[EXEDIR]", &cx.config.exedir),
|
|
]),
|
|
snapbox::str![[r#"
|
|
[EXEDIR]
|
|
[CARGO_HOME_BIN]
|
|
...
|
|
"#]],
|
|
);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn rustup_failed_path_search() {
|
|
let cx = CliTestContext::new(Scenario::SimpleV2).await;
|
|
use std::env::consts::EXE_SUFFIX;
|
|
|
|
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");
|
|
|
|
cx.config
|
|
.expect([
|
|
"rustup",
|
|
"toolchain",
|
|
"link",
|
|
"custom",
|
|
&cx.config.customdir.join("custom-1").to_string_lossy(),
|
|
])
|
|
.await
|
|
.is_ok();
|
|
|
|
cx.config
|
|
.expect(["rustup", "default", "custom"])
|
|
.await
|
|
.is_ok();
|
|
|
|
let broken = &["rustup", "run", "custom", "fake_proxy"];
|
|
cx.config
|
|
.expect(broken)
|
|
.await
|
|
.with_stderr(snapbox::str![[r#"
|
|
...
|
|
error: 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'
|
|
...
|
|
"#]])
|
|
.is_err();
|
|
|
|
// Hardlink will be automatically cleaned up by test setup code
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn rustup_failed_path_search_toolchain() {
|
|
let cx = CliTestContext::new(Scenario::SimpleV2).await;
|
|
use std::env::consts::EXE_SUFFIX;
|
|
|
|
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");
|
|
|
|
cx.config
|
|
.expect([
|
|
"rustup",
|
|
"toolchain",
|
|
"link",
|
|
"custom-1",
|
|
&cx.config.customdir.join("custom-1").to_string_lossy(),
|
|
])
|
|
.await
|
|
.is_ok();
|
|
|
|
cx.config
|
|
.expect([
|
|
"rustup",
|
|
"toolchain",
|
|
"link",
|
|
"custom-2",
|
|
&cx.config.customdir.join("custom-2").to_string_lossy(),
|
|
])
|
|
.await
|
|
.is_ok();
|
|
|
|
cx.config
|
|
.expect(["rustup", "default", "custom-2"])
|
|
.await
|
|
.is_ok();
|
|
|
|
let broken = &["rustup", "run", "custom-1", "cargo-miri"];
|
|
cx.config
|
|
.expect(broken)
|
|
.await
|
|
.with_stderr(snapbox::str![[r#"
|
|
...
|
|
note: this is a custom toolchain, which cannot use `rustup component add`
|
|
help: if you built this toolchain from source, and used `rustup toolchain link`, then you may be able to build the component with `x.py`
|
|
...
|
|
"#]])
|
|
.is_err();
|
|
|
|
let broken = &["rustup", "run", "custom-2", "cargo-miri"];
|
|
cx.config
|
|
.expect(broken)
|
|
.await
|
|
.with_stderr(snapbox::str![[r#"
|
|
...
|
|
note: this is a custom toolchain, which cannot use `rustup component add`
|
|
help: if you built this toolchain from source, and used `rustup toolchain link`, then you may be able to build the component with `x.py`
|
|
...
|
|
"#]])
|
|
.is_err();
|
|
|
|
// Hardlink will be automatically cleaned up by test setup code
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn rustup_run_not_installed() {
|
|
let cx = CliTestContext::new(Scenario::SimpleV2).await;
|
|
cx.config
|
|
.expect(["rustup", "install", "stable"])
|
|
.await
|
|
.is_ok();
|
|
cx.config
|
|
.expect(["rustup", "run", "nightly", "rustc", "--version"])
|
|
.await
|
|
.with_stderr(snapbox::str![[r#"
|
|
error: toolchain 'nightly-[HOST_TUPLE]' is not installed
|
|
help: run `rustup toolchain install nightly-[HOST_TUPLE]` to install it
|
|
|
|
"#]])
|
|
.is_err();
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn rustup_run_install() {
|
|
let cx = CliTestContext::new(Scenario::SimpleV2).await;
|
|
cx.config
|
|
.expect(["rustup", "install", "stable"])
|
|
.await
|
|
.is_ok();
|
|
cx.config
|
|
.expect([
|
|
"rustup",
|
|
"run",
|
|
"--install",
|
|
"nightly",
|
|
"cargo",
|
|
"--version",
|
|
])
|
|
.await
|
|
.is_ok();
|
|
cx.config
|
|
.expect(["rustup", "+nightly", "component", "list", "--installed"])
|
|
.await
|
|
.is_ok()
|
|
.with_stdout(snapbox::str![[r#"
|
|
cargo-[HOST_TUPLE]
|
|
rust-docs-[HOST_TUPLE]
|
|
rust-std-[HOST_TUPLE]
|
|
rustc-[HOST_TUPLE]
|
|
|
|
"#]]);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn toolchains_are_resolved_early() {
|
|
let cx = CliTestContext::new(Scenario::SimpleV2).await;
|
|
cx.config
|
|
.expect(["rustup", "default", "nightly"])
|
|
.await
|
|
.is_ok();
|
|
|
|
let full_toolchain = format!("nightly-{}", this_host_tuple());
|
|
cx.config
|
|
.expect(["rustup", "default", &full_toolchain])
|
|
.await
|
|
.with_stderr(snapbox::str![[r#"
|
|
...
|
|
info: using existing install for nightly-[HOST_TUPLE]
|
|
...
|
|
"#]])
|
|
.is_ok();
|
|
}
|
|
|
|
// #190
|
|
#[tokio::test]
|
|
async fn proxies_pass_empty_args() {
|
|
let cx = CliTestContext::new(Scenario::SimpleV2).await;
|
|
cx.config
|
|
.expect(["rustup", "default", "nightly"])
|
|
.await
|
|
.is_ok();
|
|
cx.config
|
|
.expect(["rustup", "run", "nightly", "rustc", "--empty-arg-test", ""])
|
|
.await
|
|
.is_ok();
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn rls_exists_in_toolchain() {
|
|
let cx = CliTestContext::new(Scenario::SimpleV2).await;
|
|
cx.config
|
|
.expect(["rustup", "default", "stable"])
|
|
.await
|
|
.is_ok();
|
|
cx.config
|
|
.expect(["rustup", "component", "add", "rls"])
|
|
.await
|
|
.is_ok();
|
|
|
|
assert!(cx.config.exedir.join(format!("rls{EXE_SUFFIX}")).exists());
|
|
cx.config.expect(["rls", "--version"]).await.is_ok();
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn run_rls_when_not_available_in_toolchain() {
|
|
let cx = CliTestContext::new(Scenario::UnavailableRls).await;
|
|
cx.config.set_current_dist_date("2015-01-01");
|
|
cx.config
|
|
.expect(["rustup", "default", "nightly"])
|
|
.await
|
|
.is_ok();
|
|
cx.config
|
|
.expect(["rls", "--version"])
|
|
.await
|
|
.with_stderr(snapbox::str![[r#"
|
|
error: the 'rls' component which provides the command 'rls[EXE]' is not available for the 'nightly-[HOST_TUPLE]' toolchain
|
|
|
|
"#]])
|
|
.is_err();
|
|
|
|
cx.config.set_current_dist_date("2015-01-02");
|
|
cx.config.expect(["rustup", "update"]).await.is_ok();
|
|
cx.config
|
|
.expect(["rustup", "component", "add", "rls"])
|
|
.await
|
|
.is_ok();
|
|
|
|
cx.config.expect(["rls", "--version"]).await.is_ok();
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn run_rls_when_not_installed() {
|
|
let cx = CliTestContext::new(Scenario::SimpleV2).await;
|
|
cx.config
|
|
.expect(["rustup", "default", "stable"])
|
|
.await
|
|
.is_ok();
|
|
cx.config
|
|
.expect(["rls", "--version"])
|
|
.await
|
|
.with_stderr(snapbox::str![[r#"
|
|
error: 'rls[EXE]' is not installed for the toolchain 'stable-[HOST_TUPLE]'.
|
|
help: run `rustup component add rls` to install it
|
|
|
|
"#]])
|
|
.is_err();
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn run_rls_when_not_installed_for_nightly() {
|
|
let cx = CliTestContext::new(Scenario::SimpleV2).await;
|
|
cx.config
|
|
.expect(["rustup", "default", "stable"])
|
|
.await
|
|
.is_ok();
|
|
cx.config
|
|
.expect(["rustup", "toolchain", "install", "nightly"])
|
|
.await
|
|
.is_ok();
|
|
cx.config
|
|
.expect(["rls", "+nightly", "--version"])
|
|
.await
|
|
.with_stderr(snapbox::str![[r#"
|
|
error: 'rls[EXE]' is not installed for the toolchain 'nightly-[HOST_TUPLE]'.
|
|
help: run `rustup component add --toolchain nightly-[HOST_TUPLE] rls` to install it
|
|
|
|
"#]])
|
|
.is_err();
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn run_rust_lldb_when_not_in_toolchain() {
|
|
let cx = CliTestContext::new(Scenario::UnavailableRls).await;
|
|
cx.config.set_current_dist_date("2015-01-01");
|
|
cx.config
|
|
.expect(["rustup", "default", "nightly"])
|
|
.await
|
|
.is_ok();
|
|
cx.config
|
|
.expect(["rust-lldb", "--version"])
|
|
.await
|
|
.with_stderr(snapbox::str![[r#"
|
|
error: the 'rust-lldb[EXE]' binary, normally provided by the 'rustc' component, is not applicable to the 'nightly-[HOST_TUPLE]' toolchain
|
|
|
|
"#]])
|
|
.is_err();
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn rename_rls_before() {
|
|
let cx = CliTestContext::new(Scenario::ArchivesV2).await;
|
|
cx.config.set_current_dist_date("2015-01-01");
|
|
cx.config
|
|
.expect(["rustup", "default", "nightly"])
|
|
.await
|
|
.is_ok();
|
|
cx.config
|
|
.expect(["rustup", "component", "add", "rls"])
|
|
.await
|
|
.is_ok();
|
|
|
|
cx.config.set_current_dist_date("2015-01-02");
|
|
cx.config.expect(["rustup", "update"]).await.is_ok();
|
|
|
|
assert!(cx.config.exedir.join(format!("rls{EXE_SUFFIX}")).exists());
|
|
cx.config.expect(["rls", "--version"]).await.is_ok();
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn rename_rls_after() {
|
|
let cx = CliTestContext::new(Scenario::ArchivesV2).await;
|
|
cx.config.set_current_dist_date("2015-01-01");
|
|
cx.config
|
|
.expect(["rustup", "default", "nightly"])
|
|
.await
|
|
.is_ok();
|
|
|
|
cx.config.set_current_dist_date("2015-01-02");
|
|
cx.config.expect(["rustup", "update"]).await.is_ok();
|
|
cx.config
|
|
.expect(["rustup", "component", "add", "rls-preview"])
|
|
.await
|
|
.is_ok();
|
|
|
|
assert!(cx.config.exedir.join(format!("rls{EXE_SUFFIX}")).exists());
|
|
cx.config.expect(["rls", "--version"]).await.is_ok();
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn rename_rls_add_old_name() {
|
|
let cx = CliTestContext::new(Scenario::ArchivesV2).await;
|
|
cx.config.set_current_dist_date("2015-01-01");
|
|
cx.config
|
|
.expect(["rustup", "default", "nightly"])
|
|
.await
|
|
.is_ok();
|
|
|
|
cx.config.set_current_dist_date("2015-01-02");
|
|
cx.config.expect(["rustup", "update"]).await.is_ok();
|
|
cx.config
|
|
.expect(["rustup", "component", "add", "rls"])
|
|
.await
|
|
.is_ok();
|
|
|
|
assert!(cx.config.exedir.join(format!("rls{EXE_SUFFIX}")).exists());
|
|
cx.config.expect(["rls", "--version"]).await.is_ok();
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn rename_rls_list() {
|
|
let cx = CliTestContext::new(Scenario::ArchivesV2).await;
|
|
cx.config.set_current_dist_date("2015-01-01");
|
|
cx.config
|
|
.expect(["rustup", "default", "nightly"])
|
|
.await
|
|
.is_ok();
|
|
|
|
cx.config.set_current_dist_date("2015-01-02");
|
|
cx.config.expect(["rustup", "update"]).await.is_ok();
|
|
cx.config
|
|
.expect(["rustup", "component", "add", "rls"])
|
|
.await
|
|
.is_ok();
|
|
|
|
cx.config
|
|
.expect(["rustup", "component", "list"])
|
|
.await
|
|
.with_stdout(snapbox::str![[r#"
|
|
...
|
|
rls-[HOST_TUPLE] (installed)
|
|
...
|
|
"#]])
|
|
.is_ok();
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn rename_rls_preview_list() {
|
|
let cx = CliTestContext::new(Scenario::ArchivesV2).await;
|
|
cx.config.set_current_dist_date("2015-01-01");
|
|
cx.config
|
|
.expect(["rustup", "default", "nightly"])
|
|
.await
|
|
.is_ok();
|
|
|
|
cx.config.set_current_dist_date("2015-01-02");
|
|
cx.config.expect(["rustup", "update"]).await.is_ok();
|
|
cx.config
|
|
.expect(["rustup", "component", "add", "rls-preview"])
|
|
.await
|
|
.is_ok();
|
|
|
|
cx.config
|
|
.expect(["rustup", "component", "list"])
|
|
.await
|
|
.with_stdout(snapbox::str![[r#"
|
|
...
|
|
rls-[HOST_TUPLE] (installed)
|
|
...
|
|
"#]])
|
|
.is_ok();
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn rename_rls_remove() {
|
|
let cx = CliTestContext::new(Scenario::ArchivesV2).await;
|
|
cx.config.set_current_dist_date("2015-01-01");
|
|
cx.config
|
|
.expect(["rustup", "default", "nightly"])
|
|
.await
|
|
.is_ok();
|
|
|
|
cx.config.set_current_dist_date("2015-01-02");
|
|
cx.config.expect(["rustup", "update"]).await.is_ok();
|
|
|
|
cx.config
|
|
.expect(["rustup", "component", "add", "rls"])
|
|
.await
|
|
.is_ok();
|
|
cx.config.expect(["rls", "--version"]).await.is_ok();
|
|
cx.config
|
|
.expect(["rustup", "component", "remove", "rls"])
|
|
.await
|
|
.is_ok();
|
|
cx.config
|
|
.expect(["rls", "--version"])
|
|
.await
|
|
.with_stderr(snapbox::str![[r#"
|
|
...
|
|
error: 'rls[EXE]' is not installed for the toolchain 'nightly-[HOST_TUPLE]'.
|
|
...
|
|
"#]])
|
|
.is_err();
|
|
|
|
cx.config
|
|
.expect(["rustup", "component", "add", "rls"])
|
|
.await
|
|
.is_ok();
|
|
cx.config.expect(["rls", "--version"]).await.is_ok();
|
|
cx.config
|
|
.expect(["rustup", "component", "remove", "rls-preview"])
|
|
.await
|
|
.is_ok();
|
|
cx.config
|
|
.expect(["rls", "--version"])
|
|
.await
|
|
.with_stderr(snapbox::str![[r#"
|
|
...
|
|
error: 'rls[EXE]' is not installed for the toolchain 'nightly-[HOST_TUPLE]'.
|
|
...
|
|
"#]])
|
|
.is_err();
|
|
}
|
|
|
|
// issue #3737
|
|
/// `~/.rustup/toolchains` is permitted to be a symlink.
|
|
#[tokio::test]
|
|
#[cfg(any(unix, windows))]
|
|
async fn toolchains_symlink() {
|
|
let 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(["rustup", "default", "nightly"])
|
|
.await
|
|
.is_ok();
|
|
cx.config
|
|
.expect(["rustup", "toolchain", "list"])
|
|
.await
|
|
.with_stdout(snapbox::str![[r#"
|
|
...
|
|
nightly[..]
|
|
...
|
|
"#]])
|
|
.is_ok();
|
|
cx.config
|
|
.expect(["rustc", "--version"])
|
|
.await
|
|
.with_stdout(snapbox::str![[r#"
|
|
1.3.0 (hash-nightly-2)
|
|
|
|
"#]])
|
|
.is_ok();
|
|
cx.config
|
|
.expect(["rustup", "toolchain", "uninstall", "nightly"])
|
|
.await
|
|
.is_ok();
|
|
cx.config
|
|
.expect(["rustup", "toolchain", "list"])
|
|
.await
|
|
.with_stdout(snapbox::str![[r#"
|
|
no installed toolchains
|
|
|
|
"#]])
|
|
.is_ok();
|
|
}
|
|
|
|
// issue #3344
|
|
/// `~/.rustup/tmp` and `~/.rustup/downloads` are permitted to be symlinks.
|
|
#[tokio::test]
|
|
#[cfg(any(unix, windows))]
|
|
async fn tmp_downloads_symlink() {
|
|
let 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, &cx.config.rustupdir.join("tmp")).unwrap();
|
|
|
|
let test_downloads = cwd.join("tmp-downloads");
|
|
fs::create_dir(&test_downloads).unwrap();
|
|
symlink_dir(&test_downloads, &cx.config.rustupdir.join("downloads")).unwrap();
|
|
|
|
cx.config.set_current_dist_date("2015-01-01");
|
|
cx.config
|
|
.expect(["rustup", "default", "nightly"])
|
|
.await
|
|
.is_ok();
|
|
|
|
cx.config.set_current_dist_date("2015-01-02");
|
|
cx.config.expect(["rustup", "update"]).await.is_ok();
|
|
|
|
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.
|
|
#[tokio::test]
|
|
#[cfg(any(unix, windows))]
|
|
async fn toolchain_broken_symlink() {
|
|
let 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(["rustup", "toolchain", "list"])
|
|
.await
|
|
.with_stdout(snapbox::str![[r#"
|
|
test
|
|
|
|
"#]])
|
|
.is_ok();
|
|
// Now try to uninstall it. That should work only once.
|
|
cx.config
|
|
.expect(["rustup", "toolchain", "uninstall", "test"])
|
|
.await
|
|
.with_stderr(snapbox::str![[r#"
|
|
info: uninstalling toolchain test
|
|
info: toolchain test uninstalled
|
|
|
|
"#]])
|
|
.is_ok();
|
|
cx.config
|
|
.expect(["rustup", "toolchain", "uninstall", "test"])
|
|
.await
|
|
.with_stderr(snapbox::str![[r#"
|
|
...
|
|
info: no toolchain installed for 'test'
|
|
...
|
|
"#]])
|
|
.is_ok();
|
|
}
|
|
|
|
// issue #1297
|
|
#[tokio::test]
|
|
async fn update_unavailable_rustc() {
|
|
let cx = CliTestContext::new(Scenario::Unavailable).await;
|
|
cx.config.set_current_dist_date("2015-01-01");
|
|
cx.config
|
|
.expect(["rustup", "default", "nightly"])
|
|
.await
|
|
.is_ok();
|
|
|
|
cx.config
|
|
.expect(["rustc", "--version"])
|
|
.await
|
|
.with_stdout(snapbox::str![[r#"
|
|
1.2.0 (hash-nightly-1)
|
|
|
|
"#]])
|
|
.is_ok();
|
|
|
|
// latest nightly is unavailable
|
|
cx.config.set_current_dist_date("2015-01-02");
|
|
// update should do nothing
|
|
cx.config
|
|
.expect(["rustup", "update", "nightly"])
|
|
.await
|
|
.is_ok();
|
|
cx.config
|
|
.expect(["rustc", "--version"])
|
|
.await
|
|
.with_stdout(snapbox::str![[r#"
|
|
1.2.0 (hash-nightly-1)
|
|
|
|
"#]])
|
|
.is_ok();
|
|
}
|
|
|
|
// issue 2562
|
|
#[tokio::test]
|
|
async fn install_unavailable_platform() {
|
|
let cx = CliTestContext::new(Scenario::Unavailable).await;
|
|
cx.config.set_current_dist_date("2015-01-02");
|
|
// explicit attempt to install should fail
|
|
cx.config
|
|
.expect(["rustup", "toolchain", "install", "nightly"])
|
|
.await
|
|
.with_stderr(snapbox::str![[r#"
|
|
...
|
|
error: toolchain 'nightly-[HOST_TUPLE]' is not installable
|
|
...
|
|
"#]])
|
|
.is_err();
|
|
// implicit attempt to install should fail
|
|
cx.config
|
|
.expect(["rustup", "default", "nightly"])
|
|
.await
|
|
.with_stderr(snapbox::str![[r#"
|
|
...
|
|
error: toolchain 'nightly-[HOST_TUPLE]' is not installable
|
|
...
|
|
"#]])
|
|
.is_err();
|
|
}
|
|
|
|
// issue #1329
|
|
#[tokio::test]
|
|
async fn install_beta_with_tag() {
|
|
let cx = CliTestContext::new(Scenario::BetaTag).await;
|
|
cx.config
|
|
.expect(["rustup", "default", "1.78.0-beta"])
|
|
.await
|
|
.is_ok();
|
|
cx.config
|
|
.expect(["rustc", "--version"])
|
|
.await
|
|
.with_stdout(snapbox::str![[r#"
|
|
1.78.0 (hash-1.78.0-beta-1.78.0)
|
|
|
|
"#]])
|
|
.is_ok();
|
|
|
|
cx.config
|
|
.expect(["rustup", "default", "1.79.0-beta.2"])
|
|
.await
|
|
.is_ok();
|
|
cx.config
|
|
.expect(["rustc", "--version"])
|
|
.await
|
|
.with_stdout(snapbox::str![[r#"
|
|
1.79.0 (hash-1.79.0-beta.2-1.79.0)
|
|
|
|
"#]])
|
|
.is_ok();
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn update_nightly_even_with_incompat() {
|
|
let cx = CliTestContext::new(Scenario::MissingComponent).await;
|
|
cx.config.set_current_dist_date("2019-09-12");
|
|
cx.config
|
|
.expect(["rustup", "default", "nightly"])
|
|
.await
|
|
.is_ok();
|
|
|
|
cx.config
|
|
.expect(["rustc", "--version"])
|
|
.await
|
|
.with_stdout(snapbox::str![[r#"
|
|
1.37.0 (hash-nightly-1)
|
|
|
|
"#]])
|
|
.is_ok();
|
|
cx.config
|
|
.expect(["rustup", "component", "add", "rls"])
|
|
.await
|
|
.is_ok();
|
|
cx.config.expect_component_executable("rls").await;
|
|
|
|
// latest nightly is now one that does not have RLS
|
|
cx.config.set_current_dist_date("2019-09-14");
|
|
|
|
cx.config.expect_component_executable("rls").await;
|
|
// update should bring us to latest nightly that does
|
|
cx.config
|
|
.expect(["rustup", "update", "nightly"])
|
|
.await
|
|
.is_ok();
|
|
cx.config
|
|
.expect(["rustc", "--version"])
|
|
.await
|
|
.with_stdout(snapbox::str![[r#"
|
|
1.37.0 (hash-nightly-2)
|
|
|
|
"#]])
|
|
.is_ok();
|
|
cx.config.expect_component_executable("rls").await;
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn nightly_backtrack_skips_missing() {
|
|
let cx = CliTestContext::new(Scenario::MissingNightly).await;
|
|
cx.config.set_current_dist_date("2019-09-16");
|
|
cx.config
|
|
.expect(["rustup", "default", "nightly"])
|
|
.await
|
|
.is_ok();
|
|
|
|
cx.config
|
|
.expect(["rustc", "--version"])
|
|
.await
|
|
.with_stdout(snapbox::str![[r#"
|
|
1.37.0 (hash-nightly-1)
|
|
|
|
"#]])
|
|
.is_ok();
|
|
cx.config
|
|
.expect(["rustup", "component", "add", "rls"])
|
|
.await
|
|
.is_ok();
|
|
cx.config.expect_component_executable("rls").await;
|
|
|
|
// rls is missing on latest, nightly is missing on second-to-latest
|
|
cx.config.set_current_dist_date("2019-09-18");
|
|
|
|
// update should not change nightly, and should not error
|
|
cx.config
|
|
.expect(["rustup", "update", "nightly"])
|
|
.await
|
|
.is_ok();
|
|
cx.config
|
|
.expect(["rustc", "--version"])
|
|
.await
|
|
.with_stdout(snapbox::str![[r#"
|
|
1.37.0 (hash-nightly-1)
|
|
|
|
"#]])
|
|
.is_ok();
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn completion_rustup() {
|
|
let cx = CliTestContext::new(Scenario::SimpleV2).await;
|
|
cx.config
|
|
.expect(["rustup", "completions", "bash", "rustup"])
|
|
.await
|
|
.is_ok();
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn completion_cargo() {
|
|
let cx = CliTestContext::new(Scenario::SimpleV2).await;
|
|
cx.config
|
|
.expect(["rustup", "completions", "bash", "cargo"])
|
|
.await
|
|
.is_ok();
|
|
}
|
|
|
|
#[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;
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn completion_bad_shell() {
|
|
let cx = CliTestContext::new(Scenario::SimpleV2).await;
|
|
cx.config
|
|
.expect(["rustup", "completions", "fake"])
|
|
.await
|
|
.with_stderr(snapbox::str![[r#"
|
|
...
|
|
error: invalid value 'fake' for '<SHELL>'[..]
|
|
...
|
|
"#]])
|
|
.is_err();
|
|
cx.config
|
|
.expect(["rustup", "completions", "fake", "cargo"])
|
|
.await
|
|
.with_stderr(snapbox::str![[r#"
|
|
...
|
|
error: invalid value 'fake' for '<SHELL>'[..]
|
|
...
|
|
"#]])
|
|
.is_err();
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn completion_bad_tool() {
|
|
let cx = CliTestContext::new(Scenario::SimpleV2).await;
|
|
cx.config
|
|
.expect(["rustup", "completions", "bash", "fake"])
|
|
.await
|
|
.with_stderr(snapbox::str![[r#"
|
|
...
|
|
error: invalid value 'fake' for '[COMMAND]'[..]
|
|
...
|
|
"#]])
|
|
.is_err();
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn completion_cargo_unsupported_shell() {
|
|
let cx = CliTestContext::new(Scenario::SimpleV2).await;
|
|
cx.config
|
|
.expect(["rustup", "completions", "fish", "cargo"])
|
|
.await
|
|
.with_stderr(snapbox::str![[r#"
|
|
...
|
|
error: cargo does not currently support completions for[..]
|
|
...
|
|
"#]])
|
|
.is_err();
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn add_remove_component() {
|
|
let cx = CliTestContext::new(Scenario::SimpleV2).await;
|
|
cx.config
|
|
.expect(["rustup", "default", "nightly"])
|
|
.await
|
|
.is_ok();
|
|
cx.config.expect_component_executable("rustc").await;
|
|
cx.config
|
|
.expect(["rustup", "component", "remove", "rustc"])
|
|
.await
|
|
.is_ok();
|
|
cx.config.expect_component_not_executable("rustc").await;
|
|
cx.config
|
|
.expect(["rustup", "component", "add", "rustc"])
|
|
.await
|
|
.is_ok();
|
|
cx.config.expect_component_executable("rustc").await;
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn which() {
|
|
let 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(["rustup", "toolchain", "link", "custom-1", &path_1])
|
|
.await
|
|
.is_ok();
|
|
cx.config
|
|
.expect(["rustup", "default", "custom-1"])
|
|
.await
|
|
.is_ok();
|
|
cx.config
|
|
.expect(["rustup", "which", "rustc"])
|
|
.await
|
|
.with_stdout(snapbox::str![[r#"
|
|
[..]/toolchains/custom-1/bin/rustc[EXE]
|
|
|
|
"#]])
|
|
.is_ok();
|
|
let path_2 = cx.config.customdir.join("custom-2");
|
|
let path_2 = path_2.to_string_lossy();
|
|
cx.config
|
|
.expect(["rustup", "toolchain", "link", "custom-2", &path_2])
|
|
.await
|
|
.is_ok();
|
|
cx.config
|
|
.expect(["rustup", "which", "--toolchain=custom-2", "rustc"])
|
|
.await
|
|
.with_stdout(snapbox::str![[r#"
|
|
[..]/toolchains/custom-2/bin/rustc[EXE]
|
|
|
|
"#]])
|
|
.is_ok();
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn which_asking_uninstalled_toolchain() {
|
|
let 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(["rustup", "toolchain", "link", "custom-1", &path_1])
|
|
.await
|
|
.is_ok();
|
|
cx.config
|
|
.expect(["rustup", "default", "custom-1"])
|
|
.await
|
|
.is_ok();
|
|
cx.config
|
|
.expect(["rustup", "which", "rustc"])
|
|
.await
|
|
.with_stdout(snapbox::str![[r#"
|
|
[..]/toolchains/custom-1/bin/rustc[EXE]
|
|
|
|
"#]])
|
|
.is_ok();
|
|
cx.config
|
|
.expect(["rustup", "which", "--toolchain=nightly", "rustc"])
|
|
.await
|
|
.with_stdout(snapbox::str![[r#"
|
|
[..]/toolchains/nightly-[HOST_TUPLE]/bin/rustc[EXE]
|
|
|
|
"#]])
|
|
.is_ok();
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn which_asking_uninstalled_components() {
|
|
let 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(["rustup", "toolchain", "link", "custom-1", &path_1])
|
|
.await
|
|
.is_ok();
|
|
cx.config
|
|
.expect(["rustup", "default", "custom-1"])
|
|
.await
|
|
.is_ok();
|
|
cx.config
|
|
.expect(["rustup", "which", "rustfmt"])
|
|
.await
|
|
.with_stderr(snapbox::str![[r#"
|
|
error: 'rustfmt' is not installed for the toolchain 'custom-1'.
|
|
[..]`rustup component add rustfmt`[..]
|
|
|
|
"#]])
|
|
.is_err();
|
|
|
|
let path_2 = cx.config.customdir.join("custom-2");
|
|
let path_2 = path_2.to_string_lossy();
|
|
cx.config
|
|
.expect(["rustup", "toolchain", "link", "custom-2", &path_2])
|
|
.await
|
|
.is_ok();
|
|
cx.config
|
|
.expect(["rustup", "which", "--toolchain=custom-2", "rustfmt"])
|
|
.await
|
|
.with_stderr(snapbox::str![[r#"
|
|
[..]'rustfmt' is not installed for the toolchain 'custom-2'.
|
|
[..]`rustup component add --toolchain custom-2 rustfmt`[..]
|
|
|
|
"#]])
|
|
.is_err();
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn which_unknown_binary() {
|
|
let 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(["rustup", "toolchain", "link", "custom-1", &path_1])
|
|
.await
|
|
.is_ok();
|
|
cx.config
|
|
.expect(["rustup", "default", "custom-1"])
|
|
.await
|
|
.is_ok();
|
|
cx.config
|
|
.expect(["rustup", "which", "ls"])
|
|
.await
|
|
.with_stderr(snapbox::str![[r#"
|
|
[..]unknown binary 'ls' in toolchain 'custom-1'
|
|
|
|
"#]])
|
|
.is_err();
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn override_by_toolchain_on_the_command_line() {
|
|
let cx = CliTestContext::new(Scenario::SimpleV2).await;
|
|
cx.config
|
|
.expect(["rustup", "toolchain", "install", "stable", "nightly"])
|
|
.await
|
|
.is_ok();
|
|
|
|
cx.config
|
|
.expect(["rustup", "+stable", "which", "rustc"])
|
|
.await
|
|
.with_stdout(snapbox::str![[r#"
|
|
[..]/toolchains/stable-[HOST_TUPLE]/bin/rustc[EXE]
|
|
|
|
"#]])
|
|
.is_ok();
|
|
cx.config
|
|
.expect(["rustup", "+nightly", "which", "rustc"])
|
|
.await
|
|
.with_stdout(snapbox::str![[r#"
|
|
[..]/toolchains/nightly-[HOST_TUPLE]/bin/rustc[EXE]
|
|
|
|
"#]])
|
|
.is_ok();
|
|
|
|
cx.config
|
|
.expect(["rustup", "default", "nightly"])
|
|
.await
|
|
.is_ok();
|
|
cx.config
|
|
.expect(["rustup", "+stable", "which", "rustc"])
|
|
.await
|
|
.with_stdout(snapbox::str![[r#"
|
|
[..]/toolchains/stable-[HOST_TUPLE]/bin/rustc[EXE]
|
|
|
|
"#]])
|
|
.is_ok();
|
|
cx.config
|
|
.expect(["rustup", "+nightly", "which", "rustc"])
|
|
.await
|
|
.with_stdout(snapbox::str![[r#"
|
|
[..]/toolchains/nightly-[HOST_TUPLE]/bin/rustc[EXE]
|
|
|
|
"#]])
|
|
.is_ok();
|
|
cx.config
|
|
.expect(["rustup", "+nightly", "show"])
|
|
.await
|
|
.with_stdout(snapbox::str![[r#"
|
|
...
|
|
active because: overridden by +toolchain on the command line
|
|
...
|
|
"#]])
|
|
.is_ok();
|
|
cx.config
|
|
.expect(["rustup", "+foo", "which", "rustc"])
|
|
.await
|
|
.with_stderr(snapbox::str![[r#"
|
|
error: override toolchain 'foo' is not installed: the +toolchain on the command line specifies an uninstalled toolchain
|
|
|
|
"#]])
|
|
.is_err();
|
|
cx.config
|
|
.expect(["rustup", "+stable", "set", "profile", "minimal"])
|
|
.await
|
|
.with_stderr(snapbox::str![[r#"
|
|
info: profile set to minimal
|
|
|
|
"#]])
|
|
.is_ok();
|
|
cx.config
|
|
.expect(["rustup", "default"])
|
|
.await
|
|
.with_stdout(snapbox::str![[r#"
|
|
nightly-[HOST_TUPLE][..]
|
|
|
|
"#]])
|
|
.is_ok();
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn toolchain_link_then_list_verbose() {
|
|
let 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(["rustup", "toolchain", "link", "custom-1", &path_1])
|
|
.await
|
|
.is_ok();
|
|
cx.config
|
|
.expect(["rustup", "toolchain", "list", "-v"])
|
|
.await
|
|
.with_stdout(snapbox::str![[r#"
|
|
custom-1 [..]/custom-1
|
|
|
|
"#]])
|
|
.is_ok();
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn update_self_smart_guess() {
|
|
let cx = CliTestContext::new(Scenario::SimpleV2).await;
|
|
cx.config
|
|
.expect(["rustup", "update", "self"])
|
|
.await
|
|
.is_err()
|
|
.with_stderr(snapbox::str![[r#"
|
|
...
|
|
info: if you meant to update rustup itself, use `rustup self update`
|
|
...
|
|
"#]]);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn uninstall_self_smart_guess() {
|
|
let cx = CliTestContext::new(Scenario::SimpleV2).await;
|
|
cx.config
|
|
.expect(["rustup", "uninstall", "self"])
|
|
.await
|
|
.is_ok()
|
|
.with_stderr(snapbox::str![[r#"
|
|
...
|
|
info: if you meant to uninstall rustup itself, use `rustup self uninstall`
|
|
...
|
|
"#]]);
|
|
}
|
|
|
|
// https://github.com/rust-lang/rustup/issues/4073
|
|
#[tokio::test]
|
|
async fn toolchain_install_multi_components_comma() {
|
|
let cx = CliTestContext::new(Scenario::SimpleV2).await;
|
|
let components = ["rls", "rust-docs"];
|
|
cx.config
|
|
.expect([
|
|
"rustup",
|
|
"toolchain",
|
|
"install",
|
|
"--profile=minimal",
|
|
"--component",
|
|
&components.join(","),
|
|
"nightly",
|
|
])
|
|
.await
|
|
.is_ok();
|
|
cx.config
|
|
.expect(["rustup", "+nightly", "component", "list", "--installed"])
|
|
.await
|
|
.with_stdout(snapbox::str![[r#"
|
|
...
|
|
rls-[HOST_TUPLE][..]
|
|
...
|
|
rust-docs-[HOST_TUPLE][..]
|
|
...
|
|
"#]])
|
|
.is_ok();
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn rustup_updates_cargo_env_if_proxy() {
|
|
let cx = CliTestContext::new(Scenario::SimpleV2).await;
|
|
cx.config
|
|
.expect(["rustup", "default", "stable"])
|
|
.await
|
|
.is_ok();
|
|
|
|
use std::env::consts::EXE_SUFFIX;
|
|
let proxy_path = cx
|
|
.config
|
|
.workdir
|
|
.borrow()
|
|
.join("bin")
|
|
.join(format!("cargo{EXE_SUFFIX}"));
|
|
let real_path = cx.config.expect(["rustup", "which", "cargo"]).await;
|
|
real_path.is_ok();
|
|
let real_path = &real_path.output.stdout;
|
|
|
|
fs::create_dir_all(proxy_path.parent().unwrap()).unwrap();
|
|
#[cfg(windows)]
|
|
if std::os::windows::fs::symlink_file("rustup", &proxy_path).is_err() {
|
|
// skip this test on Windows if symlinking isn't enabled.
|
|
return;
|
|
}
|
|
#[cfg(unix)]
|
|
std::os::unix::fs::symlink("rustup", &proxy_path).unwrap();
|
|
|
|
// If CARGO isn't set then we should not set it.
|
|
cx.config
|
|
.expect(["cargo", "--echo-cargo-env"])
|
|
.await
|
|
.with_stderr(snapbox::str![[r#"
|
|
...
|
|
CARGO environment variable not set[..]
|
|
...
|
|
"#]])
|
|
.is_err();
|
|
|
|
// If CARGO is set to a proxy then change it to the real CARGO path
|
|
cx.config
|
|
.expect_with_env(
|
|
["cargo", "--echo-cargo-env"],
|
|
[("CARGO", &*proxy_path.display().to_string())],
|
|
)
|
|
.await
|
|
.extend_redactions([("[REAL_PATH]", real_path)])
|
|
.with_stderr(snapbox::str![[r#"
|
|
[REAL_PATH]
|
|
"#]])
|
|
.is_ok();
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn rust_analyzer_proxy_falls_back_external() {
|
|
let cx = CliTestContext::new(Scenario::SimpleV2).await;
|
|
cx.config
|
|
.expect([
|
|
"rustup",
|
|
"toolchain",
|
|
"install",
|
|
"stable",
|
|
"--profile=minimal",
|
|
"--component=rls",
|
|
])
|
|
.await
|
|
.is_ok();
|
|
cx.config
|
|
.expect(["rustup", "default", "stable"])
|
|
.await
|
|
.is_ok();
|
|
|
|
// We pretend to have a `rust-analyzer` installation by reusing the `rls`
|
|
// proxy and mock binary.
|
|
let rls = format!("rls{EXE_SUFFIX}");
|
|
let ra = format!("rust-analyzer{EXE_SUFFIX}");
|
|
let exedir = &cx.config.exedir;
|
|
let bindir = &cx
|
|
.config
|
|
.rustupdir
|
|
.join("toolchains")
|
|
.join(format!("stable-{}", this_host_tuple()))
|
|
.join("bin");
|
|
for dir in [exedir, bindir] {
|
|
fs::rename(dir.join(&rls), dir.join(&ra)).unwrap();
|
|
}
|
|
|
|
// Base case: rustup-hosted RA installed, external RA unavailable,
|
|
// use the former.
|
|
let real_path = cx
|
|
.config
|
|
.expect(["rust-analyzer", "--echo-current-exe"])
|
|
.await;
|
|
real_path.is_ok();
|
|
let real_path = Path::new(real_path.output.stderr.trim());
|
|
|
|
assert!(real_path.is_file());
|
|
|
|
let tempdir = tempfile::Builder::new().prefix("rustup").tempdir().unwrap();
|
|
let extern_dir = tempdir.path();
|
|
let extern_path = &extern_dir.join("rust-analyzer");
|
|
fs::copy(real_path, extern_path).unwrap();
|
|
|
|
// First case: rustup-hosted and external RA both installed,
|
|
// prioritize the former.
|
|
cx.config
|
|
.expect_with_env(
|
|
["rust-analyzer", "--echo-current-exe"],
|
|
[("PATH", &*extern_dir.display().to_string())],
|
|
)
|
|
.await
|
|
.extend_redactions([("[REAL_PATH]", real_path.to_owned())])
|
|
.with_stderr(snapbox::str![[r#"
|
|
[REAL_PATH]
|
|
|
|
"#]])
|
|
.is_ok();
|
|
|
|
// Second case: rustup-hosted RA unavailable, fallback on the external RA.
|
|
fs::remove_file(bindir.join(&ra)).unwrap();
|
|
cx.config
|
|
.expect_with_env(
|
|
["rust-analyzer", "--echo-current-exe"],
|
|
[("PATH", &*extern_dir.display().to_string())],
|
|
)
|
|
.await
|
|
.extend_redactions([("[EXTERN_PATH]", extern_path)])
|
|
.with_stderr(snapbox::str![[r#"
|
|
info: `rust-analyzer` is unavailable for the active toolchain
|
|
info: falling back to "[EXTERN_PATH]"
|
|
[EXTERN_PATH]
|
|
|
|
"#]])
|
|
.is_ok();
|
|
}
|