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

224 lines
6.9 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);
#[test]
fn u64_fixture_lowers_and_runs_tests() {
let fixture = Path::new(env!("CARGO_MANIFEST_DIR")).join("../tests/u64-numeric-primitive.slo");
let llvm = run_glagol([fixture.as_os_str()]);
assert_success("compile u64 fixture", &llvm);
let stdout = String::from_utf8_lossy(&llvm.stdout);
assert!(
stdout.contains("declare void @print_u64(i64)")
&& stdout.contains("define i64 @base()")
&& stdout.contains("add i64")
&& stdout.contains("mul i64")
&& stdout.contains("udiv i64")
&& stdout.contains("icmp ugt i64")
&& stdout.contains("icmp ult i64")
&& stdout.contains("icmp eq i64")
&& stdout.contains("call void @print_u64(i64")
&& !stdout.contains("@std.io.print_u64"),
"u64 LLVM shape drifted\nstdout:\n{}",
stdout
);
let tests = run_glagol([OsStr::new("test"), fixture.as_os_str()]);
assert_success("run u64 fixture tests", &tests);
assert_eq!(
String::from_utf8_lossy(&tests.stdout),
concat!(
"test \"u64 arithmetic returns exact fixture value\" ... ok\n",
"test \"u64 comparison works in predicates\" ... ok\n",
"test \"u64 division and ordering\" ... ok\n",
"3 test(s) passed\n",
),
"u64 test runner stdout drifted"
);
}
#[test]
fn u64_formatter_and_lowering_are_visible() {
let fixture = Path::new(env!("CARGO_MANIFEST_DIR")).join("../tests/u64-numeric-primitive.slo");
let formatted = run_glagol([OsStr::new("--format"), fixture.as_os_str()]);
assert_success("format u64 fixture", &formatted);
let formatted_stdout = String::from_utf8_lossy(&formatted.stdout);
assert!(
formatted_stdout.contains("(fn base () -> u64")
&& formatted_stdout.contains("4294967296u64")
&& formatted_stdout.contains("(std.io.print_u64 (local_total))"),
"u64 formatter output omitted expected forms\nstdout:\n{}",
formatted_stdout
);
let surface = run_glagol([
OsStr::new("--inspect-lowering=surface"),
fixture.as_os_str(),
]);
assert_success("inspect u64 surface lowering", &surface);
assert_eq!(
String::from_utf8_lossy(&surface.stdout),
fs::read_to_string(
Path::new(env!("CARGO_MANIFEST_DIR"))
.join("../tests/u64-numeric-primitive.surface.lower"),
)
.expect("read u64 surface snapshot"),
"u64 surface lowering snapshot drifted"
);
let checked = run_glagol([
OsStr::new("--inspect-lowering=checked"),
fixture.as_os_str(),
]);
assert_success("inspect u64 checked lowering", &checked);
assert_eq!(
String::from_utf8_lossy(&checked.stdout),
fs::read_to_string(
Path::new(env!("CARGO_MANIFEST_DIR"))
.join("../tests/u64-numeric-primitive.checked.lower"),
)
.expect("read u64 checked snapshot"),
"u64 checked lowering snapshot drifted"
);
}
#[test]
fn mixed_u64_operands_are_rejected_clearly() {
for (name, expression, found) in [
("mixed-u64-i64", "(= 1u64 1i64)", "u64 and i64"),
("mixed-u64-u32", "(= 1u64 1u32)", "u64 and u32"),
("mixed-u64-f64", "(= 1u64 1.0)", "u64 and f64"),
] {
let fixture = write_fixture(
name,
&format!(
"(module main)\n\n(fn main () -> i32\n (if {} 0 1))\n",
expression
),
);
let output = run_glagol([fixture.as_os_str()]);
let stdout = String::from_utf8_lossy(&output.stdout);
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(
!output.status.success(),
"compiler unexpectedly accepted mixed u64 operands\nstdout:\n{}\nstderr:\n{}",
stdout,
stderr
);
assert!(
stdout.is_empty(),
"rejected compile wrote stdout:\n{}",
stdout
);
assert!(
stderr.contains("numeric operands must have the same primitive type")
&& stderr.contains("mixed i32/i64/u32/u64/f64")
&& stderr.contains(found),
"mixed numeric diagnostic drifted for {}\nstderr:\n{}",
name,
stderr
);
}
}
#[test]
fn u64_runtime_print_smoke_when_toolchain_is_available() {
let fixture =
Path::new(env!("CARGO_MANIFEST_DIR")).join("../examples/u64-numeric-primitive.slo");
let binary = unique_path("u64-numeric-primitive-bin");
let build = run_glagol([
OsStr::new("build"),
fixture.as_os_str(),
OsStr::new("-o"),
binary.as_os_str(),
]);
if !build.status.success() {
let stdout = String::from_utf8_lossy(&build.stdout);
let stderr = String::from_utf8_lossy(&build.stderr);
assert!(
stdout.is_empty(),
"failed u64 build wrote stdout:\n{}",
stdout
);
assert!(
stderr.contains("ToolchainUnavailable"),
"u64 build failed unexpectedly\nstderr:\n{}",
stderr
);
return;
}
let run = Command::new(&binary)
.output()
.expect("run u64 numeric primitive binary");
assert_success("run u64 numeric primitive binary", &run);
assert_eq!(
String::from_utf8_lossy(&run.stdout),
"4294967315\n",
"u64 runtime print stdout drifted"
);
assert!(
run.stderr.is_empty(),
"u64 runtime print wrote stderr:\n{}",
String::from_utf8_lossy(&run.stderr)
);
}
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-u64-alpha-{}-{}-{}.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 unique_path(name: &str) -> PathBuf {
let mut path = env::temp_dir();
path.push(format!(
"glagol-{}-{}-{}",
name,
std::process::id(),
NEXT_FIXTURE_ID.fetch_add(1, Ordering::Relaxed)
));
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
);
}