Files
rustup/tests/suite/cli_rustup.rs

4175 lines
105 KiB
Rust
Raw Permalink Blame History

//! Test cases for new rustup UI
use std::{
env::consts::EXE_SUFFIX,
fs,
path::{Path, PathBuf},
};
use rustup::{
for_host,
test::{
CROSS_ARCH1, CROSS_ARCH2, CliTestContext, MULTI_ARCH1, Scenario, this_host_tuple,
topical_doc_data,
},
utils::raw,
};
#[tokio::test]
async fn rustup_stable() {
let mut cx = CliTestContext::new(Scenario::None).await;
{
let cx = cx.with_dist_dir(Scenario::ArchivesV2_2015_01_01);
cx.config
.expect(["rustup", "toolchain", "add", "stable"])
.await
.is_ok();
}
let cx = cx.with_dist_dir(Scenario::SimpleV2);
cx.config
.expect(["rustup", "update"])
.await
.with_stdout(snapbox::str![[r#"
stable-[HOST_TUPLE] updated - 1.1.0 (hash-stable-1.1.0) (from 1.0.0 (hash-stable-1.0.0))
"#]])
.with_stderr(snapbox::str![[r#"
info: syncing channel updates for stable-[HOST_TUPLE]
info: latest update on 2015-01-02 for version 1.1.0 (hash-stable-1.1.0)
info: removing previous version of component cargo
...
info: cleaning up downloads & tmp directories
"#]])
.is_ok();
cx.config
.expect(["rustup", "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 rustup_stable_quiet() {
let mut cx = CliTestContext::new(Scenario::None).await;
{
let cx = cx.with_dist_dir(Scenario::ArchivesV2_2015_01_01);
cx.config
.expect(["rustup", "--quiet", "update", "stable"])
.await
.is_ok();
}
let cx = cx.with_dist_dir(Scenario::SimpleV2);
cx.config
.expect(["rustup", "--quiet", "update"])
.await
.with_stdout(snapbox::str![[r#"
stable-[HOST_TUPLE] updated - 1.1.0 (hash-stable-1.1.0) (from 1.0.0 (hash-stable-1.0.0))
"#]])
.with_stderr(snapbox::str![[""]])
.is_ok();
}
#[tokio::test]
async fn rustup_stable_no_change() {
let cx = CliTestContext::new(Scenario::ArchivesV2_2015_01_01).await;
cx.config
.expect(["rustup", "update", "stable"])
.await
.is_ok();
cx.config
.expect(["rustup", "update"])
.await
.with_stdout(snapbox::str![[r#"
stable-[HOST_TUPLE] unchanged - 1.0.0 (hash-stable-1.0.0)
"#]])
.with_stderr(snapbox::str![[r#"
info: cleaning up downloads & tmp directories
"#]])
.is_ok();
}
#[tokio::test]
async fn rustup_all_channels() {
let mut cx = CliTestContext::new(Scenario::None).await;
{
let cx = cx.with_dist_dir(Scenario::ArchivesV2_2015_01_01);
cx.config
.expect(["rustup", "toolchain", "add", "stable", "beta", "nightly"])
.await
.is_ok();
}
let cx = cx.with_dist_dir(Scenario::SimpleV2);
cx.config
.expect(["rustup", "update"])
.await
.with_stdout(snapbox::str![[r#"
stable-[HOST_TUPLE] updated - 1.1.0 (hash-stable-1.1.0) (from 1.0.0 (hash-stable-1.0.0))
beta-[HOST_TUPLE] updated - 1.2.0 (hash-beta-1.2.0) (from 1.1.0 (hash-beta-1.1.0))
nightly-[HOST_TUPLE] updated - 1.3.0 (hash-nightly-2) (from 1.2.0 (hash-nightly-1))
"#]])
.with_stderr(snapbox::str![[r#"
info: syncing channel updates for stable-[HOST_TUPLE]
info: latest update on 2015-01-02 for version 1.1.0 (hash-stable-1.1.0)
info: removing previous version of component cargo
...
info: syncing channel updates for beta-[HOST_TUPLE]
info: latest update on 2015-01-02 for version 1.2.0 (hash-beta-1.2.0)
info: removing previous version of component cargo
...
info: syncing channel updates for nightly-[HOST_TUPLE]
info: latest update on 2015-01-02 for version 1.3.0 (hash-nightly-2)
info: removing previous version of component cargo
...
info: cleaning up downloads & tmp directories
"#]])
.is_ok();
cx.config
.expect(["rustup", "+stable", "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]
"#]]);
cx.config
.expect(["rustup", "+beta", "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]
"#]]);
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 rustup_some_channels_up_to_date() {
let mut cx = CliTestContext::new(Scenario::None).await;
{
let cx = cx.with_dist_dir(Scenario::ArchivesV2_2015_01_01);
cx.config
.expect(["rustup", "toolchain", "add", "stable", "beta", "nightly"])
.await
.is_ok();
}
let cx = cx.with_dist_dir(Scenario::SimpleV2);
cx.config.expect(["rustup", "update", "beta"]).await.is_ok();
cx.config
.expect(["rustup", "update"])
.await
.with_stdout(snapbox::str![[r#"
stable-[HOST_TUPLE] updated - 1.1.0 (hash-stable-1.1.0) (from 1.0.0 (hash-stable-1.0.0))
beta-[HOST_TUPLE] unchanged - 1.2.0 (hash-beta-1.2.0)
nightly-[HOST_TUPLE] updated - 1.3.0 (hash-nightly-2) (from 1.2.0 (hash-nightly-1))
"#]])
.with_stderr(snapbox::str![[r#"
info: syncing channel updates for stable-[HOST_TUPLE]
info: latest update on 2015-01-02 for version 1.1.0 (hash-stable-1.1.0)
info: removing previous version of component cargo
...
info: syncing channel updates for nightly-[HOST_TUPLE]
info: latest update on 2015-01-02 for version 1.3.0 (hash-nightly-2)
info: removing previous version of component cargo
...
info: cleaning up downloads & tmp directories
"#]])
.is_ok();
cx.config
.expect(["rustup", "+stable", "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]
"#]]);
cx.config
.expect(["rustup", "+beta", "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]
"#]]);
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 rustup_no_channels() {
let cx = CliTestContext::new(Scenario::SimpleV2).await;
cx.config
.expect(["rustup", "update"])
.await
.with_stdout(snapbox::str![""])
.with_stderr(snapbox::str![[r#"
info: no updatable toolchains installed
info: cleaning up downloads & tmp directories
"#]])
.is_ok();
}
#[tokio::test]
async fn default() {
let cx = CliTestContext::new(Scenario::SimpleV2).await;
cx.config
.expect(["rustup", "default", "nightly"])
.await
.with_stdout(snapbox::str![[r#"
nightly-[HOST_TUPLE] installed - 1.3.0 (hash-nightly-2)
"#]])
.with_stderr(snapbox::str![[r#"
info: syncing channel updates for nightly-[HOST_TUPLE]
info: latest update on 2015-01-02 for version 1.3.0 (hash-nightly-2)
info: downloading 4 components
info: default toolchain set to nightly-[HOST_TUPLE]
"#]])
.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 default_override() {
let cx = CliTestContext::new(Scenario::SimpleV2).await;
cx.config
.expect(["rustup", "toolchain", "add", "nightly"])
.await
.is_ok();
cx.config
.expect(["rustup", "default", "stable"])
.await
.is_ok();
cx.config
.expect(["rustup", "override", "set", "nightly"])
.await
.is_ok();
cx.config
.expect(["rustup", "default", "stable"])
.await
.with_stderr(snapbox::str![[r#"
info: using existing install for stable-[HOST_TUPLE]
info: default toolchain set to stable-[HOST_TUPLE]
info: note that the toolchain 'nightly-[HOST_TUPLE]' is currently in use (directory override for '[..]')
"#]])
.is_ok();
}
#[tokio::test]
async fn rustup_zstd() {
let cx = CliTestContext::new(Scenario::ArchivesV2_2015_01_01).await;
cx.config
.expect(["rustup", "--verbose", "toolchain", "add", "nightly"])
.await
.with_stderr(snapbox::str![[r#"
...
[..]dist/2015-01-01/rust-std-nightly-[HOST_TUPLE].tar.zst[..]
...
"#]])
.is_ok();
}
#[tokio::test]
async fn add_target() {
let cx = CliTestContext::new(Scenario::SimpleV2).await;
let path = format!(
"toolchains/nightly-{}/lib/rustlib/{}/lib/libstd.rlib",
&this_host_tuple(),
CROSS_ARCH1
);
cx.config
.expect(["rustup", "default", "nightly"])
.await
.is_ok();
cx.config
.expect(["rustup", "target", "add", CROSS_ARCH1])
.await
.is_ok();
assert!(cx.config.rustupdir.has(path));
}
#[tokio::test]
async fn remove_target() {
let cx = CliTestContext::new(Scenario::SimpleV2).await;
let path = format!(
"toolchains/nightly-{}/lib/rustlib/{}/lib/libstd.rlib",
&this_host_tuple(),
CROSS_ARCH1
);
cx.config
.expect(["rustup", "default", "nightly"])
.await
.is_ok();
cx.config
.expect(["rustup", "target", "add", CROSS_ARCH1])
.await
.is_ok();
assert!(cx.config.rustupdir.has(&path));
cx.config
.expect(["rustup", "target", "remove", CROSS_ARCH1])
.await
.is_ok();
assert!(!cx.config.rustupdir.has(&path));
}
#[tokio::test]
async fn add_remove_multiple_targets() {
let cx = CliTestContext::new(Scenario::SimpleV2).await;
cx.config
.expect(["rustup", "default", "nightly"])
.await
.is_ok();
cx.config
.expect(["rustup", "target", "add", CROSS_ARCH1, CROSS_ARCH2])
.await
.is_ok();
let path = format!(
"toolchains/nightly-{}/lib/rustlib/{}/lib/libstd.rlib",
&this_host_tuple(),
CROSS_ARCH1
);
assert!(cx.config.rustupdir.has(path));
let path = format!(
"toolchains/nightly-{}/lib/rustlib/{}/lib/libstd.rlib",
&this_host_tuple(),
CROSS_ARCH2
);
assert!(cx.config.rustupdir.has(path));
cx.config
.expect(["rustup", "target", "remove", CROSS_ARCH1, CROSS_ARCH2])
.await
.is_ok();
let path = format!(
"toolchains/nightly-{}/lib/rustlib/{}/lib/libstd.rlib",
&this_host_tuple(),
CROSS_ARCH1
);
assert!(!cx.config.rustupdir.has(path));
let path = format!(
"toolchains/nightly-{}/lib/rustlib/{}/lib/libstd.rlib",
&this_host_tuple(),
CROSS_ARCH2
);
assert!(!cx.config.rustupdir.has(path));
}
#[tokio::test]
async fn list_targets() {
let cx = CliTestContext::new(Scenario::SimpleV2).await;
cx.config
.expect(["rustup", "default", "nightly"])
.await
.is_ok();
cx.config
.expect(["rustup", "target", "list"])
.await
.with_stdout(snapbox::str![[r#"
...
[CROSS_ARCH_I]
...
"#]])
.is_ok();
}
#[tokio::test]
async fn list_installed_targets() {
let cx = CliTestContext::new(Scenario::SimpleV2).await;
cx.config
.expect(["rustup", "default", "nightly"])
.await
.is_ok();
cx.config
.expect(["rustup", "target", "list", "--installed"])
.await
.with_stdout(snapbox::str![[r#"
[HOST_TUPLE]
"#]])
.is_ok();
}
#[tokio::test]
async fn add_target_explicit() {
let cx = CliTestContext::new(Scenario::SimpleV2).await;
let path = format!(
"toolchains/nightly-{}/lib/rustlib/{}/lib/libstd.rlib",
&this_host_tuple(),
CROSS_ARCH1
);
cx.config
.expect(["rustup", "toolchain", "add", "nightly"])
.await
.is_ok();
cx.config
.expect([
"rustup",
"target",
"add",
"--toolchain",
"nightly",
CROSS_ARCH1,
])
.await
.is_ok();
assert!(cx.config.rustupdir.has(path));
}
#[tokio::test]
async fn remove_target_explicit() {
let cx = CliTestContext::new(Scenario::SimpleV2).await;
let path = format!(
"toolchains/nightly-{}/lib/rustlib/{}/lib/libstd.rlib",
&this_host_tuple(),
CROSS_ARCH1
);
cx.config
.expect(["rustup", "toolchain", "add", "nightly"])
.await
.is_ok();
cx.config
.expect([
"rustup",
"target",
"add",
"--toolchain",
"nightly",
CROSS_ARCH1,
])
.await
.is_ok();
assert!(cx.config.rustupdir.has(&path));
cx.config
.expect([
"rustup",
"target",
"remove",
"--toolchain",
"nightly",
CROSS_ARCH1,
])
.await
.is_ok();
assert!(!cx.config.rustupdir.has(&path));
}
#[tokio::test]
async fn list_targets_explicit() {
let cx = CliTestContext::new(Scenario::SimpleV2).await;
cx.config
.expect(["rustup", "toolchain", "add", "nightly"])
.await
.is_ok();
cx.config
.expect(["rustup", "target", "list", "--toolchain", "nightly"])
.await
.with_stdout(snapbox::str![[r#"
...
[CROSS_ARCH_I]
...
"#]])
.is_ok();
}
#[tokio::test]
async fn link() {
let cx = CliTestContext::new(Scenario::SimpleV2).await;
let path = cx.config.customdir.join("custom-1");
let path = path.to_string_lossy();
cx.config
.expect(["rustup", "toolchain", "link", "custom", &path])
.await
.is_ok();
cx.config
.expect(["rustup", "default", "custom"])
.await
.is_ok();
cx.config
.expect(["rustc", "--version"])
.await
.with_stdout(snapbox::str![[r#"
1.0.0 (hash-c-1)
"#]])
.is_ok();
cx.config
.expect(["rustup", "show"])
.await
.with_stdout(snapbox::str![[r#"
...
custom (active, default)
...
"#]])
.is_ok();
cx.config
.expect(["rustup", "update", "nightly"])
.await
.is_ok();
cx.config
.expect(["rustup", "default", "nightly"])
.await
.is_ok();
cx.config
.expect(["rustup", "show"])
.await
.with_stdout(snapbox::str![[r#"
...
custom
...
"#]])
.is_ok();
}
// 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.
#[tokio::test]
async fn fallback_cargo_calls_correct_rustc() {
let 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(["rustup", "toolchain", "link", "custom", &path])
.await
.is_ok();
cx.config
.expect(["rustup", "default", "custom"])
.await
.is_ok();
cx.config
.expect(["rustup", "update", "nightly"])
.await
.is_ok();
cx.config
.expect(["rustc", "--version"])
.await
.with_stdout(snapbox::str![[r#"
1.0.0 (hash-c-1)
"#]])
.is_ok();
cx.config
.expect(["cargo", "--version"])
.await
.with_stdout(snapbox::str![[r#"
1.3.0 (hash-nightly-2)
"#]])
.is_ok();
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(["cargo", "--call-rustc"])
.await
.with_stdout(snapbox::str![[r#"
1.0.0 (hash-c-1)
"#]])
.is_ok();
}
// Checks that cargo can recursively invoke itself with rustup shorthand (via
// the proxy).
//
// This involves a series of chained commands:
//
// 1. Calls `cargo --recursive-cargo-subcommand`
// 2. The rustup `cargo` proxy launches, and launches the "mock" nightly cargo exe.
// 3. The nightly "mock" cargo sees --recursive-cargo-subcommand, and launches
// `cargo-foo --recursive-cargo`
// 4. `cargo-foo` sees `--recursive-cargo` and launches `cargo +nightly --version`
// 5. The rustup `cargo` proxy launches, and launches the "mock" nightly cargo exe.
// 6. The nightly "mock" cargo sees `--version` and prints the version.
//
// Previously, rustup would place the toolchain's `bin` directory in PATH for
// Windows due to some DLL issues. However, those aren't necessary anymore.
// 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.
#[tokio::test]
async fn recursive_cargo() {
let cx = CliTestContext::new(Scenario::ArchivesV2).await;
cx.config
.expect(["rustup", "default", "nightly"])
.await
.is_ok();
// 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 which_cargo = cx.config.expect(["rustup", "which", "cargo"]).await;
let real_mock_cargo = which_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(["cargo", "--recursive-cargo-subcommand"])
.await
.with_stdout(snapbox::str![[r#"
1.3.0 (hash-nightly-2)
"#]])
.is_ok();
}
#[tokio::test]
async fn show_home() {
let cx = CliTestContext::new(Scenario::None).await;
cx.config
.expect(["rustup", "show", "home"])
.await
.extend_redactions([("[RUSTUP_DIR]", &cx.config.rustupdir.to_string())])
.with_stdout(snapbox::str![[r#"
[RUSTUP_DIR]
"#]])
.with_stderr(snapbox::str![[""]])
.is_ok();
}
#[tokio::test]
async fn show_toolchain_none() {
let cx = CliTestContext::new(Scenario::None).await;
cx.config
.expect(["rustup", "show"])
.await
.extend_redactions([("[RUSTUP_DIR]", &cx.config.rustupdir.to_string())])
.with_stdout(snapbox::str![[r#"
Default host: [HOST_TUPLE]
rustup home: [RUSTUP_DIR]
installed toolchains
--------------------
active toolchain
----------------
no active toolchain
"#]])
.with_stderr(snapbox::str![[""]])
.is_ok();
}
#[tokio::test]
async fn show_toolchain_default() {
let cx = CliTestContext::new(Scenario::SimpleV2).await;
cx.config
.expect(["rustup", "default", "nightly"])
.await
.is_ok();
cx.config
.expect(["rustup", "show"])
.await
.extend_redactions([("[RUSTUP_DIR]", &cx.config.rustupdir.to_string())])
.with_stdout(snapbox::str![[r#"
Default host: [HOST_TUPLE]
rustup home: [RUSTUP_DIR]
installed toolchains
--------------------
nightly-[HOST_TUPLE] (active, default)
active toolchain
----------------
name: nightly-[HOST_TUPLE]
active because: it's the default toolchain
installed targets:
[HOST_TUPLE]
"#]])
.with_stderr(snapbox::str![[""]])
.is_ok();
}
#[tokio::test]
async fn show_no_default() {
let cx = CliTestContext::new(Scenario::SimpleV2).await;
cx.config
.expect(["rustup", "install", "nightly"])
.await
.is_ok();
cx.config
.expect(["rustup", "default", "none"])
.await
.is_ok();
cx.config
.expect(["rustup", "show"])
.await
.with_stdout(snapbox::str![[r#"
...
installed toolchains
--------------------
nightly-[HOST_TUPLE]
active toolchain
----------------
no active toolchain
"#]])
.is_ok();
}
#[tokio::test]
async fn show_no_default_active() {
let cx = CliTestContext::new(Scenario::SimpleV2).await;
cx.config
.expect(["rustup", "install", "nightly"])
.await
.is_ok();
cx.config
.expect(["rustup", "default", "none"])
.await
.is_ok();
cx.config
.expect(["rustup", "+nightly", "show"])
.await
.with_stdout(snapbox::str![[r#"
...
installed toolchains
--------------------
nightly-[HOST_TUPLE] (active)
active toolchain
----------------
name: nightly-[HOST_TUPLE]
active because: overridden by +toolchain on the command line
installed targets:
[HOST_TUPLE]
"#]])
.is_ok();
}
#[tokio::test]
async fn show_multiple_toolchains() {
let cx = CliTestContext::new(Scenario::SimpleV2).await;
cx.config
.expect(["rustup", "default", "nightly"])
.await
.is_ok();
cx.config
.expect(["rustup", "update", "stable"])
.await
.is_ok();
cx.config
.expect(["rustup", "show"])
.await
.extend_redactions([("[RUSTUP_DIR]", &cx.config.rustupdir.to_string())])
.with_stdout(snapbox::str![[r#"
Default host: [HOST_TUPLE]
rustup home: [RUSTUP_DIR]
installed toolchains
--------------------
stable-[HOST_TUPLE]
nightly-[HOST_TUPLE] (active, default)
active toolchain
----------------
name: nightly-[HOST_TUPLE]
active because: it's the default toolchain
installed targets:
[HOST_TUPLE]
"#]])
.with_stderr(snapbox::str![[""]])
.is_ok();
}
#[tokio::test]
async fn show_multiple_targets() {
let cx = CliTestContext::new(Scenario::MultiHost).await;
cx.config
.expect([
"rustup",
"default",
&format!("nightly-{MULTI_ARCH1}"),
"--force-non-host",
])
.await
.is_ok();
cx.config
.expect(["rustup", "target", "add", CROSS_ARCH2])
.await
.is_ok();
cx.config
.expect(["rustup", "show"])
.await
.extend_redactions([("[RUSTUP_DIR]", &cx.config.rustupdir.to_string())])
.with_stdout(snapbox::str![[r#"
Default host: [HOST_TUPLE]
rustup home: [RUSTUP_DIR]
installed toolchains
--------------------
nightly-[MULTI_ARCH_I] (active, default)
active toolchain
----------------
name: nightly-[MULTI_ARCH_I]
active because: it's the default toolchain
installed targets:
[CROSS_ARCH_II]
[MULTI_ARCH_I]
"#]])
.with_stderr(snapbox::str![[""]])
.is_ok();
}
#[tokio::test]
async fn show_multiple_toolchains_and_targets() {
if cfg!(target_os = "linux") && cfg!(target_arch = "x86") {
return;
}
let cx = CliTestContext::new(Scenario::MultiHost).await;
cx.config
.expect([
"rustup",
"default",
&format!("nightly-{MULTI_ARCH1}"),
"--force-non-host",
])
.await
.is_ok();
cx.config
.expect(["rustup", "target", "add", CROSS_ARCH2])
.await
.is_ok();
cx.config
.expect([
"rustup",
"update",
"--force-non-host",
&format!("stable-{MULTI_ARCH1}"),
])
.await
.is_ok();
cx.config
.expect(["rustup", "show"])
.await
.extend_redactions([("[RUSTUP_DIR]", &cx.config.rustupdir.to_string())])
.with_stdout(snapbox::str![[r#"
Default host: [HOST_TUPLE]
rustup home: [RUSTUP_DIR]
installed toolchains
--------------------
stable-[MULTI_ARCH_I]
nightly-[MULTI_ARCH_I] (active, default)
active toolchain
----------------
name: nightly-[MULTI_ARCH_I]
active because: it's the default toolchain
installed targets:
[CROSS_ARCH_II]
[MULTI_ARCH_I]
"#]])
.with_stderr(snapbox::str![[""]])
.is_ok();
}
#[tokio::test]
async fn list_default_toolchain() {
let cx = CliTestContext::new(Scenario::SimpleV2).await;
cx.config
.expect(["rustup", "default", "nightly"])
.await
.is_ok();
cx.config
.expect(["rustup", "toolchain", "list"])
.await
.with_stdout(snapbox::str![[r#"
nightly-[HOST_TUPLE] (active, default)
"#]])
.with_stderr(snapbox::str![[""]])
.is_ok();
}
#[tokio::test]
async fn list_default_toolchain_quiet() {
let cx = CliTestContext::new(Scenario::SimpleV2).await;
cx.config
.expect(["rustup", "default", "nightly"])
.await
.is_ok();
cx.config
.expect(["rustup", "toolchain", "list", "--quiet"])
.await
.with_stdout(snapbox::str![[r#"
nightly-[HOST_TUPLE]
"#]])
.with_stderr(snapbox::str![[""]])
.is_ok();
}
#[tokio::test]
async fn list_no_default_toolchain() {
let cx = CliTestContext::new(Scenario::SimpleV2).await;
cx.config
.expect(["rustup", "install", "nightly"])
.await
.is_ok();
cx.config
.expect(["rustup", "default", "none"])
.await
.is_ok();
cx.config
.expect(["rustup", "toolchain", "list"])
.await
.with_stdout(snapbox::str![[r#"
nightly-[HOST_TUPLE]
"#]])
.with_stderr(snapbox::str![[""]])
.is_ok();
}
#[tokio::test]
async fn list_no_default_override_toolchain() {
let cx = CliTestContext::new(Scenario::SimpleV2).await;
cx.config
.expect(["rustup", "override", "set", "nightly"])
.await
.is_ok();
cx.config
.expect(["rustup", "toolchain", "list"])
.await
.with_stdout(snapbox::str![[r#"
nightly-[HOST_TUPLE] (active)
"#]])
.with_stderr(snapbox::str![[""]])
.is_ok();
}
#[tokio::test]
async fn list_default_and_override_toolchain() {
let cx = CliTestContext::new(Scenario::SimpleV2).await;
cx.config
.expect(&["rustup", "default", "nightly"])
.await
.is_ok();
cx.config
.expect(&["rustup", "override", "set", "nightly"])
.await
.is_ok();
cx.config
.expect(&["rustup", "toolchain", "list"])
.await
.is_ok()
.with_stdout(snapbox::str![[r#"
nightly-[HOST_TUPLE] (active, default)
"#]])
.with_stderr(snapbox::str![[""]]);
}
#[tokio::test]
async fn show_toolchain_override() {
let cx = CliTestContext::new(Scenario::SimpleV2).await;
cx.config
.expect(["rustup", "override", "add", "nightly"])
.await
.is_ok();
cx.config
.expect(["rustup", "show"])
.await
.extend_redactions([("[RUSTUP_DIR]", &cx.config.rustupdir.to_string())])
.with_stdout(snapbox::str![[r#"
Default host: [HOST_TUPLE]
rustup home: [RUSTUP_DIR]
installed toolchains
--------------------
nightly-[HOST_TUPLE] (active)
active toolchain
----------------
name: nightly-[HOST_TUPLE]
active because: directory override for '[..]'
installed targets:
[HOST_TUPLE]
"#]])
.with_stderr(snapbox::str![[""]])
.is_ok();
}
#[tokio::test]
async fn show_toolchain_toolchain_file_override() {
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();
let cwd = cx.config.current_dir();
let toolchain_file = cwd.join("rust-toolchain");
raw::write_file(&toolchain_file, "nightly").unwrap();
cx.config
.expect(["rustup", "show"])
.await
.extend_redactions([("[RUSTUP_DIR]", &cx.config.rustupdir.to_string())])
.with_stdout(snapbox::str![[r#"
Default host: [HOST_TUPLE]
rustup home: [RUSTUP_DIR]
installed toolchains
--------------------
stable-[HOST_TUPLE] (default)
nightly-[HOST_TUPLE] (active)
active toolchain
----------------
name: nightly-[HOST_TUPLE]
active because: overridden by '[..]'
installed targets:
[HOST_TUPLE]
"#]])
.with_stderr(snapbox::str![[""]])
.is_ok();
}
#[tokio::test]
async fn show_toolchain_version_nested_file_override() {
let mut cx = CliTestContext::new(Scenario::SimpleV2).await;
cx.config
.expect(["rustup", "toolchain", "add", "stable", "beta", "nightly"])
.await
.is_ok();
cx.config
.expect(["rustup", "default", "stable"])
.await
.is_ok();
cx.config
.expect(["rustup", "toolchain", "install", "nightly"])
.await
.is_ok();
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 cx = cx.change_dir(&subdir);
cx.config
.expect(["rustup", "show"])
.await
.extend_redactions([("[RUSTUP_DIR]", &cx.config.rustupdir.to_string())])
.with_stdout(snapbox::str![[r#"
Default host: [HOST_TUPLE]
rustup home: [RUSTUP_DIR]
installed toolchains
--------------------
stable-[HOST_TUPLE] (default)
beta-[HOST_TUPLE]
nightly-[HOST_TUPLE] (active)
active toolchain
----------------
name: nightly-[HOST_TUPLE]
active because: overridden by '[..]'
installed targets:
[HOST_TUPLE]
"#]])
.with_stderr(snapbox::str![[""]])
.is_ok();
}
#[tokio::test]
async fn show_toolchain_toolchain_file_override_not_installed() {
let cx = CliTestContext::new(Scenario::SimpleV2).await;
cx.config
.expect(["rustup", "default", "stable"])
.await
.is_ok();
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.
cx.config
.expect_with_env(["rustup", "show"], [("RUSTUP_AUTO_INSTALL", "0")])
.await
.extend_redactions([
("[RUSTUP_DIR]", &cx.config.rustupdir.rustupdir),
("[TOOLCHAIN_FILE]", &toolchain_file),
])
.with_stdout(snapbox::str![[r#"
Default host: [HOST_TUPLE]
rustup home: [RUSTUP_DIR]
"#]])
.with_stderr(snapbox::str![[r#"
error: toolchain 'nightly-[HOST_TUPLE]' is not installed
help: run `rustup toolchain install` to install it
"#]])
.is_err();
}
#[tokio::test]
async fn show_toolchain_override_not_installed() {
let cx = CliTestContext::new(Scenario::SimpleV2).await;
cx.config
.expect(["rustup", "override", "add", "nightly"])
.await
.is_ok();
cx.config
.expect(["rustup", "toolchain", "remove", "nightly"])
.await
.is_ok();
cx.config
.expect(["rustup", "show"])
.await
.extend_redactions([("[RUSTUP_DIR]", &cx.config.rustupdir.to_string())])
.with_stdout(snapbox::str![[r#"
Default host: [HOST_TUPLE]
rustup home: [RUSTUP_DIR]
installed toolchains
--------------------
active toolchain
----------------
name: nightly-[HOST_TUPLE]
active because: directory override for '[..]'
installed targets:
[HOST_TUPLE]
"#]])
.is_ok();
cx.config
.expect(["rustup", "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 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 cx = cx.change_dir(emptydir.path());
cx.config
.expect(["rustup", "override", "set", "nightly", "--path", cwd_str])
.await
.is_ok();
}
cx.config
.expect(["rustup", "override", "list"])
.await
.extend_redactions([("[CWD]", cwd_str.to_string())])
.with_stdout(snapbox::str![[r#"
[CWD] nightly-[HOST_TUPLE]
"#]])
.with_stderr(snapbox::str![[""]])
.is_ok();
{
let cx = cx.change_dir(emptydir.path());
cx.config
.expect(["rustup", "override", "unset", "--path", cwd_str])
.await
.is_ok();
}
cx.config
.expect(["rustup", "override", "list"])
.await
.with_stdout(snapbox::str![[r#"
no overrides
"#]])
.with_stderr(snapbox::str![[""]])
.is_ok();
}
#[tokio::test]
async fn show_toolchain_env() {
let cx = CliTestContext::new(Scenario::SimpleV2).await;
cx.config
.expect(&["rustup", "default", "nightly"])
.await
.is_ok();
cx.config
.expect_with_env(["rustup", "show"], [("RUSTUP_TOOLCHAIN", "nightly")])
.await
.extend_redactions([("[RUSTUP_DIR]", &cx.config.rustupdir.to_string())])
.is_ok()
.with_stdout(snapbox::str![[r#"
Default host: [HOST_TUPLE]
rustup home: [RUSTUP_DIR]
installed toolchains
--------------------
nightly-[HOST_TUPLE] (active, default)
active toolchain
----------------
name: nightly-[HOST_TUPLE]
active because: overridden by environment variable RUSTUP_TOOLCHAIN
installed targets:
[HOST_TUPLE]
"#]]);
}
#[tokio::test]
async fn show_toolchain_env_not_installed() {
let cx = CliTestContext::new(Scenario::SimpleV2).await;
cx.config
.expect_with_env(["rustup", "show"], [("RUSTUP_TOOLCHAIN", "nightly")])
.await
.extend_redactions([("[RUSTUP_DIR]", &cx.config.rustupdir.to_string())])
.is_ok()
.with_stdout(snapbox::str![[r#"
Default host: [HOST_TUPLE]
rustup home: [RUSTUP_DIR]
installed toolchains
--------------------
active toolchain
----------------
name: nightly-[HOST_TUPLE]
active because: overridden by environment variable RUSTUP_TOOLCHAIN
installed targets:
[HOST_TUPLE]
"#]]);
}
#[tokio::test]
async fn show_active_toolchain() {
let cx = CliTestContext::new(Scenario::SimpleV2).await;
cx.config
.expect(&["rustup", "default", "nightly"])
.await
.is_ok();
cx.config
.expect(&["rustup", "show", "active-toolchain"])
.await
.with_stdout(snapbox::str![[r#"
nightly-[HOST_TUPLE] (default)
"#]])
.with_stderr(snapbox::str![[""]])
.is_ok();
}
#[tokio::test]
async fn show_with_verbose() {
let mut cx = CliTestContext::new(Scenario::None).await;
{
let cx = cx.with_dist_dir(Scenario::SimpleV2);
cx.config
.expect(["rustup", "default", "nightly"])
.await
.is_ok();
}
let cx = cx.with_dist_dir(Scenario::ArchivesV2_2015_01_01);
let config = &cx.config;
config
.expect(["rustup", "update", "nightly-2015-01-01"])
.await
.is_ok();
config
.expect(["rustup", "show", "--verbose"])
.await
.extend_redactions([("[RUSTUP_DIR]", &cx.config.rustupdir.to_string())])
.with_stdout(snapbox::str![[r#"
Default host: [HOST_TUPLE]
rustup home: [RUSTUP_DIR]
installed toolchains
--------------------
nightly-[HOST_TUPLE] (active, default)
1.3.0 (hash-nightly-2)
path: [RUSTUP_DIR]/toolchains/nightly-[HOST_TUPLE]
nightly-2015-01-01-[HOST_TUPLE]
1.2.0 (hash-nightly-1)
path: [RUSTUP_DIR]/toolchains/nightly-2015-01-01-[HOST_TUPLE]
active toolchain
----------------
name: nightly-[HOST_TUPLE]
active because: it's the default toolchain
compiler: 1.3.0 (hash-nightly-2)
path: [RUSTUP_DIR]/toolchains/nightly-[HOST_TUPLE]
installed targets:
[HOST_TUPLE]
"#]])
.with_stderr(snapbox::str![[""]])
.is_ok();
}
#[tokio::test]
async fn show_active_toolchain_with_verbose() {
let cx = CliTestContext::new(Scenario::SimpleV2).await;
cx.config
.expect(["rustup", "default", "nightly"])
.await
.is_ok();
cx.config
.expect(["rustup", "show", "active-toolchain", "--verbose"])
.await
.extend_redactions([("[RUSTUP_DIR]", &cx.config.rustupdir.to_string())])
.with_stdout(snapbox::str![[r#"
nightly-[HOST_TUPLE]
active because: it's the default toolchain
compiler: 1.3.0 (hash-nightly-2)
path: [RUSTUP_DIR]/toolchains/nightly-[HOST_TUPLE]
"#]])
.with_stderr(snapbox::str![[""]])
.is_ok();
}
#[tokio::test]
async fn show_active_toolchain_with_override() {
let cx = CliTestContext::new(Scenario::SimpleV2).await;
cx.config
.expect(["rustup", "default", "stable"])
.await
.is_ok();
cx.config
.expect(["rustup", "default", "nightly"])
.await
.is_ok();
cx.config
.expect(["rustup", "override", "set", "stable"])
.await
.is_ok();
cx.config
.expect(["rustup", "show", "active-toolchain"])
.await
.with_stdout(snapbox::str![[r#"
stable-[HOST_TUPLE] (directory override for '[..]')
"#]])
.is_ok();
}
#[tokio::test]
async fn show_active_toolchain_with_override_verbose() {
let cx = CliTestContext::new(Scenario::SimpleV2).await;
cx.config
.expect(["rustup", "default", "stable"])
.await
.is_ok();
cx.config
.expect(["rustup", "default", "nightly"])
.await
.is_ok();
cx.config
.expect(["rustup", "override", "set", "stable"])
.await
.is_ok();
cx.config
.expect(["rustup", "show", "active-toolchain", "--verbose"])
.await
.extend_redactions([("[RUSTUP_DIR]", &cx.config.rustupdir.to_string())])
.with_stdout(snapbox::str![[r#"
stable-[HOST_TUPLE]
active because: directory override for '[..]'
compiler: 1.1.0 (hash-stable-1.1.0)
path: [RUSTUP_DIR]/toolchains/stable-[HOST_TUPLE]
"#]])
.with_stderr(snapbox::str![[""]])
.is_ok();
}
#[tokio::test]
async fn show_active_toolchain_none() {
let cx = CliTestContext::new(Scenario::None).await;
cx.config
.expect(["rustup", "show", "active-toolchain"])
.await
.with_stdout(snapbox::str![[""]])
.with_stderr(snapbox::str![[r#"
error: no active toolchain
"#]])
.is_err();
}
#[tokio::test]
async fn show_profile() {
let cx = CliTestContext::new(Scenario::SimpleV2).await;
cx.config
.expect(["rustup", "default", "nightly"])
.await
.is_ok();
cx.config
.expect(["rustup", "show", "profile"])
.await
.with_stdout(snapbox::str![[r#"
default
"#]])
.is_ok();
// Check we get the same thing after we add or remove a component.
cx.config
.expect(["rustup", "component", "add", "rust-src"])
.await
.is_ok();
cx.config
.expect(["rustup", "show", "profile"])
.await
.with_stdout(snapbox::str![[r#"
default
"#]])
.is_ok();
cx.config
.expect(["rustup", "component", "remove", "rustc"])
.await
.is_ok();
cx.config
.expect(["rustup", "show", "profile"])
.await
.with_stdout(snapbox::str![[r#"
default
"#]])
.is_ok();
}
// #846
#[tokio::test]
async fn set_default_host() {
let cx = CliTestContext::new(Scenario::None).await;
cx.config
.expect(["rustup", "set", "default-host", &this_host_tuple()])
.await
.is_ok();
cx.config
.expect(["rustup", "show"])
.await
.with_stdout(snapbox::str![[r#"
...
Default host: [HOST_TUPLE]
...
"#]])
.is_ok();
}
// #846
#[tokio::test]
async fn set_default_host_invalid_triple() {
let cx = CliTestContext::new(Scenario::None).await;
cx.config
.expect(["rustup", "set", "default-host", "foo"])
.await
.with_stderr(snapbox::str![[r#"
error: Provided host 'foo' couldn't be converted to partial triple
"#]])
.is_err();
}
// #745
#[tokio::test]
async fn set_default_host_invalid_triple_valid_partial() {
let cx = CliTestContext::new(Scenario::None).await;
cx.config
.expect(["rustup", "set", "default-host", "x86_64-msvc"])
.await
.with_stderr(snapbox::str![[r#"
error: Provided host 'x86_64-msvc' did not specify an operating system
"#]])
.is_err();
}
// #422
#[tokio::test]
async fn update_doesnt_update_non_tracking_channels() {
let mut cx = CliTestContext::new(Scenario::None).await;
{
let cx = cx.with_dist_dir(Scenario::SimpleV2);
cx.config
.expect(["rustup", "default", "nightly"])
.await
.is_ok();
}
let cx = cx.with_dist_dir(Scenario::ArchivesV2_2015_01_01);
cx.config
.expect(["rustup", "update", "nightly-2015-01-01"])
.await
.is_ok();
cx.config
.expect(["rustup", "update"])
.await
.is_ok()
.without_stderr(&format!(
"syncing channel updates for 'nightly-2015-01-01-{}'",
this_host_tuple(),
));
}
#[tokio::test]
async fn toolchain_install_is_like_update() {
let cx = CliTestContext::new(Scenario::SimpleV2).await;
cx.config
.expect(["rustup", "toolchain", "install", "nightly"])
.await
.is_ok();
cx.config
.expect(["rustup", "run", "nightly", "rustc", "--version"])
.await
.with_stdout(snapbox::str![[r#"
1.3.0 (hash-nightly-2)
"#]])
.is_ok();
}
#[tokio::test]
async fn toolchain_install_is_like_update_quiet() {
let cx = CliTestContext::new(Scenario::SimpleV2).await;
cx.config
.expect(["rustup", "--quiet", "toolchain", "install", "nightly"])
.await
.is_ok();
cx.config
.expect(["rustup", "run", "nightly", "rustc", "--version"])
.await
.with_stdout(snapbox::str![[r#"
1.3.0 (hash-nightly-2)
"#]])
.is_ok();
}
#[tokio::test]
async fn toolchain_install_without_args_installs_active() {
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,
r#"
[toolchain]
profile = "minimal"
channel = "nightly"
"#,
)
.unwrap();
cx.config
.expect(["rustup", "toolchain", "install"])
.await
.extend_redactions([("[TOOLCHAIN_FILE]", &toolchain_file)])
.with_stderr(snapbox::str![[r#"
info: syncing channel updates for nightly-[HOST_TUPLE]
info: latest update on 2015-01-02 for version 1.3.0 (hash-nightly-2)
info: downloading component rustc
info: the active toolchain `nightly-[HOST_TUPLE]` has been installed
info: it's active because: overridden by '[TOOLCHAIN_FILE]'
"#]])
.is_ok();
cx.config
.expect(["rustup", "+nightly", "component", "list", "--installed"])
.await
.is_ok()
.with_stdout(snapbox::str![[r#"
rustc-[HOST_TUPLE]
"#]]);
cx.config
.expect(["rustup", "toolchain", "install"])
.await
.extend_redactions([("[TOOLCHAIN_FILE]", &toolchain_file)])
.with_stderr(snapbox::str![[r#"
info: using existing install for nightly-[HOST_TUPLE]
info: the active toolchain `nightly-[HOST_TUPLE]` has been installed
info: it's active because: overridden by '[TOOLCHAIN_FILE]'
"#]])
.is_ok();
}
#[tokio::test]
async fn toolchain_install_no_change_with_no_update() {
let mut cx = CliTestContext::new(Scenario::None).await;
{
let cx = cx.with_dist_dir(Scenario::ArchivesV2_2015_01_01);
cx.config
.expect(["rustup", "toolchain", "add", "stable"])
.await
.is_ok();
}
let cx = cx.with_dist_dir(Scenario::SimpleV2);
cx.config
.expect(["rustup", "install", "--no-update", "stable"])
.await
.with_stdout(snapbox::str![[r#"
stable-[HOST_TUPLE] unchanged - 1.0.0 (hash-stable-1.0.0)
"#]])
.with_stderr(snapbox::str![[""]])
.is_ok();
}
#[tokio::test]
async fn toolchain_update_is_like_update() {
let cx = CliTestContext::new(Scenario::SimpleV2).await;
cx.config
.expect(["rustup", "toolchain", "update", "nightly"])
.await
.is_ok();
cx.config
.expect(["rustup", "run", "nightly", "rustc", "--version"])
.await
.with_stdout(snapbox::str![[r#"
1.3.0 (hash-nightly-2)
"#]])
.is_ok();
}
#[tokio::test]
async fn toolchain_uninstall_is_like_uninstall() {
let mut cx = CliTestContext::new(Scenario::None).await;
{
let cx = cx.with_dist_dir(Scenario::SimpleV2);
cx.config
.expect(["rustup", "toolchain", "install", "nightly"])
.await
.is_ok();
}
cx.config
.expect(["rustup", "default", "none"])
.await
.is_ok();
cx.config
.expect(["rustup", "uninstall", "nightly"])
.await
.is_ok();
cx.config
.expect(["rustup", "show"])
.await
.with_stdout(snapbox::str![[r#"
...
installed toolchains
--------------------
active toolchain
----------------
no active toolchain
"#]])
.is_ok();
}
#[tokio::test]
async fn uninstall_multiple_toolchains_sequentially() {
let mut cx = CliTestContext::new(Scenario::None).await;
{
let cx = cx.with_dist_dir(Scenario::SimpleV2);
cx.config
.expect(["rustup", "toolchain", "install", "stable"])
.await
.is_ok();
cx.config
.expect(["rustup", "toolchain", "install", "beta"])
.await
.is_ok();
cx.config
.expect(["rustup", "toolchain", "install", "nightly"])
.await
.is_ok();
}
cx.config
.expect(["rustup", "default", "none"])
.await
.is_ok();
cx.config
.expect([
"rustup",
"toolchain",
"uninstall",
"stable",
"beta",
"nightly",
])
.await
.is_ok();
cx.config
.expect(["rustup", "toolchain", "list"])
.await
.with_stdout(snapbox::str![[r#"
no installed toolchains
"#]])
.is_ok();
}
#[tokio::test]
async fn proxy_toolchain_shorthand() {
let cx = CliTestContext::new(Scenario::SimpleV2).await;
cx.config
.expect(["rustup", "default", "stable"])
.await
.is_ok();
cx.config
.expect(["rustup", "toolchain", "update", "nightly"])
.await
.is_ok();
cx.config
.expect(["rustc", "--version"])
.await
.with_stdout(snapbox::str![[r#"
1.1.0 (hash-stable-1.1.0)
"#]])
.is_ok();
cx.config
.expect(["rustc", "+stable", "--version"])
.await
.with_stdout(snapbox::str![[r#"
1.1.0 (hash-stable-1.1.0)
"#]])
.is_ok();
cx.config
.expect(["rustc", "+nightly", "--version"])
.await
.with_stdout(snapbox::str![[r#"
1.3.0 (hash-nightly-2)
"#]])
.is_ok();
}
#[tokio::test]
async fn add_component() {
let cx = CliTestContext::new(Scenario::SimpleV2).await;
cx.config
.expect(["rustup", "default", "stable"])
.await
.is_ok();
cx.config
.expect(["rustup", "component", "add", "rust-src"])
.await
.is_ok();
let path = format!(
"toolchains/stable-{}/lib/rustlib/src/rust-src/foo.rs",
this_host_tuple()
);
assert!(cx.config.rustupdir.has(path));
}
#[tokio::test]
async fn add_component_by_target_triple() {
let cx = CliTestContext::new(Scenario::SimpleV2).await;
cx.config
.expect(["rustup", "default", "stable"])
.await
.is_ok();
cx.config
.expect([
"rustup",
"component",
"add",
&format!("rust-std-{CROSS_ARCH1}"),
])
.await
.is_ok();
let path = format!(
"toolchains/stable-{}/lib/rustlib/{CROSS_ARCH1}/lib/libstd.rlib",
this_host_tuple()
);
assert!(cx.config.rustupdir.has(path));
}
#[tokio::test]
async fn add_component_by_target_triple_renamed_from() {
let cx = CliTestContext::new(Scenario::SimpleV2).await;
cx.config
.expect(["rustup", "default", "nightly"])
.await
.is_ok();
cx.config
.expect([
"rustup",
"component",
"add",
&format!("rls-{}", this_host_tuple()),
])
.await
.is_ok();
cx.config
.expect(["rustup", "component", "list", "--installed"])
.await
.with_stdout(snapbox::str![[r#"
...
rls-[HOST_TUPLE]
...
"#]])
.is_ok();
}
#[tokio::test]
async fn add_component_by_target_triple_renamed_to() {
let cx = CliTestContext::new(Scenario::SimpleV2).await;
cx.config
.expect(["rustup", "default", "nightly"])
.await
.is_ok();
cx.config
.expect([
"rustup",
"component",
"add",
&format!("rls-preview-{}", this_host_tuple()),
])
.await
.is_ok();
cx.config
.expect(["rustup", "component", "list", "--installed"])
.await
.with_stdout(snapbox::str![[r#"
...
rls-[HOST_TUPLE]
...
"#]])
.is_ok();
}
#[tokio::test]
async fn fail_invalid_component_name() {
let cx = CliTestContext::new(Scenario::SimpleV2).await;
cx.config
.expect(&["rustup", "default", "stable"])
.await
.is_ok();
cx.config
.expect(&[
"rustup",
"component",
"add",
&format!("dummy-{CROSS_ARCH1}"),
])
.await
.with_stderr(snapbox::str![[r#"
error: toolchain 'stable-[HOST_TUPLE]' does not contain component 'dummy-[CROSS_ARCH_I]' for target '[HOST_TUPLE]'
"#]])
.is_err();
}
#[tokio::test]
async fn fail_invalid_component_target() {
let cx = CliTestContext::new(Scenario::SimpleV2).await;
cx.config
.expect(&["rustup", "default", "stable"])
.await
.is_ok();
cx.config.expect(&[
"rustup",
"component",
"add",
"rust-std-invalid-target",
])
.await
.with_stderr(snapbox::str![[r#"
...
error: toolchain 'stable-[HOST_TUPLE]' does not contain component 'rust-std-invalid-target' for target '[HOST_TUPLE]'
...
"#]])
.is_err();
}
#[tokio::test]
async fn remove_component() {
let cx = CliTestContext::new(Scenario::SimpleV2).await;
cx.config
.expect(&["rustup", "default", "stable"])
.await
.is_ok();
cx.config
.expect(&["rustup", "component", "add", "rust-src"])
.await
.is_ok();
let path = PathBuf::from(format!(
"toolchains/stable-{}/lib/rustlib/src/rust-src/foo.rs",
this_host_tuple(),
));
assert!(cx.config.rustupdir.has(&path));
cx.config
.expect(&["rustup", "component", "remove", "rust-src"])
.await
.is_ok();
assert!(!cx.config.rustupdir.has(path.parent().unwrap()));
}
#[tokio::test]
async fn remove_component_by_target_triple() {
let component_with_triple = format!("rust-std-{CROSS_ARCH1}");
let cx = CliTestContext::new(Scenario::SimpleV2).await;
cx.config
.expect(&["rustup", "default", "stable"])
.await
.is_ok();
cx.config
.expect(&["rustup", "component", "add", &component_with_triple])
.await
.is_ok();
let path = PathBuf::from(format!(
"toolchains/stable-{}/lib/rustlib/{CROSS_ARCH1}/lib/libstd.rlib",
this_host_tuple()
));
assert!(cx.config.rustupdir.has(&path));
cx.config
.expect(&["rustup", "component", "remove", &component_with_triple])
.await
.is_ok();
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_tuple()),
format!("lib/rustlib/{CROSS_ARCH1}/lib/libstd.rlib"),
format!("lib/rustlib/{CROSS_ARCH2}/lib/libstd.rlib"),
];
let component_with_triple1 = format!("rust-std-{CROSS_ARCH1}");
let component_with_triple2 = format!("rust-std-{CROSS_ARCH2}");
let cx = CliTestContext::new(Scenario::SimpleV2).await;
cx.config
.expect(&["rustup", "default", "nightly"])
.await
.is_ok();
cx.config
.expect(&[
"rustup",
"component",
"add",
"rust-src",
"rust-analysis",
&component_with_triple1,
&component_with_triple2,
])
.await
.is_ok();
for file in &files {
let path = format!("toolchains/nightly-{}/{}", this_host_tuple(), file);
assert!(cx.config.rustupdir.has(&path));
}
cx.config
.expect(&[
"rustup",
"component",
"remove",
"rust-src",
"rust-analysis",
&component_with_triple1,
&component_with_triple2,
])
.await
.is_ok();
for file in &files {
let path = PathBuf::from(format!("toolchains/nightly-{}/{}", this_host_tuple(), file));
assert!(!cx.config.rustupdir.has(path.parent().unwrap()));
}
}
#[tokio::test]
async fn remove_multiple_components_best_effort() {
let files = [
"lib/rustlib/src/rust-src/foo.rs",
for_host!("lib/rustlib/{}/analysis/libfoo.json"),
];
for (one, two, three) in [
("bad-component", "rust-src", "rust-analysis"),
("rust-src", "bad-component", "rust-analysis"),
("rust-src", "rust-analysis", "bad-component"),
] {
let cx = CliTestContext::new(Scenario::SimpleV2).await;
cx.config
.expect(&["rustup", "default", "nightly"])
.await
.is_ok();
cx.config
.expect(&["rustup", "component", "add", "rust-src", "rust-analysis"])
.await
.is_ok();
for file in &files {
let path = for_host!("toolchains/nightly-{0}/{file}");
assert!(cx.config.rustupdir.has(path));
}
cx.config
.expect(&["rustup", "component", "remove", one, two, three])
.await
.is_err();
for file in &files {
let path = PathBuf::from(for_host!("toolchains/nightly-{0}/{file}"));
assert!(!cx.config.rustupdir.has(path.parent().unwrap()));
}
}
}
#[tokio::test]
async fn remove_multiple_components_reports_all_invalid_names() {
let files = [
"lib/rustlib/src/rust-src/foo.rs",
for_host!("lib/rustlib/{}/analysis/libfoo.json"),
];
let cx = CliTestContext::new(Scenario::SimpleV2).await;
cx.config
.expect(["rustup", "default", "nightly"])
.await
.is_ok();
cx.config
.expect(["rustup", "component", "add", "rust-src", "rust-analysis"])
.await
.is_ok();
for file in &files {
let path = for_host!("toolchains/nightly-{0}/{file}");
assert!(cx.config.rustupdir.has(path));
}
cx.config
.expect([
"rustup",
"component",
"remove",
"bad-component-1",
"rust-src",
"bad-component-2",
"rust-analysis",
])
.await
.with_stderr(snapbox::str![[r#"
info: removing component rust-src
info: removing component rust-analysis
error: toolchain 'nightly-[HOST_TUPLE]' does not contain these components:
- 'bad-component-1'
- 'bad-component-2'
"#]])
.is_err();
for file in &files {
let path = PathBuf::from(for_host!("toolchains/nightly-{0}/{file}",));
assert!(!cx.config.rustupdir.has(path.parent().unwrap()));
}
}
#[tokio::test]
async fn file_override() {
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(&["rustc", "--version"])
.await
.with_stdout(snapbox::str![[r#"
1.1.0 (hash-stable-1.1.0)
"#]])
.is_ok();
let cwd = cx.config.current_dir();
let toolchain_file = cwd.join("rust-toolchain");
raw::write_file(&toolchain_file, "nightly").unwrap();
cx.config
.expect(&["rustc", "--version"])
.await
.with_stdout(snapbox::str![[r#"
1.3.0 (hash-nightly-2)
"#]])
.is_ok();
}
#[tokio::test]
async fn env_override_path() {
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();
let toolchain_path = cx
.config
.rustupdir
.join("toolchains")
.join(format!("nightly-{}", this_host_tuple()));
cx.config
.expect_with_env(
["rustc", "--version"],
[("RUSTUP_TOOLCHAIN", toolchain_path.to_str().unwrap())],
)
.await
.with_stdout(snapbox::str![[r#"
1.3.0 (hash-nightly-2)
"#]])
.is_ok();
}
#[tokio::test]
async fn plus_override_relpath_is_not_supported() {
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();
let toolchain_path = Path::new("..")
.join(cx.config.rustupdir.rustupdir.file_name().unwrap())
.join("toolchains")
.join(format!("nightly-{}", this_host_tuple()));
cx.config
.expect([
"rustc",
format!("+{}", toolchain_path.to_str().unwrap()).as_str(),
"--version",
])
.await
.with_stderr(snapbox::str![[r#"
error: relative path toolchain '[..]/toolchains/nightly-[HOST_TUPLE]'
"#]])
.is_err();
}
#[tokio::test]
async fn run_with_relpath_is_not_supported() {
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();
let toolchain_path = Path::new("..")
.join(cx.config.rustupdir.rustupdir.file_name().unwrap())
.join("toolchains")
.join(format!("nightly-{}", this_host_tuple()));
cx.config
.expect([
"rustup",
"run",
toolchain_path.to_str().unwrap(),
"rustc",
"--version",
])
.await
.with_stderr(snapbox::str![[r#"
...
error:[..] relative path toolchain '[..]/toolchains/nightly-[HOST_TUPLE]'
...
"#]])
.is_err();
}
#[tokio::test]
async fn run_with_plus_prefix_fails_with_helpful_message() {
let cx = CliTestContext::new(Scenario::SimpleV2).await;
cx.config
.expect(["rustup", "default", "stable"])
.await
.is_ok();
cx.config
.expect(["rustup", "run", "+stable", "cargo", "check"])
.await
.with_stderr(snapbox::str![[r#"
...
error:[..] invalid toolchain name '+stable'; valid toolchain names do not start with '+'
...
"#]])
.is_err();
}
#[tokio::test]
async fn plus_override_abspath_is_supported() {
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();
let toolchain_path = cx
.config
.rustupdir
.join("toolchains")
.join(format!("nightly-{}", this_host_tuple()))
.canonicalize()
.unwrap();
cx.config
.expect([
"rustc",
format!("+{}", toolchain_path.to_str().unwrap()).as_str(),
"--version",
])
.await
.is_ok();
}
#[tokio::test]
async fn run_with_abspath_is_supported() {
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();
let toolchain_path = cx
.config
.rustupdir
.join("toolchains")
.join(format!("nightly-{}", this_host_tuple()))
.canonicalize()
.unwrap();
cx.config
.expect([
"rustup",
"run",
toolchain_path.to_str().unwrap(),
"rustc",
"--version",
])
.await
.is_ok();
}
#[tokio::test]
async fn file_override_path() {
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();
let toolchain_path = cx
.config
.rustupdir
.join("toolchains")
.join(format!("nightly-{}", this_host_tuple()));
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(["rustc", "--version"])
.await
.with_stdout(snapbox::str![[r#"
1.3.0 (hash-nightly-2)
"#]])
.is_ok();
// Check that the toolchain has the right name
cx.config
.expect(["rustup", "show", "active-toolchain"])
.await
.with_stdout(snapbox::str![[r#"
[..]/toolchains/nightly-[HOST_TUPLE] (overridden by '[..]/rust-toolchain.toml')
"#]])
.is_ok();
}
#[tokio::test]
async fn proxy_override_path() {
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();
let toolchain_path = cx
.config
.rustupdir
.join("toolchains")
.join(format!("nightly-{}", this_host_tuple()));
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(["cargo", "--call-rustc"])
.await
.with_stdout(snapbox::str![[r#"
1.3.0 (hash-nightly-2)
"#]])
.is_ok();
}
#[tokio::test]
async fn file_override_path_relative_not_supported() {
let mut 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();
let toolchain_path = cx
.config
.rustupdir
.join("toolchains")
.join(format!("nightly-{}", this_host_tuple()));
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(["rustc", "--version"])
.await
.with_stderr(snapbox::str![[r#"
error: relative path toolchain '[..]/toolchains/nightly-[HOST_TUPLE]'
"#]])
.is_err();
}
#[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(["rustc", "--version"])
.await
.with_stderr(snapbox::str![[r#"
error: toolchain options are ignored for path toolchain (ephemeral)
"#]])
.is_err();
raw::write_file(
&toolchain_file,
"[toolchain]\npath=\"ephemeral\"\ncomponents=[\"dummy\"]",
)
.unwrap();
cx.config
.expect(["rustc", "--version"])
.await
.with_stderr(snapbox::str![[r#"
error: toolchain options are ignored for path toolchain (ephemeral)
"#]])
.is_err();
raw::write_file(
&toolchain_file,
"[toolchain]\npath=\"ephemeral\"\nprofile=\"minimal\"",
)
.unwrap();
cx.config
.expect(["rustc", "--version"])
.await
.with_stderr(snapbox::str![[r#"
error: toolchain options are ignored for path toolchain (ephemeral)
"#]])
.is_err();
}
#[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(["rustc", "--version"])
.await
.with_stderr(snapbox::str![[r#"
[..]cannot specify both channel (nightly) and path (ephemeral) simultaneously[..]
"#]])
.is_err();
}
#[tokio::test]
async fn file_override_subdir() {
let mut 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(["rustc", "--version"])
.await
.with_stdout(snapbox::str![[r#"
1.1.0 (hash-stable-1.1.0)
"#]])
.is_ok();
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(["rustc", "--version"])
.await
.with_stdout(snapbox::str![[r#"
1.3.0 (hash-nightly-2)
"#]])
.is_ok();
}
#[tokio::test]
async fn file_override_with_archive() {
let mut cx = CliTestContext::new(Scenario::None).await;
{
let cx = cx.with_dist_dir(Scenario::SimpleV2);
cx.config
.expect(["rustup", "default", "stable"])
.await
.is_ok();
}
let cx = cx.with_dist_dir(Scenario::ArchivesV2_2015_01_01);
cx.config
.expect(["rustup", "toolchain", "install", "nightly-2015-01-01"])
.await
.is_ok();
cx.config
.expect(["rustc", "--version"])
.await
.with_stdout(snapbox::str![[r#"
1.1.0 (hash-stable-1.1.0)
"#]])
.is_ok();
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(["rustc", "--version"])
.await
.with_stdout(snapbox::str![[r#"
1.2.0 (hash-nightly-1)
"#]])
.is_ok();
}
#[tokio::test]
async fn file_override_toml_format_select_installed_toolchain() {
let mut cx = CliTestContext::new(Scenario::None).await;
{
let cx = cx.with_dist_dir(Scenario::SimpleV2);
cx.config
.expect(["rustup", "default", "stable"])
.await
.is_ok();
}
let cx = cx.with_dist_dir(Scenario::ArchivesV2_2015_01_01);
cx.config
.expect(["rustup", "toolchain", "install", "nightly-2015-01-01"])
.await
.is_ok();
cx.config
.expect(["rustc", "--version"])
.await
.with_stdout(snapbox::str![[r#"
1.1.0 (hash-stable-1.1.0)
"#]])
.is_ok();
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();
cx.config
.expect(["rustc", "--version"])
.await
.with_stdout(snapbox::str![[r#"
1.2.0 (hash-nightly-1)
"#]])
.is_ok();
}
#[tokio::test]
async fn file_override_toml_format_install_both_toolchain_and_components() {
let mut cx = CliTestContext::new(Scenario::None).await;
{
let cx = cx.with_dist_dir(Scenario::SimpleV2);
cx.config
.expect(["rustup", "default", "stable"])
.await
.is_ok();
}
let cx = cx.with_dist_dir(Scenario::ArchivesV2_2015_01_01);
cx.config
.expect(["rustc", "--version"])
.await
.with_stdout(snapbox::str![[r#"
1.1.0 (hash-stable-1.1.0)
"#]])
.is_ok();
cx.config
.expect(["rustup", "component", "list"])
.await
.is_ok()
.without_stdout("rust-src (installed)");
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();
cx.config
.expect(["rustup", "toolchain", "install"])
.await
.is_ok();
cx.config
.expect(["rustc", "--version"])
.await
.with_stdout(snapbox::str![[r#"
1.2.0 (hash-nightly-1)
"#]])
.is_ok();
cx.config
.expect(["rustup", "component", "list"])
.await
.with_stdout(snapbox::str![[r#"
...
rust-src (installed)
...
"#]])
.is_ok();
}
#[tokio::test]
async fn file_override_toml_format_add_missing_components() {
let cx = CliTestContext::new(Scenario::SimpleV2).await;
cx.config
.expect(["rustup", "default", "stable"])
.await
.is_ok();
cx.config
.expect(["rustup", "component", "list"])
.await
.is_ok()
.without_stdout("rust-src (installed)");
let cwd = cx.config.current_dir();
let toolchain_file = cwd.join("rust-toolchain");
raw::write_file(
&toolchain_file,
r#"
[toolchain]
components = [ "rust-src" ]
"#,
)
.unwrap();
cx.config
.expect(["rustup", "toolchain", "install"])
.await
.is_ok();
cx.config
.expect(["rustup", "component", "list"])
.await
.with_stdout(snapbox::str![[r#"
...
rust-src (installed)
...
"#]])
.is_ok();
}
#[tokio::test]
async fn file_override_toml_format_add_missing_targets() {
let cx = CliTestContext::new(Scenario::SimpleV2).await;
cx.config
.expect(["rustup", "default", "stable"])
.await
.is_ok();
cx.config
.expect(["rustup", "component", "list"])
.await
.is_ok()
.without_stdout(&format!("{CROSS_ARCH2} (installed)"));
let cwd = cx.config.current_dir();
let toolchain_file = cwd.join("rust-toolchain");
raw::write_file(
&toolchain_file,
&format!(
r#"
[toolchain]
targets = [ "{CROSS_ARCH2}" ]
"#,
),
)
.unwrap();
cx.config
.expect(["rustup", "toolchain", "install"])
.await
.is_ok();
cx.config
.expect(["rustup", "component", "list"])
.await
.with_stdout(snapbox::str![[r#"
...
rust-std-[CROSS_ARCH_II] (installed)
...
"#]])
.is_ok();
}
#[tokio::test]
async fn file_override_toml_format_skip_invalid_component() {
let cx = CliTestContext::new(Scenario::SimpleV2).await;
cx.config
.expect(["rustup", "default", "stable"])
.await
.is_ok();
let cwd = cx.config.current_dir();
let toolchain_file = cwd.join("rust-toolchain");
raw::write_file(
&toolchain_file,
r#"
[toolchain]
components = [ "rust-bongo" ]
"#,
)
.unwrap();
cx.config
.expect(&["rustup", "toolchain", "install"])
.await
.with_stderr(snapbox::str![[r#"
...
warn: skipping unavailable component rust-bongo
...
"#]])
.is_ok();
}
#[tokio::test]
async fn file_override_toml_format_specify_profile() {
let cx = CliTestContext::new(Scenario::SimpleV2).await;
cx.config
.expect(&["rustup", "set", "profile", "default"])
.await
.is_ok();
cx.config
.expect(&["rustup", "default", "stable"])
.await
.is_ok();
cx.config
.expect(&["rustup", "component", "list"])
.await
// The `rust-docs-[HOST_TUPLE]` component is installed.
.with_stdout(snapbox::str![[r#"
...
rust-docs-[HOST_TUPLE] (installed)
...
"#]])
.is_ok();
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();
cx.config
.expect(&["rustup", "toolchain", "install"])
.await
.is_ok();
cx.config
.expect(&["rustup", "component", "list"])
.await
// The `rust-docs-[HOST_TUPLE]` component is not installed.
.with_stdout(snapbox::str![[r#"
...
rust-docs-[HOST_TUPLE]
...
"#]])
.is_ok();
}
#[tokio::test]
async fn default_profile_is_respected_with_rust_toolchain_file() {
let cx = CliTestContext::new(Scenario::SimpleV2).await;
cx.config
.expect(&["rustup", "set", "profile", "minimal"])
.await
.is_ok();
cx.config
.expect(&["rustup", "default", "stable"])
.await
.is_ok();
let cwd = cx.config.current_dir();
let toolchain_file = cwd.join("rust-toolchain");
raw::write_file(
&toolchain_file,
r#"
[toolchain]
channel = "nightly"
"#,
)
.unwrap();
cx.config
.expect(&["rustup", "toolchain", "install"])
.await
.is_ok();
cx.config
.expect(&["rustup", "component", "list"])
.await
.with_stdout(snapbox::str![[r#"
...
rust-docs-[HOST_TUPLE]
...
"#]])
.is_ok();
}
#[tokio::test]
async fn close_file_override_beats_far_directory_override() {
let mut cx = CliTestContext::new(Scenario::SimpleV2).await;
cx.config
.expect(&["rustup", "default", "stable"])
.await
.is_ok();
cx.config
.expect(&["rustup", "toolchain", "install", "beta"])
.await
.is_ok();
cx.config
.expect(&["rustup", "toolchain", "install", "nightly"])
.await
.is_ok();
cx.config
.expect(["rustup", "override", "set", "beta"])
.await
.is_ok();
cx.config
.expect(["rustc", "--version"])
.await
.with_stdout(snapbox::str![[r#"
1.2.0 (hash-beta-1.2.0)
"#]])
.is_ok();
let cwd = cx.config.current_dir();
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 cx = cx.change_dir(&subdir);
cx.config
.expect(["rustc", "--version"])
.await
.with_stdout(snapbox::str![[r#"
1.3.0 (hash-nightly-2)
"#]])
.is_ok();
}
// Check that toolchain overrides have the correct priority.
#[tokio::test]
async fn override_order() {
let cx = CliTestContext::new(Scenario::ArchivesV2).await;
let host = this_host_tuple();
// 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(["rustup", "install", default_tc])
.await
.is_ok();
cx.config
.expect(["rustup", "install", env_tc])
.await
.is_ok();
cx.config
.expect(["rustup", "install", dir_tc])
.await
.is_ok();
cx.config
.expect(["rustup", "install", file_tc])
.await
.is_ok();
cx.config
.expect(["rustup", "install", command_tc])
.await
.is_ok();
// No default
cx.config
.expect(["rustup", "default", "none"])
.await
.is_ok();
cx.config
.expect(["rustup", "show", "active-toolchain"])
.await
.with_stdout(snapbox::str![[""]])
.with_stderr(snapbox::str![[r#"
error: no active toolchain
"#]])
.is_err();
// Default
cx.config
.expect(["rustup", "default", default_tc])
.await
.is_ok();
cx.config
.expect(["rustup", "show", "active-toolchain"])
.await
.extend_redactions([("[DEFAULT_TC]", default_tc)])
.with_stdout(snapbox::str![[r#"
[DEFAULT_TC] (default)
"#]])
.is_ok();
// 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(["rustup", "show", "active-toolchain"])
.await
.extend_redactions([("[FILE_TC]", file_tc)])
.with_stdout(snapbox::str![[r#"
[FILE_TC] (overridden by '[..]/rust-toolchain.toml')
"#]])
.is_ok();
// dir override > file > default
cx.config
.expect(["rustup", "override", "set", dir_tc])
.await
.is_ok();
cx.config
.expect(["rustup", "show", "active-toolchain"])
.await
.extend_redactions([("[DIR_TC]", dir_tc)])
.with_stdout(snapbox::str![[r#"
[DIR_TC] (directory override for '[..]')
"#]])
.is_ok();
// env > dir override > file > default
cx.config
.expect_with_env(
["rustup", "show", "active-toolchain"],
[("RUSTUP_TOOLCHAIN", &**env_tc)],
)
.await
.extend_redactions([("[ENV_TC]", env_tc)])
.with_stdout(snapbox::str![[r#"
[ENV_TC] (overridden by environment variable RUSTUP_TOOLCHAIN)
"#]])
.is_ok();
// +toolchain > env > dir override > file > default
cx.config
.expect_with_env(
[
"rustup",
&format!("+{command_tc}"),
"show",
"active-toolchain",
],
[("RUSTUP_TOOLCHAIN", &**env_tc)],
)
.await
.extend_redactions([("[COMMAND_TC]", command_tc)])
.with_stdout(snapbox::str![[r#"
[COMMAND_TC] (overridden by +toolchain on the command line)
"#]])
.is_ok();
}
#[tokio::test]
async fn rustup_toolchain_source_cli() {
let cx = CliTestContext::new(Scenario::SimpleV2).await;
cx.config
.expect(&["rustup", "install", "nightly"])
.await
.is_ok();
cx.config
.expect(["cargo", "+nightly", "--echo-rustup-toolchain-source"])
.await
.with_stderr(snapbox::str![[r#"
...
cli
"#]]);
}
#[tokio::test]
async fn rustup_toolchain_source_env() {
let cx = CliTestContext::new(Scenario::SimpleV2).await;
cx.config
.expect_with_env(
["cargo", "--echo-rustup-toolchain-source"],
[("RUSTUP_TOOLCHAIN", "nightly")],
)
.await
.with_stderr(snapbox::str![[r#"
...
env
"#]]);
}
#[tokio::test]
async fn rustup_toolchain_source_path_override() {
let cx = CliTestContext::new(Scenario::SimpleV2).await;
cx.config
.expect(["rustup", "override", "set", "nightly"])
.await
.is_ok();
cx.config
.expect(["cargo", "--echo-rustup-toolchain-source"])
.await
.with_stderr(snapbox::str![[r#"
...
path-override
"#]]);
}
#[tokio::test]
async fn rustup_toolchain_source_toolchain_file() {
let cx = CliTestContext::new(Scenario::SimpleV2).await;
let toolchain_file = cx.config.current_dir().join("rust-toolchain.toml");
raw::write_file(&toolchain_file, "[toolchain]\nchannel='nightly'").unwrap();
cx.config
.expect(["cargo", "--echo-rustup-toolchain-source"])
.await
.with_stderr(snapbox::str![[r#"
...
toolchain-file
"#]]);
}
#[tokio::test]
async fn rustup_toolchain_source_default() {
let cx = CliTestContext::new(Scenario::SimpleV2).await;
cx.config
.expect(&["rustup", "default", "stable"])
.await
.is_ok();
cx.config
.expect(["cargo", "--echo-rustup-toolchain-source"])
.await
.with_stderr(snapbox::str![[r#"
...
default
"#]]);
}
#[tokio::test]
async fn directory_override_doesnt_need_to_exist_unless_it_is_selected() {
let cx = CliTestContext::new(Scenario::SimpleV2).await;
cx.config
.expect(["rustup", "default", "stable"])
.await
.is_ok();
cx.config
.expect(["rustup", "toolchain", "install", "beta"])
.await
.is_ok();
// not installing nightly
cx.config
.expect(["rustup", "override", "set", "beta"])
.await
.is_ok();
cx.config
.expect(["rustc", "--version"])
.await
.with_stdout(snapbox::str![[r#"
1.2.0 (hash-beta-1.2.0)
"#]])
.is_ok();
let cwd = cx.config.current_dir();
let toolchain_file = cwd.join("rust-toolchain");
raw::write_file(&toolchain_file, "nightly").unwrap();
cx.config
.expect(["rustc", "--version"])
.await
.with_stdout(snapbox::str![[r#"
1.2.0 (hash-beta-1.2.0)
"#]])
.is_ok();
}
#[tokio::test]
async fn env_override_beats_file_override() {
let cx = CliTestContext::new(Scenario::SimpleV2).await;
cx.config
.expect(["rustup", "default", "stable"])
.await
.is_ok();
cx.config
.expect(["rustup", "toolchain", "install", "beta"])
.await
.is_ok();
cx.config
.expect(["rustup", "toolchain", "install", "nightly"])
.await
.is_ok();
let cwd = cx.config.current_dir();
let toolchain_file = cwd.join("rust-toolchain");
raw::write_file(&toolchain_file, "nightly").unwrap();
cx.config
.expect_with_env(["rustc", "--version"], [("RUSTUP_TOOLCHAIN", "beta")])
.await
.with_stdout(snapbox::str![[r#"
1.2.0 (hash-beta-1.2.0)
"#]])
.is_ok();
}
#[tokio::test]
async fn plus_override_beats_file_override() {
let cx = CliTestContext::new(Scenario::SimpleV2).await;
cx.config
.expect(["rustup", "default", "stable"])
.await
.is_ok();
cx.config
.expect(["rustup", "toolchain", "install", "beta"])
.await
.is_ok();
cx.config
.expect(["rustup", "toolchain", "install", "nightly"])
.await
.is_ok();
let cwd = cx.config.current_dir();
let toolchain_file = cwd.join("rust-toolchain");
raw::write_file(&toolchain_file, "nightly").unwrap();
cx.config
.expect(["rustc", "+beta", "--version"])
.await
.with_stdout(snapbox::str![[r#"
1.2.0 (hash-beta-1.2.0)
"#]])
.is_ok();
}
#[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(["rustup", "show", "active-toolchain"])
.await
.with_stderr(snapbox::str![[r#"
...
error: custom toolchain 'gumbo' specified in override file '[..]/rust-toolchain' is not installed
...
"#]])
.is_err();
cx.config
.expect(["rustc", "--version"])
.await
.with_stderr(snapbox::str![[r#"
...
error: custom toolchain 'gumbo' specified in override file '[..]/rust-toolchain' is not installed
...
"#]])
.is_err();
}
#[tokio::test]
async fn file_override_not_installed_custom_toml() {
let cx = CliTestContext::new(Scenario::None).await;
let cwd = cx.config.current_dir();
let toolchain_file = cwd.join("rust-toolchain.toml");
raw::write_file(&toolchain_file, r#"toolchain.channel = "i-am-the-walrus""#).unwrap();
cx.config
.expect(["rustup", "show", "active-toolchain"])
.await
.with_stderr(snapbox::str![[r#"
...
error: custom toolchain 'i-am-the-walrus' specified in override file '[..]/rust-toolchain.toml' is not installed
...
"#]])
.is_err();
cx.config
.expect(["rustc", "--version"])
.await
.with_stderr(snapbox::str![[r#"
...
error: custom toolchain 'i-am-the-walrus' specified in override file '[..]/rust-toolchain.toml' is not installed
...
"#]])
.is_err();
}
#[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(["rustc", "--version"])
.await
.with_stderr(snapbox::str![[r#"
...
error: invalid toolchain name detected in override file '[..]/rust-toolchain'
...
"#]])
.is_err();
}
// https://github.com/rust-lang/rustup/issues/4053
#[tokio::test]
async fn bad_file_override_with_manip() {
let cx = CliTestContext::new(Scenario::None).await;
let cwd = cx.config.current_dir();
let toolchain_file = cwd.join("rust-toolchain.toml");
raw::write_file(
&toolchain_file,
r#"toolchain.channel = "nightly', please install with 'curl --proto '=https' --tlsv1.2 -sSf https://sh.rust-toolchain.rs/ | sh -s -- --default-toolchain nightly -y""#,
).unwrap();
cx.config
.expect(["rustup", "show", "active-toolchain"])
.await
.with_stderr(snapbox::str![[r#"
...
error: invalid toolchain name detected in override file '[..]/rust-toolchain.toml'
...
"#]])
.is_err();
cx.config
.expect(["rustc", "--version"])
.await
.with_stderr(snapbox::str![[r#"
...
error: invalid toolchain name detected in override file '[..]/rust-toolchain.toml'
...
"#]])
.is_err();
}
#[tokio::test]
async fn valid_override_settings() {
let cx = CliTestContext::new(Scenario::SimpleV2).await;
let cwd = cx.config.current_dir();
let toolchain_file = cwd.join("rust-toolchain");
cx.config
.expect(&["rustup", "default", "nightly"])
.await
.is_ok();
let nightly_with_host = format!("nightly-{}", this_host_tuple());
raw::write_file(&toolchain_file, "nightly").unwrap();
cx.config.expect(&["rustc", "--version"]).await.is_ok();
// Special case: same version as is installed is permitted.
raw::write_file(&toolchain_file, &nightly_with_host).unwrap();
cx.config.expect(&["rustc", "--version"]).await.is_ok();
let fullpath = cx
.config
.rustupdir
.clone()
.join("toolchains")
.join(&nightly_with_host);
cx.config
.expect(&[
"rustup",
"toolchain",
"link",
"system",
&format!("{}", fullpath.display()),
])
.await
.is_ok();
raw::write_file(&toolchain_file, "system").unwrap();
cx.config.expect(&["rustc", "--version"]).await.is_ok();
}
#[tokio::test]
async fn file_override_with_target_info() {
// Target info is not portable between machines, so we reject toolchain
// files that include it.
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();
cx.config
.expect(["rustc", "--version"])
.await
.remove_redactions(["[HOST_TUPLE]"])
.with_stderr(snapbox::str![[r#"
...
error: target tuple in channel name 'nightly-x86_64-unknown-linux-gnu'
...
"#]])
.is_err();
}
#[tokio::test]
async fn docs_with_path() {
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(["rustup", "doc", "--path"])
.await
.with_stdout(snapbox::str![[r#"
[..]/toolchains/stable-[HOST_TUPLE]/share/doc/rust/html/index.html
"#]])
.is_ok();
cx.config
.expect(["rustup", "doc", "--path", "--toolchain", "nightly"])
.await
.with_stdout(snapbox::str![[r#"
[..]/toolchains/nightly-[HOST_TUPLE]/share/doc/rust/html/index.html
"#]])
.is_ok();
}
#[tokio::test]
async fn docs_topical_with_path() {
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();
for (args, path) in topical_doc_data::test_cases() {
let mut cmd = vec!["rustup", "doc", "--path"];
cmd.extend(args);
cx.config
.expect(cmd)
.await
.extend_redactions([("[PATH]", path)])
.is_ok()
.with_stdout(snapbox::str![[r#"
[..]/toolchains/stable-[HOST_TUPLE]/[PATH]
"#]])
.with_stderr(snapbox::str![""]);
}
}
#[tokio::test]
async fn docs_missing() {
let cx = CliTestContext::new(Scenario::SimpleV2).await;
cx.config
.expect(["rustup", "set", "profile", "minimal"])
.await
.is_ok();
cx.config
.expect(["rustup", "default", "nightly"])
.await
.is_ok();
cx.config
.expect(["rustup", "doc"])
.await
.with_stderr(snapbox::str![[r#"
info: `rust-docs` not installed in toolchain `nightly-[HOST_TUPLE]`
help: run `rustup component add --toolchain nightly-[HOST_TUPLE] rust-docs` to install it
error: unable to view documentation which is not installed
"#]])
.is_err();
}
#[tokio::test]
async fn docs_custom() {
let cx = CliTestContext::new(Scenario::None).await;
let path = cx.config.customdir.join("custom-1");
let path = path.to_string_lossy();
cx.config
.expect(["rustup", "toolchain", "link", "custom", &path])
.await
.is_ok();
cx.config
.expect(["rustup", "default", "custom"])
.await
.is_ok();
cx.config
.expect(["rustup", "doc", "--path"])
.await
.with_stdout(snapbox::str![[r#"
[..]/toolchains/custom/share/doc/rust/html/index.html
"#]])
.is_ok();
}
#[cfg(unix)]
#[tokio::test]
async fn non_utf8_arg() {
use std::ffi::OsStr;
use std::os::unix::ffi::OsStrExt;
let cx = CliTestContext::new(Scenario::SimpleV2).await;
cx.config
.expect(["rustup", "default", "nightly"])
.await
.is_ok();
cx.config
.expect_with_env(
[
OsStr::new("rustc"),
OsStr::new("--echo-args"),
OsStr::new("echoed non-utf8 arg:"),
OsStr::from_bytes(b"\xc3\x28"),
],
[("RUST_BACKTRACE", "1")],
)
.await
.with_stderr(snapbox::str![[r#"
echoed non-utf8 arg:
...
"#]]);
}
#[cfg(windows)]
#[tokio::test]
async fn non_utf8_arg() {
use std::ffi::OsString;
use std::os::windows::ffi::OsStringExt;
let cx = CliTestContext::new(Scenario::SimpleV2).await;
cx.config
.expect(["rustup", "default", "nightly"])
.await
.is_ok();
cx.config
.expect_with_env(
[
OsString::from("rustc"),
OsString::from("--echo-args"),
OsString::from("echoed non-utf8 arg:"),
OsString::from_wide(&[0xd801, 0xd801]),
],
[("RUST_BACKTRACE", "1")],
)
.await
.with_stderr(snapbox::str![[r#"
echoed non-utf8 arg:
...
"#]]);
}
#[cfg(unix)]
#[tokio::test]
async fn non_utf8_toolchain() {
use std::ffi::OsStr;
use std::os::unix::ffi::OsStrExt;
let cx = CliTestContext::new(Scenario::SimpleV2).await;
cx.config
.expect(["rustup", "default", "nightly"])
.await
.is_ok();
cx.config
.expect_with_env(
[OsStr::new("rustc"), OsStr::from_bytes(b"+\xc3\x28")],
[("RUST_BACKTRACE", "1")],
)
.await
.with_stderr(snapbox::str![[r#"
...
error: toolchain '<27>(' is not installed
...
"#]]);
}
#[cfg(windows)]
#[tokio::test]
async fn non_utf8_toolchain() {
use std::ffi::OsString;
use std::os::windows::ffi::OsStringExt;
let cx = CliTestContext::new(Scenario::SimpleV2).await;
cx.config
.expect(["rustup", "default", "nightly"])
.await
.is_ok();
cx.config
.expect_with_env(
[
OsString::from("rustc"),
OsString::from_wide(&[u16::from(b'+'), 0xd801, 0xd801]),
],
[("RUST_BACKTRACE", "1")],
)
.await
.with_stderr(snapbox::str![[r#"
...
error: toolchain '<27><>' is not installed
...
"#]]);
}
#[tokio::test]
async fn check_host_goes_away() {
let mut cx = CliTestContext::new(Scenario::None).await;
{
let cx = cx.with_dist_dir(Scenario::HostGoesMissingBefore);
cx.config
.expect(["rustup", "default", "nightly"])
.await
.is_ok();
}
let cx = cx.with_dist_dir(Scenario::HostGoesMissingAfter);
cx.config
.expect(&["rustup", "update", "nightly"])
.await
.with_stderr(snapbox::str![[r#"
...
error: target '[HOST_TUPLE]' not found in channel[..]
...
"#]])
.is_err();
}
#[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(["rustup", "default"])
.await
.with_stdout(snapbox::str![[""]])
.with_stderr(snapbox::str![[r#"
error: no default toolchain is configured
"#]])
.is_err();
// 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,
&format!("default_toolchain = 'nightly-{}'", this_host_tuple()),
)
.unwrap();
cx.config
.expect_with_env(
["rustup", "default"],
[(
"RUSTUP_OVERRIDE_UNIX_FALLBACK_SETTINGS",
&*mock_settings_file.display().to_string(),
)],
)
.await
.with_stdout(snapbox::str![[r#"
nightly-[HOST_TUPLE] (default)
"#]])
.is_ok();
}
#[tokio::test]
async fn deny_incompatible_toolchain_install() {
let cx = CliTestContext::new(Scenario::MultiHost).await;
let arch = MULTI_ARCH1;
cx.config
.expect(["rustup", "toolchain", "install", &format!("nightly-{arch}")])
.await
.extend_redactions([("[ARCH]", arch)])
.with_stderr(snapbox::str![[r#"
error: toolchain 'nightly-[ARCH]' may not be able to run on this system
note: to build software for that platform, try `rustup target add [ARCH]` instead
note: add the `--force-non-host` flag to install the toolchain anyway
"#]])
.is_err();
}
#[tokio::test]
async fn deny_incompatible_toolchain_default() {
let cx = CliTestContext::new(Scenario::MultiHost).await;
let arch = MULTI_ARCH1;
cx.config
.expect(["rustup", "default", &format!("nightly-{arch}")])
.await
.extend_redactions([("[ARCH]", arch)])
.with_stderr(snapbox::str![[r#"
error: toolchain 'nightly-[ARCH]' may not be able to run on this system
note: to build software for that platform, try `rustup target add [ARCH]` instead
note: add the `--force-non-host` flag to install the toolchain anyway
"#]])
.is_err();
}
#[tokio::test]
async fn dont_warn_on_partial_build() {
let cx = CliTestContext::new(Scenario::SimpleV2).await;
let arch = this_host_tuple().split_once('-').unwrap().0.to_owned();
cx.config
.expect(["rustup", "toolchain", "install", &format!("nightly-{arch}")])
.await
.with_stderr(snapbox::str![[r#"
...
info: syncing channel updates for nightly-[HOST_TUPLE]
...
"#]])
.is_ok()
.without_stderr(&format!(
"toolchain 'nightly-{arch}' may not be able to run on this system."
));
}
/// Checks that `rust-toolchain.toml` files are considered
#[tokio::test]
async fn rust_toolchain_toml() {
let cx = CliTestContext::new(Scenario::SimpleV2).await;
cx.config
.expect(["rustc", "--version"])
.await
.with_stderr(snapbox::str![[r#"
...
error: rustup could not choose a version of rustc to run, because one wasn't specified explicitly, and no default is configured.
...
"#]])
.is_err();
let cwd = cx.config.current_dir();
let toolchain_file = cwd.join("rust-toolchain.toml");
raw::write_file(&toolchain_file, "[toolchain]\nchannel = \"nightly\"").unwrap();
cx.config
.expect(["rustup", "toolchain", "install"])
.await
.is_ok();
cx.config
.expect(["rustc", "--version"])
.await
.with_stdout(snapbox::str![[r#"
1.3.0 (hash-nightly-2)
"#]])
.is_ok();
}
/// Ensures that `rust-toolchain.toml` files (with `.toml` extension) only allow TOML contents
#[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();
cx.config
.expect(["rustc", "--version"])
.await
.extend_redactions([("[CWD]", &cwd)])
.with_stderr(snapbox::str![[r#"
...
error: could not parse override file: '[CWD]/rust-toolchain.toml'[..]
...
"#]])
.is_err();
}
/// Checks that a warning occurs if both `rust-toolchain` and `rust-toolchain.toml` files exist
#[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(&["rustup", "toolchain", "install"])
.await
.extend_redactions([("[CWD]", &cwd.canonicalize().unwrap())])
.with_stderr(snapbox::str![[r#"
...
warn: both [CWD]/rust-toolchain and [CWD]/rust-toolchain.toml exist; using contents of [CWD]/rust-toolchain
...
"#]])
.is_ok();
}
#[tokio::test]
async fn custom_toolchain_with_components_toolchains_profile_does_not_err() {
let cx = CliTestContext::new(Scenario::SimpleV2).await;
let cwd = cx.config.current_dir();
let toolchain_file = cwd.join("rust-toolchain.toml");
// install a toolchain so we can make a custom toolchain that links to it
cx.config
.expect(&[
"rustup",
"toolchain",
"install",
"nightly",
"--profile=minimal",
"--component=cargo",
])
.await
.with_stderr(snapbox::str![[r#"
info: syncing channel updates for nightly-[HOST_TUPLE]
info: latest update on 2015-01-02 for version 1.3.0 (hash-nightly-2)
info: downloading 2 components
info: default toolchain set to nightly-[HOST_TUPLE]
"#]])
.is_ok();
cx.config
.expect(["rustup", "+nightly", "component", "list", "--installed"])
.await
.is_ok()
.with_stdout(snapbox::str![[r#"
cargo-[HOST_TUPLE]
rustc-[HOST_TUPLE]
"#]]);
// link the toolchain
let toolchains = cx.config.rustupdir.join("toolchains");
raw::symlink_dir(
&toolchains.join(format!("nightly-{}", this_host_tuple())),
&toolchains.join("my-custom"),
)
.expect("failed to symlink");
raw::write_file(
&toolchain_file,
r#"
[toolchain]
channel = "my-custom"
components = ["rustc-dev"]
targets = ["x86_64-unknown-linux-gnu"]
profile = "minimal"
"#,
)
.unwrap();
cx.config
.expect(&["rustup", "show", "active-toolchain"])
.await
.extend_redactions([("[CWD]", &cwd)])
.with_stdout(snapbox::str![[r#"
my-custom (overridden by '[CWD]/rust-toolchain.toml')
"#]])
.is_ok();
cx.config
.expect(&["rustc", "--version"])
.await
.with_stdout(snapbox::str![[r#"
1.3.0 (hash-nightly-2)
"#]])
.is_ok();
cx.config
.expect(&["cargo", "--version"])
.await
.with_stdout(snapbox::str![[r#"
1.3.0 (hash-nightly-2)
"#]])
.is_ok();
}
// Issue #4251
#[tokio::test]
async fn show_custom_toolchain() {
let cx = CliTestContext::new(Scenario::SimpleV2).await;
cx.config
.expect(&["rustup", "default", "stable"])
.await
.is_ok();
let stable_path = cx
.config
.rustupdir
.join("toolchains")
.join(format!("stable-{}", this_host_tuple()));
cx.config
.expect([
"rustup",
"toolchain",
"link",
"stuff",
&stable_path.to_string_lossy(),
])
.await
.is_ok();
cx.config
.expect(["rustup", "+stuff", "show"])
.await
.extend_redactions([("[RUSTUP_DIR]", cx.config.rustupdir.to_string())])
.with_stdout(snapbox::str![[r#"
Default host: [HOST_TUPLE]
rustup home: [RUSTUP_DIR]
installed toolchains
--------------------
stable-[HOST_TUPLE] (default)
stuff (active)
active toolchain
----------------
name: stuff
active because: overridden by +toolchain on the command line
installed targets:
[HOST_TUPLE]
"#]])
.with_stderr(snapbox::str![[""]])
.is_ok();
}
#[tokio::test]
async fn show_custom_toolchain_without_components_file() {
let cx = CliTestContext::new(Scenario::SimpleV2).await;
cx.config
.expect(["rustup", "default", "stable"])
.await
.is_ok();
let stable_path = cx
.config
.rustupdir
.join("toolchains")
.join(format!("stable-{}", this_host_tuple()));
cx.config
.expect([
"rustup",
"toolchain",
"link",
"stuff",
&stable_path.to_string_lossy(),
])
.await
.is_ok();
let components_file = stable_path.join("lib").join("rustlib").join("components");
fs::remove_file(&components_file).unwrap();
cx.config
.expect(["rustup", "+stuff", "show"])
.await
.extend_redactions([("[RUSTUP_DIR]", cx.config.rustupdir.to_string())])
.with_stdout(snapbox::str![[r#"
Default host: [HOST_TUPLE]
rustup home: [RUSTUP_DIR]
installed toolchains
--------------------
stable-[HOST_TUPLE] (default)
stuff (active)
active toolchain
----------------
name: stuff
active because: overridden by +toolchain on the command line
installed targets:
"#]])
.with_stderr(snapbox::str![[""]])
.is_ok();
}
#[tokio::test]
async fn missing_manifest_shows_reinstall_help() {
let cx = CliTestContext::new(Scenario::SimpleV2).await;
cx.config
.expect(["rustup", "toolchain", "install", "nightly"])
.await
.is_ok();
let manifest_path = cx
.config
.rustupdir
.join("toolchains")
.join(format!("nightly-{}", this_host_tuple()))
.join("lib")
.join("rustlib")
.join("multirust-channel-manifest.toml");
fs::remove_file(&manifest_path).unwrap();
cx.config
.expect(["rustup", "component", "list", "--toolchain", "nightly"])
.await
.with_stderr(snapbox::str![[r#"
error: missing manifest in toolchain 'nightly-[HOST_TUPLE]'
help: this may happen if the toolchain installation was interrupted
help: try reinstalling or updating the toolchain
"#]])
.is_err();
}