#lang racket/base ;; Tests for Typed Racket tooltips that are normally displayed in DrRacket. ;; These tests capture those results by listening to a logger and checks that ;; certain types are recorded at the right locations. (require "test-utils.rkt" racket/list racket/match rackunit (for-syntax racket/base)) (provide tests) (gen-test-main) (define-for-syntax debug? #f) (define-syntax (debug stx) (syntax-case stx () [(_ e) debug? #'e] [_ #'(void)])) (define-logger online-check-syntax) (define receiver (make-log-receiver online-check-syntax-logger 'info 'online-check-syntax)) ;; This checks the given predicate on the tooltip vectors and will also ;; check that there's only one tooltip provided per location (define-syntax-rule (check-tooltip exp pred) (check-true (run-tooltip-test (quote exp) pred))) (define (run-tooltip-test sexp pred) (define namespace (make-base-namespace)) (define-values (in out) (make-pipe)) (port-count-lines! in) (port-count-lines! out) (write `(module a typed/racket ,sexp) out) (parameterize ([current-logger online-check-syntax-logger] [current-namespace namespace]) (eval-syntax (namespace-syntax-introduce (read-syntax 'tester in)))) (log-message online-check-syntax-logger 'info 'online-check-syntax "done" 'done) (define result (process-tooltips pred)) (clear) result) (define (process-tooltips pred) (let loop () (define result (sync receiver)) (cond [(eq? 'done (vector-ref result 2)) 'no-tooltips] [else (define stxs (vector-ref result 2)) (define tooltips (and (list? stxs) (syntax? (car stxs)) (syntax-property (car stxs) 'mouse-over-tooltips))) (if tooltips (and (pred tooltips) (unique-locations? tooltips)) (loop))]))) (define (clear) (let loop () (when (sync/timeout 0 receiver) (loop)))) ;; has-type-at? : (Listof (List String Int Int)) -> (Listof Vector) -> Boolean (define ((has-types-at? lst) tooltips) ;; turn debug? on to print the tooltip types and locations (debug (for ([tooltip (in-list tooltips)]) (match-define (vector stx start* end* type*) tooltip) (printf "~a ~a ~a~n" start* end* (if (procedure? type*) (type*) type*)))) (for/and ([entry (in-list lst)]) (match-define (list regex start end) entry) (for/or ([tooltip (in-list tooltips)]) (match-define (vector stx start* end* type*) tooltip) (and (= start start*) (= end end*) (regexp-match? regex (if (procedure? type*) (type*) type*)))))) ;; ensures there are no duplicate type tooltips for a single syntax location (define (unique-locations? tooltips) (define locations (for/list ([tooltip (in-list tooltips)]) (match-define (vector _ start end _) tooltip) (list start end))) (if (check-duplicates locations) 'duplicate-tooltips #t)) (define tests (test-suite "Tooltip tests" (check-tooltip (string-append "foo" "bar") (has-types-at? (list (list "^String$" 38 43)))) (check-tooltip (for/list : (Listof Integer) ([i (list 1 2 3)]) i) (has-types-at? (list (list "^\\(Listof Integer\\)$" 23 24) (list "^\\(Listof Integer\\)$" 72 73)))) (check-tooltip (class object% (super-new) (field [x : Integer 0]) x (set! x 3)) (has-types-at? (list (list "^Integer$" 74 75)))) (check-tooltip (values 1 2) (has-types-at? (list (list #rx"Value 1:.*One.*Value 2:.*Positive-Byte" 23 24))))))