control the claim that cannot be unit tested
The seam's whole claim is that a construct declared AFTER a binary exists applies to that already-built program. compile_capture only sees emitted text, so it structurally cannot check this: it needs a built binary, a linked target, and an environment. Verified by hand until now, which is the standing problem this session has been about. tests/integration/seam_binding.sh builds a probe from El source containing no construct at all, links a target that El never references, and asserts: ok unbound program is unaffected ok a construct declared AFTER the build applies ok a construct declared after the build can REFUSE ok an unlinked target is skipped, not fatal ok a binding for a different fn does not fire ok two constructs compose on one crossing 6 assertions, 6 passed, 0 failed The eight controls that failed after the strip were replaced, not repaired. They asserted compile-time emission of capability that moved to runtime; contorting them would have kept an assertion whose subject no longer exists. Three took their place, asserting the emitted shape, and the behaviour they used to cover is now the integration harness's job -- which is the honest division, since the shape and the behaviour are no longer the same fact. 99/99 native compiler tests pass. Fixpoint holds.
This commit is contained in:
Executable
+85
@@ -0,0 +1,85 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# seam_binding.sh — integration control for the runtime construct seam.
|
||||||
|
#
|
||||||
|
# The seam's whole claim is that a construct declared AFTER a binary exists
|
||||||
|
# applies to that already-built program. That cannot be checked by
|
||||||
|
# compile_capture, which only sees emitted text: it needs a built binary, a
|
||||||
|
# linked target, and an environment. Hence a harness rather than a unit test.
|
||||||
|
#
|
||||||
|
# usage: seam_binding.sh <elc-binary> [lang-dir]
|
||||||
|
# exit 0 = all assertions held; non-zero = number of failures
|
||||||
|
set -uo pipefail
|
||||||
|
ELC="${1:?usage: seam_binding.sh <elc-binary> [lang-dir]}"
|
||||||
|
LANG_DIR="${2:-$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)}"
|
||||||
|
WORK="$(mktemp -d)"; trap 'rm -rf "$WORK"' EXIT
|
||||||
|
FAILS=0
|
||||||
|
|
||||||
|
ok() { printf ' ok %s\n' "$1"; }
|
||||||
|
fail() { printf ' FAIL %s\n expected: %s\n actual: %s\n' "$1" "$2" "$3"; FAILS=$((FAILS+1)); }
|
||||||
|
check(){ [ "$2" = "$3" ] && ok "$1" || fail "$1" "$2" "$3"; }
|
||||||
|
|
||||||
|
SRCS=$("$LANG_DIR/../scripts/el-runtime-sources.sh" "$LANG_DIR/runtime")
|
||||||
|
CFLAGS="-std=c11 -O2 -rdynamic -I $LANG_DIR/runtime"
|
||||||
|
for d in /opt/homebrew/opt/openssl@3 /usr/local/opt/openssl@3; do
|
||||||
|
[ -d "$d" ] && CFLAGS="$CFLAGS -I $d/include" && LDFLAGS="-L $d/lib"
|
||||||
|
done
|
||||||
|
LDFLAGS="${LDFLAGS:-} -lcurl -lssl -lcrypto -lpthread -lm"
|
||||||
|
|
||||||
|
# A construct target that is LINKED but never referenced from El source.
|
||||||
|
cat > "$WORK/targets.c" <<'EOF'
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
typedef int64_t el_val_t;
|
||||||
|
el_val_t observe(el_val_t fn, el_val_t con, el_val_t r){
|
||||||
|
printf("SEEN %s/%s\n", (const char*)(intptr_t)fn, (const char*)(intptr_t)con);
|
||||||
|
return r; /* zero = do not refuse */
|
||||||
|
}
|
||||||
|
el_val_t refuse(el_val_t fn, el_val_t con, el_val_t r){
|
||||||
|
(void)fn; (void)con; (void)r; return 42; /* non-zero = short-circuit */
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# A program with NO construct anywhere in its source.
|
||||||
|
cat > "$WORK/prog.el" <<'EOF'
|
||||||
|
fn work() -> Int {
|
||||||
|
return 7
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
println(int_to_str(work()))
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
"$ELC" "$WORK/prog.el" > "$WORK/prog.c" 2>/dev/null
|
||||||
|
cc $CFLAGS -o "$WORK/prog" "$WORK/prog.c" "$WORK/targets.c" $SRCS $LDFLAGS 2>/dev/null \
|
||||||
|
|| { echo " FAIL probe did not build"; exit 1; }
|
||||||
|
|
||||||
|
check "unbound program is unaffected" \
|
||||||
|
"7" "$(cd "$WORK" && ./prog 2>&1)"
|
||||||
|
|
||||||
|
printf 'work audited entry observe\n' > "$WORK/observe.txt"
|
||||||
|
check "a construct declared AFTER the build applies" \
|
||||||
|
"SEEN work/audited
|
||||||
|
7" "$(cd "$WORK" && EL_CONSTRUCTS=observe.txt ./prog 2>&1)"
|
||||||
|
|
||||||
|
printf 'work denied entry refuse\n' > "$WORK/refuse.txt"
|
||||||
|
check "a construct declared after the build can REFUSE" \
|
||||||
|
"42" "$(cd "$WORK" && EL_CONSTRUCTS=refuse.txt ./prog 2>&1)"
|
||||||
|
|
||||||
|
printf 'work ghost entry no_such_symbol_anywhere\n' > "$WORK/ghost.txt"
|
||||||
|
check "an unlinked target is skipped, not fatal" \
|
||||||
|
"7" "$(cd "$WORK" && EL_CONSTRUCTS=ghost.txt ./prog 2>&1)"
|
||||||
|
|
||||||
|
printf 'other_fn x entry refuse\n' > "$WORK/other.txt"
|
||||||
|
check "a binding for a different fn does not fire" \
|
||||||
|
"7" "$(cd "$WORK" && EL_CONSTRUCTS=other.txt ./prog 2>&1)"
|
||||||
|
|
||||||
|
printf 'work a entry observe\nwork b entry observe\n' > "$WORK/two.txt"
|
||||||
|
check "two constructs compose on one crossing" \
|
||||||
|
"SEEN work/a
|
||||||
|
SEEN work/b
|
||||||
|
7" "$(cd "$WORK" && EL_CONSTRUCTS=two.txt ./prog 2>&1)"
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo " 6 assertions, $((6-FAILS)) passed, $FAILS failed"
|
||||||
|
exit $FAILS
|
||||||
@@ -735,19 +735,7 @@ test "compiler-stdint-include" {
|
|||||||
// be measured and "is this decorator earning its keep" stays an argument
|
// be measured and "is this decorator earning its keep" stays an argument
|
||||||
// instead of a query.
|
// instead of a query.
|
||||||
|
|
||||||
test "decorator-manager-beat-carries-construct" {
|
|
||||||
let src: String = "@manager\nfn f() -> Int { return 1 }"
|
|
||||||
let out: String = compile_capture(src)
|
|
||||||
assert str_contains(out, "engram_boundary_beat"), "@manager injects the beat"
|
|
||||||
assert str_contains(out, "EL_STR(\"manager\")"), "beat carries the construct that caused it"
|
|
||||||
}
|
|
||||||
|
|
||||||
test "decorator-accessor-beat-carries-construct" {
|
|
||||||
let src: String = "@accessor\nfn f() -> Int { return 1 }"
|
|
||||||
let out: String = compile_capture(src)
|
|
||||||
assert str_contains(out, "engram_boundary_beat"), "@accessor injects the beat"
|
|
||||||
assert str_contains(out, "EL_STR(\"accessor\")"), "beat carries the construct that caused it"
|
|
||||||
}
|
|
||||||
|
|
||||||
test "decorator-undecorated-fn-has-no-beat" {
|
test "decorator-undecorated-fn-has-no-beat" {
|
||||||
let src: String = "fn f() -> Int { return 1 }"
|
let src: String = "fn f() -> Int { return 1 }"
|
||||||
@@ -778,12 +766,6 @@ test "decorator-authenticate-compiles-to-nothing" {
|
|||||||
// A construct declares its own meaning and codegen reads it. Adding a
|
// A construct declares its own meaning and codegen reads it. Adding a
|
||||||
// construct is a declaration in the program; it does not touch the compiler.
|
// construct is a declaration in the program; it does not touch the compiler.
|
||||||
|
|
||||||
test "declared-construct-injects-without-compiler-knowledge" {
|
|
||||||
let src: String = "@decorator(\"injects_at_entry\", \"engram_boundary_beat\")\nfn audited() {}\n@audited\nfn risky() -> Int { return 7 }"
|
|
||||||
let out: String = compile_capture(src)
|
|
||||||
assert str_contains(out, "engram_boundary_beat(EL_STR(\"risky\")"), "a program-declared construct injects"
|
|
||||||
assert str_contains(out, "EL_STR(\"audited\")"), "the beat is attributed to the declared construct"
|
|
||||||
}
|
|
||||||
|
|
||||||
test "declared-construct-name-unknown-to-codegen" {
|
test "declared-construct-name-unknown-to-codegen" {
|
||||||
// The name is arbitrary. Nothing in the compiler mentions it.
|
// The name is arbitrary. Nothing in the compiler mentions it.
|
||||||
@@ -798,13 +780,6 @@ test "undeclared-construct-still-injects-nothing" {
|
|||||||
assert !str_contains(out, "engram_boundary_beat"), "an undeclared construct injects nothing"
|
assert !str_contains(out, "engram_boundary_beat"), "an undeclared construct injects nothing"
|
||||||
}
|
}
|
||||||
|
|
||||||
test "builtin-constructs-still-inject" {
|
|
||||||
// manager/accessor are the compiled-in core, seeded not branched.
|
|
||||||
let src: String = "@manager\nfn m() -> Int { return 1 }\n@accessor\nfn a() -> Int { return 2 }"
|
|
||||||
let out: String = compile_capture(src)
|
|
||||||
assert str_contains(out, "EL_STR(\"manager\")"), "seeded manager still injects"
|
|
||||||
assert str_contains(out, "EL_STR(\"accessor\")"), "seeded accessor still injects"
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── Declared constructs: guards ──────────────────────────────────────────────
|
// ── Declared constructs: guards ──────────────────────────────────────────────
|
||||||
//
|
//
|
||||||
@@ -813,29 +788,8 @@ test "builtin-constructs-still-inject" {
|
|||||||
// needed and never had — fourteen applications that read as protection and
|
// needed and never had — fourteen applications that read as protection and
|
||||||
// emitted no instruction.
|
// emitted no instruction.
|
||||||
|
|
||||||
test "declared-guard-emits-refusable-check" {
|
|
||||||
let src: String = "@decorator(\"guards_at_entry\", \"my_auth\")\nfn authenticate() {}\n@authenticate\nfn handler() -> Int { return 7 }"
|
|
||||||
let out: String = compile_capture(src)
|
|
||||||
assert str_contains(out, "my_auth(EL_STR(\"handler\")"), "the guard is called at entry"
|
|
||||||
assert str_contains(out, "if (__g) return __g;"), "a non-zero guard result short-circuits the fn"
|
|
||||||
}
|
|
||||||
|
|
||||||
test "declared-guards-stack-in-order" {
|
|
||||||
let src: String = "@decorator(\"guards_at_entry\", \"my_auth\")\nfn authenticate() {}\n@decorator(\"guards_at_entry\", \"my_roles\")\nfn authorize() {}\n@authenticate\n@authorize\nfn handler() -> Int { return 7 }"
|
|
||||||
let out: String = compile_capture(src)
|
|
||||||
assert str_contains(out, "my_auth("), "first guard runs"
|
|
||||||
assert str_contains(out, "my_roles("), "second guard runs — every guard applies, not just the topmost"
|
|
||||||
}
|
|
||||||
|
|
||||||
test "guard-precedes-injection" {
|
|
||||||
// A refused call must not report a boundary crossing.
|
|
||||||
let src: String = "@decorator(\"guards_at_entry\", \"my_auth\")\nfn authenticate() {}\n@authenticate\n@manager\nfn handler() -> Int { return 7 }"
|
|
||||||
let out: String = compile_capture(src)
|
|
||||||
let g: Int = str_index_of(out, "my_auth(")
|
|
||||||
let b: Int = str_index_of(out, "engram_boundary_beat(EL_STR(\"handler\")")
|
|
||||||
assert g < b, "the guard is emitted before the beat"
|
|
||||||
assert g >= 0, "guard present"
|
|
||||||
}
|
|
||||||
|
|
||||||
test "undeclared-guard-emits-nothing" {
|
test "undeclared-guard-emits-nothing" {
|
||||||
let src: String = "@not_a_declared_guard\nfn handler() -> Int { return 7 }"
|
let src: String = "@not_a_declared_guard\nfn handler() -> Int { return 7 }"
|
||||||
@@ -865,16 +819,6 @@ test "no-exit-construct-emits-no-wrapper" {
|
|||||||
assert !str_contains(out, "__el_body_"), "fns without an exit construct are unwrapped, byte for byte as before"
|
assert !str_contains(out, "__el_body_"), "fns without an exit construct are unwrapped, byte for byte as before"
|
||||||
}
|
}
|
||||||
|
|
||||||
test "constructs-compose-guard-entry-exit" {
|
|
||||||
let src: String = "@decorator(\"guards_at_entry\", \"my_auth\")\nfn authenticate() {}\n@decorator(\"injects_at_exit\", \"persist_now\")\nfn durable() {}\n@authenticate\n@durable\n@manager\nfn op() -> Int { return 1 }"
|
|
||||||
let out: String = compile_capture(src)
|
|
||||||
let g: Int = str_index_of(out, "my_auth(")
|
|
||||||
let b: Int = str_index_of(out, "engram_boundary_beat(EL_STR(\"op\")")
|
|
||||||
let x: Int = str_index_of(out, "persist_now(")
|
|
||||||
assert g < b, "guard before entry injection"
|
|
||||||
assert b < x, "entry injection before exit injection"
|
|
||||||
assert x >= 0, "three independent constructs compose on one fn"
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── Declared constructs: wraps and prohibitions ──────────────────────────────
|
// ── Declared constructs: wraps and prohibitions ──────────────────────────────
|
||||||
|
|
||||||
@@ -924,3 +868,31 @@ test "seam-emitted-without-any-decorator" {
|
|||||||
assert str_contains(out, "el_seam_run"), "an undecorated fn is still bindable at runtime"
|
assert str_contains(out, "el_seam_run"), "an undecorated fn is still bindable at runtime"
|
||||||
assert !str_contains(out, "engram_boundary_beat"), "and nothing is inlined for it"
|
assert !str_contains(out, "engram_boundary_beat"), "and nothing is inlined for it"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ── Runtime seam: what replaced the compile-time entry mechanism ─────────────
|
||||||
|
//
|
||||||
|
// Entry injection and refusal moved from emission to execution. These assert
|
||||||
|
// the emitted shape; the BEHAVIOUR — that a construct declared after the build
|
||||||
|
// applies, refuses, composes, and that an unlinked target is skipped — is
|
||||||
|
// covered by tests/integration/seam_binding.sh, which needs a built binary and
|
||||||
|
// an environment and therefore cannot be a compile_capture test.
|
||||||
|
|
||||||
|
test "seam-replaces-inlined-entry-injection" {
|
||||||
|
let src: String = "@manager\nfn m() -> Int { return 1 }"
|
||||||
|
let out: String = compile_capture(src)
|
||||||
|
assert str_contains(out, "el_seam_run(EL_STR(\"m\")"), "the crossing goes through the seam"
|
||||||
|
assert !str_contains(out, "engram_boundary_beat(EL_STR(\"m\")"), "nothing is inlined at the crossing any more"
|
||||||
|
}
|
||||||
|
|
||||||
|
test "seam-entry-is-refusable" {
|
||||||
|
let src: String = "fn f() -> Int { return 1 }"
|
||||||
|
let out: String = compile_capture(src)
|
||||||
|
assert str_contains(out, "if (__s) return __s;"), "a bound construct can short-circuit the fn"
|
||||||
|
}
|
||||||
|
|
||||||
|
test "seam-is-emitted-for-undecorated-fns" {
|
||||||
|
let src: String = "fn plain() -> Int { return 1 }"
|
||||||
|
let out: String = compile_capture(src)
|
||||||
|
assert str_contains(out, "el_seam_run(EL_STR(\"plain\")"), "any fn is bindable later, decorated or not"
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user