racket/collects/redex/tests/bitmap-test-util.rkt
Robby Findler 2170e172a4 tidy up gui
Also improve the speed of compute-diffs by approximately 350x
(from 18+ seconds to about 50 milliseconds on the test case
that's currently failing)
2013-03-06 12:31:49 -06:00

250 lines
10 KiB
Racket

#lang racket/base
(require framework
slideshow/pict
racket/runtime-path
racket/gui/base
(for-syntax racket/base)
racket/class
racket/promise
"../pict.rkt"
"../reduction-semantics.rkt")
(provide btest done show-bitmap-test-gui?)
(define show-bitmap-test-gui? (make-parameter #t))
(define just-save-failures? (getenv "PLTJUSTSAVEFAILURES"))
(define tests 0)
(define failed-tests 0)
(define failed-panels '())
(define (done)
(if (zero? failed-tests)
(printf "bitmap-test.rkt: ~a tests, all passed\n" tests)
(eprintf "bitmap-test.rkt: ~a tests, ~a failed\n" tests failed-tests)))
(define-syntax (btest stx)
(syntax-case stx ()
[(_ test-exp bitmap-filename)
#`(test/proc
#,(syntax-line stx)
(λ () test-exp)
bitmap-filename)]))
(define-runtime-path bmps-dir (format "bmps-~a" (system-type)))
(define (test/proc line-number pict-thunk raw-bitmap-filename)
(set! tests (+ tests 1))
(let* ([pict (set-fonts/call pict-thunk)]
[bitmap-filename
(build-path bmps-dir
raw-bitmap-filename)]
[old-bitmap (if (file-exists? bitmap-filename)
(read-bitmap bitmap-filename)
(let* ([bm (make-bitmap 100 20)]
[bdc (make-object bitmap-dc% bm)])
(send bdc clear)
(send bdc draw-text "does not exist" 0 0)
(send bdc set-bitmap #f)
bm))]
[new-bitmap ((if (eq? (system-type) 'unix)
make-bitmap
make-screen-bitmap)
(ceiling (inexact->exact (pict-width pict)))
(ceiling (inexact->exact (pict-height pict))))]
[bdc (make-object bitmap-dc% new-bitmap)])
(send bdc clear)
(draw-pict pict bdc 0 0)
(send bdc set-bitmap #f)
(unless (bitmaps-same? old-bitmap new-bitmap)
(set! failed-tests (+ failed-tests 1))
(cond
[just-save-failures?
(eprintf "saving ~a\n" bitmap-filename)
(void (send new-bitmap save-file bitmap-filename 'png))]
[else
(when (show-bitmap-test-gui?)
(let ([failed-panel (make-failed-panel line-number bitmap-filename old-bitmap new-bitmap)])
(set! failed-panels (append failed-panels (list failed-panel)))))]))))
(define (set-fonts/call thunk)
(case (system-type)
[(unix)
(let ([rewrite-style
(λ (s)
(let loop ([s s])
(cond
[(pair? s) (cons (loop (car s)) (loop (cdr s)))]
[(eq? s 'roman) (verify-face "DejaVu Serif")]
[(eq? s 'swiss) (verify-face "DejaVu Sans")]
[else s])))])
(parameterize ([label-style (rewrite-style (label-style))]
[literal-style (rewrite-style (literal-style))]
[metafunction-style (rewrite-style (metafunction-style))]
[non-terminal-style (rewrite-style (non-terminal-style))]
[non-terminal-subscript-style (rewrite-style (non-terminal-subscript-style))]
[non-terminal-superscript-style (rewrite-style (non-terminal-superscript-style))]
[default-style (rewrite-style (default-style))])
(thunk)))]
[else
(thunk)]))
(define (verify-face face)
(unless (member face (get-face-list))
(error 'verify-face "unknown face: ~s" face))
face)
(define (bitmaps-same? old-bitmap new-bitmap)
(let ([w (send old-bitmap get-width)]
[h (send old-bitmap get-height)])
(and (= w (send new-bitmap get-width))
(= h (send new-bitmap get-height))
(let ([bytes1 (make-bytes (* w h 4))]
[bytes2 (make-bytes (* w h 4))])
(send old-bitmap get-argb-pixels 0 0 w h bytes1)
(send new-bitmap get-argb-pixels 0 0 w h bytes2)
(equal? bytes1 bytes2)))))
(define test-result-single-panel #f)
(define (get-test-result-single-panel)
(cond
[test-result-single-panel
test-result-single-panel]
[else
(let ()
(define f (new frame% [label "bitmap-test.rkt failures"]))
(define lined (new vertical-panel% [parent f] [style '(border)]))
(define sp (new panel:single% [parent lined]))
(define current-index 0)
(define hp (new horizontal-panel% [parent f] [alignment '(center center)]))
(define prev
(new button%
[label "Prev"]
[parent hp]
[callback
(λ (x y)
(set! current-index (modulo (- current-index 1) (length failed-panels)))
(update-gui))]))
(define next (new button%
[label "Next"]
[parent hp]
[callback
(λ (x y)
(set! current-index (modulo (+ current-index 1) (length failed-panels)))
(update-gui))]))
(define (update-gui)
(send sp active-child (list-ref failed-panels current-index)))
(set! test-result-single-panel sp)
(send f show #t)
sp)]))
(define (compute-diffs old-bitmap new-bitmap)
(define ow (send old-bitmap get-width))
(define nw (send new-bitmap get-width))
(define oh (send old-bitmap get-height))
(define nh (send new-bitmap get-height))
(define w (max ow nw))
(define h (max oh nh))
(define old-bytes (make-bytes (* ow oh 4)))
(define new-bytes (make-bytes (* nw nh 4)))
(define diff-bytes (make-bytes (* w h 4) 255))
(define number-of-different-pixels 0)
(send old-bitmap get-argb-pixels 0 0 ow oh old-bytes)
(send new-bitmap get-argb-pixels 0 0 nw nh new-bytes)
(let loop ([x 0])
(unless (= x w)
(let loop ([y 0])
(unless (= y h)
(define diff-start (* 4 (+ (* y w) x)))
(cond
[(and (< x nw)
(< y nh)
(< x ow)
(< y oh))
(define old-start (* 4 (+ (* y ow) x)))
(define new-start (* 4 (+ (* y nw) x)))
(define a (bytes-ref old-bytes old-start))
(define r (bytes-ref old-bytes (+ old-start 1)))
(define g (bytes-ref old-bytes (+ old-start 2)))
(define b (bytes-ref old-bytes (+ old-start 3)))
(cond
[(and (= a (bytes-ref new-bytes new-start))
(= r (bytes-ref new-bytes (+ new-start 1)))
(= g (bytes-ref new-bytes (+ new-start 2)))
(= b (bytes-ref new-bytes (+ new-start 3))))
(bytes-set! diff-bytes diff-start a)
(bytes-set! diff-bytes (+ diff-start 1) r)
(bytes-set! diff-bytes (+ diff-start 2) g)
(bytes-set! diff-bytes (+ diff-start 3) b)]
[else
(set! number-of-different-pixels (+ number-of-different-pixels 1))
;; don't need to set diff-start or (+ diff-start 1) since
;; the bytes are initialized to 255
(bytes-set! diff-bytes (+ diff-start 2) 0)
(bytes-set! diff-bytes (+ diff-start 3) 0)])]
[else
(bytes-set! diff-bytes (+ diff-start 2) 0)
(bytes-set! diff-bytes (+ diff-start 3) 0)])
(loop (+ y 1))))
(loop (+ x 1))))
(define diff-bitmap (make-bitmap w h))
(send diff-bitmap set-argb-pixels 0 0 w h diff-bytes)
(values diff-bitmap number-of-different-pixels))
(define (make-failed-panel line-number filename old-bitmap new-bitmap)
(define diff-bitmap 'unk)
(define number-of-different-pixels #f)
(define f (new vertical-panel% [parent (get-test-result-single-panel)]))
(define msg (new message% [label (format "line ~a" line-number)] [parent f]))
(define hp (new horizontal-panel% [parent f]))
(define vp1 (new vertical-panel% [parent hp]))
(define vp2 (new vertical-panel% [parent hp]))
(define computing/differences-msg (new message% [label ""] [auto-resize #t] [parent f]))
(define chk (new check-box%
[label "Show diff"]
[parent f]
[callback
(λ (_1 _2)
(define (update-check)
(cond
[(send chk get-value)
(send right-hand set-label (force diff-bitmap))]
[else
(send right-hand set-label new-bitmap)]))
(cond
[(eq? diff-bitmap 'unk)
(send chk enable #f)
(send computing/differences-msg set-label "Computing diff ...")
(thread
(λ ()
(define-values (_diff-bitmap number-of-different-pixels) (compute-diffs old-bitmap new-bitmap))
(set! diff-bitmap _diff-bitmap)
(queue-callback
(λ ()
(send computing/differences-msg set-label (format "~a pixels different" number-of-different-pixels))
(send chk enable #t)
(update-check)))))]
[else
(update-check)]))]))
(define btn (new button%
[parent f]
[label "Save"]
[callback
(λ (x y)
(send new-bitmap save-file filename 'png))]))
(define left-label (new message%
[parent vp1]
[label (format "Old ~ax~a" (send old-bitmap get-width) (send old-bitmap get-height))]))
(define left-hand (new message%
[parent vp1]
[label old-bitmap]))
(define right-label (new message%
[parent vp2]
[label (format "New ~ax~a" (send new-bitmap get-width) (send new-bitmap get-height))]))
(define right-hand (new message%
[parent vp2]
[label new-bitmap]))
(send left-hand set-label old-bitmap)
(send right-hand set-label new-bitmap)
f)