clucumber 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (139) hide show
  1. data/LICENSE +1 -1
  2. data/README.md +4 -9
  3. data/lib/clucumber/clucumber-bootstrap.lisp +32 -0
  4. data/lib/clucumber/vendor/cl-interpol/alias.lisp +55 -0
  5. data/lib/clucumber/vendor/cl-interpol/cl-interpol.asd +56 -0
  6. data/lib/clucumber/vendor/cl-interpol/load.lisp +53 -0
  7. data/lib/clucumber/vendor/cl-interpol/packages.lisp +40 -0
  8. data/lib/clucumber/vendor/cl-interpol/read.lisp +716 -0
  9. data/lib/clucumber/vendor/cl-interpol/specials.lisp +113 -0
  10. data/lib/clucumber/vendor/cl-interpol/test/packages.lisp +34 -0
  11. data/lib/clucumber/vendor/cl-interpol/test/tests.lisp +128 -0
  12. data/lib/clucumber/vendor/cl-interpol/test.lisp +177 -0
  13. data/lib/clucumber/vendor/cl-interpol/test2.lisp +6254 -0
  14. data/lib/clucumber/vendor/cl-interpol/unicode.lisp +13912 -0
  15. data/lib/clucumber/vendor/cl-interpol/util.lisp +122 -0
  16. data/lib/clucumber/vendor/cl-ppcre/api.lisp +1262 -0
  17. data/lib/clucumber/vendor/cl-ppcre/charmap.lisp +152 -0
  18. data/lib/clucumber/vendor/cl-ppcre/charset.lisp +242 -0
  19. data/lib/clucumber/vendor/cl-ppcre/chartest.lisp +98 -0
  20. data/lib/clucumber/vendor/cl-ppcre/cl-ppcre-test.asd +34 -0
  21. data/lib/clucumber/vendor/cl-ppcre/cl-ppcre-unicode/packages.lisp +38 -0
  22. data/lib/clucumber/vendor/cl-ppcre/cl-ppcre-unicode/resolver.lisp +61 -0
  23. data/lib/clucumber/vendor/cl-ppcre/cl-ppcre-unicode.asd +58 -0
  24. data/lib/clucumber/vendor/cl-ppcre/cl-ppcre.asd +79 -0
  25. data/lib/clucumber/vendor/cl-ppcre/closures.lisp +471 -0
  26. data/lib/clucumber/vendor/cl-ppcre/convert.lisp +875 -0
  27. data/lib/clucumber/vendor/cl-ppcre/errors.lisp +84 -0
  28. data/lib/clucumber/vendor/cl-ppcre/lexer.lisp +737 -0
  29. data/lib/clucumber/vendor/cl-ppcre/lispworks-defsystem.lisp +57 -0
  30. data/lib/clucumber/vendor/cl-ppcre/load.lisp +67 -0
  31. data/lib/clucumber/vendor/cl-ppcre/optimize.lisp +578 -0
  32. data/lib/clucumber/vendor/cl-ppcre/packages.lisp +68 -0
  33. data/lib/clucumber/vendor/cl-ppcre/parser.lisp +319 -0
  34. data/lib/clucumber/vendor/cl-ppcre/ppcre-tests.lisp +269 -0
  35. data/lib/clucumber/vendor/cl-ppcre/regex-class-util.lisp +555 -0
  36. data/lib/clucumber/vendor/cl-ppcre/regex-class.lisp +259 -0
  37. data/lib/clucumber/vendor/cl-ppcre/repetition-closures.lisp +833 -0
  38. data/lib/clucumber/vendor/cl-ppcre/scanner.lisp +506 -0
  39. data/lib/clucumber/vendor/cl-ppcre/specials.lisp +172 -0
  40. data/lib/clucumber/vendor/cl-ppcre/test/packages.lisp +37 -0
  41. data/lib/clucumber/vendor/cl-ppcre/test/perl-tests.lisp +150 -0
  42. data/lib/clucumber/vendor/cl-ppcre/test/tests.lisp +159 -0
  43. data/lib/clucumber/vendor/cl-ppcre/test/unicode-tests.lisp +80 -0
  44. data/lib/clucumber/vendor/cl-ppcre/util.lisp +201 -0
  45. data/lib/clucumber/vendor/cl-unicode/alias.lisp +135 -0
  46. data/lib/clucumber/vendor/cl-unicode/api.lisp +412 -0
  47. data/lib/clucumber/vendor/cl-unicode/build/char-info.lisp +133 -0
  48. data/lib/clucumber/vendor/cl-unicode/build/dump.lisp +239 -0
  49. data/lib/clucumber/vendor/cl-unicode/build/read.lisp +280 -0
  50. data/lib/clucumber/vendor/cl-unicode/build/util.lisp +182 -0
  51. data/lib/clucumber/vendor/cl-unicode/cl-unicode.asd +90 -0
  52. data/lib/clucumber/vendor/cl-unicode/conditions.lisp +54 -0
  53. data/lib/clucumber/vendor/cl-unicode/derived.lisp +120 -0
  54. data/lib/clucumber/vendor/cl-unicode/hash-tables.lisp +20 -0
  55. data/lib/clucumber/vendor/cl-unicode/lists.lisp +10 -0
  56. data/lib/clucumber/vendor/cl-unicode/methods.lisp +15 -0
  57. data/lib/clucumber/vendor/cl-unicode/packages.lisp +76 -0
  58. data/lib/clucumber/vendor/cl-unicode/specials.lisp +135 -0
  59. data/lib/clucumber/vendor/cl-unicode/test/packages.lisp +34 -0
  60. data/lib/clucumber/vendor/cl-unicode/test/tests.lisp +150 -0
  61. data/lib/clucumber/vendor/cl-unicode/test-functions.lisp +94 -0
  62. data/lib/clucumber/vendor/cl-unicode/util.lisp +274 -0
  63. data/lib/clucumber/vendor/lift/compare/fiveam-tests.lisp +14 -0
  64. data/lib/clucumber/vendor/lift/compare/lift-tests.lisp +13 -0
  65. data/lib/clucumber/vendor/lift/compare/rt-tests.lisp +16 -0
  66. data/lib/clucumber/vendor/lift/compare/xlunit-tests.lisp +15 -0
  67. data/lib/clucumber/vendor/lift/dev/changes.lisp +61 -0
  68. data/lib/clucumber/vendor/lift/dev/config.lisp +354 -0
  69. data/lib/clucumber/vendor/lift/dev/copy-file.lisp +117 -0
  70. data/lib/clucumber/vendor/lift/dev/introspection.lisp +232 -0
  71. data/lib/clucumber/vendor/lift/dev/lift-interface.lisp +56 -0
  72. data/lib/clucumber/vendor/lift/dev/lift-notes.lisp +202 -0
  73. data/lib/clucumber/vendor/lift/dev/lift-randomized.lisp +45 -0
  74. data/lib/clucumber/vendor/lift/dev/lift.lisp +2383 -0
  75. data/lib/clucumber/vendor/lift/dev/macros.lisp +229 -0
  76. data/lib/clucumber/vendor/lift/dev/measuring.lisp +156 -0
  77. data/lib/clucumber/vendor/lift/dev/packages.lisp +161 -0
  78. data/lib/clucumber/vendor/lift/dev/port.lisp +151 -0
  79. data/lib/clucumber/vendor/lift/dev/prototypes.lisp +282 -0
  80. data/lib/clucumber/vendor/lift/dev/random-testing.lisp +124 -0
  81. data/lib/clucumber/vendor/lift/dev/report-locations.lisp +13 -0
  82. data/lib/clucumber/vendor/lift/dev/reports.lisp +916 -0
  83. data/lib/clucumber/vendor/lift/dev/utilities.lisp +242 -0
  84. data/lib/clucumber/vendor/lift/docs/package.lisp +6 -0
  85. data/lib/clucumber/vendor/lift/docs/setup.lisp +17 -0
  86. data/lib/clucumber/vendor/lift/examples/basic-examples.lisp +289 -0
  87. data/lib/clucumber/vendor/lift/examples/random-testing.lisp +32 -0
  88. data/lib/clucumber/vendor/lift/lift-documentation.asd +28 -0
  89. data/lib/clucumber/vendor/lift/lift-test.asd +35 -0
  90. data/lib/clucumber/vendor/lift/lift.asd +77 -0
  91. data/lib/clucumber/vendor/lift/test/equality-tests.lisp +40 -0
  92. data/lib/clucumber/vendor/lift/test/finding-tests.lisp +32 -0
  93. data/lib/clucumber/vendor/lift/test/lift-test.lisp +783 -0
  94. data/lib/clucumber/vendor/lift/test/order-of-operations.lisp +54 -0
  95. data/lib/clucumber/vendor/lift/test/packages.lisp +17 -0
  96. data/lib/clucumber/vendor/lift/test/test-config-files.lisp +20 -0
  97. data/lib/clucumber/vendor/lift/test/test-dynamic-variables.lisp +57 -0
  98. data/lib/clucumber/vendor/lift/test/test-maximum-problems.lisp +74 -0
  99. data/lib/clucumber/vendor/lift/test/test-prototypes.lisp +278 -0
  100. data/lib/clucumber/vendor/lift/test/test-timeout.lisp +37 -0
  101. data/lib/clucumber/vendor/lift/test/tests-in-progress.lisp +62 -0
  102. data/lib/clucumber/vendor/lift/test/testsuite-expects.lisp +60 -0
  103. data/lib/clucumber/vendor/lift/timeout/package.lisp +13 -0
  104. data/lib/clucumber/vendor/lift/timeout/with-timeout.lisp +123 -0
  105. data/lib/clucumber/vendor/lift/website/stuff/Temp.lisp +34 -0
  106. data/lib/clucumber/vendor/st-json/st-json.asd +3 -0
  107. data/lib/clucumber/vendor/st-json/st-json.lisp +310 -0
  108. data/lib/clucumber/vendor/trivial-backtrace/dev/backtrace.lisp +127 -0
  109. data/lib/clucumber/vendor/trivial-backtrace/dev/fallback.lisp +10 -0
  110. data/lib/clucumber/vendor/trivial-backtrace/dev/map-backtrace.lisp +103 -0
  111. data/lib/clucumber/vendor/trivial-backtrace/dev/mucking.lisp +75 -0
  112. data/lib/clucumber/vendor/trivial-backtrace/dev/packages.lisp +13 -0
  113. data/lib/clucumber/vendor/trivial-backtrace/dev/utilities.lisp +104 -0
  114. data/lib/clucumber/vendor/trivial-backtrace/test/packages.lisp +5 -0
  115. data/lib/clucumber/vendor/trivial-backtrace/test/test-setup.lisp +4 -0
  116. data/lib/clucumber/vendor/trivial-backtrace/test/tests.lisp +16 -0
  117. data/lib/clucumber/vendor/trivial-backtrace/trivial-backtrace-test.asd +22 -0
  118. data/lib/clucumber/vendor/trivial-backtrace/trivial-backtrace.asd +35 -0
  119. data/lib/clucumber/vendor/usocket/backend/allegro.lisp +199 -0
  120. data/lib/clucumber/vendor/usocket/backend/armedbear.lisp +491 -0
  121. data/lib/clucumber/vendor/usocket/backend/clisp.lisp +260 -0
  122. data/lib/clucumber/vendor/usocket/backend/cmucl.lisp +266 -0
  123. data/lib/clucumber/vendor/usocket/backend/lispworks.lisp +741 -0
  124. data/lib/clucumber/vendor/usocket/backend/mcl.lisp +369 -0
  125. data/lib/clucumber/vendor/usocket/backend/openmcl.lisp +206 -0
  126. data/lib/clucumber/vendor/usocket/backend/sbcl.lisp +424 -0
  127. data/lib/clucumber/vendor/usocket/backend/scl.lisp +261 -0
  128. data/lib/clucumber/vendor/usocket/condition.lisp +227 -0
  129. data/lib/clucumber/vendor/usocket/package.lisp +82 -0
  130. data/lib/clucumber/vendor/usocket/server.lisp +45 -0
  131. data/lib/clucumber/vendor/usocket/test/package.lisp +13 -0
  132. data/lib/clucumber/vendor/usocket/test/test-usocket.lisp +166 -0
  133. data/lib/clucumber/vendor/usocket/usocket-test.asd +26 -0
  134. data/lib/clucumber/vendor/usocket/usocket.asd +37 -0
  135. data/lib/clucumber/vendor/usocket/usocket.lisp +542 -0
  136. data/lib/clucumber/vendor/usocket/vendor/kqueue.lisp +1 -0
  137. data/lib/clucumber/vendor/usocket/vendor/split-sequence.lisp +245 -0
  138. data/lib/clucumber.rb +29 -7
  139. metadata +151 -5
@@ -0,0 +1,2383 @@
1
+ ;;;-*- Mode: Lisp; Package: lift -*-
2
+
3
+ (in-package #:lift)
4
+
5
+ ;;; ---------------------------------------------------------------------------
6
+ ;;; global environment thingies
7
+ ;;; ---------------------------------------------------------------------------
8
+
9
+ (defparameter *make-testsuite-arguments*
10
+ '(:run-setup :test-slot-names :equality-test :log-file :timeout
11
+ :default-initargs :profile :expected-failure :expected-error))
12
+
13
+ (defvar *current-testsuite-name* nil)
14
+ (defvar *current-test-case-name* nil)
15
+
16
+ (defvar *test-is-being-defined?* nil)
17
+ (defvar *test-is-being-compiled?* nil)
18
+ (defvar *test-is-being-loaded?* nil)
19
+ (defvar *test-is-being-executed?* nil)
20
+
21
+ (defvar *test-maximum-error-count* nil
22
+ "The maximum numbers of errors to allow during a [run-tests][].
23
+
24
+ If `*test-maximum-error-count*` is nil, then a call to run-tests
25
+ will continue regardless of the number of errors. If it a positive
26
+ integer, then run-tests will stop as soon as the number of test-errors
27
+ if greater than or equal to its value. Setting this to some small
28
+ value can help prevent running lengthly test-suites when there are many
29
+ errors. See also [\\*test-maximum-failure-count\\*][].")
30
+
31
+ (defvar *test-maximum-failure-count* nil
32
+ "The maximum numbers of failures to allow during a [run-tests][].
33
+
34
+ If `*test-maximum-failure-count*` is nil, then a call to run-tests
35
+ will continue regardless of the number of failures. If it a positive
36
+ integer, then run-tests will stop as soon as the number of test-failures
37
+ if greater than or equal to its value. Setting this to some small
38
+ value can help prevent running lengthly test-suites when there are many
39
+ failures. See also [\\*test-maximum-error-count\\*][].")
40
+
41
+ (defvar *lift-debug-output* *debug-io*
42
+ "Messages from LIFT will be sent to this stream. It can set to nil or
43
+ to an output stream. It defaults to *debug-io*.")
44
+
45
+ (defvar *test-maximum-time* 2
46
+ "Maximum number of seconds a process test is allowed to run before we give up.")
47
+
48
+ (defvar *test-break-on-errors?* nil)
49
+ (defvar *test-break-on-failures?* nil)
50
+ (defvar *test-do-children?* t)
51
+ (defparameter *test-ignore-warnings?* nil
52
+ "If true, LIFT will not cause a test to fail if a warning occurs while
53
+ the test is running. Note that this may interact oddly with ensure-warning.")
54
+ (defparameter *test-print-when-defined?* nil)
55
+ (defparameter *test-evaluate-when-defined?* t)
56
+ (defparameter *test-scratchpad* nil
57
+ "A place to put things. This is set to nil before every test.")
58
+ (defparameter *test-notepad* nil
59
+ "Another place to put things \(see {ref *test-scratchpad*}\).")
60
+
61
+ (defparameter *lift-equality-test* 'equal
62
+ "The function used in ensure-same to test if two things are equal. If metatilities is loaded, then you might want to use samep.")
63
+
64
+ (defvar *test-describe-if-not-successful?* nil
65
+ ;; Was t, but this behavior was extremely annoying since each
66
+ ;; time a test-restul appears in a stack backtrace it is printed
67
+ ;; over many unstructured lines.
68
+ "If true, then a complete test description is printed when there are any test warnings or failures. Otherwise, one would need to explicity call describe.")
69
+
70
+ (defvar *test-print-length* :follow-print
71
+ "The print-length in effect when LIFT prints test results. It works exactly like `*print-length*` except that it can also take on the value :follow-print. In this case, it will be set to the value of `*print-length*`.")
72
+ (defvar *test-print-level* :follow-print
73
+ "The print-level in effect when LIFT prints test results. It works exactly like `*print-level*` except that it can also take on the value :follow-print. In this case, it will be set to whatever `*print-level*` is.")
74
+
75
+ (defvar *test-print-testsuite-names* t
76
+ "If true, LIFT will print the name of each test suite to *debug-io* before it begins to run the suite. See also: *test-print-test-case-names*.")
77
+
78
+ (defvar *test-print-test-case-names* nil
79
+ "If true, LIFT will print the name of each test-case before it runs. See also: *test-print-testsuite-names*.")
80
+
81
+ (defparameter *lift-tests-to-skip* nil
82
+ "A lift of test-suites and (testsuite test-case) pairs that LIFT will ignore
83
+ during calls to run-tests.")
84
+
85
+ (defvar *test-result* nil
86
+ "Set to the most recent test result by calls to run-test or run-tests.")
87
+
88
+ (defvar *test-environment* nil)
89
+
90
+ (defvar *test-metadata* (list)
91
+ "A place for LIFT to put stuff.")
92
+
93
+ (defvar *current-test* nil
94
+ "The current testsuite.")
95
+
96
+ (defvar *testsuite-test-count* nil
97
+ "Temporary variable used to 'communicate' between deftestsuite and addtest.")
98
+
99
+ (defvar *lift-dribble-pathname* nil
100
+ "If bound, then test output from run-tests will be sent to this file in
101
+ in addition to *lift-standard-output*. It can be set to nil or to a pathname.")
102
+
103
+ (defvar *lift-report-pathname* nil
104
+ "If bound to a pathname or stream, then a summary of test information will
105
+ be written to it for later processing. It can be set to:
106
+
107
+ * `nil` - generate no output
108
+ * pathname designator - send output to this pathname
109
+ * `t` - send output to a pathname constructed from the name of the system
110
+ being tested (this only works if ASDF is being used to test the system).
111
+
112
+ As an example of the last case, if LIFT is testing a system named ...
113
+ ")
114
+
115
+ (defvar *lift-standard-output* *standard-output*
116
+ "Output from tests will be sent to this stream. If can set to nil or
117
+ to an output stream. It defaults to *standard-output*.")
118
+
119
+ (defvar *lift-if-dribble-exists* :append
120
+ "Specifies what to do to any existing file at *lift-dribble-pathname*. It
121
+ can be :supersede, :append, or :error.")
122
+
123
+ (defvar *test-show-expected-p* t)
124
+
125
+ (defvar *test-show-details-p* t)
126
+
127
+ (defvar *test-show-code-p* t)
128
+
129
+ ;;; ---------------------------------------------------------------------------
130
+ ;;; Error messages and warnings
131
+ ;;; ---------------------------------------------------------------------------
132
+
133
+ (defparameter +lift-test-name-not-supplied-with-test-class+
134
+ "if you specify a test-class, you must also specify a test-name.")
135
+
136
+ (defparameter +lift-test-class-not-found+
137
+ "test class '~S' not found.")
138
+
139
+ (defparameter +lift-confused-about-arguments+
140
+ "I'm confused about what you said?!")
141
+
142
+ (defparameter +lift-no-current-test-class+
143
+ "There is no current-test-class to use as a default.")
144
+
145
+ (defparameter +lift-could-not-find-test+
146
+ "Could not find test: ~S.~S")
147
+
148
+ (defparameter +run-tests-null-test-case+
149
+ "There is no current testsuite (possibly because
150
+ none have been defined yet?). You can specify the
151
+ testsuite to test by evaluating (run-tests :suite <suitename>).")
152
+
153
+ (defparameter +lift-unable-to-parse-test-name-and-class+
154
+ "")
155
+
156
+
157
+ ;;; ---------------------------------------------------------------------------
158
+ ;;; test conditions
159
+ ;;; ---------------------------------------------------------------------------
160
+
161
+ (define-condition lift-compile-error (error)
162
+ ((msg :initform ""
163
+ :reader msg
164
+ :initarg :lift-message))
165
+ (:report (lambda (c s)
166
+ (format s "Compile error: '~S'" (msg c)))))
167
+
168
+ (define-condition testsuite-not-defined (lift-compile-error)
169
+ ((testsuite-name :reader testsuite-name
170
+ :initarg :testsuite-name))
171
+ (:report (lambda (c s)
172
+ (format s "Test class ~A not defined before it was used."
173
+ (testsuite-name c)))))
174
+
175
+ (define-condition testsuite-ambiguous (lift-compile-error)
176
+ ((testsuite-name :reader testsuite-name
177
+ :initarg :testsuite-name)
178
+ (possible-matches :reader possible-matches
179
+ :initarg :possible-matches))
180
+ (:report
181
+ (lambda (c s)
182
+ (format s "There are several test suites named ~s: they are ~{~s~^, ~}"
183
+ (testsuite-name c)
184
+ (possible-matches c)))))
185
+
186
+ (define-condition test-case-not-defined (lift-compile-error)
187
+ ((testsuite-name :reader testsuite-name
188
+ :initarg :testsuite-name)
189
+ (test-case-name :reader test-case-name
190
+ :initarg :test-case-name))
191
+ (:report (lambda (c s)
192
+ (format s "Testsuite ~s has no test-case named ~s."
193
+ (testsuite-name c)
194
+ (test-case-name c)))))
195
+
196
+ (define-condition test-case-ambiguous (lift-compile-error)
197
+ ((testsuite-name :reader testsuite-name
198
+ :initarg :testsuite-name)
199
+ (test-case-name :reader test-case-name
200
+ :initarg :test-case-name)
201
+ (possible-matches :reader possible-matches
202
+ :initarg possible-matches))
203
+ (:report
204
+ (lambda (c s)
205
+ (format s "There are several test cases named ~s.~s: they are ~{~s~^, ~}"
206
+ (testsuite-name c)
207
+ (test-case-name c)
208
+ (possible-matches c)))))
209
+
210
+ (define-condition test-condition (warning)
211
+ ((message :initform ""
212
+ :initarg :message
213
+ :accessor message))
214
+ (:report (lambda (c s)
215
+ (when (message c)
216
+ (format s "~%~A" (message c))))))
217
+
218
+ (define-condition test-timeout-condition (test-condition)
219
+ ((maximum-time :initform *test-maximum-time*
220
+ :accessor maximum-time
221
+ :initarg :maximum-time))
222
+ (:report (lambda (c s)
223
+ (format s "Test ran out of time (longer than ~S-second~:P)"
224
+ (maximum-time c)))))
225
+
226
+ (define-condition ensure-failed-error (test-condition)
227
+ ((assertion :initform ""
228
+ :accessor assertion
229
+ :initarg :assertion))
230
+ (:report (lambda (c s)
231
+ (format s "Ensure failed: ~S ~@[(~a)~]"
232
+ (assertion c) (message c)))))
233
+
234
+ (define-condition ensure-null-failed-error (ensure-failed-error)
235
+ ((value :initform ""
236
+ :accessor value
237
+ :initarg :value)
238
+ (assertion :initform ""
239
+ :accessor assertion
240
+ :initarg :assertion))
241
+ (:report (lambda (c s)
242
+ (format s "Ensure null failed: ~s evaluates to ~s ~@[(~a)~]"
243
+ (assertion c) (value c) (message c)))))
244
+
245
+ (define-condition ensure-expected-condition (test-condition)
246
+ ((expected-condition-type
247
+ :initform nil
248
+ :accessor expected-condition-type
249
+ :initarg :expected-condition-type)
250
+ (the-condition
251
+ :initform nil
252
+ :accessor the-condition
253
+ :initarg :the-condition))
254
+ (:report (lambda (c s)
255
+ (let ((the-condition (the-condition c)))
256
+ (format s "Expected ~S but got ~S~@[:~_ ~A~]"
257
+ (expected-condition-type c)
258
+ (type-of the-condition)
259
+ (and (typep the-condition 'condition)
260
+ the-condition))))))
261
+
262
+ (define-condition ensure-expected-no-warning-condition (test-condition)
263
+ ((the-condition
264
+ :initform nil
265
+ :accessor the-condition
266
+ :initarg :the-condition))
267
+ (:report (lambda (c s)
268
+ (format s "Expected no warnings but got ~S"
269
+ (the-condition c)))))
270
+
271
+ (define-condition failed-comparison-condition (test-condition)
272
+ ((first-value :accessor first-value
273
+ :initarg :first-value)
274
+ (second-value :accessor second-value
275
+ :initarg :second-value)
276
+ (test :accessor test
277
+ :initarg :test)))
278
+
279
+ (define-condition ensure-not-same (failed-comparison-condition)
280
+ ()
281
+ (:report (lambda (c s)
282
+ (format s "Ensure-same: ~S is not ~S to ~S~@[ (~a)~]"
283
+ (first-value c) (test c) (second-value c)
284
+ (message c)))))
285
+
286
+ (define-condition ensure-same (failed-comparison-condition)
287
+ ()
288
+ (:report (lambda (c s)
289
+ (format s "Ensure-different: ~S is ~S to ~S~@[ (~a)~]"
290
+ (first-value c) (test c) (second-value c)
291
+ (message c)))))
292
+
293
+ (define-condition ensure-cases-failure (test-condition)
294
+ ((total :initarg :total :initform 0)
295
+ (problems :initarg :problems :initform nil))
296
+ (:report (lambda (condition stream)
297
+ (format
298
+ stream
299
+ "Ensure-cases: ~d out of ~d cases failed. Failing cases are:"
300
+ (length (slot-value condition 'problems))
301
+ (slot-value condition 'total))
302
+ (format
303
+ stream
304
+ "~&~@< ~@;~{~% ~{~20s ~3,8@t~a~}~^, ~}~:>"
305
+ (slot-value condition 'problems)))))
306
+
307
+ (define-condition unexpected-success-failure (test-condition)
308
+ ((expected :reader expected :initarg :expected)
309
+ (expected-more :reader expected-more :initarg :expected-more))
310
+ (:report (lambda (c s)
311
+ (format s "Test succeeded but we expected ~s (~s)"
312
+ (expected c)
313
+ (expected-more c)))))
314
+
315
+ (defun build-lift-error-message (context message &rest arguments)
316
+ (format nil "~A: ~A"
317
+ context
318
+ (apply #'format nil message arguments)))
319
+
320
+ (defun signal-lift-error (context message &rest arguments)
321
+ (let ((c (make-condition
322
+ 'lift-compile-error
323
+ :lift-message (apply #'build-lift-error-message
324
+ context message arguments))))
325
+ (unless (signal c)
326
+ (error c))))
327
+
328
+ (defun report-lift-error (context message &rest arguments)
329
+ (format *debug-io* "~&~A."
330
+ (apply #'build-lift-error-message context message arguments))
331
+ (values))
332
+
333
+ (defun lift-report-condition (c)
334
+ (format *debug-io* "~&~A." c))
335
+
336
+ (defmacro ensure (predicate &key report arguments)
337
+ "If ensure's `predicate` evaluates to false, then it will generate a
338
+ test failure. You can use the `report` and `arguments` keyword parameters
339
+ to customize the report generated in test results. For example:
340
+
341
+ (ensure (= 23 12)
342
+ :report \"I hope ~a does not = ~a\"
343
+ :arguments (12 23))
344
+
345
+ will generate a message like
346
+
347
+ Warning: Ensure failed: (= 23 12) (I hope 12 does not = 23)
348
+ "
349
+ (let ((gpredicate (gensym)))
350
+ `(let ((,gpredicate ,predicate))
351
+ (if ,gpredicate
352
+ (values ,gpredicate)
353
+ (let ((condition (make-condition
354
+ 'ensure-failed-error
355
+ :assertion ',predicate
356
+ ,@(when report
357
+ `(:message
358
+ (format nil ,report ,@arguments))))))
359
+ (if (find-restart 'ensure-failed)
360
+ (invoke-restart 'ensure-failed condition)
361
+ (warn condition)))))))
362
+
363
+ (defmacro ensure-null (predicate &key report arguments)
364
+ "If ensure-null's `predicate` evaluates to true, then it will generate a
365
+ test failure. You can use the `report` and `arguments` keyword parameters
366
+ to customize the report generated in test results. See [ensure][] for more
367
+ details."
368
+ (let ((g (gensym)))
369
+ `(let ((,g ,predicate))
370
+ (if (null ,g)
371
+ t
372
+ (let ((condition (make-condition 'ensure-null-failed-error
373
+ :value ,g
374
+ :assertion ',predicate
375
+ ,@(when report
376
+ `(:message (format nil ,report ,@arguments))))))
377
+ (if (find-restart 'ensure-failed)
378
+ (invoke-restart 'ensure-failed condition)
379
+ (warn condition)))))))
380
+
381
+ (defmacro ensure-condition (condition &body body)
382
+ "This macro is used to make sure that body really does produce condition."
383
+ (setf condition (remove-leading-quote condition))
384
+ (destructuring-bind (condition &key report arguments)
385
+ (if (consp condition) condition (list condition))
386
+ (let ((g (gensym)))
387
+ `(let ((,g nil))
388
+ (unwind-protect
389
+ (handler-case
390
+ (progn ,@body)
391
+ (,condition (cond)
392
+ (declare (ignore cond)) (setf ,g t))
393
+ (condition (cond)
394
+ (setf ,g t)
395
+ (let ((c (make-condition
396
+ 'ensure-expected-condition
397
+ :expected-condition-type ',condition
398
+ :the-condition cond
399
+ ,@(when report
400
+ `(:message
401
+ (format nil ,report ,arguments))))))
402
+ (if (find-restart 'ensure-failed)
403
+ (invoke-restart 'ensure-failed c)
404
+ (warn c)))))
405
+ (when (not ,g)
406
+ (if (find-restart 'ensure-failed)
407
+ (invoke-restart
408
+ 'ensure-failed
409
+ (make-condition
410
+ 'ensure-expected-condition
411
+ :expected-condition-type ',condition
412
+ :the-condition nil
413
+ ,@(when report
414
+ `(:message (format nil ,report ,arguments)))))
415
+ (warn "Ensure-condition didn't get the condition it expected."))))))))
416
+
417
+ (defmacro ensure-no-warning (&body body)
418
+ "This macro is used to make sure that body produces no warning."
419
+ (let ((g (gensym))
420
+ (gcondition (gensym)))
421
+ `(let ((,g nil)
422
+ (,gcondition nil))
423
+ (unwind-protect
424
+ (handler-case
425
+ (progn ,@body)
426
+ (warning (c)
427
+ (setf ,gcondition c ,g t)))
428
+ (when ,g
429
+ (let ((c (make-condition
430
+ 'ensure-expected-no-warning-condition
431
+ :the-condition ,gcondition)))
432
+ (if (find-restart 'ensure-failed)
433
+ (invoke-restart 'ensure-failed c)
434
+ (warn c))))))))
435
+
436
+ (defmacro ensure-warning (&body body)
437
+ "Ensure-warning evaluates its body. If the body does *not* signal a
438
+ warning, then ensure-warning will generate a test failure."
439
+ `(ensure-condition warning ,@body))
440
+
441
+ (defmacro ensure-error (&body body)
442
+ "Ensure-error evaluates its body. If the body does *not* signal an
443
+ error, then ensure-error will generate a test failure."
444
+ `(ensure-condition error ,@body))
445
+
446
+ (defmacro ensure-same
447
+ (form values &key (test nil test-specified-p)
448
+ (report nil) (arguments nil)
449
+ (ignore-multiple-values? nil))
450
+ "Ensure same compares value-or-values-1 value-or-values-2 or
451
+ each value of value-or-values-1 value-or-values-2 (if they are
452
+ multiple values) using test. If a problem is encountered
453
+ ensure-same raises a warning which uses report as a format string
454
+ and arguments as arguments to that string (if report and arguments
455
+ are supplied). If ensure-same is used within a test, a test failure
456
+ is generated instead of a warning"
457
+ (%build-ensure-comparison form values 'unless
458
+ test test-specified-p report arguments
459
+ ignore-multiple-values?))
460
+
461
+ (defmacro ensure-different
462
+ (form values &key (test nil test-specified-p)
463
+ (report nil) (arguments nil)
464
+ (ignore-multiple-values? nil))
465
+ "Ensure-different compares value-or-values-1 value-or-values-2 or each value of value-or-values-1 and value-or-values-2 (if they are multiple values) using test. If any comparison returns true, then ensure-different raises a warning which uses report as a format string and `arguments` as arguments to that string (if report and `arguments` are supplied). If ensure-different is used within a test, a test failure is generated instead of a warning"
466
+ (%build-ensure-comparison form values 'when
467
+ test test-specified-p report arguments
468
+ ignore-multiple-values?))
469
+
470
+ (defun %build-ensure-comparison
471
+ (form values guard-fn test test-specified-p report arguments
472
+ ignore-multiple-values?)
473
+ (setf test (remove-leading-quote test))
474
+ (when (and (consp test)
475
+ (eq (first test) 'function))
476
+ (setf test (second test)))
477
+ (let ((gblock (gensym "block-"))
478
+ (ga (gensym "a-"))
479
+ (gb (gensym "b-"))
480
+ (gtest (gensym "test-")))
481
+ `(block ,gblock
482
+ (flet ((,gtest (,ga ,gb)
483
+ (,@(cond (test-specified-p
484
+ (if (atom test)
485
+ (list test)
486
+ `(funcall ,test)))
487
+ (t
488
+ `(funcall *lift-equality-test*)))
489
+ ,ga ,gb)))
490
+ (loop for value in (,(if ignore-multiple-values?
491
+ 'list 'multiple-value-list) ,form)
492
+ for other-value in (,(if ignore-multiple-values?
493
+ 'list 'multiple-value-list) ,values) do
494
+ (,guard-fn (,gtest value other-value)
495
+ (,(ecase guard-fn
496
+ (unless 'maybe-raise-not-same-condition)
497
+ (when 'maybe-raise-ensure-same-condition))
498
+ value other-value
499
+ ,(if test-specified-p (list 'quote test) '*lift-equality-test*)
500
+ ,report ,@arguments)
501
+ (return-from ,gblock nil))))
502
+ (values t))))
503
+
504
+ (defun maybe-raise-not-same-condition (value-1 value-2 test
505
+ report &rest arguments)
506
+ (let ((condition (make-condition 'ensure-not-same
507
+ :first-value value-1
508
+ :second-value value-2
509
+ :test test
510
+ :message (when report
511
+ (apply #'format nil
512
+ report arguments)))))
513
+ (if (find-restart 'ensure-failed)
514
+ (invoke-restart 'ensure-failed condition)
515
+ (warn condition))))
516
+
517
+ (defun maybe-raise-ensure-same-condition (value-1 value-2 test
518
+ report &rest arguments)
519
+ (let ((condition (make-condition 'ensure-same
520
+ :first-value value-1
521
+ :second-value value-2
522
+ :test test
523
+ :message (when report
524
+ (apply #'format nil
525
+ report arguments)))))
526
+ (if (find-restart 'ensure-failed)
527
+ (invoke-restart 'ensure-failed condition)
528
+ (warn condition))))
529
+
530
+ (defmacro ensure-cases ((&rest vars) (&rest cases) &body body)
531
+ (let ((case (gensym))
532
+ (total (gensym))
533
+ (problems (gensym))
534
+ (single-var-p (= (length vars) 1)))
535
+ `(let ((,problems nil) (,total 0))
536
+ (loop for ,case in ,cases do
537
+ (incf ,total)
538
+ (destructuring-bind ,vars ,(if single-var-p `(list ,case) case)
539
+ (restart-case
540
+ (progn ,@body)
541
+ (ensure-failed (cond)
542
+ (push (list ,case cond) ,problems)))))
543
+ (if ,problems
544
+ (let ((condition (make-condition
545
+ 'ensure-cases-failure
546
+ :total ,total
547
+ :problems ,problems)))
548
+ (if (find-restart 'ensure-failed)
549
+ (invoke-restart 'ensure-failed condition)
550
+ (warn condition)))
551
+ ;; return true if we're happy
552
+ t))))
553
+
554
+
555
+ ;;; ---------------------------------------------------------------------------
556
+ ;;; test-mixin
557
+ ;;; ---------------------------------------------------------------------------
558
+
559
+ (defclass test-mixin ()
560
+ ((name :initform nil :initarg :name :accessor name :reader testsuite-name)
561
+ (run-setup :reader run-setup :initarg :run-setup)
562
+ (done-setup? :initform nil :reader done-setup?)
563
+ (done-dynamics? :initform nil :reader done-dynamics?)
564
+ (test-slot-names :initform nil :initarg :test-slot-names
565
+ :reader test-slot-names)
566
+ (current-step :initform :created :accessor current-step)
567
+ (current-method :initform nil :accessor current-method)
568
+ (save-equality-test :initform nil :reader save-equality-test)
569
+ (log-file :initform nil :initarg :log-file :reader log-file)
570
+ (test-data :initform nil :accessor test-data)
571
+ (expected-failure-p :initform nil :initarg :expected-failure-p
572
+ :reader expected-failure-p)
573
+ (expected-error-p :initform nil :initarg :expected-error-p
574
+ :reader expected-error-p)
575
+ (expected-problem-p :initform nil :initarg :expected-problem-p
576
+ :reader expected-problem-p)
577
+ (suite-initargs
578
+ :initform nil
579
+ :accessor suite-initargs)
580
+ (profile
581
+ :initform nil
582
+ :initarg :profile
583
+ :accessor profile))
584
+ (:documentation "A test suite")
585
+ (:default-initargs
586
+ :run-setup :once-per-test-case))
587
+
588
+ (defclass test-result ()
589
+ ((results-for :initform nil
590
+ :initarg :results-for
591
+ :accessor results-for)
592
+ (tests-run :initform nil :accessor tests-run)
593
+ (suites-run :initform nil :accessor suites-run)
594
+ (failures :initform nil :accessor failures)
595
+ (expected-failures :initform nil :accessor expected-failures)
596
+ (errors :initform nil :accessor errors)
597
+ (expected-errors :initform nil :accessor expected-errors)
598
+ (test-mode :initform :single :initarg :test-mode :accessor test-mode)
599
+ (test-interactive? :initform nil
600
+ :initarg :test-interactive? :accessor test-interactive?)
601
+ (real-start-time :initarg :real-start-time :reader real-start-time)
602
+ (start-time :accessor start-time :initform nil)
603
+ (end-time :accessor end-time)
604
+ (real-end-time :accessor real-end-time)
605
+ (real-start-time-universal
606
+ :initarg :real-start-time-universal :reader real-start-time-universal)
607
+ (start-time-universal :accessor start-time-universal :initform nil)
608
+ (end-time-universal :accessor end-time-universal)
609
+ (real-end-time-universal :accessor real-end-time-universal)
610
+ (properties :initform nil :accessor test-result-properties)
611
+ (tests-to-skip :initform nil
612
+ :initarg :tests-to-skip
613
+ :reader tests-to-skip
614
+ :writer %set-tests-to-skip))
615
+ (:documentation
616
+ "A `test-result` instance contains all of the information collectd by
617
+ LIFT during a test run.")
618
+ (:default-initargs
619
+ :test-interactive? *test-is-being-defined?*
620
+ :real-start-time (get-internal-real-time)
621
+ :real-start-time-universal (get-universal-time)
622
+ :tests-to-skip *lift-tests-to-skip*))
623
+
624
+ (defmethod initialize-instance :after
625
+ ((result test-result) &key tests-to-skip)
626
+ (when tests-to-skip
627
+ (%set-tests-to-skip
628
+ (mapcar (lambda (datum)
629
+ (cond ((or (atom datum)
630
+ (= (length datum) 1))
631
+ (cons (find-testsuite datum) nil))
632
+ ((= (length datum) 2)
633
+ (cons (find-testsuite (first datum))
634
+ (or (and (keywordp (second datum)) (second datum))
635
+ (find-test-case (find-testsuite (first datum))
636
+ (second datum)))))
637
+ (t
638
+ (warn "Unable to interpret skip datum ~a. Ignoring."
639
+ datum))))
640
+ tests-to-skip)
641
+ result)))
642
+
643
+ (defun test-result-property (result property &optional default)
644
+ (getf (test-result-properties result) property default))
645
+
646
+ (defun (setf test-result-property) (value result property)
647
+ (setf (getf (test-result-properties result) property) value))
648
+
649
+ (defun print-lift-message (message &rest args)
650
+ (apply #'format *lift-debug-output* message args)
651
+ (force-output *lift-debug-output*))
652
+
653
+ (defgeneric testsuite-setup (testsuite result)
654
+ (:documentation "Setup at the testsuite-level")
655
+ (:method ((testsuite test-mixin) (result test-result))
656
+ (values))
657
+ (:method :before ((testsuite test-mixin) (result test-result))
658
+ (when (and *test-print-testsuite-names*
659
+ (eq (test-mode result) :multiple))
660
+ (print-lift-message "~&Start: ~a" (type-of testsuite)))
661
+ (push (type-of testsuite) (suites-run result))
662
+ (setf (current-step testsuite) :testsuite-setup)))
663
+
664
+ (defgeneric testsuite-expects-error (testsuite)
665
+ (:documentation "Returns whether or not the testsuite as a whole expects an error.")
666
+ (:method ((testsuite test-mixin))
667
+ nil))
668
+
669
+ (defgeneric testsuite-expects-failure (testsuite)
670
+ (:documentation "Returns whether or not the testsuite as a whole expects to fail.")
671
+ (:method ((testsuite test-mixin))
672
+ nil))
673
+
674
+ (defgeneric testsuite-run (testsuite result)
675
+ (:documentation "Run the cases in this suite and it's children."))
676
+
677
+ (defgeneric testsuite-teardown (testsuite result)
678
+ (:documentation "Cleanup at the testsuite level.")
679
+ (:method ((testsuite test-mixin) (result test-result))
680
+ ;; no-op
681
+ )
682
+ (:method :after ((testsuite test-mixin) (result test-result))
683
+ (setf (current-step testsuite) :testsuite-teardown
684
+ (real-end-time result) (get-internal-real-time)
685
+ (real-end-time-universal result) (get-universal-time))))
686
+
687
+ (defgeneric setup-test (testsuite)
688
+ (:documentation "Setup for a test-case. By default it does nothing."))
689
+
690
+ (defgeneric test-case-teardown (testsuite result)
691
+ (:documentation "Tear-down a test-case. By default it does nothing.")
692
+ (:method-combination progn :most-specific-first))
693
+
694
+ (defgeneric testsuite-methods (testsuite)
695
+ (:documentation "Returns a list of the test methods defined for test. I.e.,
696
+ the methods that should be run to do the tests for this test."))
697
+
698
+ (defgeneric lift-test (suite name)
699
+ (:documentation ""))
700
+
701
+ (defgeneric do-testing (testsuite result fn)
702
+ (:documentation ""))
703
+
704
+ (defgeneric end-test (result case method-name)
705
+ (:documentation ""))
706
+
707
+ (defgeneric run-test-internal (suite name result &rest args)
708
+ (:documentation ""))
709
+
710
+ (defgeneric run-tests-internal (suite &rest args
711
+ &key &allow-other-keys)
712
+ (:documentation ""))
713
+
714
+ (defgeneric start-test (result case method-name)
715
+ (:documentation ""))
716
+
717
+ (defgeneric test-report-code (testsuite method)
718
+ (:documentation ""))
719
+
720
+ (defgeneric testsuite-p (thing)
721
+ (:documentation "Determine whether or not `thing` is a testsuite. Thing can be a symbol naming a suite, a subclass of `test-mixin` or an instance of a test suite. Returns nil if `thing` is not a testsuite and the symbol naming the suite if it is."))
722
+
723
+ (defgeneric testsuite-name->gf (case name)
724
+ (:documentation ""))
725
+
726
+ (defgeneric testsuite-name->method (class name)
727
+ (:documentation ""))
728
+
729
+ (defgeneric flet-test-function (testsuite function-name &rest args)
730
+ (:documentation ""))
731
+
732
+ (defgeneric equality-test (testsuite)
733
+ (:documentation ""))
734
+
735
+ (defgeneric do-testing-in-environment (testsuite result function)
736
+ (:documentation ""))
737
+
738
+ (defgeneric skip-test-case (result suite-name test-case-name)
739
+ )
740
+
741
+ (defgeneric describe-test-result (result stream &key &allow-other-keys)
742
+ )
743
+
744
+ (defgeneric write-profile-information (testsuite))
745
+
746
+ (defmethod write-profile-information ((suite t))
747
+ )
748
+
749
+ (defmethod equality-test ((suite test-mixin))
750
+ #'equal)
751
+
752
+ (defmethod setup-test :before ((test test-mixin))
753
+ (setf *test-scratchpad* nil
754
+ (current-step test) :test-setup))
755
+
756
+ (defmethod setup-test ((test test-mixin))
757
+ (values))
758
+
759
+ (defmethod test-case-teardown progn ((test test-mixin) (result test-result))
760
+ (values))
761
+
762
+ (defmethod test-case-teardown :around ((test test-mixin) (result test-result))
763
+ (setf (current-step test) :test-teardown)
764
+ (call-next-method))
765
+
766
+ (defmethod initialize-instance :after ((testsuite test-mixin) &rest initargs
767
+ &key &allow-other-keys)
768
+ (when (null (testsuite-name testsuite))
769
+ (setf (slot-value testsuite 'name)
770
+ (symbol-name (type-of testsuite))))
771
+ ;; FIXME - maybe remove LIFT standard arguments?
772
+ (setf (suite-initargs testsuite) initargs))
773
+
774
+ (defmethod print-object ((tc test-mixin) stream)
775
+ (print-unreadable-object (tc stream :identity t :type t)
776
+ (format stream "~a" (testsuite-name tc))))
777
+
778
+ ;;; ---------------------------------------------------------------------------
779
+ ;;; macros
780
+ ;;; ---------------------------------------------------------------------------
781
+
782
+ (defvar *current-definition* nil
783
+ "An associative-container which saves interesting information about
784
+ the thing being defined.")
785
+
786
+ (defun initialize-current-definition ()
787
+ (setf *current-definition* nil))
788
+
789
+ (defun set-definition (name value)
790
+ (let ((current (assoc name *current-definition*)))
791
+ (if current
792
+ (setf (cdr current) value)
793
+ (push (cons name value) *current-definition*)))
794
+
795
+ (values value))
796
+
797
+ (defun def (name &optional (definition *current-definition*))
798
+ (when definition (cdr (assoc name definition))))
799
+
800
+ (defun (setf def) (value name)
801
+ (set-definition name value))
802
+
803
+ (defvar *code-blocks* nil)
804
+
805
+ (defstruct (code-block (:type list) (:conc-name nil))
806
+ block-name (priority 0) filter code operate-when)
807
+
808
+ (defgeneric block-handler (name value)
809
+ (:documentation "")
810
+ (:method ((name t) (value t))
811
+ (error "Unknown clause: ~A" name)))
812
+
813
+ (defun add-code-block (name priority operate-when filter handler code)
814
+ (let ((current (assoc name *code-blocks*))
815
+ (value (make-code-block
816
+ :operate-when operate-when
817
+ :block-name name
818
+ :priority priority
819
+ :filter filter
820
+ :code code)))
821
+ (if current
822
+ (setf (cdr current) value)
823
+ (push (cons name value) *code-blocks*))
824
+ (eval
825
+ `(defmethod block-handler ((name (eql ',name)) value)
826
+ (declare (ignorable value))
827
+ ,@handler)))
828
+ (setf *code-blocks* (sort *code-blocks* #'<
829
+ :key (lambda (name.cb)
830
+ (priority (cdr name.cb))))))
831
+
832
+ (defmacro with-test-slots (&body body)
833
+ `(symbol-macrolet ((lift-result (getf (test-data *current-test*) :result)))
834
+ ;; case111 - LW complains otherwise
835
+ (declare (ignorable lift-result)
836
+ ,@(when (def :dynamic-variables)
837
+ `((special ,@(mapcar #'car (def :dynamic-variables))))))
838
+ (symbol-macrolet
839
+ ,(mapcar #'(lambda (local)
840
+ `(,local (test-environment-value ',local)))
841
+ (test-slots (def :testsuite-name)))
842
+ (declare (ignorable ,@(test-slots (def :testsuite-name))))
843
+ (macrolet
844
+ ,(mapcar (lambda (spec)
845
+ (destructuring-bind (name arglist) spec
846
+ `(,name ,arglist
847
+ `(flet-test-function
848
+ *current-test* ',',name ,,@arglist))))
849
+ (def :function-specs))
850
+ (progn ,@body)))))
851
+
852
+ (defvar *deftest-clauses*
853
+ '(:setup :teardown :test :documentation :tests :export-p :export-slots
854
+ :run-setup :dynamic-variables :equality-test :categories :function))
855
+
856
+ (defmacro deftest (testsuite-name superclasses slots &rest
857
+ clauses-and-options)
858
+ "The `deftest` form is obsolete, see [deftestsuite][]."
859
+
860
+ (warn "Deftest is obsolete, use deftestsuite instead.")
861
+ `(deftestsuite ,testsuite-name ,superclasses ,slots ,@clauses-and-options))
862
+
863
+ (setf *code-blocks* nil)
864
+
865
+ (add-code-block
866
+ :setup 1 :methods
867
+ (lambda ()
868
+ (or (def :setup) (def :direct-slot-names)))
869
+ '((setf (def :setup) (cleanup-parsed-parameter value)))
870
+ 'build-setup-test-method)
871
+
872
+ (add-code-block
873
+ :teardown 100 :methods
874
+ (lambda () (or (def :teardown) (def :direct-slot-names)))
875
+ '((setf (def :teardown) (cleanup-parsed-parameter value)))
876
+ 'build-test-teardown-method)
877
+
878
+ (add-code-block
879
+ :function 0 :methods
880
+ (lambda () (def :functions))
881
+ '((push value (def :functions)))
882
+ 'build-test-local-functions)
883
+
884
+ (add-code-block
885
+ :documentation 0 :class-def
886
+ nil
887
+ '((setf (def :documentation) (first value)))
888
+ nil)
889
+
890
+ (add-code-block
891
+ :export-p 0 :class-def
892
+ nil
893
+ '((setf (def :export-p) (first value)))
894
+ nil)
895
+
896
+ (add-code-block
897
+ :export-slots 0 :class-def
898
+ nil
899
+ '((setf (def :export-slots) (first value)))
900
+ nil)
901
+
902
+ (add-code-block
903
+ :run-setup 0 :class-def
904
+ nil
905
+ '((push (first value) (def :default-initargs))
906
+ (push :run-setup (def :default-initargs))
907
+ (setf (def :run-setup) (first value)))
908
+ 'check-run-setup-value)
909
+
910
+ (defun %valid-run-setup-values ()
911
+ '(:once-per-session :once-per-suite
912
+ :once-per-test-case :never))
913
+
914
+ (defun check-run-setup-value ()
915
+ (when (def :run-setup)
916
+ (unless (member (def :run-setup) (%valid-run-setup-values))
917
+ (error "The :run-setup option must be one of ~{~a~^, ~}."
918
+ (%valid-run-setup-values)))))
919
+
920
+ (add-code-block
921
+ :equality-test 0 :methods
922
+ (lambda () (def :equality-test))
923
+ '((setf (def :equality-test) (cleanup-parsed-parameter value)))
924
+ 'build-test-equality-test)
925
+
926
+ (add-code-block
927
+ :expected-error 0 :methods
928
+ (lambda () (def :expected-error))
929
+ '((setf (def :expected-error) (cleanup-parsed-parameter value)))
930
+ 'build-testsuite-expected-error)
931
+
932
+ (add-code-block
933
+ :expected-failure 0 :methods
934
+ (lambda () (def :expected-failure))
935
+ '((setf (def :expected-failure) (cleanup-parsed-parameter value)))
936
+ 'build-testsuite-expected-failure)
937
+
938
+ (add-code-block
939
+ :log-file 0 :class-def
940
+ nil
941
+ '((push (first value) (def :default-initargs))
942
+ (push :log-file (def :default-initargs)))
943
+ nil)
944
+
945
+ (add-code-block
946
+ :dynamic-variables 0 :class-def
947
+ nil
948
+ '((setf (def :direct-dynamic-variables) value))
949
+ nil)
950
+
951
+ (add-code-block
952
+ :categories 0 :class-def
953
+ nil
954
+ '((push value (def :categories)))
955
+ nil)
956
+
957
+ (add-code-block
958
+ :default-initargs 1 :class-def
959
+ (lambda () (def :default-initargs))
960
+ '((dolist (x (reverse (cleanup-parsed-parameter value)))
961
+ (push x (def :default-initargs))))
962
+ nil)
963
+
964
+ (defmacro deftestsuite (testsuite-name superclasses slots &rest
965
+ clauses-and-options)
966
+ "
967
+ Creates a testsuite named `testsuite-name` and, optionally, the code required for test setup, test tear-down and the actual test-cases. A testsuite is a collection of test-cases and other testsuites.
968
+
969
+ Test suites can have multiple superclasses (just like the classes that they are). Usually, these will be other test classes and the class hierarchy becomes the test case hierarchy. If necessary, however, non-testsuite classes can also be used as superclasses.
970
+
971
+ Slots are specified as in defclass with the following additions:
972
+
973
+ * Initargs and accessors are automatically defined. If a slot is named`my-slot`, then the initarg will be `:my-slot` and the accessors will be `my-slot` and `(setf my-slot)`.
974
+ * If the second argument is not a CLOS slot option keyword, then it will be used as the `:initform` for the slot. I.e., if you have
975
+
976
+ (deftestsuite my-test ()
977
+ ((my-slot 23)))
978
+
979
+ then `my-slot` will be initialized to 23 during test setup.
980
+
981
+ Test options are one of :setup, :teardown, :test, :tests, :documentation, :export-p, :dynamic-variables, :export-slots, :function, :categories, :run-setup, or :equality-test.
982
+
983
+ * :categories - a list of symbols. Categories allow you to groups tests into clusters outside of the basic hierarchy. This provides finer grained control on selecting which tests to run. May be specified multiple times.
984
+
985
+ * :documentation - a string specifying any documentation for the test. Should only be specified once.
986
+
987
+ * :dynamic-variables - a list of atoms or pairs of the form (name value). These specify any special variables that should be bound in a let around the body of the test. The name should be symbol designating a special variable. The value (if supplied) will be bound to the variable. If the value is not supplied, the variable will be bound to nil. Should only be specified once.
988
+
989
+ * :equality-test - the name of the function to be used by default in calls to ensure-same and ensure-different. Should only be supplied once.
990
+
991
+ * :export-p - If true, the testsuite name will be exported from the current package. Should only be specified once.
992
+
993
+ * :export-slots - if true, any slots specified in the test suite will be exported from the current package. Should only be specified once.
994
+
995
+ * :function - creates a locally accessible function for this test suite. May be specified multiple times.
996
+
997
+ * :run-setup - specify when to run the setup code for this test suite. Allowed values are
998
+
999
+ * :once-per-test-case or t (the default)
1000
+ * :once-per-session
1001
+ * :once-per-suite
1002
+ * :never or nil
1003
+
1004
+ :run-setup is handy when a testsuite has a time consuming setup phase that you do not want to repeat for every test.
1005
+
1006
+ * :setup - a list of forms to be evaluated before each test case is run. Should only be specified once.
1007
+
1008
+ * :teardown - a list of forms to be evaluated after each test case is run. Should only be specified once.
1009
+
1010
+ * :test - Define a single test case. Can be specified multiple times.
1011
+
1012
+ * :tests - Define multiple test cases for this test suite. Can be specified multiple times.
1013
+ "
1014
+ #+no-lift-tests
1015
+ `(values)
1016
+ #-no-lift-tests
1017
+ (let ((test-list nil)
1018
+ (options nil)
1019
+ (return (gensym)))
1020
+ ;; convert any clause like :setup foo into (:setup foo)
1021
+ (setf clauses-and-options
1022
+ (convert-clauses-into-lists clauses-and-options *deftest-clauses*))
1023
+ (initialize-current-definition)
1024
+ (setf (def :testsuite-name) testsuite-name)
1025
+ (setf (def :superclasses) (mapcar #'find-testsuite superclasses))
1026
+ (setf (def :deftestsuite) t)
1027
+ ;; parse clauses into defs
1028
+ (loop for clause in clauses-and-options do
1029
+ (typecase clause
1030
+ (symbol (pushnew clause options))
1031
+ (cons (destructuring-bind (kind &rest spec) clause
1032
+ (case kind
1033
+ (:test (push (first spec) test-list))
1034
+ (:tests
1035
+ (loop for test in spec do
1036
+ (push test test-list)))
1037
+ (t (block-handler kind spec)))))
1038
+ (t (error "When parsing ~S" clause))))
1039
+ (let ((slot-names nil) (slot-specs nil))
1040
+ (loop for slot in (if (listp slots) slots (list slots)) do
1041
+ (push (if (consp slot) (first slot) slot) slot-names)
1042
+ (push (parse-brief-slot slot) slot-specs))
1043
+ (setf (def :slot-specs) (nreverse slot-specs)
1044
+ (def :direct-slot-names) (nreverse slot-names)
1045
+ (def :slots-parsed) t))
1046
+ ;;?? issue 27: breaks 'encapsulation' of code-block mechanism
1047
+ (setf (def :function-specs)
1048
+ (loop for spec in (def :functions) collect
1049
+ (destructuring-bind (name arglist &body body) (first spec)
1050
+ (declare (ignore body))
1051
+ `(,name ,arglist))))
1052
+ ;;?? needed
1053
+ (empty-test-tables testsuite-name)
1054
+ (compute-superclass-inheritence)
1055
+ (prog2
1056
+ (setf *testsuite-test-count* 0)
1057
+ `(eval-when (:compile-toplevel :load-toplevel :execute)
1058
+ (eval-when (:compile-toplevel)
1059
+ (push ',return *test-is-being-compiled?*))
1060
+ (eval-when (:load-toplevel)
1061
+ (push ',return *test-is-being-loaded?*))
1062
+ (eval-when (:execute)
1063
+ (push ',return *test-is-being-executed?*))
1064
+ ;; remove previous methods (do this _before_ we define the class)
1065
+ (unless (or *test-is-being-compiled?*
1066
+ *test-is-being-loaded?*)
1067
+ #+(or)
1068
+ (print (list :cle *test-is-being-compiled?*
1069
+ *test-is-being-loaded?*
1070
+ *test-is-being-loaded?*))
1071
+ (remove-previous-definitions ',(def :testsuite-name)))
1072
+ ,(build-test-class)
1073
+ (unwind-protect
1074
+ (let ((*test-is-being-defined?* t))
1075
+ (setf *current-test-case-name* nil)
1076
+ (setf *current-testsuite-name* ',(def :testsuite-name)
1077
+ (test-slots ',(def :testsuite-name))
1078
+ ',(def :slot-names)
1079
+ (testsuite-dynamic-variables ',(def :testsuite-name))
1080
+ ',(def :dynamic-variables)
1081
+ ;;?? issue 27: breaks 'encapsulation' of code-block
1082
+ ;; mechanism
1083
+ (testsuite-function-specs ',(def :testsuite-name))
1084
+ ',(def :function-specs))
1085
+ ,@(when (def :export-p)
1086
+ `((export '(,(def :testsuite-name)))))
1087
+ ,@(when (def :export-slots?)
1088
+ `((export ',(def :direct-slot-names))))
1089
+ ;; make a place to save test-case information
1090
+ (empty-test-tables ',(def :testsuite-name))
1091
+ ;; create methods
1092
+ ;; setup :before
1093
+ ,@(loop for (nil . block) in *code-blocks*
1094
+ when (and block
1095
+ (code block)
1096
+ (eq (operate-when block) :methods)
1097
+ (or (not (filter block))
1098
+ (funcall (filter block)))) collect
1099
+ (funcall (code block)))
1100
+ ,@(when (def :dynamic-variables)
1101
+ `((defmethod do-testing :around
1102
+ ((suite ,(def :testsuite-name)) result fn)
1103
+ (declare (ignore result fn)
1104
+ (special
1105
+ ,@(mapcar
1106
+ #'car (def :dynamic-variables))))
1107
+ (cond ((done-dynamics? suite)
1108
+ (call-next-method))
1109
+ (t
1110
+ (setf (slot-value suite 'done-dynamics?) t)
1111
+ (let* (,@(def :dynamic-variables))
1112
+ (declare (special
1113
+ ,@(mapcar
1114
+ #'car (def :dynamic-variables))))
1115
+ (call-next-method)))))))
1116
+ ;; tests
1117
+ ,@(when test-list
1118
+ `((let ((*test-evaluate-when-defined?* nil))
1119
+ ,@(loop for test in (nreverse test-list) collect
1120
+ `(addtest (,(def :testsuite-name))
1121
+ ,@test))
1122
+ (setf *testsuite-test-count* nil))))
1123
+ ,(if (and test-list *test-evaluate-when-defined?*)
1124
+ `(unless (or *test-is-being-compiled?*
1125
+ *test-is-being-loaded?*)
1126
+ (let ((*test-break-on-errors?* *test-break-on-errors?*))
1127
+ (run-tests :suite ',testsuite-name)))
1128
+ `(find-class ',testsuite-name)))
1129
+ ;; cleanup
1130
+ (setf *test-is-being-compiled?*
1131
+ (remove ',return *test-is-being-compiled?*))
1132
+ (setf *test-is-being-loaded?*
1133
+ (remove ',return *test-is-being-loaded?*))
1134
+ (setf *test-is-being-executed?*
1135
+ (remove ',return *test-is-being-executed?*)))))))
1136
+
1137
+ (defun compute-superclass-inheritence ()
1138
+ ;;?? issue 27: break encapsulation of code blocks
1139
+ ;;?? we assume that we won't have too deep a hierarchy or too many
1140
+ ;; dv's or functions so that having lots of duplicate names is OK
1141
+ (let ((slots nil)
1142
+ (dynamic-variables nil)
1143
+ (function-specs nil))
1144
+ (dolist (super (def :superclasses))
1145
+ (cond ((find-testsuite super)
1146
+ (setf slots (append slots (test-slots super))
1147
+ dynamic-variables
1148
+ (append dynamic-variables
1149
+ (testsuite-dynamic-variables super))
1150
+ function-specs
1151
+ (append function-specs
1152
+ (testsuite-function-specs super))))
1153
+ (t
1154
+ (error 'testsuite-not-defined :testsuite-name super))))
1155
+ (setf (def :slot-names)
1156
+ (remove-duplicates (append (def :direct-slot-names) slots))
1157
+ (def :dynamic-variables)
1158
+ (remove-duplicates
1159
+ (append (%build-pairs (def :direct-dynamic-variables))
1160
+ dynamic-variables)
1161
+ :key #'car)
1162
+ (def :function-specs)
1163
+ (remove-duplicates
1164
+ (append (def :function-specs) function-specs)))
1165
+ (setf (def :superclasses)
1166
+ (loop for class in (def :superclasses)
1167
+ unless (some (lambda (oter)
1168
+ (and (not (eq class oter))
1169
+ (member class (superclasses oter))))
1170
+ (def :superclasses)) collect
1171
+ class))))
1172
+
1173
+ (defun %build-pairs (putative-pairs)
1174
+ (let ((result nil))
1175
+ (dolist (putative-pair putative-pairs)
1176
+ (if (atom putative-pair)
1177
+ (push (list putative-pair nil) result)
1178
+ (push putative-pair result)))
1179
+ (nreverse result)))
1180
+
1181
+ (defmacro addtest (name &body test)
1182
+ "Adds a single new test-case to the most recently defined testsuite."
1183
+ #+no-lift-tests
1184
+ `nil
1185
+ #-no-lift-tests
1186
+ (let ((body nil)
1187
+ (return (gensym))
1188
+ (options nil)
1189
+ (looks-like-suite-name (looks-like-suite-name-p name))
1190
+ (looks-like-code (looks-like-code-p name)))
1191
+ (cond ((and looks-like-suite-name looks-like-code)
1192
+ (error "Can't disambiguate suite name from possible code."))
1193
+ (looks-like-suite-name
1194
+ ;; testsuite given
1195
+ (setf (def :testsuite-name) (first name)
1196
+ options (rest name)
1197
+ name nil body test))
1198
+ (t
1199
+ ;; the 'name' is really part of the test...
1200
+ (setf body (cons name test))))
1201
+ (unless (def :testsuite-name)
1202
+ (when *current-testsuite-name*
1203
+ (setf (def :testsuite-name) *current-testsuite-name*)))
1204
+ (unless (def :testsuite-name)
1205
+ (signal-lift-error 'add-test +lift-no-current-test-class+))
1206
+ (unless (or (def :deftestsuite)
1207
+ (find-testsuite (def :testsuite-name)))
1208
+ (signal-lift-error 'add-test +lift-test-class-not-found+
1209
+ (def :testsuite-name)))
1210
+ `(eval-when (:compile-toplevel :load-toplevel :execute)
1211
+ (eval-when (:compile-toplevel)
1212
+ (push ',return *test-is-being-compiled?*))
1213
+ (eval-when (:load-toplevel)
1214
+ (push ',return *test-is-being-loaded?*))
1215
+ (eval-when (:execute)
1216
+ (push ',return *test-is-being-executed?*))
1217
+ (unwind-protect
1218
+ (let ((*test-is-being-defined?* t))
1219
+ (muffle-redefinition-warnings
1220
+ ,(build-test-test-method (def :testsuite-name) body options))
1221
+ (setf *current-testsuite-name* ',(def :testsuite-name))
1222
+ (if *test-evaluate-when-defined?*
1223
+ (unless (or *test-is-being-compiled?*
1224
+ *test-is-being-loaded?*)
1225
+ (let ((*test-break-on-errors?* (testing-interactively-p)))
1226
+ (run-test)))
1227
+ (values)))
1228
+ ;; cleanup
1229
+ (setf *test-is-being-compiled?*
1230
+ (remove ',return *test-is-being-compiled?*)
1231
+ *test-is-being-loaded?*
1232
+ (remove ',return *test-is-being-loaded?*)
1233
+ *test-is-being-executed?*
1234
+ (remove ',return *test-is-being-executed?*))))))
1235
+
1236
+ (defun looks-like-suite-name-p (form)
1237
+ (and (consp form)
1238
+ (atom (first form))
1239
+ (find-testsuite (first form))
1240
+ (property-list-p (rest form))))
1241
+
1242
+ (defun property-list-p (form)
1243
+ (and (listp form)
1244
+ (block check-it
1245
+ (let ((even? t))
1246
+ (loop for x in form
1247
+ for want-keyword? = t then (not want-keyword?) do
1248
+ (when (and want-keyword? (not (keywordp x)))
1249
+ (return-from check-it nil))
1250
+ (setf even? (not even?)))
1251
+ (return-from check-it even?)))))
1252
+
1253
+ #|
1254
+ (property-list-p '(:a :b))
1255
+ (property-list-p '(:a 2 :b 3 :c 5 :d 8))
1256
+ (property-list-p nil)
1257
+
1258
+ (property-list-p 3)
1259
+ (property-list-p '(3))
1260
+ (property-list-p '(3 :a))
1261
+ (property-list-p '(:a 3 :b))
1262
+ |#
1263
+
1264
+ (defun looks-like-code-p (name)
1265
+ (declare (ignore name))
1266
+ ;; FIXME - stub
1267
+ nil)
1268
+
1269
+ (defun remove-test (&key (test-case *current-test-case-name*)
1270
+ (suite *current-testsuite-name*))
1271
+ (assert suite nil "Test suite could not be determined.")
1272
+ (assert test-case nil "Test-case could not be determined.")
1273
+ (setf (testsuite-tests suite)
1274
+ (remove test-case (testsuite-tests suite))))
1275
+
1276
+ (defun run-test (&rest args
1277
+ &key (test-case *current-test-case-name*)
1278
+ (name test-case name-supplied-p)
1279
+ (suite *current-testsuite-name*)
1280
+ (break-on-errors? *test-break-on-errors?*)
1281
+ (break-on-failures? *test-break-on-failures?*)
1282
+ (do-children? *test-do-children?*)
1283
+ (result nil)
1284
+ (profile nil)
1285
+ (testsuite-initargs nil))
1286
+ "Run a single testcase in a test suite. Will run the most recently defined or run testcase unless the name and suite arguments are used to override them."
1287
+ (when name-supplied-p
1288
+ (setf test-case name))
1289
+ (assert suite nil "Test suite could not be determined.")
1290
+ (assert test-case nil "Test-case could not be determined.")
1291
+ (let ((args-copy (copy-list args)))
1292
+ (declare (ignore args-copy))
1293
+ (remf args :suite)
1294
+ (remf args :break-on-errors?)
1295
+ (remf args :break-on-failures?)
1296
+ (remf args :run-setup)
1297
+ (remf args :dribble)
1298
+ (remf args :config)
1299
+ (remf args :report-pathname)
1300
+ (remf args :do-children?)
1301
+ (remf args :tests-to-skip)
1302
+ (remf args :testsuite-initargs)
1303
+ (let* ((*test-break-on-errors?* break-on-errors?)
1304
+ (*test-break-on-failures?* break-on-failures?)
1305
+ (*test-do-children?* do-children?)
1306
+ (*current-test*
1307
+ (make-testsuite
1308
+ suite
1309
+ (if (find :profile testsuite-initargs)
1310
+ testsuite-initargs
1311
+ (setf testsuite-initargs
1312
+ `(:profile ,profile ,@testsuite-initargs))))))
1313
+ (unless result
1314
+ (setf result (make-test-result suite :single)))
1315
+ (prog1
1316
+ (let ((*current-test-case-name* (find-test-case suite test-case))
1317
+ (*current-testsuite-name* suite)
1318
+ (*test-result* result))
1319
+ (do-testing-in-environment
1320
+ *current-test* result
1321
+ (lambda ()
1322
+ (apply #'run-test-internal
1323
+ *current-test* *current-test-case-name* result nil))))
1324
+ (setf *test-result* result)
1325
+ (setf *current-test-case-name* (find-test-case suite test-case)
1326
+ *current-testsuite-name* suite)))))
1327
+
1328
+ (defun make-testsuite (suite-name args)
1329
+ (let ((testsuite (find-testsuite suite-name :errorp t)))
1330
+ (if testsuite
1331
+ (apply #'make-instance testsuite args)
1332
+ (error "Testsuite ~a not found." suite-name))))
1333
+
1334
+ (defmethod do-testing-in-environment :around ((suite test-mixin) result fn)
1335
+ (declare (ignore fn))
1336
+ (tagbody
1337
+ :test-start
1338
+ (restart-case
1339
+ (handler-bind ((warning #'muffle-warning)
1340
+ ; ignore warnings...
1341
+ (error
1342
+ (lambda (condition)
1343
+ (report-test-problem
1344
+ 'testsuite-error result suite
1345
+ *current-test-case-name* condition
1346
+ :backtrace (get-backtrace condition))
1347
+ (if *test-break-on-errors?*
1348
+ (invoke-debugger condition)
1349
+ (go :test-end)))))
1350
+ (unwind-protect
1351
+ (let ((*lift-equality-test* (equality-test suite)))
1352
+ (testsuite-setup suite result)
1353
+ (call-next-method)
1354
+ result)
1355
+ ;; cleanup
1356
+ (testsuite-teardown suite result)))
1357
+ (ensure-failed (condition)
1358
+ (report-test-problem
1359
+ 'testsuite-failure result suite
1360
+ *current-test-case-name* condition))
1361
+ (retry-test () :report "Retry the test."
1362
+ (go :test-start)))
1363
+ :test-end)
1364
+ (values result))
1365
+
1366
+ (defmethod do-testing-in-environment ((suite test-mixin) result fn)
1367
+ (do-testing suite result fn)
1368
+ (values result))
1369
+
1370
+ (defmethod do-testing ((suite test-mixin) result fn)
1371
+ (funcall fn)
1372
+ (values result))
1373
+
1374
+ (defmethod run-tests-internal ((suite symbol) &rest args
1375
+ &key &allow-other-keys)
1376
+ (let ((*current-test* (make-testsuite suite args))
1377
+ (passthrough-arguments nil))
1378
+ (loop for arg in '(:result :do-children?)
1379
+ when (getf args arg) do
1380
+ (push (getf args arg) passthrough-arguments)
1381
+ (push arg passthrough-arguments))
1382
+ (apply #'run-tests-internal *current-test* passthrough-arguments)))
1383
+
1384
+ (defmethod run-tests-internal
1385
+ ((case test-mixin) &key
1386
+ (result (make-test-result (class-of case) :multiple))
1387
+ (do-children? *test-do-children?*))
1388
+ (let ((*test-do-children?* do-children?))
1389
+ (do-testing-in-environment
1390
+ case result
1391
+ (lambda ()
1392
+ (testsuite-run case result)))
1393
+ (setf *test-result* result)))
1394
+
1395
+ (defun run-tests (&rest args &key
1396
+ (suite nil)
1397
+ (break-on-errors? *test-break-on-errors?*)
1398
+ (break-on-failures? *test-break-on-failures?*)
1399
+ (config nil)
1400
+ (dribble *lift-dribble-pathname*)
1401
+ (report-pathname t)
1402
+ (profile nil)
1403
+ ;(timeout nil)
1404
+ (do-children? *test-do-children?*)
1405
+ (testsuite-initargs nil)
1406
+ result
1407
+ &allow-other-keys)
1408
+ "Run all of the tests in a suite."
1409
+ (declare (ignore profile))
1410
+ (prog1
1411
+ (let ((args-copy (copy-list args)))
1412
+ (remf args :suite)
1413
+ (remf args :break-on-errors?)
1414
+ (remf args :break-on-failures?)
1415
+ (remf args :run-setup)
1416
+ (remf args :dribble)
1417
+ (remf args :config)
1418
+ (remf args :report-pathname)
1419
+ (remf args :do-children?)
1420
+ (remf args :tests-to-skip)
1421
+ (remf args :testsuite-initargs)
1422
+ (let* ((*lift-report-pathname*
1423
+ (cond ((null report-pathname) nil)
1424
+ ((eq report-pathname t)
1425
+ (report-summary-pathname))))
1426
+ (*test-do-children?* do-children?)
1427
+ (report-pathname *lift-report-pathname*))
1428
+ (when report-pathname
1429
+ (ensure-directories-exist report-pathname))
1430
+ (cond ((and suite config)
1431
+ (error "Specify either configuration file or test suite
1432
+ but not both."))
1433
+ (config
1434
+ (unless result
1435
+ (setf result
1436
+ (apply #'make-test-result config :multiple args)))
1437
+ (when report-pathname
1438
+ (write-report-header report-pathname result args-copy))
1439
+ (let* ((*test-result* result))
1440
+ (setf result (run-tests-from-file config))))
1441
+ ((or suite (setf suite *current-testsuite-name*))
1442
+ (unless result
1443
+ (setf result
1444
+ (apply #'make-test-result suite :multiple args)))
1445
+ (when report-pathname
1446
+ (write-report-header report-pathname result args-copy))
1447
+ (let* ((*test-break-on-errors?* break-on-errors?)
1448
+ (*test-break-on-failures?* break-on-failures?)
1449
+ (*test-result* result)
1450
+ (dribble-stream
1451
+ (when dribble
1452
+ (open dribble
1453
+ :direction :output
1454
+ :if-does-not-exist :create
1455
+ :if-exists *lift-if-dribble-exists*)))
1456
+ (*standard-output*
1457
+ (maybe-add-dribble
1458
+ *lift-standard-output* dribble-stream))
1459
+ (*error-output* (maybe-add-dribble
1460
+ *error-output* dribble-stream))
1461
+ (*debug-io* (maybe-add-dribble
1462
+ *debug-io* dribble-stream)))
1463
+ (unwind-protect
1464
+ (with-simple-restart (cancel-testing
1465
+ "Cancel testing of ~a"
1466
+ *current-testsuite-name*)
1467
+ (dolist (testsuite (if (consp suite)
1468
+ suite (list suite)))
1469
+ (let ((*current-testsuite-name* testsuite))
1470
+ (apply #'run-tests-internal testsuite
1471
+ :result result
1472
+ testsuite-initargs))
1473
+ (setf *current-testsuite-name* testsuite)))
1474
+ ;; cleanup
1475
+ (when dribble-stream
1476
+ (close dribble-stream)))
1477
+ ;; FIXME -- ugh!
1478
+ (setf (tests-run result) (reverse (tests-run result)))
1479
+ (when report-pathname
1480
+ (write-report-footer report-pathname result))
1481
+ (values result)))
1482
+ (t
1483
+ (error "There is not a current test suite and neither suite
1484
+ nor configuration file options were specified.")))))
1485
+ (setf *test-result* result)))
1486
+
1487
+ (defun maybe-add-dribble (stream dribble-stream)
1488
+ (if dribble-stream
1489
+ (values (make-broadcast-stream stream dribble-stream) t)
1490
+ (values stream nil)))
1491
+
1492
+ (defun skip-test-case-p (result suite-name test-case-name)
1493
+ (find-if (lambda (skip-datum)
1494
+ (and (eq suite-name (car skip-datum))
1495
+ (or (null (cdr skip-datum))
1496
+ (eq test-case-name (cdr skip-datum)))))
1497
+ (tests-to-skip result)))
1498
+
1499
+ (defmethod skip-test-case (result suite-name test-case-name)
1500
+ (declare (ignore result suite-name test-case-name))
1501
+ )
1502
+
1503
+ (defun skip-test-suite-children-p (result testsuite)
1504
+ (let ((suite-name (class-name (class-of testsuite))))
1505
+ (find-if (lambda (skip-datum)
1506
+ (and (eq suite-name (car skip-datum))
1507
+ (eq :including-children (cdr skip-datum))))
1508
+ (tests-to-skip result))))
1509
+
1510
+ (defmethod testsuite-run ((testsuite test-mixin) (result test-result))
1511
+ (unless (start-time result)
1512
+ (setf (start-time result) (get-internal-real-time)
1513
+ (start-time-universal result) (get-universal-time)))
1514
+ (unwind-protect
1515
+ (let* ((methods (testsuite-methods testsuite))
1516
+ (suite-name (class-name (class-of testsuite)))
1517
+ (*current-testsuite-name* suite-name))
1518
+ (loop for method in methods do
1519
+ (if (skip-test-case-p result suite-name method)
1520
+ (skip-test-case result suite-name method)
1521
+ (run-test-internal testsuite method result)))
1522
+ (when (and *test-do-children?*
1523
+ (not (skip-test-suite-children-p result testsuite)))
1524
+ (loop for subclass in (direct-subclasses (class-of testsuite))
1525
+ when (and (testsuite-p subclass)
1526
+ (not (member (class-name subclass)
1527
+ (suites-run result)))) do
1528
+ (run-tests-internal (class-name subclass)
1529
+ :result result))))
1530
+ (setf (end-time result) (get-universal-time))))
1531
+
1532
+ (defmethod run-test-internal ((suite symbol) (name symbol) result
1533
+ &rest args &key &allow-other-keys)
1534
+ (let ((*current-test* (make-testsuite suite args))
1535
+ (passthrough-arguments nil))
1536
+ (loop for arg in '(:result :do-children?)
1537
+ when (getf args arg) do
1538
+ (push (getf args arg) passthrough-arguments)
1539
+ (push arg passthrough-arguments))
1540
+ (apply #'run-test-internal
1541
+ *current-test* name result passthrough-arguments)))
1542
+
1543
+ (defmethod run-test-internal ((suite test-mixin) (name symbol) result
1544
+ &rest _)
1545
+ (declare (ignore _))
1546
+ (let ((result-pushed? nil)
1547
+ (*current-test-case-name* name)
1548
+ (error nil))
1549
+ (flet ((maybe-push-result ()
1550
+ ;(print (list :mpr result-pushed? (test-data suite)))
1551
+ (let ((datum (list (type-of suite)
1552
+ *current-test-case-name* (test-data suite))))
1553
+ (cond ((null result-pushed?)
1554
+ (setf result-pushed? t)
1555
+ (push datum (tests-run result)))
1556
+ (t
1557
+ ;; replace
1558
+ (setf (first (tests-run result)) datum))))))
1559
+ (when (and *test-print-test-case-names*
1560
+ (eq (test-mode result) :multiple))
1561
+ (print-lift-message "~& run: ~a" name))
1562
+ (tagbody
1563
+ :test-start
1564
+ (restart-case
1565
+ (handler-bind ((warning #'muffle-warning)
1566
+ ; ignore warnings...
1567
+ (error
1568
+ (lambda (condition)
1569
+ (report-test-problem
1570
+ 'test-error result suite
1571
+ *current-test-case-name* condition
1572
+ :backtrace (get-backtrace condition))
1573
+ (if (and *test-break-on-errors?*
1574
+ (not (testcase-expects-error-p)))
1575
+ (invoke-debugger condition)
1576
+ (go :test-end)))))
1577
+ (setf (current-method suite) name)
1578
+ (start-test result suite name)
1579
+ (unwind-protect
1580
+ (progn
1581
+ (setup-test suite)
1582
+ (setf (current-step suite) :testing)
1583
+ (multiple-value-bind (result measures error-condition)
1584
+ (while-measuring (t measure-space measure-seconds)
1585
+ (lift-test suite name))
1586
+ (declare (ignore result))
1587
+ (setf error error-condition)
1588
+ (destructuring-bind (space seconds) measures
1589
+ (setf (getf (test-data suite) :seconds) seconds
1590
+ (getf (test-data suite) :conses) space)))
1591
+ (when error
1592
+ (error error))
1593
+ (check-for-surprises suite))
1594
+ ;; cleanup
1595
+ (maybe-push-result)
1596
+ (test-case-teardown suite result)
1597
+ (end-test result suite name)))
1598
+ (ensure-failed (condition)
1599
+ (report-test-problem
1600
+ 'test-failure result suite
1601
+ *current-test-case-name* condition)
1602
+ (if (and *test-break-on-failures?*
1603
+ (not (testcase-expects-failure-p)))
1604
+ (invoke-debugger condition)
1605
+ (go :test-end)))
1606
+ (retry-test () :report "Retry the test."
1607
+ (go :test-start)))
1608
+ :test-end)
1609
+ (maybe-push-result))
1610
+ (when *lift-report-pathname*
1611
+ (let ((current (first (tests-run result))))
1612
+ (summarize-single-test
1613
+ :save (first current) (second current) (third current)
1614
+ :stream *lift-report-pathname*))))
1615
+ (setf *current-test-case-name* name
1616
+ *test-result* result))
1617
+
1618
+ (defun testcase-expects-error-p (&optional (test *current-test*))
1619
+ (let* ((options (getf (test-data test) :options)))
1620
+ (or (testsuite-expects-error test)
1621
+ (second (member :expected-error options)))))
1622
+
1623
+ (defun testcase-expects-failure-p (&optional (test *current-test*))
1624
+ (let* ((options (getf (test-data test) :options)))
1625
+ (or (testsuite-expects-failure test)
1626
+ (second (member :expected-failure options)))))
1627
+
1628
+ (defun testcase-expects-problem-p (&optional (test *current-test*))
1629
+ (let* ((options (getf (test-data test) :options)))
1630
+ (second (member :expected-problem options))))
1631
+
1632
+ (defun check-for-surprises (testsuite)
1633
+ (let* ((expected-failure-p (testcase-expects-failure-p testsuite))
1634
+ (expected-error-p (testcase-expects-error-p testsuite))
1635
+ (expected-problem-p (testcase-expects-problem-p testsuite))
1636
+ (condition nil))
1637
+ (cond
1638
+ (expected-failure-p
1639
+ (setf (slot-value testsuite 'expected-failure-p) expected-failure-p))
1640
+ (expected-error-p
1641
+ (setf (slot-value testsuite 'expected-error-p) expected-error-p))
1642
+ (expected-problem-p
1643
+ (setf (slot-value testsuite 'expected-problem-p) expected-problem-p)))
1644
+ (cond
1645
+ ((expected-failure-p testsuite)
1646
+ (setf condition
1647
+ (make-condition 'unexpected-success-failure
1648
+ :expected :failure
1649
+ :expected-more (expected-failure-p testsuite))))
1650
+ ((expected-error-p testsuite)
1651
+ (setf condition
1652
+ (make-condition 'unexpected-success-failure
1653
+ :expected :error
1654
+ :expected-more (expected-error-p testsuite))))
1655
+ ((expected-problem-p testsuite)
1656
+ (setf condition
1657
+ (make-condition 'unexpected-success-failure
1658
+ :expected :problem
1659
+ :expected-more (expected-problem-p testsuite)))))
1660
+ (when condition
1661
+ (if (find-restart 'ensure-failed)
1662
+ (invoke-restart 'ensure-failed condition)
1663
+ (warn condition)))))
1664
+
1665
+ (defun report-test-problem (problem-type result suite method condition
1666
+ &rest args)
1667
+ ;; ick
1668
+ (let ((docs nil)
1669
+ (option nil))
1670
+ (declare (ignorable docs option))
1671
+ (cond ((and (eq problem-type 'test-failure)
1672
+ (not (typep condition 'unexpected-success-failure))
1673
+ (testcase-expects-failure-p suite))
1674
+ (setf problem-type 'test-expected-failure
1675
+ option :expected-failure))
1676
+ ((and (eq problem-type 'test-error)
1677
+ (testcase-expects-error-p suite))
1678
+ (setf problem-type 'test-expected-error
1679
+ option :expected-error))
1680
+ ((and (or (eq problem-type 'test-failure)
1681
+ (eq problem-type 'test-error))
1682
+ (testcase-expects-problem-p suite))
1683
+ (setf problem-type (or (and (eq problem-type 'test-failure)
1684
+ 'test-expected-failure)
1685
+ (and (eq problem-type 'test-error)
1686
+ 'test-expected-error))
1687
+ option :expected-problem)))
1688
+ (let ((problem (apply #'make-instance problem-type
1689
+ :testsuite suite
1690
+ :test-method method
1691
+ :test-condition condition
1692
+ :test-step (current-step suite) args)))
1693
+ (setf (getf (test-data suite) :problem) problem)
1694
+ (etypecase problem
1695
+ ((or test-failure testsuite-failure) (push problem (failures result)))
1696
+ (test-expected-failure (push problem (expected-failures result)))
1697
+ ((or test-error testsuite-error) (push problem (errors result)))
1698
+ (test-expected-error (push problem (expected-errors result))))
1699
+ (when (and *test-maximum-failure-count*
1700
+ (numberp *test-maximum-failure-count*)
1701
+ (>= (length (failures result)) *test-maximum-failure-count*))
1702
+ (cancel-testing :failures))
1703
+ (when (and *test-maximum-error-count*
1704
+ (numberp *test-maximum-error-count*)
1705
+ (>= (length (errors result)) *test-maximum-error-count*))
1706
+ (cancel-testing :errors))
1707
+ problem)))
1708
+
1709
+ (defun cancel-testing (why)
1710
+ (declare (ignore why))
1711
+ (flet ((do-it (name)
1712
+ ;; should just use find-restart but I was experimenting
1713
+ (let* ((restarts (compute-restarts))
1714
+ (it (find name restarts :key #'restart-name :from-end nil)))
1715
+ (when it
1716
+ (invoke-restart it)))))
1717
+ (do-it 'cancel-testing-from-configuration)
1718
+ (do-it 'cancel-testing)))
1719
+
1720
+ ;;; ---------------------------------------------------------------------------
1721
+ ;;; test-result and printing
1722
+ ;;; ---------------------------------------------------------------------------
1723
+
1724
+ (defun get-test-print-length ()
1725
+ (let ((foo *test-print-length*))
1726
+ (if (eq foo :follow-print) *print-length* foo)))
1727
+
1728
+ (defun get-test-print-level ()
1729
+ (let ((foo *test-print-level*))
1730
+ (if (eq foo :follow-print) *print-level* foo)))
1731
+
1732
+ (defmethod start-test ((result test-result) (suite test-mixin) name)
1733
+ (declare (ignore name))
1734
+ (setf (current-step suite) :start-test
1735
+ (test-data suite)
1736
+ `(:start-time ,(get-internal-real-time)
1737
+ :start-time-universal ,(get-universal-time))))
1738
+
1739
+ (defmethod end-test ((result test-result) (suite test-mixin) name)
1740
+ (declare (ignore name))
1741
+ (setf (current-step suite) :end-test
1742
+ (getf (test-data suite) :end-time) (get-internal-real-time)
1743
+ (end-time result) (get-internal-real-time)
1744
+ (getf (test-data suite) :end-time-universal) (get-universal-time)
1745
+ (end-time-universal result) (get-universal-time)))
1746
+
1747
+ (defun make-test-result (for test-mode &rest args)
1748
+ (apply #'make-instance 'test-result
1749
+ :results-for for
1750
+ :test-mode test-mode
1751
+ args))
1752
+
1753
+ (defun testing-interactively-p ()
1754
+ (values nil))
1755
+
1756
+ (defmethod print-object ((tr test-result) stream)
1757
+ (let ((complete-success? (and (null (errors tr))
1758
+ (null (failures tr))
1759
+ (null (expected-failures tr))
1760
+ (null (expected-errors tr)))))
1761
+ (let* ((*print-level* (get-test-print-level))
1762
+ (*print-length* (get-test-print-length))
1763
+ (non-failure-failures
1764
+ (count-if
1765
+ (lambda (failure)
1766
+ (member (class-of (test-condition failure))
1767
+ (subclasses 'unexpected-success-failure :proper? nil)))
1768
+ (expected-failures tr)))
1769
+ (expected-failures (- (length (expected-failures tr))
1770
+ non-failure-failures)))
1771
+ (print-unreadable-object (tr stream)
1772
+ (cond ((and (null (tests-run tr)) complete-success?)
1773
+ (format stream "~A: no tests defined" (results-for tr)))
1774
+ ((eq (test-mode tr) :single)
1775
+ (cond ((test-interactive? tr)
1776
+ ;; interactive
1777
+ (cond (complete-success?
1778
+ (format stream "Test passed"))
1779
+ ((errors tr)
1780
+ (format stream "Error during testing"))
1781
+ ((expected-errors tr)
1782
+ (format stream "Expected error during testing"))
1783
+ ((failures tr)
1784
+ (format stream "Test failed"))
1785
+ ((plusp non-failure-failures)
1786
+ (format stream "Test succeeded unexpectedly"))
1787
+ (t
1788
+ (format stream "Test failed expectedly"))))
1789
+ (t
1790
+ ;; from run-test
1791
+ (format stream "~A.~A ~A"
1792
+ (results-for tr)
1793
+ (first (first (tests-run tr)))
1794
+ (cond (complete-success?
1795
+ "passed")
1796
+ ((errors tr)
1797
+ "Error")
1798
+ (t
1799
+ "failed")))
1800
+ (when (or (expected-errors tr) (expected-failures tr))
1801
+ (format stream "(~[~:;, ~:*~A expected failure~:P~]~[~:;, ~:*~A succeeded unexpectedly~]~[~:;, ~:*~A expected error~:P~])"
1802
+ expected-failures non-failure-failures
1803
+ (expected-errors tr))))))
1804
+ (t
1805
+ ;; multiple tests run
1806
+ (format stream "Results for ~A " (results-for tr))
1807
+ (if complete-success?
1808
+ (format stream "[~A Successful test~:P]"
1809
+ (length (tests-run tr)))
1810
+ (format stream "~A Test~:P~[~:;, ~:*~A Failure~:P~]~[~:;, ~:*~A Error~:P~]~[~:;, ~:*~A Expected failure~:P~]~[~:;, ~:*~A Expected error~:P~]"
1811
+ (length (tests-run tr))
1812
+ (length (failures tr))
1813
+ (length (errors tr))
1814
+ (length (expected-failures tr))
1815
+ (length (expected-errors tr))))))
1816
+ ;; note that suites with no tests think that they are completely
1817
+ ;; successful. Optimistic little buggers, huh?
1818
+ (when (and (not complete-success?) *test-describe-if-not-successful?*)
1819
+ (format stream "~%")
1820
+ (print-test-result-details stream tr t t))))))
1821
+
1822
+ (defmethod describe-object ((result test-result) stream)
1823
+ (describe-test-result result stream))
1824
+
1825
+ (defmethod describe-test-result (result stream
1826
+ &key
1827
+ (show-details-p *test-show-details-p*)
1828
+ (show-expected-p *test-show-expected-p*)
1829
+ (show-code-p *test-show-code-p*))
1830
+ (let* ((number-of-failures (length (failures result)))
1831
+ (number-of-errors (length (errors result)))
1832
+ (number-of-expected-errors (length (expected-errors result)))
1833
+ (non-failure-failures
1834
+ (count-if
1835
+ (lambda (failure)
1836
+ (member (class-of (test-condition failure))
1837
+ (subclasses 'unexpected-success-failure :proper? nil)))
1838
+ (expected-failures result)))
1839
+ (number-of-expected-failures (- (length (expected-failures result))
1840
+ non-failure-failures))
1841
+ (*print-level* (get-test-print-level))
1842
+ (*print-length* (get-test-print-length)))
1843
+ (unless *test-is-being-defined?*
1844
+ (print-test-summary result stream)
1845
+ (when (and show-details-p
1846
+ (or (plusp number-of-failures)
1847
+ (plusp number-of-expected-failures)
1848
+ (plusp number-of-errors)
1849
+ (plusp number-of-expected-errors)))
1850
+ (format stream "~%~%")
1851
+ (print-test-result-details
1852
+ stream result show-expected-p show-code-p)
1853
+ (print-test-summary result stream)))))
1854
+
1855
+ (defun print-test-summary (result stream)
1856
+ (let* ((number-of-failures (length (failures result)))
1857
+ (number-of-errors (length (errors result)))
1858
+ (number-of-expected-errors (length (expected-errors result)))
1859
+ (non-failure-failures
1860
+ (count-if
1861
+ (lambda (failure)
1862
+ (member (class-of (test-condition failure))
1863
+ (subclasses 'unexpected-success-failure :proper? nil)))
1864
+ (expected-failures result)))
1865
+ (number-of-expected-failures (- (length (expected-failures result))
1866
+ non-failure-failures)))
1867
+ (format stream "~&Test Report for ~A: ~D test~:P run"
1868
+ (results-for result) (length (tests-run result)))
1869
+ (cond ((or (failures result) (errors result)
1870
+ (expected-failures result) (expected-errors result))
1871
+ (format stream "~[~:;, ~:*~A Error~:P~]~[~:;, ~:*~A Failure~:P~]~[~:;, ~:*~A Expected error~:P~]~[~:;, ~:*~A Expected failure~:P~]~[~:;, ~:*~A Successful Surprise~:P~]."
1872
+ number-of-errors
1873
+ number-of-failures
1874
+ number-of-expected-errors
1875
+ number-of-expected-failures
1876
+ non-failure-failures))
1877
+ ((or (expected-failures result) (expected-errors result))
1878
+ (format stream ", all passed *~[~:;, ~:*~A Expected error~:P~]~[~:;, ~:*~A Expected failure~:P~])."
1879
+ number-of-expected-errors
1880
+ number-of-expected-failures))
1881
+ (t
1882
+ (format stream ", all passed!")))))
1883
+
1884
+ (defun print-test-result-details (stream result show-expected-p show-code-p)
1885
+ (loop for report in (errors result) do
1886
+ (print-test-problem "ERROR : " report stream
1887
+ show-code-p))
1888
+ (loop for report in (failures result) do
1889
+ (print-test-problem "Failure: " report stream
1890
+ show-code-p))
1891
+ (when show-expected-p
1892
+ (loop for report in (expected-failures result) do
1893
+ (print-test-problem "Expected failure: " report stream
1894
+ show-code-p))
1895
+ (loop for report in (expected-errors result) do
1896
+ (print-test-problem "Expected Error : " report stream
1897
+ show-code-p))))
1898
+
1899
+ (defun print-test-problem (prefix report stream show-code-p)
1900
+ (let* ((suite (testsuite report))
1901
+ (method (test-method report))
1902
+ (condition (test-condition report))
1903
+ (code (test-report-code suite method))
1904
+ (testsuite-name method)
1905
+ (*print-level* (get-test-print-level))
1906
+ (*print-length* (get-test-print-length)))
1907
+ (let ((*package* (symbol-package method)))
1908
+ (format stream "~&~A~(~A : ~A~)" prefix (type-of suite) testsuite-name)
1909
+ (let ((doc-string (gethash testsuite-name
1910
+ (test-case-documentation
1911
+ (class-name (class-of suite))))))
1912
+ (when doc-string
1913
+ (format stream "~&~A" doc-string)))
1914
+ (if show-code-p
1915
+ (setf code (with-output-to-string (out)
1916
+ (pprint code out)))
1917
+ (setf code nil))
1918
+ (format stream "~&~< ~@;~
1919
+ ~@[Condition: ~<~@;~A~:>~]~
1920
+ ~@[~&Code : ~a~]~
1921
+ ~&~:>" (list (list condition) code)))))
1922
+
1923
+
1924
+ ;;; ---------------------------------------------------------------------------
1925
+ ;;; test-reports
1926
+ ;;; ---------------------------------------------------------------------------
1927
+
1928
+ (defclass test-problem-mixin ()
1929
+ ((testsuite :initform nil :initarg :testsuite :reader testsuite)
1930
+ (test-method :initform nil :initarg :test-method :reader test-method)
1931
+ (test-condition :initform nil
1932
+ :initarg :test-condition
1933
+ :reader test-condition)
1934
+ (test-problem-kind :reader test-problem-kind :allocation :class)
1935
+ (test-step :initform nil :initarg :test-step :reader test-step)))
1936
+
1937
+ (defmethod print-object ((problem test-problem-mixin) stream)
1938
+ (print-unreadable-object (problem stream)
1939
+ (format stream "TEST-~@:(~A~): ~A in ~A"
1940
+ (test-problem-kind problem)
1941
+ (name (testsuite problem))
1942
+ (test-method problem))))
1943
+
1944
+ (defclass generic-problem (test-problem-mixin)
1945
+ ((test-problem-kind :initarg :test-problem-kind
1946
+ :allocation :class)))
1947
+
1948
+ (defclass expected-problem-mixin ()
1949
+ ((documentation :initform nil
1950
+ :initarg :documentation
1951
+ :accessor failure-documentation)))
1952
+
1953
+ (defclass test-expected-failure (expected-problem-mixin generic-problem)
1954
+ ()
1955
+ (:default-initargs
1956
+ :test-problem-kind "Expected failure"))
1957
+
1958
+ (defclass test-failure (generic-problem)
1959
+ ()
1960
+ (:default-initargs
1961
+ :test-problem-kind "failure"))
1962
+
1963
+ (defclass test-error-mixin (generic-problem)
1964
+ ((backtrace :initform nil :initarg :backtrace :reader backtrace)))
1965
+
1966
+ (defclass test-expected-error (expected-problem-mixin test-error-mixin)
1967
+ ()
1968
+ (:default-initargs
1969
+ :test-problem-kind "Expected error"))
1970
+
1971
+ (defclass test-error (test-error-mixin)
1972
+ ()
1973
+ (:default-initargs
1974
+ :test-problem-kind "Error"))
1975
+
1976
+ (defclass testsuite-error (test-error-mixin)
1977
+ ()
1978
+ (:default-initargs
1979
+ :test-problem-kind "Testsuite error"))
1980
+
1981
+ (defclass testsuite-failure (generic-problem)
1982
+ ()
1983
+ (:default-initargs
1984
+ :test-problem-kind "Testsuite failure"))
1985
+
1986
+ (defmethod test-report-code ((testsuite test-mixin) (method symbol))
1987
+ (let* ((class-name (class-name (class-of testsuite))))
1988
+ (gethash method
1989
+ (test-name->code-table class-name))))
1990
+
1991
+ ;;; ---------------------------------------------------------------------------
1992
+ ;;; utilities
1993
+ ;;; ---------------------------------------------------------------------------
1994
+
1995
+ (defun remove-test-methods (test-name)
1996
+ (prog1
1997
+ (length (testsuite-tests test-name))
1998
+ (setf (testsuite-tests test-name) nil)))
1999
+
2000
+ (defun remove-previous-definitions (classname)
2001
+ "Remove the methods of this class and all its subclasses."
2002
+ (let ((classes-removed nil)
2003
+ (class (find-class classname nil))
2004
+ (removed-count 0))
2005
+ (when class
2006
+ (loop for subclass in (subclasses class :proper? nil) do
2007
+ (push subclass classes-removed)
2008
+ (incf removed-count
2009
+ (remove-test-methods (class-name subclass)))
2010
+ #+Ignore
2011
+ ;;?? causing more trouble than it solves...??
2012
+ (setf (find-class (class-name subclass)) nil))
2013
+
2014
+ (unless (length-1-list-p classes-removed)
2015
+ (format *debug-io*
2016
+ "~&;;; Removed Test suite ~(~A~) and its subclasses (~{~<~s~>~^, ~})."
2017
+ classname (sort
2018
+ (delete classname
2019
+ (mapcar #'class-name classes-removed))
2020
+ #'string-lessp)))
2021
+ (unless (zerop removed-count)
2022
+ (format *debug-io*
2023
+ "~&;;; Removed ~D methods from test suite ~(~A~)~@[ and its subclasses~]."
2024
+ removed-count classname
2025
+ (not (length-1-list-p classes-removed)))))))
2026
+
2027
+ (defun (setf test-environment-value) (value name)
2028
+ (push (cons name value) *test-environment*)
2029
+ (values value))
2030
+
2031
+ (defun test-environment-value (name)
2032
+ (cdr (assoc name *test-environment*)))
2033
+
2034
+ (defun remove-from-test-environment (name)
2035
+ (setf *test-environment*
2036
+ (remove name *test-environment* :key #'car :count 1)))
2037
+
2038
+ (defun build-test-local-functions ()
2039
+ `(progn
2040
+ ,@(mapcar
2041
+ (lambda (function-spec)
2042
+ (destructuring-bind (name arglist &body body) (first function-spec)
2043
+ `(defmethod flet-test-function ((testsuite ,(def :testsuite-name))
2044
+ (function-name (eql ',name))
2045
+ &rest args)
2046
+ (with-test-slots
2047
+ ,(if arglist
2048
+ `(destructuring-bind ,arglist args
2049
+ ,@body)
2050
+ `(progn ,@body))))))
2051
+ (def :functions))))
2052
+
2053
+ (defun build-test-equality-test ()
2054
+ (let ((test-name (def :testsuite-name))
2055
+ (equality-test (def :equality-test)))
2056
+ `(progn
2057
+ (defmethod equality-test ((testsuite ,test-name))
2058
+ ,equality-test))))
2059
+
2060
+ (defun build-testsuite-expected-error ()
2061
+ (let ((test-name (def :testsuite-name))
2062
+ (expected-error (def :expected-error)))
2063
+ `(progn
2064
+ (defmethod testsuite-expects-error ((testsuite ,test-name))
2065
+ (with-test-slots
2066
+ ,expected-error)))))
2067
+
2068
+ (defun build-testsuite-expected-failure ()
2069
+ (let ((test-name (def :testsuite-name))
2070
+ (expected-failure (def :expected-failure)))
2071
+ `(progn
2072
+ (defmethod testsuite-expects-failure ((testsuite ,test-name))
2073
+ (with-test-slots
2074
+ ,expected-failure)))))
2075
+
2076
+ (defun build-test-teardown-method ()
2077
+ (let ((test-name (def :testsuite-name))
2078
+ (slot-names (def :direct-slot-names))
2079
+ (teardown (def :teardown)))
2080
+ (when teardown
2081
+ (unless (consp teardown)
2082
+ (setf teardown (list teardown)))
2083
+ (when (length-1-list-p teardown)
2084
+ (setf teardown (list teardown)))
2085
+ (when (symbolp (first teardown))
2086
+ (setf teardown (list teardown))))
2087
+ (let* ((teardown-code `(,@(when teardown
2088
+ `((with-test-slots ,@teardown)))))
2089
+ (test-code `(,@teardown-code
2090
+ ,@(mapcar (lambda (slot)
2091
+ `(remove-from-test-environment ',slot))
2092
+ slot-names))))
2093
+ `(progn
2094
+ ,@(when teardown-code
2095
+ `((defmethod test-case-teardown progn ((testsuite ,test-name)
2096
+ (result test-result))
2097
+ (when (run-teardown-p testsuite :test-case)
2098
+ ,@test-code))))
2099
+ ,@(when teardown-code
2100
+ `((defmethod testsuite-teardown ((testsuite ,test-name)
2101
+ (result test-result))
2102
+ (when (run-teardown-p testsuite :testsuite)
2103
+ ,@test-code))))))))
2104
+
2105
+ (defun build-setup-test-method ()
2106
+ (let ((test-name (def :testsuite-name))
2107
+ (setup (def :setup)))
2108
+ ;;?? ewww, this smells bad
2109
+ (when setup
2110
+ (unless (consp setup)
2111
+ (setf setup (list setup)))
2112
+ (when (length-1-list-p setup)
2113
+ (setf setup (list setup)))
2114
+ (when (symbolp (first setup))
2115
+ (setf setup (list setup))))
2116
+ (let ((ginitargs (gensym "initargs-")))
2117
+ (multiple-value-bind (slots initforms)
2118
+ (%gather-up-initforms)
2119
+ (when (or setup slots)
2120
+ `(progn
2121
+ (defmethod setup-test :after ((testsuite ,test-name))
2122
+ (with-test-slots
2123
+ ,@(when slots
2124
+ `((let ((,ginitargs (suite-initargs testsuite)))
2125
+ ,@(loop for slot-name in slots
2126
+ for initform in initforms
2127
+ for keyword = (intern (symbol-name slot-name)
2128
+ :keyword)
2129
+ collect
2130
+ `(setf (test-environment-value ',slot-name)
2131
+ (or (getf ,ginitargs ,keyword)
2132
+ ,initform))))))
2133
+ ,@setup))))))))
2134
+
2135
+ (defun %gather-up-initforms ()
2136
+ (let ((initforms nil)
2137
+ (slot-names nil)
2138
+ (slot-specs (def :slot-specs)))
2139
+ (loop for slot in (def :direct-slot-names)
2140
+ for spec = (assoc slot slot-specs) do
2141
+ (push (getf (rest spec) :initform) initforms)
2142
+ (push (first spec) slot-names))
2143
+ (values (nreverse slot-names) (nreverse initforms))))
2144
+
2145
+ (defmethod setup-test :around ((test test-mixin))
2146
+ (when (run-setup-p test)
2147
+ (call-next-method)
2148
+ (setf (slot-value test 'done-setup?) t)))
2149
+
2150
+ (defun run-setup-p (testsuite)
2151
+ (case (run-setup testsuite)
2152
+ (:once-per-session (error "not implemented"))
2153
+ (:once-per-suite (not (done-setup? testsuite)))
2154
+ ((:once-per-test-case t) t)
2155
+ ((:never nil) nil)
2156
+ (t (error "Don't know about ~s for run-setup" (run-setup testsuite)))))
2157
+
2158
+ (defun run-teardown-p (testsuite when)
2159
+ (ecase when
2160
+ (:test-case
2161
+ (ecase (run-setup testsuite)
2162
+ (:once-per-session nil)
2163
+ (:once-per-suite nil)
2164
+ ((:once-per-test-case t) t)
2165
+ ((:never nil) nil)))
2166
+ (:testsuite
2167
+ (ecase (run-setup testsuite)
2168
+ (:once-per-session nil)
2169
+ (:once-per-suite t)
2170
+ ((:once-per-test-case t) nil)
2171
+ ((:never nil) nil)))))
2172
+
2173
+ (defun build-test-test-method (test-class test-body options)
2174
+ (multiple-value-bind (test-name body documentation name-supplied?)
2175
+ (parse-test-body test-body)
2176
+ (declare (ignorable name-supplied?))
2177
+ (unless (consp (first body))
2178
+ (setf body (list body)))
2179
+ `(progn
2180
+ (setf (gethash ',test-name (test-name->code-table ',test-class)) ',body
2181
+ (gethash ',body (test-code->name-table ',test-class)) ',test-name)
2182
+ ,(when documentation
2183
+ `(setf (gethash ',test-name (test-case-documentation ',test-class))
2184
+ ,documentation))
2185
+ #+(or mcl ccl)
2186
+ ,@(when name-supplied?
2187
+ `((ccl:record-source-file ',test-name 'test-case)))
2188
+ (unless (find ',test-name (testsuite-tests ',test-class))
2189
+ (setf (testsuite-tests ',test-class)
2190
+ (append (testsuite-tests ',test-class) (list ',test-name))))
2191
+ (defmethod lift-test ((testsuite ,test-class) (case (eql ',test-name)))
2192
+ ,@(when options
2193
+ `((setf (getf (test-data testsuite) :options)
2194
+ (list ,@(loop for (k v) on options by #'cddr append
2195
+ (list k v))))))
2196
+ (with-test-slots ,@body))
2197
+ (setf *current-test-case-name* ',test-name)
2198
+ (when (and *test-print-when-defined?*
2199
+ (not (or *test-is-being-compiled?*
2200
+ )))
2201
+ (format *debug-io* "~&;Test Created: ~(~S.~S~)."
2202
+ ',test-class ',test-name))
2203
+ *current-test-case-name*)))
2204
+
2205
+ (defun parse-test-body (test-body)
2206
+ (let ((test-name nil)
2207
+ (body nil)
2208
+ (parsed-body nil)
2209
+ (documentation nil)
2210
+ (test-number (1+ (testsuite-test-count *current-testsuite-name*)))
2211
+ (name-supplied? nil))
2212
+ ;; parse out any documentation
2213
+ (loop for form in test-body do
2214
+ (if (and (consp form)
2215
+ (keywordp (first form))
2216
+ (eq :documentation (first form)))
2217
+ (setf documentation (second form))
2218
+ (push form parsed-body)))
2219
+ (setf test-body (nreverse parsed-body))
2220
+ (setf test-name (first test-body))
2221
+ (cond ((symbolp test-name)
2222
+ (setf test-name
2223
+ (intern (format nil "~A" test-name))
2224
+ body (rest test-body)
2225
+ name-supplied? t))
2226
+ ((and (test-code->name-table *current-testsuite-name*)
2227
+ (setf test-name
2228
+ (gethash test-body
2229
+ (test-code->name-table *current-testsuite-name*))))
2230
+ (setf body test-body))
2231
+ (t
2232
+ (setf test-name
2233
+ (intern (format nil "TEST-~A"
2234
+ test-number))
2235
+ body test-body)))
2236
+ (values test-name body documentation name-supplied?)))
2237
+
2238
+ (defun build-test-class ()
2239
+ ;; for now, we don't generate code from :class-def code-blocks
2240
+ ;; they are executed only for effect.
2241
+ (loop for (nil . block) in *code-blocks*
2242
+ when (and block
2243
+ (code block)
2244
+ (eq (operate-when block) :class-def)
2245
+ (or (not (filter block))
2246
+ (funcall (filter block)))) collect
2247
+ (funcall (code block)))
2248
+ (unless (some (lambda (superclass)
2249
+ (testsuite-p superclass))
2250
+ (def :superclasses))
2251
+ (pushnew 'test-mixin (def :superclasses)))
2252
+ ;; build basic class and standard class
2253
+ `(defclass ,(def :testsuite-name) (,@(def :superclasses))
2254
+ nil
2255
+ ,@(when (def :documentation)
2256
+ `((:documentation ,(def :documentation))))
2257
+ (:default-initargs
2258
+ :test-slot-names ',(def :slot-names)
2259
+ ,@(def :default-initargs))))
2260
+
2261
+ (defun parse-test-slots (slot-specs)
2262
+ (loop for spec in slot-specs collect
2263
+ (let ((parsed-spec spec))
2264
+ (if (member :initform parsed-spec)
2265
+ (let ((pos (position :initform parsed-spec)))
2266
+ (append (subseq parsed-spec 0 pos)
2267
+ (subseq parsed-spec (+ pos 2))))
2268
+ parsed-spec))))
2269
+
2270
+ (defmethod testsuite-p ((classname symbol))
2271
+ (let ((class (find-class classname nil)))
2272
+ (handler-case
2273
+ (and class
2274
+ (typep (allocate-instance class) 'test-mixin)
2275
+ classname)
2276
+ (error (c) (declare (ignore c)) (values nil)))))
2277
+
2278
+ (defmethod testsuite-p ((object standard-object))
2279
+ (testsuite-p (class-name (class-of object))))
2280
+
2281
+ (defmethod testsuite-p ((class standard-class))
2282
+ (testsuite-p (class-name class)))
2283
+
2284
+ (defmethod testsuite-methods ((classname symbol))
2285
+ (testsuite-tests classname))
2286
+
2287
+ (defmethod testsuite-methods ((test test-mixin))
2288
+ (testsuite-methods (class-name (class-of test))))
2289
+
2290
+ (defmethod testsuite-methods ((test standard-class))
2291
+ (testsuite-methods (class-name test)))
2292
+
2293
+
2294
+ ;; some handy properties
2295
+ (defclass-property test-slots)
2296
+ (defclass-property test-code->name-table)
2297
+ (defclass-property test-name->code-table)
2298
+ (defclass-property test-case-documentation)
2299
+ (defclass-property testsuite-tests)
2300
+ (defclass-property testsuite-dynamic-variables)
2301
+
2302
+ ;;?? issue 27: break encapsulation of code blocks
2303
+ (defclass-property testsuite-function-specs)
2304
+
2305
+ (defun empty-test-tables (test-name)
2306
+ (when (find-class test-name nil)
2307
+ (setf (test-code->name-table test-name)
2308
+ (make-hash-table :test #'equal)
2309
+ (test-name->code-table test-name)
2310
+ (make-hash-table :test #'equal)
2311
+ (test-case-documentation test-name)
2312
+ (make-hash-table :test #'equal))))
2313
+
2314
+ (pushnew :timeout *deftest-clauses*)
2315
+
2316
+ (add-code-block
2317
+ :timeout 1 :class-def
2318
+ (lambda () (def :timeout))
2319
+ '((setf (def :timeout) (cleanup-parsed-parameter value)))
2320
+ (lambda ()
2321
+ (unless (some (lambda (super)
2322
+ (member (find-class 'process-test-mixin)
2323
+ (superclasses super)))
2324
+ (def :superclasses))
2325
+ (pushnew 'process-test-mixin (def :superclasses)))
2326
+ (push (def :timeout) (def :default-initargs))
2327
+ (push :maximum-time (def :default-initargs))
2328
+ nil))
2329
+
2330
+ (defclass process-test-mixin (test-mixin)
2331
+ ((maximum-time :initform *test-maximum-time*
2332
+ :accessor maximum-time
2333
+ :initarg :maximum-time)))
2334
+
2335
+ (defclass test-timeout-failure (test-failure)
2336
+ ((test-problem-kind :initform "Timeout" :allocation :class)))
2337
+
2338
+ (defmethod lift-test :around ((suite test-mixin) name)
2339
+ (if (profile suite)
2340
+ (with-profile-report ((format nil "~a-~a"
2341
+ (testsuite-name suite) name)
2342
+ (profile suite))
2343
+ (call-next-method))
2344
+ (call-next-method)))
2345
+
2346
+ (defmethod do-testing :around ((testsuite process-test-mixin) result fn)
2347
+ (declare (ignore fn))
2348
+ (handler-case
2349
+ (with-timeout ((maximum-time testsuite))
2350
+ (call-next-method))
2351
+ (timeout-error
2352
+ (c)
2353
+ (declare (ignore c))
2354
+ (report-test-problem
2355
+ 'test-timeout-failure result testsuite (current-method testsuite)
2356
+ (make-instance 'test-timeout-condition
2357
+ :maximum-time (maximum-time testsuite))))))
2358
+
2359
+ ;;?? might be "cleaner" with a macrolet (cf. lift-result)
2360
+ (defun lift-property (name)
2361
+ (when *current-test*
2362
+ (getf (getf (test-data *current-test*) :properties) name)))
2363
+
2364
+ #+(or)
2365
+ (setf (getf (getf (third (first (tests-run *test-result*))) :properties) :foo)
2366
+ 3)
2367
+
2368
+ (defun (setf lift-property) (value name)
2369
+ (when *current-test*
2370
+ (setf (getf (getf (test-data *current-test*) :properties) name) value)))
2371
+
2372
+
2373
+ #+Later
2374
+ (defmacro with-test (&body forms)
2375
+ "Execute forms in the context of the current test class."
2376
+ (let* ((testsuite-name *current-testsuite-name*)
2377
+ (test-case (make-instance test-class)))
2378
+ `(eval-when (:execute)
2379
+ (prog2
2380
+ (setup-test ,test-case)
2381
+ (progn
2382
+ (with-test-slots ,@forms))
2383
+ (test-case-teardown ,test-case result)))))