;; lib/guest/test-runner.sx — per-suite test harness for guest test files. ;; ;; Across the codebase 142+ test files implement the identical four-form ;; boilerplate: `-test-pass`, `-test-fail`, `-test-fails`, and ;; an `-test` recording function. Only the prefix differs. This kit ;; collapses the boilerplate to a per-suite mutable dict + a recording ;; helper, so each test file goes from ~12 lines of harness to ~3: ;; ;; (define ke-suite (refl-make-test-suite)) ;; (define ke-test (fn (n a e) (refl-test ke-suite n a e))) ;; (define ke-tests-run! (fn () (refl-test-report ke-suite))) ;; ;; The suite is a mutable dict `{:pass N :fail N :fails LIST}`. Each ;; failed assertion appends `{:name NAME :expected EXPECTED :actual ACT}` ;; to :fails — same shape every existing harness already produces. ;; ;; The `:fails` list is mutated in place via `append!`, so callers who ;; have a reference to it see the same updates. (Same semantic the ;; existing per-suite globals had — just held in the suite dict now.) ;; ;; Public API ;; (refl-make-test-suite) — fresh suite ;; (refl-test SUITE NAME ACT EXP) — record one assertion ;; (refl-test-report SUITE) — return {:total :passed :failed :fails} ;; (refl-test-pass? SUITE) — convenience: all green? ;; (refl-test-suite? V) — predicate (define refl-make-test-suite (fn () {:fail 0 :pass 0 :fails (list)})) (define refl-test-suite? (fn (v) (and (dict? v) (number? (get v :pass)) (number? (get v :fail))))) (define refl-test (fn (suite name actual expected) (cond ((= actual expected) (dict-set! suite :pass (+ (get suite :pass) 1))) (:else (begin (dict-set! suite :fail (+ (get suite :fail) 1)) (append! (get suite :fails) {:name name :actual actual :expected expected})))))) (define refl-test-report (fn (suite) {:total (+ (get suite :pass) (get suite :fail)) :passed (get suite :pass) :failed (get suite :fail) :fails (get suite :fails)})) (define refl-test-pass? (fn (suite) (= (get suite :fail) 0)))