(library (srfi n78)
(export check
=>
check-ec
check-report
check-set-mode!
check-reset!
check-passed?)
(import (rnrs)
(rnrs mutable-pairs)
(srfi n42))
(define (make-box v) (cons v '()))
(define (box-ref b) (car b))
(define (box-set! b v) (set-car! b v))
(define check:write write)
(define check:mode (make-box #f))
(define (check-set-mode! mode)
(box-set! check:mode
(case mode
((off) 0)
((summary) 1)
((report-failed) 10)
((report) 100)
(else (error "unrecognized mode" mode)))))
(define check:correct (make-box #f))
(define check:failed (make-box #f))
(define (check-reset!)
(box-set! check:correct 0)
(box-set! check:failed '()))
(define (check:add-correct!)
(box-set! check:correct (+ (box-ref check:correct) 1)))
(define (check:add-failed! expression actual-result expected-result)
(box-set! check:failed
(cons (list expression actual-result expected-result)
(box-ref check:failed))))
(define (check:report-expression expression)
(newline)
(check:write expression)
(display " => "))
(define (check:report-actual-result actual-result)
(check:write actual-result)
(display " ; "))
(define (check:report-correct cases)
(display "correct")
(if (not (= cases 1))
(begin (display " (")
(display cases)
(display " cases checked)")))
(newline))
(define (check:report-failed expected-result)
(display "*** failed ***")
(newline)
(display " ; expected result: ")
(check:write expected-result)
(newline))
(define (check-report)
(if (>= (box-ref check:mode) 1)
(begin
(newline)
(display "; *** checks *** : ")
(display (box-ref check:correct))
(display " correct, ")
(display (length (box-ref check:failed)))
(display " failed.")
(if (or (null? (box-ref check:failed)) (<= (box-ref check:mode) 1))
(newline)
(let* ((w (car (reverse (box-ref check:failed))))
(expression (car w))
(actual-result (cadr w))
(expected-result (caddr w)))
(display " First failed example:")
(newline)
(check:report-expression expression)
(check:report-actual-result actual-result)
(check:report-failed expected-result))))))
(define (check-passed? expected-total-count)
(and (= (length (box-ref check:failed)) 0)
(= (box-ref check:correct) expected-total-count)))
(define (check:proc expression thunk equal expected-result)
(case (box-ref check:mode)
((0) #f)
((1)
(let ((actual-result (thunk)))
(if (equal actual-result expected-result)
(check:add-correct!)
(check:add-failed! expression actual-result expected-result))))
((10)
(let ((actual-result (thunk)))
(if (equal actual-result expected-result)
(check:add-correct!)
(begin
(check:report-expression expression)
(check:report-actual-result actual-result)
(check:report-failed expected-result)
(check:add-failed! expression actual-result expected-result)))))
((100)
(check:report-expression expression)
(let ((actual-result (thunk)))
(check:report-actual-result actual-result)
(if (equal actual-result expected-result)
(begin (check:report-correct 1)
(check:add-correct!))
(begin (check:report-failed expected-result)
(check:add-failed! expression
actual-result
expected-result)))))
(else (error "unrecognized check:mode" (box-ref check:mode))))
(if #f #f))
(define-syntax check
(syntax-rules (=>)
((check expr => expected)
(check expr (=> equal?) expected))
((check expr (=> equal) expected)
(if (>= (box-ref check:mode) 1)
(check:proc 'expr (lambda () expr) equal expected)))))
(define (check:proc-ec w)
(let ((correct? (car w))
(expression (cadr w))
(actual-result (caddr w))
(expected-result (cadddr w))
(cases (car (cddddr w))))
(if correct?
(begin (if (>= (box-ref check:mode) 100)
(begin (check:report-expression expression)
(check:report-actual-result actual-result)
(check:report-correct cases)))
(check:add-correct!))
(begin (if (>= (box-ref check:mode) 10)
(begin (check:report-expression expression)
(check:report-actual-result actual-result)
(check:report-failed expected-result)))
(check:add-failed! expression
actual-result
expected-result)))))
(define-syntax check-ec:make
(syntax-rules (=>)
((check-ec:make qualifiers expr (=> equal) expected (arg ...))
(if (>= (box-ref check:mode) 1)
(check:proc-ec
(let ((cases 0))
(let ((w (first-ec
#f
qualifiers
(:let equal-pred equal)
(:let expected-result expected)
(:let actual-result
(let ((arg arg) ...) expr))
(begin (set! cases (+ cases 1)))
(if (not (equal-pred actual-result expected-result)))
(list (list 'let (list (list 'arg arg) ...) 'expr)
actual-result
expected-result
cases))))
(if w
(cons #f w)
(list #t
'(check-ec qualifiers
expr (=> equal)
expected (arg ...))
(if #f #f)
(if #f #f)
cases)))))))))
(define-syntax check-ec
(syntax-rules (nested =>)
((check-ec expr => expected)
(check-ec:make (nested) expr (=> equal?) expected ()))
((check-ec expr (=> equal) expected)
(check-ec:make (nested) expr (=> equal) expected ()))
((check-ec expr => expected (arg ...))
(check-ec:make (nested) expr (=> equal?) expected (arg ...)))
((check-ec expr (=> equal) expected (arg ...))
(check-ec:make (nested) expr (=> equal) expected (arg ...)))
((check-ec qualifiers expr => expected)
(check-ec:make qualifiers expr (=> equal?) expected ()))
((check-ec qualifiers expr (=> equal) expected)
(check-ec:make qualifiers expr (=> equal) expected ()))
((check-ec qualifiers expr => expected (arg ...))
(check-ec:make qualifiers expr (=> equal?) expected (arg ...)))
((check-ec qualifiers expr (=> equal) expected (arg ...))
(check-ec:make qualifiers expr (=> equal) expected (arg ...)))
((check-ec (nested q1 ...) q etc ...)
(check-ec (nested q1 ... q) etc ...))
((check-ec q1 q2 etc ...)
(check-ec (nested q1 q2) etc ...))))
(check-set-mode! 'report)
(check-reset!)
)