diff --git a/src/test/clitools.rs b/src/test/clitools.rs index 190b4b7a1d..b5268216b2 100644 --- a/src/test/clitools.rs +++ b/src/test/clitools.rs @@ -543,6 +543,8 @@ pub enum Scenario { SimpleV2, /// One date, v1 manifests SimpleV1, + /// One date, v2 manifests, `rust-analyzer` available as an extension component + RustAnalyzer, /// One date, v2 manifests, MULTI_ARCH1 host MultiHost, /// One date, v2 manifests, beta with tag @@ -627,6 +629,13 @@ impl Scenario { Release::beta("1.2.0", "2015-01-02"), Release::stable("1.1.0", "2015-01-02"), ], + Self::RustAnalyzer => vec![ + Release::new("nightly", "1.3.0", "2015-01-02", "2") + .with_rls(RlsStatus::Renamed) + .with_rust_analyzer(), + Release::beta("1.2.0", "2015-01-02").with_rust_analyzer(), + Release::stable("1.1.0", "2015-01-02").with_rust_analyzer(), + ], Self::MultiHost => vec![ Release::new("nightly", "1.3.0", "2015-01-02", "2").multi_arch(), Release::beta("1.2.0", "2015-01-02").multi_arch(), @@ -677,6 +686,7 @@ impl Scenario { | Self::ArchivesV2TwoVersions | Self::BetaTag | Self::MultiHost + | Self::RustAnalyzer | Self::Unavailable | Self::UnavailableRls | Self::RemovedRls @@ -723,26 +733,27 @@ impl ConstState { Self { const_dist_dir, scenarios: enum_map! { - Scenario::ArchivesV1 => RwLock::new(None), - Scenario::ArchivesV2 => RwLock::new(None), - Scenario::ArchivesV2_2015_01_01 => RwLock::new(None), - Scenario::ArchivesV2TwoVersions => RwLock::new(None), - Scenario::BetaTag => RwLock::new(None), - Scenario::Empty => RwLock::new(None), - Scenario::Full => RwLock::new(None), - Scenario::HostGoesMissingBefore => RwLock::new(None), - Scenario::HostGoesMissingAfter => RwLock::new(None), - Scenario::MissingComponent => RwLock::new(None), - Scenario::MissingComponentMulti => RwLock::new(None), - Scenario::MissingNightly => RwLock::new(None), - Scenario::MultiHost => RwLock::new(None), - Scenario::None => RwLock::new(None), - Scenario::RemovedRls => RwLock::new(None), - Scenario::SimpleV1 => RwLock::new(None), - Scenario::SimpleV2 => RwLock::new(None), - Scenario::Unavailable => RwLock::new(None), - Scenario::UnavailableRls => RwLock::new(None), - Scenario::RecentStable => RwLock::new(None), + Scenario::ArchivesV1 + | Scenario::ArchivesV2 + | Scenario::ArchivesV2TwoVersions + | Scenario::ArchivesV2_2015_01_01 + | Scenario::BetaTag + | Scenario::Empty + | Scenario::Full + | Scenario::HostGoesMissingAfter + | Scenario::HostGoesMissingBefore + | Scenario::MissingComponent + | Scenario::MissingComponentMulti + | Scenario::MissingNightly + | Scenario::MultiHost + | Scenario::None + | Scenario::RecentStable + | Scenario::RemovedRls + | Scenario::RustAnalyzer + | Scenario::SimpleV1 + | Scenario::SimpleV2 + | Scenario::Unavailable + | Scenario::UnavailableRls => RwLock::new(None), }, } } @@ -849,6 +860,7 @@ async fn setup_test_state(test_dist_dir: TempDir) -> (TempDir, Config) { let rustc_path = config.exedir.join(format!("rustc{EXE_SUFFIX}")); let cargo_path = config.exedir.join(format!("cargo{EXE_SUFFIX}")); let rls_path = config.exedir.join(format!("rls{EXE_SUFFIX}")); + let rust_analyzer_path = config.exedir.join(format!("rust-analyzer{EXE_SUFFIX}")); let rust_lldb_path = config.exedir.join(format!("rust-lldb{EXE_SUFFIX}")); const ESTIMATED_LINKS_PER_TEST: usize = 6 * 2; @@ -885,6 +897,7 @@ async fn setup_test_state(test_dist_dir: TempDir) -> (TempDir, Config) { hard_link(&rustup_path, rustc_path).unwrap(); hard_link(&rustup_path, cargo_path).unwrap(); hard_link(&rustup_path, rls_path).unwrap(); + hard_link(&rustup_path, rust_analyzer_path).unwrap(); hard_link(&rustup_path, rust_lldb_path).unwrap(); // Make sure the host tuple matches the build tuple. Otherwise testing a 32-bit build of diff --git a/src/test/dist.rs b/src/test/dist.rs index 930f38c744..4c8e59466f 100644 --- a/src/test/dist.rs +++ b/src/test/dist.rs @@ -85,6 +85,7 @@ pub(super) struct Release { version: String, hash: String, rls: RlsStatus, + rust_analyzer: bool, available: bool, multi_arch: bool, } @@ -111,6 +112,11 @@ impl Release { self } + pub(super) fn with_rust_analyzer(mut self) -> Self { + self.rust_analyzer = true; + self + } + pub(super) fn unavailable(mut self) -> Self { self.available = false; self @@ -136,32 +142,17 @@ impl Release { available: true, multi_arch: false, rls: RlsStatus::Available, + rust_analyzer: false, } } pub(super) fn mock(&self) -> MockChannel { if self.available { - MockChannel::new( - &self.channel, - &self.date, - &self.version, - &self.hash, - self.rls, - self.multi_arch, - false, - ) + MockChannel::new(self, self.multi_arch, false) } else if self.multi_arch { // unavailable but multiarch means to build only with host==MULTI_ARCH1 // instead of true multiarch - MockChannel::new( - &self.channel, - &self.date, - &self.version, - &self.hash, - self.rls, - false, - true, - ) + MockChannel::new(self, false, true) } else { MockChannel::unavailable(&self.channel, &self.date, &self.version, &self.hash) } @@ -287,15 +278,17 @@ pub(crate) struct MockChannel { } impl MockChannel { - pub(super) fn new( - channel: &str, - date: &str, - version: &str, - version_hash: &str, - rls: RlsStatus, - multi_arch: bool, - swap_tuples: bool, - ) -> Self { + pub(super) fn new(release: &Release, multi_arch: bool, swap_tuples: bool) -> Self { + let Release { + channel, + date, + version, + hash: version_hash, + rls, + rust_analyzer, + available: _, + multi_arch: _, + } = release; // Build the mock installers let host_tuple = if swap_tuples { MULTI_ARCH1.to_owned() @@ -323,7 +316,7 @@ impl MockChannel { all.rustc.push((rustc, host_tuple.clone())); all.cargo.push((cargo, host_tuple.clone())); - if rls != RlsStatus::Unavailable { + if *rls != RlsStatus::Unavailable { let rls = MockInstallerBuilder::rls(version, version_hash, rls.pkg_name()); all.rls.push((rls, host_tuple.clone())); } else { @@ -333,6 +326,11 @@ impl MockChannel { )); } + if *rust_analyzer { + let ra = MockInstallerBuilder::rust_analyzer(version, version_hash); + all.rust_analyzer.push((ra, host_tuple.clone())); + } + all.docs.push((rust_docs, host_tuple.clone())); all.src.push((rust_src, "*".to_string())); all.analysis.push((rust_analysis, "*".to_string())); @@ -350,7 +348,7 @@ impl MockChannel { all.rustc.push((rustc, target_tuple.clone())); all.cargo.push((cargo, target_tuple.clone())); - if rls != RlsStatus::Unavailable { + if *rls != RlsStatus::Unavailable { let rls = MockInstallerBuilder::rls(version, version_hash, rls.pkg_name()); all.rls.push((rls, target_tuple.clone())); } else { @@ -360,6 +358,11 @@ impl MockChannel { )); } + if *rust_analyzer { + let ra = MockInstallerBuilder::rust_analyzer(version, version_hash); + all.rust_analyzer.push((ra, target_tuple.clone())); + } + all.docs.push((rust_docs, target_tuple.to_string())); all.combined.push((rust, target_tuple)); } @@ -389,7 +392,7 @@ impl MockChannel { // Add subcomponents of the rust package { - let rust_pkg = packages.last_mut().unwrap(); + let rust_pkg = packages.iter_mut().find(|p| p.name == "rust").unwrap(); for target_pkg in rust_pkg.targets.iter_mut() { let target = &target_pkg.target; target_pkg.components.push(MockComponent { @@ -412,13 +415,13 @@ impl MockChannel { target: target.to_string(), is_extension: false, }); - if rls == RlsStatus::Renamed { + if *rls == RlsStatus::Renamed { target_pkg.components.push(MockComponent { name: "rls-preview".to_string(), target: target.to_string(), is_extension: true, }); - } else if rls == RlsStatus::Available { + } else if *rls == RlsStatus::Available { target_pkg.components.push(MockComponent { name: "rls".to_string(), target: target.to_string(), @@ -431,6 +434,13 @@ impl MockChannel { is_extension: true, }) } + if *rust_analyzer { + target_pkg.components.push(MockComponent { + name: "rust-analyzer-preview".to_string(), + target: target.to_string(), + is_extension: true, + }); + } for other_target in &all_std_archs { if other_target != target { target_pkg.components.push(MockComponent { @@ -455,9 +465,15 @@ impl MockChannel { } let mut renames = HashMap::new(); - if rls == RlsStatus::Renamed { + if *rls == RlsStatus::Renamed { renames.insert("rls".to_owned(), "rls-preview".to_owned()); } + if *rust_analyzer { + renames.insert( + "rust-analyzer".to_owned(), + "rust-analyzer-preview".to_owned(), + ); + } Self { name: channel.to_string(), @@ -513,6 +529,7 @@ struct MockChannelContent { rustc: Vec<(MockInstallerBuilder, String)>, cargo: Vec<(MockInstallerBuilder, String)>, rls: Vec<(MockInstallerBuilder, String)>, + rust_analyzer: Vec<(MockInstallerBuilder, String)>, docs: Vec<(MockInstallerBuilder, String)>, src: Vec<(MockInstallerBuilder, String)>, analysis: Vec<(MockInstallerBuilder, String)>, @@ -524,7 +541,7 @@ impl MockChannelContent { self, rls_name: &'static str, ) -> Vec<(&'static str, Vec<(MockInstallerBuilder, String)>)> { - vec![ + let mut packages = vec![ ("rust-std", self.std), ("rustc", self.rustc), ("cargo", self.cargo), @@ -533,7 +550,14 @@ impl MockChannelContent { ("rust-src", self.src), ("rust-analysis", self.analysis), ("rust", self.combined), - ] + ]; + + // Not every release ships `rust-analyzer`. + if !self.rust_analyzer.is_empty() { + packages.push(("rust-analyzer-preview", self.rust_analyzer)); + } + + packages } } diff --git a/src/test/mock.rs b/src/test/mock.rs index d5600b92d2..556addf21a 100644 --- a/src/test/mock.rs +++ b/src/test/mock.rs @@ -76,6 +76,15 @@ impl MockInstallerBuilder { } } + pub(super) fn rust_analyzer(version: &str, version_hash: &str) -> Self { + Self { + components: vec![MockComponentBuilder { + name: "rust-analyzer-preview".to_string(), + files: mock_bin("rust-analyzer", version, version_hash), + }], + } + } + pub(super) fn rust_doc() -> Self { let mut files: Vec = topical_doc_data::unique_paths() .map(|x| MockFile::new(x, b"")) diff --git a/tests/suite/cli_misc.rs b/tests/suite/cli_misc.rs index e7ae790352..46d748ada4 100644 --- a/tests/suite/cli_misc.rs +++ b/tests/suite/cli_misc.rs @@ -1706,57 +1706,34 @@ CARGO environment variable not set[..] } #[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(); - } +async fn rust_analyzer_proxy_uses_hosted_without_external() { + let cx = CliTestContext::new(Scenario::RustAnalyzer).await; + cx.config.expect(RUST_ANALYZER_SETUP_CMD).await.is_ok(); - // 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()); +} + +#[tokio::test] +async fn rust_analyzer_proxy_uses_hosted_over_external() { + let cx = CliTestContext::new(Scenario::RustAnalyzer).await; + cx.config.expect(RUST_ANALYZER_SETUP_CMD).await.is_ok(); let tempdir = tempfile::Builder::new().prefix("rustup").tempdir().unwrap(); let extern_dir = tempdir.path(); let extern_path = &extern_dir.join("rust-analyzer"); + let real_path = cx + .config + .expect(["rust-analyzer", "--echo-current-exe"]) + .await; + let real_path = Path::new(real_path.output.stderr.trim()); 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"], @@ -1769,9 +1746,27 @@ async fn rust_analyzer_proxy_falls_back_external() { "#]]) .is_ok(); +} + +#[tokio::test] +async fn rust_analyzer_proxy_uses_external_without_hosted() { + let cx = CliTestContext::new(Scenario::RustAnalyzer).await; + cx.config.expect(RUST_ANALYZER_SETUP_CMD).await.is_ok(); + + let tempdir = tempfile::Builder::new().prefix("rustup").tempdir().unwrap(); + let extern_dir = tempdir.path(); + let extern_path = &extern_dir.join("rust-analyzer"); + let real_path = cx + .config + .expect(["rust-analyzer", "--echo-current-exe"]) + .await; + let real_path = Path::new(real_path.output.stderr.trim()); + fs::copy(real_path, extern_path).unwrap(); + cx.config + .expect(["rustup", "component", "remove", "rust-analyzer"]) + .await + .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"], @@ -1788,6 +1783,16 @@ info: falling back to "[EXTERN_PATH]" .is_ok(); } +const RUST_ANALYZER_SETUP_CMD: &[&str] = &[ + "rustup", + "toolchain", + "install", + "stable", + "--default", + "--profile=minimal", + "--component=rust-analyzer", +]; + #[tokio::test] async fn warn_auto_install_on_proxy() { let cx = CliTestContext::new(Scenario::SimpleV2).await;