Skip to content

Commit bba2d08

Browse files
committed
add check-equal?/values and check-match/values
1 parent 9803e2c commit bba2d08

File tree

4 files changed

+143
-8
lines changed

4 files changed

+143
-8
lines changed

rackunit-doc/rackunit/scribblings/check.scrbl

+35-3
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
#lang scribble/doc
22
@(require "base.rkt")
33

4-
@(require (for-label racket/match))
4+
@(require (for-label racket/match racket/list))
55

66
@(define rackunit-eval (make-base-eval))
77
@(interaction-eval #:eval rackunit-eval (require rackunit))
8+
@(interaction-eval #:eval rackunit-eval (require racket/list))
89
@(interaction-eval #:eval rackunit-eval (error-print-context-length 0))
910

1011
@title{Checks}
@@ -17,8 +18,9 @@ for customizing how failures are handled).
1718

1819
Although checks are implemented as macros, which is
1920
necessary to grab source location, they are conceptually
20-
functions (with the exception of @racket[check-match] below).
21-
This means, for instance, checks always evaluate
21+
functions (with the exception of @racket[check-match],
22+
@racket[check-equal?/values], and @racket[check-match/values]
23+
below). This means, for instance, checks always evaluate
2224
their arguments. You can use checks as first class
2325
functions, though you will lose precision in the reported
2426
source locations if you do so.
@@ -224,6 +226,36 @@ This check fails because of a failure to match:
224226

225227
}
226228

229+
@defform[(check-equal?/values expr expr)]{
230+
231+
Like @racket[check-equal?], except handling multiple values.
232+
233+
@interaction[#:eval rackunit-eval
234+
(check-equal?/values (quotient/remainder 67 12)
235+
(values 5 7))
236+
(check-equal?/values (split-at (list 'a 'b 'c 'd 'e) 2)
237+
(values (list 'a 'b)
238+
(list 'c 'd 'e)))
239+
]
240+
241+
}
242+
243+
@defform*[#:literals (values)
244+
((check-match/values expr (values pattern ...))
245+
(check-match/values expr (values pattern ...) #:when pred)
246+
(check-match/values expr (values pattern ...) #:unless pred))]{
247+
248+
Like @racket[check-match], except handling multiple values.
249+
250+
@interaction[#:eval rackunit-eval
251+
(check-match/values (split-at (list 1 3 4 6 8) 2)
252+
(values (list (? odd?) ...)
253+
(list (? even?) ...)))
254+
]
255+
256+
}
257+
258+
227259

228260
@defproc[(check (op (-> any any any))
229261
(v1 any)

rackunit-lib/rackunit/private/check.rkt

+62-3
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@
4040
check-not-eqv?
4141
check-not-equal?
4242
check-match
43+
check-equal?/values
44+
check-match/values
4345
fail)
4446

4547
(define current-check-handler (make-parameter display-test-failure/error))
@@ -227,8 +229,65 @@
227229
(make-check-actual actual-val)
228230
(make-check-expected 'expected))
229231
(lambda ()
230-
(check-true (match actual-val
231-
[expected pred]
232-
[_ #f]))))))]
232+
(check-not-false (match actual-val
233+
[expected pred]
234+
[_ #f]))))))]
233235
[(_ actual expected)
234236
(syntax/loc stx (check-match actual expected #t))]))
237+
238+
;; NOTE: Like check-match, the check-equal?/values and check-match/values forms
239+
;; do not evaluate their arguments like functions would, so they're defined
240+
;; with define-syntax instead
241+
(define-syntax check-equal?/values
242+
(lambda (stx)
243+
(syntax-case stx ()
244+
[(_ actual expected)
245+
(quasisyntax
246+
(let ([actual-lst (call-with-values (λ () actual) list)]
247+
[expected-lst (call-with-values (λ () expected) list)])
248+
(with-check-info*
249+
(list (make-check-name 'check-equal?/values)
250+
(make-check-location
251+
(syntax->location (quote-syntax #,(datum->syntax #f 'loc stx))))
252+
(make-check-expression '#,(syntax->datum stx))
253+
(make-check-actual (cons 'values (map printed actual-lst)))
254+
(make-check-expected (cons 'values (map printed expected-lst))))
255+
(lambda ()
256+
(check-equal? actual-lst expected-lst)))))])))
257+
258+
(define-syntax check-match/values
259+
(lambda (stx)
260+
(syntax-case stx (values)
261+
[(_ actual (values expected ...))
262+
(syntax/loc stx
263+
(check-match/values actual
264+
(values expected ...)
265+
#:when #t))]
266+
[(_ actual (values expected ...) #:unless unless-condition)
267+
(syntax/loc stx
268+
(check-match/values actual
269+
(values expected ...)
270+
#:when (not unless-condition)))]
271+
[(_ actual (values expected ...) #:when pred)
272+
(quasisyntax
273+
(let ([actual-lst (call-with-values (λ () actual) list)])
274+
(with-check-info*
275+
(list (make-check-name 'check-match/values)
276+
(make-check-location
277+
(syntax->location (quote-syntax #,(datum->syntax #f 'loc stx))))
278+
(make-check-expression '#,(syntax->datum stx))
279+
(make-check-actual (cons 'values (map printed actual-lst)))
280+
(make-check-expected '(values expected ...)))
281+
(lambda ()
282+
(check-not-false (match actual-lst
283+
[(list expected ...) pred]
284+
[_ #f]))))))])))
285+
286+
;; A helper struct for check-equal?/values and check-match/values
287+
(struct printed (val) #:transparent
288+
#:property prop:custom-write
289+
(lambda (this out mode)
290+
(if (integer? mode)
291+
(print (printed-val this) out mode)
292+
(print (printed-val this) out))))
293+

rackunit-lib/rackunit/private/test.rkt

+2
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,8 @@
109109
check-not-equal?
110110
check-regexp-match
111111
check-match
112+
check-equal?/values
113+
check-match/values
112114
fail)
113115

114116

rackunit-test/tests/rackunit/check-test.rkt

+44-2
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,30 @@
122122
(check-match (data 1 2 (data 1 2 3))
123123
(data _ _ (data x y z))
124124
(equal? (+ x y z) 6))))
125-
125+
126+
(test-case "Trivial check-match/values test"
127+
(check-match/values "whatever" (values _)))
128+
129+
(test-case "Simple check-match/values test"
130+
(check-match/values (values 1 2 3) (values _ _ 3)))
131+
132+
(test-case "Using check-match/values with ellipses"
133+
(check-match/values (values 1 2 4 5)
134+
(values 1 (? even? es) ... 5)
135+
#:when (equal? (apply + es) 6)))
136+
137+
(test-case "check-match/values with nested struct"
138+
(let ()
139+
(struct data (f1 f2 f3))
140+
(define (f)
141+
(values (data 1 2 (data 1 2 3))
142+
(data 4 5 (data 6 7 8))))
143+
(check-match/values (f)
144+
(values (data _ 2 (data x y z))
145+
(data _ 5 (data a b c)))
146+
#:when (equal? (+ x y z a b c) 27))))
147+
148+
126149
;; Failures
127150
(make-failure-test "check-equal? failure"
128151
check-equal? 1 2)
@@ -153,12 +176,31 @@
153176
(make-failure-test "check-= failure"
154177
check-= 1.0 2.0 0.0)
155178

179+
180+
;; check-match
156181
(make-failure-test/stx "check-match failure pred"
157182
check-match 5 x (even? x))
158183

159184
(make-failure-test/stx "check-match failure match"
160185
check-match (list 4 5) (list _))
161-
186+
187+
;; check-match/values
188+
(make-failure-test/stx "check-match/values: wrong number of values"
189+
check-match/values (values 3 4) (values _))
190+
191+
(make-failure-test/stx "check-match/values: right number, one value wrong"
192+
check-match/values (values 1 2 3) (values 1 2 4))
193+
194+
(make-failure-test/stx "check-match/values: when-condition failure"
195+
check-match/values (values 1 2 3) (values x y z)
196+
#:when (odd? (+ x y z)))
197+
198+
(make-failure-test/stx "check-match/values: failure with ellipses"
199+
check-match/values
200+
(values 1 2 4 5)
201+
(values 1 (? even? es) ...))
202+
203+
162204
(test-case "check-= allows differences within epsilon"
163205
(check-= 1.0 1.09 1.1))
164206

0 commit comments

Comments
 (0)