Skip to content

Commit faca5d9

Browse files
committed
add check-equal?/values and check-match/values
1 parent 5ffb7ce commit faca5d9

File tree

4 files changed

+139
-6
lines changed

4 files changed

+139
-6
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
@@ -39,6 +39,8 @@
3939
check-not-eqv?
4040
check-not-equal?
4141
check-match
42+
check-equal?/values
43+
check-match/values
4244
fail)
4345

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

rackunit-lib/rackunit/private/test.rkt

+2
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,8 @@
111111
check-not-equal?
112112
check-regexp-match
113113
check-match
114+
check-equal?/values
115+
check-match/values
114116
fail)
115117

116118

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

+40
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,29 @@
126126
(check-match (data 1 2 (data 1 2 3))
127127
(data _ _ (data x y z))
128128
(equal? (+ x y z) 6))))
129+
130+
(test-case "Trivial check-match/values test"
131+
(check-match/values "whatever" (values _)))
132+
133+
(test-case "Simple check-match/values test"
134+
(check-match/values (values 1 2 3) (values _ _ 3)))
135+
136+
(test-case "Using check-match/values with ellipses"
137+
(check-match/values (values 1 2 4 5)
138+
(values 1 (? even? es) ... 5)
139+
#:when (equal? (apply + es) 6)))
140+
141+
(test-case "check-match/values with nested struct"
142+
(let ()
143+
(struct data (f1 f2 f3))
144+
(define (f)
145+
(values (data 1 2 (data 1 2 3))
146+
(data 4 5 (data 6 7 8))))
147+
(check-match/values (f)
148+
(values (data _ 2 (data x y z))
149+
(data _ 5 (data a b c)))
150+
#:when (equal? (+ x y z a b c) 27))))
151+
129152

130153
;; Failures
131154
(make-failure-test "check-equal? failure"
@@ -157,11 +180,28 @@
157180
(make-failure-test "check-= failure"
158181
check-= 1.0 2.0 0.0)
159182

183+
;; check-match
160184
(make-failure-test/stx "check-match failure pred"
161185
check-match 5 x (even? x))
162186

163187
(make-failure-test/stx "check-match failure match"
164188
check-match (list 4 5) (list _))
189+
190+
;; check-match/values
191+
(make-failure-test/stx "check-match/values: wrong number of values"
192+
check-match/values (values 3 4) (values _))
193+
194+
(make-failure-test/stx "check-match/values: right number, one value wrong"
195+
check-match/values (values 1 2 3) (values 1 2 4))
196+
197+
(make-failure-test/stx "check-match/values: when-condition failure"
198+
check-match/values (values 1 2 3) (values x y z)
199+
#:when (odd? (+ x y z)))
200+
201+
(make-failure-test/stx "check-match/values: failure with ellipses"
202+
check-match/values
203+
(values 1 2 4 5)
204+
(values 1 (? even? es) ...))
165205

166206
(test-case "check-= allows differences within epsilon"
167207
(check-= 1.0 1.09 1.1))

0 commit comments

Comments
 (0)