replace transform: don't fall on NIL.
[sbcl.git] / tests / test-util.lisp
blob1046b68c0a4675b748fa4e323ca8204f141753f8
1 #+gc-stress
2 (sb-thread:make-thread (lambda ()
3 (loop (gc :full t) (sleep 0.001)))
4 :name "gc stress")
6 (defpackage :test-util
7 (:use :cl :sb-ext)
8 (:export #:with-test #:report-test-status #:*failures*
9 #:really-invoke-debugger
10 #:*break-on-failure* #:*break-on-expected-failure*
11 #:*elapsed-times*
13 ;; type tools
14 #:random-type
15 #:type-evidently-=
16 #:ctype=
17 #:type-specifiers-equal
18 #:assert-tri-eq
19 #:random-type
20 #:deep-size
22 ;; thread tools
23 #:*n-cpus*
24 #:make-kill-thread #:make-join-thread
25 #:wait-for-threads
26 #:process-all-interrupts
27 #:test-interrupt
28 ;; cause tests to run in multiple threads
29 #:enable-test-parallelism
31 ;; MAP-OPTIMIZATION-*
32 #:map-optimization-quality-combinations
33 #:map-optimize-declarations
35 ;; CHECKED-COMPILE and friends
36 #:checked-compile #:checked-compile-and-assert
37 #:checked-compile-capturing-source-paths
38 #:checked-compile-condition-source-paths
39 #:assemble
40 #:get-simple-fun-instruction-model
42 #:scratch-dir-name
43 #:scratch-file-name
44 #:*scratch-file-prefix*
45 #:with-scratch-file
46 #:with-test-directory
47 #:generate-test-directory-name
48 #:*test-directory*
49 #:opaque-identity
50 #:runtime #:split-string #:integer-sequence #:shuffle))
52 (in-package :test-util)
54 (defvar *test-count* 0)
55 (defvar *test-file* nil)
56 (defvar *failures* nil)
57 (defvar *break-on-failure* nil)
58 (defvar *break-on-expected-failure* nil)
60 (defvar *threads-to-kill*)
61 (defvar *threads-to-join*)
63 (defvar *n-cpus*
64 (max 1
65 #-win32 (sb-alien:alien-funcall
66 (sb-alien:extern-alien "sysconf"
67 (function sb-alien:long sb-alien:int))
68 sb-unix::sc-nprocessors-onln)
69 #+win32 (sb-alien:extern-alien "os_number_of_processors" sb-alien:int)))
71 (defun setenv (name value)
72 #-win32
73 (let ((r (sb-alien:alien-funcall
74 (sb-alien:extern-alien
75 "setenv" (function sb-alien:int (sb-alien:c-string :not-null t)
76 (sb-alien:c-string :not-null t) sb-alien:int))
77 name value 1)))
78 (if (minusp r)
79 (error "setenv: ~a" (sb-int:strerror))
80 r))
81 #+win32
82 (let ((r (sb-alien:alien-funcall
83 (sb-alien:extern-alien "_putenv" (function sb-alien:int (sb-alien:c-string :not-null t)))
84 (format nil "~A=~A" name value))))
85 (if (minusp r)
86 (error "putenv: ~a" (sb-int:strerror))
87 r)))
89 (setenv "SBCL_MACHINE_TYPE" (machine-type))
90 (setenv "SBCL_SOFTWARE_TYPE" (software-type))
93 ;;; Type tools
95 (defun random-type (n)
96 `(integer ,(random n) ,(+ n (random n))))
98 (defun type-evidently-= (x y)
99 (and (subtypep x y) (subtypep y x)))
101 (defun type-specifiers-equal (left right)
102 (let ((a (sb-kernel:values-specifier-type left)))
103 ;; SPECIFIER-TYPE is a memoized function, and TYPE= is a trivial
104 ;; operation if A and B are EQ.
105 ;; To actually exercise the type operation, remove the memoized parse.
106 (sb-int:drop-all-hash-caches)
107 (let ((b (sb-kernel:values-specifier-type right)))
108 (sb-kernel:type= a b))))
109 ;;; This isn't a great name. Prefer to use TYPE-SPECIFIERS-EQUAL instead
110 (defun ctype= (a b) (type-specifiers-equal a b))
112 (defmacro assert-tri-eq (expected-result expected-certainp form)
113 (sb-int:with-unique-names (result certainp)
114 `(multiple-value-bind (,result ,certainp) ,form
115 (assert (eq ,expected-result ,result))
116 (assert (eq ,expected-certainp ,certainp)))))
119 ;;; Thread tools
121 (defun make-kill-thread (&rest args)
122 #-sb-thread (error "can't make-kill-thread ~s" args)
123 #+sb-thread
124 (let ((thread (apply #'sb-thread:make-thread args)))
125 #-win32 ;; poor thread interruption on safepoints
126 (when (boundp '*threads-to-kill*)
127 (push thread *threads-to-kill*))
128 thread))
130 #+sb-thread
131 (defun make-join-thread (&rest args)
132 (let ((thread (apply #'sb-thread:make-thread args)))
133 (when (boundp '*threads-to-join*)
134 (push thread *threads-to-join*))
135 thread))
137 (defun wait-for-threads (threads)
138 (mapc (lambda (thread) (sb-thread:join-thread thread :default nil)) threads)
139 (assert (not (some #'sb-thread:thread-alive-p threads))))
141 (defun process-all-interrupts (&optional (thread sb-thread:*current-thread*))
142 (sb-ext:wait-for (null (sb-thread::thread-interruptions thread))))
144 (defun test-interrupt (function-to-interrupt &optional quit-p)
145 ;; Tests of interrupting a newly created thread can fail if the creator runs
146 ;; so quickly that it bypasses execution of the created thread. So the creator
147 ;; needs to wait, to have a facsimile of the situation prior to implementation
148 ;; of the so-called pauseless thread start feature.
149 ;; Wouldn't you know, it's just reintroducing a startup semaphore.
150 ;; And interruption tests are even more likely to fail with :sb-safepoint.
151 ;; Noneless, this tries to be robust enough to pass.
152 (let* ((sem (sb-thread:make-semaphore))
153 (child (make-kill-thread
154 (lambda ()
155 (sb-thread:signal-semaphore sem)
156 (funcall function-to-interrupt)))))
157 (sb-thread:wait-on-semaphore sem)
158 (format t "interrupting child ~A~%" child)
159 (sb-thread:interrupt-thread child
160 (lambda ()
161 (format t "child pid ~A~%" sb-thread:*current-thread*)
162 (when quit-p (sb-thread:abort-thread))))
163 (process-all-interrupts child)
164 child))
166 (defun log-msg (stream &rest args)
167 (prog1 (apply #'format stream "~&::: ~@?~%" args)
168 (force-output stream)))
170 (defun log-msg/non-pretty (stream &rest args)
171 (let ((*print-pretty* nil))
172 (apply #'log-msg stream args)))
174 (defvar *elapsed-times*)
175 (defun record-test-elapsed-time (test-name start-time)
176 (let ((et (- (get-internal-real-time) start-time)))
177 ;; ATOMIC in case we have within-file test concurrency
178 ;; (not sure if it actually works, but it looks right anyway)
179 (sb-ext:atomic-push (cons et test-name) *elapsed-times*)))
181 (defun run-test (test-function name fails-on
182 &aux (start-time (get-internal-real-time)))
183 (start-test)
184 (catch 'skip-test
185 (let (#+sb-thread (threads (sb-thread:list-all-threads))
186 (*threads-to-join* nil)
187 (*threads-to-kill* nil))
188 (handler-bind ((error (lambda (error)
189 (if (expected-failure-p fails-on)
190 (fail-test :expected-failure name error)
191 (fail-test :unexpected-failure name error))
192 (return-from run-test)))
193 (timeout (lambda (error)
194 (if (expected-failure-p fails-on)
195 (fail-test :expected-failure name error t)
196 (fail-test :unexpected-failure name error t))
197 (return-from run-test))))
198 ;; Non-pretty is for cases like (with-test (:name (let ...)) ...
199 (log-msg/non-pretty *trace-output* "Running ~S" name)
200 (funcall test-function)
201 #+sb-thread
202 (let ((any-leftover nil))
203 (dolist (thread *threads-to-join*)
204 (ignore-errors (sb-thread:join-thread thread)))
205 (dolist (thread *threads-to-kill*)
206 (ignore-errors (sb-thread:terminate-thread thread)))
207 (setf threads (union (union *threads-to-kill*
208 *threads-to-join*)
209 threads))
210 (dolist (thread (sb-thread:list-all-threads))
211 (unless (or (not (sb-thread:thread-alive-p thread))
212 (eql (the sb-thread:thread thread)
213 sb-thread:*current-thread*)
214 (member thread threads)
215 (sb-thread:thread-ephemeral-p thread))
216 (setf any-leftover thread)
217 #-win32
218 (ignore-errors (sb-thread:terminate-thread thread))))
219 (when any-leftover
220 (fail-test :leftover-thread name any-leftover)
221 (return-from run-test)))
222 (if (expected-failure-p fails-on)
223 (fail-test :unexpected-success name nil)
224 ;; Non-pretty is for cases like (with-test (:name (let ...)) ...
225 (log-msg/non-pretty *trace-output* "Success ~S" name)))))
226 (record-test-elapsed-time name start-time))
228 ;;; Like RUN-TEST but do not perform any of the automated thread management.
229 ;;; Since multiple threads are executing tests, there is no reason to kill
230 ;;; unrecognized threads.
231 (sb-ext:define-load-time-global *output-mutex* (sb-thread:make-mutex :name "run-tests output"))
232 (defun run-test-concurrently (test-spec)
233 (destructuring-bind (test-body . name) test-spec
234 (sb-thread:with-mutex (*output-mutex*)
235 (log-msg/non-pretty *trace-output* "Running ~S" name))
236 (let ((stream (make-string-output-stream)))
237 (let ((*standard-output* stream)
238 (*error-output* stream))
239 (let ((f (compile nil `(lambda () ,@test-body))))
240 (funcall f))
241 (let ((string (get-output-stream-string stream)))
242 (sb-thread:with-mutex (*output-mutex*)
243 (when (plusp (length string))
244 (log-msg/non-pretty *trace-output* "Output from ~S" name)
245 (write-string string *trace-output*))
246 (log-msg/non-pretty *trace-output* "Success ~S" name)))))))
248 (defvar *deferred-test-forms*)
249 (defun enable-test-parallelism ()
250 (let ((n (sb-ext:posix-getenv "SBCL_TEST_PARALLEL")))
251 (when n
252 (setq *deferred-test-forms* (vector (parse-integer n) nil nil)))))
254 ;;; Tests which are not broken in any way and do not mandate sequential
255 ;;; execution are pushed on a worklist to execute in multiple threads.
256 ;;; The purpose of running tests in parallel is to exercise the compiler
257 ;;; to show that it works without acquiring the world lock,
258 ;;; but the nice side effect is that the tests finish quicker.
259 (defmacro with-test ((&key fails-on broken-on skipped-on name serial slow)
260 &body body)
261 ;; Failing and skipped tests are written into a summary file which is later read back.
262 ;; To guarantee readability there can't be symbols in random packages.
263 (setq name (sb-int:named-let ensure-ok ((x name))
264 (etypecase x
265 (cons (cons (ensure-ok (car x)) (ensure-ok (cdr x))))
266 (symbol (let ((package (symbol-package x)))
267 (if (or (null package)
268 (sb-int:system-package-p package)
269 (eql package (find-package "CL"))
270 (eql package (find-package "KEYWORD")))
271 x (copy-symbol x))))
272 (integer x)
273 (character `(code-char ,(char-code x)))
274 (string x))))
275 (cond
276 ((broken-p broken-on)
277 `(progn
278 (start-test)
279 (fail-test :skipped-broken ',name "Test broken on this platform")))
280 ((skipped-p skipped-on)
281 `(progn
282 (start-test)
283 (fail-test :skipped-disabled ',name "Test disabled for this combination of platform and features")))
284 ((and (boundp '*deferred-test-forms*)
285 (not serial)
286 (or (not fails-on)
287 (not (expected-failure-p fails-on))))
288 ;; To effectively parallelize calls to COMPILE, we must defer compilation
289 ;; until a worker thread has picked off the test from shared worklist.
290 ;; Thus we push only the form to be compiled, not a lambda.
291 `(push (cons ',body ',name)
292 (elt *deferred-test-forms* ,(if slow 1 2))))
294 `(run-test (lambda () ,@body)
295 ',name
296 ',fails-on))))
298 (defun report-test-status ()
299 (with-standard-io-syntax
300 (with-open-file (stream #.(merge-pathnames "test-status.lisp-expr"
301 *load-pathname*)
302 :direction :output
303 :if-exists :supersede)
304 (format stream "~s~%" *failures*))))
306 (defun start-test ()
307 (unless (eq *test-file* *load-pathname*)
308 (setf *test-file* *load-pathname*)
309 (setf *test-count* 0))
310 (incf *test-count*))
312 (defun really-invoke-debugger (condition)
313 (with-simple-restart (continue "Continue")
314 (let ((*invoke-debugger-hook* *invoke-debugger-hook*))
315 (enable-debugger)
316 (invoke-debugger condition))))
318 (defun fail-test (type test-name condition &optional backtrace)
319 (if (stringp condition)
320 (log-msg *trace-output* "~@<~A ~S ~:_~A~:>"
321 type test-name condition)
322 (log-msg *trace-output* "~@<~A ~S ~:_due to ~S: ~4I~:_\"~A\"~:>"
323 type test-name (type-of condition) condition))
324 (push (list type *test-file* (or test-name *test-count*))
325 *failures*)
326 (unless (stringp condition)
327 (when backtrace
328 (sb-debug:print-backtrace :from :interrupted-frame))
329 (when (or (and *break-on-failure*
330 (not (eq type :expected-failure)))
331 *break-on-expected-failure*)
332 (really-invoke-debugger condition))))
334 (defun expected-failure-p (fails-on)
335 (sb-impl::featurep fails-on))
337 (defun broken-p (broken-on)
338 (sb-impl::featurep broken-on))
340 (defun skipped-p (skipped-on)
341 (sb-impl::featurep skipped-on))
343 ;;;; MAP-{OPTIMIZATION-QUALITY-COMBINATIONS,OPTIMIZE-DECLARATIONS}
345 (sb-int:defconstant-eqx +optimization-quality-names+
346 '(speed safety debug compilation-speed space) #'equal)
348 (sb-int:defconstant-eqx +optimization-quality-keywords+
349 '(:speed :safety :debug :compilation-speed :space) #'equal)
351 (deftype optimization-quality-range-designator ()
352 '(or (eql nil) ; skip quality
353 (integer 0 3) ; one value
354 (cons (or (eql nil) (integer 0 3)) list) ; list of values, nil means skip
355 (eql t))) ; all values
357 ;;; Call FUNCTION with the specified combinations of optimization
358 ;;; quality values.
360 ;;; MAP-OPTIMIZATION-QUALITY-COMBINATIONS calls FUNCTION with keyword
361 ;;; argument thus expecting a lambda list of the form
363 ;;; (&key speed safety debug compilation-speed space)
365 ;;; or any subset compatible with the generated combinations.
367 ;;; MAP-OPTIMIZE-DECLARATIONS calls FUNCTION with a list intended to
368 ;;; be spliced into a DECLARE form like this:
370 ;;; (lambda (quality-values)
371 ;;; `(declare (optimize ,@quality-values)))
373 ;;; The set of combinations is controlled via keyword arguments
375 ;;; :FILTER FILTER-FUNCTION
376 ;;; A function that should be called with optimization quality
377 ;;; keyword arguments and whose return value controls whether
378 ;;; FUNCTION should be called for the given combination.
380 ;;; (:SPEED | :SAFETY | :DEBUG | :COMPILATION-SPEED | :SPACE) SPEC
381 ;;; Specify value range for the given optimization quality. SPEC
382 ;;; can be
384 ;;; NIL
385 ;;; Omit the quality.
387 ;;; (INTEGER 0 3)
389 ;;; Use the specified value for the quality.
391 ;;; (NIL | (INTEGER 0 3))*
392 ;;; Generate the specified values. A "value" of NIL omits the
393 ;;; quality from the combination.
395 ;;; T
396 ;;; Generate all values (0, 1, 2, 3) for the quality.
397 (declaim (ftype (function #.`(function
398 &key
399 ,@(mapcar #'list +optimization-quality-keywords+
400 '#1=(optimization-quality-range-designator . #1#))
401 (:filter function)))
402 map-optimization-quality-combinations
403 map-optimize-declarations))
404 (defun map-optimization-quality-combinations
405 (function &key (speed t) (safety t) (debug t) (compilation-speed t) (space t)
406 filter)
407 (labels ((map-quantity-values (values thunk)
408 (typecase values
409 ((eql t)
410 (dotimes (i 4) (funcall thunk i)))
411 (cons
412 (map nil thunk values))
413 ((integer 0 3)
414 (funcall thunk values))))
415 (one-quality (qualities specs values)
416 (let ((quality (first qualities))
417 (spec (first specs)))
418 (cond
419 ((not quality)
420 (when (or (not filter) (apply filter values))
421 (apply function values)))
422 ((not spec)
423 (one-quality (rest qualities) (rest specs) values))
425 (map-quantity-values
426 spec
427 (lambda (value)
428 (one-quality (rest qualities) (rest specs)
429 (if value
430 (list* quality value values)
431 values)))))))))
432 (one-quality +optimization-quality-keywords+
433 (list speed safety debug compilation-speed space)
434 '())))
436 (defun map-optimize-declarations
437 (function &rest args
438 &key speed safety debug compilation-speed space filter)
439 (declare (ignore speed safety debug compilation-speed space filter))
440 (apply #'map-optimization-quality-combinations
441 (lambda (&rest args &key &allow-other-keys)
442 (funcall function (loop for name in +optimization-quality-names+
443 for keyword in +optimization-quality-keywords+
444 for value = (getf args keyword)
445 when value collect (list name value))))
446 args))
448 (defun expand-optimize-specifier (specifier)
449 (etypecase specifier
450 (cons
451 specifier)
452 ((eql nil)
453 '(:speed nil :safety nil :debug nil :compilation-speed nil :space nil))
454 ((eql :default)
455 '(:speed 1 :safety 1 :debug 1 :compilation-speed 1 :space 1))
456 ((eql :maximally-safe)
457 (list :filter (lambda (&key safety &allow-other-keys)
458 (= safety 3))))
459 ((eql :safe)
460 (list :filter (lambda (&key speed safety &allow-other-keys)
461 (and (> safety 0) (>= safety speed)))))
462 ((eql :quick)
463 '(:compilation-speed 1 :space 1))
464 ((eql :quick/incomplete)
465 '(:compilation-speed nil :space nil))
466 ((eql :all)
467 '())))
469 (defun map-optimization-quality-combinations* (function specifier)
470 (apply #'map-optimization-quality-combinations
471 function (expand-optimize-specifier specifier)))
473 (defun map-optimize-declarations* (function specifier)
474 (apply #'map-optimize-declarations
475 function (expand-optimize-specifier specifier)))
477 ;;;; CHECKED-COMPILE
479 (defun prepare-form (thing &key optimize)
480 (cond
481 ((functionp thing)
482 (error "~@<~S is a function, not a form.~@:>" thing))
483 ((not optimize)
484 thing)
485 ((typep thing '(cons (eql sb-int:named-lambda)))
486 `(,@(subseq thing 0 3)
487 (declare (optimize ,@optimize))
488 ,@(nthcdr 3 thing)))
489 ((typep thing '(cons (eql lambda)))
490 `(,(first thing) ,(second thing)
491 (declare (optimize ,@optimize))
492 ,@(nthcdr 2 thing)))
494 (error "~@<Cannot splice ~A declaration into forms other than ~
495 ~{~S~#[~; and ~:;, ~]~}: ~S.~@:>"
496 'optimize '(lambda sb-int:named-lambda) thing))))
498 (defun compile-capturing-output-and-conditions
499 (form &key name condition-transform)
500 (let ((warnings '())
501 (style-warnings '())
502 (notes '())
503 (compiler-errors '())
504 (error-output (make-string-output-stream)))
505 (flet ((maybe-transform (condition)
506 (if condition-transform
507 (funcall condition-transform condition)
508 condition)))
509 (handler-bind ((sb-ext:compiler-note
510 (lambda (condition)
511 (push (maybe-transform condition) notes)
512 (muffle-warning condition)))
513 (style-warning
514 (lambda (condition)
515 (push (maybe-transform condition) style-warnings)
516 (muffle-warning condition)))
517 (warning
518 (lambda (condition)
519 (push (maybe-transform condition) warnings)
520 (muffle-warning condition)))
521 (sb-c:compiler-error
522 (lambda (condition)
523 (push (maybe-transform condition) compiler-errors))))
524 (multiple-value-bind (function warnings-p failure-p)
525 (let ((*error-output* error-output))
526 (compile name form))
527 (values function warnings-p failure-p
528 warnings style-warnings notes compiler-errors
529 error-output))))))
531 (defun print-form-and-optimize (stream form-and-optimize &optional colonp atp)
532 (declare (ignore colonp atp))
533 (destructuring-bind (form . optimize) form-and-optimize
534 (format stream "~@:_~@:_~2@T~S~@:_~@:_~
535 with ~:[~
536 default optimization policy~
538 ~:*~@:_~@:_~2@T~S~@:_~@:_~
539 optimization policy~
541 form optimize)))
543 (defun print-signaled-conditions (stream conditions &optional colonp atp)
544 (declare (ignore colonp atp))
545 (format stream "~{~@:_~@:_~{~/sb-ext:print-symbol-with-prefix/: ~A~}~}"
546 (mapcar (lambda (condition)
547 (list (type-of condition) condition))
548 conditions)))
550 ;;; Compile FORM capturing and muffling all [style-]warnings and notes
551 ;;; and return six values: 1) the compiled function 2) a Boolean
552 ;;; indicating whether compilation failed 3) a list of warnings 4) a
553 ;;; list of style-warnings 5) a list of notes 6) a list of
554 ;;; SB-C:COMPILER-ERROR conditions.
556 ;;; An error can be signaled when COMPILE indicates failure as well as
557 ;;; in case [style-]warning or note conditions are signaled. The
558 ;;; keyword parameters
559 ;;; ALLOW-{FAILURE,[STYLE-]WARNINGS,NOTES,COMPILER-ERRORS} control
560 ;;; this behavior. All but ALLOW-NOTES default to NIL.
562 ;;; Arguments to the
563 ;;; ALLOW-{FAILURE,[STYLE-]WARNINGS,NOTES,COMPILER-ERRORS} keyword
564 ;;; parameters are interpreted as type specifiers restricting the
565 ;;; allowed conditions of the respective kind.
567 ;;; When supplied, the value of CONDITION-TRANSFORM has to be a
568 ;;; function of one argument, the condition currently being
569 ;;; captured. The returned value is captured and later returned in
570 ;;; place of the condition.
571 (defun checked-compile (form
572 &key
573 name
574 allow-failure
575 allow-warnings
576 allow-style-warnings
577 (allow-notes t)
578 (allow-compiler-errors allow-failure)
579 condition-transform
580 optimize)
581 (sb-int:binding* ((prepared-form (prepare-form form :optimize optimize))
582 ((function nil failure-p
583 warnings style-warnings notes compiler-errors
584 error-output)
585 (compile-capturing-output-and-conditions
586 prepared-form :name name :condition-transform condition-transform)))
587 (labels ((fail (kind conditions &optional allowed-type)
588 (error "~@<Compilation of~/test-util::print-form-and-optimize/ ~
589 signaled ~A~P:~/test-util::print-signaled-conditions/~
590 ~@[~@:_~@:_Allowed type is ~
591 ~/sb-impl:print-type-specifier/.~]~@:>"
592 (cons form optimize) kind (length conditions) conditions
593 allowed-type))
594 (check-conditions (kind conditions allow)
595 (cond
596 (allow
597 (let ((offenders (remove-if (lambda (condition)
598 (typep condition allow))
599 conditions)))
600 (when offenders
601 (fail kind offenders allow))))
602 (conditions
603 (fail kind conditions)))))
605 (when (and (not allow-failure) failure-p)
606 (let ((output (get-output-stream-string error-output)))
607 (error "~@<Compilation of~/test-util::print-form-and-optimize/ ~
608 failed~@[ with output~
609 ~@:_~@:_~2@T~@<~@;~A~:>~@:_~@:_~].~@:>"
610 (cons form optimize) (when (plusp (length output)) output))))
612 (check-conditions "warning" warnings allow-warnings)
613 (check-conditions "style-warning" style-warnings allow-style-warnings)
614 (check-conditions "note" notes allow-notes)
615 (check-conditions "compiler-error" compiler-errors allow-compiler-errors)
617 ;; Since we may have prevented warnings from being taken
618 ;; into account for FAILURE-P by muffling them, adjust the
619 ;; second return value accordingly.
620 (values function (when (or failure-p warnings) t)
621 warnings style-warnings notes compiler-errors))))
623 (defun print-arguments (stream arguments &optional colonp atp)
624 (declare (ignore colonp atp))
625 (format stream "~:[~
626 without arguments ~
627 ~;~:*~
628 with arguments~@:_~@:_~
629 ~2@T~@<~{~S~^~@:_~}~:>~@:_~@:_~
631 arguments))
633 (defun call-capturing-values-and-conditions (function &rest args)
634 (let ((values nil)
635 (conditions '()))
636 (block nil
637 (handler-bind ((condition (lambda (condition)
638 (push condition conditions)
639 (typecase condition
640 (warning
641 (muffle-warning condition))
642 (serious-condition
643 (return))))))
644 (setf values (multiple-value-list (apply function args)))))
645 (values values (nreverse conditions))))
647 (defun %checked-compile-and-assert-one-case
648 (form optimize function args-thunk expected test allow-conditions)
649 (if (eq args-thunk :return-type)
650 (let ((type (sb-kernel:%simple-fun-type function)))
651 (unless (or (eq type 'function)
652 #.(and (not (member :unwind-to-frame-and-call-vop sb-impl:+internal-features+))
653 '(member '(debug 3) optimize :test #'equal))
654 (type-specifiers-equal (caddr type) expected))
655 (error "~@<The derived type of~
656 ~/test-util::print-form-and-optimize/ ~
657 is ~/sb-impl:print-type-specifier/
658 while
659 ~/sb-impl:print-type-specifier/
660 is expected~@:>"
661 (cons form optimize) type expected)))
662 (let ((args (multiple-value-list (funcall args-thunk))))
663 (flet ((failed-to-signal (expected-type)
664 (error "~@<Calling the result of compiling~
665 ~/test-util::print-form-and-optimize/ ~
666 ~/test-util::print-arguments/~
667 returned normally instead of signaling a ~
668 condition of type ~
669 ~/sb-impl:print-type-specifier/.~@:>"
670 (cons form optimize) args expected-type))
671 (signaled-unexpected (conditions)
672 (error "~@<Calling the result of compiling~
673 ~/test-util::print-form-and-optimize/ ~
674 ~/test-util::print-arguments/~
675 signaled unexpected condition~P~
676 ~/test-util::print-signaled-conditions/~
677 .~@:>"
678 (cons form optimize) args (length conditions) conditions))
679 (returned-unexpected (values expected test)
680 (error "~@<Calling the result of compiling~
681 ~/test-util::print-form-and-optimize/ ~
682 ~/test-util::print-arguments/~
683 returned values~@:_~@:_~
684 ~2@T~<~{~S~^~@:_~}~:>~@:_~@:_~
685 which is not ~S to~@:_~@:_~
686 ~2@T~<~{~S~^~@:_~}~:>~@:_~@:_~
687 .~@:>"
688 (cons form optimize) args
689 (list values) test (list expected))))
690 (multiple-value-bind (values conditions)
691 (apply #'call-capturing-values-and-conditions function args)
692 (typecase expected
693 ((cons (eql condition) (cons t null))
694 (let* ((expected-condition-type (second expected))
695 (unexpected (remove-if (lambda (condition)
696 (typep condition
697 expected-condition-type))
698 conditions))
699 (expected (set-difference conditions unexpected)))
700 (cond
701 (unexpected
702 (signaled-unexpected unexpected))
703 ((null expected)
704 (failed-to-signal expected-condition-type)))))
706 (let ((expected (funcall expected)))
707 (cond
708 ((and conditions
709 (not (and allow-conditions
710 (every (lambda (condition)
711 (typep condition allow-conditions))
712 conditions))))
713 (signaled-unexpected conditions))
714 ((not (funcall test values expected))
715 (returned-unexpected values expected test)))))))))))
717 (defun %checked-compile-and-assert-one-compilation
718 (form optimize other-checked-compile-args cases)
719 (let ((function (apply #'checked-compile form
720 (if optimize
721 (list* :optimize optimize
722 other-checked-compile-args)
723 other-checked-compile-args))))
724 (loop for (args-thunk values test allow-conditions) in cases
725 do (%checked-compile-and-assert-one-case
726 form optimize function args-thunk values test allow-conditions))))
728 (defun %checked-compile-and-assert (form checked-compile-args cases)
729 (let ((optimize (getf checked-compile-args :optimize))
730 (other-args (loop for (key value) on checked-compile-args by #'cddr
731 unless (eq key :optimize)
732 collect key and collect value)))
733 (map-optimize-declarations*
734 (lambda (&optional optimize)
735 (%checked-compile-and-assert-one-compilation
736 form optimize other-args cases))
737 optimize)))
739 ;;; Compile FORM using CHECKED-COMPILE, then call the resulting
740 ;;; function with arguments and assert expected return values
741 ;;; according to CASES.
743 ;;; Elements of CASES are of the form
745 ;;; ((&rest ARGUMENT-FORMS) VALUES-FORM &key TEST ALLOW-CONDITIONS)
747 ;;; where ARGUMENT-FORMS are evaluated to produce the arguments for
748 ;;; one call of the function and VALUES-FORM is evaluated to produce
749 ;;; the expected return values for that function call.
751 ;;; TEST is used to compare a list of the values returned by the
752 ;;; function call to the list of values obtained by calling
753 ;;; VALUES-FORM.
755 ;;; If supplied, the value of ALLOW-CONDITIONS is a type-specifier
756 ;;; indicating which conditions should be allowed (and ignored) during
757 ;;; the function call.
759 ;;; If VALUES-FORM is of the form
761 ;;; (CONDITION CONDITION-TYPE)
763 ;;; the function call is expected to signal the designated condition
764 ;;; instead of returning values. CONDITION-TYPE is evaluated.
766 ;;; The OPTIMIZE keyword parameter controls the optimization policies
767 ;;; (or policy) used when compiling FORM. The argument is interpreted
768 ;;; as described for MAP-OPTIMIZE-DECLARATIONS*.
770 ;;; The other keyword parameters, NAME and
771 ;;; ALLOW-{WARNINGS,STYLE-WARNINGS,NOTES}, behave as with
772 ;;; CHECKED-COMPILE.
773 (defmacro checked-compile-and-assert ((&key name
774 allow-warnings
775 allow-style-warnings
776 (allow-notes t)
777 (optimize :quick))
778 form &body cases)
779 (flet ((make-case-form (case)
780 (if (typep case '(cons (member :return-type)))
781 `',case
782 (destructuring-bind (args values &key (test ''equal testp)
783 allow-conditions)
784 case
785 (let ((conditionp (typep values '(cons (eql condition) (cons t null)))))
786 (when (and testp conditionp)
787 (sb-ext:with-current-source-form (case)
788 (error "~@<Cannot use ~S with ~S ~S.~@:>"
789 values :test test)))
790 `(list (lambda () (values ,@args))
791 ,(if conditionp
792 `(list 'condition ,(second values))
793 `(lambda () (multiple-value-list ,values)))
794 ,test
795 ,allow-conditions))))))
796 `(%checked-compile-and-assert
797 ,form (list :name ,name
798 :allow-warnings ,allow-warnings
799 :allow-style-warnings ,allow-style-warnings
800 :allow-notes ,allow-notes
801 :optimize ,optimize)
802 (list ,@(mapcar #'make-case-form cases)))))
804 ;;; Like CHECKED-COMPILE, but for each captured condition, capture and
805 ;;; later return a cons
807 ;;; (CONDITION . SOURCE-PATH)
809 ;;; instead. SOURCE-PATH is the path of the source form associated to
810 ;;; CONDITION.
811 (defun checked-compile-capturing-source-paths (form &rest args)
812 (labels ((context-source-path ()
813 (let ((context (sb-c::find-error-context nil)))
814 (sb-c::compiler-error-context-original-source-path
815 context)))
816 (add-source-path (condition)
817 (cons condition (context-source-path))))
818 (apply #'checked-compile form :condition-transform #'add-source-path
819 args)))
821 ;;; Similar to CHECKED-COMPILE, but allow compilation failure and
822 ;;; warnings and only return source paths associated to those
823 ;;; conditions.
824 (defun checked-compile-condition-source-paths (form)
825 (let ((source-paths '()))
826 (labels ((context-source-path ()
827 (let ((context (sb-c::find-error-context nil)))
828 (sb-c::compiler-error-context-original-source-path
829 context)))
830 (push-source-path (condition)
831 (declare (ignore condition))
832 (push (context-source-path) source-paths)))
833 (checked-compile form
834 :allow-failure t
835 :allow-warnings t
836 :allow-style-warnings t
837 :condition-transform #'push-source-path))
838 (nreverse source-paths)))
840 ;;; Repeat calling THUNK until its cumulated runtime, measured using
841 ;;; GET-INTERNAL-RUN-TIME, is larger than PRECISION. Repeat this
842 ;;; REPETITIONS many times and return the time one call to THUNK took
843 ;;; in seconds as a float, according to the minimum of the cumulated
844 ;;; runtimes over the repetitions.
845 ;;; This allows to easily measure the runtime of expressions that take
846 ;;; much less time than one internal time unit. Also, the results are
847 ;;; unaffected, modulo quantization effects, by changes to
848 ;;; INTERNAL-TIME-UNITS-PER-SECOND.
849 ;;; Taking the minimum is intended to reduce the error introduced by
850 ;;; garbage collections occurring at unpredictable times. The inner
851 ;;; loop doubles the number of calls to THUNK each time before again
852 ;;; measuring the time spent, so that the time measurement overhead
853 ;;; doesn't distort the result if calling THUNK takes very little time.
854 (defun runtime* (thunk repetitions precision)
855 (loop repeat repetitions
856 minimize
857 (loop with start = (get-internal-run-time)
858 with duration = 0
859 for n = 1 then (* n 2)
860 for total-runs = n then (+ total-runs n)
861 for gc-start = *gc-run-time*
862 do (dotimes (i n)
863 (funcall thunk))
864 (setf duration (- (get-internal-run-time) start
865 (- *gc-run-time* gc-start)))
866 when (> duration precision)
867 return (/ (float duration)
868 (float total-runs)))
869 into min-internal-time-units-per-call
870 finally (return (/ min-internal-time-units-per-call
871 (float internal-time-units-per-second)))))
873 (defmacro runtime (form &key (repetitions 5) (precision (* 30
874 (/ internal-time-units-per-second 1000))))
875 `(runtime* (lambda () ,form) ,repetitions ,precision))
877 (declaim (notinline opaque-identity))
878 (defun opaque-identity (x) x)
879 (compile 'opaque-identity) ; in case this file was loaded as interpreted code
881 (defun split-string (string delimiter)
882 (loop for begin = 0 then (1+ end)
883 for end = (position delimiter string) then (position delimiter string :start begin)
884 collect (subseq string begin end)
885 while end))
887 (defun integer-sequence (n)
888 (loop for i below n collect i))
890 (defun shuffle (sequence)
891 (typecase sequence
892 (list
893 (coerce (shuffle (coerce sequence 'vector)) 'list))
894 (vector ; destructive
895 (let ((vector sequence))
896 (loop for lim from (1- (length vector)) downto 0
897 for chosen = (random (1+ lim))
898 unless (= chosen lim)
899 do (rotatef (aref vector chosen) (aref vector lim)))
900 vector))))
902 ;;; Return a random file name to avoid writing into the source tree.
903 ;;; We can't use any of the interfaces provided in libc because those are inadequate
904 ;;; for purposes of COMPILE-FILE. This is not trying to be robust against attacks.
905 (defvar *scratch-file-prefix* "sbcl-scratch")
906 (defun scratch-dir-name ()
907 (let ((dir (posix-getenv #+win32 "TMP" #+unix "TMPDIR"))
908 (file (format nil "~a~d/" *scratch-file-prefix* (sb-unix:unix-getpid))))
909 (if dir
910 (namestring
911 (merge-pathnames
912 file (parse-native-namestring dir nil *default-pathname-defaults*
913 :as-directory t)))
914 (concatenate 'string "/tmp/" file))))
915 (defun scratch-file-name (&optional extension)
916 (let ((a (make-array 10 :element-type 'character)))
917 (dotimes (i 10)
918 (setf (aref a i) (code-char (+ (char-code #\a) (random 26)))))
919 (let ((dir (posix-getenv #+win32 "TMP" #+unix "TMPDIR"))
920 (file (format nil "~a~d~a~@[.~a~]" *scratch-file-prefix*
921 (sb-unix:unix-getpid) a extension)))
922 (if dir
923 (namestring
924 (merge-pathnames
925 ;; TRUENAME - wtf ?
926 file (truename (parse-native-namestring dir nil *default-pathname-defaults*
927 :as-directory t))))
928 (concatenate 'string "/tmp/" file)))))
930 (defmacro with-scratch-file ((var &optional extension) &body forms)
931 (sb-int:with-unique-names (tempname)
932 `(let ((,tempname (scratch-file-name ,extension)))
933 (unwind-protect
934 (let ((,var ,tempname)) ,@forms) ; rebind, as test might asssign into VAR
935 (ignore-errors (delete-file ,tempname))))))
937 (defvar *test-directory*)
939 (defun generate-test-directory-name ()
940 ;; Why aren't we using TMPDIR???
941 (merge-pathnames
942 (make-pathname :directory `(:relative ,(write-to-string (sb-unix:unix-getpid))))
943 (parse-native-namestring (posix-getenv "TEST_DIRECTORY")
944 nil *default-pathname-defaults*
945 :as-directory t)))
947 (defun call-with-test-directory (fn)
948 ;; FIXME: this writes into the source directory depending on whether
949 ;; TEST_DIRECTORY has been made to point elsewhere or not.
950 (let ((test-directory (generate-test-directory-name)))
951 (ensure-directories-exist test-directory)
952 (unwind-protect
953 ;; WHY REBIND *DEFAULT-PATHNAME-DEFAULTS* ? THIS SUCKS!
954 ;; (It means we can't use the WITH-TEST-DIRECTORY macro for most things)
955 (let ((*default-pathname-defaults* test-directory)
956 (*test-directory* test-directory))
957 (funcall fn test-directory))
958 (delete-directory test-directory :recursive t))))
960 (defmacro with-test-directory ((&optional (test-directory-var (gensym)))
961 &body body)
962 `(call-with-test-directory (lambda (,test-directory-var)
963 (declare (ignorable ,test-directory-var))
964 ,@body)))
966 ;;; Take a list of lists and assemble them as though they are
967 ;;; instructions inside the body of a vop. There is no need
968 ;;; to use the INST macro in front of each list.
969 ;;; As a special case, if an atom is the symbol LABEL, it will be
970 ;;; changed to a generated label. At most one such atom may appear.
971 (defun assemble (instructions)
972 (let ((segment (sb-assem:make-segment))
973 (label))
974 (sb-assem:assemble (segment 'nil)
975 (dolist (inst instructions)
976 (setq inst (copy-list inst))
977 (mapl (lambda (cell &aux (x (car cell)))
978 (when (and (symbolp x) (string= x "LABEL"))
979 (setq label (sb-assem:gen-label))
980 (rplaca cell label)))
981 inst)
982 (apply #'sb-assem:inst* (car inst) (cdr inst)))
983 (when label
984 (sb-assem::%emit-label segment nil label)))
985 (sb-assem:segment-buffer
986 (sb-assem:finalize-segment segment))))
988 (defun get-simple-fun-instruction-model (fun)
989 (declare (type sb-kernel:simple-fun fun))
990 (sb-disassem:get-inst-space) ; for effect
991 (let* ((code (sb-kernel:fun-code-header fun))
992 (segment (sb-disassem:make-code-segment code
993 (sb-sys:sap- (sb-vm:simple-fun-entry-sap fun)
994 (sb-kernel:code-instructions code))
995 (sb-kernel:%simple-fun-text-len fun)))
996 (dstate (sb-disassem:make-dstate nil)))
997 (setf (sb-disassem::dstate-absolutize-jumps dstate) nil
998 (sb-disassem:dstate-segment dstate) segment
999 (sb-disassem:dstate-segment-sap dstate) (funcall (sb-disassem:seg-sap-maker segment)))
1000 (sb-int:collect ((result))
1001 (loop (let ((pc (sb-disassem:dstate-cur-offs dstate)))
1002 (result (cons pc (sb-disassem:disassemble-instruction dstate))))
1003 (when (>= (sb-disassem:dstate-cur-offs dstate) (sb-disassem:seg-length segment))
1004 (return)))
1005 (result))))
1007 ;;; If a test file is not very tolerant of the statistical profiler, then
1008 ;;; it should call this. There seem to be at least 2 common categories of failure:
1009 ;;; * the stack exhaustion test can get a profiling signal when you're already
1010 ;;; near exhaustion, and then the profiler exhausts the stack, which isn't
1011 ;;; handled very well. Example:
1012 ;;; Control stack exhausted, fault: 0xd76b9ff8, PC: 0x806cad2
1013 ;;; 0: fp=0xd76ba008 pc=0x806cad2 Foreign function search_dynamic_space
1014 ;;; 1: fp=0xd76ba028 pc=0x80633d1 Foreign function search_all_gc_spaces
1015 ;;; 2: fp=0xd76ba048 pc=0x805647e Foreign function component_ptr_from_pc
1016 ;;; 3: fp=0xd76baa18 pc=0x8065dfd Foreign function (null)
1017 ;;; 4: fp=0xd76baa98 pc=0x806615a Foreign function record_backtrace_from_context
1018 ;;; 5: fp=0xd76baab8 pc=0x806625e Foreign function sigprof_handler
1019 ;;; * debugger-related tests. I'm not sure why.
1020 (defun disable-profiling ()
1021 (when (find-package "SB-SPROF")
1022 (format t "INFO: disabling SB-SPROF~%")
1023 (funcall (intern "STOP-PROFILING" "SB-SPROF"))))
1025 ;;; This unexported symbol emulates SB-RT. Please don't use it in new tests
1026 (defmacro deftest (name form &rest results) ; use SB-RT syntax
1027 `(test-util:with-test (:name ,(sb-int:keywordicate name))
1028 (assert (equalp (multiple-value-list ,form) ',results))))
1030 ;;; Compute size of OBJ including descendants.
1031 ;;; LEAFP specifies what object types to treat as not reaching
1032 ;;; any other object. You pretty much have to treat symbols
1033 ;;; as leaves, otherwise you reach a package and then the result
1034 ;;; just explodes to beyond the point of being useful.
1035 ;;; (It works, but might reach the entire heap)
1036 ;;; To turn this into an actual thing, we'd want to reduce the consing.
1037 (defun deep-size (obj &optional (leafp (lambda (x)
1038 (typep x '(or package symbol sb-kernel:fdefn
1039 function sb-kernel:code-component
1040 sb-kernel:layout sb-kernel:classoid)))))
1041 (let ((worklist (list obj))
1042 (seen (make-hash-table :test 'eq))
1043 (tot-bytes 0))
1044 (setf (gethash obj seen) t)
1045 (flet ((visit (thing)
1046 (when (sb-vm:is-lisp-pointer (sb-kernel:get-lisp-obj-address thing))
1047 (unless (or (funcall leafp thing)
1048 (gethash thing seen))
1049 (push thing worklist)
1050 (setf (gethash thing seen) t)))))
1051 (loop
1052 (unless worklist (return))
1053 (let ((x (pop worklist)))
1054 (incf tot-bytes (primitive-object-size x))
1055 (sb-vm:do-referenced-object (x visit)))))
1056 ;; Secondary values is number of visited objects not incl. original one.
1057 (values tot-bytes
1058 (1- (hash-table-count seen))
1059 seen)))
1061 ;;; x86-64 does not permit var-alloc to translate %make-funcallable-instance
1062 ;;; so this case has to be amenable to fixed-alloc.
1063 (defun make-funcallable-instance (n)
1064 (funcall
1065 (compile nil
1066 `(lambda () (sb-kernel:%make-funcallable-instance ,n)))))