385 lines
9.8 KiB
Rust
385 lines
9.8 KiB
Rust
use std::{
|
|
fs,
|
|
path::PathBuf,
|
|
process::{Command, Output},
|
|
sync::atomic::{AtomicUsize, Ordering},
|
|
time::{SystemTime, UNIX_EPOCH},
|
|
};
|
|
|
|
static NEXT_FIXTURE_ID: AtomicUsize = AtomicUsize::new(0);
|
|
|
|
#[test]
|
|
fn file_list_preserves_order_and_does_not_execute_bodies() {
|
|
let fixture = write_fixture(
|
|
"file-list",
|
|
r#"
|
|
(module main)
|
|
|
|
(test "alpha first"
|
|
true)
|
|
|
|
(test "beta would fail"
|
|
false)
|
|
"#,
|
|
);
|
|
|
|
let output = run_glagol(["test".as_ref(), "--list".as_ref(), fixture.as_os_str()]);
|
|
|
|
assert_success_stdout(
|
|
"file test list",
|
|
output,
|
|
"test \"alpha first\" ... selected\n\
|
|
test \"beta would fail\" ... selected\n\
|
|
2 test(s) selected (total_discovered 2, selected 2, passed 0, failed 0, skipped 0, filter none)\n",
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn file_list_filter_marks_selected_and_skipped_in_order() {
|
|
let fixture = write_fixture(
|
|
"file-list-filter",
|
|
r#"
|
|
(module main)
|
|
|
|
(test "alpha first"
|
|
false)
|
|
|
|
(test "beta second"
|
|
false)
|
|
"#,
|
|
);
|
|
|
|
let output = run_glagol([
|
|
"test".as_ref(),
|
|
"--list".as_ref(),
|
|
fixture.as_os_str(),
|
|
"--filter".as_ref(),
|
|
"beta".as_ref(),
|
|
]);
|
|
|
|
assert_success_stdout(
|
|
"file filtered test list",
|
|
output,
|
|
"test \"alpha first\" ... skipped\n\
|
|
test \"beta second\" ... selected\n\
|
|
1 test(s) selected (total_discovered 2, selected 1, passed 0, failed 0, skipped 1, filter \"beta\")\n",
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn legacy_run_tests_list_matches_test_subcommand_list() {
|
|
let fixture = write_fixture(
|
|
"legacy-list",
|
|
r#"
|
|
(module main)
|
|
|
|
(test "legacy first"
|
|
false)
|
|
|
|
(test "legacy second"
|
|
false)
|
|
"#,
|
|
);
|
|
|
|
let subcommand = run_glagol(["test".as_ref(), "--list".as_ref(), fixture.as_os_str()]);
|
|
let legacy = run_glagol([
|
|
"--run-tests".as_ref(),
|
|
"--list".as_ref(),
|
|
fixture.as_os_str(),
|
|
]);
|
|
|
|
assert_success("test --list", &subcommand);
|
|
assert_success("legacy --run-tests --list", &legacy);
|
|
assert_eq!(
|
|
legacy.stdout, subcommand.stdout,
|
|
"legacy list output differed from test subcommand"
|
|
);
|
|
assert!(legacy.stderr.is_empty(), "legacy list wrote stderr");
|
|
}
|
|
|
|
#[test]
|
|
fn project_list_preserves_topological_order_and_filter_counts() {
|
|
let project = write_project(
|
|
"project-list",
|
|
&[(
|
|
"provider",
|
|
"(module provider (export value))\n\n\
|
|
(fn value () -> i32\n 1)\n\n\
|
|
(test \"provider first\"\n false)\n",
|
|
)],
|
|
"(module main)\n\n\
|
|
(import provider (value))\n\n\
|
|
(fn main () -> i32\n (value))\n\n\
|
|
(test \"consumer second\"\n false)\n",
|
|
);
|
|
|
|
let output = run_glagol([
|
|
"test".as_ref(),
|
|
"--list".as_ref(),
|
|
project.as_os_str(),
|
|
"--filter".as_ref(),
|
|
"consumer".as_ref(),
|
|
]);
|
|
|
|
assert_success_stdout(
|
|
"project filtered list",
|
|
output,
|
|
"test \"provider first\" ... skipped\n\
|
|
test \"consumer second\" ... selected\n\
|
|
1 test(s) selected (total_discovered 2, selected 1, passed 0, failed 0, skipped 1, filter \"consumer\")\n",
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn workspace_list_preserves_package_order_without_running_tests() {
|
|
let workspace = write_workspace(
|
|
"workspace-list",
|
|
"[workspace]\nmembers = [\"packages/app\", \"packages/util\"]\n",
|
|
&[
|
|
WorkspacePackageSpec {
|
|
member: "packages/util",
|
|
manifest: "[package]\nname = \"util\"\nversion = \"0.1.0\"\n",
|
|
modules: &[(
|
|
"util",
|
|
"(module util (export answer))\n\n\
|
|
(fn answer () -> i32\n 41)\n\n\
|
|
(test \"util provider first\"\n false)\n",
|
|
)],
|
|
},
|
|
WorkspacePackageSpec {
|
|
member: "packages/app",
|
|
manifest: "[package]\nname = \"app\"\nversion = \"0.1.0\"\n\n[dependencies]\nutil = { path = \"../util\" }\n",
|
|
modules: &[(
|
|
"main",
|
|
"(module main)\n\n\
|
|
(import util.util (answer))\n\n\
|
|
(fn main () -> i32\n (answer))\n\n\
|
|
(test \"app consumer second\"\n false)\n",
|
|
)],
|
|
},
|
|
],
|
|
);
|
|
|
|
let output = run_glagol(["test".as_ref(), "--list".as_ref(), workspace.as_os_str()]);
|
|
|
|
assert_success_stdout(
|
|
"workspace list",
|
|
output,
|
|
"test \"util provider first\" ... selected\n\
|
|
test \"app consumer second\" ... selected\n\
|
|
2 test(s) selected (total_discovered 2, selected 2, passed 0, failed 0, skipped 0, filter none)\n",
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn ordinary_test_output_stays_byte_stable() {
|
|
let fixture = write_fixture(
|
|
"ordinary-test",
|
|
r#"
|
|
(module main)
|
|
|
|
(test "alpha first"
|
|
true)
|
|
|
|
(test "beta second"
|
|
true)
|
|
"#,
|
|
);
|
|
|
|
let subcommand = run_glagol(["test".as_ref(), fixture.as_os_str()]);
|
|
assert_success_stdout(
|
|
"ordinary test",
|
|
subcommand,
|
|
"test \"alpha first\" ... ok\n\
|
|
test \"beta second\" ... ok\n\
|
|
2 test(s) passed\n",
|
|
);
|
|
|
|
let legacy = run_glagol(["--run-tests".as_ref(), fixture.as_os_str()]);
|
|
assert_success_stdout(
|
|
"ordinary legacy run-tests",
|
|
legacy,
|
|
"test \"alpha first\" ... ok\n\
|
|
test \"beta second\" ... ok\n\
|
|
2 test(s) passed\n",
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn list_mode_reuses_checked_diagnostics_for_invalid_tests() {
|
|
let fixture = write_fixture(
|
|
"invalid-list",
|
|
r#"
|
|
(module main)
|
|
|
|
(test "not bool"
|
|
1)
|
|
"#,
|
|
);
|
|
|
|
let output = run_glagol(["test".as_ref(), "--list".as_ref(), fixture.as_os_str()]);
|
|
|
|
assert_failure_stderr_contains(
|
|
"invalid list",
|
|
&output,
|
|
&[
|
|
"TestExpressionNotBool",
|
|
"Expected:",
|
|
"bool",
|
|
"Found:",
|
|
"i32",
|
|
],
|
|
);
|
|
assert!(
|
|
output.stdout.is_empty(),
|
|
"invalid list wrote stdout:\n{}",
|
|
String::from_utf8_lossy(&output.stdout)
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn list_flag_is_rejected_outside_test_mode() {
|
|
let fixture = write_fixture(
|
|
"list-outside-test",
|
|
r#"
|
|
(module main)
|
|
|
|
(fn main () -> i32
|
|
0)
|
|
"#,
|
|
);
|
|
|
|
let output = run_glagol(["check".as_ref(), "--list".as_ref(), fixture.as_os_str()]);
|
|
|
|
assert_failure_stderr_contains(
|
|
"check --list",
|
|
&output,
|
|
&["`--list` is only supported with `test` and `--run-tests`"],
|
|
);
|
|
}
|
|
|
|
fn write_fixture(name: &str, source: &str) -> PathBuf {
|
|
let path = unique_path(name).with_extension("slo");
|
|
fs::write(&path, source).expect("write fixture");
|
|
path
|
|
}
|
|
|
|
fn write_project(name: &str, modules: &[(&str, &str)], main: &str) -> PathBuf {
|
|
let root = unique_path(name);
|
|
let src = root.join("src");
|
|
fs::create_dir_all(&src).expect("create project src");
|
|
fs::write(
|
|
root.join("slovo.toml"),
|
|
format!(
|
|
"[project]\nname = \"{}\"\nsource_root = \"src\"\nentry = \"main\"\n",
|
|
name
|
|
),
|
|
)
|
|
.expect("write project manifest");
|
|
fs::write(src.join("main.slo"), main).expect("write project main");
|
|
for (module, source) in modules {
|
|
fs::write(src.join(format!("{}.slo", module)), source).expect("write project module");
|
|
}
|
|
root
|
|
}
|
|
|
|
struct WorkspacePackageSpec<'a> {
|
|
member: &'a str,
|
|
manifest: &'a str,
|
|
modules: &'a [(&'a str, &'a str)],
|
|
}
|
|
|
|
fn write_workspace(
|
|
name: &str,
|
|
workspace_manifest: &str,
|
|
packages: &[WorkspacePackageSpec<'_>],
|
|
) -> PathBuf {
|
|
let root = unique_path(name);
|
|
fs::create_dir_all(&root).expect("create workspace root");
|
|
fs::write(root.join("slovo.toml"), workspace_manifest).expect("write workspace manifest");
|
|
for package in packages {
|
|
let package_root = root.join(package.member);
|
|
let src = package_root.join("src");
|
|
fs::create_dir_all(&src).expect("create workspace package src");
|
|
fs::write(package_root.join("slovo.toml"), package.manifest)
|
|
.expect("write workspace package manifest");
|
|
for (module, source) in package.modules {
|
|
fs::write(src.join(format!("{}.slo", module)), source)
|
|
.expect("write workspace package module");
|
|
}
|
|
}
|
|
root
|
|
}
|
|
|
|
fn unique_path(name: &str) -> PathBuf {
|
|
let id = NEXT_FIXTURE_ID.fetch_add(1, Ordering::SeqCst);
|
|
let nanos = SystemTime::now()
|
|
.duration_since(UNIX_EPOCH)
|
|
.map(|duration| duration.as_nanos())
|
|
.unwrap_or(0);
|
|
std::env::temp_dir().join(format!(
|
|
"glagol-test-discovery-beta19-{}-{}-{}-{}",
|
|
std::process::id(),
|
|
nanos,
|
|
id,
|
|
name
|
|
))
|
|
}
|
|
|
|
fn run_glagol<I, S>(args: I) -> Output
|
|
where
|
|
I: IntoIterator<Item = S>,
|
|
S: AsRef<std::ffi::OsStr>,
|
|
{
|
|
Command::new(env!("CARGO_BIN_EXE_glagol"))
|
|
.args(args)
|
|
.output()
|
|
.expect("run glagol")
|
|
}
|
|
|
|
fn assert_success(context: &str, output: &Output) {
|
|
assert!(
|
|
output.status.success(),
|
|
"{} failed\nstdout:\n{}\nstderr:\n{}",
|
|
context,
|
|
String::from_utf8_lossy(&output.stdout),
|
|
String::from_utf8_lossy(&output.stderr)
|
|
);
|
|
}
|
|
|
|
fn assert_success_stdout(context: &str, output: Output, expected: &str) {
|
|
assert_success(context, &output);
|
|
assert_eq!(
|
|
String::from_utf8_lossy(&output.stdout),
|
|
expected,
|
|
"{} stdout mismatch",
|
|
context
|
|
);
|
|
assert!(
|
|
output.stderr.is_empty(),
|
|
"{} wrote stderr:\n{}",
|
|
context,
|
|
String::from_utf8_lossy(&output.stderr)
|
|
);
|
|
}
|
|
|
|
fn assert_failure_stderr_contains(context: &str, output: &Output, expected: &[&str]) {
|
|
assert!(
|
|
!output.status.success(),
|
|
"{} unexpectedly succeeded\nstdout:\n{}\nstderr:\n{}",
|
|
context,
|
|
String::from_utf8_lossy(&output.stdout),
|
|
String::from_utf8_lossy(&output.stderr)
|
|
);
|
|
let stderr = String::from_utf8_lossy(&output.stderr);
|
|
for needle in expected {
|
|
assert!(
|
|
stderr.contains(needle),
|
|
"{} stderr missing `{}`:\n{}",
|
|
context,
|
|
needle,
|
|
stderr
|
|
);
|
|
}
|
|
}
|