Files
AILang/crates/ailang-check/tests/embed_export_gate.rs
T
Brummel 7ae92d3e60 refactor(test): hoist duplicated test helpers into ailang-test-support
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.
2026-06-02 01:54:37 +02:00

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"));
}