540 lines
17 KiB
Rust
540 lines
17 KiB
Rust
use std::{
|
|
ffi::OsStr,
|
|
fs,
|
|
path::{Path, PathBuf},
|
|
process::{Command, Output},
|
|
sync::atomic::{AtomicUsize, Ordering},
|
|
time::{SystemTime, UNIX_EPOCH},
|
|
};
|
|
|
|
static NEXT_TEMP_ID: AtomicUsize = AtomicUsize::new(0);
|
|
|
|
struct MatrixEntry {
|
|
path: &'static str,
|
|
expected_tests: usize,
|
|
run_tests: bool,
|
|
}
|
|
|
|
const MATRIX: &[MatrixEntry] = &[
|
|
MatrixEntry {
|
|
path: "examples/projects/basic",
|
|
expected_tests: 2,
|
|
run_tests: true,
|
|
},
|
|
MatrixEntry {
|
|
path: "examples/projects/enum-imports",
|
|
expected_tests: 9,
|
|
run_tests: true,
|
|
},
|
|
MatrixEntry {
|
|
path: "examples/projects/std-import-cli",
|
|
expected_tests: 17,
|
|
run_tests: true,
|
|
},
|
|
MatrixEntry {
|
|
path: "examples/projects/std-import-env",
|
|
expected_tests: 23,
|
|
run_tests: true,
|
|
},
|
|
MatrixEntry {
|
|
path: "examples/projects/std-import-fs",
|
|
expected_tests: 35,
|
|
run_tests: true,
|
|
},
|
|
MatrixEntry {
|
|
path: "examples/projects/std-import-io",
|
|
expected_tests: 12,
|
|
run_tests: true,
|
|
},
|
|
MatrixEntry {
|
|
path: "examples/projects/std-import-json",
|
|
expected_tests: 12,
|
|
run_tests: true,
|
|
},
|
|
MatrixEntry {
|
|
path: "examples/projects/std-import-math",
|
|
expected_tests: 4,
|
|
run_tests: true,
|
|
},
|
|
MatrixEntry {
|
|
path: "examples/projects/std-import-net",
|
|
expected_tests: 9,
|
|
run_tests: true,
|
|
},
|
|
MatrixEntry {
|
|
path: "examples/projects/std-import-num",
|
|
expected_tests: 5,
|
|
run_tests: true,
|
|
},
|
|
MatrixEntry {
|
|
path: "examples/projects/std-import-option",
|
|
expected_tests: 36,
|
|
run_tests: true,
|
|
},
|
|
MatrixEntry {
|
|
path: "examples/projects/std-import-process",
|
|
expected_tests: 21,
|
|
run_tests: true,
|
|
},
|
|
MatrixEntry {
|
|
path: "examples/projects/std-import-random",
|
|
expected_tests: 2,
|
|
run_tests: true,
|
|
},
|
|
MatrixEntry {
|
|
path: "examples/projects/std-import-result",
|
|
expected_tests: 26,
|
|
run_tests: true,
|
|
},
|
|
MatrixEntry {
|
|
path: "examples/projects/std-import-string",
|
|
expected_tests: 12,
|
|
run_tests: true,
|
|
},
|
|
MatrixEntry {
|
|
path: "examples/projects/std-import-time",
|
|
expected_tests: 3,
|
|
run_tests: true,
|
|
},
|
|
MatrixEntry {
|
|
path: "examples/projects/std-import-vec_bool",
|
|
expected_tests: 19,
|
|
run_tests: true,
|
|
},
|
|
MatrixEntry {
|
|
path: "examples/projects/std-import-vec_f64",
|
|
expected_tests: 20,
|
|
run_tests: true,
|
|
},
|
|
MatrixEntry {
|
|
path: "examples/projects/std-import-vec_i32",
|
|
expected_tests: 22,
|
|
run_tests: true,
|
|
},
|
|
MatrixEntry {
|
|
path: "examples/projects/std-import-vec_i64",
|
|
expected_tests: 20,
|
|
run_tests: true,
|
|
},
|
|
MatrixEntry {
|
|
path: "examples/projects/std-import-vec_string",
|
|
expected_tests: 19,
|
|
run_tests: true,
|
|
},
|
|
MatrixEntry {
|
|
path: "examples/projects/std-layout-local-cli",
|
|
expected_tests: 17,
|
|
run_tests: true,
|
|
},
|
|
MatrixEntry {
|
|
path: "examples/projects/std-layout-local-env",
|
|
expected_tests: 23,
|
|
run_tests: true,
|
|
},
|
|
MatrixEntry {
|
|
path: "examples/projects/std-layout-local-fs",
|
|
expected_tests: 35,
|
|
run_tests: true,
|
|
},
|
|
MatrixEntry {
|
|
path: "examples/projects/std-layout-local-io",
|
|
expected_tests: 12,
|
|
run_tests: true,
|
|
},
|
|
MatrixEntry {
|
|
path: "examples/projects/std-layout-local-json",
|
|
expected_tests: 12,
|
|
run_tests: true,
|
|
},
|
|
MatrixEntry {
|
|
path: "examples/projects/std-layout-local-math",
|
|
expected_tests: 7,
|
|
run_tests: true,
|
|
},
|
|
MatrixEntry {
|
|
path: "examples/projects/std-layout-local-net",
|
|
expected_tests: 9,
|
|
run_tests: true,
|
|
},
|
|
MatrixEntry {
|
|
path: "examples/projects/std-layout-local-num",
|
|
expected_tests: 5,
|
|
run_tests: true,
|
|
},
|
|
MatrixEntry {
|
|
path: "examples/projects/std-layout-local-option",
|
|
expected_tests: 36,
|
|
run_tests: true,
|
|
},
|
|
MatrixEntry {
|
|
path: "examples/projects/std-layout-local-process",
|
|
expected_tests: 21,
|
|
run_tests: true,
|
|
},
|
|
MatrixEntry {
|
|
path: "examples/projects/std-layout-local-random",
|
|
expected_tests: 3,
|
|
run_tests: true,
|
|
},
|
|
MatrixEntry {
|
|
path: "examples/projects/std-layout-local-result",
|
|
expected_tests: 26,
|
|
run_tests: true,
|
|
},
|
|
MatrixEntry {
|
|
path: "examples/projects/std-layout-local-string",
|
|
expected_tests: 12,
|
|
run_tests: true,
|
|
},
|
|
MatrixEntry {
|
|
path: "examples/projects/std-layout-local-time",
|
|
expected_tests: 3,
|
|
run_tests: true,
|
|
},
|
|
MatrixEntry {
|
|
path: "examples/projects/std-layout-local-vec_bool",
|
|
expected_tests: 19,
|
|
run_tests: true,
|
|
},
|
|
MatrixEntry {
|
|
path: "examples/projects/std-layout-local-vec_f64",
|
|
expected_tests: 20,
|
|
run_tests: true,
|
|
},
|
|
MatrixEntry {
|
|
path: "examples/projects/std-layout-local-vec_i32",
|
|
expected_tests: 22,
|
|
run_tests: true,
|
|
},
|
|
MatrixEntry {
|
|
path: "examples/projects/std-layout-local-vec_i64",
|
|
expected_tests: 20,
|
|
run_tests: true,
|
|
},
|
|
MatrixEntry {
|
|
path: "examples/projects/std-layout-local-vec_string",
|
|
expected_tests: 19,
|
|
run_tests: true,
|
|
},
|
|
MatrixEntry {
|
|
path: "examples/projects/stdlib-composition",
|
|
expected_tests: 3,
|
|
run_tests: true,
|
|
},
|
|
MatrixEntry {
|
|
path: "examples/workspaces/exp-5-local",
|
|
expected_tests: 2,
|
|
run_tests: true,
|
|
},
|
|
MatrixEntry {
|
|
path: "examples/workspaces/std-import-option",
|
|
expected_tests: 1,
|
|
run_tests: true,
|
|
},
|
|
];
|
|
|
|
#[test]
|
|
fn user_project_conformance_matrix_checks_lists_and_runs_stable_tests() {
|
|
assert_matrix_inventory();
|
|
|
|
let repo_root = repo_root();
|
|
for entry in MATRIX {
|
|
let fixture = repo_root.join(entry.path);
|
|
assert!(
|
|
fixture.join("slovo.toml").is_file(),
|
|
"{} must remain an existing project or workspace fixture",
|
|
entry.path
|
|
);
|
|
|
|
let check = run_glagol(
|
|
[OsStr::new("check"), fixture.as_os_str()],
|
|
&temp_root(entry.path, "check"),
|
|
);
|
|
assert_success_no_stderr(entry.path, "check", &check);
|
|
assert_stdout_eq(entry.path, "check", &check, "");
|
|
|
|
let list = run_glagol(
|
|
[
|
|
OsStr::new("test"),
|
|
OsStr::new("--list"),
|
|
fixture.as_os_str(),
|
|
],
|
|
&temp_root(entry.path, "list"),
|
|
);
|
|
assert_success_no_stderr(entry.path, "test --list", &list);
|
|
assert_stdout_contains(
|
|
entry.path,
|
|
"test --list",
|
|
&list,
|
|
&format!(
|
|
"{} test(s) selected (total_discovered {}, selected {}, passed 0, failed 0, skipped 0, filter none)",
|
|
entry.expected_tests, entry.expected_tests, entry.expected_tests
|
|
),
|
|
);
|
|
|
|
if entry.run_tests {
|
|
let test = run_glagol(
|
|
[OsStr::new("test"), fixture.as_os_str()],
|
|
&temp_root(entry.path, "test"),
|
|
);
|
|
assert_success_no_stderr(entry.path, "test", &test);
|
|
assert_stdout_contains(
|
|
entry.path,
|
|
"test",
|
|
&test,
|
|
&format!("{} test(s) passed", entry.expected_tests),
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
fn assert_matrix_inventory() {
|
|
assert_eq!(MATRIX.len(), 43, "beta25 fixture-root count changed");
|
|
assert_eq!(
|
|
MATRIX
|
|
.iter()
|
|
.map(|entry| entry.expected_tests)
|
|
.sum::<usize>(),
|
|
655,
|
|
"beta25 discovered-test count changed"
|
|
);
|
|
|
|
let matrix_paths = MATRIX
|
|
.iter()
|
|
.map(|entry| entry.path.to_owned())
|
|
.collect::<Vec<_>>();
|
|
let mut sorted_matrix_paths = matrix_paths.clone();
|
|
sorted_matrix_paths.sort();
|
|
assert_eq!(
|
|
matrix_paths, sorted_matrix_paths,
|
|
"beta25 user-project conformance matrix must remain sorted by repository-relative path"
|
|
);
|
|
|
|
let discovered_paths = discover_fixture_paths(&repo_root());
|
|
assert_eq!(
|
|
discovered_paths, matrix_paths,
|
|
"beta25 user-project conformance matrix must cover every top-level example project and workspace fixture"
|
|
);
|
|
|
|
let inventory = MATRIX
|
|
.iter()
|
|
.map(|entry| {
|
|
format!(
|
|
"{}|tests={}|run_tests={}",
|
|
entry.path, entry.expected_tests, entry.run_tests
|
|
)
|
|
})
|
|
.collect::<Vec<_>>()
|
|
.join("\n");
|
|
|
|
assert_eq!(
|
|
inventory,
|
|
concat!(
|
|
"examples/projects/basic|tests=2|run_tests=true\n",
|
|
"examples/projects/enum-imports|tests=9|run_tests=true\n",
|
|
"examples/projects/std-import-cli|tests=17|run_tests=true\n",
|
|
"examples/projects/std-import-env|tests=23|run_tests=true\n",
|
|
"examples/projects/std-import-fs|tests=35|run_tests=true\n",
|
|
"examples/projects/std-import-io|tests=12|run_tests=true\n",
|
|
"examples/projects/std-import-json|tests=12|run_tests=true\n",
|
|
"examples/projects/std-import-math|tests=4|run_tests=true\n",
|
|
"examples/projects/std-import-net|tests=9|run_tests=true\n",
|
|
"examples/projects/std-import-num|tests=5|run_tests=true\n",
|
|
"examples/projects/std-import-option|tests=36|run_tests=true\n",
|
|
"examples/projects/std-import-process|tests=21|run_tests=true\n",
|
|
"examples/projects/std-import-random|tests=2|run_tests=true\n",
|
|
"examples/projects/std-import-result|tests=26|run_tests=true\n",
|
|
"examples/projects/std-import-string|tests=12|run_tests=true\n",
|
|
"examples/projects/std-import-time|tests=3|run_tests=true\n",
|
|
"examples/projects/std-import-vec_bool|tests=19|run_tests=true\n",
|
|
"examples/projects/std-import-vec_f64|tests=20|run_tests=true\n",
|
|
"examples/projects/std-import-vec_i32|tests=22|run_tests=true\n",
|
|
"examples/projects/std-import-vec_i64|tests=20|run_tests=true\n",
|
|
"examples/projects/std-import-vec_string|tests=19|run_tests=true\n",
|
|
"examples/projects/std-layout-local-cli|tests=17|run_tests=true\n",
|
|
"examples/projects/std-layout-local-env|tests=23|run_tests=true\n",
|
|
"examples/projects/std-layout-local-fs|tests=35|run_tests=true\n",
|
|
"examples/projects/std-layout-local-io|tests=12|run_tests=true\n",
|
|
"examples/projects/std-layout-local-json|tests=12|run_tests=true\n",
|
|
"examples/projects/std-layout-local-math|tests=7|run_tests=true\n",
|
|
"examples/projects/std-layout-local-net|tests=9|run_tests=true\n",
|
|
"examples/projects/std-layout-local-num|tests=5|run_tests=true\n",
|
|
"examples/projects/std-layout-local-option|tests=36|run_tests=true\n",
|
|
"examples/projects/std-layout-local-process|tests=21|run_tests=true\n",
|
|
"examples/projects/std-layout-local-random|tests=3|run_tests=true\n",
|
|
"examples/projects/std-layout-local-result|tests=26|run_tests=true\n",
|
|
"examples/projects/std-layout-local-string|tests=12|run_tests=true\n",
|
|
"examples/projects/std-layout-local-time|tests=3|run_tests=true\n",
|
|
"examples/projects/std-layout-local-vec_bool|tests=19|run_tests=true\n",
|
|
"examples/projects/std-layout-local-vec_f64|tests=20|run_tests=true\n",
|
|
"examples/projects/std-layout-local-vec_i32|tests=22|run_tests=true\n",
|
|
"examples/projects/std-layout-local-vec_i64|tests=20|run_tests=true\n",
|
|
"examples/projects/std-layout-local-vec_string|tests=19|run_tests=true\n",
|
|
"examples/projects/stdlib-composition|tests=3|run_tests=true\n",
|
|
"examples/workspaces/exp-5-local|tests=2|run_tests=true\n",
|
|
"examples/workspaces/std-import-option|tests=1|run_tests=true",
|
|
),
|
|
"beta25 user-project conformance matrix changed without updating the inventory assertion"
|
|
);
|
|
}
|
|
|
|
fn discover_fixture_paths(repo_root: &Path) -> Vec<String> {
|
|
let mut paths = Vec::new();
|
|
for parent in ["examples/projects", "examples/workspaces"] {
|
|
let parent_path = repo_root.join(parent);
|
|
for entry in fs::read_dir(&parent_path).unwrap_or_else(|err| {
|
|
panic!(
|
|
"read fixture inventory `{}`: {}",
|
|
parent_path.display(),
|
|
err
|
|
);
|
|
}) {
|
|
let entry = entry.unwrap_or_else(|err| {
|
|
panic!(
|
|
"read fixture entry under `{}`: {}",
|
|
parent_path.display(),
|
|
err
|
|
);
|
|
});
|
|
let path = entry.path();
|
|
if path.join("slovo.toml").is_file() {
|
|
paths.push(
|
|
path.strip_prefix(repo_root)
|
|
.expect("fixture path under repository root")
|
|
.to_string_lossy()
|
|
.replace('\\', "/"),
|
|
);
|
|
}
|
|
}
|
|
}
|
|
paths.sort();
|
|
paths
|
|
}
|
|
|
|
fn repo_root() -> PathBuf {
|
|
Path::new(env!("CARGO_MANIFEST_DIR"))
|
|
.parent()
|
|
.expect("compiler crate has repository parent")
|
|
.to_path_buf()
|
|
}
|
|
|
|
fn run_glagol<I, S>(args: I, cwd: &Path) -> Output
|
|
where
|
|
I: IntoIterator<Item = S>,
|
|
S: AsRef<OsStr>,
|
|
{
|
|
fs::create_dir_all(cwd).unwrap_or_else(|err| {
|
|
panic!("create command cwd `{}`: {}", cwd.display(), err);
|
|
});
|
|
let mut command = Command::new(env!("CARGO_BIN_EXE_glagol"));
|
|
configure_conformance_env(&mut command);
|
|
command
|
|
.args(args)
|
|
.current_dir(cwd)
|
|
.output()
|
|
.expect("run glagol")
|
|
}
|
|
|
|
fn configure_conformance_env(command: &mut Command) {
|
|
for key in [
|
|
"GLAGOL_STD_IMPORT_ENV_ALPHA_UNLIKELY_MISSING",
|
|
"GLAGOL_STD_LAYOUT_LOCAL_ENV_ALPHA_UNLIKELY_MISSING",
|
|
] {
|
|
command.env_remove(key);
|
|
}
|
|
|
|
for (key, value) in [
|
|
(
|
|
"GLAGOL_STD_IMPORT_ENV_ALPHA_PRESENT",
|
|
"glagol-std-import-env-alpha-value",
|
|
),
|
|
("GLAGOL_STD_IMPORT_ENV_ALPHA_PRESENT_I32", "42"),
|
|
("GLAGOL_STD_IMPORT_ENV_ALPHA_PRESENT_I64", "42000000000"),
|
|
("GLAGOL_STD_IMPORT_ENV_ALPHA_PRESENT_U32", "42"),
|
|
("GLAGOL_STD_IMPORT_ENV_ALPHA_PRESENT_U64", "4294967296"),
|
|
("GLAGOL_STD_IMPORT_ENV_ALPHA_PRESENT_F64", "42.5"),
|
|
("GLAGOL_STD_IMPORT_ENV_ALPHA_PRESENT_BOOL", "true"),
|
|
("GLAGOL_STD_IMPORT_ENV_ALPHA_INVALID", "not-a-number"),
|
|
(
|
|
"GLAGOL_STD_LAYOUT_LOCAL_ENV_ALPHA_PRESENT",
|
|
"glagol-std-layout-local-env-alpha-value",
|
|
),
|
|
("GLAGOL_STD_LAYOUT_LOCAL_ENV_ALPHA_PRESENT_I32", "42"),
|
|
(
|
|
"GLAGOL_STD_LAYOUT_LOCAL_ENV_ALPHA_PRESENT_I64",
|
|
"42000000000",
|
|
),
|
|
("GLAGOL_STD_LAYOUT_LOCAL_ENV_ALPHA_PRESENT_U32", "42"),
|
|
(
|
|
"GLAGOL_STD_LAYOUT_LOCAL_ENV_ALPHA_PRESENT_U64",
|
|
"4294967296",
|
|
),
|
|
("GLAGOL_STD_LAYOUT_LOCAL_ENV_ALPHA_PRESENT_F64", "42.5"),
|
|
("GLAGOL_STD_LAYOUT_LOCAL_ENV_ALPHA_PRESENT_BOOL", "true"),
|
|
("GLAGOL_STD_LAYOUT_LOCAL_ENV_ALPHA_INVALID", "not-a-number"),
|
|
] {
|
|
command.env(key, value);
|
|
}
|
|
}
|
|
|
|
fn temp_root(path: &str, command: &str) -> PathBuf {
|
|
let id = NEXT_TEMP_ID.fetch_add(1, Ordering::Relaxed);
|
|
let nanos = SystemTime::now()
|
|
.duration_since(UNIX_EPOCH)
|
|
.expect("system clock before UNIX_EPOCH")
|
|
.as_nanos();
|
|
let fixture = path
|
|
.chars()
|
|
.map(|ch| if ch == '/' || ch == '-' { '_' } else { ch })
|
|
.collect::<String>();
|
|
std::env::temp_dir().join(format!(
|
|
"glagol-user-project-conformance-beta25-{}-{}-{}-{}-{}",
|
|
std::process::id(),
|
|
nanos,
|
|
id,
|
|
fixture,
|
|
command
|
|
))
|
|
}
|
|
|
|
fn assert_success_no_stderr(fixture: &str, command: &str, output: &Output) {
|
|
assert!(
|
|
output.status.success(),
|
|
"{} `{}` failed\nstatus: {:?}\nstdout:\n{}\nstderr:\n{}",
|
|
fixture,
|
|
command,
|
|
output.status.code(),
|
|
String::from_utf8_lossy(&output.stdout),
|
|
String::from_utf8_lossy(&output.stderr)
|
|
);
|
|
assert!(
|
|
output.stderr.is_empty(),
|
|
"{} `{}` wrote stderr:\n{}",
|
|
fixture,
|
|
command,
|
|
String::from_utf8_lossy(&output.stderr)
|
|
);
|
|
}
|
|
|
|
fn assert_stdout_eq(fixture: &str, command: &str, output: &Output, expected: &str) {
|
|
assert_eq!(
|
|
String::from_utf8_lossy(&output.stdout),
|
|
expected,
|
|
"{} `{}` stdout mismatch",
|
|
fixture,
|
|
command
|
|
);
|
|
}
|
|
|
|
fn assert_stdout_contains(fixture: &str, command: &str, output: &Output, needle: &str) {
|
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
|
assert!(
|
|
stdout.contains(needle),
|
|
"{} `{}` stdout did not contain `{}`:\n{}",
|
|
fixture,
|
|
command,
|
|
needle,
|
|
stdout
|
|
);
|
|
}
|