jolt/host/chez/run-unit.ss
Yogthos 33eff7c7d8 Clean up codebase: rename stdlib layer, strip porting residue, fix tooling
Rename src/jolt -> stdlib (the runtime-loaded layer; jolt-core stays the
seed-baked layer) and update the loader / emit-image / doc paths. Drop dead
code: the spike/ experiments, the duplicate clojuredocs-export.edn (json moves
to tools/), the Janet-era jolt.http binding, and the orphaned
persistent_vector.clj whose ns/path didn't even match.

Strip porting residue from comments and docstrings across host/chez, jolt-core,
stdlib, tests, and docs: internal issue ids, "Phase N" markers, and the "vs
Janet" historical exposition, leaving present-tense descriptions and the real
JVM-Clojure semantic contrasts. Same pass over the corpus suite labels. The seed
is unchanged (docstrings/comments aren't emitted), so the self-host fixpoint and
corpus are untouched.

Port tools/spec_coverage.py off the dead janet probe to bin/joltc and regenerate
coverage.md; drop the dead :host/janet rule from certify.clj and regenerate the
conformance profile. Add docs/host-interop.md (the JVM shims and how to register
your own host class from a library) and a writing-style note in CLAUDE.md.

Stabilize the four racy concurrency corpus cases (future-cancel and agent
send/send-off): give the future a sleeping body and the agent a slow action, so
cancel reliably catches an in-flight future and deref reliably reads the
pre-update snapshot. They certify deterministically now, so drop their :flaky
allowlist entries and the orphaned legend.
2026-06-22 22:18:00 -04:00

104 lines
4.9 KiB
Scheme

;; run-unit.ss — host-specific unit gate.
;;
;; Loads the checked-in seed + spine, reads test/chez/unit.edn, and for each case
;; evaluates :expr (wrapped in (do ...), as `joltc -e` does) and compares its PRINTED
;; value (jolt-final-str) to the literal :expected string. :expected :throws asserts
;; the case raises. These cover host-specific behavior (dot-forms, java statics, io,
;; reader, walk, …) that isn't in the JVM-portable corpus. Global state is reset
;; between cases for per-case isolation.
;;
;; chez --script host/chez/run-unit.ss
(import (chezscheme))
(load "host/chez/rt.ss")
(set-chez-ns! "clojure.core")
(load "host/chez/seed/prelude.ss")
(load "host/chez/post-prelude.ss")
(set-chez-ns! "user")
(load "host/chez/host-contract.ss")
(load "host/chez/seed/image.ss")
(load "host/chez/compile-eval.ss")
(define (slurp path)
(call-with-input-file path
(lambda (p)
(let loop ((cs '()) (c (read-char p)))
(if (eof-object? c) (list->string (reverse cs))
(loop (cons c cs) (read-char p)))))))
(define cases (jolt-read-string (slurp "test/chez/unit.edn")))
(define kw-suite (keyword #f "suite"))
(define kw-expr (keyword #f "expr"))
(define kw-expected (keyword #f "expected"))
(define kw-throws (keyword #f "throws"))
;; --- per-case isolation (snapshot the world after setup, restore each case) -------
(define zj-base (let ((h (make-hashtable string-hash string=?)))
(vector-for-each (lambda (k) (hashtable-set! h k #t)) (hashtable-keys var-table)) h))
(define zj-roots '())
(vector-for-each (lambda (k) (let ((c (hashtable-ref var-table k #f)))
(when c (set! zj-roots (cons (cons c (var-cell-root c)) zj-roots)))))
(hashtable-keys var-table))
(define (zj-snap ht) (let ((h (make-hashtable string-hash string=?)))
(vector-for-each (lambda (k) (hashtable-set! h k #t)) (hashtable-keys ht)) h))
(define (zj-prune! ht base) (vector-for-each
(lambda (k) (unless (hashtable-ref base k #f) (hashtable-delete! ht k))) (hashtable-keys ht)))
(define zj-ns-base (zj-snap ns-registry))
(define zj-type-base (zj-snap type-registry))
(define zj-ghier (var-cell-lookup "clojure.core" "global-hierarchy"))
(define (zj-reset!)
(vector-for-each (lambda (k) (unless (hashtable-ref zj-base k #f) (hashtable-delete! var-table k)))
(hashtable-keys var-table))
(for-each (lambda (cr) (unless (eq? (var-cell-root (car cr)) (cdr cr))
(var-cell-root-set! (car cr) (cdr cr)))) zj-roots)
(zj-prune! ns-registry zj-ns-base)
(zj-prune! type-registry zj-type-base)
(hashtable-clear! ns-alias-table)
(hashtable-clear! ns-refer-table)
(hashtable-clear! ns-refer-all-table)
(when zj-ghier (jolt-invoke (var-deref "clojure.core" "reset!")
(var-cell-root zj-ghier) (jolt-invoke (var-deref "clojure.core" "make-hierarchy"))))
(set-chez-ns! "user"))
;; --- run ------------------------------------------------------------------------
(define pass 0)
(define fails '()) ; (suite expr msg)
(define suite-pass (make-hashtable string-hash string=?))
(define suite-total (make-hashtable string-hash string=?))
(define (bump! ht k) (hashtable-set! ht k (+ 1 (hashtable-ref ht k 0))))
(let loop ((i 0))
(when (< i (pvec-count cases))
(let* ((row (pvec-nth-d cases i jolt-nil))
(suite (jolt-get row kw-suite))
(expr (jolt-get row kw-expr))
(expected (jolt-get row kw-expected))
(throws? (eq? expected kw-throws))
(sink (open-output-string)))
(bump! suite-total suite)
(guard (e (#t (if throws?
(begin (set! pass (+ pass 1)) (bump! suite-pass suite))
(set! fails (cons (list suite expr "raised") fails)))))
(let ((got (jolt-final-str
(parameterize ((current-output-port sink))
(jolt-compile-eval (string-append "(do " expr ")") "user")))))
(cond
(throws? (set! fails (cons (list suite expr (string-append "expected throw; got " got)) fails)))
((string=? got expected) (begin (set! pass (+ pass 1)) (bump! suite-pass suite)))
(else (set! fails (cons (list suite expr
(string-append "want `" expected "` got `" got "`")) fails))))))
(zj-reset!))
(loop (+ i 1))))
(printf "\nunit gate: ~a/~a passed\n" pass (pvec-count cases))
(let-values (((ks vs) (hashtable-entries suite-total)))
(for-each (lambda (p)
(printf " ~a/~a ~a\n" (hashtable-ref suite-pass (car p) 0) (cdr p) (car p)))
(list-sort (lambda (a b) (string<? (car a) (car b)))
(vector->list (vector-map cons ks vs)))))
(when (> (length fails) 0)
(printf "\n~a FAIL(s):\n" (length fails))
(for-each (lambda (f) (printf " [~a] ~a\n ~a\n" (car f) (caddr f) (cadr f)))
(list-head (reverse fails) (min 40 (length fails)))))
(flush-output-port)
(exit (if (> (length fails) 0) 1 0))