raco test: responsible-party and varying-party logging, lock names

The responsible party for a test defaults to the enclosing package's
author.

Also, add support for a `test-timeouts` fallback in "info.rkt".
This commit is contained in:
Matthew Flatt 2013-12-31 14:52:07 -07:00
parent 796f82e4ad
commit 59cbefe47a
2 changed files with 403 additions and 179 deletions

View File

@ -14,6 +14,7 @@
racket/system racket/system
rackunit/log rackunit/log
pkg/lib pkg/lib
pkg/path
setup/collects setup/collects
setup/getinfo) setup/getinfo)
@ -37,6 +38,13 @@
(define single-file? #t) (define single-file? #t)
(define lock-file-dir (or (getenv "PLTLOCKDIR")
(find-system-path 'temp-dir)))
(define max-lock-delay (or (let ([n (string->number (or (getenv "PLTLOCKTIME") ""))])
(and (real? n)
n))
(* 4 60 60))) ; default: wait at most 4 hours
;; Stub for running a test in a process: ;; Stub for running a test in a process:
(module process racket/base (module process racket/base
(require rackunit/log) (require rackunit/log)
@ -88,11 +96,15 @@
;; Run each test in its own place or process, and collect both test ;; Run each test in its own place or process, and collect both test
;; results and whether any output went to stderr. ;; results and whether any output went to stderr.
(define (dynamic-require-elsewhere p d args (define (dynamic-require-elsewhere p d args
#:id id
#:mode [mode (or default-mode #:mode [mode (or default-mode
(if single-file? (if single-file?
'direct 'direct
'process))] 'process))]
#:timeout [timeout default-timeout]) #:timeout timeout
#:responsible responsible
#:lock-name lock-name
#:random? random?)
(define c (make-custodian)) (define c (make-custodian))
(define timeout? #f) (define timeout? #f)
(with-handlers ([exn:fail? (lambda (exn) (with-handlers ([exn:fail? (lambda (exn)
@ -102,111 +114,131 @@
(extract-file-name p) (extract-file-name p)
(exn-message exn))) (exn-message exn)))
(summary 1 1 (current-label) #f (if timeout? 1 0)))]) (summary 1 1 (current-label) #f (if timeout? 1 0)))])
(define e (open-output-bytes)) (define (go)
(define e (open-output-bytes))
(define stdout (if quiet-program? (define stdout (if quiet-program?
(open-output-nowhere) (open-output-nowhere)
(current-output-port))) (current-output-port)))
(define stderr (if quiet-program? (define stderr (if quiet-program?
e e
(if check-stderr? (if check-stderr?
(tee-output-port (current-error-port) e) (tee-output-port (current-error-port) e)
(current-error-port)))) (current-error-port))))
(define-values (result-code test-results) (unless quiet?
(case mode (when responsible
[(direct) (fprintf stdout "raco test:~a @(test-responsible '~s)\n"
(define pre (test-log #:display? #f #:exit? #f)) id
(define done? #f) responsible))
(define t (when random?
(parameterize ([current-output-port stdout] (fprintf stdout "raco test:~a @(test-random #t)\n"
[current-error-port stderr] id)))
[current-command-line-arguments (list->vector args)])
(thread (define-values (result-code test-results)
(lambda () (case mode
(dynamic-require p d) [(direct)
((executable-yield-handler) 0) (define pre (test-log #:display? #f #:exit? #f))
(set! done? #t))))) (define done? #f)
(unless (thread? (sync/timeout timeout t)) (define t
(set! timeout? #t) (parameterize ([current-output-port stdout]
(error 'test "timeout after ~a seconds" timeout)) [current-error-port stderr]
(unless done? [current-command-line-arguments (list->vector args)])
(error 'test "test raised an exception")) (thread
(define post (test-log #:display? #f #:exit? #f)) (lambda ()
(values 0 (dynamic-require p d)
(cons (- (car post) (car pre)) ((executable-yield-handler) 0)
(- (cdr post) (cdr pre))))] (set! done? #t)))))
[(place) (unless (thread? (sync/timeout timeout t))
;; Start the test place: (set! timeout? #t)
(define-values (pl in out/f err/f) (error 'test "timeout after ~a seconds" timeout))
(parameterize ([current-custodian c]) (unless done?
(dynamic-place* '(submod compiler/commands/test place) (error 'test "test raised an exception"))
'go (define post (test-log #:display? #f #:exit? #f))
#:in (current-input-port) (values 0
#:out stdout (cons (- (car post) (car pre))
#:err stderr))) (- (cdr post) (cdr pre))))]
[(place)
;; Send the module path to test: ;; Start the test place:
(place-channel-put pl (list p d (current-directory) args)) (define-values (pl in out/f err/f)
(parameterize ([current-custodian c])
(dynamic-place* '(submod compiler/commands/test place)
'go
#:in (current-input-port)
#:out stdout
#:err stderr)))
;; Send the module path to test:
(place-channel-put pl (list p d (current-directory) args))
;; Wait for the place to finish: ;; Wait for the place to finish:
(unless (sync/timeout timeout (place-dead-evt pl)) (unless (sync/timeout timeout (place-dead-evt pl))
(set! timeout? #t) (set! timeout? #t)
(error 'test "timeout after ~a seconds" timeout)) (error 'test "timeout after ~a seconds" timeout))
;; Get result code and test results: ;; Get result code and test results:
(values (place-wait pl) (values (place-wait pl)
(sync/timeout 0 pl))] (sync/timeout 0 pl))]
[(process) [(process)
(define tmp-file (make-temporary-file)) (define tmp-file (make-temporary-file))
(define ps (define ps
(parameterize ([current-output-port stdout] (parameterize ([current-output-port stdout]
[current-error-port stderr] [current-error-port stderr]
[current-subprocess-custodian-mode 'kill] [current-subprocess-custodian-mode 'kill]
[current-custodian c]) [current-custodian c])
(apply process*/ports (apply process*/ports
stdout stdout
(current-input-port) (current-input-port)
stderr stderr
(find-exe) (find-exe)
"-l" "-l"
"racket/base" "racket/base"
"-e" "-e"
"(dynamic-require '(submod compiler/commands/test process) #f)" "(dynamic-require '(submod compiler/commands/test process) #f)"
tmp-file tmp-file
(format "~s" (normalize-module-path p)) (format "~s" (normalize-module-path p))
(format "~s" d) (format "~s" d)
args))) args)))
(define proc (list-ref ps 4)) (define proc (list-ref ps 4))
(unless (sync/timeout timeout (thread (lambda () (proc 'wait)))) (unless (sync/timeout timeout (thread (lambda () (proc 'wait))))
(set! timeout? #t) (set! timeout? #t)
(error 'test "timeout after ~a seconds" timeout)) (error 'test "timeout after ~a seconds" timeout))
(define results (define results
(with-handlers ([exn:fail:read? (lambda () #f)]) (with-handlers ([exn:fail:read? (lambda () #f)])
(call-with-input-file* tmp-file read))) (call-with-input-file* tmp-file read)))
(values (proc 'exit-code) (values (proc 'exit-code)
(and (pair? results) (and (pair? results)
(exact-positive-integer? (car results)) (exact-positive-integer? (car results))
(exact-positive-integer? (cdr results)) (exact-positive-integer? (cdr results))
results))])) results))]))
;; Shut down the place/process (usually a no-op unless it timed out): ;; Shut down the place/process (usually a no-op unless it timed out):
(custodian-shutdown-all c) (custodian-shutdown-all c)
;; Check results: ;; Check results:
(when check-stderr? (when check-stderr?
(unless (equal? #"" (get-output-bytes e)) (unless (equal? #"" (get-output-bytes e))
(error 'test "non-empty stderr: ~e" (get-output-bytes e)))) (error 'test "non-empty stderr: ~e" (get-output-bytes e))))
(unless (zero? result-code) (unless (zero? result-code)
(error 'test "non-zero exit: ~e" result-code)) (error 'test "non-zero exit: ~e" result-code))
(cond (cond
[test-results [test-results
(summary (car test-results) (cdr test-results) (current-label) #f 0)] (summary (car test-results) (cdr test-results) (current-label) #f 0)]
[else [else
(summary 0 1 (current-label) #f 0)]))) (summary 0 1 (current-label) #f 0)]))
;; Serialize the above with a lock, if any:
(if lock-name
(call-with-file-lock/timeout
#:max-delay max-lock-delay
(build-path lock-file-dir lock-name)
'exclusive
go
(lambda () (error 'test "could not obtain lock: ~s" lock-name)))
(go))))
;; For recording stderr while also propagating to the original stderr: ;; For recording stderr while also propagating to the original stderr:
(define (tee-output-port p1 p2) (define (tee-output-port p1 p2)
@ -242,7 +274,14 @@
(append mod '(config)) (append mod '(config))
(error 'test "cannot add test-config submodule to path: ~s" mod))) (error 'test "cannot add test-config submodule to path: ~s" mod)))
(define (dynamic-require* p d args try-config?) (define (dynamic-require* p d
#:id id
#:try-config? try-config?
#:args args
#:timeout timeout
#:responsible responsible
#:lock-name lock-name
#:random? random?)
(define lookup (define lookup
(or (cond (or (cond
[(not try-config?) #f] [(not try-config?) #f]
@ -252,10 +291,17 @@
(lambda (what get-default) (get-default)))) (lambda (what get-default) (get-default))))
(dynamic-require-elsewhere (dynamic-require-elsewhere
p d args p d args
#:id id
#:responsible (lookup 'responsible
(lambda () responsible))
#:timeout (if default-timeout #:timeout (if default-timeout
(lookup 'timeout (lookup 'timeout
(lambda () default-timeout)) (lambda () timeout))
+inf.0))) +inf.0)
#:lock-name (lookup 'lock-name
(lambda () lock-name))
#:random? (lookup 'random?
(lambda () random?))))
(define current-label (make-parameter "???")) (define current-label (make-parameter "???"))
(struct summary (failed total label body-res timeout)) (struct summary (failed total label body-res timeout))
@ -370,7 +416,14 @@
;; Perform test of one module (in parallel, as allowed by ;; Perform test of one module (in parallel, as allowed by
;; `task-sema`): ;; `task-sema`):
(define (test-module p mod args try-config? #:sema continue-sema) (define (test-module p mod
#:sema continue-sema
#:try-config? try-config?
#:args [args '()]
#:timeout [timeout +inf.0]
#:responsible [responsible #f]
#:lock-name [lock-name #f]
#:random? [random? #f])
(call-with-semaphore (call-with-semaphore
task-sema ; limits parallelism task-sema ; limits parallelism
(lambda () (lambda ()
@ -397,7 +450,16 @@
(flush-output)) (flush-output))
id))) id)))
(begin0 (begin0
(dynamic-require* mod 0 args try-config?) (dynamic-require* mod 0
#:id (if (jobs . <= . 1)
""
(format " ~a" id))
#:try-config? try-config?
#:args args
#:timeout timeout
#:responsible responsible
#:lock-name lock-name
#:random? random?)
(call-with-semaphore (call-with-semaphore
ids-lock ids-lock
(lambda () (lambda ()
@ -416,7 +478,9 @@
(cond (cond
[(directory-exists? p) [(directory-exists? p)
(set! single-file? #f) (set! single-file? #f)
(if (omit-path? (path->directory-path p)) (define dir-p (path->directory-path p))
(check-info dir-p)
(if (omit-path? dir-p)
(summary 0 0 #f null 0) (summary 0 0 #f null 0)
(with-summary (with-summary
`(directory ,p) `(directory ,p)
@ -431,13 +495,29 @@
(or (not check-suffix?) (or (not check-suffix?)
(regexp-match rx:default-suffixes p) (regexp-match rx:default-suffixes p)
(get-cmdline p #f)) (get-cmdline p #f))
(not (omit-path? p))) (begin (check-info p)
(define args (get-cmdline p)) (not (omit-path? p))))
;; The above `omit-path?` loads "info.rkt" files
(define norm-p (normalize-info-path p))
(define args (get-cmdline norm-p))
(define timeout (get-timeout norm-p))
(define lock-name (get-lock-name norm-p))
(define responsible (get-responsible norm-p))
(define random? (get-random norm-p))
(parameterize ([current-directory (let-values ([(base name dir?) (split-path p)]) (parameterize ([current-directory (let-values ([(base name dir?) (split-path p)])
(if (path? base) (if (path? base)
base base
(current-directory)))]) (current-directory)))])
(define file-name (file-name-from-path p)) (define file-name (file-name-from-path p))
(define (test-this-module mod try-config?)
(test-module p mod
#:try-config? try-config?
#:sema continue-sema
#:args args
#:timeout timeout
#:responsible responsible
#:lock-name lock-name
#:random? random?))
(with-summary (with-summary
`(file ,p) `(file ,p)
(let ([something-wasnt-declared? #f] (let ([something-wasnt-declared? #f]
@ -454,13 +534,13 @@
#f] #f]
[(module-declared? mod #t) [(module-declared? mod #t)
(set! did-one? #t) (set! did-one? #t)
(test-module p mod args #t #:sema continue-sema)] (test-this-module mod #t)]
[else [else
(set! something-wasnt-declared? #t) (set! something-wasnt-declared? #t)
#f])) #f]))
(list (list
(and (and run-anyways? something-wasnt-declared?) (and (and run-anyways? something-wasnt-declared?)
(test-module p file-name args #f #:sema continue-sema))))))))] (test-this-module file-name #f))))))))]
[(not (file-exists? p)) [(not (file-exists? p))
(error 'test "given path ~e does not exist" p)] (error 'test "given path ~e does not exist" p)]
[else (summary 0 0 #f null 0)])])) [else (summary 0 0 #f null 0)])]))
@ -559,11 +639,16 @@
(define omit-paths (make-hash)) (define omit-paths (make-hash))
(define command-line-arguments (make-hash)) (define command-line-arguments (make-hash))
(define timeouts (make-hash))
(define lock-names (make-hash))
(define responsibles (make-hash))
(define randoms (make-hash))
(define pkg-cache (make-hash))
(define collects-cache (make-hash)) (define collects-cache (make-hash))
(define info-done (make-hash)) (define info-done (make-hash))
(define (check-info p check-up?) (define (check-dir-info p)
(define-values (base name dir?) (split-path p)) (define-values (base name dir?) (split-path p))
(define dir (normalize-info-path (define dir (normalize-info-path
(if dir? (if dir?
@ -572,60 +657,91 @@
(path->complete-path base) (path->complete-path base)
(current-directory))))) (current-directory)))))
(when (and check-up? (not dir?))
;; Check enclosing collection
(define c (path->collects-relative p #:cache collects-cache))
(when (list? c)
(check-info/parents dir
(apply build-path (map bytes->path (reverse (cdr (reverse (cdr c)))))))))
(unless (hash-ref info-done dir #f) (unless (hash-ref info-done dir #f)
(hash-set! info-done dir #t) (hash-set! info-done dir #t)
(define info (get-info/full dir)) (define info (get-info/full dir))
(when info (when info
(define v (info 'test-omit-paths (lambda () '())))
(define (bad what v) (define (bad what v)
(log-error "bad `~a' in \"info.rkt\": ~e" what v)) (log-error "bad `~a' in \"info.rkt\": ~e" what v))
(cond
[(eq? v 'all)
(hash-set! omit-paths dir #t)]
[(list? v)
(for ([i (in-list v)])
(unless (path-string? i) (bad 'test-omit-paths v))
(define p (normalize-info-path (path->complete-path i dir)))
(define dp (if (directory-exists? p)
(path->directory-path p)
p))
(hash-set! omit-paths dp #t))]
[else (bad 'test-omit-paths v)])
(define a (info 'test-command-line-arguments (lambda () '()))) (define (get-members table what all-ok?)
(unless (list? a) (bad 'test-command-line-arguments a)) (define v (info what (lambda () '())))
(for ([arg (in-list a)]) (cond
(unless (and (list? arg) [(and all-ok? (eq? v 'all))
(= 2 (length arg)) (hash-set! table dir #t)]
(path-string? (car arg)) [(list? v)
(list? (cadr arg)) (for ([i (in-list v)])
(andmap path-string? (cadr arg))) (unless (path-string? i) (bad what v))
(bad 'test-command-line-arguments a)) (define p (normalize-info-path (path->complete-path i dir)))
(hash-set! command-line-arguments (define dp (if (directory-exists? p)
(normalize-info-path (path->complete-path (car arg) dir)) (path->directory-path p)
(cadr arg)))))) p))
(hash-set! table dp #t))]
[else (bad what v)]))
(get-members omit-paths 'test-omit-paths #t)
(get-members randoms 'test-randoms #t)
(define (get-keyed table what check? #:ok-all? [ok-all? #f])
(define a (info what (lambda () '())))
(if (list? a)
(for ([arg (in-list a)])
(unless (and (list? arg)
(= 2 (length arg))
(or (path-string? (car arg))
(and ok-all?
(eq? (car arg) 'all)))
(check? (cadr arg)))
(bad what a))
(hash-set! table
(normalize-info-path (if (eq? (car arg) 'all)
dir
(path->complete-path (car arg) dir)))
(cadr arg)))
(bad what a)))
(get-keyed command-line-arguments
'test-command-line-arguments
(lambda (v) (and (list? v)
(andmap path-string? v))))
(get-keyed timeouts
'test-timeouts
(lambda (v) (real? v)))
(get-keyed lock-names
'test-lock-names
(lambda (v) (or (not v)
(and (string? v)
(path-string? v)))))
(get-keyed responsibles
'test-responsibles
ok-responsible?
#:ok-all? #t)
(get-keyed randoms
'test-random
(lambda (v) (string? v))))))
(define (check-info/parents dir subpath) (define (check-info/parents dir subpath)
(let loop ([dir dir] [subpath subpath]) (let loop ([dir dir] [subpath subpath])
(unless (hash-ref info-done dir #f) (check-dir-info dir)
(check-info dir #f) (define-values (next-subpath subpath-name subpath-dir?) (split-path subpath))
(define-values (next-subpath subpath-name subpath-dir?) (split-path subpath)) (define-values (next-dir dir-name dir-dir?) (split-path dir))
(define-values (next-dir dir-name dir-dir?) (split-path dir)) (when (path? next-subpath)
(when (path? next-subpath) (loop next-dir next-subpath))))
(loop next-dir next-subpath)))))
(define (check-info p)
(check-dir-info p)
;; Check enclosing collection
(define-values (base name dir?) (split-path p))
(define c (if dir?
#f
(path->collects-relative p #:cache collects-cache)))
(when (list? c)
(check-info/parents base
(apply build-path (map bytes->path (reverse (cdr (reverse (cdr c)))))))))
(define (normalize-info-path p) (define (normalize-info-path p)
(simplify-path (path->complete-path p) #f)) (simplify-path (path->complete-path p) #f))
(define (omit-path? p) (define (omit-path? p)
(check-info p #t)
(let ([p (normalize-info-path p)]) (let ([p (normalize-info-path p)])
(or (hash-ref omit-paths p #f) (or (hash-ref omit-paths p #f)
(let-values ([(base name dir?) (split-path p)]) (let-values ([(base name dir?) (split-path p)])
@ -633,8 +749,40 @@
(omit-path? base)))))) (omit-path? base))))))
(define (get-cmdline p [default null]) (define (get-cmdline p [default null])
(let ([p (normalize-info-path p)]) (hash-ref command-line-arguments p default))
(hash-ref command-line-arguments p default)))
(define (get-timeout p) (hash-ref timeouts p +inf.0))
(define (get-lock-name p) (hash-ref lock-names p #f))
(define (get-responsible p)
(or (let loop ([p p])
(or (hash-ref responsibles p #f)
(let-values ([(base name dir?) (split-path p)])
(and (path? base)
(loop base)))))
;; Check package authors:
(let-values ([(pkg subpath) (path->pkg+subpath p #:cache pkg-cache)])
(and pkg
(let ([pkg-dir (if (path? subpath)
(apply build-path
(drop-right (explode-path p)
(length (explode-path subpath))))
pkg)])
(define info (get-info/full pkg-dir))
(and info
(let ([v (info 'pkg-authors (lambda () #f))])
(and (ok-responsible? v)
v))))))))
(define (get-random p) (hash-ref randoms p #f))
(define (ok-responsible? v)
(or (string? v)
(symbol? v)
(and (list? v)
(andmap (lambda (v) (or (symbol? v) (string? v)))
v))))
;; -------------------------------------------------- ;; --------------------------------------------------

View File

@ -3,17 +3,19 @@
scribble/bnf scribble/bnf
"common.rkt" "common.rkt"
(for-label racket/runtime-path (for-label racket/runtime-path
racket/base
launcher/launcher launcher/launcher
rackunit/log)) rackunit/log))
@title[#:tag "test"]{@exec{raco test}: Run tests} @title[#:tag "test"]{@exec{raco test}: Run tests}
The @exec{raco test} command requires and (by default) runs the The @exec{raco test} command requires and runs the (by default)
@racket[test] submodule (if any) associated with each path given on @racket[test] submodule associated with each path given on the command
the command line. By default, each test is run in a separate Racket line. Command-line flag can control which submodule is run, whether to
process. Command-line flag can control which submodule is run, whether run the main module if no submodule is found, and whether to run tests
to run the main module if no submodule is found, and whether to run directly, in separate processes (the default), or in separate places.
tests as processes or places. The current directory is set to a test file's directory before running
the file.
When an argument path refers to a directory, the tool recursively When an argument path refers to a directory, the tool recursively
discovers and runs all files within the directory that end in discovers and runs all files within the directory that end in
@ -24,8 +26,7 @@ discovers and runs all files within the directory that end in
A test is counted as failing if it logs a failing test code via A test is counted as failing if it logs a failing test code via
@racket[test-log!], causes Racket to exit with a non-zero exit code, or @racket[test-log!], causes Racket to exit with a non-zero exit code, or
(when @Flag{e} or @DFlag{check-stderr} is specified) if it produces (when @Flag{e} or @DFlag{check-stderr} is specified) if it produces
output on the error port. The current directory is set to a test output on the error port.
file's directory before running the file.
The @exec{raco test} command accepts several flags: The @exec{raco test} command accepts several flags:
@ -104,7 +105,8 @@ The @exec{raco test} command accepts several flags:
--- count any stderr output as a test failure.} --- count any stderr output as a test failure.}
@item{@Flag{q} or @DFlag{quiet} @item{@Flag{q} or @DFlag{quiet}
--- suppresses output of progress information.} --- suppresses output of progress information, responsible
parties, and varying output (see @secref["test-responsible"]).}
@item{@DFlag{table} or @Flag{t} @item{@DFlag{table} or @Flag{t}
--- Print a summary table after all tests. If a test uses --- Print a summary table after all tests. If a test uses
@ -115,6 +117,8 @@ The @exec{raco test} command accepts several flags:
] ]
@section[#:tag "test-config"]{Test Configuration by Submodule}
When @exec{raco test} runs a test in a submodule, a @racket[config] When @exec{raco test} runs a test in a submodule, a @racket[config]
sub-submodule can provide additional configuration for running the sub-submodule can provide additional configuration for running the
test. The @racket[config] sub-submodule should use the test. The @racket[config] sub-submodule should use the
@ -123,32 +127,104 @@ identifiers:
@itemlist[ @itemlist[
@item{@racket[timeout] --- override the default timeout for the test, @item{@racket[timeout] --- a real number to override the default
when timeouts are enabled.} timeout for the test, which applies only when timeouts are
enabled.}
@item{@racket[responsible] --- a string, symbol, or list of symbols
and strings identifying a responsible party that should be
notified when the test fails. See @secref["test-responsible"].}
@item{@racket[lock-name] --- a string that names a lock file that is
used to serialize tests (i.e., tests that have the same lock
name do not run concurrently). The lock file's location is
determined by the @envvar{PLTLOCKDIR} enviornment varible or
defaults to @racket[(find-system-path 'temp-dir)]. The maximum
time to wait on the lock file is determined by the
@envvar{PLTLOCKTIME} environment variable or defaults to 4
hours.}
@item{@racket[random?] --- if true, indicates that the test's output
is expected to vary. See @secref["test-responsible"].}
] ]
To prevent @exec{raco test} from running a particular file, normally
the file should contain a submodule that takes no action. In some @section[#:tag "test-config-info"]{Test Configuration by @filepath{info.rkt}}
cases, however, adding a submodule is inconvenient or impossible
(e.g., because the file will not always compile). Thus, @exec{raco Submodule-based test configuration is preferred (see
test} also consults any @filepath{info.rkt} file in the candidate test @secref["test-config"]). In particular, to prevent @exec{raco test}
file's directory; in the case of a file within a collection, from running a particular file, normally the file should contain a
@filepath{info.rkt} files from any enclosing collection directories submodule that takes no action.
are also consulted. The following @filepath{info.rkt} fields are
recognized: In some cases, however, adding a submodule is inconvenient or
impossible (e.g., because the file will not always compile). Thus,
@exec{raco test} also consults any @filepath{info.rkt} file in the
candidate test file's directory. In the case of a file within a
collection, @filepath{info.rkt} files from any enclosing collection
directories are also consulted for @racket[test-omit-paths]. Finally,
for a file within a package, the package's @filepath{info.rkt} is
consulted for @racket[pkg-authors] to set the default responsible
parties (see @secref["test-responsible"]) for all files in the
package.
The following @filepath{info.rkt} fields are recognized:
@itemlist[ @itemlist[
@item{@racket[test-omit-paths] --- a list of path strings (relative @item{@racket[test-omit-paths] --- a list of path strings (relative
to the enclosing directory) or @racket['all] to omit all files within to the enclosing directory) or @racket['all] to omit all files
the enclosing directory. When a path string refers to a directory, within the enclosing directory. When a path string refers to a
all files within the directory are omitted.} directory, all files within the directory are omitted.}
@item{@racket[test-command-line-arguments] --- a list of @item{@racket[test-command-line-arguments] --- a list of
@racket[(list _module-path-string (list _argument-path-string ...))], @racket[(list _module-path-string (list _argument-path-string
where @racket[current-command-line-arguments] is set to a vector that ...))], where @racket[current-command-line-arguments] is set to
contains the @racket[_argument-path-string] when running a vector that contains the @racket[_argument-path-string] when
@racket[_module-path-string].} running @racket[_module-path-string].}
@item{@racket[test-timeouts] --- a list of @racket[(list
_module-path-string _real-number)] to override the default
timeout for @racket[_module-path-string].}
@item{@racket[test-responsibles] --- a list of @racket[(list
_module-path-string _party)] or @racket[(list 'all _party)] to
override the default responsible party for
@racket[_module-path-string] or all files within the directory
(except as overridden), respectively. Each @racket[_party] is a
string, symbol, or list of symbols and strings. See
@secref["test-responsible"].}
@item{@racket[test-lock-names] --- a list of @racket[(list
_module-path-string _lock-string)] to declare a lock file name
for @racket[_module-path-string]. See @racket[lock-name] in
@secref["test-config"].}
@item{@racket[test-randoms] --- a list of path strings (relative to
the enclosing directory) for modules whose output varies.
See @secref["test-responsible"].}
] ]
@section[#:tag "test-responsible"]{Responsible-Party and Varying-Output Logging}
When a test has a declared responsible party, then the test's output
is prefixed with a
@verbatim[#:indent 2]{raco test:@nonterm{which} @"@"(test-responsible '@nonterm{responsible})}
line, where @nonterm{which} is a space followed by an exact
non-negative number indicating a parallel task when parallelism is
enabled (or empty otherwise), and @nonterm{responsible} is a string,
symbol, or list datum.
When a test's output (as written to stdout) is expected to vary across
runs---aside from varying output that has the same form as produced by
@racket[time]---then it should be declared as varying. In that case,
the test's output is prefixed with a
@verbatim[#:indent 2]{raco test:@nonterm{which} @"@"(test-random #t)}
line.