fix: require manifests for licensed gates
This commit is contained in:
@@ -40,10 +40,27 @@ cargo xtask ci
|
|||||||
- разместите игровые каталоги в [`testdata/`](testdata);
|
- разместите игровые каталоги в [`testdata/`](testdata);
|
||||||
- игровые ресурсы в репозиторий не включаются, так как защищены авторским правом.
|
- игровые ресурсы в репозиторий не включаются, так как защищены авторским правом.
|
||||||
|
|
||||||
Локальный licensed gate:
|
Локальный licensed gate использует некоммитимый manifest:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cargo xtask acceptance report --suite licensed --stage 5 --root testdata
|
cat > /private/tmp/fparkan-corpora.toml <<'EOF'
|
||||||
|
schema = 1
|
||||||
|
|
||||||
|
[[corpus]]
|
||||||
|
id = "part1-local"
|
||||||
|
kind = "part1"
|
||||||
|
root = "/absolute/path/to/IS"
|
||||||
|
expected_profile = "parkan-is-part1"
|
||||||
|
|
||||||
|
[[corpus]]
|
||||||
|
id = "part2-local"
|
||||||
|
kind = "part2"
|
||||||
|
root = "/absolute/path/to/IS2"
|
||||||
|
expected_profile = "parkan-is-part2"
|
||||||
|
EOF
|
||||||
|
|
||||||
|
FPARKAN_CORPORA_MANIFEST=/private/tmp/fparkan-corpora.toml \
|
||||||
|
cargo xtask acceptance report --suite licensed --stage 5
|
||||||
```
|
```
|
||||||
|
|
||||||
## Contributing & Support
|
## Contributing & Support
|
||||||
|
|||||||
@@ -268,16 +268,16 @@ mod tests {
|
|||||||
RenderCase {
|
RenderCase {
|
||||||
root: "IS",
|
root: "IS",
|
||||||
mission: "MISSIONS/CAMPAIGN/CAMPAIGN.00/Mission.01/data.tma",
|
mission: "MISSIONS/CAMPAIGN/CAMPAIGN.00/Mission.01/data.tma",
|
||||||
expected: "{\"mission\":\"MISSIONS/CAMPAIGN/CAMPAIGN.00/Mission.01/data.tma\",\"objects\":33,\"frames\":1,\"tick\":1,\"draws\":33,\"captures\":1,\"last_capture_bytes\":810,\"hash\":\"8584c4307bc911fc82bf909018662f392f3982bf909018666298bde408fe4242\"}",
|
expected: "{\"mission\":\"MISSIONS/CAMPAIGN/CAMPAIGN.00/Mission.01/data.tma\",\"objects\":33,\"frames\":1,\"tick\":1,\"draws\":33,\"captures\":1,\"last_capture_bytes\":810,\"hash\":\"ca17cc76e55c45e83c1c9c1c088e84bf1a698be91a7730943210fe27596af841\"}",
|
||||||
},
|
},
|
||||||
RenderCase {
|
RenderCase {
|
||||||
root: "IS2",
|
root: "IS2",
|
||||||
mission: "MISSIONS/Campaign/CAMPAIGN.00/Mission.02/data.tma",
|
mission: "MISSIONS/Campaign/CAMPAIGN.00/Mission.02/data.tma",
|
||||||
expected: "{\"mission\":\"MISSIONS/Campaign/CAMPAIGN.00/Mission.02/data.tma\",\"objects\":10,\"frames\":1,\"tick\":1,\"draws\":10,\"captures\":1,\"last_capture_bytes\":235,\"hash\":\"c52267cb14f699cb73b958e46c99c23ec23e73b958e46c99b3650afbcce56291\"}",
|
expected: "{\"mission\":\"MISSIONS/Campaign/CAMPAIGN.00/Mission.02/data.tma\",\"objects\":10,\"frames\":1,\"tick\":1,\"draws\":10,\"captures\":1,\"last_capture_bytes\":235,\"hash\":\"5d720b3ab690076a398a79a404850bbeaee2e33811b5bb570ec8a96d4a7a2fc4\"}",
|
||||||
},
|
},
|
||||||
] {
|
] {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
run(&render_args(&workspace_root().join("testdata").join(case.root), case.mission)),
|
run(&render_args(&licensed_root(case.root), case.mission)),
|
||||||
Ok(case.expected.to_string())
|
Ok(case.expected.to_string())
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -313,11 +313,20 @@ mod tests {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn workspace_root() -> PathBuf {
|
fn licensed_root(name: &str) -> PathBuf {
|
||||||
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
let variable = match name {
|
||||||
.parent()
|
"IS" => "FPARKAN_CORPUS_PART1_ROOT",
|
||||||
.and_then(Path::parent)
|
"IS2" => "FPARKAN_CORPUS_PART2_ROOT",
|
||||||
.expect("workspace root")
|
_ => panic!("unknown licensed corpus part: {name}"),
|
||||||
.to_path_buf()
|
};
|
||||||
|
let root = std::env::var_os(variable)
|
||||||
|
.map(PathBuf::from)
|
||||||
|
.unwrap_or_else(|| panic!("{variable} is required for licensed corpus tests"));
|
||||||
|
assert!(
|
||||||
|
root.is_dir(),
|
||||||
|
"licensed corpus root is missing: {}",
|
||||||
|
root.display()
|
||||||
|
);
|
||||||
|
root
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -543,10 +543,20 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn fixture_root(part: &str) -> PathBuf {
|
fn fixture_root(part: &str) -> PathBuf {
|
||||||
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
let variable = match part {
|
||||||
.join("../..")
|
"IS" => "FPARKAN_CORPUS_PART1_ROOT",
|
||||||
.join("testdata")
|
"IS2" => "FPARKAN_CORPUS_PART2_ROOT",
|
||||||
.join(part)
|
_ => panic!("unknown licensed corpus part: {part}"),
|
||||||
|
};
|
||||||
|
let root = std::env::var_os(variable)
|
||||||
|
.map(PathBuf::from)
|
||||||
|
.unwrap_or_else(|| panic!("{variable} is required for licensed corpus tests"));
|
||||||
|
assert!(
|
||||||
|
root.is_dir(),
|
||||||
|
"licensed corpus root is missing: {}",
|
||||||
|
root.display()
|
||||||
|
);
|
||||||
|
root
|
||||||
}
|
}
|
||||||
|
|
||||||
fn repository_with_archives(
|
fn repository_with_archives(
|
||||||
|
|||||||
@@ -511,13 +511,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
#[ignore = "requires licensed corpus"]
|
#[ignore = "requires licensed corpus"]
|
||||||
fn report_for_testdata_roots() {
|
fn report_for_testdata_roots() {
|
||||||
let root = Path::new(env!("CARGO_MANIFEST_DIR"))
|
let root = licensed_root("IS");
|
||||||
.join("../..")
|
|
||||||
.join("testdata")
|
|
||||||
.join("IS");
|
|
||||||
if !root.is_dir() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let manifest = discover(&root, DiscoverOptions::default()).expect("manifest");
|
let manifest = discover(&root, DiscoverOptions::default()).expect("manifest");
|
||||||
let report = report(&root, &manifest).expect("report");
|
let report = report(&root, &manifest).expect("report");
|
||||||
assert!(report.files > 0);
|
assert!(report.files > 0);
|
||||||
@@ -892,10 +886,24 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn testdata_root(part: &str) -> PathBuf {
|
fn testdata_root(part: &str) -> PathBuf {
|
||||||
Path::new(env!("CARGO_MANIFEST_DIR"))
|
licensed_root(part)
|
||||||
.join("../..")
|
}
|
||||||
.join("testdata")
|
|
||||||
.join(part)
|
fn licensed_root(part: &str) -> PathBuf {
|
||||||
|
let variable = match part {
|
||||||
|
"IS" => "FPARKAN_CORPUS_PART1_ROOT",
|
||||||
|
"IS2" => "FPARKAN_CORPUS_PART2_ROOT",
|
||||||
|
_ => panic!("unknown licensed corpus part: {part}"),
|
||||||
|
};
|
||||||
|
let root = std::env::var_os(variable)
|
||||||
|
.map(PathBuf::from)
|
||||||
|
.unwrap_or_else(|| panic!("{variable} is required for licensed corpus tests"));
|
||||||
|
assert!(
|
||||||
|
root.is_dir(),
|
||||||
|
"licensed corpus root is missing: {}",
|
||||||
|
root.display()
|
||||||
|
);
|
||||||
|
root
|
||||||
}
|
}
|
||||||
|
|
||||||
fn assert_discovered_paths_stay_under_root(part: &str) {
|
fn assert_discovered_paths_stay_under_root(part: &str) {
|
||||||
|
|||||||
@@ -846,9 +846,7 @@ mod tests {
|
|||||||
#[ignore = "requires licensed corpus"]
|
#[ignore = "requires licensed corpus"]
|
||||||
fn licensed_corpus_fxid_exact_eof_and_distribution() {
|
fn licensed_corpus_fxid_exact_eof_and_distribution() {
|
||||||
for (corpus, expected_count) in [("IS", 923_usize), ("IS2", 1065_usize)] {
|
for (corpus, expected_count) in [("IS", 923_usize), ("IS2", 1065_usize)] {
|
||||||
let Some(root) = corpus_root(corpus) else {
|
let root = corpus_root(corpus);
|
||||||
continue;
|
|
||||||
};
|
|
||||||
let mut count = 0usize;
|
let mut count = 0usize;
|
||||||
let mut opcodes = BTreeMap::<FxOpcode, usize>::new();
|
let mut opcodes = BTreeMap::<FxOpcode, usize>::new();
|
||||||
let mut time_modes = BTreeMap::<u32, usize>::new();
|
let mut time_modes = BTreeMap::<u32, usize>::new();
|
||||||
@@ -898,9 +896,7 @@ mod tests {
|
|||||||
("IS", 923_usize, 467_usize, 10_553_431_922_547_057_702_u64),
|
("IS", 923_usize, 467_usize, 10_553_431_922_547_057_702_u64),
|
||||||
("IS2", 1065_usize, 532_usize, 9_217_284_592_334_143_531_u64),
|
("IS2", 1065_usize, 532_usize, 9_217_284_592_334_143_531_u64),
|
||||||
] {
|
] {
|
||||||
let Some(root) = corpus_root(corpus) else {
|
let root = corpus_root(corpus);
|
||||||
continue;
|
|
||||||
};
|
|
||||||
let mut count = 0usize;
|
let mut count = 0usize;
|
||||||
let mut emitting = 0usize;
|
let mut emitting = 0usize;
|
||||||
let mut hash = FNV_OFFSET;
|
let mut hash = FNV_OFFSET;
|
||||||
@@ -992,12 +988,21 @@ mod tests {
|
|||||||
dst[..len].copy_from_slice(&src[..len]);
|
dst[..len].copy_from_slice(&src[..len]);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn corpus_root(name: &str) -> Option<PathBuf> {
|
fn corpus_root(name: &str) -> PathBuf {
|
||||||
let root = Path::new(env!("CARGO_MANIFEST_DIR"))
|
let variable = match name {
|
||||||
.join("../..")
|
"IS" => "FPARKAN_CORPUS_PART1_ROOT",
|
||||||
.join("testdata")
|
"IS2" => "FPARKAN_CORPUS_PART2_ROOT",
|
||||||
.join(name);
|
_ => panic!("unknown licensed corpus part: {name}"),
|
||||||
root.is_dir().then_some(root)
|
};
|
||||||
|
let root = std::env::var_os(variable)
|
||||||
|
.map(PathBuf::from)
|
||||||
|
.unwrap_or_else(|| panic!("{variable} is required for licensed corpus tests"));
|
||||||
|
assert!(
|
||||||
|
root.is_dir(),
|
||||||
|
"licensed corpus root is missing: {}",
|
||||||
|
root.display()
|
||||||
|
);
|
||||||
|
root
|
||||||
}
|
}
|
||||||
|
|
||||||
fn files_under(root: &Path) -> Vec<PathBuf> {
|
fn files_under(root: &Path) -> Vec<PathBuf> {
|
||||||
|
|||||||
@@ -1129,9 +1129,7 @@ mod tests {
|
|||||||
("IS", 905_usize, 439_usize, 95_usize),
|
("IS", 905_usize, 439_usize, 95_usize),
|
||||||
("IS2", 1127_usize, 515_usize, 95_usize),
|
("IS2", 1127_usize, 515_usize, 95_usize),
|
||||||
] {
|
] {
|
||||||
let Some(root) = corpus_root(corpus) else {
|
let root = corpus_root(corpus);
|
||||||
continue;
|
|
||||||
};
|
|
||||||
let mut mat0_count = 0usize;
|
let mut mat0_count = 0usize;
|
||||||
let mut archive_wear_count = 0usize;
|
let mut archive_wear_count = 0usize;
|
||||||
let mut standalone_wear_count = 0usize;
|
let mut standalone_wear_count = 0usize;
|
||||||
@@ -1185,12 +1183,21 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn corpus_root(name: &str) -> Option<PathBuf> {
|
fn corpus_root(name: &str) -> PathBuf {
|
||||||
let root = Path::new(env!("CARGO_MANIFEST_DIR"))
|
let variable = match name {
|
||||||
.join("../..")
|
"IS" => "FPARKAN_CORPUS_PART1_ROOT",
|
||||||
.join("testdata")
|
"IS2" => "FPARKAN_CORPUS_PART2_ROOT",
|
||||||
.join(name);
|
_ => panic!("unknown licensed corpus part: {name}"),
|
||||||
root.is_dir().then_some(root)
|
};
|
||||||
|
let root = std::env::var_os(variable)
|
||||||
|
.map(PathBuf::from)
|
||||||
|
.unwrap_or_else(|| panic!("{variable} is required for licensed corpus tests"));
|
||||||
|
assert!(
|
||||||
|
root.is_dir(),
|
||||||
|
"licensed corpus root is missing: {}",
|
||||||
|
root.display()
|
||||||
|
);
|
||||||
|
root
|
||||||
}
|
}
|
||||||
|
|
||||||
fn files_under(root: &Path) -> Vec<PathBuf> {
|
fn files_under(root: &Path) -> Vec<PathBuf> {
|
||||||
|
|||||||
@@ -992,9 +992,7 @@ mod tests {
|
|||||||
("IS", 29_usize, 34_usize, 101_usize, 864_usize, 28_usize),
|
("IS", 29_usize, 34_usize, 101_usize, 864_usize, 28_usize),
|
||||||
("IS2", 31_usize, 61_usize, 91_usize, 885_usize, 41_usize),
|
("IS2", 31_usize, 61_usize, 91_usize, 885_usize, 41_usize),
|
||||||
] {
|
] {
|
||||||
let Some(root) = corpus_root(corpus) else {
|
let root = corpus_root(corpus);
|
||||||
continue;
|
|
||||||
};
|
|
||||||
let mut files = 0usize;
|
let mut files = 0usize;
|
||||||
let mut paths = 0usize;
|
let mut paths = 0usize;
|
||||||
let mut clans = 0usize;
|
let mut clans = 0usize;
|
||||||
@@ -1143,12 +1141,21 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn corpus_root(name: &str) -> Option<PathBuf> {
|
fn corpus_root(name: &str) -> PathBuf {
|
||||||
let root = Path::new(env!("CARGO_MANIFEST_DIR"))
|
let variable = match name {
|
||||||
.join("../..")
|
"IS" => "FPARKAN_CORPUS_PART1_ROOT",
|
||||||
.join("testdata")
|
"IS2" => "FPARKAN_CORPUS_PART2_ROOT",
|
||||||
.join(name);
|
_ => panic!("unknown licensed corpus part: {name}"),
|
||||||
root.is_dir().then_some(root)
|
};
|
||||||
|
let root = std::env::var_os(variable)
|
||||||
|
.map(PathBuf::from)
|
||||||
|
.unwrap_or_else(|| panic!("{variable} is required for licensed corpus tests"));
|
||||||
|
assert!(
|
||||||
|
root.is_dir(),
|
||||||
|
"licensed corpus root is missing: {}",
|
||||||
|
root.display()
|
||||||
|
);
|
||||||
|
root
|
||||||
}
|
}
|
||||||
|
|
||||||
fn files_under(root: &Path) -> Vec<PathBuf> {
|
fn files_under(root: &Path) -> Vec<PathBuf> {
|
||||||
|
|||||||
@@ -1239,9 +1239,7 @@ mod tests {
|
|||||||
#[ignore = "requires licensed corpus"]
|
#[ignore = "requires licensed corpus"]
|
||||||
fn licensed_corpus_msh_assets_validate() {
|
fn licensed_corpus_msh_assets_validate() {
|
||||||
for (corpus, expected) in [("IS", 435_usize), ("IS2", 511_usize)] {
|
for (corpus, expected) in [("IS", 435_usize), ("IS2", 511_usize)] {
|
||||||
let Some(root) = corpus_root(corpus) else {
|
let root = corpus_root(corpus);
|
||||||
continue;
|
|
||||||
};
|
|
||||||
let mut count = 0usize;
|
let mut count = 0usize;
|
||||||
for path in files_under(&root) {
|
for path in files_under(&root) {
|
||||||
let Ok(bytes) = std::fs::read(&path) else {
|
let Ok(bytes) = std::fs::read(&path) else {
|
||||||
@@ -1304,9 +1302,7 @@ mod tests {
|
|||||||
13_040_438_305_408_523_893_u64,
|
13_040_438_305_408_523_893_u64,
|
||||||
),
|
),
|
||||||
] {
|
] {
|
||||||
let Some(root) = corpus_root(corpus) else {
|
let root = corpus_root(corpus);
|
||||||
continue;
|
|
||||||
};
|
|
||||||
let mut models = 0usize;
|
let mut models = 0usize;
|
||||||
let mut animated_models = 0usize;
|
let mut animated_models = 0usize;
|
||||||
let mut node_samples = 0usize;
|
let mut node_samples = 0usize;
|
||||||
@@ -1725,12 +1721,21 @@ mod tests {
|
|||||||
name.len() >= 4 && name[name.len() - 4..].eq_ignore_ascii_case(b".msh")
|
name.len() >= 4 && name[name.len() - 4..].eq_ignore_ascii_case(b".msh")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn corpus_root(name: &str) -> Option<PathBuf> {
|
fn corpus_root(name: &str) -> PathBuf {
|
||||||
let root = Path::new(env!("CARGO_MANIFEST_DIR"))
|
let variable = match name {
|
||||||
.join("../..")
|
"IS" => "FPARKAN_CORPUS_PART1_ROOT",
|
||||||
.join("testdata")
|
"IS2" => "FPARKAN_CORPUS_PART2_ROOT",
|
||||||
.join(name);
|
_ => panic!("unknown licensed corpus part: {name}"),
|
||||||
root.is_dir().then_some(root)
|
};
|
||||||
|
let root = std::env::var_os(variable)
|
||||||
|
.map(PathBuf::from)
|
||||||
|
.unwrap_or_else(|| panic!("{variable} is required for licensed corpus tests"));
|
||||||
|
assert!(
|
||||||
|
root.is_dir(),
|
||||||
|
"licensed corpus root is missing: {}",
|
||||||
|
root.display()
|
||||||
|
);
|
||||||
|
root
|
||||||
}
|
}
|
||||||
|
|
||||||
fn files_under(root: &Path) -> Vec<PathBuf> {
|
fn files_under(root: &Path) -> Vec<PathBuf> {
|
||||||
|
|||||||
@@ -1948,10 +1948,14 @@ mod tests {
|
|||||||
expected_files: usize,
|
expected_files: usize,
|
||||||
expected_entries: usize,
|
expected_entries: usize,
|
||||||
) -> Result<CorpusGateResult, String> {
|
) -> Result<CorpusGateResult, String> {
|
||||||
let root = Path::new(env!("CARGO_MANIFEST_DIR"))
|
let variable = match name {
|
||||||
.join("../..")
|
"IS" => "FPARKAN_CORPUS_PART1_ROOT",
|
||||||
.join("testdata")
|
"IS2" => "FPARKAN_CORPUS_PART2_ROOT",
|
||||||
.join(name);
|
_ => return Err(format!("unknown licensed corpus part: {name}")),
|
||||||
|
};
|
||||||
|
let root = std::env::var_os(variable)
|
||||||
|
.map(PathBuf::from)
|
||||||
|
.ok_or_else(|| format!("{variable} is required for licensed corpus tests"))?;
|
||||||
if !root.is_dir() {
|
if !root.is_dir() {
|
||||||
return Err(format!(
|
return Err(format!(
|
||||||
"licensed corpus root is missing: {}",
|
"licensed corpus root is missing: {}",
|
||||||
|
|||||||
@@ -1768,8 +1768,9 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[ignore = "requires licensed corpus"]
|
||||||
fn resolves_known_part1_registry_cases() {
|
fn resolves_known_part1_registry_cases() {
|
||||||
let root = corpus_root("IS").expect("part 1 root");
|
let root = corpus_root("IS");
|
||||||
let vfs = Arc::new(DirectoryVfs::new(&root));
|
let vfs = Arc::new(DirectoryVfs::new(&root));
|
||||||
let repo = CachedResourceRepository::new(vfs.clone());
|
let repo = CachedResourceRepository::new(vfs.clone());
|
||||||
let cases = [
|
let cases = [
|
||||||
@@ -1799,9 +1800,10 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[ignore = "requires licensed corpus"]
|
||||||
fn resolves_some_registry_entries_in_both_corpora() {
|
fn resolves_some_registry_entries_in_both_corpora() {
|
||||||
for corpus in ["IS", "IS2"] {
|
for corpus in ["IS", "IS2"] {
|
||||||
let root = corpus_root(corpus).expect("corpus root");
|
let root = corpus_root(corpus);
|
||||||
let objects = std::fs::read(root.join("objects.rlb")).expect("objects.rlb");
|
let objects = std::fs::read(root.join("objects.rlb")).expect("objects.rlb");
|
||||||
let document = fparkan_nres::decode(
|
let document = fparkan_nres::decode(
|
||||||
Arc::from(objects.into_boxed_slice()),
|
Arc::from(objects.into_boxed_slice()),
|
||||||
@@ -1830,7 +1832,7 @@ mod tests {
|
|||||||
fn licensed_corpora_unit_dat_parse_counts() {
|
fn licensed_corpora_unit_dat_parse_counts() {
|
||||||
let cases = [("IS", 425, 5_219), ("IS2", 676, 8_145)];
|
let cases = [("IS", 425, 5_219), ("IS2", 676, 8_145)];
|
||||||
for (corpus, expected_files, expected_records) in cases {
|
for (corpus, expected_files, expected_records) in cases {
|
||||||
let root = corpus_root(corpus).expect("corpus root");
|
let root = corpus_root(corpus);
|
||||||
let mut dat_paths = Vec::new();
|
let mut dat_paths = Vec::new();
|
||||||
collect_unit_dat_files(&root, &mut dat_paths);
|
collect_unit_dat_files(&root, &mut dat_paths);
|
||||||
dat_paths.sort();
|
dat_paths.sort();
|
||||||
@@ -1863,7 +1865,7 @@ mod tests {
|
|||||||
#[ignore = "requires licensed corpus"]
|
#[ignore = "requires licensed corpus"]
|
||||||
fn licensed_corpora_registry_payloads_are_record_aligned() {
|
fn licensed_corpora_registry_payloads_are_record_aligned() {
|
||||||
for corpus in ["IS", "IS2"] {
|
for corpus in ["IS", "IS2"] {
|
||||||
let root = corpus_root(corpus).expect("corpus root");
|
let root = corpus_root(corpus);
|
||||||
let objects = std::fs::read(root.join("objects.rlb")).expect("objects.rlb");
|
let objects = std::fs::read(root.join("objects.rlb")).expect("objects.rlb");
|
||||||
let document = fparkan_nres::decode(
|
let document = fparkan_nres::decode(
|
||||||
Arc::from(objects.into_boxed_slice()),
|
Arc::from(objects.into_boxed_slice()),
|
||||||
@@ -1909,12 +1911,21 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn corpus_root(name: &str) -> Option<std::path::PathBuf> {
|
fn corpus_root(name: &str) -> std::path::PathBuf {
|
||||||
let root = Path::new(env!("CARGO_MANIFEST_DIR"))
|
let variable = match name {
|
||||||
.join("../..")
|
"IS" => "FPARKAN_CORPUS_PART1_ROOT",
|
||||||
.join("testdata")
|
"IS2" => "FPARKAN_CORPUS_PART2_ROOT",
|
||||||
.join(name);
|
_ => panic!("unknown licensed corpus part: {name}"),
|
||||||
root.is_dir().then_some(root)
|
};
|
||||||
|
let root = std::env::var_os(variable)
|
||||||
|
.map(std::path::PathBuf::from)
|
||||||
|
.unwrap_or_else(|| panic!("{variable} is required for licensed corpus tests"));
|
||||||
|
assert!(
|
||||||
|
root.is_dir(),
|
||||||
|
"licensed corpus root is missing: {}",
|
||||||
|
root.display()
|
||||||
|
);
|
||||||
|
root
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generated_acyclic_graph(
|
fn generated_acyclic_graph(
|
||||||
|
|||||||
@@ -666,7 +666,7 @@ fn c_name_bytes(raw: &[u8; 12]) -> &[u8] {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use fparkan_vfs::{DirectoryVfs, MemoryVfs};
|
use fparkan_vfs::{DirectoryVfs, MemoryVfs};
|
||||||
use std::path::Path;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn cached_repository_reads_synthetic_nres() {
|
fn cached_repository_reads_synthetic_nres() {
|
||||||
@@ -937,10 +937,14 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn licensed_repository_gate(corpus: &str) -> Result<(), String> {
|
fn licensed_repository_gate(corpus: &str) -> Result<(), String> {
|
||||||
let root = Path::new(env!("CARGO_MANIFEST_DIR"))
|
let variable = match corpus {
|
||||||
.join("../..")
|
"IS" => "FPARKAN_CORPUS_PART1_ROOT",
|
||||||
.join("testdata")
|
"IS2" => "FPARKAN_CORPUS_PART2_ROOT",
|
||||||
.join(corpus);
|
_ => return Err(format!("unknown licensed corpus part: {corpus}")),
|
||||||
|
};
|
||||||
|
let root = std::env::var_os(variable)
|
||||||
|
.map(PathBuf::from)
|
||||||
|
.ok_or_else(|| format!("{variable} is required for licensed corpus tests"))?;
|
||||||
if !root.is_dir() {
|
if !root.is_dir() {
|
||||||
return Err(format!(
|
return Err(format!(
|
||||||
"licensed corpus root is missing: {}",
|
"licensed corpus root is missing: {}",
|
||||||
|
|||||||
@@ -2066,10 +2066,14 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn corpus_files(name: &str) -> Result<Vec<PathBuf>, String> {
|
fn corpus_files(name: &str) -> Result<Vec<PathBuf>, String> {
|
||||||
let root = Path::new(env!("CARGO_MANIFEST_DIR"))
|
let variable = match name {
|
||||||
.join("../..")
|
"IS" => "FPARKAN_CORPUS_PART1_ROOT",
|
||||||
.join("testdata")
|
"IS2" => "FPARKAN_CORPUS_PART2_ROOT",
|
||||||
.join(name);
|
_ => return Err(format!("unknown licensed corpus part: {name}")),
|
||||||
|
};
|
||||||
|
let root = std::env::var_os(variable)
|
||||||
|
.map(PathBuf::from)
|
||||||
|
.ok_or_else(|| format!("{variable} is required for licensed corpus tests"))?;
|
||||||
if !root.is_dir() {
|
if !root.is_dir() {
|
||||||
return Err(format!(
|
return Err(format!(
|
||||||
"licensed corpus root is missing: {}",
|
"licensed corpus root is missing: {}",
|
||||||
|
|||||||
@@ -697,7 +697,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
#[ignore = "requires licensed corpus"]
|
#[ignore = "requires licensed corpus"]
|
||||||
fn load_trace_records_preparation_before_registration_and_raw_transforms() {
|
fn load_trace_records_preparation_before_registration_and_raw_transforms() {
|
||||||
let root = workspace_root().join("testdata").join("IS");
|
let root = licensed_root("IS");
|
||||||
let vfs: Arc<dyn Vfs> = Arc::new(DirectoryVfs::new(&root));
|
let vfs: Arc<dyn Vfs> = Arc::new(DirectoryVfs::new(&root));
|
||||||
let mut engine = create(
|
let mut engine = create(
|
||||||
EngineConfig {
|
EngineConfig {
|
||||||
@@ -739,7 +739,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
#[ignore = "requires licensed corpus"]
|
#[ignore = "requires licensed corpus"]
|
||||||
fn missing_map_and_missing_reachable_resource_fail_before_registration() {
|
fn missing_map_and_missing_reachable_resource_fail_before_registration() {
|
||||||
let root = workspace_root().join("testdata").join("IS");
|
let root = licensed_root("IS");
|
||||||
for (denied, mission) in [
|
for (denied, mission) in [
|
||||||
(
|
(
|
||||||
DenyRule::Suffix("Land.map"),
|
DenyRule::Suffix("Land.map"),
|
||||||
@@ -783,7 +783,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
#[ignore = "requires licensed corpus"]
|
#[ignore = "requires licensed corpus"]
|
||||||
fn registration_phase_failure_uses_normal_teardown_and_keeps_engine_world() {
|
fn registration_phase_failure_uses_normal_teardown_and_keeps_engine_world() {
|
||||||
let root = workspace_root().join("testdata").join("IS");
|
let root = licensed_root("IS");
|
||||||
let vfs: Arc<dyn Vfs> = Arc::new(DirectoryVfs::new(root));
|
let vfs: Arc<dyn Vfs> = Arc::new(DirectoryVfs::new(root));
|
||||||
let mut engine = create(
|
let mut engine = create(
|
||||||
EngineConfig {
|
EngineConfig {
|
||||||
@@ -827,9 +827,9 @@ mod tests {
|
|||||||
mission: "MISSIONS/CAMPAIGN/CAMPAIGN.00/Mission.01/data.tma",
|
mission: "MISSIONS/CAMPAIGN/CAMPAIGN.00/Mission.01/data.tma",
|
||||||
object_count: 33,
|
object_count: 33,
|
||||||
expected_hash: [
|
expected_hash: [
|
||||||
0x19, 0xdc, 0xd3, 0x9b, 0x35, 0xad, 0x90, 0x6c, 0x92, 0x2d, 0x83, 0x7b, 0x7a,
|
0xc7, 0xb0, 0x6e, 0x0a, 0x31, 0x1f, 0x5d, 0x8c, 0xde, 0x64, 0xa5, 0x33, 0x1f,
|
||||||
0xb3, 0xa6, 0x15, 0xa6, 0x15, 0x92, 0x2d, 0x83, 0x7b, 0x7a, 0xb3, 0xe9, 0xcd,
|
0x2c, 0xd0, 0x2c, 0x21, 0x44, 0x2f, 0x34, 0x5d, 0x16, 0xe8, 0x94, 0xaf, 0xa2,
|
||||||
0x9a, 0x56, 0x48, 0xb6, 0x0c, 0xee,
|
0x2b, 0xa9, 0xd4, 0x24, 0xd2, 0xf9,
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
HeadlessCase {
|
HeadlessCase {
|
||||||
@@ -837,9 +837,9 @@ mod tests {
|
|||||||
mission: "MISSIONS/Campaign/CAMPAIGN.00/Mission.02/data.tma",
|
mission: "MISSIONS/Campaign/CAMPAIGN.00/Mission.02/data.tma",
|
||||||
object_count: 10,
|
object_count: 10,
|
||||||
expected_hash: [
|
expected_hash: [
|
||||||
0x59, 0x6e, 0x88, 0xcc, 0xd0, 0x3a, 0xd9, 0x68, 0x1b, 0x2d, 0xcb, 0x0d, 0x91,
|
0x3c, 0xe5, 0xa6, 0x39, 0x47, 0x86, 0x76, 0xe1, 0xb2, 0x1a, 0x8e, 0x96, 0x3d,
|
||||||
0x19, 0x5a, 0x27, 0x5a, 0x27, 0x1b, 0x2d, 0xcb, 0x0d, 0x91, 0x19, 0x44, 0x66,
|
0x60, 0x6e, 0xc6, 0x8c, 0xe2, 0x28, 0x4f, 0x57, 0xd9, 0xe1, 0xe4, 0xb5, 0x95,
|
||||||
0x68, 0x9d, 0x6c, 0xb4, 0x2c, 0x37,
|
0xdf, 0x88, 0xd3, 0x2f, 0x4a, 0x4d,
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
] {
|
] {
|
||||||
@@ -855,8 +855,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
#[ignore = "requires licensed corpus"]
|
#[ignore = "requires licensed corpus"]
|
||||||
fn licensed_corpora_load_all_mission_foundations() {
|
fn licensed_corpora_load_all_mission_foundations() {
|
||||||
let root = workspace_root();
|
let part1 = load_all(&licensed_root("IS"));
|
||||||
let part1 = load_all(&root.join("testdata").join("IS"));
|
|
||||||
assert_eq!(part1.missions, 29);
|
assert_eq!(part1.missions, 29);
|
||||||
assert_eq!(part1.paths, 34);
|
assert_eq!(part1.paths, 34);
|
||||||
assert_eq!(part1.clans, 101);
|
assert_eq!(part1.clans, 101);
|
||||||
@@ -876,7 +875,7 @@ mod tests {
|
|||||||
assert_eq!(part1.texture_requests, part1.texture_resolved);
|
assert_eq!(part1.texture_requests, part1.texture_resolved);
|
||||||
assert_eq!(part1.lightmap_requests, part1.lightmap_resolved);
|
assert_eq!(part1.lightmap_requests, part1.lightmap_resolved);
|
||||||
|
|
||||||
let part2 = load_all(&root.join("testdata").join("IS2"));
|
let part2 = load_all(&licensed_root("IS2"));
|
||||||
assert_eq!(part2.missions, 31);
|
assert_eq!(part2.missions, 31);
|
||||||
assert_eq!(part2.paths, 61);
|
assert_eq!(part2.paths, 61);
|
||||||
assert_eq!(part2.clans, 91);
|
assert_eq!(part2.clans, 91);
|
||||||
@@ -928,7 +927,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn run_headless_case(case: HeadlessCase) -> WorldSnapshot {
|
fn run_headless_case(case: HeadlessCase) -> WorldSnapshot {
|
||||||
let root = workspace_root().join("testdata").join(case.root);
|
let root = licensed_root(case.root);
|
||||||
let vfs: Arc<dyn Vfs> = Arc::new(DirectoryVfs::new(root));
|
let vfs: Arc<dyn Vfs> = Arc::new(DirectoryVfs::new(root));
|
||||||
let mut engine = create(
|
let mut engine = create(
|
||||||
EngineConfig {
|
EngineConfig {
|
||||||
@@ -1048,12 +1047,21 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn workspace_root() -> PathBuf {
|
fn licensed_root(name: &str) -> PathBuf {
|
||||||
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
let variable = match name {
|
||||||
.parent()
|
"IS" => "FPARKAN_CORPUS_PART1_ROOT",
|
||||||
.and_then(Path::parent)
|
"IS2" => "FPARKAN_CORPUS_PART2_ROOT",
|
||||||
.expect("workspace root")
|
_ => panic!("unknown licensed corpus part: {name}"),
|
||||||
.to_path_buf()
|
};
|
||||||
|
let root = std::env::var_os(variable)
|
||||||
|
.map(PathBuf::from)
|
||||||
|
.unwrap_or_else(|| panic!("{variable} is required for licensed corpus tests"));
|
||||||
|
assert!(
|
||||||
|
root.is_dir(),
|
||||||
|
"licensed corpus root is missing: {}",
|
||||||
|
root.display()
|
||||||
|
);
|
||||||
|
root
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
|
|||||||
@@ -1494,9 +1494,7 @@ Generator 1
|
|||||||
("IS", 33_usize, 299_450_usize, 275_882_usize),
|
("IS", 33_usize, 299_450_usize, 275_882_usize),
|
||||||
("IS2", 32_usize, 188_024_usize, 184_454_usize),
|
("IS2", 32_usize, 188_024_usize, 184_454_usize),
|
||||||
] {
|
] {
|
||||||
let Some(root) = corpus_root(corpus) else {
|
let root = corpus_root(corpus);
|
||||||
continue;
|
|
||||||
};
|
|
||||||
let mut files = 0usize;
|
let mut files = 0usize;
|
||||||
let mut vertices = 0usize;
|
let mut vertices = 0usize;
|
||||||
let mut faces = 0usize;
|
let mut faces = 0usize;
|
||||||
@@ -1540,9 +1538,7 @@ Generator 1
|
|||||||
#[ignore = "requires licensed corpus"]
|
#[ignore = "requires licensed corpus"]
|
||||||
fn licensed_corpus_build_dat_validate() {
|
fn licensed_corpus_build_dat_validate() {
|
||||||
for (corpus, expected_ai_prefix) in [("IS", false), ("IS2", true)] {
|
for (corpus, expected_ai_prefix) in [("IS", false), ("IS2", true)] {
|
||||||
let Some(root) = corpus_root(corpus) else {
|
let root = corpus_root(corpus);
|
||||||
continue;
|
|
||||||
};
|
|
||||||
let path = root.join("BuildDat.lst");
|
let path = root.join("BuildDat.lst");
|
||||||
let bytes = std::fs::read(&path).expect("read BuildDat.lst");
|
let bytes = std::fs::read(&path).expect("read BuildDat.lst");
|
||||||
let categories =
|
let categories =
|
||||||
@@ -1591,9 +1587,7 @@ Generator 1
|
|||||||
("IS", 33_usize, 34_662_usize, 197_698_usize, 20_usize),
|
("IS", 33_usize, 34_662_usize, 197_698_usize, 20_usize),
|
||||||
("IS2", 32_usize, 18_984_usize, 114_968_usize, 14_usize),
|
("IS2", 32_usize, 18_984_usize, 114_968_usize, 14_usize),
|
||||||
] {
|
] {
|
||||||
let Some(root) = corpus_root(corpus) else {
|
let root = corpus_root(corpus);
|
||||||
continue;
|
|
||||||
};
|
|
||||||
let mut files = 0usize;
|
let mut files = 0usize;
|
||||||
let mut areals = 0usize;
|
let mut areals = 0usize;
|
||||||
let mut vertices = 0usize;
|
let mut vertices = 0usize;
|
||||||
@@ -1883,12 +1877,21 @@ Generator 1
|
|||||||
out.extend_from_slice(&value.to_le_bytes());
|
out.extend_from_slice(&value.to_le_bytes());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn corpus_root(name: &str) -> Option<PathBuf> {
|
fn corpus_root(name: &str) -> PathBuf {
|
||||||
let root = Path::new(env!("CARGO_MANIFEST_DIR"))
|
let variable = match name {
|
||||||
.join("../..")
|
"IS" => "FPARKAN_CORPUS_PART1_ROOT",
|
||||||
.join("testdata")
|
"IS2" => "FPARKAN_CORPUS_PART2_ROOT",
|
||||||
.join(name);
|
_ => panic!("unknown licensed corpus part: {name}"),
|
||||||
root.is_dir().then_some(root)
|
};
|
||||||
|
let root = std::env::var_os(variable)
|
||||||
|
.map(PathBuf::from)
|
||||||
|
.unwrap_or_else(|| panic!("{variable} is required for licensed corpus tests"));
|
||||||
|
assert!(
|
||||||
|
root.is_dir(),
|
||||||
|
"licensed corpus root is missing: {}",
|
||||||
|
root.display()
|
||||||
|
);
|
||||||
|
root
|
||||||
}
|
}
|
||||||
|
|
||||||
fn files_under(root: &Path) -> Vec<PathBuf> {
|
fn files_under(root: &Path) -> Vec<PathBuf> {
|
||||||
|
|||||||
@@ -800,9 +800,7 @@ mod tests {
|
|||||||
("IS", 33_usize, 34_662_usize),
|
("IS", 33_usize, 34_662_usize),
|
||||||
("IS2", 32_usize, 18_984_usize),
|
("IS2", 32_usize, 18_984_usize),
|
||||||
] {
|
] {
|
||||||
let Some(root) = corpus_root(corpus) else {
|
let root = corpus_root(corpus);
|
||||||
continue;
|
|
||||||
};
|
|
||||||
let mut files = 0usize;
|
let mut files = 0usize;
|
||||||
let mut areals = 0usize;
|
let mut areals = 0usize;
|
||||||
let mut located_centers = 0usize;
|
let mut located_centers = 0usize;
|
||||||
@@ -856,9 +854,7 @@ mod tests {
|
|||||||
("IS", 33_usize, 275_882_usize),
|
("IS", 33_usize, 275_882_usize),
|
||||||
("IS2", 32_usize, 184_454_usize),
|
("IS2", 32_usize, 184_454_usize),
|
||||||
] {
|
] {
|
||||||
let Some(root) = corpus_root(corpus) else {
|
let root = corpus_root(corpus);
|
||||||
continue;
|
|
||||||
};
|
|
||||||
let mut files = 0usize;
|
let mut files = 0usize;
|
||||||
let mut faces = 0usize;
|
let mut faces = 0usize;
|
||||||
for path in files_under(&root) {
|
for path in files_under(&root) {
|
||||||
@@ -1051,12 +1047,21 @@ mod tests {
|
|||||||
vertices.first().copied()
|
vertices.first().copied()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn corpus_root(name: &str) -> Option<PathBuf> {
|
fn corpus_root(name: &str) -> PathBuf {
|
||||||
let root = Path::new(env!("CARGO_MANIFEST_DIR"))
|
let variable = match name {
|
||||||
.join("../..")
|
"IS" => "FPARKAN_CORPUS_PART1_ROOT",
|
||||||
.join("testdata")
|
"IS2" => "FPARKAN_CORPUS_PART2_ROOT",
|
||||||
.join(name);
|
_ => panic!("unknown licensed corpus part: {name}"),
|
||||||
root.is_dir().then_some(root)
|
};
|
||||||
|
let root = std::env::var_os(variable)
|
||||||
|
.map(PathBuf::from)
|
||||||
|
.unwrap_or_else(|| panic!("{variable} is required for licensed corpus tests"));
|
||||||
|
assert!(
|
||||||
|
root.is_dir(),
|
||||||
|
"licensed corpus root is missing: {}",
|
||||||
|
root.display()
|
||||||
|
);
|
||||||
|
root
|
||||||
}
|
}
|
||||||
|
|
||||||
fn files_under(root: &Path) -> Vec<PathBuf> {
|
fn files_under(root: &Path) -> Vec<PathBuf> {
|
||||||
|
|||||||
@@ -1074,9 +1074,7 @@ mod tests {
|
|||||||
#[ignore = "requires licensed corpus"]
|
#[ignore = "requires licensed corpus"]
|
||||||
fn licensed_corpus_texm_assets_validate_and_decode_mip0() {
|
fn licensed_corpus_texm_assets_validate_and_decode_mip0() {
|
||||||
for (corpus, expected) in [("IS", 518_usize), ("IS2", 631_usize)] {
|
for (corpus, expected) in [("IS", 518_usize), ("IS2", 631_usize)] {
|
||||||
let Some(root) = corpus_root(corpus) else {
|
let root = corpus_root(corpus);
|
||||||
continue;
|
|
||||||
};
|
|
||||||
let mut count = 0usize;
|
let mut count = 0usize;
|
||||||
for path in files_under(&root) {
|
for path in files_under(&root) {
|
||||||
let Ok(bytes) = std::fs::read(&path) else {
|
let Ok(bytes) = std::fs::read(&path) else {
|
||||||
@@ -1158,12 +1156,21 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn corpus_root(name: &str) -> Option<PathBuf> {
|
fn corpus_root(name: &str) -> PathBuf {
|
||||||
let root = Path::new(env!("CARGO_MANIFEST_DIR"))
|
let variable = match name {
|
||||||
.join("../..")
|
"IS" => "FPARKAN_CORPUS_PART1_ROOT",
|
||||||
.join("testdata")
|
"IS2" => "FPARKAN_CORPUS_PART2_ROOT",
|
||||||
.join(name);
|
_ => panic!("unknown licensed corpus part: {name}"),
|
||||||
root.is_dir().then_some(root)
|
};
|
||||||
|
let root = std::env::var_os(variable)
|
||||||
|
.map(PathBuf::from)
|
||||||
|
.unwrap_or_else(|| panic!("{variable} is required for licensed corpus tests"));
|
||||||
|
assert!(
|
||||||
|
root.is_dir(),
|
||||||
|
"licensed corpus root is missing: {}",
|
||||||
|
root.display()
|
||||||
|
);
|
||||||
|
root
|
||||||
}
|
}
|
||||||
|
|
||||||
fn files_under(root: &Path) -> Vec<PathBuf> {
|
fn files_under(root: &Path) -> Vec<PathBuf> {
|
||||||
|
|||||||
+231
-19
@@ -10,6 +10,10 @@ use std::fs;
|
|||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
|
|
||||||
|
const CORPORA_MANIFEST_ENV: &str = "FPARKAN_CORPORA_MANIFEST";
|
||||||
|
const PART1_ROOT_ENV: &str = "FPARKAN_CORPUS_PART1_ROOT";
|
||||||
|
const PART2_ROOT_ENV: &str = "FPARKAN_CORPUS_PART2_ROOT";
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let args = std::env::args().skip(1).collect::<Vec<_>>();
|
let args = std::env::args().skip(1).collect::<Vec<_>>();
|
||||||
let code = match run(&args) {
|
let code = match run(&args) {
|
||||||
@@ -46,12 +50,12 @@ fn run(args: &[String]) -> Result<(), String> {
|
|||||||
}
|
}
|
||||||
[cmd, suite, rest @ ..] if cmd == "test" && suite == "synthetic" => {
|
[cmd, suite, rest @ ..] if cmd == "test" && suite == "synthetic" => {
|
||||||
let options = parse_test_options(rest, PathBuf::from("testdata"))?;
|
let options = parse_test_options(rest, PathBuf::from("testdata"))?;
|
||||||
run_stage_tests(options.stage, TestSuite::Synthetic)
|
run_stage_tests(options.stage, TestSuite::Synthetic, None)
|
||||||
}
|
}
|
||||||
[cmd, suite, rest @ ..] if cmd == "test" && suite == "licensed" => {
|
[cmd, suite, rest @ ..] if cmd == "test" && suite == "licensed" => {
|
||||||
let options = parse_test_options(rest, PathBuf::from("testdata"))?;
|
let options = parse_test_options(rest, PathBuf::from("testdata"))?;
|
||||||
validate_licensed_root(&options.root)?;
|
let roots = load_licensed_roots(options.manifest.as_deref())?;
|
||||||
run_stage_tests(options.stage, TestSuite::Licensed)
|
run_stage_tests(options.stage, TestSuite::Licensed, Some(&roots))
|
||||||
}
|
}
|
||||||
[cmd, subcmd, rest @ ..] if cmd == "corpus" && subcmd == "baseline" => {
|
[cmd, subcmd, rest @ ..] if cmd == "corpus" && subcmd == "baseline" => {
|
||||||
let root = parse_root(rest)?;
|
let root = parse_root(rest)?;
|
||||||
@@ -62,7 +66,7 @@ fn run(args: &[String]) -> Result<(), String> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
_ => Err(
|
_ => Err(
|
||||||
"usage: cargo xtask ci | policy | acceptance report --suite synthetic|licensed [--stage 0..5|all] [--root testdata] [--out <path>] | acceptance audit [--roadmap <path>] [--coverage <path>] [--out <path>] [--strict] | package --target <triple> --app viewer|game|headless|cli | test synthetic|licensed [--stage 0..5|all] [--root testdata] | corpus baseline --root <path>"
|
"usage: cargo xtask ci | policy | acceptance report --suite synthetic|licensed [--stage 0..5|all] [--manifest corpora.toml] [--out <path>] | acceptance audit [--roadmap <path>] [--coverage <path>] [--out <path>] [--strict] | package --target <triple> --app viewer|game|headless|cli | test synthetic|licensed [--stage 0..5|all] [--manifest corpora.toml] | corpus baseline --root <path>"
|
||||||
.to_string(),
|
.to_string(),
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
@@ -94,6 +98,23 @@ fn cargo_owned(args: &[String]) -> Result<(), String> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn cargo_with_env(args: &[&str], envs: &[(&str, &Path)]) -> Result<(), String> {
|
||||||
|
let cargo = std::env::var_os("CARGO").unwrap_or_else(|| "cargo".into());
|
||||||
|
let mut command = Command::new(cargo);
|
||||||
|
command.args(args);
|
||||||
|
for (key, value) in envs {
|
||||||
|
command.env(key, value);
|
||||||
|
}
|
||||||
|
let status = command
|
||||||
|
.status()
|
||||||
|
.map_err(|err| format!("failed to run cargo: {err}"))?;
|
||||||
|
if status.success() {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(format!("cargo exited with {status}"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn clippy_rustup(args: &[&str]) -> Result<(), String> {
|
fn clippy_rustup(args: &[&str]) -> Result<(), String> {
|
||||||
let rustup = std::env::var_os("RUSTUP").unwrap_or_else(|| "rustup".into());
|
let rustup = std::env::var_os("RUSTUP").unwrap_or_else(|| "rustup".into());
|
||||||
let status = Command::new(rustup)
|
let status = Command::new(rustup)
|
||||||
@@ -153,19 +174,124 @@ fn collect_rust_files(dir: &Path, out: &mut Vec<PathBuf>) -> Result<(), String>
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn validate_licensed_root(root: &Path) -> Result<(), String> {
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
for part in ["IS", "IS2"] {
|
struct LicensedCorpusRoots {
|
||||||
let part_root = root.join(part);
|
part1: PathBuf,
|
||||||
if !part_root.is_dir() {
|
part2: PathBuf,
|
||||||
return Err(format!(
|
|
||||||
"licensed corpus part is missing: {}",
|
|
||||||
part_root.display()
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl LicensedCorpusRoots {
|
||||||
|
fn envs(&self) -> [(&str, &Path); 2] {
|
||||||
|
[
|
||||||
|
(PART1_ROOT_ENV, self.part1.as_path()),
|
||||||
|
(PART2_ROOT_ENV, self.part2.as_path()),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_licensed_roots(manifest: Option<&Path>) -> Result<LicensedCorpusRoots, String> {
|
||||||
|
let manifest = manifest
|
||||||
|
.map(Path::to_path_buf)
|
||||||
|
.or_else(|| std::env::var_os(CORPORA_MANIFEST_ENV).map(PathBuf::from))
|
||||||
|
.ok_or_else(|| {
|
||||||
|
format!(
|
||||||
|
"licensed tests require --manifest or {CORPORA_MANIFEST_ENV}=<absolute corpora.toml>"
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
parse_licensed_manifest(&manifest)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_licensed_manifest(path: &Path) -> Result<LicensedCorpusRoots, String> {
|
||||||
|
let text = fs::read_to_string(path).map_err(|err| format!("{}: {err}", path.display()))?;
|
||||||
|
let mut part1 = None;
|
||||||
|
let mut part2 = None;
|
||||||
|
let mut current_kind: Option<String> = None;
|
||||||
|
let mut current_root: Option<PathBuf> = None;
|
||||||
|
|
||||||
|
for raw_line in text.lines() {
|
||||||
|
let line = raw_line.split('#').next().unwrap_or_default().trim();
|
||||||
|
if line.is_empty() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if line == "[[corpus]]" {
|
||||||
|
flush_manifest_entry(&mut part1, &mut part2, &mut current_kind, &mut current_root)?;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let Some((key, value)) = line.split_once('=') else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let key = key.trim();
|
||||||
|
match key {
|
||||||
|
"kind" => current_kind = Some(parse_manifest_string(value.trim())?),
|
||||||
|
"root" => current_root = Some(PathBuf::from(parse_manifest_string(value.trim())?)),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
flush_manifest_entry(&mut part1, &mut part2, &mut current_kind, &mut current_root)?;
|
||||||
|
|
||||||
|
let roots = LicensedCorpusRoots {
|
||||||
|
part1: part1.ok_or_else(|| "licensed manifest is missing kind = \"part1\"".to_string())?,
|
||||||
|
part2: part2.ok_or_else(|| "licensed manifest is missing kind = \"part2\"".to_string())?,
|
||||||
|
};
|
||||||
|
validate_licensed_part("part1", &roots.part1)?;
|
||||||
|
validate_licensed_part("part2", &roots.part2)?;
|
||||||
|
Ok(roots)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush_manifest_entry(
|
||||||
|
part1: &mut Option<PathBuf>,
|
||||||
|
part2: &mut Option<PathBuf>,
|
||||||
|
current_kind: &mut Option<String>,
|
||||||
|
current_root: &mut Option<PathBuf>,
|
||||||
|
) -> Result<(), String> {
|
||||||
|
let Some(kind) = current_kind.take() else {
|
||||||
|
*current_root = None;
|
||||||
|
return Ok(());
|
||||||
|
};
|
||||||
|
let root = current_root
|
||||||
|
.take()
|
||||||
|
.ok_or_else(|| format!("licensed manifest entry {kind} is missing root"))?;
|
||||||
|
match kind.as_str() {
|
||||||
|
"part1" => assign_manifest_root(part1, root, "part1"),
|
||||||
|
"part2" => assign_manifest_root(part2, root, "part2"),
|
||||||
|
_ => Ok(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn assign_manifest_root(
|
||||||
|
target: &mut Option<PathBuf>,
|
||||||
|
root: PathBuf,
|
||||||
|
kind: &str,
|
||||||
|
) -> Result<(), String> {
|
||||||
|
if target.replace(root).is_some() {
|
||||||
|
return Err(format!("licensed manifest contains duplicate {kind} root"));
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_manifest_string(value: &str) -> Result<String, String> {
|
||||||
|
let trimmed = value.trim();
|
||||||
|
if let Some(quoted) = trimmed
|
||||||
|
.strip_prefix('"')
|
||||||
|
.and_then(|value| value.strip_suffix('"'))
|
||||||
|
{
|
||||||
|
Ok(quoted.to_string())
|
||||||
|
} else {
|
||||||
|
Err(format!("manifest value must be a quoted string: {trimmed}"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn validate_licensed_part(kind: &str, root: &Path) -> Result<(), String> {
|
||||||
|
if root.is_dir() {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(format!(
|
||||||
|
"licensed corpus {kind} root is missing: {}",
|
||||||
|
root.display()
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn parse_root(args: &[String]) -> Result<PathBuf, String> {
|
fn parse_root(args: &[String]) -> Result<PathBuf, String> {
|
||||||
let mut iter = args.iter();
|
let mut iter = args.iter();
|
||||||
while let Some(arg) = iter.next() {
|
while let Some(arg) = iter.next() {
|
||||||
@@ -717,6 +843,7 @@ impl fmt::Display for Stage {
|
|||||||
struct TestOptions {
|
struct TestOptions {
|
||||||
stage: Stage,
|
stage: Stage,
|
||||||
root: PathBuf,
|
root: PathBuf,
|
||||||
|
manifest: Option<PathBuf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
@@ -747,6 +874,7 @@ struct AcceptanceOptions {
|
|||||||
suite: TestSuite,
|
suite: TestSuite,
|
||||||
stage: Stage,
|
stage: Stage,
|
||||||
root: PathBuf,
|
root: PathBuf,
|
||||||
|
manifest: Option<PathBuf>,
|
||||||
out: PathBuf,
|
out: PathBuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -762,6 +890,7 @@ fn parse_test_options(args: &[String], default_root: PathBuf) -> Result<TestOpti
|
|||||||
let mut options = TestOptions {
|
let mut options = TestOptions {
|
||||||
stage: Stage::All,
|
stage: Stage::All,
|
||||||
root: default_root,
|
root: default_root,
|
||||||
|
manifest: None,
|
||||||
};
|
};
|
||||||
let mut iter = args.iter();
|
let mut iter = args.iter();
|
||||||
while let Some(arg) = iter.next() {
|
while let Some(arg) = iter.next() {
|
||||||
@@ -778,6 +907,12 @@ fn parse_test_options(args: &[String], default_root: PathBuf) -> Result<TestOpti
|
|||||||
.ok_or_else(|| "--root requires a path".to_string())?;
|
.ok_or_else(|| "--root requires a path".to_string())?;
|
||||||
options.root = PathBuf::from(value);
|
options.root = PathBuf::from(value);
|
||||||
}
|
}
|
||||||
|
"--manifest" => {
|
||||||
|
let value = iter
|
||||||
|
.next()
|
||||||
|
.ok_or_else(|| "--manifest requires a path".to_string())?;
|
||||||
|
options.manifest = Some(PathBuf::from(value));
|
||||||
|
}
|
||||||
_ => return Err(format!("unknown test option: {arg}")),
|
_ => return Err(format!("unknown test option: {arg}")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -788,6 +923,7 @@ fn parse_acceptance_options(args: &[String]) -> Result<AcceptanceOptions, String
|
|||||||
let mut suite = None;
|
let mut suite = None;
|
||||||
let mut stage = Stage::All;
|
let mut stage = Stage::All;
|
||||||
let mut root = PathBuf::from("testdata");
|
let mut root = PathBuf::from("testdata");
|
||||||
|
let mut manifest = None;
|
||||||
let mut out = None;
|
let mut out = None;
|
||||||
let mut iter = args.iter();
|
let mut iter = args.iter();
|
||||||
while let Some(arg) = iter.next() {
|
while let Some(arg) = iter.next() {
|
||||||
@@ -810,6 +946,12 @@ fn parse_acceptance_options(args: &[String]) -> Result<AcceptanceOptions, String
|
|||||||
.ok_or_else(|| "--root requires a path".to_string())?;
|
.ok_or_else(|| "--root requires a path".to_string())?;
|
||||||
root = PathBuf::from(value);
|
root = PathBuf::from(value);
|
||||||
}
|
}
|
||||||
|
"--manifest" => {
|
||||||
|
let value = iter
|
||||||
|
.next()
|
||||||
|
.ok_or_else(|| "--manifest requires a path".to_string())?;
|
||||||
|
manifest = Some(PathBuf::from(value));
|
||||||
|
}
|
||||||
"--out" => {
|
"--out" => {
|
||||||
let value = iter
|
let value = iter
|
||||||
.next()
|
.next()
|
||||||
@@ -832,6 +974,7 @@ fn parse_acceptance_options(args: &[String]) -> Result<AcceptanceOptions, String
|
|||||||
suite,
|
suite,
|
||||||
stage,
|
stage,
|
||||||
root,
|
root,
|
||||||
|
manifest,
|
||||||
out,
|
out,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -1156,10 +1299,12 @@ fn json_escape(value: &str) -> String {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn run_acceptance_report(options: &AcceptanceOptions) -> Result<(), String> {
|
fn run_acceptance_report(options: &AcceptanceOptions) -> Result<(), String> {
|
||||||
if options.suite == TestSuite::Licensed {
|
let roots = if options.suite == TestSuite::Licensed {
|
||||||
validate_licensed_root(&options.root)?;
|
Some(load_licensed_roots(options.manifest.as_deref())?)
|
||||||
}
|
} else {
|
||||||
run_stage_tests(options.stage, options.suite)?;
|
None
|
||||||
|
};
|
||||||
|
run_stage_tests(options.stage, options.suite, roots.as_ref())?;
|
||||||
|
|
||||||
if let Some(parent) = options.out.parent() {
|
if let Some(parent) = options.out.parent() {
|
||||||
fs::create_dir_all(parent).map_err(|err| format!("{}: {err}", parent.display()))?;
|
fs::create_dir_all(parent).map_err(|err| format!("{}: {err}", parent.display()))?;
|
||||||
@@ -1208,23 +1353,36 @@ fn stage_report_packages(stage: Stage) -> Vec<&'static str> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_stage_tests(stage: Stage, suite: TestSuite) -> Result<(), String> {
|
fn run_stage_tests(
|
||||||
|
stage: Stage,
|
||||||
|
suite: TestSuite,
|
||||||
|
roots: Option<&LicensedCorpusRoots>,
|
||||||
|
) -> Result<(), String> {
|
||||||
let mut suffix = Vec::new();
|
let mut suffix = Vec::new();
|
||||||
if suite == TestSuite::Licensed {
|
if suite == TestSuite::Licensed {
|
||||||
suffix.extend(["--", "--ignored"]);
|
suffix.extend(["--", "--ignored"]);
|
||||||
}
|
}
|
||||||
|
let envs = roots.map(LicensedCorpusRoots::envs);
|
||||||
match stage {
|
match stage {
|
||||||
Stage::All => {
|
Stage::All => {
|
||||||
let mut args = vec!["test", "--workspace", "--locked", "--offline"];
|
let mut args = vec!["test", "--workspace", "--locked", "--offline"];
|
||||||
args.extend(suffix);
|
args.extend(suffix);
|
||||||
|
if let Some(envs) = envs {
|
||||||
|
cargo_with_env(&args, &envs)
|
||||||
|
} else {
|
||||||
cargo(&args)
|
cargo(&args)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
Stage::Number(number) => {
|
Stage::Number(number) => {
|
||||||
for package in stage_packages(number)? {
|
for package in stage_packages(number)? {
|
||||||
let mut args = vec!["test", "-p", package, "--locked", "--offline"];
|
let mut args = vec!["test", "-p", package, "--locked", "--offline"];
|
||||||
args.extend(suffix.iter().copied());
|
args.extend(suffix.iter().copied());
|
||||||
|
if let Some(envs) = envs {
|
||||||
|
cargo_with_env(&args, &envs)?;
|
||||||
|
} else {
|
||||||
cargo(&args)?;
|
cargo(&args)?;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1276,6 +1434,13 @@ mod tests {
|
|||||||
values.iter().map(|value| (*value).to_string()).collect()
|
values.iter().map(|value| (*value).to_string()).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn temp_dir(name: &str) -> PathBuf {
|
||||||
|
let suffix = std::time::SystemTime::now()
|
||||||
|
.duration_since(std::time::UNIX_EPOCH)
|
||||||
|
.map_or(0, |duration| duration.as_nanos());
|
||||||
|
std::env::temp_dir().join(format!("fparkan-xtask-{name}-{suffix}"))
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parses_stage_and_root_options() {
|
fn parses_stage_and_root_options() {
|
||||||
let args = strings(&["--stage", "3", "--root", "fixtures"]);
|
let args = strings(&["--stage", "3", "--root", "fixtures"]);
|
||||||
@@ -1286,6 +1451,7 @@ mod tests {
|
|||||||
Ok(TestOptions {
|
Ok(TestOptions {
|
||||||
stage: Stage::Number(3),
|
stage: Stage::Number(3),
|
||||||
root: PathBuf::from("fixtures"),
|
root: PathBuf::from("fixtures"),
|
||||||
|
manifest: None,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1297,8 +1463,8 @@ mod tests {
|
|||||||
"licensed",
|
"licensed",
|
||||||
"--stage",
|
"--stage",
|
||||||
"5",
|
"5",
|
||||||
"--root",
|
"--manifest",
|
||||||
"testdata",
|
"corpora.toml",
|
||||||
"--out",
|
"--out",
|
||||||
"target/report.json",
|
"target/report.json",
|
||||||
]));
|
]));
|
||||||
@@ -1309,6 +1475,7 @@ mod tests {
|
|||||||
suite: TestSuite::Licensed,
|
suite: TestSuite::Licensed,
|
||||||
stage: Stage::Number(5),
|
stage: Stage::Number(5),
|
||||||
root: PathBuf::from("testdata"),
|
root: PathBuf::from("testdata"),
|
||||||
|
manifest: Some(PathBuf::from("corpora.toml")),
|
||||||
out: PathBuf::from("target/report.json"),
|
out: PathBuf::from("target/report.json"),
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@@ -1320,6 +1487,7 @@ mod tests {
|
|||||||
suite: TestSuite::Licensed,
|
suite: TestSuite::Licensed,
|
||||||
stage: Stage::Number(0),
|
stage: Stage::Number(0),
|
||||||
root: PathBuf::from("/private/game"),
|
root: PathBuf::from("/private/game"),
|
||||||
|
manifest: Some(PathBuf::from("/private/corpora.toml")),
|
||||||
out: PathBuf::from("target/report.json"),
|
out: PathBuf::from("target/report.json"),
|
||||||
};
|
};
|
||||||
let report = render_acceptance_report(&options);
|
let report = render_acceptance_report(&options);
|
||||||
@@ -1421,10 +1589,54 @@ mod tests {
|
|||||||
Ok(TestOptions {
|
Ok(TestOptions {
|
||||||
stage: Stage::All,
|
stage: Stage::All,
|
||||||
root: PathBuf::from("testdata"),
|
root: PathBuf::from("testdata"),
|
||||||
|
manifest: None,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parses_licensed_corpora_manifest() -> Result<(), String> {
|
||||||
|
let root = temp_dir("manifest");
|
||||||
|
let part1 = root.join("IS");
|
||||||
|
let part2 = root.join("IS2");
|
||||||
|
fs::create_dir_all(&part1).map_err(|err| err.to_string())?;
|
||||||
|
fs::create_dir_all(&part2).map_err(|err| err.to_string())?;
|
||||||
|
let manifest = root.join("corpora.toml");
|
||||||
|
fs::write(
|
||||||
|
&manifest,
|
||||||
|
format!(
|
||||||
|
"schema = 1\n\n[[corpus]]\nid = \"part1-local\"\nkind = \"part1\"\nroot = \"{}\"\nexpected_profile = \"parkan-is-part1\"\n\n[[corpus]]\nid = \"part2-local\"\nkind = \"part2\"\nroot = \"{}\"\nexpected_profile = \"parkan-is-part2\"\n",
|
||||||
|
part1.display(),
|
||||||
|
part2.display()
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.map_err(|err| err.to_string())?;
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
parse_licensed_manifest(&manifest)?,
|
||||||
|
LicensedCorpusRoots { part1, part2 }
|
||||||
|
);
|
||||||
|
fs::remove_dir_all(root).map_err(|err| err.to_string())?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn licensed_roots_require_manifest_configuration() {
|
||||||
|
let previous = std::env::var_os(CORPORA_MANIFEST_ENV);
|
||||||
|
std::env::remove_var(CORPORA_MANIFEST_ENV);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
load_licensed_roots(None),
|
||||||
|
Err(format!(
|
||||||
|
"licensed tests require --manifest or {CORPORA_MANIFEST_ENV}=<absolute corpora.toml>"
|
||||||
|
))
|
||||||
|
);
|
||||||
|
|
||||||
|
if let Some(value) = previous {
|
||||||
|
std::env::set_var(CORPORA_MANIFEST_ENV, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn rejects_unknown_stage() {
|
fn rejects_unknown_stage() {
|
||||||
assert_eq!(Stage::parse("6"), Err("stage out of range: 6".to_string()));
|
assert_eq!(Stage::parse("6"), Err("stage out of range: 6".to_string()));
|
||||||
|
|||||||
Reference in New Issue
Block a user