(module check mzscheme
(require (lib "contract.ss")
(lib "plt-match.ss")
(only (lib "list.ss" "srfi" "1") append-map filter filter-map)
(lib "cut.ss" "srfi" "26")
(file "../base.ss")
(file "../list.ss"))
(require-for-syntax
(file "../syntax.ss"))
(define-struct check-result (message annotations) #f)
(define-struct (check-problem check-result) () #f)
(define-struct (check-success check-result) () #f)
(define-struct (check-failure check-problem) () #f)
(define-struct (check-warning check-problem) () #f)
(define-struct (check-error check-problem) (exn) #f)
(define (check-result-exn result)
(if (check-error? result)
(check-error-exn result)
#f))
(define (pass)
(list (make-check-success "Okay" null)))
(define (fail message)
(list (make-check-failure message null)))
(define (warn message)
(list (make-check-warning message null)))
(define (check-with-handlers thunk)
(with-handlers ([exn? (lambda (exn)
(list (make-check-error "Exception raised" null exn)))])
(thunk)))
(define check-all append)
(define (check-with-annotations annotations results)
(map (cut annotate-check-result <> annotations)
results))
(define check-until-problems
(match-lambda*
[(list) null]
[(list-rest head tail)
(let* ([results (head)]
[problems (check-results->problems results)])
(if (null? problems)
(apply check-until-problems tail)
results))]))
(define (check-problems? results)
(ormap check-problem? results))
(define (check-results->problems results)
(define-values (warnings failures errors)
(check-results->warnings+failures+errors results))
(append errors failures warnings))
(define (check-results->warnings+failures+errors results)
(let loop ([results results]
[warnings null]
[failures null]
[errors null])
(match results
[(list)
(values (reverse warnings)
(reverse failures)
(reverse errors))]
[(list-rest (? check-success? result) other)
(loop other
warnings
failures
errors)]
[(list-rest (? check-warning? result) other)
(loop other
(cons result warnings)
failures
errors)]
[(list-rest (? check-failure? result) other)
(loop other
warnings
(cons result failures)
errors)]
[(list-rest (? check-error? result) other)
(loop other
warnings
failures
(cons result errors))])))
(define (check-result-annotation? result key)
(if (assoc key (check-result-annotations result))
#t
#f))
(define (check-result-annotation result key)
(assoc-value key (check-result-annotations result)))
(define (check-result-annotation/default result key default)
(assoc-value/default key (check-result-annotations result) default))
(define (annotate-check-result result annotations)
(let ([message (check-result-message result)]
[annotations (alist-merge (check-result-annotations result) annotations 'second)])
(cond [(check-success? result) (make-check-success message annotations)]
[(check-failure? result) (make-check-failure message annotations)]
[(check-warning? result) (make-check-warning message annotations)]
[(check-error? result) (make-check-error message annotations (check-error-exn result))])))
(provide/contract
[struct check-result ([message string?] [annotations (listof (cons/c symbol? any/c))])]
[struct (check-success check-result) ([message string?] [annotations (listof (cons/c symbol? any/c))])]
[struct (check-problem check-success) ([message string?] [annotations (listof (cons/c symbol? any/c))])]
[struct (check-warning check-problem) ([message string?] [annotations (listof (cons/c symbol? any/c))])]
[struct (check-failure check-problem) ([message string?] [annotations (listof (cons/c symbol? any/c))])]
[struct (check-error check-problem) ([message string?] [annotations (listof (cons/c symbol? any/c))] [exn exn?])]
[check-result-exn (-> check-result? (or/c exn? false/c))]
[pass (-> (list/c check-success?))]
[fail (-> string? (list/c check-failure?))]
[warn (-> string? (list/c check-warning?))]
[check-all (->* () (listof (listof check-result?)) ((listof check-result?)))]
[check-with-handlers (-> (-> (listof check-result?)) (listof check-result?))]
[check-with-annotations (-> (listof (cons/c symbol? any/c)) (listof check-result?) (listof check-result?))]
[check-until-problems (->* () (listof procedure?) ((listof check-problem?)))]
[check-problems? (-> (listof check-result?) boolean?)]
[check-results->problems (-> (listof check-result?) (listof check-problem?))]
[check-results->warnings+failures+errors (->* ((listof check-result?))
((listof check-warning?)
(listof check-failure?)
(listof check-error?)))]
[check-result-annotation? (-> check-result? symbol? boolean?)]
[check-result-annotation (-> check-result? symbol? any/c)]
[check-result-annotation/default (-> check-result? symbol? any/c any/c)]
[annotate-check-result (-> check-result? (listof (cons/c symbol? any/c)) check-result?)])
)