~ chicken-core (master) /tests/test.scm
Trap1;;;; test.scm - minimal testing framework2;3; by Alex Shinn, lifted from match-test by felix45(import (only chicken.string ->string))6(import (only chicken.time current-process-milliseconds))7(import (only (scheme base) make-parameter))89(define *current-group-name* "")10(define *pass* 0)11(define *fail* 0)12(define *start* 0)13(define *total-pass* 0)14(define *total-fail* 0)15(define *total-start* 0)16(define *fail-token* (gensym))1718(define (run-test name thunk expect eq pass-msg fail-msg)19 (let ((result (thunk)))20 (cond21 ((eq expect result)22 (set! *pass* (+ *pass* 1))23 (format-result pass-msg name expect result))24 (else25 (set! *fail* (+ *fail* 1))26 (format-result fail-msg name expect result)))))2728(define (format-result ls name expect result)29 (let lp ((ls ls))30 (cond31 ((null? ls) (newline))32 ((eq? (car ls) 'expect) (write expect) (lp (cdr ls)))33 ((eq? (car ls) 'result) (write result) (lp (cdr ls)))34 ((eq? (car ls) 'name) (if name (begin (display #\space) (display name))) (lp (cdr ls)))35 (else (display (car ls)) (lp (cdr ls))))))3637(define (test-begin . o)38 (set! *current-group-name* (if (null? o) "<unnamed>" (car o)))39 (print "== " *current-group-name* " ==")40 (set! *total-pass* (+ *total-pass* *pass*))41 (set! *total-fail* (+ *total-fail* *fail*))42 (set! *pass* 0)43 (set! *fail* 0)44 (set! *start* (current-process-milliseconds))45 (when (= 0 *total-start*)46 (set! *total-start* (current-process-milliseconds))))4748(define (format-float n prec)49 (let* ((str (number->string n))50 (len (string-length str)))51 (let lp ((i (- len 1)))52 (cond53 ((negative? i)54 (string-append str "." (make-string prec #\0)))55 ((eqv? #\. (string-ref str i))56 (let ((diff (+ 1 (- prec (- len i)))))57 (cond58 ((positive? diff)59 (string-append str (make-string diff #\0)))60 ((negative? diff)61 (substring str 0 (+ i prec 1)))62 (else63 str))))64 (else65 (lp (- i 1)))))))6667(define (format-percent num denom)68 (let ((x (if (zero? denom) num (exact->inexact (/ num denom)))))69 (format-float (* 100 x) 2)))7071(define (test-end . o)72 (let ((end (current-process-milliseconds))73 (total (+ *pass* *fail*)))74 (print " " total " tests completed in "75 (format-float (exact->inexact (/ (- end *start*) 1000)) 3)76 " seconds")77 (print " " *pass* " ("78 (format-percent *pass* total)79 "%) tests passed")80 (print " " *fail* " ("81 (format-percent *fail* total)82 "%) tests failed"))83 (print "-- " *current-group-name* " --\n\n"))8485(define (test-exit . o)86 (print " TOTALS: ")87 (set! *total-pass* (+ *total-pass* *pass*)) ; should be 088 (set! *total-fail* (+ *total-fail* *fail*)) ; should be 089 (let ((end (current-process-milliseconds))90 (total (+ *total-pass* *total-fail*)))91 (print " " total " tests completed in "92 (format-float (exact->inexact (/ (- end *total-start*) 1000)) 3)93 " seconds")94 (print " " *total-pass* " ("95 (format-percent *total-pass* total)96 "%) tests passed")97 (print " " *total-fail* " ("98 (format-percent *total-fail* total)99 "%) tests failed")100 (exit (if (zero? *total-fail*) 0 1))))101102(define (run-equal name thunk expect eq)103 (run-test name thunk expect eq104 '("(PASS)" name)105 '("(FAIL)" name ": expected " expect " but got " result)))106107(define current-test-epsilon (make-parameter 1e-5))108109(define (approx-equal? a b epsilon)110 (cond111 ((> (abs a) (abs b)) (approx-equal? b a epsilon))112 ((zero? a) (< (abs b) epsilon))113 (else (< (abs (/ (- a b) b)) epsilon))))114115(define (test-equal? expect res)116 (or (equal? expect res)117 (and (number? expect)118 (inexact? expect)119 (inexact? res)120 (approx-equal? expect res (current-test-epsilon)))))121122(define current-test-comparator (make-parameter test-equal?))123124(define-syntax test-equal125 (syntax-rules ()126 ((_ name expr value eq) (run-equal name (lambda () expr) value eq))127 ((_ name expr value) (run-equal name (lambda () expr) value (current-test-comparator)))128 ((_ expr value) (run-equal (->string 'expr) (lambda () expr) value (current-test-comparator)))))129130(define-syntax test-error131 (syntax-rules ()132 ((_ name expr)133 (run-equal134 name135 (lambda () (handle-exceptions ex *fail-token* expr))136 *fail-token* eq?) )137 ((_ expr) (test-error 'expr expr))))138139(define-syntax test-assert140 (syntax-rules ()141 ((_ name expr) (run-equal name (lambda () (if expr #t #f)) #t eq?))142 ((_ expr) (run-equal (->string expr) (lambda () (if expr #t #f)) #t eq?))))143144(define-syntax test-group145 (syntax-rules ()146 ((_ name body ...)147 (begin148 (print "\n" name ":\n")149 body ...))))