#lang s-exp "../base.rkt" (require (for-syntax racket/base)) (provide check-expect ;check-within ;check-error run-tests) (define *tests* '()) (define-for-syntax (syntax-location-values stx) (list (syntax-source stx) ;; can be path or symbol (syntax-position stx) (syntax-line stx) (syntax-column stx) (syntax-span stx))) (define-for-syntax (check-at-toplevel! who stx) (unless (eq? (syntax-local-context) 'module) (raise-syntax-error #f (format "~a: found a test that is not at the top level." who) stx))) (define-syntax (check-expect stx) (syntax-case stx () [(_ test expected) (begin (check-at-toplevel! 'check-expect stx) (with-syntax ([stx stx] [(id offset line column span) (syntax-location-values stx)]) #'(accumulate-test! (lambda () (check-expect* 'stx (srcloc 'id line column offset span) (lambda () test) (lambda () expected))))))])) ;; (define-syntax (check-within stx) ;; (syntax-case stx () ;; [(_ test expected delta) ;; (begin ;; (check-at-toplevel! 'check-within stx) ;; (with-syntax ([stx stx] ;; [(id offset line column span) ;; (syntax-location-values stx)]) ;; #'(accumulate-test! ;; (lambda () ;; (check-within* 'stx ;; (make-location 'id offset line column span) ;; (lambda () test) ;; (lambda () expected) ;; (lambda () delta))))))])) ;; (define-syntax (check-error stx) ;; (syntax-case stx () ;; [(_ test expected-msg) ;; (begin ;; (check-at-toplevel! 'check-error stx) ;; (with-syntax ([stx stx] ;; [(id offset line column span) ;; (syntax-location-values stx)]) ;; #'(accumulate-test! ;; (lambda () ;; (check-error* 'stx ;; (make-location 'id offset line column span) ;; (lambda () test) ;; (lambda () expected-msg))))))])) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (define (check-expect* test-datum a-loc test-thunk expected-thunk) ; (with-handlers ([void ; (lambda (exn) ; (printf "check-expect: ~s" ; (exn-message exn)) ; (newline) ; (display-location test-datum a-loc) ; #f)]) (let ([expected-value (expected-thunk)] [test-value (test-thunk)]) (cond [(equal? test-value expected-value) #t] [else (printf "check-expect: actual value ~s differs from ~s, the expected value\n" test-value expected-value) (display-location a-loc) #f]))) (define (display-location a-loc) (printf " at: ~s, line ~s, column ~s\n" (srcloc-source a-loc) (srcloc-line a-loc) (srcloc-column a-loc))) ;; (define (check-within* test-datum a-loc test-thunk expected-thunk delta-thunk) ;; ;(with-handlers ([void ;; ; (lambda (exn) ;; ; (printf "check-within: ~s" ;; ; (exn-message exn)) ;; ; (newline) ;; ; (display-location test-datum a-loc) ;; ; #f)]) ;; (let ([expected-value (expected-thunk)] ;; [test-value (test-thunk)] ;; [delta-value (delta-thunk)]) ;; (cond ;; [(not (real? delta-value)) ;; (printf "check-within requires an inexact number for the range. ~s is not inexact.\n" delta-value) ;; ;;(display-location test-datum a-loc) ;; #f] ;; [(equal~? test-value expected-value delta-value) ;; #t] ;; [else ;; (printf "check-within: actual value ~s differs from ~s, the expected value.\n" test-value expected-value) ;; ;;(display-location test-datum a-loc) ;; #f]))) ;; (define (check-error* test-datum a-loc test-thunk expected-message-thunk) ;; (with-handlers ([void ;; (lambda (exn) ;; (printf "check-error: ~s" ;; (exn-message exn)) ;; (newline) ;; (display-location test-datum a-loc) ;; #f)]) ;; (let ([expected-message (expected-message-thunk)]) ;; (with-handlers ;; ([unexpected-no-error? ;; (lambda (une) ;; (printf "check-error expected the error ~s, but got ~s instead.\n" ;; expected-message ;; (unexpected-no-error-result une)) ;; (display-location test-datum a-loc) ;; #f)] ;; [exn:fail? ;; (lambda (exn) ;; (cond [(string=? (exn-message exn) expected-message) ;; #t] ;; [else ;; (printf "check-error: expected the error ~s, but got ~s instead.\n" ;; expected-message ;; (exn-message exn)) ;; (display-location test-datum a-loc) ;; #f]))]) ;; (let ([result (test-thunk)]) ;; (raise (make-unexpected-no-error result))))))) ;; a test is a thunk of type: (-> boolean) ;; where it returns true if the test was successful, ;; false otherwise. ;; accumulate-test! (define (accumulate-test! a-test) (set! *tests* (cons a-test *tests*))) ;; test-suffixed: number -> string (define (test-suffixed n) (case n [(0) "zero tests"] [(1) "one test"] [else (format "~a tests" n)])) ;; capitalize: string -> string (define (capitalize s) (cond [(> (string-length s) 0) (string-append (string (char-upcase (string-ref s 0))) (substring s 1))] [else s])) ;; run-tests: -> void (define (run-tests) (when (> (length *tests*) 0) ;; Run through the tests (printf "Running tests...\n") (let loop ([tests-passed 0] [tests-failed 0] [tests (reverse *tests*)]) (cond [(empty? tests) ;; Report test results (cond [(= tests-passed (length *tests*)) (display (case (length *tests*) [(1) "The test passed!"] [(2) "Both tests passed!"] [else (format "All ~a tests passed!" (length *tests*))])) (newline)] [else (printf "Ran ~a.\n" (test-suffixed (length *tests*))) (printf "~a passed.\n" (capitalize (test-suffixed tests-passed))) (printf "~a failed.\n" (capitalize (test-suffixed tests-failed)))]) (set! *tests* '())] [else (let* ([test-thunk (first tests)] [test-result (test-thunk)]) (cond [test-result (loop (add1 tests-passed) tests-failed (rest tests))] [else (loop tests-passed (add1 tests-failed) (rest tests))]))])))) (define-struct unexpected-no-error (result))