slovo/compiler/tests/integer_remainder_alpha.rs
2026-05-22 08:38:43 +02:00

156 lines
4.4 KiB
Rust

use std::{
env,
ffi::OsStr,
fs,
path::{Path, PathBuf},
process::{Command, Output},
sync::atomic::{AtomicUsize, Ordering},
};
static NEXT_FIXTURE_ID: AtomicUsize = AtomicUsize::new(0);
const EXPECTED_TEST_OUTPUT: &str = concat!(
"test \"i32 remainder\" ... ok\n",
"test \"i32 signed remainder\" ... ok\n",
"test \"i64 remainder\" ... ok\n",
"test \"i64 signed remainder\" ... ok\n",
"test \"integer remainder summary\" ... ok\n",
"5 test(s) passed\n",
);
#[test]
fn integer_remainder_fixture_formats_lowers_and_runs() {
let fixture = Path::new(env!("CARGO_MANIFEST_DIR")).join("../examples/integer-remainder.slo");
let fmt = run_glagol([
OsStr::new("fmt"),
OsStr::new("--check"),
fixture.as_os_str(),
]);
assert_success("format integer remainder fixture", &fmt);
let check = run_glagol([OsStr::new("check"), fixture.as_os_str()]);
assert_success_stdout(check, "", "check integer remainder fixture");
let llvm = run_glagol([fixture.as_os_str()]);
assert_success("compile integer remainder fixture", &llvm);
let stdout = String::from_utf8_lossy(&llvm.stdout);
assert!(
stdout.contains("srem i32") && stdout.contains("srem i64"),
"integer remainder LLVM shape drifted\nstdout:\n{}",
stdout
);
let tests = run_glagol([OsStr::new("test"), fixture.as_os_str()]);
assert_success_stdout(
tests,
EXPECTED_TEST_OUTPUT,
"test integer remainder fixture",
);
}
#[test]
fn integer_remainder_rejections_are_explicit() {
let f64_fixture = write_fixture(
"f64-remainder",
"(module main)\n\n(fn main () -> i32\n (if (= (% 5.0 2.0) 1.0) 0 1))\n",
);
let f64_output = run_glagol([f64_fixture.as_os_str()]);
assert_failure_contains(
&f64_output,
"f64 remainder rejection",
&[
"UnsupportedF64Remainder",
"floating-point remainder is not supported",
"f64 remainder remains deferred",
],
);
let zero_fixture = write_fixture(
"remainder-by-zero",
"(module main)\n\n(test \"remainder by zero\"\n (= (% 1 0) 0))\n\n(fn main () -> i32\n 0)\n",
);
let zero_output = run_glagol([OsStr::new("test"), zero_fixture.as_os_str()]);
assert_failure_contains(
&zero_output,
"remainder by zero test runtime",
&["TestRuntimeError", "remainder by zero in test"],
);
}
fn run_glagol<I, S>(args: I) -> Output
where
I: IntoIterator<Item = S>,
S: AsRef<OsStr>,
{
Command::new(env!("CARGO_BIN_EXE_glagol"))
.args(args)
.current_dir(Path::new(env!("CARGO_MANIFEST_DIR")))
.output()
.expect("run glagol")
}
fn write_fixture(name: &str, source: &str) -> PathBuf {
let mut path = env::temp_dir();
path.push(format!(
"glagol-{}-{}-{}.slo",
name,
std::process::id(),
NEXT_FIXTURE_ID.fetch_add(1, Ordering::Relaxed)
));
fs::write(&path, source).unwrap_or_else(|err| panic!("write `{}`: {}", path.display(), err));
path
}
fn assert_success(context: &str, output: &Output) {
let stdout = String::from_utf8_lossy(&output.stdout);
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(
output.status.success(),
"{} failed\nstdout:\n{}\nstderr:\n{}",
context,
stdout,
stderr
);
assert!(stderr.is_empty(), "{} wrote stderr:\n{}", context, stderr);
}
fn assert_success_stdout(output: Output, expected: &str, context: &str) {
assert_success(context, &output);
assert_eq!(
String::from_utf8_lossy(&output.stdout),
expected,
"{} stdout drifted",
context
);
}
fn assert_failure_contains(output: &Output, context: &str, expected: &[&str]) {
let stdout = String::from_utf8_lossy(&output.stdout);
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(
!output.status.success(),
"{} unexpectedly succeeded\nstdout:\n{}\nstderr:\n{}",
context,
stdout,
stderr
);
assert!(
stdout.is_empty(),
"{} wrote stdout on failure:\n{}",
context,
stdout
);
for needle in expected {
assert!(
stderr.contains(needle),
"{} stderr missing `{}`\nstderr:\n{}",
context,
needle,
stderr
);
}
}