Merge pull request #70 from florence/coverage-type

Change the coverage object type
This commit is contained in:
Spencer Florence 2015-05-20 11:13:09 -05:00
commit 30195b87a2
15 changed files with 195 additions and 189 deletions

View File

@ -3,7 +3,8 @@
make-cover-environment clear-coverage! make-cover-environment clear-coverage!
get-test-coverage get-test-coverage
current-cover-environment environment? current-cover-environment environment?
environment-compile environment-namespace) environment-compile environment-namespace
coverage-wrapper-map)
#| #|
@ -32,6 +33,7 @@ in "coverage.rkt". This raw coverage information is converted to a usable form b
racket/port racket/port
"private/shared.rkt" "private/shared.rkt"
"private/file-utils.rkt" "private/file-utils.rkt"
"private/format-utils.rkt"
"strace.rkt") "strace.rkt")
;; An environment has: ;; An environment has:
@ -189,13 +191,8 @@ in "coverage.rkt". This raw coverage information is converted to a usable form b
(define (clear-coverage!) (define (clear-coverage!)
(current-cover-environment (make-cover-environment))) (current-cover-environment (make-cover-environment)))
(define (make-kernel-namespace) (define (make-cover-environment [ns (make-empty-namespace)])
(define ns (make-empty-namespace)) (kernelize-namespace! ns)
(define cns (current-namespace))
(namespace-attach-module cns ''#%builtin ns)
ns)
(define (make-cover-environment [ns (make-kernel-namespace)])
(parameterize ([current-namespace ns]) (parameterize ([current-namespace ns])
(define ann (load-annotate-top)) (define ann (load-annotate-top))
(environment (environment
@ -204,6 +201,10 @@ in "coverage.rkt". This raw coverage information is converted to a usable form b
ann ann
(load-raw-coverage)))) (load-raw-coverage))))
(define (kernelize-namespace! ns)
(define cns (current-namespace))
(namespace-attach-module cns ''#%builtin ns))
(define (get-annotate-top) (define (get-annotate-top)
(get-val environment-ann-top)) (get-val environment-ann-top))
(define (load-annotate-top) (define (load-annotate-top)
@ -217,13 +218,6 @@ in "coverage.rkt". This raw coverage information is converted to a usable form b
(define (load-cover-name) (define (load-cover-name)
(dynamic-require 'cover/coverage 'cover-name)) (dynamic-require 'cover/coverage 'cover-name))
(define (load-cover-setter)
(dynamic-require 'cover/coverage '!))
#;
(define (get-check-handler-parameter)
(namespace-variable-value (module->namespace 'rackunit)
'current-check-handler))
(define (get-namespace) (define (get-namespace)
(get-val environment-namespace)) (get-val environment-namespace))
@ -234,7 +228,10 @@ in "coverage.rkt". This raw coverage information is converted to a usable form b
(define (get-val access) (define (get-val access)
(access (current-cover-environment))) (access (current-cover-environment)))
;; -> [Hashof PathString (Listof (List Boolean srcloc))] (struct coverage-wrapper (map function)
#:property prop:procedure (struct-field-index function))
;; -> coverage/c
;; returns a hash of file to a list, where the first of the list is if ;; returns a hash of file to a list, where the first of the list is if
;; that srcloc was covered or not ;; that srcloc was covered or not
;; based on <pkgs>/drracket/drracket/private/debug.rkt ;; based on <pkgs>/drracket/drracket/private/debug.rkt
@ -255,7 +252,16 @@ in "coverage.rkt". This raw coverage information is converted to a usable form b
(lambda (l) (cons v l)) (lambda (l) (cons v l))
null)) null))
;; Make the hash map immutable ;; Make the hash map immutable
(for/hash ([(k v) (in-hash out)]) (values k v)))) (define coverage (for/hash ([(k v) (in-hash out)]) (values k v)))
(define file-map (make-hash))
(coverage-wrapper
coverage
(lambda (key location)
(define f
(hash-ref! file-map key
(lambda ()
(make-covered? coverage key))))
(f location)))))
(define current-cover-environment (define current-cover-environment
(make-parameter (make-cover-environment))) (make-parameter (make-cover-environment)))
@ -287,7 +293,6 @@ in "coverage.rkt". This raw coverage information is converted to a usable form b
racket/format racket/format
racket/lazy-require) racket/lazy-require)
;; break cyclic dependency in testing ;; break cyclic dependency in testing
(lazy-require ["private/format-utils.rkt" (make-covered?)])
(define-runtime-path simple-multi/2.rkt "tests/simple-multi/2.rkt") (define-runtime-path simple-multi/2.rkt "tests/simple-multi/2.rkt")
(define env (make-cover-environment)) (define env (make-cover-environment))
(define ns (environment-namespace env)) (define ns (environment-namespace env))
@ -302,8 +307,7 @@ in "coverage.rkt". This raw coverage information is converted to a usable form b
(namespace-require `(file ,modpath))) (namespace-require `(file ,modpath)))
(check-equal? (eval `(two)) 10) (check-equal? (eval `(two)) 10)
(define x (get-test-coverage env)) (define x (get-test-coverage env))
(define covered? (define covered? (curry x file))
(make-covered? (hash-ref x file) file))
(for ([_ (in-string (file->string file))] (for ([_ (in-string (file->string file))]
[i (in-naturals 1)]) [i (in-naturals 1)])
(define c (covered? i)) (define c (covered? i))

View File

@ -10,7 +10,6 @@
(contract-out (contract-out
[coverage/c contract?] [coverage/c contract?]
[file-coverage/c contract?]
[test-files! (->* () (#:submod symbol? [test-files! (->* () (#:submod symbol?
#:env environment?) #:env environment?)
#:rest #:rest
@ -32,11 +31,6 @@
[get-test-coverage (->* () (environment?) coverage/c)] [get-test-coverage (->* () (environment?) coverage/c)]
[irrelevant-submodules (parameter/c (or/c #f (listof symbol?)))] [irrelevant-submodules (parameter/c (or/c #f (listof symbol?)))]
[make-covered?
(-> file-coverage/c path-string?
(->* (exact-positive-integer?)
(#:byte? boolean?)
(or/c 'covered 'uncovered 'irrelevant)))]
[generate-coveralls-coverage coverage-gen/c] [generate-coveralls-coverage coverage-gen/c]
[generate-html-coverage coverage-gen/c] [generate-html-coverage coverage-gen/c]

View File

@ -1,8 +1,8 @@
#lang racket/base #lang racket/base
(provide coverage/c file-coverage/c coverage-gen/c) (provide coverage/c coverage-gen/c)
(require racket/contract) (require racket/contract)
(define file-coverage/c (listof (list/c boolean? srcloc?)))
;; if its a file path, will be an absolute path ;; if its a file path, will be an absolute path
(define coverage/c (hash/c any/c file-coverage/c)) (define coverage/c (-> any/c exact-positive-integer?
(define coverage-gen/c (->* (coverage/c) (path-string?) any)) (or/c 'covered 'uncovered 'irrelevant)))
(define coverage-gen/c (->* (coverage/c (listof path-string?)) (path-string?) any))

View File

@ -10,7 +10,7 @@
racket/runtime-path racket/runtime-path
racket/string racket/string
racket/system racket/system
"format-utils.rkt" "file-utils.rkt"
"shared.rkt") "shared.rkt")
@ -35,14 +35,14 @@
;; Coverage [path-string] -> Void ;; Coverage [path-string] -> Void
(define-runtime-path post "curl.sh") (define-runtime-path post "curl.sh")
(define (generate-coveralls-coverage coverage [dir "coverage"]) (define (generate-coveralls-coverage coverage files [dir "coverage"])
(send-coveralls-info (generate-and-save-data coverage dir))) (send-coveralls-info (generate-and-save-data coverage files dir)))
(define (generate-and-save-data coverage dir) (define (generate-and-save-data coverage files dir)
(make-directory* dir) (make-directory* dir)
(define coverage-path dir) (define coverage-path dir)
(define coverage-file (build-path coverage-path "coverage.json")) (define coverage-file (build-path coverage-path "coverage.json"))
(define data (generate-coveralls-report coverage)) (define data (generate-coveralls-report coverage files))
(vprintf "writing json to file ~s\n" coverage-file) (vprintf "writing json to file ~s\n" coverage-file)
(with-output-to-file coverage-file (with-output-to-file coverage-file
(thunk (write-json data)) (thunk (write-json data))
@ -57,7 +57,7 @@
(define temp-dir (make-temporary-file "covertmp~a" 'directory)) (define temp-dir (make-temporary-file "covertmp~a" 'directory))
(test-files! tests/prog.rkt) (test-files! tests/prog.rkt)
(define coverage (get-test-coverage)) (define coverage (get-test-coverage))
(define data-file (generate-and-save-data coverage temp-dir)) (define data-file (generate-and-save-data coverage (list (->absolute tests/prog.rkt)) temp-dir))
(define rfile (build-path temp-dir "coverage.json")) (define rfile (build-path temp-dir "coverage.json"))
(check-equal? data-file rfile) (check-equal? data-file rfile)
(check-true (file-exists? rfile))))) (check-true (file-exists? rfile)))))
@ -75,8 +75,8 @@
(unless result (unless result
(error 'coveralls "request to coveralls failed")))) (error 'coveralls "request to coveralls failed"))))
(define (generate-coveralls-report coverage) (define (generate-coveralls-report coverage files)
(define json (generate-source-files coverage)) (define json (generate-source-files coverage files))
(define build-type (determine-build-type)) (define build-type (determine-build-type))
(define git-info (get-git-info)) (define git-info (get-git-info))
(hash-merge json (hash-merge build-type git-info))) (hash-merge json (hash-merge build-type git-info)))
@ -89,7 +89,8 @@
(test-files! (path->string (simplify-path tests/prog.rkt))) (test-files! (path->string (simplify-path tests/prog.rkt)))
(define coverage (get-test-coverage)) (define coverage (get-test-coverage))
(define report (define report
(with-env ("COVERALLS_REPO_TOKEN" "abc") (generate-coveralls-report coverage))) (with-env ("COVERALLS_REPO_TOKEN" "abc")
(generate-coveralls-report coverage (list (->absolute file)))))
(check-equal? (check-equal?
(hash-ref report 'source_files) (hash-ref report 'source_files)
(list (hasheq 'source (file->string tests/prog.rkt) (list (hasheq 'source (file->string tests/prog.rkt)
@ -124,13 +125,13 @@
'service_job_id "abc" 'service_job_id "abc"
'repo_token #f)))) 'repo_token #f))))
;; Coverage -> JSexpr ;; Coverage (Listof PathString) -> JSexpr
;; Generates a string that represents a valid coveralls json_file object ;; Generates a string that represents a valid coveralls json_file object
(define (generate-source-files coverage) (define (generate-source-files coverage files)
(define src-files (define src-files
(for/list ([file (in-list (hash-keys coverage))] (for/list ([file (in-list files)]
#:when (absolute-path? file)) #:when (absolute-path? file))
(define local-file (path->string (find-relative-path (current-directory) file))) (define local-file (path->string (->relative file)))
(define src (file->string file)) (define src (file->string file))
(define c (line-coverage coverage file)) (define c (line-coverage coverage file))
(hasheq 'source src 'coverage c 'name local-file))) (hasheq 'source src 'coverage c 'name local-file)))
@ -144,7 +145,7 @@
(test-files! (path->string (simplify-path tests/prog.rkt))) (test-files! (path->string (simplify-path tests/prog.rkt)))
(define coverage (get-test-coverage)) (define coverage (get-test-coverage))
(check-equal? (check-equal?
(generate-source-files coverage) (generate-source-files coverage (list file))
(hasheq 'source_files (hasheq 'source_files
(list (hasheq 'source (file->string tests/prog.rkt) (list (hasheq 'source (file->string tests/prog.rkt)
'coverage (line-coverage coverage file) 'coverage (line-coverage coverage file)
@ -156,9 +157,8 @@
;; Coverage PathString Covered? -> [Listof CoverallsCoverage] ;; Coverage PathString Covered? -> [Listof CoverallsCoverage]
;; Get the line coverage for the file to generate a coverage report ;; Get the line coverage for the file to generate a coverage report
(define (line-coverage coverage file) (define (line-coverage coverage file)
(define covered? (make-covered? (hash-ref coverage file) file)) (define covered? (curry coverage file))
(define split-src (string-split (file->string file) "\n")) (define split-src (string-split (file->string file) "\n"))
(define file-coverage (hash-ref coverage file))
(define (process-coverage value rst-of-line) (define (process-coverage value rst-of-line)
(case (covered? value) (case (covered? value)
['covered (if (equal? 'uncovered rst-of-line) rst-of-line 'covered)] ['covered (if (equal? 'uncovered rst-of-line) rst-of-line 'covered)]

View File

@ -7,8 +7,8 @@
(define (->relative path) (define (->relative path)
(build-path (build-path
(find-relative-path (find-relative-path
(current-directory) (simple-form-path (current-directory))
path))) (simple-form-path path))))
(module+ test (module+ test
(parameterize ([current-directory (build-path "/test")]) (parameterize ([current-directory (build-path "/test")])
@ -19,8 +19,8 @@
(define (->absolute path) (define (->absolute path)
(if (absolute-path? path) (if (absolute-path? path)
(path->string (simplify-path path)) (path->string (simple-form-path path))
(path->string (simplify-path (build-path (current-directory) path))))) (path->string (simple-form-path (build-path (current-directory) path)))))
(module+ test (module+ test
(parameterize ([current-directory (build-path "/")]) (parameterize ([current-directory (build-path "/")])
(check-equal? (->absolute "a") "/a") (check-equal? (->absolute "a") "/a")

View File

@ -13,55 +13,57 @@
syntax/parse syntax/parse
"shared.rkt") "shared.rkt")
(module+ test (require rackunit "../cover.rkt" racket/runtime-path racket/set)) (module+ test (require rackunit racket/runtime-path racket/set))
;;;;; a Coverage is the output of (get-test-coverage) ;;;;; a Coverage is the output of (hash-of any (listof (list boolean srcloc?)))
;;;;; a FileCoverage is the values of the hashmap from (get-test-coverage)
;;;;; utils ;;;;; utils
;;; a Cover is (U 'covered 'uncovered 'irrelevant) ;;; a Cover is (U 'covered 'uncovered 'irrelevant)
;; [Hashof PathString [Hashof Natural Cover]] ;; Coverage Any -> [Nat -> Cover]
(define (make-covered? coverage key)
;; A Covered? is a [Nat [#:byte? Boolean] -> Cover] (unless (hash-has-key? coverage key)
(error 'cover "no coverage information for ~s" key))
;; FileCoverage PathString #:ignored-submods (maybe (listof symbol)) -> Covered? (define c (hash-ref coverage key))
(define (make-covered? c path)
(define submods (irrelevant-submodules)) (define submods (irrelevant-submodules))
(define vec
(list->vector (string->list (file->string path))))
(define file/byte->str-offset (make-byte->str-offset vec))
(define file-location-coverage-cache (define file-location-coverage-cache
(coverage-cache-file path c submods)) (coverage-cache-file key c submods))
(lambda (loc #:byte? [byte? #f]) (lambda (loc)
(hash-ref file-location-coverage-cache (if (not byte?) loc (- loc (file/byte->str-offset loc))) (hash-ref file-location-coverage-cache loc
'missing))) 'irrelevant)))
;; (or/c #f (listof symbol)) ;; (or/c #f (listof symbol))
(define irrelevant-submodules (make-parameter #f)) (define irrelevant-submodules (make-parameter #f))
;; Path FileCoverage -> [Hashof Natural Cover] ;; Path FileCoverage -> [Hashof Natural Cover]
;; build a hash caching coverage info for that file ;; build a hash caching coverage info for that file
(define (coverage-cache-file f c submods) (define (coverage-cache-file key c submods)
(vprintf "caching coverage info for ~s\n" f) (vprintf "caching coverage info for ~s\n" key)
(with-input-from-file f (if (not (path-string? key))
(thunk (for/hash ([i (in-range 1 (biggest c))])
(define lexer (values i (raw-covered? i c)))
(maybe-wrap-lexer (with-input-from-file key
(with-handlers ([exn:fail:read? (const racket-lexer)]) (thunk
(define f (read-language)) (define lexer
(if f (maybe-wrap-lexer
(f 'color-lexer racket-lexer) (with-handlers ([exn:fail:read? (const racket-lexer)])
racket-lexer)))) (define f (read-language))
(define irrelevant? (make-irrelevant? lexer f submods)) (if f
(define file-length (string-length (file->string f))) (f 'color-lexer racket-lexer)
(define cache racket-lexer))))
(for/hash ([i (in-range 1 (add1 file-length))]) (define irrelevant? (make-irrelevant? lexer key submods))
(values i (define file-length (string-length (file->string key)))
(cond [(irrelevant? i) 'irrelevant] (define cache
[else (raw-covered? i c)])))) (for/hash ([i (in-range 1 (add1 file-length))])
cache))) (values i
(cond [(irrelevant? i) 'irrelevant]
[else (raw-covered? i c)]))))
cache))))
;; FileCoverage -> Natural
(define (biggest c)
(apply max (map second c)))
(define (maybe-wrap-lexer lexer) (define (maybe-wrap-lexer lexer)
(if (procedure-arity-includes? lexer 3) (if (procedure-arity-includes? lexer 3)
@ -161,21 +163,26 @@
(loop (add1 s) (+ b l))]))) (loop (add1 s) (+ b l))])))
(module+ test (module+ test
(require racket/lazy-require)
(lazy-require ["../cover.rkt"
(make-cover-environment
test-files!
get-test-coverage)])
(define-runtime-path cover.rkt "../cover.rkt")
(define current-cover-environment
(dynamic-require cover.rkt 'current-cover-environment))
(define-runtime-path path2 "../tests/prog.rkt") (define-runtime-path path2 "../tests/prog.rkt")
(parameterize ([irrelevant-submodules #f]) (parameterize ([irrelevant-submodules #f])
(test-begin (test-begin
(parameterize ([current-cover-environment (make-cover-environment)]) (parameterize ([current-cover-environment (make-cover-environment)])
(define f (path->string (simplify-path path2))) (define f (path->string (simplify-path path2)))
(test-files! f) (test-files! f)
(define coverage (hash-ref (get-test-coverage) f)) (define coverage (get-test-coverage))
(define covered? (make-covered? coverage f)) (define covered? (curry coverage f))
(check-equal? (covered? 14) 'irrelevant) (check-equal? (covered? 14) 'irrelevant)
(check-equal? (covered? 14 #:byte? #t) 'irrelevant)
(check-equal? (covered? 17) 'irrelevant) (check-equal? (covered? 17) 'irrelevant)
(check-equal? (covered? 28) 'irrelevant) (check-equal? (covered? 28) 'irrelevant)
(check-equal? (covered? 35) 'covered) (check-equal? (covered? 35) 'covered)
(check-equal? (covered? 50) 'uncovered)
(check-equal? (covered? 51 #:byte? #t) 'uncovered)
(check-equal? (covered? 52) 'irrelevant) (check-equal? (covered? 52) 'irrelevant)
(check-equal? (covered? 53) 'irrelevant) (check-equal? (covered? 53) 'irrelevant)
(check-equal? (covered? 54) 'irrelevant))))) (check-equal? (covered? 54) 'irrelevant)))))

View File

@ -13,7 +13,6 @@
syntax/parse syntax/parse
unstable/sequence unstable/sequence
(only-in xml write-xexpr) (only-in xml write-xexpr)
"../format-utils.rkt"
"../shared.rkt") "../shared.rkt")
@ -27,9 +26,9 @@
[else 'uncovered]))) [else 'uncovered])))
;;; Coverage [PathString] -> Void ;;; Coverage [PathString] -> Void
(define (generate-html-coverage coverage [d "coverage"]) (define (generate-html-coverage coverage files [d "coverage"])
(define dir (simplify-path d)) (define dir (simplify-path d))
(define fs (get-files coverage dir)) (define fs (get-files coverage files dir))
(define asset-path (build-path dir "assets/")) (define asset-path (build-path dir "assets/"))
(write-files fs) (write-files fs)
(delete-directory/files asset-path #:must-exist? #f) (delete-directory/files asset-path #:must-exist? #f)
@ -40,13 +39,13 @@
(define temp-dir (make-temporary-file "covertmp~a" 'directory)) (define temp-dir (make-temporary-file "covertmp~a" 'directory))
(test-files! tests/basic/prog.rkt) (test-files! tests/basic/prog.rkt)
(define coverage (get-test-coverage)) (define coverage (get-test-coverage))
(generate-html-coverage coverage temp-dir) (generate-html-coverage coverage (list (->absolute tests/basic/prog.rkt)) temp-dir)
(check-true (file-exists? (build-path temp-dir "tests/basic/prog.html")))) (check-true (file-exists? (build-path temp-dir "tests/basic/prog.html"))))
(clear-coverage!))) (clear-coverage!)))
(define (get-files coverage dir) (define (get-files coverage files dir)
(define file-list (define file-list
(for/list ([(k v) (in-hash coverage)] (for/list ([k (in-list files)]
#:when (absolute-path? k)) #:when (absolute-path? k))
(vprintf "building html coverage for: ~a\n" k) (vprintf "building html coverage for: ~a\n" k)
(define exploded (explode-path k)) (define exploded (explode-path k))
@ -58,14 +57,14 @@
(define output-file (define output-file
(apply build-path (append coverage-dir-list (list relative-output-file)))) (apply build-path (append coverage-dir-list (list relative-output-file))))
(define output-dir (apply build-path coverage-dir-list)) (define output-dir (apply build-path coverage-dir-list))
(define assets-path (define assets-path
(path->string (path->string
(apply build-path (apply build-path
(append (build-list (sub1 (length coverage-dir-list)) (const "..")) (append (build-list (sub1 (length coverage-dir-list)) (const ".."))
(list "assets/"))))) (list "assets/")))))
(define xexpr (make-html-file (hash-ref coverage k) k assets-path)) (define xexpr (make-html-file coverage k assets-path))
(list output-file output-dir xexpr))) (list output-file output-dir xexpr)))
(define index (generate-index coverage)) (define index (generate-index coverage files))
(cons (list (build-path dir "index.html") dir index) (cons (list (build-path dir "index.html") dir index)
file-list)) file-list))
@ -77,7 +76,7 @@
(define d "coverage") (define d "coverage")
(test-files! f) (test-files! f)
(define coverage (get-test-coverage)) (define coverage (get-test-coverage))
(define files (get-files coverage d)) (define files (get-files coverage (list f) d))
(define (maybe-path->string p) (define (maybe-path->string p)
(if (string? p) p (path->string p))) (if (string? p) p (path->string p)))
(check-equal? (list->set (map (compose maybe-path->string first) (check-equal? (list->set (map (compose maybe-path->string first)
@ -114,7 +113,7 @@
;; FileCoverage PathString Path -> Xexpr ;; FileCoverage PathString Path -> Xexpr
(define (make-html-file coverage path assets-path) (define (make-html-file coverage path assets-path)
(define covered? (make-covered? coverage path)) (define covered? (curry coverage path))
(define cover-info (expression-coverage/file path covered?)) (define cover-info (expression-coverage/file path covered?))
(define-values (covered total) (values (first cover-info) (second cover-info))) (define-values (covered total) (values (first cover-info) (second cover-info)))
`(html () `(html ()
@ -132,8 +131,8 @@
(test-begin (test-begin
(define f (path->string (simplify-path tests/basic/prog.rkt))) (define f (path->string (simplify-path tests/basic/prog.rkt)))
(test-files! f) (test-files! f)
(define cov (hash-ref (get-test-coverage) f)) (define cov (get-test-coverage))
(define covered? (make-covered? cov f)) (define covered? (curry cov f))
(check-equal? (make-html-file cov f "assets/") (check-equal? (make-html-file cov f "assets/")
`(html () `(html ()
(head () (head ()
@ -156,8 +155,7 @@
(test-begin (test-begin
(define f (path->string (simplify-path tests/basic/prog.rkt))) (define f (path->string (simplify-path tests/basic/prog.rkt)))
(test-files! f) (test-files! f)
(define cov (hash-ref (get-test-coverage) f)) (define covered? (curry (get-test-coverage) f))
(define covered? (make-covered? cov f))
(define lines (string-split (file->string f) "\n")) (define lines (string-split (file->string f) "\n"))
(check-equal? (file->html f covered?) (check-equal? (file->html f covered?)
`(div () `(div ()
@ -230,10 +228,10 @@
;; Index File ;; Index File
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Coverage PathString -> Xexpr ;; Coverage (Listof PathString) -> Xexpr
;; Generate the index html page for the given coverage information ;; Generate the index html page for the given coverage information
(define (generate-index coverage) (define (generate-index coverage files)
(define expression-coverage (expression-coverage/all coverage)) (define expression-coverage (expression-coverage/all coverage files))
`(html `(html
(head () (head ()
(meta ([charset "utf-8"])) (meta ([charset "utf-8"]))
@ -338,11 +336,11 @@
;; the first element is the number of covered expressions ;; the first element is the number of covered expressions
;; the second element is the total number of expressions. This will never be 0. ;; the second element is the total number of expressions. This will never be 0.
;; Coverage -> [Hash FilePath ExpressionInfo] ;; Coverage (Listof PathString) -> [Hash FilePath ExpressionInfo]
;; returns a hash that maps file paths to an ExpressionInfo ;; returns a hash that maps file paths to an ExpressionInfo
(define (expression-coverage/all coverage) (define (expression-coverage/all coverage files)
(for/hash ([(file data) (in-hash coverage)]) (for/hash ([file (in-list files)])
(values file (expression-coverage/file file (make-covered? data file))))) (values file (expression-coverage/file file (curry coverage file)))))
;; FilePath Covered? -> ExpressionInfo ;; FilePath Covered? -> ExpressionInfo
;; Takes a file path and a Covered? and ;; Takes a file path and a Covered? and
@ -352,27 +350,33 @@
;; we don't need to look at the span because the coverage is expression based ;; we don't need to look at the span because the coverage is expression based
(define p (syntax-position e)) (define p (syntax-position e))
(if p (if p
(covered? p #:byte? #t) (covered? p)
'missing)) 'missing))
(define e (define e
(with-module-reading-parameterization (with-module-reading-parameterization
(thunk (with-input-from-file path read-syntax)))) (thunk (with-input-from-file path
(lambda ()
(port-count-lines! (current-input-port))
(read-syntax))))))
(define (ret e) (values (e->n e) (a->n e))) (define (ret e) (values (e->n e) (a->n e)))
(define (a->n e) (define (a->n e)
(case (is-covered? e) (case (is-covered? e)
[(covered uncovered) 1] [(covered uncovered) 1]
[else 0])) [else 0]))
(define (e->n e) (if (eq? (is-covered? e) 'covered) 1 0)) (define (e->n e) (if (eq? (is-covered? e) 'covered) 1 0))
(define-values (covered total) (define-values (covered total)
(let recur ([e e]) (let recur ([e e])
(syntax-parse e (syntax-parse e
[(v ...) [(v ...)
(for/fold ([covered (e->n e)] [count (a->n e)]) (for/fold ([covered (e->n e)] [count (a->n e)])
([e (in-syntax e)]) ([v (in-syntax e)])
(define-values (cov cnt) (recur e)) (define-values (cov cnt) (recur v))
(values (+ covered cov) (values (+ covered cov)
(+ count cnt)))] (+ count cnt)))]
[e:expr (ret #'e)] [e:expr (ret #'e)]
[_ (values 0 0)]))) [_ (values 0 0)])))
(list covered total)) (list covered total))

View File

@ -1,7 +1,7 @@
#lang racket/base #lang racket/base
(require racket/pretty) (require racket/pretty "../cover.rkt")
(provide generate-raw-coverage) (provide generate-raw-coverage)
(define (generate-raw-coverage coverage [dir "coverage"]) (define (generate-raw-coverage coverage files [dir "coverage"])
(with-output-to-file (build-path dir "coverage.rktl") (with-output-to-file (build-path dir "coverage.rktl")
#:exists 'replace #:exists 'replace
(lambda () (pretty-write coverage)))) (lambda () (pretty-write (coverage-wrapper-map coverage)))))

View File

@ -74,11 +74,12 @@
(hash-ref (get-formats) output-format (hash-ref (get-formats) output-format
(lambda _ (error 'cover "given unknown coverage output format: ~s" output-format)))) (lambda _ (error 'cover "given unknown coverage output format: ~s" output-format))))
(printf "generating test coverage for ~s\n" files) (printf "generating test coverage for ~s\n" files)
(define passed (keyword-apply test-files! '(#:submod) (list submod) files)) (define passed (apply test-files! #:submod submod files))
(define coverage (remove-excluded-paths (get-test-coverage) exclude-paths)) (define coverage (get-test-coverage))
(define cleaned-files (remove-excluded-paths files exclude-paths))
(printf "dumping coverage info into ~s\n" coverage-dir) (printf "dumping coverage info into ~s\n" coverage-dir)
(parameterize ([irrelevant-submodules irrel-submods]) (parameterize ([irrelevant-submodules irrel-submods])
(generate-coverage coverage coverage-dir)) (generate-coverage coverage cleaned-files coverage-dir))
(unless passed (unless passed
(printf "some tests failed\n"))) (printf "some tests failed\n")))
@ -204,22 +205,23 @@
'("/Users/florence/playground/cover/tests/error-file.rkt"))) '("/Users/florence/playground/cover/tests/error-file.rkt")))
(check-false (should-omit? "/Test/t.rkt" '("/OtherDir")))) (check-false (should-omit? "/Test/t.rkt" '("/OtherDir"))))
;; Coverage -> Coverage ;; (listof (U path (list Path Vector)) (listof path) -> (listof path-string)
(define (remove-excluded-paths cover paths) (define (remove-excluded-paths files paths)
(for/hash ([(k v) (in-hash cover)] (define (->path p) (if (path-string? p) p (first p)))
(for/list ([k (in-list (map ->path files))]
#:unless (and (is-excluded-path? k paths) #:unless (and (is-excluded-path? k paths)
(vprintf "excluding path ~s from output\n" k))) (vprintf "excluding path ~s from output\n" k)))
(vprintf "including path ~s in output\n" k) (vprintf "including path ~s in output\n" k)
(values k v))) k))
(module+ test (module+ test
(parameterize ([current-directory (build-path "/tests")]) (parameterize ([current-directory (build-path "/tests")])
(check-equal? (remove-excluded-paths (check-equal? (remove-excluded-paths
(hash "/tests/tests/x.rkt" null (list (list "/tests/tests/x.rkt" #())
"/tests/x/tests/x/x.rkt" null "/tests/x/tests/x/x.rkt"
"/tests/x.rkt" null) "/tests/x.rkt")
'("tests")) '("tests"))
(hash "/tests/x.rkt" null)))) (list "/tests/x.rkt"))))
;; PathString [ListOf PathString]-> any/c ;; PathString [ListOf PathString]-> any/c

View File

@ -11,20 +11,19 @@ functions of test coverage.
@section[#:tag "higher"]{A High Level API} @section[#:tag "higher"]{A High Level API}
@deftogether[(@defthing[coverage/c @defthing[coverage/c
contract? contract?
#:value (hash/c any/c file-coverage/c)] #:value (-> any/c exact-positive-integer?
@defthing[file-coverage/c contract? #:value (listof (list/c boolean? srcloc?))])]{ (or/c 'covered 'uncovered 'irrelevant))]{
Coverage information is represented as a mapping from a file's key and a character
location to whether or not that location in the file was covered.
Coverage information is a hash map mapping absolute file paths to a list detailing the coverage of The files key is determined by what @racket[syntax-source] is on the syntax of the program after
that file. The file is keyed on the @racket[syntax-source] of the syntax objects from that reading it. Typically this is the @racket[string?] for of the absolute path of the file path. If
file. Usually this will be the absolute path to the file. The file coverage information is a list of coverage was run manually, as in the @tech{Lower Lever API}, this value may be something
lists, mapping a boolean to a range of characters within the file. True means the @racket[srcloc] else.
structure represents an expression that was run, and False means the structure represents an
expression that was not run. Some expressions may not be represented directly in this coverage The character locations are @racket[1] indexed.
information. For example, type annotations in @racketmodname[typed/racket] removed during macro
expansion and are thus neither run or not run. Note that the @racket[srcloc]s are one indexed,
meaning a @racket[1] represents the first character in the file.}
@defproc[(test-files! (#:submod submod symbol? 'test) @defproc[(test-files! (#:submod submod symbol? 'test)
(files (files
@ -41,37 +40,32 @@ not wrapped by a @racket[chaperone?] or @racket[impersonator?], nor may its elem
coverage information is still collected when test fail. Test coverage info is added to existing coverage information is still collected when test fail. Test coverage info is added to existing
coverage info.} coverage info.}
@defproc[(clear-coverage! [environment environment? (current-coverage-environment)]) any]{ @defproc[(clear-coverage! [environment environment? (current-cover-environment)]) any]{
Clears all coverage information.} Clears all coverage information.}
@defproc[(get-test-coverage [environment environment? (current-coverage-environment)]) coverage/c]{ @defproc[(get-test-coverage [environment environment? (current-cover-environment)]) coverage/c]{
Gets the current coverage information.} Gets the current coverage information.}
@defproc[(make-covered? (coverage file-coverage/c) (path path-string?))
(->* (exact-positive-integer?)
(#:byte? boolean?)
(or/c 'covered 'uncovered 'irrelevant))
]{
Given some location in a file and the
coverage information for that file @racket[make-covered?] returns
a functions that determines if some @racket[1] indexed character or byte location
in that file is covered. By default it checks character locations.
There are three possible results: @itemize[@item{@racket['irrelevant] --- The location is not There are three possible results for coverage: @itemize[@item{@racket['irrelevant] --- The location
considered relevant to coverage information. It is either not in the coverage information; is in a is not considered relevant to coverage information. It is either not in the coverage information;
submodule specified by @racket[irrelevant-submodules]; is a @racket[begin-for-syntax] form; or lexes is in a submodule specified by @racket[irrelevant-submodules]; is a @racket[begin-for-syntax] form;
(in the sense of that languages, @racket[_color-lexer]) as a comment or whitespace.} or lexes (in the sense of that language's @racket[_color-lexer]) as a comment or whitespace.}
@item{@racket['covered] --- The location is not @racket['irrelevant] and is covered} @item{@racket['covered] --- The location is not @racket['irrelevant] and is covered}
@item{@racket['uncovered] --- The location is not @racket['uncovered] and is not covered}] } @item{@racket['uncovered] --- The location is not @racket['uncovered] and is not covered}] }
@defthing[irrelevant-submodules (parameter/c (or/c #f (listof symbol?)))]{ @defthing[irrelevant-submodules (parameter/c (or/c #f (listof symbol?)))]{
A parameter that controls with submodules are considered irrelevant by @racket[make-covered?]. It A parameter that controls with submodules are considered irrelevant by @racket[get-test-coverage]. It
defaults to @racket[#f], which tells @racket[make-covered?] to consider all submodules defaults to @racket[#f], which tells @racket[make-covered?] to consider all submodules
irrelevant. If its value is a list, then each element of that list is the name of a submodule to be irrelevant. If its value is a list, then each element of that list is the name of a submodule to be
considered irrelevant.} considered irrelevant.}
@deftogether[(@defproc[(generate-coveralls-coverage (c coverage/c) (p path-string? "coverage")) any] @deftogether[(@defproc[(generate-coveralls-coverage (c coverage/c) (files (listof path-string?))
@defproc[(generate-html-coverage (c coverage/c) (p path-string? "coverage")) any])]{ (p path-string? "coverage"))
any]
@defproc[(generate-html-coverage (c coverage/c) (files (listof path-string?))
(p path-string? "coverage"))
any])]{
Generates coverage information in the coveralls and html formats. Equivalent to the specifications Generates coverage information in the coveralls and html formats. Equivalent to the specifications
of the @Flag{c} argument to @exec{raco cover}. Both use @racket[make-covered?] to determine file of the @Flag{c} argument to @exec{raco cover}. Both use @racket[make-covered?] to determine file
@ -81,18 +75,19 @@ coverage.}
The high level API may not be enough for some applications. For example an IDE may need separate The high level API may not be enough for some applications. For example an IDE may need separate
instances of the coverage table, or may need direct access to the namespace code is run in. For this instances of the coverage table, or may need direct access to the namespace code is run in. For this
purpose @racket[cover] directly expose coverage environments. purpose @racket[cover] directly expose coverage environments in its @deftech{Lower Lever API}.
Coverage environments are values that package together a coverage namespace, a compiler for Coverage environments are values that package together a coverage namespace, a compiler for
annotating code, and a coverage table to write coverage results to. All other coverage functions use annotating code, and a coverage table to write coverage results to. All other coverage functions use
the @racket[current-coverage-environment] for code coverage, unless explicitly given a different the @racket[current-cover-environment] for code coverage, unless explicitly given a different
environment. environment.
@defproc[(environment? [v any/c]) any/c]{ @defproc[(environment? [v any/c]) any/c]{
Tests if the given value is a coverage environment.} Tests if the given value is a coverage environment.}
@defthing[current-coverage-environment (parameter/c environment?)]{ @defparam[current-cover-environment environment environment?
#:value (make-cover-environment)]{
The current coverage environment. Defaults to an environment built from The current coverage environment. Defaults to an environment built from
@racket[make-base-namespace]} @racket[make-empty-namespace].}
@defproc[(environment-namespace [environment environment?]) namespace?]{ @defproc[(environment-namespace [environment environment?]) namespace?]{
Get the namespace that coverage should be run in. This is the same namespace given to Get the namespace that coverage should be run in. This is the same namespace given to
@racket[make-cover-environment]} @racket[make-cover-environment]}
@ -102,7 +97,9 @@ Get the namespace that coverage should be run in. This is the same namespace giv
Returns a value suitable for @racket[current-compile] that will compile code with coverage Returns a value suitable for @racket[current-compile] that will compile code with coverage
annotations. That code must be run in @racket[environment]'s namespace.} annotations. That code must be run in @racket[environment]'s namespace.}
@defproc[(make-cover-environment [namespace namespace? (make-base-namespace)]) environment?]{ @defproc[(make-cover-environment [namespace namespace? (make-empty-namespace)]) environment?]{
Makes a coverage environment such that @racket[environment-namespace] will return Makes a coverage environment such that @racket[environment-namespace] will return
@racket[namespace], and @racket[namespace] will be set up to handle coverage information.} @racket[namespace], and @racket[namespace] will be set up to handle coverage information. If
@racket[namespace] has been used in a different environment the two environments will share coverage
information.}

View File

@ -6,5 +6,5 @@
(thunk (thunk
(define r (path->string reader.rkt)) (define r (path->string reader.rkt))
(test-files! r) (test-files! r)
(define c (make-covered? (hash-ref (get-test-coverage) r) r)) (define c (curry (get-test-coverage) r))
(c 10)))) (c 10))))

View File

@ -7,8 +7,7 @@
(test-files! syntax.rkt) (test-files! syntax.rkt)
(define x (get-test-coverage)) (define x (get-test-coverage))
(define c? (define c?
(make-covered? (hash-ref x (path->string syntax.rkt)) (curry x (path->string syntax.rkt)))
(path->string syntax.rkt)))
(for ([i (in-naturals 1)] (for ([i (in-naturals 1)]
[_ (in-string (file->string syntax.rkt))]) [_ (in-string (file->string syntax.rkt))])
(check-not-eq? (c? i) 'uncovered (~a i))) (check-not-eq? (c? i) 'uncovered (~a i)))

View File

@ -1,21 +1,21 @@
#lang racket #lang racket
(require "../main.rkt" rackunit racket/runtime-path) (require "../main.rkt" (only-in "../cover.rkt" coverage-wrapper-map) rackunit racket/runtime-path)
(define-runtime-path error "error-file.rkt") (define-runtime-path error "error-file.rkt")
(define-runtime-path main "main.rkt") (define-runtime-path main "main.rkt")
(test-begin (test-begin
(after (after
(define (do-test files) (define (do-test files)
(define o (open-output-string)) (parameterize ([current-cover-environment (make-cover-environment)])
(parameterize ([current-error-port o]) (define o (open-output-string))
(apply test-files! files)) (parameterize ([current-error-port o])
(define s (get-output-string o)) (apply test-files! files))
(define c (get-test-coverage)) (define s (get-output-string o))
(define covered (hash-keys c)) (define c (get-test-coverage))
(for-each (define covered (hash-keys (coverage-wrapper-map c)))
(lambda (x) (check-not-false (member x covered) s)) (for-each
files) (lambda (x) (check-not-false (member x covered) s))
(clear-coverage!)) files)))
(define files (map path->string (list error main))) (define files (map path->string (list error main)))
(do-test files) (do-test files)
(do-test (reverse files)) (do-test (reverse files))

View File

@ -3,8 +3,8 @@
;; for every .rkt file in those directories it loads ;; for every .rkt file in those directories it loads
;; tests that file and checks its coverage against an ;; tests that file and checks its coverage against an
;; .rktl file of the same name ;; .rktl file of the same name
(require (only-in "../main.rkt" test-files! clear-coverage! get-test-coverage irrelevant-submodules (require (only-in cover test-files! clear-coverage! get-test-coverage irrelevant-submodules)
make-covered?) (only-in "../cover.rkt" coverage-wrapper-map)
"../private/file-utils.rkt" "../private/file-utils.rkt"
racket/runtime-path rackunit) racket/runtime-path rackunit)
@ -24,11 +24,10 @@
(define coverage (get-test-coverage)) (define coverage (get-test-coverage))
(for ([(program cover) covered]) (for ([(program cover) covered])
(define actual-coverage (hash-ref coverage program))
(define-values (expected-coverage expected-uncoverage) (define-values (expected-coverage expected-uncoverage)
(with-input-from-file cover (lambda () (values (ranges->numbers (read)) (with-input-from-file cover (lambda () (values (ranges->numbers (read))
(ranges->numbers (read)))))) (ranges->numbers (read))))))
(define covered? (make-covered? actual-coverage program)) (define covered? (curry coverage program))
(define (test-range range type) (define (test-range range type)
(for ([i range]) (for ([i range])
(define v (covered? i)) (define v (covered? i))
@ -66,8 +65,8 @@
(test-begin (test-begin
(after (after
(test-files! (->absolute prog.rkt)) (test-files! (->absolute prog.rkt))
(define abs (get-test-coverage)) (define abs (coverage-wrapper-map (get-test-coverage)))
(test-files! (build-path (->relative prog.rkt))) (test-files! (build-path (->relative prog.rkt)))
(define rel (get-test-coverage)) (define rel (coverage-wrapper-map (get-test-coverage)))
(check-equal? abs rel) (check-equal? abs rel)
(clear-coverage!)))) (clear-coverage!))))

View File

@ -1,6 +1,6 @@
#lang racket #lang racket
(require cover rackunit racket/runtime-path) (require cover rackunit racket/runtime-path (only-in "../cover.rkt" coverage-wrapper-map))
(define-runtime-path file "cross-phase-persist.rkt") (define-runtime-path file "cross-phase-persist.rkt")
(parameterize ([current-cover-environment (make-cover-environment)]) (parameterize ([current-cover-environment (make-cover-environment)])
(test-files! file) (test-files! file)
(check-equal? (get-test-coverage) (hash))) (check-equal? (coverage-wrapper-map (get-test-coverage)) (hash)))