|
1 | 1 | #lang racket/base |
2 | 2 |
|
3 | | -(provide check-fail/error |
4 | | - check-fail |
5 | | - check-fail* |
6 | | - check-fail/info) |
| 3 | +(provide check-fail) |
7 | 4 |
|
8 | 5 | (require (for-syntax racket/base) |
9 | 6 | racket/function |
10 | 7 | racket/list |
11 | 8 | rackunit/log |
12 | 9 | syntax/parse/define |
13 | 10 | rackunit |
14 | | - rackunit/private/check-info) |
| 11 | + (only-in rackunit/private/check-info |
| 12 | + current-check-info |
| 13 | + pretty-info)) |
15 | 14 |
|
16 | 15 |
|
17 | | -(define-check (check-fail pred-or-msg chk-thnk) |
18 | | - (contract-pred-or-msg! 'check-fail pred-or-msg) |
| 16 | +(define-check (check-fail tree chk-thnk) |
| 17 | + (contract-tree! 'check-fail tree) |
19 | 18 | (contract-thunk! 'check-fail chk-thnk) |
20 | 19 | (define failure (check-raise-value chk-thnk)) |
21 | | - (with-expected pred-or-msg |
22 | | - (assert-failure failure) |
23 | | - (assert-check-failure failure) |
24 | | - (if (procedure? pred-or-msg) |
25 | | - (unless (pred-or-msg failure) |
26 | | - (with-actual failure |
27 | | - (fail-check "Wrong exception raised"))) |
28 | | - (let ([msg (exn-message failure)]) |
29 | | - (unless (regexp-match? pred-or-msg msg) |
30 | | - (with-actual failure |
31 | | - (with-check-info (['actual-msg msg]) |
32 | | - (fail-check "Wrong exception raised")))))))) |
33 | | - |
34 | | -(define-check (check-fail/info expected-info chk-thnk) |
35 | | - (contract-info! 'check-fail/info expected-info) |
36 | | - (contract-thunk! 'check-fail/info chk-thnk) |
37 | | - (define failure (check-raise-value chk-thnk)) |
38 | | - (with-expected expected-info |
39 | | - (assert-failure failure) |
40 | | - (assert-check-failure failure) |
41 | | - (define (has-expected-name? info) |
42 | | - (equal? (check-info-name info) (check-info-name expected-info))) |
43 | | - (define infos (exn:test:check-stack failure)) |
44 | | - (define info-names (map check-info-name infos)) |
45 | | - (define matching-infos (filter has-expected-name? infos)) |
46 | | - (when (empty? matching-infos) |
47 | | - (with-check-info (['actual-info-names info-names]) |
48 | | - (fail-check "Check failure did not contain the expected info"))) |
49 | | - (unless (member expected-info matching-infos) |
50 | | - (with-check-info* (map make-check-actual matching-infos) |
51 | | - (λ () (fail-check "Check failure contained info(s) with matching name but unexpected value")))))) |
52 | | - |
53 | | -(define-check (check-fail* chk-thnk) |
54 | | - (contract-thunk! 'check-fail* chk-thnk) |
55 | | - (define failure (check-raise-value chk-thnk)) |
56 | | - (assert-failure failure) |
57 | | - (assert-check-failure failure)) |
58 | | - |
59 | | -(define-check (check-fail/error pred-or-msg chk-thnk) |
60 | | - (contract-pred-or-msg! 'check-fail/error pred-or-msg) |
61 | | - (contract-thunk! 'check-fail/error chk-thnk) |
62 | | - (define failure (check-raise-value chk-thnk)) |
63 | | - (with-expected pred-or-msg |
64 | | - (assert-failure failure) |
65 | | - (assert-not-check-failure failure) |
66 | | - (cond |
67 | | - [(procedure? pred-or-msg) |
68 | | - (unless (pred-or-msg failure) |
69 | | - (with-actual failure |
70 | | - (fail-check "Wrong error raised")))] |
71 | | - [(exn? failure) |
72 | | - (define msg (exn-message failure)) |
73 | | - (unless (regexp-match? pred-or-msg msg) |
74 | | - (with-actual failure |
75 | | - (with-check-info (['actual-msg msg]) |
76 | | - (fail-check "Wrong error raised"))))] |
77 | | - [else |
78 | | - (with-actual failure |
79 | | - (fail-check "Wrong error raised"))]))) |
| 20 | + (unless (exn:test:check? failure) |
| 21 | + (with-actual failure |
| 22 | + (fail-check "Check raised error instead of failing"))) |
| 23 | + (check-tree-assert tree failure)) |
80 | 24 |
|
81 | 25 | ;; Shorthands for adding infos |
82 | 26 |
|
83 | 27 | (define-simple-macro (with-actual act:expr body:expr ...) |
84 | | - (with-check-info* (list (make-check-actual act)) (λ () body ...))) |
| 28 | + (with-check-info* (error-info act) (λ () body ...))) |
85 | 29 |
|
86 | | -(define-simple-macro (with-expected exp:expr body:expr ...) |
87 | | - (with-check-info* (list (make-check-expected exp)) (λ () body ...))) |
| 30 | +(define (list/if . vs) (filter values vs)) |
88 | 31 |
|
89 | | -;; Pseudo-contract helpers, to be replaced with real check contracts eventually |
| 32 | +(define (error-info raised) |
| 33 | + (list/if (make-check-actual raised) |
| 34 | + (and (exn? raised) |
| 35 | + (make-check-info 'actual-message (exn-message raised))) |
| 36 | + (and (exn:test:check? raised) |
| 37 | + (make-check-info 'actual-info |
| 38 | + (nested-info |
| 39 | + (exn:test:check-stack raised)))))) |
90 | 40 |
|
91 | | -(define (contract-pred-or-msg! name pred-or-msg) |
92 | | - (unless (or (and (procedure? pred-or-msg) |
93 | | - (procedure-arity-includes? pred-or-msg 1)) |
94 | | - (regexp? pred-or-msg)) |
95 | | - (define ctrct "(or/c (-> any/c boolean?) regexp?)") |
96 | | - (raise-argument-error name ctrct pred-or-msg))) |
| 41 | +;; Pseudo-contract helpers, to be replaced with real check contracts eventually |
97 | 42 |
|
98 | 43 | (define (contract-thunk! name thnk) |
99 | | - (unless (procedure? thnk) (raise-argument-error name "(-> any)" thnk))) |
100 | | - |
101 | | -(define (contract-info! name info) |
102 | | - (unless (check-info? info) (raise-argument-error name "check-info?" info))) |
| 44 | + (unless (and (procedure? thnk) |
| 45 | + (procedure-arity-includes? thnk 0)) |
| 46 | + (raise-argument-error name "(-> any)" thnk))) |
| 47 | + |
| 48 | +(define (contract-tree! name tree) |
| 49 | + (for ([v (in-list (flatten tree))]) |
| 50 | + (unless (or (and (procedure? v) |
| 51 | + (procedure-arity-includes? v 1)) |
| 52 | + (regexp? v) |
| 53 | + (check-info? v)) |
| 54 | + (define ctrct "(or/c (-> any/c boolean?) regexp? check-info?)") |
| 55 | + (raise-argument-error name ctrct v)))) |
103 | 56 |
|
104 | 57 | ;; Extracting raised values from checks |
105 | 58 |
|
|
109 | 62 | ;; instead of writing to stdout / stderr, 2) the inner check doesn't log |
110 | 63 | ;; any pass or fail information to rackunit/log, and 3) the inner check's info |
111 | 64 | ;; stack is independent of the outer check's info stack. |
112 | | - (parameterize ([current-check-handler raise] |
113 | | - [test-log-enabled? #f] |
114 | | - [current-check-info (list)]) |
115 | | - (with-handlers ([(negate exn:break?) values]) (chk-thnk) #f))) |
| 65 | + (or (parameterize ([current-check-handler raise] |
| 66 | + [test-log-enabled? #f] |
| 67 | + [current-check-info (list)]) |
| 68 | + (with-handlers ([(negate exn:break?) values]) (chk-thnk) #f)) |
| 69 | + (fail-check "Check passed unexpectedly"))) |
116 | 70 |
|
117 | 71 | ;; Assertion helpers |
118 | 72 |
|
119 | | -(define (assert-failure maybe-failure) |
120 | | - (unless maybe-failure (fail-check "No check failure occurred"))) |
121 | | - |
122 | | -(define (assert-check-failure failure) |
123 | | - (unless (exn:test:check? failure) |
124 | | - (with-actual failure |
125 | | - (fail-check "A value other than a check failure was raised")))) |
126 | | - |
127 | | -(define (assert-not-check-failure failure) |
128 | | - (when (exn:test:check? failure) |
129 | | - (with-actual failure |
130 | | - (fail-check "Wrong error raised")))) |
| 73 | +(struct failure (type expected) #:transparent) |
| 74 | + |
| 75 | +(define (assert-pred raised pred) |
| 76 | + (and (not (pred raised)) |
| 77 | + (failure 'predicate pred))) |
| 78 | + |
| 79 | +(define (assert-regexp exn rx) |
| 80 | + (and (not (regexp-match? rx (exn-message exn))) |
| 81 | + (failure 'message rx))) |
| 82 | + |
| 83 | +(define (assert-info exn info) |
| 84 | + (and (not (member info (exn:test:check-stack exn))) |
| 85 | + (failure 'info info))) |
| 86 | + |
| 87 | +(define (assert assertion raised) |
| 88 | + ((cond [(procedure? assertion) assert-pred] |
| 89 | + [(regexp? assertion) assert-regexp] |
| 90 | + [(check-info? assertion) assert-info]) |
| 91 | + raised assertion)) |
| 92 | + |
| 93 | +(define (assertions-adjust assertions raised) |
| 94 | + (define is-exn? (exn? raised)) |
| 95 | + (define has-regexps? (ormap regexp? assertions)) |
| 96 | + (define adjust-regexps? (and has-regexps? (not is-exn?))) |
| 97 | + (if adjust-regexps? |
| 98 | + (cons exn? (filter-not regexp? assertions)) |
| 99 | + assertions)) |
| 100 | + |
| 101 | +(define (assertion-tree-apply tree raised) |
| 102 | + (define assertions (assertions-adjust (flatten tree) raised)) |
| 103 | + (filter-map (λ (a) (assert a raised)) assertions)) |
| 104 | + |
| 105 | +(define (failure-list->info failures) |
| 106 | + (define vs |
| 107 | + (if (equal? (length failures) 1) |
| 108 | + (pretty-info (failure-expected (first failures))) |
| 109 | + (nested-info (for/list ([f (in-list failures)]) |
| 110 | + (make-check-info (failure-type f) |
| 111 | + (pretty-info (failure-expected f))))))) |
| 112 | + (make-check-info 'expected vs)) |
| 113 | + |
| 114 | +(define (check-tree-assert tree raised) |
| 115 | + (with-actual raised |
| 116 | + (define failures (assertion-tree-apply tree raised)) |
| 117 | + (unless (empty? failures) |
| 118 | + (with-check-info* (list (failure-list->info failures)) |
| 119 | + fail-check)))) |
0 commit comments