227 lines
5.7 KiB
Rust
227 lines
5.7 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 standard_json_lowers_to_private_runtime_helper() {
|
|
let fixture = write_fixture(
|
|
"lowering",
|
|
r#"
|
|
(module main)
|
|
|
|
(fn main () -> i32
|
|
(if (= (std.json.quote_string "slo\"vo") "\"slo\\\"vo\"")
|
|
0
|
|
1))
|
|
"#,
|
|
);
|
|
let output = run_glagol([fixture.as_os_str()]);
|
|
assert_success("compile standard json lowering", &output);
|
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
|
|
|
assert!(
|
|
stdout.contains("declare ptr @__glagol_json_quote_string(ptr)")
|
|
&& stdout.contains("call ptr @__glagol_json_quote_string(")
|
|
&& !stdout.contains("@std.json.quote_string"),
|
|
"standard json LLVM shape drifted\nstdout:\n{}",
|
|
stdout
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_runner_reports_deterministic_json_quoting() {
|
|
let fixture = write_fixture(
|
|
"test-runner",
|
|
r#"
|
|
(module main)
|
|
|
|
(test "quote plain string"
|
|
(= (std.json.quote_string "slovo") "\"slovo\""))
|
|
|
|
(test "quote embedded quote"
|
|
(= (std.json.quote_string "slo\"vo") "\"slo\\\"vo\""))
|
|
|
|
(test "quote backslash"
|
|
(= (std.json.quote_string "slo\\vo") "\"slo\\\\vo\""))
|
|
|
|
(test "quote newline tab"
|
|
(= (std.json.quote_string "line\n\tnext") "\"line\\n\\tnext\""))
|
|
"#,
|
|
);
|
|
let output = run_glagol([OsStr::new("test"), fixture.as_os_str()]);
|
|
assert_success("run standard json tests", &output);
|
|
assert_eq!(
|
|
String::from_utf8_lossy(&output.stdout),
|
|
concat!(
|
|
"test \"quote plain string\" ... ok\n",
|
|
"test \"quote embedded quote\" ... ok\n",
|
|
"test \"quote backslash\" ... ok\n",
|
|
"test \"quote newline tab\" ... ok\n",
|
|
"4 test(s) passed\n",
|
|
),
|
|
"standard json test runner stdout drifted"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn standard_json_diagnostics_cover_promoted_and_deferred_names() {
|
|
let cases = [
|
|
(
|
|
"quote-arity",
|
|
r#"
|
|
(module main)
|
|
|
|
(fn main () -> i32
|
|
(std.json.quote_string))
|
|
"#,
|
|
"ArityMismatch",
|
|
),
|
|
(
|
|
"quote-type",
|
|
r#"
|
|
(module main)
|
|
|
|
(fn main () -> i32
|
|
(std.json.quote_string 42)
|
|
0)
|
|
"#,
|
|
"TypeMismatch",
|
|
),
|
|
(
|
|
"parse-object-deferred",
|
|
r#"
|
|
(module main)
|
|
|
|
(fn main () -> i32
|
|
(std.json.parse_object_result "{}"))
|
|
"#,
|
|
"UnsupportedStandardLibraryCall",
|
|
),
|
|
(
|
|
"promoted-shadow",
|
|
r#"
|
|
(module main)
|
|
|
|
(fn std.json.quote_string ((value string)) -> string
|
|
value)
|
|
|
|
(fn main () -> i32
|
|
(if (= (std.json.quote_string "x") "\"x\"") 0 1))
|
|
"#,
|
|
"DuplicateFunction",
|
|
),
|
|
];
|
|
|
|
for (name, source, diagnostic) in cases {
|
|
let fixture = write_fixture(name, source);
|
|
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 `{}`\nstdout:\n{}\nstderr:\n{}",
|
|
name,
|
|
stdout,
|
|
stderr
|
|
);
|
|
assert!(
|
|
stdout.is_empty(),
|
|
"rejected compile wrote stdout:\n{}",
|
|
stdout
|
|
);
|
|
assert!(
|
|
stderr.contains(diagnostic),
|
|
"diagnostic `{}` was not reported for `{}`\nstderr:\n{}",
|
|
diagnostic,
|
|
name,
|
|
stderr
|
|
);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn hosted_json_quote_smoke_when_clang_is_available() {
|
|
if !clang_is_available() {
|
|
eprintln!("skipping standard json runtime smoke: set GLAGOL_CLANG or install clang");
|
|
return;
|
|
}
|
|
|
|
let fixture = write_fixture(
|
|
"hosted",
|
|
r#"
|
|
(module main)
|
|
|
|
(fn main () -> i32
|
|
(if (= (std.json.quote_string "line\nnext") "\"line\\nnext\"")
|
|
0
|
|
1))
|
|
"#,
|
|
);
|
|
let binary = fixture.with_extension(env::consts::EXE_EXTENSION);
|
|
let build = run_glagol([
|
|
OsStr::new("build"),
|
|
fixture.as_os_str(),
|
|
OsStr::new("-o"),
|
|
binary.as_os_str(),
|
|
]);
|
|
assert_success("build standard json hosted smoke", &build);
|
|
|
|
let run = Command::new(&binary)
|
|
.output()
|
|
.unwrap_or_else(|err| panic!("run `{}`: {}", binary.display(), err));
|
|
assert_success("run standard json hosted smoke", &run);
|
|
}
|
|
|
|
fn write_fixture(name: &str, source: &str) -> PathBuf {
|
|
let id = NEXT_FIXTURE_ID.fetch_add(1, Ordering::Relaxed);
|
|
let dir = env::temp_dir().join(format!("glagol-standard-json-{id}-{name}"));
|
|
fs::create_dir_all(&dir).unwrap_or_else(|err| panic!("create `{}`: {}", dir.display(), err));
|
|
let path = dir.join("main.slo");
|
|
fs::write(&path, source).unwrap_or_else(|err| panic!("write `{}`: {}", path.display(), err));
|
|
path
|
|
}
|
|
|
|
fn clang_is_available() -> bool {
|
|
if env::var_os("GLAGOL_CLANG").is_some() {
|
|
return true;
|
|
}
|
|
Command::new("clang")
|
|
.arg("--version")
|
|
.output()
|
|
.is_ok_and(|output| output.status.success())
|
|
}
|
|
|
|
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)
|
|
.current_dir(Path::new(env!("CARGO_MANIFEST_DIR")))
|
|
.output()
|
|
.expect("run glagol")
|
|
}
|
|
|
|
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);
|
|
}
|