diff --git a/spec/tests/test-freeze.sx b/spec/tests/test-freeze.sx new file mode 100644 index 0000000..668358f --- /dev/null +++ b/spec/tests/test-freeze.sx @@ -0,0 +1,75 @@ +;; ========================================================================== +;; test-freeze.sx — Freeze scope and content addressing tests +;; ========================================================================== + +(defsuite "freeze-scope" + (deftest "freeze captures signal values" + (let ((s (signal 42))) + (freeze-scope "t1" (fn () + (freeze-signal "val" s))) + (let ((frozen (cek-freeze-scope "t1"))) + (assert-equal "t1" (get frozen "name")) + (assert-equal 42 (get (get frozen "signals") "val"))))) + + (deftest "thaw restores signal values" + (let ((s (signal 10))) + (freeze-scope "t2" (fn () + (freeze-signal "x" s))) + (let ((sx (freeze-to-sx "t2"))) + (reset! s 999) + (assert-equal 999 (deref s)) + (thaw-from-sx sx) + (assert-equal 10 (deref s))))) + + (deftest "multiple signals in scope" + (let ((a (signal "hello")) + (b (signal 42)) + (c (signal true))) + (freeze-scope "t3" (fn () + (freeze-signal "a" a) + (freeze-signal "b" b) + (freeze-signal "c" c))) + (let ((frozen (cek-freeze-scope "t3"))) + (assert-equal "hello" (get (get frozen "signals") "a")) + (assert-equal 42 (get (get frozen "signals") "b")) + (assert-equal true (get (get frozen "signals") "c"))))) + + (deftest "freeze-to-sx round trip" + (let ((s (signal "data"))) + (freeze-scope "t4" (fn () + (freeze-signal "s" s))) + (let ((sx (freeze-to-sx "t4"))) + (assert-true (string? sx)) + (assert-true (contains? sx "data")) + (reset! s "changed") + (thaw-from-sx sx) + (assert-equal "data" (deref s)))))) + +(defsuite "content-addressing" + (deftest "content-hash deterministic" + (assert-equal (content-hash "hello") (content-hash "hello"))) + + (deftest "content-hash different for different input" + (assert-false (= (content-hash "hello") (content-hash "world")))) + + (deftest "content-put and get" + (let ((cid (content-put "test data"))) + (assert-equal "test data" (content-get cid)))) + + (deftest "freeze-to-cid round trip" + (let ((s (signal 77))) + (freeze-scope "t5" (fn () + (freeze-signal "v" s))) + (let ((cid (freeze-to-cid "t5"))) + (assert-true (string? cid)) + (reset! s 0) + (assert-true (thaw-from-cid cid)) + (assert-equal 77 (deref s))))) + + (deftest "same state same cid" + (let ((s (signal 42))) + (freeze-scope "t6" (fn () + (freeze-signal "n" s))) + (let ((cid1 (freeze-to-cid "t6")) + (cid2 (freeze-to-cid "t6"))) + (assert-equal cid1 cid2))))) diff --git a/spec/tests/test-primitives.sx b/spec/tests/test-primitives.sx new file mode 100644 index 0000000..c574988 --- /dev/null +++ b/spec/tests/test-primitives.sx @@ -0,0 +1,188 @@ +;; ========================================================================== +;; test-primitives.sx — Exhaustive tests for all pure primitives +;; ========================================================================== + +;; -------------------------------------------------------------------------- +;; Arithmetic +;; -------------------------------------------------------------------------- + +(defsuite "arithmetic" + (deftest "add" (assert-equal 3 (+ 1 2))) + (deftest "add multiple" (assert-equal 10 (+ 1 2 3 4))) + (deftest "add zero" (assert-equal 5 (+ 5 0))) + (deftest "add negative" (assert-equal -1 (+ 1 -2))) + (deftest "subtract" (assert-equal 3 (- 5 2))) + (deftest "subtract negative" (assert-equal 7 (- 5 -2))) + (deftest "multiply" (assert-equal 12 (* 3 4))) + (deftest "multiply zero" (assert-equal 0 (* 5 0))) + (deftest "multiply negative" (assert-equal -6 (* 2 -3))) + (deftest "divide" (assert-equal 3 (/ 9 3))) + (deftest "divide float" (assert-equal 2.5 (/ 5 2))) + (deftest "mod" (assert-equal 1 (mod 7 3))) + (deftest "mod negative" (assert-true (or (= (mod -1 3) 2) (= (mod -1 3) -1)))) + (deftest "inc" (assert-equal 6 (inc 5))) + (deftest "dec" (assert-equal 4 (dec 5))) + (deftest "abs positive" (assert-equal 5 (abs 5))) + (deftest "abs negative" (assert-equal 5 (abs -5))) + (deftest "abs zero" (assert-equal 0 (abs 0))) + (deftest "min" (assert-equal 2 (min 2 5))) + (deftest "max" (assert-equal 5 (max 2 5)))) + +;; -------------------------------------------------------------------------- +;; Comparison +;; -------------------------------------------------------------------------- + +(defsuite "comparison" + (deftest "equal numbers" (assert-true (= 1 1))) + (deftest "not equal numbers" (assert-false (= 1 2))) + (deftest "equal strings" (assert-true (= "a" "a"))) + (deftest "less than" (assert-true (< 1 2))) + (deftest "not less than" (assert-false (< 2 1))) + (deftest "greater than" (assert-true (> 2 1))) + (deftest "not greater than" (assert-false (> 1 2))) + (deftest "less equal" (assert-true (<= 1 1))) + (deftest "less equal less" (assert-true (<= 1 2))) + (deftest "greater equal" (assert-true (>= 2 2))) + (deftest "greater equal greater" (assert-true (>= 3 2))) + (deftest "not" (assert-true (not false))) + (deftest "not true" (assert-false (not true))) + (deftest "not nil" (assert-true (not nil)))) + +;; -------------------------------------------------------------------------- +;; Predicates +;; -------------------------------------------------------------------------- + +(defsuite "predicates" + (deftest "nil? nil" (assert-true (nil? nil))) + (deftest "nil? number" (assert-false (nil? 0))) + (deftest "nil? string" (assert-false (nil? ""))) + (deftest "number? num" (assert-true (number? 42))) + (deftest "number? string" (assert-false (number? "42"))) + (deftest "number? bool" (assert-false (number? true))) + (deftest "string? str" (assert-true (string? "hi"))) + (deftest "string? num" (assert-false (string? 42))) + (deftest "list? list" (assert-true (list? (list 1 2)))) + (deftest "list? empty" (assert-true (list? (list)))) + (deftest "list? string" (assert-false (list? "hi"))) + (deftest "dict? dict" (assert-true (dict? (dict "a" 1)))) + (deftest "dict? list" (assert-false (dict? (list 1)))) + (deftest "empty? empty list" (assert-true (empty? (list)))) + (deftest "empty? nonempty" (assert-false (empty? (list 1)))) + (deftest "empty? empty string" (assert-true (empty? ""))) + (deftest "empty? nonempty string" (assert-false (empty? "a"))) + (deftest "empty? nil" (assert-true (empty? nil)))) + +;; -------------------------------------------------------------------------- +;; String operations +;; -------------------------------------------------------------------------- + +(defsuite "strings" + (deftest "str concat" (assert-equal "hello world" (str "hello" " " "world"))) + (deftest "str number" (assert-equal "42" (str 42))) + (deftest "str empty" (assert-equal "" (str))) + (deftest "len string" (assert-equal 5 (len "hello"))) + (deftest "len empty" (assert-equal 0 (len ""))) + (deftest "slice" (assert-equal "ell" (slice "hello" 1 4))) + (deftest "slice from" (assert-equal "llo" (slice "hello" 2))) + (deftest "slice empty" (assert-equal "" (slice "hello" 2 2))) + (deftest "join" (assert-equal "a,b,c" (join "," (list "a" "b" "c")))) + (deftest "join empty" (assert-equal "" (join "," (list)))) + (deftest "join single" (assert-equal "a" (join "," (list "a")))) + (deftest "split" (assert-equal (list "a" "b" "c") (split "a,b,c" ","))) + (deftest "upper" (assert-equal "HELLO" (upper "hello"))) + (deftest "lower" (assert-equal "hello" (lower "HELLO"))) + (deftest "trim" (assert-equal "hi" (trim " hi "))) + (deftest "contains?" (assert-true (contains? "hello world" "world"))) + (deftest "contains? false" (assert-false (contains? "hello" "xyz"))) + (deftest "starts-with?" (assert-true (starts-with? "hello" "hel"))) + (deftest "starts-with? false" (assert-false (starts-with? "hello" "xyz"))) + (deftest "ends-with?" (assert-true (ends-with? "hello" "llo"))) + (deftest "ends-with? false" (assert-false (ends-with? "hello" "xyz"))) + (deftest "replace" (assert-equal "hXllo" (replace "hello" "e" "X"))) + (deftest "string-length" (assert-equal 5 (string-length "hello"))) + (deftest "index-of found" (assert-equal 2 (index-of "hello" "l"))) + (deftest "index-of not found" (assert-equal -1 (index-of "hello" "z")))) + +;; -------------------------------------------------------------------------- +;; List operations +;; -------------------------------------------------------------------------- + +(defsuite "lists" + (deftest "list create" (assert-equal (list 1 2 3) (list 1 2 3))) + (deftest "first" (assert-equal 1 (first (list 1 2 3)))) + (deftest "first empty" (assert-nil (first (list)))) + (deftest "rest" (assert-equal (list 2 3) (rest (list 1 2 3)))) + (deftest "rest single" (assert-equal (list) (rest (list 1)))) + (deftest "rest empty" (assert-equal (list) (rest (list)))) + (deftest "nth" (assert-equal 2 (nth (list 1 2 3) 1))) + (deftest "nth out of bounds" (assert-nil (nth (list 1 2) 5))) + (deftest "last" (assert-equal 3 (last (list 1 2 3)))) + (deftest "last single" (assert-equal 1 (last (list 1)))) + (deftest "len list" (assert-equal 3 (len (list 1 2 3)))) + (deftest "len empty" (assert-equal 0 (len (list)))) + (deftest "cons" (assert-equal (list 0 1 2) (cons 0 (list 1 2)))) + (deftest "append" (assert-equal (list 1 2 3 4) (append (list 1 2) (list 3 4)))) + (deftest "append element" (assert-equal (list 1 2 3) (append (list 1 2) (list 3)))) + (deftest "slice list" (assert-equal (list 2 3) (slice (list 1 2 3 4) 1 3))) + (deftest "concat" (assert-equal (list 1 2 3 4) (concat (list 1 2) (list 3 4)))) + (deftest "reverse" (assert-equal (list 3 2 1) (reverse (list 1 2 3)))) + (deftest "reverse empty" (assert-equal (list) (reverse (list)))) + (deftest "contains? list" (assert-true (contains? (list 1 2 3) 2))) + (deftest "contains? list false" (assert-false (contains? (list 1 2 3) 5))) + (deftest "range" (assert-equal (list 0 1 2) (range 0 3))) + (deftest "range step" (assert-equal (list 0 2 4) (range 0 6 2))) + (deftest "flatten" (assert-equal (list 1 2 3 4) (flatten (list (list 1 2) (list 3 4)))))) + +;; -------------------------------------------------------------------------- +;; Dict operations +;; -------------------------------------------------------------------------- + +(defsuite "dicts" + (deftest "dict create" (assert-equal 1 (get (dict "a" 1 "b" 2) "a"))) + (deftest "get missing" (assert-nil (get (dict "a" 1) "z"))) + (deftest "get default" (assert-equal 99 (get (dict "a" 1) "z" 99))) + (deftest "keys" (assert-true (contains? (keys (dict "a" 1 "b" 2)) "a"))) + (deftest "has-key?" (assert-true (has-key? (dict "a" 1) "a"))) + (deftest "has-key? false" (assert-false (has-key? (dict "a" 1) "z"))) + (deftest "assoc" (assert-equal 2 (get (assoc (dict "a" 1) "b" 2) "b"))) + (deftest "dissoc" (assert-false (has-key? (dissoc (dict "a" 1 "b" 2) "a") "a"))) + (deftest "len dict" (assert-equal 2 (len (dict "a" 1 "b" 2)))) + (deftest "len empty dict" (assert-equal 0 (len (dict)))) + (deftest "empty? dict" (assert-true (empty? (dict)))) + (deftest "empty? nonempty dict" (assert-false (empty? (dict "a" 1))))) + +;; -------------------------------------------------------------------------- +;; Higher-order functions +;; -------------------------------------------------------------------------- + +(defsuite "higher-order" + (deftest "map" (assert-equal (list 2 4 6) (map (fn (x) (* x 2)) (list 1 2 3)))) + (deftest "map empty" (assert-equal (list) (map (fn (x) x) (list)))) + (deftest "filter" (assert-equal (list 2 4) (filter (fn (x) (= (mod x 2) 0)) (list 1 2 3 4 5)))) + (deftest "filter none" (assert-equal (list) (filter (fn (x) false) (list 1 2 3)))) + (deftest "reduce" (assert-equal 10 (reduce (fn (acc x) (+ acc x)) 0 (list 1 2 3 4)))) + (deftest "reduce empty" (assert-equal 0 (reduce (fn (acc x) (+ acc x)) 0 (list)))) + (deftest "some true" (assert-true (some (fn (x) (> x 3)) (list 1 2 3 4 5)))) + (deftest "some false" (assert-false (some (fn (x) (> x 10)) (list 1 2 3)))) + (deftest "some empty" (assert-false (some (fn (x) true) (list)))) + (deftest "every? true" (assert-true (every? (fn (x) (> x 0)) (list 1 2 3)))) + (deftest "every? false" (assert-false (every? (fn (x) (> x 2)) (list 1 2 3)))) + (deftest "every? empty" (assert-true (every? (fn (x) false) (list)))) + (deftest "for-each returns nil" + (let ((log (list))) + (for-each (fn (x) (append! log x)) (list 1 2 3)) + (assert-equal (list 1 2 3) log))) + (deftest "map-indexed" + (assert-equal (list (list 0 "a") (list 1 "b")) + (map-indexed (fn (i x) (list i x)) (list "a" "b"))))) + +;; -------------------------------------------------------------------------- +;; Type coercion +;; -------------------------------------------------------------------------- + +(defsuite "type-coercion" + (deftest "str bool" (assert-true (or (= (str true) "true") (= (str true) "True")))) + (deftest "str nil" (assert-equal "" (str nil))) + (deftest "str list" (assert-true (not (empty? (str (list 1 2 3)))))) + (deftest "parse-int" (assert-equal 42 (parse-int "42"))) + (deftest "parse-float skipped" (assert-true true)))