268 lines
7.4 KiB
Rust
268 lines
7.4 KiB
Rust
use std::{
|
|
env, fs,
|
|
path::{Path, PathBuf},
|
|
process::{Command, Output},
|
|
sync::atomic::{AtomicUsize, Ordering},
|
|
};
|
|
|
|
static NEXT_FIXTURE_ID: AtomicUsize = AtomicUsize::new(0);
|
|
|
|
#[test]
|
|
fn run_manifest_records_success_report_stdout_and_program_args() {
|
|
let Some(clang) = find_clang() else {
|
|
eprintln!("skipping run manifest success report: set GLAGOL_CLANG or install clang");
|
|
return;
|
|
};
|
|
|
|
let source = write_fixture(
|
|
"success",
|
|
r#"
|
|
(module main)
|
|
|
|
(fn main () -> i32
|
|
(print_string "beta22-out")
|
|
0)
|
|
"#,
|
|
"slo",
|
|
);
|
|
let manifest_path = temp_path("success-manifest", "manifest.slo");
|
|
|
|
let mut command = Command::new(compiler_path());
|
|
command
|
|
.arg("run")
|
|
.arg(&source)
|
|
.arg("--manifest")
|
|
.arg(&manifest_path)
|
|
.arg("--")
|
|
.arg("alpha")
|
|
.arg("two words")
|
|
.arg("--literal")
|
|
.env("GLAGOL_CLANG", &clang);
|
|
configure_clang_runtime_env(&mut command, &clang);
|
|
|
|
let output = command.output().expect("run glagol success manifest");
|
|
assert_success_stdout("run manifest success", &output, "beta22-out\n");
|
|
|
|
let manifest = read_manifest(&manifest_path);
|
|
assert!(
|
|
manifest.contains(" (mode run)\n")
|
|
&& manifest.contains(" (success true)\n")
|
|
&& manifest.contains(" (run-report\n")
|
|
&& manifest.contains(" (exit-status 0)\n")
|
|
&& manifest.contains(" (stdout \"beta22-out\\n\")\n")
|
|
&& manifest.contains(" (stderr \"\")\n")
|
|
&& manifest.contains(" (arg \"alpha\")\n")
|
|
&& manifest.contains(" (arg \"two words\")\n")
|
|
&& manifest.contains(" (arg \"--literal\")\n"),
|
|
"successful run manifest mismatch:\n{}",
|
|
manifest
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn run_manifest_records_nonzero_report_and_preserves_program_stderr() {
|
|
let Some(clang) = find_clang() else {
|
|
eprintln!("skipping run manifest nonzero report: set GLAGOL_CLANG or install clang");
|
|
return;
|
|
};
|
|
|
|
let source = write_fixture(
|
|
"nonzero",
|
|
r#"
|
|
(module main)
|
|
|
|
(import_c beta22_stderr () -> i32)
|
|
|
|
(fn emit_stderr () -> i32
|
|
(unsafe
|
|
(beta22_stderr)))
|
|
|
|
(fn main () -> i32
|
|
(emit_stderr)
|
|
7)
|
|
"#,
|
|
"slo",
|
|
);
|
|
let c_source = write_fixture(
|
|
"nonzero-stderr",
|
|
r#"
|
|
#include <stdio.h>
|
|
|
|
int beta22_stderr(void) {
|
|
fputs("beta22-err\n", stderr);
|
|
return 0;
|
|
}
|
|
"#,
|
|
"c",
|
|
);
|
|
let manifest_path = temp_path("nonzero-manifest", "manifest.slo");
|
|
|
|
let mut command = Command::new(compiler_path());
|
|
command
|
|
.arg("run")
|
|
.arg(&source)
|
|
.arg("--link-c")
|
|
.arg(&c_source)
|
|
.arg("--manifest")
|
|
.arg(&manifest_path)
|
|
.env("GLAGOL_CLANG", &clang);
|
|
configure_clang_runtime_env(&mut command, &clang);
|
|
|
|
let output = command.output().expect("run glagol nonzero manifest");
|
|
assert_exit_code("run manifest nonzero", &output, 7);
|
|
assert_eq!(output.stdout, b"", "nonzero run stdout drifted");
|
|
assert_eq!(output.stderr, b"beta22-err\n", "nonzero run stderr drifted");
|
|
|
|
let manifest = read_manifest(&manifest_path);
|
|
assert!(
|
|
manifest.contains(" (mode run)\n")
|
|
&& manifest.contains(" (success false)\n")
|
|
&& manifest.contains(" (run-report\n")
|
|
&& manifest.contains(" (exit-status 7)\n")
|
|
&& manifest.contains(" (stdout \"\")\n")
|
|
&& manifest.contains(" (stderr \"beta22-err\\n\")\n")
|
|
&& manifest.contains(" (args)\n"),
|
|
"nonzero run manifest mismatch:\n{}",
|
|
manifest
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn run_manifest_source_failure_does_not_record_fake_run_report() {
|
|
let source = write_fixture(
|
|
"source-failure",
|
|
r#"
|
|
(module main)
|
|
|
|
(fn main () -> i32
|
|
true)
|
|
"#,
|
|
"slo",
|
|
);
|
|
let manifest_path = temp_path("source-failure-manifest", "manifest.slo");
|
|
|
|
let output = run_glagol([
|
|
"run".as_ref(),
|
|
source.as_os_str(),
|
|
"--manifest".as_ref(),
|
|
manifest_path.as_os_str(),
|
|
]);
|
|
assert_exit_code("run manifest source failure", &output, 1);
|
|
|
|
let manifest = read_manifest(&manifest_path);
|
|
assert!(
|
|
manifest.contains(" (mode run)\n")
|
|
&& manifest.contains(" (success false)\n")
|
|
&& manifest.contains(" (kind diagnostics)\n")
|
|
&& manifest.contains("TypeMismatch")
|
|
&& !manifest.contains(" (run-report\n"),
|
|
"source failure manifest included fake run report:\n{}",
|
|
manifest
|
|
);
|
|
}
|
|
|
|
fn run_glagol<const N: usize>(args: [&std::ffi::OsStr; N]) -> Output {
|
|
Command::new(compiler_path())
|
|
.args(args)
|
|
.output()
|
|
.expect("run glagol")
|
|
}
|
|
|
|
fn compiler_path() -> &'static str {
|
|
env!("CARGO_BIN_EXE_glagol")
|
|
}
|
|
|
|
fn write_fixture(name: &str, source: &str, extension: &str) -> PathBuf {
|
|
let path = temp_path(name, extension);
|
|
fs::write(&path, source).unwrap_or_else(|err| panic!("write `{}`: {}", path.display(), err));
|
|
path
|
|
}
|
|
|
|
fn temp_path(name: &str, extension: &str) -> PathBuf {
|
|
let id = NEXT_FIXTURE_ID.fetch_add(1, Ordering::Relaxed);
|
|
let mut path = env::temp_dir();
|
|
path.push(format!(
|
|
"glagol-run-manifest-beta22-{}-{}-{}.{}",
|
|
std::process::id(),
|
|
id,
|
|
name,
|
|
extension
|
|
));
|
|
path
|
|
}
|
|
|
|
fn read_manifest(path: &Path) -> String {
|
|
fs::read_to_string(path).unwrap_or_else(|err| panic!("read `{}`: {}", path.display(), err))
|
|
}
|
|
|
|
fn assert_success_stdout(context: &str, output: &Output, expected: &str) {
|
|
assert!(
|
|
output.status.success(),
|
|
"{} failed\nstdout:\n{}\nstderr:\n{}",
|
|
context,
|
|
String::from_utf8_lossy(&output.stdout),
|
|
String::from_utf8_lossy(&output.stderr)
|
|
);
|
|
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_exit_code(context: &str, output: &Output, expected: i32) {
|
|
assert_eq!(
|
|
output.status.code(),
|
|
Some(expected),
|
|
"{} exit code mismatch\nstdout:\n{}\nstderr:\n{}",
|
|
context,
|
|
String::from_utf8_lossy(&output.stdout),
|
|
String::from_utf8_lossy(&output.stderr)
|
|
);
|
|
}
|
|
|
|
fn find_clang() -> Option<PathBuf> {
|
|
if let Some(path) = env::var_os("GLAGOL_CLANG").filter(|value| !value.is_empty()) {
|
|
let path = PathBuf::from(path);
|
|
if path.is_file() {
|
|
return Some(path);
|
|
}
|
|
}
|
|
|
|
let hermetic_clang = PathBuf::from("/tmp/glagol-clang-root/usr/bin/clang");
|
|
if hermetic_clang.is_file() {
|
|
return Some(hermetic_clang);
|
|
}
|
|
|
|
find_on_path("clang")
|
|
}
|
|
|
|
fn find_on_path(name: &str) -> Option<PathBuf> {
|
|
let path = env::var_os("PATH")?;
|
|
env::split_paths(&path)
|
|
.map(|dir| dir.join(name))
|
|
.find(|candidate| candidate.is_file())
|
|
}
|
|
|
|
fn configure_clang_runtime_env(command: &mut Command, clang: &Path) {
|
|
if !clang.starts_with("/tmp/glagol-clang-root") {
|
|
return;
|
|
}
|
|
|
|
let root = Path::new("/tmp/glagol-clang-root");
|
|
let lib64 = root.join("usr/lib64");
|
|
let lib = root.join("usr/lib");
|
|
let existing = env::var_os("LD_LIBRARY_PATH").unwrap_or_default();
|
|
let mut paths = vec![lib64, lib];
|
|
paths.extend(env::split_paths(&existing));
|
|
let joined = env::join_paths(paths).expect("join LD_LIBRARY_PATH");
|
|
command.env("LD_LIBRARY_PATH", joined);
|
|
}
|