Skip to content

Commit fd87381

Browse files
committed
test(shard): cover destination-scoped installed roots
1 parent cbd14ad commit fd87381

1 file changed

Lines changed: 122 additions & 0 deletions

File tree

‎tests/stack_shard_destinations.rs‎

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
//! Explicit module destinations must not borrow a root from another tree.
2+
use serde_json::Value;
3+
use std::{fs, path::Path, process::Command};
4+
5+
fn fixture(root: &Path) {
6+
fs::create_dir_all(root.join("robot")).unwrap();
7+
for name in ["Foo", "Need", "Reuse"] {
8+
fs::write(
9+
root.join("robot").join(format!("{name}-1.eb")),
10+
format!(
11+
"name = '{name}'\nversion = '1'\ntoolchain = SYSTEM\n{}",
12+
if name == "Foo" {
13+
"dependencies = [('Need', '1'), ('Reuse', '1')]\n"
14+
} else {
15+
""
16+
}
17+
),
18+
)
19+
.unwrap();
20+
}
21+
for name in ["Foo", "Reuse"] {
22+
fs::create_dir_all(root.join("cpu").join(name)).unwrap();
23+
fs::write(root.join("cpu").join(name).join("1.lua"), "-- module\n").unwrap();
24+
}
25+
fs::create_dir_all(root.join("noarch")).unwrap();
26+
}
27+
28+
fn run(root: &Path, row: &str, label: &str, lifted: bool) -> std::process::Output {
29+
fs::write(root.join("rows"), format!("{row}\n")).unwrap();
30+
let mut cmd = Command::new(env!("CARGO_BIN_EXE_eb-stack"));
31+
cmd.env_remove("EASYBUILD_FILTER_DEPS")
32+
.args(["stack", "shard", "--build-list"])
33+
.arg(root.join("rows"))
34+
.arg("--easyconfigs")
35+
.arg(root.join("robot"))
36+
.arg("--installed-modules-dir")
37+
.arg(root.join("cpu"))
38+
.args(["--shards", "1", "--materialize", "--build-robot-path"])
39+
.arg(root.join("robot"))
40+
.arg("--out-dir")
41+
.arg(root.join(label));
42+
if lifted {
43+
cmd.arg("--lift-cpu-prerequisites");
44+
}
45+
cmd.output().unwrap()
46+
}
47+
48+
fn plan(root: &Path, row: &str, label: &str, lifted: bool) -> Value {
49+
let output = run(root, row, label, lifted);
50+
assert!(output.status.success(), "{}", String::from_utf8_lossy(&output.stderr));
51+
serde_json::from_slice(&fs::read(root.join(label).join("plan.json")).unwrap()).unwrap()
52+
}
53+
54+
#[test]
55+
fn explicit_destination_controls_root_closure_cost_and_row_retention() {
56+
for separator in ["=", " "] {
57+
for lifted in [false, true] {
58+
let tmp = tempfile::tempdir().unwrap();
59+
let root = tmp.path();
60+
fixture(root);
61+
let row = format!(
62+
"Foo-1.eb --installpath-modules{separator}{}",
63+
root.join("noarch").display()
64+
);
65+
let missing = plan(root, &row, "missing", lifted);
66+
assert_eq!(missing["lines"][0]["text"], row);
67+
let closure = missing["lines"][0]["closure"].as_array().unwrap();
68+
assert!(closure.contains(&Value::from("Foo/1")));
69+
assert!(closure.contains(&Value::from("Need/1")));
70+
assert!(!closure.contains(&Value::from("Reuse/1")));
71+
assert_eq!(closure.len(), 2);
72+
let filename = if lifted { "cpu-prerequisites.list" } else { "shard-01.list" };
73+
assert!(fs::read_to_string(root.join("missing").join(filename)).unwrap().lines().any(|l| l == row));
74+
let local_filename = filename.replace(".list", ".local.list");
75+
assert!(fs::read_to_string(root.join("missing").join(local_filename)).unwrap().contains("--installpath-modules"));
76+
if !lifted {
77+
assert_eq!(missing["shards"][0]["weight"], 2);
78+
assert!(missing["shards"][0]["inode_footprint"].as_u64().unwrap() > 1000);
79+
}
80+
fs::create_dir_all(root.join("noarch/all/Foo")).unwrap();
81+
fs::write(root.join("noarch/all/Foo/1.lua"), "-- module\n").unwrap();
82+
let present = plan(root, &row, "present", lifted);
83+
assert_eq!(present["lines"][0]["closure"], serde_json::json!([]));
84+
assert_ne!(missing["inputs"]["sha256"], present["inputs"]["sha256"]);
85+
assert_eq!(present["lines"][0]["text"], row);
86+
}
87+
}
88+
}
89+
90+
#[test]
91+
fn default_roots_reuse_global_modules_but_explicit_roots_do_not_require_global_presence() {
92+
let tmp = tempfile::tempdir().unwrap();
93+
let root = tmp.path();
94+
fixture(root);
95+
let default = plan(root, "Foo-1.eb", "default", false);
96+
assert_eq!(default["lines"][0]["closure"], serde_json::json!([]));
97+
fs::rename(root.join("cpu/Foo"), root.join("unrelated")).unwrap();
98+
fs::create_dir_all(root.join("noarch/all/Foo")).unwrap();
99+
fs::write(root.join("noarch/all/Foo/1"), "# Tcl module\n").unwrap();
100+
let row = format!("Foo-1.eb --installpath-modules={}", root.join("noarch").display());
101+
let explicit = plan(root, &row, "explicit", false);
102+
assert_eq!(explicit["lines"][0]["closure"], serde_json::json!([]));
103+
}
104+
105+
#[test]
106+
fn malformed_or_unresolved_explicit_destinations_cannot_reuse_global_roots() {
107+
let tmp = tempfile::tempdir().unwrap();
108+
let root = tmp.path();
109+
fixture(root);
110+
for option in [
111+
"--installpath-modules",
112+
"--installpath-modules=",
113+
"--installpath-modules relative/path",
114+
"--installpath-modules=$UNRESOLVED/modules",
115+
"--installpath-modules=/root/$UNRESOLVED",
116+
"--installpath-modules=/one --installpath-modules=/two",
117+
] {
118+
let output = run(root, &format!("Foo-1.eb {option}"), "invalid", false);
119+
assert!(!output.status.success(), "{option}");
120+
assert!(String::from_utf8_lossy(&output.stderr).contains("literal absolute path"));
121+
}
122+
}

0 commit comments

Comments
 (0)