Skip to content

Commit

Permalink
feat: move rackunit/log to raco/testing
Browse files Browse the repository at this point in the history
Previously, `rackunit/log` is used by both `raco test` and various
testing framework to log testing results. However, this organization
doesn't make much sense, as it gives privileges to rackunit over
other testing frameworks (technically `rackunit/log` lives in the
`testing-util-lib` package, so it's not a rackunit package proper,
but it's still very close)

This commit moves `rackunit/log` to `raco/testing`, which makes
`raco test` "neutral". This also eliminates the dependency on
`testing-util-lib`/`rackunit-lib`, for both `raco test` itself,
and also other testing frameworks.

Fixes #4965
  • Loading branch information
sorawee authored and mflatt committed Apr 23, 2024
1 parent 50f8446 commit 6d396d6
Show file tree
Hide file tree
Showing 5 changed files with 156 additions and 15 deletions.
20 changes: 10 additions & 10 deletions pkgs/compiler-lib/compiler/commands/test.rkt
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
compiler/find-exe
raco/command-name
racket/system
rackunit/log
raco/testing
pkg/lib
pkg/path
setup/collects
Expand Down Expand Up @@ -65,7 +65,7 @@

;; Stub for running a test in a process:
(module process racket/base
(require rackunit/log
(require raco/testing
racket/file
compiler/private/cm-minimal)
;; Arguments are a temp file to hold test results, the module
Expand Down Expand Up @@ -98,13 +98,13 @@
result-file
#:exists 'truncate
(lambda (o)
(write (test-log #:display? #f #:exit? #f) o)))
(write (test-report #:display? #f #:exit? #f) o)))
(exit 0))

;; Driver for running a test in a place:
(module place racket/base
(require racket/place
rackunit/log
raco/testing
compiler/private/cm-minimal)
(provide go)
(define (go pch)
Expand All @@ -124,7 +124,7 @@
((executable-yield-handler) 0))
;; If the tests use `rackunit`, collect result stats:
(define test-results
(test-log #:display? #f #:exit? #f))
(test-report #:display? #f #:exit? #f))

;; Return test results. If we don't get this far, the result
;; code of the place determines whether it the test counts as
Expand Down Expand Up @@ -190,7 +190,7 @@
(define-values (result-code test-results)
(case mode
[(direct)
(define pre (test-log #:display? #f #:exit? #f))
(define pre (test-report #:display? #f #:exit? #f))
(define done? #f)
(define t
(parameterize ([current-output-port stdout]
Expand All @@ -208,7 +208,7 @@
(error test-exe-name "timeout after ~a seconds" timeout))
(unless done?
(error test-exe-name "test raised an exception"))
(define post (test-log #:display? #f #:exit? #f))
(define post (test-report #:display? #f #:exit? #f))
(values 0
(cons (- (car post) (car pre))
(- (cdr post) (cdr pre))))]
Expand Down Expand Up @@ -1199,15 +1199,15 @@
(display-summary sum))
(unless (or (eq? default-mode 'direct)
(and (not default-mode) single-file?))
;; Re-log failures and successes, and then report using `test-log`.
;; (This is awkward; is it better to not try to use `test-log`?)
;; Re-log failures and successes, and then report using `test-report`.
;; (This is awkward; is it better to not try to use `test-report`?)
(for ([s (in-list sum)])
(for ([i (in-range (summary-failed s))])
(test-log! #f))
(for ([i (in-range (- (summary-total s)
(summary-failed s)))])
(test-log! #t))))
(test-log #:display? #t #:exit? #f)
(test-report #:display? #t #:exit? #f)
(define sum1 (call-with-summary #f (lambda () sum)))
(exit (cond
[(positive? (summary-timeout sum1)) 2]
Expand Down
3 changes: 1 addition & 2 deletions pkgs/compiler-lib/info.rkt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

(define deps '(["base" #:version "8.1.0.2"]
"scheme-lib"
"rackunit-lib"
["zo-lib" #:version "1.3"]))

(define implies '("zo-lib"))
Expand All @@ -13,7 +12,7 @@

(define pkg-authors '(mflatt))

(define version "1.12")
(define version "1.13")

(define license
'(Apache-2.0 OR MIT))
39 changes: 39 additions & 0 deletions pkgs/compiler-lib/raco/testing.rkt
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
;; This code originally appeared in rackunit/log

#lang racket/base

(provide test-log-enabled?
test-log!
test-report)

(define test-log-enabled?
(make-parameter #t (lambda (v) (and v #t))))

(define TOTAL 0)
(define FAILED 0)

(define-syntax-rule (inc! id)
(set! id (add1 id)))

(define (test-log! result)
(when (test-log-enabled?)
(inc! TOTAL)
(unless result
(inc! FAILED))))

(define (test-report #:display? [display? #f]
#:exit? [exit? #f])
(when display?
(unless (zero? TOTAL)
(cond
[(zero? FAILED)
(printf "~a test~a passed\n"
TOTAL
(if (= TOTAL 1) "" "s"))]
[else
(eprintf "~a/~a test failures\n"
FAILED TOTAL)])))
(when exit?
(unless (zero? FAILED)
(exit 1)))
(cons FAILED TOTAL))
35 changes: 32 additions & 3 deletions pkgs/racket-doc/scribblings/raco/test.scrbl
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@
"common.rkt"
(for-label racket/runtime-path
racket/base
racket/contract
launcher/launcher
rackunit/log
raco/testing
compiler/module-suffix
compiler/cm))

Expand Down Expand Up @@ -192,7 +193,7 @@ The @exec{raco test} command accepts several flags:
@item{@DFlag{table} or @Flag{t}
--- Print a summary table after all tests. If a test uses
@racketmodname[rackunit], or if a test at least uses
@racket[test-log!] from @racketmodname[rackunit/log] to log
@racket[test-log!] from @racketmodname[raco/testing] to log
successes and failures, the table reports test and failure
counts based on the log.}

Expand Down Expand Up @@ -378,4 +379,32 @@ the test's output is prefixed with a

line.


@section{Logging Test Results}
@defmodule[raco/testing]

This module provides a general purpose library for tracking test results
and displaying a summary message. The command @exec{raco test} uses this library
to display test results. Therefore, any testing framework that wants to integrate
with @exec{raco test} should also use this library to log test results.

@defproc[(test-log! [result any/c]) void?]{
Adds a test result to the running log. If @racket[result] is false,
then the test is considered a failure.}

@defproc[(test-report [#:display? display? any/c #f]
[#:exit? exit? any/c #f])
(cons/c exact-nonnegative-integer?
exact-nonnegative-integer?)]{
Processes the running test log. The first integer is the failed tests, the
second is the total tests. If @racket[display?] is true, then a message is
displayed. If there were failures, the message is printed on
@racket[(current-error-port)]. If @racket[exit?] is true, then if there were
failures, calls @racket[(exit 1)].}

@defboolparam[test-log-enabled? enabled? #:value #t]{
When set to @racket[#f], @racket[test-log!] is a no-op. This is useful to
dynamically disable certain tests whose failures are expected and shouldn't be
counted in the test log, such as when testing a custom check's failure
behavior.}

@history[#:added "1.13"]
74 changes: 74 additions & 0 deletions pkgs/racket-test/tests/racket/testing.rkt
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
#lang racket/base
(require rackunit
raco/testing)

(module test racket/base
(require syntax/location)
;; Use a separate namespace to avoid logging results
;; in this namespace (where `raco test` would see errors).
(parameterize ([current-namespace (make-base-namespace)])
(dynamic-require (quote-module-path "..") #f)))

(define-syntax-rule (&& label stdout-e stdout-p)
(let ()
(define stdout-ev stdout-e)
(define stdout-av stdout-p)
(unless (equal? stdout-ev stdout-av)
(error 'log "bad ~a\n actual: ~v\n expected: ~v"
label stdout-av stdout-ev))))

;; TODO(jackfirth): This testing system is pretty weird, and it's only necessary
;; because the test log is a pair of global variables. That makes it hard to
;; keep these tests from affecting the test log used by raco test. If the test
;; log were a parameter, then these tests could simply create a new log and
;; parameterize the current log to it. We should probably do that instead.
(define-syntax-rule (& test-e stdout-e stderr-e exit-e)
(let ()
(define stdout-p (open-output-string))
(define stderr-p (open-output-string))
(define exit-av 0)
(parameterize ([current-output-port stdout-p]
[current-error-port stderr-p]
[exit-handler (λ (ec) (set! exit-av ec))])
test-e)
(&& 'stdout stdout-e (get-output-string stdout-p))
(&& 'stderr stderr-e (get-output-string stderr-p))
(&& 'exit-code exit-e exit-av)))

(& (test-report) "" "" 0)
(& (test-report #:display? #t) "" "" 0)
(& (test-report #:exit? #t) "" "" 0)
(& (test-report #:display? #t #:exit? #t) "" "" 0)

(check-true #t)

(& (test-report) "" "" 0)
(& (test-report #:display? #t) "1 test passed\n" "" 0)
(& (test-report #:exit? #t) "" "" 0)
(& (test-report #:display? #t #:exit? #t) "1 test passed\n" "" 0)

(parameterize ([current-error-port (open-output-string)])
(check-true #f))

(& (test-report) "" "" 0)
(& (test-report #:display? #t) "" "1/2 test failures\n" 0)
(& (test-report #:exit? #t) "" "" 1)
(& (test-report #:display? #t #:exit? #t) "" "1/2 test failures\n" 1)

(parameterize ([test-log-enabled? #f])
(check-true #t)
(& (test-report) "" "" 0)
(& (test-report #:display? #t) "" "1/2 test failures\n" 0)
(& (test-report #:exit? #t) "" "" 1)
(& (test-report #:display? #t #:exit? #t) "" "1/2 test failures\n" 1))

(test-begin
(check-true #t))

(& (test-report #:display? #t) "" "1/3 test failures\n" 0)

(parameterize ([current-error-port (open-output-string)])
(test-begin
(check-true #f)))

(& (test-report #:display? #t) "" "2/4 test failures\n" 0)

0 comments on commit 6d396d6

Please sign in to comment.