Revision e02be919

b/gnuradio-core/src/guile/gnuradio/test-suite/guile-test
1
#!../libguile/guile \
2
-e main -s
3
!#
4

5
;;;; guile-test --- run the Guile test suite
6
;;;; Jim Blandy <ji[email protected]> --- May 1999
7
;;;;
8
;;;; 	Copyright (C) 1999, 2001, 2006 Free Software Foundation, Inc.
9
;;;;
10
;;;; This program is free software; you can redistribute it and/or modify
11
;;;; it under the terms of the GNU General Public License as published by
12
;;;; the Free Software Foundation; either version 2, or (at your option)
13
;;;; any later version.
14
;;;;
15
;;;; This program is distributed in the hope that it will be useful,
16
;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17
;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18
;;;; GNU General Public License for more details.
19
;;;;
20
;;;; You should have received a copy of the GNU General Public License
21
;;;; along with this software; see the file COPYING.  If not, write to
22
;;;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
23
;;;; Boston, MA 02110-1301 USA
24

25

26
;;;; Usage: [guile -e main -s] guile-test [OPTIONS] [TEST ...]
27
;;;;
28
;;;; Run tests from the Guile test suite.  Report failures and
29
;;;; unexpected passes to the standard output, along with a summary of
30
;;;; all the results.  Record each reported test outcome in the log
31
;;;; file, `guile.log'.  The exit status is #f if any of the tests
32
;;;; fail or pass unexpectedly.
33
;;;;
34
;;;; Normally, guile-test scans the test directory, and executes all
35
;;;; files whose names end in `.test'.  (It assumes they contain
36
;;;; Scheme code.)  However, you can have it execute specific tests by
37
;;;; listing their filenames on the command line.
38
;;;;
39
;;;; The option `--test-suite' can be given to specify the test
40
;;;; directory.  If no such option is given, the test directory is
41
;;;; taken from the environment variable TEST_SUITE_DIR (if defined),
42
;;;; otherwise a default directory that is hardcoded in this file is
43
;;;; used (see "Installation" below).
44
;;;;
45
;;;; If present, the `--log-file LOG' option tells `guile-test' to put
46
;;;; the log output in a file named LOG.
47
;;;;
48
;;;; If present, the `--debug' option will enable a debugging mode.
49
;;;;
50
;;;; If present, the `--flag-unresolved' option will cause guile-test
51
;;;; to exit with failure status if any tests are UNRESOLVED.
52
;;;;
53
;;;;
54
;;;; Installation:
55
;;;;
56
;;;; If you change the #! line at the top of this script to point at
57
;;;; the Guile interpreter you want to test, you can call this script
58
;;;; as an executable instead of having to pass it as a parameter to
59
;;;; guile via "guile -e main -s guile-test".  Further, you can edit
60
;;;; the definition of default-test-suite to point to the parent
61
;;;; directory of the `tests' tree, which makes it unnecessary to set
62
;;;; the environment variable `TEST_SUITE_DIR'.
63
;;;;
64
;;;;
65
;;;; Shortcomings:
66
;;;;
67
;;;; At the moment, due to a simple-minded implementation, test files
68
;;;; must live in the test directory, and you must specify their names
69
;;;; relative to the top of the test directory.  If you want to send
70
;;;; me a patch that fixes this, but still leaves sane test names in
71
;;;; the log file, that would be great.  At the moment, all the tests
72
;;;; I care about are in the test directory, though.
73
;;;;
74
;;;; It would be nice if you could specify the Guile interpreter you
75
;;;; want to test on the command line.  As it stands, if you want to
76
;;;; change which Guile interpreter you're testing, you need to edit
77
;;;; the #! line at the top of this file, which is stupid.
78

79
(define (main . args)
80
  (let ((module (resolve-module '(test-suite guile-test))))
81
    (apply (module-ref module 'main) args)))
82

83
(define-module (test-suite guile-test)
84
  :use-module (test-suite lib)
85
  :use-module (ice-9 getopt-long)
86
  :use-module (ice-9 and-let-star)
87
  :use-module (ice-9 rdelim)
88
  :export (main data-file-name test-file-name))
89

90

91
;;; User configurable settings:
92
(define default-test-suite
93
  (string-append (getenv "HOME") "/bogus-path/test-suite"))
94

95

96
;;; Variables that will receive their actual values later.
97
(define test-suite default-test-suite)
98

99
(define tmp-dir #f)
100

101

102
;;; General utilities, that probably should be in a library somewhere.
103

104
;;; Enable debugging
105
(define (enable-debug-mode)
106
  (write-line %load-path)
107
  (set! %load-verbosely #t)
108
  (debug-enable 'backtrace 'debug))
109

110
;;; Traverse the directory tree at ROOT, applying F to the name of
111
;;; each file in the tree, including ROOT itself.  For a subdirectory
112
;;; SUB, if (F SUB) is true, we recurse into SUB.  Do not follow
113
;;; symlinks.
114
(define (for-each-file f root)
115

116
  ;; A "hard directory" is a path that denotes a directory and is not a
117
  ;; symlink.
118
  (define (file-is-hard-directory? filename)
119
    (eq? (stat:type (lstat filename)) 'directory))
120

121
  (let visit ((root root))
122
    (let ((should-recur (f root)))
123
      (if (and should-recur (file-is-hard-directory? root))
124
	  (let ((dir (opendir root)))
125
	    (let loop ()
126
	      (let ((entry (readdir dir)))
127
		(cond
128
		 ((eof-object? entry) #f)
129
		 ((or (string=? entry ".")
130
		      (string=? entry "..")
131
                      (string=? entry "CVS")
132
                      (string=? entry "RCS"))
133
		  (loop))
134
		 (else
135
		  (visit (string-append root "/" entry))
136
		  (loop))))))))))
137

138

139
;;; The test driver.
140

141

142
;;; Localizing test files and temporary data files.
143

144
(define (data-file-name filename)
145
  (in-vicinity tmp-dir filename))
146

147
(define (test-file-name test)
148
  (in-vicinity test-suite test))
149

150
;;; Return a list of all the test files in the test tree.
151
(define (enumerate-tests test-dir)
152
  (let ((root-len (+ 1 (string-length test-dir)))
153
	(tests '()))
154
    (for-each-file (lambda (file)
155
		     (if (has-suffix? file ".test")
156
			 (let ((short-name
157
				(substring file root-len)))
158
			   (set! tests (cons short-name tests))))
159
		     #t)
160
		   test-dir)
161

162
    ;; for-each-file presents the files in whatever order it finds
163
    ;; them in the directory.  We sort them here, so they'll always
164
    ;; appear in the same order.  This makes it easier to compare test
165
    ;; log files mechanically.
166
    (sort tests string<?)))
167

168
(define (main args)
169
  (let ((options (getopt-long args
170
			      `((test-suite
171
				 (single-char #\t)
172
				 (value #t))
173
                                (flag-unresolved
174
                                 (single-char #\u))
175
				(log-file
176
				 (single-char #\l)
177
				 (value #t))
178
				(debug
179
				 (single-char #\d))))))
180
    (define (opt tag default)
181
      (let ((pair (assq tag options)))
182
	(if pair (cdr pair) default)))
183

184
    (if (opt 'debug #f)
185
	(enable-debug-mode))
186

187
    (set! test-suite
188
	  (or (opt 'test-suite #f)
189
	      (getenv "TEST_SUITE_DIR")
190
	      default-test-suite))
191

192
    ;; directory where temporary files are created.
193
    ;; when run from "make check", this must be under the build-dir,
194
    ;; not the src-dir.
195
    (set! tmp-dir (getcwd))
196

197
    (let* ((tests
198
	    (let ((foo (opt '() '())))
199
	      (if (null? foo)
200
		  (enumerate-tests test-suite)
201
		  foo)))
202
	   (log-file
203
	    (opt 'log-file "guile.log")))
204

205
      ;; Open the log file.
206
      (let ((log-port (open-output-file log-file)))
207

208
	;; Register some reporters.
209
	(let ((global-pass #t)
210
	      (counter (make-count-reporter)))
211
	  (register-reporter (car counter))
212
	  (register-reporter (make-log-reporter log-port))
213
	  (register-reporter user-reporter)
214
	  (register-reporter (lambda results
215
			       (case (car results)
216
                                 ((unresolved)
217
                                  (and (opt 'flag-unresolved #f)
218
                                       (set! global-pass #f)))
219
				 ((fail upass error)
220
				  (set! global-pass #f)))))
221

222
	  ;; Run the tests.
223
	  (for-each (lambda (test)
224
                      (display (string-append "Running " test "\n"))
225
		      (with-test-prefix test
226
			(load (test-file-name test))))
227
		    tests)
228

229
	  ;; Display the final counts, both to the user and in the log
230
	  ;; file.
231
	  (let ((counts ((cadr counter))))
232
	    (print-counts counts)
233
	    (print-counts counts log-port))
234

235
	  (close-port log-port)
236
	  (quit global-pass))))))
237

238

239
;;; Local Variables:
240
;;; mode: scheme
241
;;; End:
b/gnuradio-core/src/guile/gnuradio/test-suite/lib.scm
1
;;;; test-suite/lib.scm --- generic support for testing
2
;;;; Copyright (C) 1999, 2000, 2001, 2004, 2006, 2007 Free Software Foundation, Inc.
3
;;;;
4
;;;; This program is free software; you can redistribute it and/or modify
5
;;;; it under the terms of the GNU General Public License as published by
6
;;;; the Free Software Foundation; either version 2, or (at your option)
7
;;;; any later version.
8
;;;;
9
;;;; This program is distributed in the hope that it will be useful,
10
;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
11
;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
;;;; GNU General Public License for more details.
13
;;;;
14
;;;; You should have received a copy of the GNU General Public License
15
;;;; along with this software; see the file COPYING.  If not, write to
16
;;;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17
;;;; Boston, MA 02110-1301 USA
18

19
(define-module (test-suite lib)
20
  :use-module (ice-9 stack-catch)
21
  :use-module (ice-9 regex)
22
  :export (
23

24
 ;; Exceptions which are commonly being tested for.
25
 exception:bad-variable
26
 exception:missing-expression
27
 exception:out-of-range exception:unbound-var
28
 exception:used-before-defined
29
 exception:wrong-num-args exception:wrong-type-arg
30
 exception:numerical-overflow
31
 exception:struct-set!-denied
32
 exception:system-error
33
 exception:miscellaneous-error
34
 exception:string-contains-nul
35

36
 ;; Reporting passes and failures.
37
 run-test
38
 pass-if expect-fail
39
 pass-if-exception expect-fail-exception
40

41
 ;; Naming groups of tests in a regular fashion.
42
 with-test-prefix with-test-prefix* current-test-prefix
43
 format-test-name
44

45
 ;; Using the debugging evaluator.
46
 with-debugging-evaluator with-debugging-evaluator*
47

48
 ;; Reporting results in various ways.
49
 register-reporter unregister-reporter reporter-registered?
50
 make-count-reporter print-counts
51
 make-log-reporter
52
 full-reporter
53
 user-reporter))
54

55

56
;;;; If you're using Emacs's Scheme mode:
57
;;;;   (put 'with-test-prefix 'scheme-indent-function 1)
58

59

60
;;;; CORE FUNCTIONS
61
;;;;
62
;;;; The function (run-test name expected-result thunk) is the heart of the
63
;;;; testing environment.  The first parameter NAME is a unique name for the
64
;;;; test to be executed (for an explanation of this parameter see below under
65
;;;; TEST NAMES).  The second parameter EXPECTED-RESULT is a boolean value
66
;;;; that indicates whether the corresponding test is expected to pass.  If
67
;;;; EXPECTED-RESULT is #t the test is expected to pass, if EXPECTED-RESULT is
68
;;;; #f the test is expected to fail.  Finally, THUNK is the function that
69
;;;; actually performs the test.  For example:
70
;;;;
71
;;;;    (run-test "integer addition" #t (lambda () (= 2 (+ 1 1))))
72
;;;;
73
;;;; To report success, THUNK should either return #t or throw 'pass.  To
74
;;;; report failure, THUNK should either return #f or throw 'fail.  If THUNK
75
;;;; returns a non boolean value or throws 'unresolved, this indicates that
76
;;;; the test did not perform as expected.  For example the property that was
77
;;;; to be tested could not be tested because something else went wrong.
78
;;;; THUNK may also throw 'untested to indicate that the test was deliberately
79
;;;; not performed, for example because the test case is not complete yet.
80
;;;; Finally, if THUNK throws 'unsupported, this indicates that this test
81
;;;; requires some feature that is not available in the configured testing
82
;;;; environment.  All other exceptions thrown by THUNK are considered as
83
;;;; errors.
84
;;;;
85
;;;;
86
;;;; Convenience macros for tests expected to pass or fail
87
;;;;
88
;;;; * (pass-if name body) is a short form for
89
;;;;   (run-test name #t (lambda () body))
90
;;;; * (expect-fail name body) is a short form for
91
;;;;   (run-test name #f (lambda () body))
92
;;;;
93
;;;; For example:
94
;;;;
95
;;;;    (pass-if "integer addition" (= 2 (+ 1 1)))
96
;;;;
97
;;;;
98
;;;; Convenience macros to test for exceptions
99
;;;;
100
;;;; The following macros take exception parameters which are pairs
101
;;;; (type . message), where type is a symbol that denotes an exception type
102
;;;; like 'wrong-type-arg or 'out-of-range, and message is a string holding a
103
;;;; regular expression that describes the error message for the exception
104
;;;; like "Argument .* out of range".
105
;;;;
106
;;;; * (pass-if-exception name exception body) will pass if the execution of
107
;;;;   body causes the given exception to be thrown.  If no exception is
108
;;;;   thrown, the test fails.  If some other exception is thrown, is is an
109
;;;;   error.
110
;;;; * (expect-fail-exception name exception body) will pass unexpectedly if
111
;;;;   the execution of body causes the given exception to be thrown.  If no
112
;;;;   exception is thrown, the test fails expectedly.  If some other
113
;;;;   exception is thrown, it is an error.
114

115

116
;;;; TEST NAMES
117
;;;;
118
;;;; Every test in the test suite has a unique name, to help
119
;;;; developers find tests that are failing (or unexpectedly passing),
120
;;;; and to help gather statistics.
121
;;;;
122
;;;; A test name is a list of printable objects.  For example:
123
;;;; ("ports.scm" "file" "read and write back list of strings")
124
;;;; ("ports.scm" "pipe" "read")
125
;;;;
126
;;;; Test names may contain arbitrary objects, but they always have
127
;;;; the following properties:
128
;;;; - Test names can be compared with EQUAL?.
129
;;;; - Test names can be reliably stored and retrieved with the standard WRITE
130
;;;;   and READ procedures; doing so preserves their identity.
131
;;;;
132
;;;; For example:
133
;;;;
134
;;;;    (pass-if "simple addition" (= 4 (+ 2 2)))
135
;;;;
136
;;;; In that case, the test name is the list ("simple addition").
137
;;;;
138
;;;; In the case of simple tests the expression that is tested would often
139
;;;; suffice as a test name by itself.  Therefore, the convenience macros
140
;;;; pass-if and expect-fail provide a shorthand notation that allows to omit
141
;;;; a test name in such cases.
142
;;;;
143
;;;; * (pass-if expression) is a short form for
144
;;;;   (run-test 'expression #t (lambda () expression))
145
;;;; * (expect-fail expression) is a short form for
146
;;;;   (run-test 'expression #f (lambda () expression))
147
;;;;
148
;;;; For example:
149
;;;;
150
;;;;    (pass-if (= 2 (+ 1 1)))
151
;;;;
152
;;;; The WITH-TEST-PREFIX syntax and WITH-TEST-PREFIX* procedure establish
153
;;;; a prefix for the names of all tests whose results are reported
154
;;;; within their dynamic scope.  For example:
155
;;;;
156
;;;; (begin
157
;;;;   (with-test-prefix "basic arithmetic"
158
;;;;     (pass-if "addition" (= (+ 2 2) 4))
159
;;;;     (pass-if "subtraction" (= (- 4 2) 2)))
160
;;;;   (pass-if "multiplication" (= (* 2 2) 4)))
161
;;;;
162
;;;; In that example, the three test names are:
163
;;;;   ("basic arithmetic" "addition"),
164
;;;;   ("basic arithmetic" "subtraction"), and
165
;;;;   ("multiplication").
166
;;;;
167
;;;; WITH-TEST-PREFIX can be nested.  Each WITH-TEST-PREFIX postpends
168
;;;; a new element to the current prefix:
169
;;;;
170
;;;; (with-test-prefix "arithmetic"
171
;;;;   (with-test-prefix "addition"
172
;;;;     (pass-if "integer" (= (+ 2 2) 4))
173
;;;;     (pass-if "complex" (= (+ 2+3i 4+5i) 6+8i)))
174
;;;;   (with-test-prefix "subtraction"
175
;;;;     (pass-if "integer" (= (- 2 2) 0))
176
;;;;     (pass-if "complex" (= (- 2+3i 1+2i) 1+1i))))
177
;;;;
178
;;;; The four test names here are:
179
;;;;   ("arithmetic" "addition" "integer")
180
;;;;   ("arithmetic" "addition" "complex")
181
;;;;   ("arithmetic" "subtraction" "integer")
182
;;;;   ("arithmetic" "subtraction" "complex")
183
;;;;
184
;;;; To print a name for a human reader, we DISPLAY its elements,
185
;;;; separated by ": ".  So, the last set of test names would be
186
;;;; reported as:
187
;;;;
188
;;;;   arithmetic: addition: integer
189
;;;;   arithmetic: addition: complex
190
;;;;   arithmetic: subtraction: integer
191
;;;;   arithmetic: subtraction: complex
192
;;;;
193
;;;; The Guile benchmarks use with-test-prefix to include the name of
194
;;;; the source file containing the test in the test name, to help
195
;;;; developers to find failing tests, and to provide each file with its
196
;;;; own namespace.
197

198

199
;;;; REPORTERS
200
;;;;
201
;;;; A reporter is a function which we apply to each test outcome.
202
;;;; Reporters can log results, print interesting results to the
203
;;;; standard output, collect statistics, etc.
204
;;;;
205
;;;; A reporter function takes two mandatory arguments, RESULT and TEST, and
206
;;;; possibly additional arguments depending on RESULT; its return value
207
;;;; is ignored.  RESULT has one of the following forms:
208
;;;;
209
;;;; pass         - The test named TEST passed.
210
;;;;                Additional arguments are ignored.
211
;;;; upass        - The test named TEST passed unexpectedly.
212
;;;;                Additional arguments are ignored.
213
;;;; fail         - The test named TEST failed.
214
;;;;                Additional arguments are ignored.
215
;;;; xfail        - The test named TEST failed, as expected.
216
;;;;                Additional arguments are ignored.
217
;;;; unresolved   - The test named TEST did not perform as expected, for
218
;;;;                example the property that was to be tested could not be
219
;;;;                tested because something else went wrong.
220
;;;;                Additional arguments are ignored.
221
;;;; untested     - The test named TEST was not actually performed, for
222
;;;;                example because the test case is not complete yet.
223
;;;;                Additional arguments are ignored.
224
;;;; unsupported  - The test named TEST requires some feature that is not
225
;;;;                available in the configured testing environment.
226
;;;;                Additional arguments are ignored.
227
;;;; error        - An error occurred while the test named TEST was
228
;;;;                performed.  Since this result means that the system caught
229
;;;;                an exception it could not handle, the exception arguments
230
;;;;                are passed as additional arguments.
231
;;;;
232
;;;; This library provides some standard reporters for logging results
233
;;;; to a file, reporting interesting results to the user, and
234
;;;; collecting totals.
235
;;;;
236
;;;; You can use the REGISTER-REPORTER function and friends to add
237
;;;; whatever reporting functions you like.  If you don't register any
238
;;;; reporters, the library uses FULL-REPORTER, which simply writes
239
;;;; all results to the standard output.
240

241

242
;;;; MISCELLANEOUS
243
;;;;
244

245
;;; Define some exceptions which are commonly being tested for.
246
(define exception:bad-variable
247
  (cons 'syntax-error "Bad variable"))
248
(define exception:missing-expression
249
  (cons 'misc-error "^missing or extra expression"))
250
(define exception:out-of-range
251
  (cons 'out-of-range "^.*out of range"))
252
(define exception:unbound-var
253
  (cons 'unbound-variable "^Unbound variable"))
254
(define exception:used-before-defined
255
  (cons 'unbound-variable "^Variable used before given a value"))
256
(define exception:wrong-num-args
257
  (cons 'wrong-number-of-args "^Wrong number of arguments"))
258
(define exception:wrong-type-arg
259
  (cons 'wrong-type-arg "^Wrong type"))
260
(define exception:numerical-overflow
261
  (cons 'numerical-overflow "^Numerical overflow"))
262
(define exception:struct-set!-denied
263
  (cons 'misc-error "^set! denied for field"))
264
(define exception:system-error
265
  (cons 'system-error ".*"))
266
(define exception:miscellaneous-error
267
  (cons 'misc-error "^.*"))
268

269
;; as per throw in scm_to_locale_stringn()
270
(define exception:string-contains-nul
271
  (cons 'misc-error "^string contains #\\\\nul character"))
272

273

274
;;; Display all parameters to the default output port, followed by a newline.
275
(define (display-line . objs)
276
  (for-each display objs)
277
  (newline))
278

279
;;; Display all parameters to the given output port, followed by a newline.
280
(define (display-line-port port . objs)
281
  (for-each (lambda (obj) (display obj port)) objs)
282
  (newline port))
283

284

285
;;;; CORE FUNCTIONS
286
;;;;
287

288
;;; The central testing routine.
289
;;; The idea is taken from Greg, the GNUstep regression test environment.
290
(define run-test #f)
291
(let ((test-running #f))
292
  (define (local-run-test name expect-pass thunk)
293
    (if test-running
294
	(error "Nested calls to run-test are not permitted.")
295
	(let ((test-name (full-name name)))
296
	  (set! test-running #t)
297
	  (catch #t
298
	    (lambda ()
299
	      (let ((result (thunk)))
300
		(if (eq? result #t) (throw 'pass))
301
		(if (eq? result #f) (throw 'fail))
302
		(throw 'unresolved)))
303
	    (lambda (key . args)
304
	      (case key
305
		((pass)
306
		 (report (if expect-pass 'pass 'upass) test-name))
307
		((fail)
308
		 (report (if expect-pass 'fail 'xfail) test-name))
309
		((unresolved untested unsupported)
310
		 (report key test-name))
311
		((quit)
312
		 (report 'unresolved test-name)
313
		 (quit))
314
		(else
315
		 (report 'error test-name (cons key args))))))
316
	  (set! test-running #f))))
317
  (set! run-test local-run-test))
318

319
;;; A short form for tests that are expected to pass, taken from Greg.
320
(defmacro pass-if (name . rest)
321
  (if (and (null? rest) (pair? name))
322
      ;; presume this is a simple test, i.e. (pass-if (even? 2))
323
      ;; where the body should also be the name.
324
      `(run-test ',name #t (lambda () ,name))
325
      `(run-test ,name #t (lambda () ,@rest))))
326

327
;;; A short form for tests that are expected to fail, taken from Greg.
328
(defmacro expect-fail (name . rest)
329
  (if (and (null? rest) (pair? name))
330
      ;; presume this is a simple test, i.e. (expect-fail (even? 2))
331
      ;; where the body should also be the name.
332
      `(run-test ',name #f (lambda () ,name))
333
      `(run-test ,name #f (lambda () ,@rest))))
334

335
;;; A helper function to implement the macros that test for exceptions.
336
(define (run-test-exception name exception expect-pass thunk)
337
  (run-test name expect-pass
338
    (lambda ()
339
      (stack-catch (car exception)
340
	(lambda () (thunk) #f)
341
	(lambda (key proc message . rest)
342
	  (cond
343
           ;; handle explicit key
344
           ((string-match (cdr exception) message)
345
            #t)
346
           ;; handle `(error ...)' which uses `misc-error' for key and doesn't
347
           ;; yet format the message and args (we have to do it here).
348
           ((and (eq? 'misc-error (car exception))
349
                 (list? rest)
350
                 (string-match (cdr exception)
351
                               (apply simple-format #f message (car rest))))
352
            #t)
353
           ;; handle syntax errors which use `syntax-error' for key and don't
354
           ;; yet format the message and args (we have to do it here).
355
           ((and (eq? 'syntax-error (car exception))
356
                 (list? rest)
357
                 (string-match (cdr exception)
358
                               (apply simple-format #f message (car rest))))
359
            #t)
360
           ;; unhandled; throw again
361
           (else
362
            (apply throw key proc message rest))))))))
363

364
;;; A short form for tests that expect a certain exception to be thrown.
365
(defmacro pass-if-exception (name exception body . rest)
366
  `(,run-test-exception ,name ,exception #t (lambda () ,body ,@rest)))
367

368
;;; A short form for tests expected to fail to throw a certain exception.
369
(defmacro expect-fail-exception (name exception body . rest)
370
  `(,run-test-exception ,name ,exception #f (lambda () ,body ,@rest)))
371

372

373
;;;; TEST NAMES
374
;;;;
375

376
;;;; Turn a test name into a nice human-readable string.
377
(define (format-test-name name)
378
  (call-with-output-string
379
   (lambda (port)
380
     (let loop ((name name)
381
		(separator ""))
382
       (if (pair? name)
383
	   (begin
384
	     (display separator port)
385
	     (display (car name) port)
386
	     (loop (cdr name) ": ")))))))
387

388
;;;; For a given test-name, deliver the full name including all prefixes.
389
(define (full-name name)
390
  (append (current-test-prefix) (list name)))
391

392
;;; A fluid containing the current test prefix, as a list.
393
(define prefix-fluid (make-fluid))
394
(fluid-set! prefix-fluid '())
395
(define (current-test-prefix)
396
  (fluid-ref prefix-fluid))
397

398
;;; Postpend PREFIX to the current name prefix while evaluting THUNK.
399
;;; The name prefix is only changed within the dynamic scope of the
400
;;; call to with-test-prefix*.  Return the value returned by THUNK.
401
(define (with-test-prefix* prefix thunk)
402
  (with-fluids ((prefix-fluid
403
		 (append (fluid-ref prefix-fluid) (list prefix))))
404
    (thunk)))
405

406
;;; (with-test-prefix PREFIX BODY ...)
407
;;; Postpend PREFIX to the current name prefix while evaluating BODY ...
408
;;; The name prefix is only changed within the dynamic scope of the
409
;;; with-test-prefix expression.  Return the value returned by the last
410
;;; BODY expression.
411
(defmacro with-test-prefix (prefix . body)
412
  `(with-test-prefix* ,prefix (lambda () ,@body)))
413

414
;;; Call THUNK using the debugging evaluator.
415
(define (with-debugging-evaluator* thunk)
416
  (let ((dopts #f))
417
    (dynamic-wind
418
	(lambda ()
419
	  (set! dopts (debug-options))
420
	  (debug-enable 'debug))
421
	thunk
422
	(lambda ()
423
	  (debug-options dopts)))))
424

425
;;; Evaluate BODY... using the debugging evaluator.
426
(define-macro (with-debugging-evaluator . body)
427
  `(with-debugging-evaluator* (lambda () ,@body)))
428

429

430

431
;;;; REPORTERS
432
;;;;
433

434
;;; The global list of reporters.
435
(define reporters '())
436

437
;;; The default reporter, to be used only if no others exist.
438
(define default-reporter #f)
439

440
;;; Add the procedure REPORTER to the current set of reporter functions.
441
;;; Signal an error if that reporter procedure object is already registered.
442
(define (register-reporter reporter)
443
  (if (memq reporter reporters)
444
      (error "register-reporter: reporter already registered: " reporter))
445
  (set! reporters (cons reporter reporters)))
446

447
;;; Remove the procedure REPORTER from the current set of reporter
448
;;; functions.  Signal an error if REPORTER is not currently registered.
449
(define (unregister-reporter reporter)
450
  (if (memq reporter reporters)
451
      (set! reporters (delq! reporter reporters))
452
      (error "unregister-reporter: reporter not registered: " reporter)))
453

454
;;; Return true iff REPORTER is in the current set of reporter functions.
455
(define (reporter-registered? reporter)
456
  (if (memq reporter reporters) #t #f))
457

458
;;; Send RESULT to all currently registered reporter functions.
459
(define (report . args)
460
  (if (pair? reporters)
461
      (for-each (lambda (reporter) (apply reporter args))
462
		reporters)
463
      (apply default-reporter args)))
464

465

466
;;;; Some useful standard reporters:
467
;;;; Count reporters count the occurrence of each test result type.
468
;;;; Log reporters write all test results to a given log file.
469
;;;; Full reporters write all test results to the standard output.
470
;;;; User reporters write interesting test results to the standard output.
471

472
;;; The complete list of possible test results.
473
(define result-tags
474
  '((pass        "PASS"        "passes:                 ")
475
    (fail        "FAIL"        "failures:               ")
476
    (upass       "UPASS"       "unexpected passes:      ")
477
    (xfail       "XFAIL"       "expected failures:      ")
478
    (unresolved  "UNRESOLVED"  "unresolved test cases:  ")
479
    (untested    "UNTESTED"    "untested test cases:    ")
480
    (unsupported "UNSUPPORTED" "unsupported test cases: ")
481
    (error       "ERROR"       "errors:                 ")))
482

483
;;; The list of important test results.
484
(define important-result-tags
485
  '(fail upass unresolved error))
486

487
;;; Display a single test result in formatted form to the given port
488
(define (print-result port result name . args)
489
  (let* ((tag (assq result result-tags))
490
	 (label (if tag (cadr tag) #f)))
491
    (if label
492
	(begin
493
	  (display label port)
494
	  (display ": " port)
495
	  (display (format-test-name name) port)
496
	  (if (pair? args)
497
	      (begin
498
		(display " - arguments: " port)
499
		(write args port)))
500
	  (newline port))
501
	(error "(test-suite lib) FULL-REPORTER: unrecognized result: "
502
	       result))))
503

504
;;; Return a list of the form (COUNTER RESULTS), where:
505
;;; - COUNTER is a reporter procedure, and
506
;;; - RESULTS is a procedure taking no arguments which returns the
507
;;;   results seen so far by COUNTER.  The return value is an alist
508
;;;   mapping outcome symbols (`pass', `fail', etc.) onto counts.
509
(define (make-count-reporter)
510
  (let ((counts (map (lambda (tag) (cons (car tag) 0)) result-tags)))
511
    (list
512
     (lambda (result name . args)
513
       (let ((pair (assq result counts)))
514
	 (if pair
515
	     (set-cdr! pair (+ 1 (cdr pair)))
516
	     (error "count-reporter: unexpected test result: "
517
		    (cons result (cons name args))))))
518
     (lambda ()
519
       (append counts '())))))
520

521
;;; Print a count reporter's results nicely.  Pass this function the value
522
;;; returned by a count reporter's RESULTS procedure.
523
(define (print-counts results . port?)
524
  (let ((port (if (pair? port?)
525
		  (car port?)
526
		  (current-output-port))))
527
    (newline port)
528
    (display-line-port port "Totals for this test run:")
529
    (for-each
530
     (lambda (tag)
531
       (let ((result (assq (car tag) results)))
532
	 (if result
533
	     (display-line-port port (caddr tag) (cdr result))
534
	     (display-line-port port
535
				"Test suite bug: "
536
				"no total available for `" (car tag) "'"))))
537
     result-tags)
538
    (newline port)))
539

540
;;; Return a reporter procedure which prints all results to the file
541
;;; FILE, in human-readable form.  FILE may be a filename, or a port.
542
(define (make-log-reporter file)
543
  (let ((port (if (output-port? file) file
544
		  (open-output-file file))))
545
    (lambda args
546
      (apply print-result port args)
547
      (force-output port))))
548

549
;;; A reporter that reports all results to the user.
550
(define (full-reporter . args)
551
  (apply print-result (current-output-port) args))
552

553
;;; A reporter procedure which shows interesting results (failures,
554
;;; unexpected passes etc.) to the user.
555
(define (user-reporter result name . args)
556
  (if (memq result important-result-tags)
557
      (apply full-reporter result name args)))
558

559
(set! default-reporter full-reporter)
/dev/null
1
#!../libguile/guile \
2
-e main -s
3
!#
4

5
;;;; guile-test --- run the Guile test suite
6
;;;; Jim Blandy <[email protected]> --- May 1999
7
;;;;
8
;;;; 	Copyright (C) 1999, 2001, 2006 Free Software Foundation, Inc.
9
;;;;
10
;;;; This program is free software; you can redistribute it and/or modify
11
;;;; it under the terms of the GNU General Public License as published by
12
;;;; the Free Software Foundation; either version 2, or (at your option)
13
;;;; any later version.
14
;;;;
15
;;;; This program is distributed in the hope that it will be useful,
16
;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17
;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18
;;;; GNU General Public License for more details.
19
;;;;
20
;;;; You should have received a copy of the GNU General Public License
21
;;;; along with this software; see the file COPYING.  If not, write to
22
;;;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
23
;;;; Boston, MA 02110-1301 USA
24

25

26
;;;; Usage: [guile -e main -s] guile-test [OPTIONS] [TEST ...]
27
;;;;
28
;;;; Run tests from the Guile test suite.  Report failures and
29
;;;; unexpected passes to the standard output, along with a summary of
30
;;;; all the results.  Record each reported test outcome in the log
31
;;;; file, `guile.log'.  The exit status is #f if any of the tests
32
;;;; fail or pass unexpectedly.
33
;;;;
34
;;;; Normally, guile-test scans the test directory, and executes all
35
;;;; files whose names end in `.test'.  (It assumes they contain
36
;;;; Scheme code.)  However, you can have it execute specific tests by
37
;;;; listing their filenames on the command line.
38
;;;;
39
;;;; The option `--test-suite' can be given to specify the test
40
;;;; directory.  If no such option is given, the test directory is
41
;;;; taken from the environment variable TEST_SUITE_DIR (if defined),
42
;;;; otherwise a default directory that is hardcoded in this file is
43
;;;; used (see "Installation" below).
44
;;;;
45
;;;; If present, the `--log-file LOG' option tells `guile-test' to put
46
;;;; the log output in a file named LOG.
47
;;;;
48
;;;; If present, the `--debug' option will enable a debugging mode.
49
;;;;
50
;;;; If present, the `--flag-unresolved' option will cause guile-test
51
;;;; to exit with failure status if any tests are UNRESOLVED.
52
;;;;
53
;;;;
54
;;;; Installation:
55
;;;;
56
;;;; If you change the #! line at the top of this script to point at
57
;;;; the Guile interpreter you want to test, you can call this script
58
;;;; as an executable instead of having to pass it as a parameter to
59
;;;; guile via "guile -e main -s guile-test".  Further, you can edit
60
;;;; the definition of default-test-suite to point to the parent
61
;;;; directory of the `tests' tree, which makes it unnecessary to set
62
;;;; the environment variable `TEST_SUITE_DIR'.
63
;;;;
64
;;;;
65
;;;; Shortcomings:
66
;;;;
67
;;;; At the moment, due to a simple-minded implementation, test files
68
;;;; must live in the test directory, and you must specify their names
69
;;;; relative to the top of the test directory.  If you want to send
70
;;;; me a patch that fixes this, but still leaves sane test names in
71
;;;; the log file, that would be great.  At the moment, all the tests
72
;;;; I care about are in the test directory, though.
73
;;;;
74
;;;; It would be nice if you could specify the Guile interpreter you
75
;;;; want to test on the command line.  As it stands, if you want to
76
;;;; change which Guile interpreter you're testing, you need to edit
77
;;;; the #! line at the top of this file, which is stupid.
78

79
(define (main . args)
80
  (let ((module (resolve-module '(test-suite guile-test))))
81
    (apply (module-ref module 'main) args)))
82

83
(define-module (test-suite guile-test)
84
  :use-module (test-suite lib)
85
  :use-module (ice-9 getopt-long)
86
  :use-module (ice-9 and-let-star)
87
  :use-module (ice-9 rdelim)
88
  :export (main data-file-name test-file-name))
89

90

91
;;; User configurable settings:
92
(define default-test-suite
93
  (string-append (getenv "HOME") "/bogus-path/test-suite"))
94

95

96
;;; Variables that will receive their actual values later.
97
(define test-suite default-test-suite)
98

99
(define tmp-dir #f)
100

101

102
;;; General utilities, that probably should be in a library somewhere.
103

104
;;; Enable debugging
105
(define (enable-debug-mode)
106
  (write-line %load-path)
107
  (set! %load-verbosely #t)
108
  (debug-enable 'backtrace 'debug))
109

110
;;; Traverse the directory tree at ROOT, applying F to the name of
111
;;; each file in the tree, including ROOT itself.  For a subdirectory
112
;;; SUB, if (F SUB) is true, we recurse into SUB.  Do not follow
113
;;; symlinks.
114
(define (for-each-file f root)
115

116
  ;; A "hard directory" is a path that denotes a directory and is not a
117
  ;; symlink.
118
  (define (file-is-hard-directory? filename)
119
    (eq? (stat:type (lstat filename)) 'directory))
120

121
  (let visit ((root root))
122
    (let ((should-recur (f root)))
123
      (if (and should-recur (file-is-hard-directory? root))
124
	  (let ((dir (opendir root)))
125
	    (let loop ()
126
	      (let ((entry (readdir dir)))
127
		(cond
128
		 ((eof-object? entry) #f)
129
		 ((or (string=? entry ".")
130
		      (string=? entry "..")
131
                      (string=? entry "CVS")
132
                      (string=? entry "RCS"))
133
		  (loop))
134
		 (else
135
		  (visit (string-append root "/" entry))
136
		  (loop))))))))))
137

138

139
;;; The test driver.
140

141

142
;;; Localizing test files and temporary data files.
143

144
(define (data-file-name filename)
145
  (in-vicinity tmp-dir filename))
146

147
(define (test-file-name test)
148
  (in-vicinity test-suite test))
149

150
;;; Return a list of all the test files in the test tree.
151
(define (enumerate-tests test-dir)
152
  (let ((root-len (+ 1 (string-length test-dir)))
153
	(tests '()))
154
    (for-each-file (lambda (file)
155
		     (if (has-suffix? file ".test")
156
			 (let ((short-name
157
				(substring file root-len)))
158
			   (set! tests (cons short-name tests))))
159
		     #t)
160
		   test-dir)
161

162
    ;; for-each-file presents the files in whatever order it finds
163
    ;; them in the directory.  We sort them here, so they'll always
164
    ;; appear in the same order.  This makes it easier to compare test
165
    ;; log files mechanically.
166
    (sort tests string<?)))
167

168
(define (main args)
169
  (let ((options (getopt-long args
170
			      `((test-suite
171
				 (single-char #\t)
172
				 (value #t))
173
                                (flag-unresolved
174
                                 (single-char #\u))
175
				(log-file
176
				 (single-char #\l)
177
				 (value #t))
178
				(debug
179
				 (single-char #\d))))))
180
    (define (opt tag default)
181
      (let ((pair (assq tag options)))
182
	(if pair (cdr pair) default)))
183

184
    (if (opt 'debug #f)
185
	(enable-debug-mode))
186

187
    (set! test-suite
188
	  (or (opt 'test-suite #f)
189
	      (getenv "TEST_SUITE_DIR")
190
	      default-test-suite))
191

192
    ;; directory where temporary files are created.
193
    ;; when run from "make check", this must be under the build-dir,
194
    ;; not the src-dir.
195
    (set! tmp-dir (getcwd))
196

197
    (let* ((tests
198
	    (let ((foo (opt '() '())))
199
	      (if (null? foo)
200
		  (enumerate-tests test-suite)
201
		  foo)))
202
	   (log-file
203
	    (opt 'log-file "guile.log")))
204

205
      ;; Open the log file.
206
      (let ((log-port (open-output-file log-file)))
207

208
	;; Register some reporters.
209
	(let ((global-pass #t)
210
	      (counter (make-count-reporter)))
211
	  (register-reporter (car counter))
212
	  (register-reporter (make-log-reporter log-port))
213
	  (register-reporter user-reporter)
214
	  (register-reporter (lambda results
215
			       (case (car results)
216
                                 ((unresolved)
217
                                  (and (opt 'flag-unresolved #f)
218
                                       (set! global-pass #f)))
219
				 ((fail upass error)
220
				  (set! global-pass #f)))))
221

222
	  ;; Run the tests.
223
	  (for-each (lambda (test)
224
                      (display (string-append "Running " test "\n"))
225
		      (with-test-prefix test
226
			(load (test-file-name test))))
227
		    tests)
228

229
	  ;; Display the final counts, both to the user and in the log
230
	  ;; file.
231
	  (let ((counts ((cadr counter))))
232
	    (print-counts counts)
233
	    (print-counts counts log-port))
234

235
	  (close-port log-port)
236
	  (quit global-pass))))))
237

238

239
;;; Local Variables:
240
;;; mode: scheme
241
;;; End:
/dev/null
1
;;;; test-suite/lib.scm --- generic support for testing
2
;;;; Copyright (C) 1999, 2000, 2001, 2004, 2006, 2007 Free Software Foundation, Inc.
3
;;;;
4
;;;; This program is free software; you can redistribute it and/or modify
5
;;;; it under the terms of the GNU General Public License as published by
6
;;;; the Free Software Foundation; either version 2, or (at your option)
7
;;;; any later version.
8
;;;;
9
;;;; This program is distributed in the hope that it will be useful,
10
;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
11
;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
;;;; GNU General Public License for more details.
13
;;;;
14
;;;; You should have received a copy of the GNU General Public License
15
;;;; along with this software; see the file COPYING.  If not, write to
16
;;;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17
;;;; Boston, MA 02110-1301 USA
18

19
(define-module (test-suite lib)
20
  :use-module (ice-9 stack-catch)
21
  :use-module (ice-9 regex)
22
  :export (
23

24
 ;; Exceptions which are commonly being tested for.
25
 exception:bad-variable
26
 exception:missing-expression
27
 exception:out-of-range exception:unbound-var
28
 exception:used-before-defined
29
 exception:wrong-num-args exception:wrong-type-arg
30
 exception:numerical-overflow
31
 exception:struct-set!-denied
32
 exception:system-error
33
 exception:miscellaneous-error
34
 exception:string-contains-nul
35

36
 ;; Reporting passes and failures.
37
 run-test
38
 pass-if expect-fail
39
 pass-if-exception expect-fail-exception
40

41
 ;; Naming groups of tests in a regular fashion.
42
 with-test-prefix with-test-prefix* current-test-prefix
43
 format-test-name
44

45
 ;; Using the debugging evaluator.
46
 with-debugging-evaluator with-debugging-evaluator*
47

48
 ;; Reporting results in various ways.
49
 register-reporter unregister-reporter reporter-registered?
50
 make-count-reporter print-counts
51
 make-log-reporter
52
 full-reporter
53
 user-reporter))
54

55

56
;;;; If you're using Emacs's Scheme mode:
57
;;;;   (put 'with-test-prefix 'scheme-indent-function 1)
58

59

60
;;;; CORE FUNCTIONS
61
;;;;
62
;;;; The function (run-test name expected-result thunk) is the heart of the
63
;;;; testing environment.  The first parameter NAME is a unique name for the
64
;;;; test to be executed (for an explanation of this parameter see below under
65
;;;; TEST NAMES).  The second parameter EXPECTED-RESULT is a boolean value
66
;;;; that indicates whether the corresponding test is expected to pass.  If
67
;;;; EXPECTED-RESULT is #t the test is expected to pass, if EXPECTED-RESULT is
68
;;;; #f the test is expected to fail.  Finally, THUNK is the function that
69
;;;; actually performs the test.  For example:
70
;;;;
71
;;;;    (run-test "integer addition" #t (lambda () (= 2 (+ 1 1))))
72
;;;;
73
;;;; To report success, THUNK should either return #t or throw 'pass.  To
74
;;;; report failure, THUNK should either return #f or throw 'fail.  If THUNK
75
;;;; returns a non boolean value or throws 'unresolved, this indicates that
76
;;;; the test did not perform as expected.  For example the property that was
77
;;;; to be tested could not be tested because something else went wrong.
78
;;;; THUNK may also throw 'untested to indicate that the test was deliberately
79
;;;; not performed, for example because the test case is not complete yet.
80
;;;; Finally, if THUNK throws 'unsupported, this indicates that this test
81
;;;; requires some feature that is not available in the configured testing
82
;;;; environment.  All other exceptions thrown by THUNK are considered as
83
;;;; errors.
84
;;;;
85
;;;;
86
;;;; Convenience macros for tests expected to pass or fail
87
;;;;
88
;;;; * (pass-if name body) is a short form for
89
;;;;   (run-test name #t (lambda () body))
90
;;;; * (expect-fail name body) is a short form for
91
;;;;   (run-test name #f (lambda () body))
92
;;;;
93
;;;; For example:
94
;;;;
95
;;;;    (pass-if "integer addition" (= 2 (+ 1 1)))
96
;;;;
97
;;;;
98
;;;; Convenience macros to test for exceptions
99
;;;;
100
;;;; The following macros take exception parameters which are pairs
101
;;;; (type . message), where type is a symbol that denotes an exception type
102
;;;; like 'wrong-type-arg or 'out-of-range, and message is a string holding a
103
;;;; regular expression that describes the error message for the exception
104
;;;; like "Argument .* out of range".
105
;;;;
106
;;;; * (pass-if-exception name exception body) will pass if the execution of
107
;;;;   body causes the given exception to be thrown.  If no exception is
108
;;;;   thrown, the test fails.  If some other exception is thrown, is is an
109
;;;;   error.
110
;;;; * (expect-fail-exception name exception body) will pass unexpectedly if
111
;;;;   the execution of body causes the given exception to be thrown.  If no
112
;;;;   exception is thrown, the test fails expectedly.  If some other
113
;;;;   exception is thrown, it is an error.
114

115

116
;;;; TEST NAMES
117
;;;;
118
;;;; Every test in the test suite has a unique name, to help
119
;;;; developers find tests that are failing (or unexpectedly passing),
120
;;;; and to help gather statistics.
121
;;;;
122
;;;; A test name is a list of printable objects.  For example:
123
;;;; ("ports.scm" "file" "read and write back list of strings")
124
;;;; ("ports.scm" "pipe" "read")
125
;;;;
126
;;;; Test names may contain arbitrary objects, but they always have
127
;;;; the following properties:
128
;;;; - Test names can be compared with EQUAL?.
129
;;;; - Test names can be reliably stored and retrieved with the standard WRITE
130
;;;;   and READ procedures; doing so preserves their identity.
131
;;;;
132
;;;; For example:
133
;;;;
134
;;;;    (pass-if "simple addition" (= 4 (+ 2 2)))
135
;;;;
136
;;;; In that case, the test name is the list ("simple addition").
137
;;;;
138
;;;; In the case of simple tests the expression that is tested would often
139
;;;; suffice as a test name by itself.  Therefore, the convenience macros
140
;;;; pass-if and expect-fail provide a shorthand notation that allows to omit
141
;;;; a test name in such cases.
142
;;;;
143
;;;; * (pass-if expression) is a short form for
144
;;;;   (run-test 'expression #t (lambda () expression))
145
;;;; * (expect-fail expression) is a short form for
146
;;;;   (run-test 'expression #f (lambda () expression))
147
;;;;
148
;;;; For example:
149
;;;;
150
;;;;    (pass-if (= 2 (+ 1 1)))
151
;;;;
152
;;;; The WITH-TEST-PREFIX syntax and WITH-TEST-PREFIX* procedure establish
153
;;;; a prefix for the names of all tests whose results are reported
154
;;;; within their dynamic scope.  For example:
155
;;;;
156
;;;; (begin
157
;;;;   (with-test-prefix "basic arithmetic"
158
;;;;     (pass-if "addition" (= (+ 2 2) 4))
159
;;;;     (pass-if "subtraction" (= (- 4 2) 2)))
160
;;;;   (pass-if "multiplication" (= (* 2 2) 4)))
161
;;;;
162
;;;; In that example, the three test names are:
163
;;;;   ("basic arithmetic" "addition"),
164
;;;;   ("basic arithmetic" "subtraction"), and
165
;;;;   ("multiplication").
166
;;;;
167
;;;; WITH-TEST-PREFIX can be nested.  Each WITH-TEST-PREFIX postpends
168
;;;; a new element to the current prefix:
169
;;;;
170
;;;; (with-test-prefix "arithmetic"
171
;;;;   (with-test-prefix "addition"
172
;;;;     (pass-if "integer" (= (+ 2 2) 4))
173
;;;;     (pass-if "complex" (= (+ 2+3i 4+5i) 6+8i)))
174
;;;;   (with-test-prefix "subtraction"
175
;;;;     (pass-if "integer" (= (- 2 2) 0))
176
;;;;     (pass-if "complex" (= (- 2+3i 1+2i) 1+1i))))
177
;;;;
178
;;;; The four test names here are:
179
;;;;   ("arithmetic" "addition" "integer")
180
;;;;   ("arithmetic" "addition" "complex")
181
;;;;   ("arithmetic" "subtraction" "integer")
182
;;;;   ("arithmetic" "subtraction" "complex")
183
;;;;
184
;;;; To print a name for a human reader, we DISPLAY its elements,
185
;;;; separated by ": ".  So, the last set of test names would be
186
;;;; reported as:
187
;;;;
188
;;;;   arithmetic: addition: integer
189
;;;;   arithmetic: addition: complex
190
;;;;   arithmetic: subtraction: integer
191
;;;;   arithmetic: subtraction: complex
192
;;;;
193
;;;; The Guile benchmarks use with-test-prefix to include the name of
194
;;;; the source file containing the test in the test name, to help
195
;;;; developers to find failing tests, and to provide each file with its
196
;;;; own namespace.
197

198

199
;;;; REPORTERS
200
;;;;
201
;;;; A reporter is a function which we apply to each test outcome.
202
;;;; Reporters can log results, print interesting results to the
203
;;;; standard output, collect statistics, etc.
204
;;;;
205
;;;; A reporter function takes two mandatory arguments, RESULT and TEST, and
206
;;;; possibly additional arguments depending on RESULT; its return value
207
;;;; is ignored.  RESULT has one of the following forms:
208
;;;;
209
;;;; pass         - The test named TEST passed.
210
;;;;                Additional arguments are ignored.
211
;;;; upass        - The test named TEST passed unexpectedly.
212
;;;;                Additional arguments are ignored.
213
;;;; fail         - The test named TEST failed.
214
;;;;                Additional arguments are ignored.
215
;;;; xfail        - The test named TEST failed, as expected.
216
;;;;                Additional arguments are ignored.
217
;;;; unresolved   - The test named TEST did not perform as expected, for
218
;;;;                example the property that was to be tested could not be
219
;;;;                tested because something else went wrong.
220
;;;;                Additional arguments are ignored.
221
;;;; untested     - The test named TEST was not actually performed, for
222
;;;;                example because the test case is not complete yet.
223
;;;;                Additional arguments are ignored.
224
;;;; unsupported  - The test named TEST requires some feature that is not
225
;;;;                available in the configured testing environment.
226
;;;;                Additional arguments are ignored.
227
;;;; error        - An error occurred while the test named TEST was
228
;;;;                performed.  Since this result means that the system caught
229
;;;;                an exception it could not handle, the exception arguments
230
;;;;                are passed as additional arguments.
231
;;;;
232
;;;; This library provides some standard reporters for logging results
233
;;;; to a file, reporting interesting results to the user, and
234
;;;; collecting totals.
235
;;;;
236
;;;; You can use the REGISTER-REPORTER function and friends to add
237
;;;; whatever reporting functions you like.  If you don't register any
238
;;;; reporters, the library uses FULL-REPORTER, which simply writes
239
;;;; all results to the standard output.
240

241

242
;;;; MISCELLANEOUS
243
;;;;
244

245
;;; Define some exceptions which are commonly being tested for.
246
(define exception:bad-variable
247
  (cons 'syntax-error "Bad variable"))
248
(define exception:missing-expression
249
  (cons 'misc-error "^missing or extra expression"))
250
(define exception:out-of-range
251
  (cons 'out-of-range "^.*out of range"))
252
(define exception:unbound-var
253
  (cons 'unbound-variable "^Unbound variable"))
254
(define exception:used-before-defined
255
  (cons 'unbound-variable "^Variable used before given a value"))
256
(define exception:wrong-num-args
257
  (cons 'wrong-number-of-args "^Wrong number of arguments"))
258
(define exception:wrong-type-arg
259
  (cons 'wrong-type-arg "^Wrong type"))
260
(define exception:numerical-overflow
261
  (cons 'numerical-overflow "^Numerical overflow"))
262
(define exception:struct-set!-denied
263
  (cons 'misc-error "^set! denied for field"))
264
(define exception:system-error
265
  (cons 'system-error ".*"))
266
(define exception:miscellaneous-error
267
  (cons 'misc-error "^.*"))
268

269
;; as per throw in scm_to_locale_stringn()
270
(define exception:string-contains-nul
271
  (cons 'misc-error "^string contains #\\\\nul character"))
272

273

274
;;; Display all parameters to the default output port, followed by a newline.
275
(define (display-line . objs)
276
  (for-each display objs)
277
  (newline))
278

279
;;; Display all parameters to the given output port, followed by a newline.
280
(define (display-line-port port . objs)
281
  (for-each (lambda (obj) (display obj port)) objs)
282
  (newline port))
283

284

285
;;;; CORE FUNCTIONS
286
;;;;
287

288
;;; The central testing routine.
289
;;; The idea is taken from Greg, the GNUstep regression test environment.
290
(define run-test #f)
291
(let ((test-running #f))
292
  (define (local-run-test name expect-pass thunk)
293
    (if test-running
294
	(error "Nested calls to run-test are not permitted.")
295
	(let ((test-name (full-name name)))
296
	  (set! test-running #t)
297
	  (catch #t
298
	    (lambda ()
299
	      (let ((result (thunk)))
300
		(if (eq? result #t) (throw 'pass))
301
		(if (eq? result #f) (throw 'fail))
302
		(throw 'unresolved)))
303
	    (lambda (key . args)
304
	      (case key
305
		((pass)
306
		 (report (if expect-pass 'pass 'upass) test-name))
307
		((fail)
308
		 (report (if expect-pass 'fail 'xfail) test-name))
309
		((unresolved untested unsupported)
310
		 (report key test-name))
311
		((quit)
312
		 (report 'unresolved test-name)
313
		 (quit))
314
		(else
315
		 (report 'error test-name (cons key args))))))
316
	  (set! test-running #f))))
317
  (set! run-test local-run-test))
318

319
;;; A short form for tests that are expected to pass, taken from Greg.
320
(defmacro pass-if (name . rest)
321
  (if (and (null? rest) (pair? name))
322
      ;; presume this is a simple test, i.e. (pass-if (even? 2))
323
      ;; where the body should also be the name.
324
      `(run-test ',name #t (lambda () ,name))
325
      `(run-test ,name #t (lambda () ,@rest))))
326

327
;;; A short form for tests that are expected to fail, taken from Greg.
328
(defmacro expect-fail (name . rest)
329
  (if (and (null? rest) (pair? name))
330
      ;; presume this is a simple test, i.e. (expect-fail (even? 2))
331
      ;; where the body should also be the name.
332
      `(run-test ',name #f (lambda () ,name))
333
      `(run-test ,name #f (lambda () ,@rest))))
334

335
;;; A helper function to implement the macros that test for exceptions.
336
(define (run-test-exception name exception expect-pass thunk)
337
  (run-test name expect-pass
338
    (lambda ()
339
      (stack-catch (car exception)
340
	(lambda () (thunk) #f)
341
	(lambda (key proc message . rest)
342
	  (cond
343
           ;; handle explicit key
344
           ((string-match (cdr exception) message)
345
            #t)
346
           ;; handle `(error ...)' which uses `misc-error' for key and doesn't
347
           ;; yet format the message and args (we have to do it here).
348
           ((and (eq? 'misc-error (car exception))
349
                 (list? rest)
350
                 (string-match (cdr exception)
351
                               (apply simple-format #f message (car rest))))
352
            #t)
353
           ;; handle syntax errors which use `syntax-error' for key and don't
354
           ;; yet format the message and args (we have to do it here).
355
           ((and (eq? 'syntax-error (car exception))
356
                 (list? rest)
357
                 (string-match (cdr exception)
358
                               (apply simple-format #f message (car rest))))
359
            #t)
360
           ;; unhandled; throw again
361
           (else
362
            (apply throw key proc message rest))))))))
363

364
;;; A short form for tests that expect a certain exception to be thrown.
365
(defmacro pass-if-exception (name exception body . rest)
366
  `(,run-test-exception ,name ,exception #t (lambda () ,body ,@rest)))
367

368
;;; A short form for tests expected to fail to throw a certain exception.
369
(defmacro expect-fail-exception (name exception body . rest)
370
  `(,run-test-exception ,name ,exception #f (lambda () ,body ,@rest)))
371

372

373
;;;; TEST NAMES
374
;;;;
375

376
;;;; Turn a test name into a nice human-readable string.
377
(define (format-test-name name)
378
  (call-with-output-string
379
   (lambda (port)
380
     (let loop ((name name)
381
		(separator ""))
382
       (if (pair? name)
383
	   (begin
384
	     (display separator port)
385
	     (display (car name) port)
386
	     (loop (cdr name) ": ")))))))
387

388
;;;; For a given test-name, deliver the full name including all prefixes.
389
(define (full-name name)
390
  (append (current-test-prefix) (list name)))
391

392
;;; A fluid containing the current test prefix, as a list.
393
(define prefix-fluid (make-fluid))
394
(fluid-set! prefix-fluid '())
395
(define (current-test-prefix)
396
  (fluid-ref prefix-fluid))
397

398
;;; Postpend PREFIX to the current name prefix while evaluting THUNK.
399
;;; The name prefix is only changed within the dynamic scope of the
400
;;; call to with-test-prefix*.  Return the value returned by THUNK.
401
(define (with-test-prefix* prefix thunk)
402
  (with-fluids ((prefix-fluid
403
		 (append (fluid-ref prefix-fluid) (list prefix))))
404
    (thunk)))
405

406
;;; (with-test-prefix PREFIX BODY ...)
407
;;; Postpend PREFIX to the current name prefix while evaluating BODY ...
408
;;; The name prefix is only changed within the dynamic scope of the
409
;;; with-test-prefix expression.  Return the value returned by the last
410
;;; BODY expression.
411
(defmacro with-test-prefix (prefix . body)
412
  `(with-test-prefix* ,prefix (lambda () ,@body)))
413

414
;;; Call THUNK using the debugging evaluator.
415
(define (with-debugging-evaluator* thunk)
416
  (let ((dopts #f))
417
    (dynamic-wind
418
	(lambda ()
419
	  (set! dopts (debug-options))
420
	  (debug-enable 'debug))
421
	thunk
422
	(lambda ()
423
	  (debug-options dopts)))))
424

425
;;; Evaluate BODY... using the debugging evaluator.
426
(define-macro (with-debugging-evaluator . body)
427
  `(with-debugging-evaluator* (lambda () ,@body)))
428

429

430

... This diff was truncated because it exceeds the maximum size that can be displayed.

Also available in: Unified diff