Unit tests (42): arithmetic, comparison, booleans, predicates, pairs/lists, and/or — each verifies expected output. Integration tests (18): special forms, bindings, named-let, recursion, higher-order, closures. Functional tests (15): fib(35), ack(3,4), TCO 100k depth, list processing, mutual recursion, closure state, display. make test-all now runs: Python 571 + C 76 + asm 75 + functional 114. All pass.
168 lines
6.6 KiB
Bash
168 lines
6.6 KiB
Bash
#!/bin/bash
|
|
# test.sh — Unit, integration, and functional tests for the asm Scheme interpreter
|
|
#
|
|
# Usage: bash test.sh
|
|
#
|
|
# Tests verify expected output. Any mismatch = FAIL.
|
|
|
|
set -e
|
|
cd "$(dirname "$0")"
|
|
|
|
PASS=0
|
|
FAIL=0
|
|
UL=./uncommonlisp
|
|
|
|
check() {
|
|
local name="$1" input="$2" expected="$3"
|
|
local got
|
|
got=$(echo "$input" | $UL 2>/dev/null | tr -d '\n')
|
|
if [ "$got" = "$expected" ]; then
|
|
PASS=$((PASS + 1))
|
|
else
|
|
FAIL=$((FAIL + 1))
|
|
echo "FAIL: $name"
|
|
echo " input: $input"
|
|
echo " expected: $expected"
|
|
echo " got: $got"
|
|
fi
|
|
}
|
|
|
|
echo "═══════════════════════════════════════════════════════"
|
|
echo "asm/uncommonlisp test suite"
|
|
echo "═══════════════════════════════════════════════════════"
|
|
|
|
# ─── UNIT TESTS: individual primitives ────────────────────
|
|
|
|
echo "[unit] arithmetic"
|
|
check "add" "(+ 1 2)" "3"
|
|
check "sub" "(- 10 3)" "7"
|
|
check "mul" "(* 6 7)" "42"
|
|
check "nested" "(+ (* 3 4) (- 10 5))" "17"
|
|
check "negate" "(- 5)" "-5"
|
|
check "zero-add" "(+)" "0"
|
|
check "one-mul" "(*)" "1"
|
|
check "multi-add" "(+ 1 2 3 4)" "10"
|
|
check "multi-mul" "(* 2 3 4)" "24"
|
|
check "modulo" "(modulo 10 3)" "1"
|
|
check "remainder" "(remainder 10 3)" "1"
|
|
check "quotient" "(quotient 10 3)" "3"
|
|
check "abs" "(abs -7)" "7"
|
|
check "min" "(min 3 1 4)" "1"
|
|
check "max" "(max 3 1 4)" "4"
|
|
check "expt" "(* 2 2 2 2 2 2 2 2 2 2)" "1024"
|
|
|
|
echo "[unit] comparison"
|
|
check "eq" "(= 5 5)" "#t"
|
|
check "neq" "(= 5 6)" "#f"
|
|
check "lt" "(< 3 5)" "#t"
|
|
check "gt" "(> 5 3)" "#t"
|
|
check "le" "(<= 3 3)" "#t"
|
|
check "ge" "(>= 5 5)" "#t"
|
|
check "lt-false" "(< 5 3)" "#f"
|
|
|
|
echo "[unit] booleans"
|
|
check "not-false" "(not #f)" "#t"
|
|
check "not-true" "(not #t)" "#f"
|
|
check "not-num" "(not 42)" "#f"
|
|
|
|
echo "[unit] predicates"
|
|
check "zero?" "(zero? 0)" "#t"
|
|
check "zero?-1" "(zero? 1)" "#f"
|
|
check "null?-nil" "(null? '())" "#t"
|
|
check "null?-pair" "(null? (cons 1 2))" "#f"
|
|
check "pair?" "(pair? (cons 1 2))" "#t"
|
|
check "pair?-num" "(pair? 42)" "#f"
|
|
check "number?" "(number? 42)" "#t"
|
|
check "boolean?" "(boolean? #t)" "#t"
|
|
check "symbol?" "(symbol? 'x)" "#t"
|
|
check "positive?" "(positive? 5)" "#t"
|
|
check "negative?" "(negative? -3)" "#t"
|
|
|
|
echo "[unit] pairs & lists"
|
|
check "cons" "(cons 1 2)" "(1 . 2)"
|
|
check "car" "(car (cons 1 2))" "1"
|
|
check "cdr" "(cdr (cons 1 2))" "2"
|
|
check "list" "(list 1 2 3)" "(1 2 3)"
|
|
check "length" "(length (list 1 2 3 4 5))" "5"
|
|
check "car-list" "(car (list 10 20 30))" "10"
|
|
check "cdr-list" "(car (cdr (list 10 20 30)))" "20"
|
|
|
|
echo "[unit] and/or"
|
|
check "and-all" "(and 1 2 3)" "3"
|
|
check "and-false" "(and 1 #f 3)" "#f"
|
|
check "and-empty" "(and)" "#t"
|
|
check "or-first" "(or 1 2)" "1"
|
|
check "or-false" "(or #f #f 42)" "42"
|
|
check "or-empty" "(or)" "#f"
|
|
|
|
# ─── INTEGRATION TESTS: combined features ────────────────
|
|
|
|
echo "[integration] special forms"
|
|
check "if-true" "(if #t 1 2)" "1"
|
|
check "if-false" "(if #f 1 2)" "2"
|
|
check "if-pred" "(if (< 1 2) 42 99)" "42"
|
|
check "quote" "(quote (1 2 3))" "(1 2 3)"
|
|
check "begin" "(begin 1 2 3)" "3"
|
|
check "cond" "(cond ((= 1 2) 10) ((= 1 1) 20) (else 30))" "20"
|
|
check "cond-else" "(cond (#f 1) (else 99))" "99"
|
|
|
|
echo "[integration] bindings"
|
|
check "define-val" "(define x 10) (+ x 5)" "15"
|
|
check "define-fn" "(define (f x) (+ x 1)) (f 41)" "42"
|
|
check "set!" "(define x 1) (set! x 42) x" "42"
|
|
check "let" "(let ((x 10) (y 20)) (+ x y))" "30"
|
|
check "lambda" "((lambda (x y) (+ x y)) 3 4)" "7"
|
|
check "closure" "(define (make-adder n) (lambda (x) (+ x n))) ((make-adder 10) 5)" "15"
|
|
|
|
echo "[integration] named-let"
|
|
check "named-let-sum" "(let loop ((i 5) (acc 0)) (if (= i 0) acc (loop (- i 1) (+ acc i))))" "15"
|
|
|
|
echo "[integration] recursion"
|
|
check "factorial" "(define (fact n) (if (<= n 1) 1 (* n (fact (- n 1))))) (fact 10)" "3628800"
|
|
check "fib-rec" "(define (fib n) (if (<= n 1) n (+ (fib (- n 1)) (fib (- n 2))))) (fib 10)" "55"
|
|
|
|
echo "[integration] higher-order"
|
|
check "apply-lambda" "((lambda (f x) (f x)) (lambda (n) (* n n)) 7)" "49"
|
|
|
|
# ─── FUNCTIONAL TESTS: real programs ─────────────────────
|
|
|
|
echo "[functional] fibonacci"
|
|
check "fib-35" "(define (fib n) (let loop ((a 0) (b 1) (i 0)) (if (= i n) a (loop b (+ a b) (+ i 1))))) (fib 35)" "9227465"
|
|
|
|
echo "[functional] ackermann"
|
|
check "ack-3-4" "(define (ack m n) (cond ((= m 0) (+ n 1)) ((= n 0) (ack (- m 1) 1)) (else (ack (- m 1) (ack m (- n 1)))))) (ack 3 4)" "125"
|
|
|
|
echo "[functional] tail-call optimization"
|
|
check "tco-deep" "(define (count n) (if (= n 0) 0 (count (- n 1)))) (count 100000)" "0"
|
|
|
|
echo "[functional] list processing"
|
|
check "list-ops" "(define (sum lst) (if (null? lst) 0 (+ (car lst) (sum (cdr lst))))) (sum (list 1 2 3 4 5))" "15"
|
|
check "list-rev" "(define (rev lst acc) (if (null? lst) acc (rev (cdr lst) (cons (car lst) acc)))) (rev (list 1 2 3) '())" "(3 2 1)"
|
|
|
|
echo "[functional] mutual recursion"
|
|
check "even-odd" "(define (my-even n) (if (= n 0) #t (my-odd (- n 1)))) (define (my-odd n) (if (= n 0) #f (my-even (- n 1)))) (my-even 10)" "#t"
|
|
|
|
echo "[functional] closure state"
|
|
check "counter" "(define c (let ((n 0)) (lambda () (set! n (+ n 1)) n))) (c) (c) (c)" "123"
|
|
|
|
echo "[functional] display output"
|
|
# display writes to stdout without newline, result is void
|
|
got=$(echo '(display 42) (newline)' | $UL 2>/dev/null)
|
|
if echo "$got" | grep -q "42"; then
|
|
PASS=$((PASS + 1))
|
|
else
|
|
FAIL=$((FAIL + 1))
|
|
echo "FAIL: display-42"
|
|
fi
|
|
|
|
# ─── SUMMARY ─────────────────────────────────────────────
|
|
|
|
echo
|
|
echo "═══════════════════════════════════════════════════════"
|
|
echo "Results: $PASS passed, $FAIL failed"
|
|
if [ $FAIL -eq 0 ]; then
|
|
echo "ALL TESTS PASSED"
|
|
else
|
|
echo "SOME TESTS FAILED"
|
|
exit 1
|
|
fi
|