Open jackfirth opened 6 years ago
One possibility: create a syntax parameter, say test
, that in regular mode will disappear, but when a certain condition is met (e.g., some environment variable is set), will splice its body into the enclosing form. Then we can write something like this?
(test (require rackunit))
(define (make-adder num-1)
(define (result num-2)
(+ num-1 num-2))
(test
(check-equal (result 5) (+ num-1 5))
(check-equal (result 10) (+ num-1 10)))
result)
(test
(check-equal ((make-adder 3) 6) 9)
(check-equal ((make-adder 4) 2) 6))
The problem is, something will need to provide test
. Racket probably should not do that. But if rackunit
does that then the entire rackunit
will be needed at runtime too...
I just spoke with BenL (one of the major compiler writers) to clarify the semantics of nested tests in Pyret. A nested test is run every time the function is run (and apparently there is no way to turn those off). A top-level test is dealt with like something in module+-test. Library tests are thus ignored if code imports a library.
What this means philosophically and technically is that these tests are contracts. In the past I have suggested to think of contracts as generalizations of predicate tests and of the latter as generalizations of unit tests. Of course when A generalizes B, A can express B (functionally at least, if not non-functional properties such as "only when module+ test is required").
I conjecture that we could compile nested tests into Racket contracts locally, possibly with a macro, with a good syntax design, and that should be done.
;; - - -
It cannot work with test submodules for a number of reasons. The most important one is that the nested tests close over locally defined vars, including function parameters. So you would need some form of lambda lifting and protocol between tests and functions. So the "cannot" means, at a minimum, that this sounds way too expensive.
Given the above analysis, I would say this should go into the contract package not the rackunit one.
Finally this is the first time I see a reason to be able to run off contracts (partially). :-)
;; - - -
@rfindler @chrdimo
In the Pyret language, tests may be included within the definition of a function using a
where
block:This runs the two
sum()
calls in thewhere
block as test cases. This even works for nested functions, with nested test cases run whenever the enclosing function's test cases result in a call to the enclosing function:example originally from this mailing list discussion
Here, the nested
result
function's test cases are run twice: once each whenmake-adder(3)
andmake-adder(4)
are called while testing the enclosingmake-adder
function. I'd like something like this for Racket, but there's a few things I'm unsure of:module+ test
avoids runtime dependencies on testing frameworks and I'd like a solution to this problem to preserve that feature.Discussion highly welcome, cc @mfelleisen