Files
rose-ash/spec/tests/test-gate-pins.sx
giles 88e03daf4b W14: pin K49 void-elements spec fix; discover sx_render.ml regen drift (test-only)
K49: area/base/embed/param/track were in VOID_ELEMENTS but missing from
HTML_TAGS — render fell through to "Undefined symbol: base". dc7aa709 fixed
spec/render.sx; add suite gate-K49-void-elements-renderable (3 tests): the
spec registry contains all five, and render-to-html renders each as a
self-closing void. 264 passed / 0 failed under OCaml run_tests.

DISCOVERY (recorded in the briefing's Blocked section): the generated
hosts/ocaml/lib/sx_render.ml was never regenerated after the spec fix — its
stale html_tags_list still lacks the five tags, so the runner's native
render-html path STILL errors. Fix is a bootstrap_render.py regen (hosts
lane, out of scope for this test-only loop). Live evidence for F13
(regen-diff CI gate). Pin covers the spec side only for now.

Also corrects the checklist label: K49 = void elements; the depth/cycle
guard is K16 (OPEN, W8).

Test-only: no semantics edits, no push.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 23:43:34 +00:00

164 lines
6.5 KiB
Plaintext

;; ==========================================================================
;; test-gate-pins.sx — W14 regression pins for the review's landed fixes
;;
;; The quick-wins batch (commit dc7aa709 + siblings) landed real semantics
;; fixes but shipped WITHOUT pinning tests, so a regression would pass
;; silently. This file pins each confirmed-and-fixed finding with a minimal
;; repro lifted from the review lane files (plans/sx-review/*.md). One suite
;; per finding.
;;
;; TEST-ONLY: no semantics edits. If a pin fails, the fix regressed — do NOT
;; relax the assertion; investigate the evaluator/primitive change.
;; NB: assert= uses `=` (not `equal?`); compare lists with `=`.
;; ==========================================================================
;; --------------------------------------------------------------------------
;; K18 [W7, high] expt silently wrapped at 63-bit int — now promotes to float
;; like +/*. Repro (core.md): (expt 2 62) -> -4611686018427387904 (wrapped);
;; (expt 2 100) -> 0. Fixed: both are positive floats.
;; --------------------------------------------------------------------------
(defsuite
"gate-K18-expt-overflow"
(deftest
"small integer exponents stay exact"
(do
(assert= (expt 2 0) 1)
(assert= (expt 2 10) 1024)))
(deftest
"expt 2^62 does not wrap to a negative int"
(assert (> (expt 2 62) 0)))
(deftest
"expt 2^100 does not wrap to zero"
(assert (> (expt 2 100) 0)))
(deftest
"expt 2^100 promotes to float"
(assert (number? (expt 2 100)))))
;; --------------------------------------------------------------------------
;; K20 [W7, high] contains? did not support dicts in the real runtime —
;; (contains? {:a 1} :a) threw "contains?: 2 args", contradicting its :doc
;; ("Dicts: key check"). Fixed: dict key membership works; lists/strings
;; unchanged. Repro (core.md).
;; --------------------------------------------------------------------------
(defsuite
"gate-K20-contains-dict"
(deftest
"contains? finds a present dict key"
(assert (contains? {:a 1 :b 2} :a)))
(deftest
"contains? reports a missing dict key as false"
(assert (not (contains? {:a 1 :b 2} :zz))))
(deftest
"contains? still works on list membership"
(do
(assert (contains? (list 10 20 30) 20))
(assert
(not (contains? (list 10 20 30) 99)))))
(deftest
"contains? still works on string substrings"
(assert (contains? "hello" "ell"))))
;; --------------------------------------------------------------------------
;; K09 [W5, high] R7RS longhand (unquote-splicing X) silently no-spliced —
;; only shorthand ,@/`splice-unquote` was recognized, so the longhand
;; serialized literally (zero-splice). Fixed: aliased to splice-unquote.
;; Repro (core.md).
;; --------------------------------------------------------------------------
(defsuite
"gate-K09-longhand-unquote-splicing"
(deftest
"longhand unquote-splicing splices a list"
(assert=
(quasiquote
(1
(unquote-splicing (list 2 3))
4))
(list 1 2 3 4)))
(deftest
"longhand unquote-splicing of an empty list contributes nothing"
(assert=
(quasiquote (0 (unquote-splicing (list)) 9))
(list 0 9)))
(deftest
"shorthand splice-unquote still works"
(assert=
(quasiquote (a (splice-unquote (list 2 3)) z))
(list (quote a) 2 3 (quote z)))))
;; --------------------------------------------------------------------------
;; K11 [W5, high] guard re-raise sentinel was a plain forgeable symbol — a
;; body/clause legitimately returning (list '__guard-reraise__ X) was
;; misread as a re-raise of X. Fixed: sentinel gensym'd per execution, so a
;; user value with that head is returned as data. Repro (core.md).
;; --------------------------------------------------------------------------
(defsuite
"gate-K11-guard-reraise-forgeable"
(deftest
"body value shaped like the sentinel is returned as data"
(assert=
(guard (e (true "caught")) (list (quote __guard-reraise__) "hi"))
(list (quote __guard-reraise__) "hi")))
(deftest
"clause returning the forged sentinel is not re-raised"
(assert=
(guard
(e (true (list (quote __guard-reraise__) "forged")))
(error "boom"))
(list (quote __guard-reraise__) "forged"))))
;; --------------------------------------------------------------------------
;; K39 [W5, med] `do` misparsed a first form whose head is a list (an IIFE)
;; as a Scheme do-loop binding spec. Repro (core.md): (do ((fn (x) x) 5) 99)
;; threw "first: expected list, got 5"; expected 99. Fixed: `do` is begin.
;; --------------------------------------------------------------------------
(defsuite
"gate-K39-do-iife-head"
(deftest
"do with an IIFE first form returns the last form (not a do-loop)"
(assert= (do ((fn (x) x) 5) 99) 99))
(deftest
"do with a single IIFE form returns its value"
(assert= (do ((fn () 42))) 42)))
;; --------------------------------------------------------------------------
;; K49 [W8, med] Five void elements (area base embed param track) were in
;; VOID_ELEMENTS but missing from HTML_TAGS — render fell through to
;; function-call dispatch: (render-to-html '(base :href "x")) threw
;; "Undefined symbol: base". dc7aa709 fixed the SPEC registry
;; (spec/render.sx). NB: the generated OCaml render library
;; (hosts/ocaml/lib/sx_render.ml, bootstrap_render.py output) still carries
;; a STALE html_tags_list without these five — the runner's native
;; `render-html` convenience therefore still errors. That regen drift is
;; W14 item F13 (regen-diff gate); this suite pins the spec side only.
;; --------------------------------------------------------------------------
(defsuite
"gate-K49-void-elements-renderable"
(deftest
"spec HTML_TAGS registry contains all five void elements"
(for-each
(fn
(t)
(assert (contains? HTML_TAGS t) (str "HTML_TAGS missing " t)))
(list "area" "base" "embed" "param" "track")))
(deftest
"spec render-to-html renders base self-closing with attr"
(assert-equal
"<base href=\"x\" />"
(render-to-html (quote (base :href "x")) (make-env))))
(deftest
"spec render-to-html renders all five as self-closing voids"
(for-each
(fn
(form)
(let
((html (render-to-html form (make-env))))
(assert
(string-contains? html "/>")
(str (first form) " not self-closing: " html))))
(list
(quote (area))
(quote (base))
(quote (embed))
(quote (param))
(quote (track))))))