7ae92d3e60
closes #60 The fixture-corpus filter (NON_PARSEABLE_FIXTURES + list_ail_fixtures) was copy-pasted across three test crates and drifted out of sync as new reject fixtures landed; the examples_dir / workspace_root path walk was reimplemented ~30 more times across the suite, under two spellings (workspace_root / ws_root). Introduce crates/ailang-test-support — a zero-dependency, dev-only leaf crate — as the single home for these helpers: - NON_PARSEABLE_FIXTURES, list_ail_fixtures - workspace_root, examples_dir - canonical_workspace_root (symlink-resolved variant, for the tsan/ race tests that feed the root into native build steps) All integration-test copies across ailang-core, ailang-surface, ailang-prose, ailang-check, and ail now import from the shared crate; the local definitions and their now-orphaned path imports are removed. Path helpers resolve via the support crate's own CARGO_MANIFEST_DIR, so they return the correct absolute paths from any caller. ail_bin helpers are intentionally left in place: they depend on env!("CARGO_BIN_EXE_ail"), which Cargo only defines when compiling the ail crate's own integration tests, so they cannot move to a shared crate. Behaviour-identical: same paths, same fixture lists; full workspace test suite green. Net -177 LOC.
93 lines
3.2 KiB
Rust
93 lines
3.2 KiB
Rust
//! Embedding-ABI M1 export-signature gate (spec §3, clause-3
|
|
//! discriminator): an `(export …)` fn whose params/ret are not all
|
|
//! `Int`/`Float`, or whose effect set is non-empty, MUST fail
|
|
//! `ail check` with the new diagnostic. A scalar+pure export passes.
|
|
|
|
use ailang_test_support::examples_dir;
|
|
|
|
fn check_codes(file: &str) -> Vec<String> {
|
|
let path = examples_dir().join(file);
|
|
let ws = ailang_surface::load_workspace(&path)
|
|
.unwrap_or_else(|e| panic!("load {file}: {e}"));
|
|
ailang_check::check_workspace(&ws)
|
|
.into_iter()
|
|
.map(|d| d.code)
|
|
.collect()
|
|
}
|
|
|
|
#[test]
|
|
fn str_param_export_rejected() {
|
|
assert!(check_codes("embed_export_str_param_rejected.ail")
|
|
.iter().any(|c| c == "export-non-scalar-signature"));
|
|
}
|
|
|
|
// M3 RE-POINT (deliberate, reviewed — spec 2026-05-18-embedding-abi-m3
|
|
// §"must-fail axis"): the fixture this asserts on
|
|
// (embed_export_adt_ret_rejected.ail) is single-ctor two-Int = M3-VALID.
|
|
// Task 2 re-points the fixture to a genuinely-still-rejected ADT
|
|
// (multi-ctor + Str field). This pin's *meaning* changes from
|
|
// "any ADT rejected" to "non-M3-shaped ADT rejected"; it is NOT
|
|
// silently inverted — the rejection (export-non-scalar-signature)
|
|
// stays asserted, on a fixture that still genuinely fails it.
|
|
#[test]
|
|
fn adt_ret_export_rejected() {
|
|
assert!(check_codes("embed_export_adt_ret_rejected.ail")
|
|
.iter().any(|c| c == "export-non-scalar-signature"));
|
|
}
|
|
|
|
#[test]
|
|
fn io_export_rejected() {
|
|
assert!(check_codes("embed_export_io_rejected.ail")
|
|
.iter().any(|c| c == "export-has-effects"));
|
|
}
|
|
|
|
#[test]
|
|
fn combined_effectful_export_rejected_on_signature_first() {
|
|
let codes = check_codes("embed_export_effectful_rejected.ail");
|
|
assert!(codes.iter().any(|c| c == "export-non-scalar-signature"),
|
|
"combined fixture must fail signature-first; got {codes:?}");
|
|
}
|
|
|
|
#[test]
|
|
fn scalar_pure_export_passes() {
|
|
let codes = check_codes("embed_export_float_ok.ail");
|
|
assert!(!codes.iter().any(|c|
|
|
c == "export-non-scalar-signature" || c == "export-has-effects"),
|
|
"scalar+pure export must pass the gate; got {codes:?}");
|
|
}
|
|
|
|
#[test]
|
|
fn headline_int_export_passes() {
|
|
let codes = check_codes("embed_backtest_step.ail");
|
|
assert!(!codes.iter().any(|c|
|
|
c == "export-non-scalar-signature" || c == "export-has-effects"),
|
|
"headline export must pass the gate; got {codes:?}");
|
|
}
|
|
|
|
#[test]
|
|
fn record_export_passes() {
|
|
// single-ctor (Float,Int) record export — M3-valid
|
|
let codes = check_codes("embed_export_record_ok.ail");
|
|
assert!(!codes.iter().any(|c|
|
|
c == "export-non-scalar-signature" || c == "export-has-effects"),
|
|
"single-ctor scalar record export must pass the gate; got {codes:?}");
|
|
}
|
|
|
|
#[test]
|
|
fn str_field_record_export_rejected() {
|
|
assert!(check_codes("embed_export_str_field_record_rejected.ail")
|
|
.iter().any(|c| c == "export-non-scalar-signature"));
|
|
}
|
|
|
|
#[test]
|
|
fn list_field_record_export_rejected() {
|
|
assert!(check_codes("embed_export_list_field_record_rejected.ail")
|
|
.iter().any(|c| c == "export-non-scalar-signature"));
|
|
}
|
|
|
|
#[test]
|
|
fn multictor_export_rejected() {
|
|
assert!(check_codes("embed_export_multictor_rejected.ail")
|
|
.iter().any(|c| c == "export-non-scalar-signature"));
|
|
}
|