Files
rose-ash/spec/tests/test-io-suspension.sx
giles 2727577702 VM import suspension for browser lazy loading
Bytecode compiler now emits OP_PERFORM for (import ...) and compiles
(define-library ...) bodies. The VM stores the import request in
globals["__io_request"] and stops the run loop — no exceptions needed.
vm-execute-module returns a suspension dict, vm-resume-module continues.

Browser: sx_browser.ml detects suspension dicts from execute_module and
returns JS {suspended, op, request, resume} objects. The sx-platform.js
while loop handles cascading suspensions via handleImportSuspension.

13 modules load via .sxbc bytecode in 226ms (manifest-driven), both
islands hydrate, all handlers wired. 2650/2650 tests pass including
6 new vm-import-suspension tests.

Also: consolidated sx-platform-2.js → sx-platform.js, fixed
vm-execute-module missing code-from-value call, fixed bootstrap.py
protocol registry transpiler issues.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 17:11:12 +00:00

264 lines
10 KiB
Plaintext

;; IO suspension tests — verifies perform/cek-step-loop/cek-resume
(defsuite
"io-suspend-basic"
(deftest
"perform creates suspended state"
(let
((state (cek-step-loop (make-cek-state (quote (perform {:op "test"})) (make-env) (list)))))
(assert (cek-suspended? state))
(assert (not (cek-terminal? state)))))
(deftest
"suspended state carries IO request"
(let
((state (cek-step-loop (make-cek-state (quote (perform {:service "blog" :op "query"})) (make-env) (list)))))
(let
((req (cek-io-request state)))
(assert= (get req "op") "query")
(assert= (get req "service") "blog"))))
(deftest
"cek-resume delivers result"
(let
((state (cek-step-loop (make-cek-state (quote (perform {:op "test"})) (make-env) (list)))))
(let
((final (cek-resume state 42)))
(assert (cek-terminal? final))
(assert= (cek-value final) 42))))
(deftest
"cek-resume with string result"
(let
((state (cek-step-loop (make-cek-state (quote (perform {:op "test"})) (make-env) (list)))))
(let
((final (cek-resume state "hello")))
(assert (cek-terminal? final))
(assert= (cek-value final) "hello"))))
(deftest
"cek-run errors on suspension"
(let
((result (cek-try (fn () (cek-run (make-cek-state (quote (perform {:op "test"})) (make-env) (list)))))))
(assert= (symbol-name (first result)) "error"))))
(defsuite
"io-suspend-control-flow"
(deftest
"perform inside let — result used in binding"
(let
((state (cek-step-loop (make-cek-state (quote (let ((x (perform {:op "get-value"}))) (+ x 10))) (make-env) (list)))))
(assert (cek-suspended? state))
(let
((final (cek-resume state 32)))
(assert (cek-terminal? final))
(assert= (cek-value final) 42))))
(deftest
"perform inside if condition"
(let
((state (cek-step-loop (make-cek-state (quote (if (perform {:op "check"}) "yes" "no")) (make-env) (list)))))
(assert (cek-suspended? state))
(let
((final (cek-resume state true)))
(assert (cek-terminal? final))
(assert= (cek-value final) "yes"))))
(deftest
"perform inside if — false branch"
(let
((state (cek-step-loop (make-cek-state (quote (if (perform {:op "check"}) "yes" "no")) (make-env) (list)))))
(let
((final (cek-resume state false)))
(assert (cek-terminal? final))
(assert= (cek-value final) "no"))))
(deftest
"sequential performs — two suspensions"
(let
((state1 (cek-step-loop (make-cek-state (quote (let ((a (perform {:op "first"}))) (let ((b (perform {:op "second"}))) (+ a b)))) (make-env) (list)))))
(assert (cek-suspended? state1))
(assert= (get (cek-io-request state1) "op") "first")
(let
((state2 (cek-resume state1 10)))
(assert (cek-suspended? state2))
(assert= (get (cek-io-request state2) "op") "second")
(let
((final (cek-resume state2 32)))
(assert (cek-terminal? final))
(assert= (cek-value final) 42)))))
(deftest
"perform inside begin — not last expr"
(let
((state (cek-step-loop (make-cek-state (quote (begin (perform {:op "side-effect"}) "done")) (make-env) (list)))))
(assert (cek-suspended? state))
(let
((final (cek-resume state nil)))
(assert (cek-terminal? final))
(assert= (cek-value final) "done")))))
(defsuite
"io-suspend-functions"
(deftest
"perform inside lambda"
(let
((state (cek-step-loop (make-cek-state (quote ((fn (x) (+ x (perform {:op "get"}))) 10)) (make-env) (list)))))
(assert (cek-suspended? state))
(let
((final (cek-resume state 32)))
(assert (cek-terminal? final))
(assert= (cek-value final) 42))))
(deftest
"perform result passed to function"
(let
((state (cek-step-loop (make-cek-state (quote (let ((double (fn (x) (* x 2)))) (double (perform {:op "get-val"})))) (make-env) (list)))))
(assert (cek-suspended? state))
(let
((final (cek-resume state 21)))
(assert (cek-terminal? final))
(assert= (cek-value final) 42)))))
(defsuite
"io-suspend-values"
(deftest
"resume with nil"
(let
((state (cek-step-loop (make-cek-state (quote (perform {:op "test"})) (make-env) (list)))))
(let
((final (cek-resume state nil)))
(assert (cek-terminal? final))
(assert (nil? (cek-value final))))))
(deftest
"resume with list"
(let
((state (cek-step-loop (make-cek-state (quote (perform {:op "fetch"})) (make-env) (list)))))
(let
((final (cek-resume state (list 1 2 3))))
(assert (cek-terminal? final))
(assert= (len (cek-value final)) 3))))
(deftest
"resume with dict"
(let
((state (cek-step-loop (make-cek-state (quote (let ((result (perform {:op "query"}))) (get result "name"))) (make-env) (list)))))
(let
((final (cek-resume state {:name "alice"})))
(assert (cek-terminal? final))
(assert= (cek-value final) "alice")))))
(defsuite
"io-suspend-jit"
(deftest
"named function with perform suspends"
(let
((state (cek-step-loop (make-cek-state (quote (begin (define fetch-data (fn (key) (perform {:op "fetch" :key key}))) (fetch-data "users"))) (make-env) (list)))))
(assert (cek-suspended? state))
(assert= (get (cek-io-request state) "op") "fetch")
(let
((final (cek-resume state (list "alice" "bob"))))
(assert (cek-terminal? final))
(assert= (len (cek-value final)) 2))))
(deftest
"named function with perform and computation"
(let
((state (cek-step-loop (make-cek-state (quote (begin (define fetch-and-count (fn (key) (let ((data (perform {:op "fetch" :key key}))) (len data)))) (fetch-and-count "items"))) (make-env) (list)))))
(assert (cek-suspended? state))
(let
((final (cek-resume state (list 1 2 3 4 5))))
(assert (cek-terminal? final))
(assert= (cek-value final) 5))))
(deftest
"two named functions with sequential performs"
(let
((state1 (cek-step-loop (make-cek-state (quote (begin (define get-name (fn () (perform {:op "get-name"}))) (define get-age (fn () (perform {:op "get-age"}))) (str (get-name) " is " (get-age) " years old"))) (make-env) (list)))))
(assert (cek-suspended? state1))
(assert= (get (cek-io-request state1) "op") "get-name")
(let
((state2 (cek-resume state1 "Alice")))
(assert (cek-suspended? state2))
(assert= (get (cek-io-request state2) "op") "get-age")
(let
((final (cek-resume state2 30)))
(assert (cek-terminal? final))
(assert= (cek-value final) "Alice is 30 years old"))))))
(defsuite
"io-suspend-cross-boundary"
(deftest
"function calling component that performs IO"
(let
((state (cek-step-loop (make-cek-state (quote (begin (defcomp ~data-loader (&key source) (perform {:op "load" :source source})) (define render (fn (src) (let ((data (~data-loader :source src))) (str "loaded: " (len data) " items")))) (render "products"))) (make-env) (list)))))
(assert (cek-suspended? state))
(assert= (get (cek-io-request state) "op") "load")
(assert= (get (cek-io-request state) "source") "products")
(let
((final (cek-resume state (list "a" "b" "c"))))
(assert (cek-terminal? final))
(assert= (cek-value final) "loaded: 3 items")))))
(defsuite
"io-suspend-error-handling"
(deftest
"guard wraps perform — normal completion"
(let
((state (cek-step-loop (make-cek-state (quote (guard (e (true (str "caught: " e))) (perform {:op "get"}))) (make-env) (list)))))
(assert (cek-suspended? state))
(let
((final (cek-resume state "ok-result")))
(assert (cek-terminal? final))
(assert= (cek-value final) "ok-result"))))
(deftest
"perform result flows through let in guard body"
(let
((state (cek-step-loop (make-cek-state (quote (guard (e (true "error")) (let ((x (perform {:op "get"}))) (+ x 1)))) (make-env) (list)))))
(assert (cek-suspended? state))
(let
((final (cek-resume state 41)))
(assert (cek-terminal? final))
(assert= (cek-value final) 42)))))
(defsuite
"vm-import-suspension"
(deftest
"vm-execute-module runs trivial bytecode"
(let
((globals (dict)) (code (compile-module (quote (42)))))
(assert= (vm-execute-module code globals) 42)))
(deftest
"vm-execute-module converts code-from-value internally"
(let
((globals (dict))
(code (compile-module (quote ((define x 99) x)))))
(assert= (vm-execute-module code globals) 99)))
(deftest
"compile-module handles import form"
(let
((code (compile-module (quote ((import (test lib)))))))
(assert (dict? code))
(assert (not (nil? (get code :bytecode))))))
(deftest
"vm-execute-module returns suspension dict on import"
(let
((globals (dict))
(code (compile-module (quote ((import (test lib)))))))
(let
((result (vm-execute-module code globals)))
(assert (dict? result) "result should be a dict")
(assert= (get result :suspended) true)
(assert= (get result :op) "import"))))
(deftest
"vm-resume-module continues after suspension"
(let
((globals (dict))
(code
(compile-module (quote ((import (test lib)) (define x 42) x)))))
(let
((r1 (vm-execute-module code globals)))
(assert= (get r1 :suspended) true)
(let ((r2 (vm-resume-module r1))) (assert= r2 42)))))
(deftest
"vm multiple sequential imports suspend and resume"
(let
((globals (dict))
(code
(compile-module
(quote ((import (test a)) (import (test b)) (define x 99) x)))))
(let
((r1 (vm-execute-module code globals)))
(assert= (get r1 :suspended) true)
(let
((r2 (vm-resume-module r1)))
(assert= (get r2 :suspended) true)
(let ((r3 (vm-resume-module r2))) (assert= r3 99)))))))