racket/collects/rackunit/private/gui/model2rml.rkt
2010-05-17 12:07:32 -04:00

459 lines
18 KiB
Racket

#lang racket/base
(require racket/class
unstable/class-iop
racket/list
racket/gui
racket/match
racket/file
mrlib/include-bitmap
(prefix-in drlink: "drracket-ui.rkt")
"interfaces.rkt"
"config.rkt")
(provide model-renderer%
output-icon)
(define (output-icon)
(make-object image-snip% (include-bitmap "output-icon.png")))
(define top-align (make-object style-delta% 'change-alignment 'top))
(define model-renderer%
(class object%
(init-field controller)
(init-field editor)
(super-new)
(define/public (render-model/short model)
(cond [(is-a? model suite<%>) ;; (test-suite? test)
(render-suite-short-form model)]
[(is-a? model case<%>) ;; (test-case? test)
(render-case-short-form model)]))
(define/public (render-model/long model)
(cond [(is-a? model suite<%>) ;; (test-suite? test)
(render-suite-long-form model)]
[(is-a? model case<%>) ;; (test-case? test)
(render-case-long-form model)]))
(define/private (put styles . texts)
(send/apply editor insert/styles styles texts))
(define/private (put+click styles clickback . texts)
(send/apply editor insert/styles+click styles clickback texts))
(define/private (blank)
(send editor newline))
(define/private (n-things number singular plural)
(if (= 1 number)
(format "~a ~a" number singular)
(format "~a ~a" number plural)))
(define/private (render-suite-long-form model)
(let [(suite (send/i model suite<%> get-test))
(children (send/i model suite<%> get-children))
(parent (send/i model suite<%> get-parent))]
(let* [(successes
(filter (lambda (c) (send/i c result<%> success?)) children))
(failures
(filter (lambda (c) (send/i c result<%> failure?)) children))
(errors
(filter (lambda (c) (send/i c result<%> error?)) children))
(unexecuted
(filter (lambda (c) (not (send/i c result<%> finished?))) children))
(finished? (send/i model suite<%> finished?))
(num-tests (length children))
(count-successes (length successes))
(count-failures (length failures))
(count-errors (length errors))
(count-unexecuted (length unexecuted))
(total-successes (send/i model suite<%> get-total-successes))
(total-failures (send/i model suite<%> get-total-failures))
(total-cases (send/i model suite<%> get-total-cases))
(output? (send/i model suite<%> has-output?))
(trash? (send/i model suite<%> has-trash?))]
(put '(large italic bold) (send/i model suite<%> get-name) "\n")
(when parent
(put '() " (in ")
(put+click '(clickback bold)
(lambda _ (send/i controller controller<%> set-selected-model parent))
(send/i parent result<%> get-name))
(put '() ")\n"))
(blank)
(if (positive? total-cases)
(begin (put '() (format "Total: ~a"
(n-things total-successes "success" "successes")))
(when (positive? total-failures)
(put '() (format ", ~a"
(n-things total-failures "failure" "failures"))))
(blank)
(blank)
(when trash?
(put (list top-align) (output-icon))
(put '() "Tests did not clean up resources.\n"))
(when output?
(put (list top-align) (output-icon))
(put '() "Tests produced output.\n"))
(when (or trash? output?)
(blank))
(when (positive? count-failures)
(put '(large)
(format "Failures (~a/~a)\n"
count-failures
num-tests))
(for-each (lambda (m) (render-model/short m)) failures)
(blank))
(when (positive? count-errors)
(put '(large)
(format "Errors (~a/~a)\n"
count-errors
num-tests))
(for-each (lambda (m) (render-model/short m)) errors)
(blank))
(when (positive? count-unexecuted)
(put '(large)
(format "Unexecuted (~a/~a)\n"
(length unexecuted)
num-tests))
(for-each (lambda (m) (render-model/short m)) unexecuted)
(blank))
(when (positive? count-successes)
(put '(large)
(format "Successes (~a/~a)\n"
count-successes
num-tests))
(for-each (lambda (m) (render-model/short m)) successes)
(blank)))
(if finished?
(put '()
"This test suite is empty.")
(put '(test-unexecuted)
"This test suite has not been executed."))))))
(define/private (render-model-link model suite?)
(let ([styles (if suite? '(clickback bold) '(clickback))])
(put+click styles
(lambda _ (send/i controller controller<%> set-selected-model model))
(send/i model result<%> get-name))
(when (or (send/i model result<%> has-output?)
(send/i model result<%> has-trash?))
(put styles (output-icon)))))
(define/private (render-suite-short-form model)
(let [(suite (send/i model suite<%> get-test))]
(let* [(children (send/i model suite<%> get-children))
(successes
(filter (lambda (c) (send/i c result<%> success?)) children))
(failures
(filter (lambda (c) (send/i c result<%> failure?)) children))
(errors
(filter (lambda (c) (send/i c result<%> error?)) children))
(unexecuted
(filter (lambda (c) (not (send/i c result<%> finished?))) children))
(num-tests (length children))
(total-failures (send/i model suite<%> get-total-failures))]
(let [(style (if (and (null? failures) (null? errors)) 'normal 'red))]
(render-model-link model #t)
(if (send/i model suite<%> finished?)
(when (positive? total-failures)
(put `(,style)
(format " (~a)"
(n-things total-failures "failure" "failures"))))
(put '(test-unexecuted) " not yet executed"))
(blank)))))
(define/private (render-case-short-form model)
(cond [(send/i model case<%> success?)
(render-success-short-form model)]
[(send/i model case<%> failure?)
(render-failure-short-form model)]
[(send/i model case<%> error?)
(render-error-short-form model)]
[(not (send/i model case<%> finished?))
(render-not-executed-short-form model)]))
(define/private (render-success-short-form model)
(render-model-link model #f)
(put '() " succeeded\n"))
(define/private (render-failure-short-form model)
(let* [(exn (send/i model case<%> get-result))
(exnmsg (send/i model case<%> get-property prop:failure-message))
(exnname (send/i model case<%> get-property prop:failure-assertion))]
(render-model-link model #f)
(put '() " failed")
(when exnname
(put '() " on ")
(put '(fail-type) (format "~a" exnname)))
(when exnmsg
(put '() " with message: ")
(put '(exn-message) exnmsg))
(blank)))
(define/private (render-error-short-form model)
(let [(exn (send/i model case<%> get-result))]
(render-model-link model #f)
(cond [(exn? exn)
(put '() " threw an exception of type ")
(put '(exn-type) (format "~a" (object-name exn)))
(put '() " with message: ")
(put '(exn-message) (exn-message exn))]
[else
(put '() (format " raised the value ~e" exn))])
(blank)))
(define/private (render-not-executed-short-form model)
(render-model-link model #f)
(put '(test-unexecuted) " has not been executed\n"))
(define/private (render-case-long-form model)
(cond [(send/i model case<%> success?)
(render-success-long-form model)]
[(send/i model case<%> failure?)
(render-failure-long-form model)]
[(send/i model case<%> error?)
(render-error-long-form model)]
[(not (send/i model case<%> finished?))
(render-not-executed-long-form model)])
(when (send/i model case<%> finished?)
(render-timing model)
(render-trash model)
(render-output model)))
(define/private (render-model-link* model suite?)
(let [(parent (send/i model result<%> get-parent))]
(let ([styles (if suite? '(bold) '())])
(put `(large italic ,@styles)
(send/i model result<%> get-name))
(blank)
(when parent
(put '() " in ")
(put+click `(clickback bold)
(lambda _ (send/i controller controller<%> set-selected-model parent))
(send/i parent result<%> get-name))
(blank))
(blank))))
(define/private (render-success-long-form model)
(render-model-link* model #f)
(put '() "The test case succeeded.\n\n"))
(define/private (render-failure-long-form model)
(render-model-link* model #f)
(let* [(exn (send/i model case<%> get-result))
(failure-msgs (send/i model case<%> get-property-set prop:failure-message))
(messages
(if (string? (exn-message exn))
(cons (exn-message exn) failure-msgs)
failure-msgs))
(exnname (send/i model case<%> get-property prop:failure-assertion))
(exnlocs
(send/i model case<%> get-property-set prop:failure-location))
(expecteds (send/i model case<%> get-property-set 'expected))
(actuals (send/i model case<%> get-property-set 'actual))
(params (send/i model case<%> get-property prop:failure-parameters))
(other-properties
(filter (lambda (p) (not (known-property? (car p))))
(send/i model case<%> get-all-properties)))
(exn2 (send/i model case<%> get-property 'exception))]
(put '() "The test case failed on ")
(put '(fail-type) (format "~a" exnname))
(put '() ".\n\n")
(render-source-location "Check location" exnlocs)
(render-backtrace-link "Backtrace of check failure:" exn)
(render-messages messages)
(if (and (pair? expecteds) (pair? actuals))
(render-expected+actual (car expecteds) (car actuals))
(render-parameters params))
(render-embedded-exception exn2)
(render-other-properties other-properties)
))
(define/private (render-error-long-form model)
(render-model-link* model #f)
(let [(exn (send/i model case<%> get-result))]
(cond [(exn? exn)
(put '() "The test case threw an exception of type ")
(put '(exn-type) (format "~a" (object-name exn)))
(put '() ".\n\n")
(render-backtrace-link "Exception backtrace:" exn)
(render-messages (list (exn-message exn)))
(render-value "Exception value:\n" exn)]
[else
(put '() "The test raised a value that was not "
"an instance of an exception struct.\n\n")
(render-value "Raised value:\n" exn)])))
(define/private (render-not-executed-long-form model)
(render-model-link* model #f)
(put '(test-unexecuted)
"The test case has not been executed."))
(define/private (render-value text value)
(put '() text)
(render-value-box value)
(blank))
(define/private (render-value-box value)
(send editor insert-wide-box
(lambda (editor)
(send editor insert/styles '(value) (format "~v" value)))))
(define/private (render-value-box/display value)
(send editor insert-wide-box
(lambda (editor)
(send editor insert/styles '(value) (format "~a" value)))))
(define/private (render-messages messages)
(when (pair? messages)
(put '() "Message:\n")
(for-each
(lambda (message)
(send editor insert-wide-box
(lambda (editor)
(send editor insert/styles '(exn-message) message))))
messages)
(blank)))
(define/private (render-expected+actual expected actual)
(put '() "Actual:\n")
(render-value-box actual)
(put '() "Expected:\n")
(render-value-box expected)
(blank))
(define/private (render-parameters parameters)
(when (and parameters (pair? parameters))
(put '() "Parameters:\n")
(for-each (lambda (parameter) (render-value-box parameter))
parameters)
(blank)))
(define/private (render-other-properties properties)
(when (pair? properties)
(put '() "Additional information:\n")
(for-each (lambda (p) (render-other-property (car p) (cdr p)))
properties)
(blank)))
(define/private (render-other-property key value)
(put '() (format "key ~s:" key))
(when (exn? value)
(inline-backtrace-link "" value))
(put '() "\n")
(render-value-box value))
(define/private (render-embedded-exception exn)
(when (exn? exn)
(put '() "Received exception")
(inline-backtrace-link "" exn)
(put '() ":\n")
(render-value-box exn)
(blank)))
(define/private (render-source-location label location-reps)
(when (pair? location-reps)
(let* ([location-reps (reverse location-reps)]
[rep0 (car location-reps)]
[reps (cdr location-reps)])
(put '() label ": ")
(inline-source-location/1 rep0)
(blank)
(when (pair? reps)
(put '() "Inner locations: ")
(for-each (lambda (r) (inline-source-location/1 r)) reps)
(blank))
(blank)))
(unless (pair? location-reps)
(put '() label " is not available.")
(blank)
(blank)))
(define/private (inline-source-location/1 location)
(match location
[(list src line col pos span)
(let* ([file-name (resolve-source-file src)]
[short-file-name
(if (or (path? file-name) (string? file-name))
(path->string (file-name-from-path file-name))
file-name)]
[source-location (format "~a:~a:~a" short-file-name line col)])
(inline-source-location/h source-location src pos span))]
[_ (put '() "not usable")]))
(define/private (inline-source-location/h source-location src pos span)
(if (and src (drlink:can-show-source?))
(put+click '(clickback)
(lambda _ (drlink:show-source src pos span))
source-location)
(put '() source-location)))
(define/private (render-backtrace-link text exn)
(when (exn? exn)
(if (drlink:has-backtrace? exn)
(inline-backtrace-link text exn)
(begin (put '() text)
(put '() " not available")))
(blank)
(blank)))
(define/private (inline-backtrace-link text exn)
(when (drlink:has-backtrace? exn)
(put '() text))
(when (drlink:has-errortrace-backtrace? exn)
(put '() " ")
(put+click '(clickback)
(lambda _ (drlink:show-errortrace-backtrace exn))
"[from DrRacket]"))
(when (drlink:has-primitive-backtrace? exn)
(put '() " ")
(put+click '(clickback)
(lambda _ (drlink:show-primitive-backtrace exn))
"[from racket]")))
(define/private (render-output model)
(let [(output (send/i model case<%> get-output))]
(when (pair? output)
(put '() "Output:\n")
(send editor insert-wide-box
(lambda (editor)
(for ([mode+text output])
(let ([styles
(if (eq? (car mode+text) 'error)
'(red italic)
'(darkblue))]
[buf (apply bytes-append
(reverse (cdr mode+text)))])
(send editor insert/styles styles
(bytes->string/utf-8 buf #\?))))))
(blank))))
(define/private (render-timing model)
(let [(timing (send/i model case<%> get-timing))]
(when timing
(let ([cpu (car timing)]
[real (cadr timing)]
[gc (caddr timing)])
(put '() "Timing:\n")
(put '() (format "cpu: ~a; real: ~a; gc: ~a\n\n"
cpu real gc))))))
(define/private (render-trash model)
(let ([trash (send/i model case<%> get-trash)])
(when (pair? trash)
(put '() "Test did not clean up resources:\n")
(for-each (lambda (t) (render-value-box/display t)) trash)
(blank))))
(define/private (resolve-source-file src)
(or (and (is-a? src editor<%>)
(let* [(tmp?-box (box #t))
(filename (send src get-filename tmp?-box))]
(if (and filename (not (unbox tmp?-box)))
filename
#f)))
(cond [(path? src) (path->string src)]
[(string? src) src]
[else 'unknown])))
))