Skip to content

Commit afb2f66

Browse files
committed
test(cli): show override handling of symlinked and relative paths
Overrides are stored and looked up by canonical path, which breaks down once a directory is reached through a different path (#1138): - an override whose directory later moved behind a symlink no longer applies, yet is listed as a normal directory and cannot be removed with `--path`, which resolves to the target instead - `--path <symlink>` stores the target's path rather than the given one - a relative `--path` that does not exist yet is stored verbatim and can never match a directory
1 parent 7ceb753 commit afb2f66

1 file changed

Lines changed: 256 additions & 0 deletions

File tree

‎tests/suite/cli_exact.rs‎

Lines changed: 256 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
//! is exactly as expected.
33
44
use std::fs;
5+
#[cfg(unix)]
6+
use std::path::PathBuf;
57

68
use rustup::{
79
test::{CROSS_ARCH1, CROSS_ARCH2, CliTestContext, MULTI_ARCH1, Scenario, this_host_tuple},
@@ -576,6 +578,260 @@ info: you may remove overrides for non-existent directories with
576578
"#]]);
577579
}
578580

581+
/// Sets an override on `old`, then moves that directory to `new` and leaves a
582+
/// symlink to it behind at `old`.
583+
#[cfg(unix)]
584+
async fn override_moved_behind_symlink(cx: &CliTestContext) -> (PathBuf, PathBuf) {
585+
let cwd = fs::canonicalize(cx.config.current_dir()).unwrap();
586+
let old = cwd.join("old");
587+
let new = cwd.join("new");
588+
fs::create_dir(&old).unwrap();
589+
cx.config
590+
.expect([
591+
"rustup",
592+
"override",
593+
"set",
594+
"nightly",
595+
"--path",
596+
old.to_str().unwrap(),
597+
])
598+
.await
599+
.is_ok();
600+
fs::rename(&old, &new).unwrap();
601+
raw::symlink_dir(&new, &old).unwrap();
602+
(old, new)
603+
}
604+
605+
#[tokio::test]
606+
#[cfg(unix)]
607+
async fn override_ignored_for_dir_moved_behind_symlink() {
608+
let mut cx = CliTestContext::new(Scenario::SimpleV2).await;
609+
cx.config
610+
.expect(["rustup", "default", "stable"])
611+
.await
612+
.is_ok();
613+
let (old, _new) = override_moved_behind_symlink(&cx).await;
614+
615+
// The override is stored under the directory's old path, which the
616+
// directory itself no longer resolves to.
617+
let cx = cx.change_dir(&old);
618+
cx.config
619+
.expect(["rustc", "--version"])
620+
.await
621+
.is_ok()
622+
.with_stdout(snapbox::str![[r#"
623+
1.1.0 (hash-stable-1.1.0)
624+
625+
"#]]);
626+
}
627+
628+
#[tokio::test]
629+
#[cfg(unix)]
630+
async fn list_overrides_with_dir_moved_behind_symlink() {
631+
let cx = CliTestContext::new(Scenario::SimpleV2).await;
632+
let (old, _new) = override_moved_behind_symlink(&cx).await;
633+
634+
// The symlink leads to a directory, so the override is not nonexistent.
635+
cx.config
636+
.expect(["rustup", "override", "list"])
637+
.await
638+
.extend_redactions([("[OLD]", old)])
639+
.is_ok()
640+
.with_stdout(snapbox::str![[r#"
641+
[OLD] nightly
642+
643+
"#]])
644+
.with_stderr(snapbox::str![[""]]);
645+
cx.config
646+
.expect(["rustup", "override", "unset", "--nonexistent"])
647+
.await
648+
.is_ok()
649+
.with_stdout(snapbox::str![[""]])
650+
.with_stderr(snapbox::str![[r#"
651+
info: no nonexistent paths detected
652+
653+
"#]]);
654+
}
655+
656+
#[tokio::test]
657+
#[cfg(unix)]
658+
async fn remove_override_with_path_moved_behind_symlink() {
659+
let cx = CliTestContext::new(Scenario::SimpleV2).await;
660+
let (old, _new) = override_moved_behind_symlink(&cx).await;
661+
662+
// `--path` resolves the symlink, so the override stored under `old` is
663+
// not found.
664+
cx.config
665+
.expect([
666+
"rustup",
667+
"override",
668+
"unset",
669+
"--path",
670+
old.to_str().unwrap(),
671+
])
672+
.await
673+
.extend_redactions([("[OLD]", old.clone())])
674+
.is_ok()
675+
.with_stdout(snapbox::str![[""]])
676+
.with_stderr(snapbox::str![[r#"
677+
info: no override toolchain for '[OLD]'
678+
679+
"#]]);
680+
cx.config
681+
.expect(["rustup", "override", "list"])
682+
.await
683+
.extend_redactions([("[OLD]", old)])
684+
.is_ok()
685+
.with_stdout(snapbox::str![[r#"
686+
[OLD] nightly
687+
688+
"#]])
689+
.with_stderr(snapbox::str![[""]]);
690+
}
691+
692+
#[tokio::test]
693+
#[cfg(unix)]
694+
async fn remove_override_with_path_symlink_removes_target() {
695+
let cx = CliTestContext::new(Scenario::SimpleV2).await;
696+
let (old, new) = override_moved_behind_symlink(&cx).await;
697+
cx.config
698+
.expect([
699+
"rustup",
700+
"override",
701+
"set",
702+
"nightly",
703+
"--path",
704+
new.to_str().unwrap(),
705+
])
706+
.await
707+
.is_ok();
708+
cx.config
709+
.expect(["rustup", "override", "list"])
710+
.await
711+
.extend_redactions([("[OLD]", old.clone()), ("[NEW]", new.clone())])
712+
.is_ok()
713+
.with_stdout(snapbox::str![[r#"
714+
[NEW] nightly
715+
[OLD] nightly
716+
717+
"#]])
718+
.with_stderr(snapbox::str![[""]]);
719+
720+
// `--path` resolves the symlink, so this removes the override of `new`
721+
// and leaves the one stored under `old` behind.
722+
cx.config
723+
.expect([
724+
"rustup",
725+
"override",
726+
"unset",
727+
"--path",
728+
old.to_str().unwrap(),
729+
])
730+
.await
731+
.extend_redactions([("[OLD]", old.clone())])
732+
.is_ok()
733+
.with_stdout(snapbox::str![[""]])
734+
.with_stderr(snapbox::str![[r#"
735+
info: override toolchain for '[OLD]' removed
736+
737+
"#]]);
738+
cx.config
739+
.expect(["rustup", "override", "list"])
740+
.await
741+
.extend_redactions([("[OLD]", old), ("[NEW]", new)])
742+
.is_ok()
743+
.with_stdout(snapbox::str![[r#"
744+
[OLD] nightly
745+
746+
"#]])
747+
.with_stderr(snapbox::str![[""]]);
748+
}
749+
750+
#[tokio::test]
751+
#[cfg(unix)]
752+
async fn override_set_with_path_symlink() {
753+
let mut cx = CliTestContext::new(Scenario::SimpleV2).await;
754+
let cwd = fs::canonicalize(cx.config.current_dir()).unwrap();
755+
let target = cwd.join("target");
756+
let link = cwd.join("link");
757+
fs::create_dir(&target).unwrap();
758+
raw::symlink_dir(&target, &link).unwrap();
759+
cx.config
760+
.expect([
761+
"rustup",
762+
"override",
763+
"set",
764+
"nightly",
765+
"--path",
766+
link.to_str().unwrap(),
767+
])
768+
.await
769+
.is_ok();
770+
771+
// The symlink is resolved before the override is stored.
772+
cx.config
773+
.expect(["rustup", "override", "list"])
774+
.await
775+
.extend_redactions([("[LINK]", link.clone()), ("[TARGET]", target.clone())])
776+
.is_ok()
777+
.with_stdout(snapbox::str![[r#"
778+
[TARGET] nightly
779+
780+
"#]])
781+
.with_stderr(snapbox::str![[""]]);
782+
for dir in [&target, &link] {
783+
let cx = cx.change_dir(dir);
784+
cx.config
785+
.expect(["rustc", "--version"])
786+
.await
787+
.is_ok()
788+
.with_stdout(snapbox::str![[r#"
789+
1.3.0 (hash-nightly-2)
790+
791+
"#]]);
792+
}
793+
}
794+
795+
#[tokio::test]
796+
async fn override_set_with_path_relative_nonexistent() {
797+
let mut cx = CliTestContext::new(Scenario::SimpleV2).await;
798+
cx.config
799+
.expect(["rustup", "default", "stable"])
800+
.await
801+
.is_ok();
802+
cx.config
803+
.expect(["rustup", "override", "set", "nightly", "--path", "later"])
804+
.await
805+
.is_ok();
806+
807+
// The relative path is stored as given, so it can never match a directory.
808+
cx.config
809+
.expect(["rustup", "override", "list"])
810+
.await
811+
.is_ok()
812+
.with_stdout(snapbox::str![[r#"
813+
later (not a directory) nightly
814+
815+
816+
"#]])
817+
.with_stderr(snapbox::str![[r#"
818+
info: you may remove overrides for non-existent directories with
819+
`rustup override unset --nonexistent`
820+
821+
"#]]);
822+
let later = cx.config.current_dir().join("later");
823+
fs::create_dir(&later).unwrap();
824+
let cx = cx.change_dir(&later);
825+
cx.config
826+
.expect(["rustc", "--version"])
827+
.await
828+
.is_ok()
829+
.with_stdout(snapbox::str![[r#"
830+
1.1.0 (hash-stable-1.1.0)
831+
832+
"#]]);
833+
}
834+
579835
#[tokio::test]
580836
async fn update_no_manifest() {
581837
let cx = CliTestContext::new(Scenario::SimpleV2).await;

0 commit comments

Comments
 (0)