2 (sb-thread:make-thread
(lambda ()
3 (loop (gc :full t
) (sleep 0.001)))
8 (:export
#:with-test
#:report-test-status
#:*failures
*
9 #:really-invoke-debugger
10 #:*break-on-failure
* #:*break-on-expected-failure
*
17 #:type-specifiers-equal
24 #:make-kill-thread
#:make-join-thread
26 #:process-all-interrupts
28 ;; cause tests to run in multiple threads
29 #:enable-test-parallelism
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
40 #:get-simple-fun-instruction-model
44 #:*scratch-file-prefix
*
47 #:generate-test-directory-name
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
*)
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
)
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
))
79 (error "setenv: ~a" (sb-int:strerror
))
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
))))
86 (error "putenv: ~a" (sb-int:strerror
))
89 (setenv "SBCL_MACHINE_TYPE" (machine-type))
90 (setenv "SBCL_SOFTWARE_TYPE" (software-type))
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
)))))
121 (defun make-kill-thread (&rest args
)
122 #-sb-thread
(error "can't make-kill-thread ~s" args
)
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
*))
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
*))
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
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
161 (format t
"child pid ~A~%" sb-thread
:*current-thread
*)
162 (when quit-p
(sb-thread:abort-thread
))))
163 (process-all-interrupts 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)))
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
)
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
*
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
)
218 (ignore-errors (sb-thread:terminate-thread thread
))))
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
))))
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")))
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
)
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
))
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")))
273 (character `(code-char ,(char-code x
)))
276 ((broken-p broken-on
)
279 (fail-test :skipped-broken
',name
"Test broken on this platform")))
280 ((skipped-p skipped-on
)
283 (fail-test :skipped-disabled
',name
"Test disabled for this combination of platform and features")))
284 ((and (boundp '*deferred-test-forms
*)
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
)
298 (defun report-test-status ()
299 (with-standard-io-syntax
300 (with-open-file (stream #.
(merge-pathnames "test-status.lisp-expr"
303 :if-exists
:supersede
)
304 (format stream
"~s~%" *failures
*))))
307 (unless (eq *test-file
* *load-pathname
*)
308 (setf *test-file
* *load-pathname
*)
309 (setf *test-count
* 0))
312 (defun really-invoke-debugger (condition)
313 (with-simple-restart (continue "Continue")
314 (let ((*invoke-debugger-hook
* *invoke-debugger-hook
*))
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
*))
326 (unless (stringp condition
)
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
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
385 ;;; Omit the quality.
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.
396 ;;; Generate all values (0, 1, 2, 3) for the quality.
397 (declaim (ftype (function #.
`(function
399 ,@(mapcar #'list
+optimization-quality-keywords
+
400 '#1=(optimization-quality-range-designator .
#1#))
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
)
407 (labels ((map-quantity-values (values thunk
)
410 (dotimes (i 4) (funcall thunk i
)))
412 (map nil thunk values
))
414 (funcall thunk values
))))
415 (one-quality (qualities specs values
)
416 (let ((quality (first qualities
))
417 (spec (first specs
)))
420 (when (or (not filter
) (apply filter values
))
421 (apply function values
)))
423 (one-quality (rest qualities
) (rest specs
) values
))
428 (one-quality (rest qualities
) (rest specs
)
430 (list* quality value values
)
432 (one-quality +optimization-quality-keywords
+
433 (list speed safety debug compilation-speed space
)
436 (defun map-optimize-declarations
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
))))
448 (defun expand-optimize-specifier (specifier)
453 '(:speed nil
:safety nil
:debug nil
:compilation-speed nil
:space nil
))
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
)
460 (list :filter
(lambda (&key speed safety
&allow-other-keys
)
461 (and (> safety
0) (>= safety speed
)))))
463 '(:compilation-speed
1 :space
1))
464 ((eql :quick
/incomplete
)
465 '(:compilation-speed nil
:space nil
))
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
)))
479 (defun prepare-form (thing &key optimize
)
482 (error "~@<~S is a function, not a form.~@:>" thing
))
485 ((typep thing
'(cons (eql sb-int
:named-lambda
)))
486 `(,@(subseq thing
0 3)
487 (declare (optimize ,@optimize
))
489 ((typep thing
'(cons (eql lambda
)))
490 `(,(first thing
) ,(second thing
)
491 (declare (optimize ,@optimize
))
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
)
503 (compiler-errors '())
504 (error-output (make-string-output-stream)))
505 (flet ((maybe-transform (condition)
506 (if condition-transform
507 (funcall condition-transform condition
)
509 (handler-bind ((sb-ext:compiler-note
511 (push (maybe-transform condition
) notes
)
512 (muffle-warning condition
)))
515 (push (maybe-transform condition
) style-warnings
)
516 (muffle-warning condition
)))
519 (push (maybe-transform condition
) warnings
)
520 (muffle-warning condition
)))
523 (push (maybe-transform condition
) compiler-errors
))))
524 (multiple-value-bind (function warnings-p failure-p
)
525 (let ((*error-output
* error-output
))
527 (values function warnings-p failure-p
528 warnings style-warnings notes compiler-errors
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~@:_~@:_~
536 default optimization policy~
538 ~:*~@:_~@:_~2@T~S~@:_~@:_~
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
))
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.
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
578 (allow-compiler-errors allow-failure
)
581 (sb-int:binding
* ((prepared-form (prepare-form form
:optimize optimize
))
582 ((function nil failure-p
583 warnings style-warnings notes compiler-errors
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
594 (check-conditions (kind conditions allow
)
597 (let ((offenders (remove-if (lambda (condition)
598 (typep condition allow
))
601 (fail kind offenders allow
))))
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
))
628 with arguments~@:_~@:_~
629 ~2@T~@<~{~S~^~@:_~}~:>~@:_~@:_~
633 (defun call-capturing-values-and-conditions (function &rest args
)
637 (handler-bind ((condition (lambda (condition)
638 (push condition conditions
)
641 (muffle-warning condition
))
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/
659 ~/sb-impl:print-type-specifier/
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 ~
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/~
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~^~@:_~}~:>~@:_~@:_~
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
)
693 ((cons (eql condition
) (cons t null
))
694 (let* ((expected-condition-type (second expected
))
695 (unexpected (remove-if (lambda (condition)
697 expected-condition-type
))
699 (expected (set-difference conditions unexpected
)))
702 (signaled-unexpected unexpected
))
704 (failed-to-signal expected-condition-type
)))))
706 (let ((expected (funcall expected
)))
709 (not (and allow-conditions
710 (every (lambda (condition)
711 (typep condition allow-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
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
))
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
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
773 (defmacro checked-compile-and-assert
((&key name
779 (flet ((make-case-form (case)
780 (if (typep case
'(cons (member :return-type
)))
782 (destructuring-bind (args values
&key
(test ''equal testp
)
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.~@:>"
790 `(list (lambda () (values ,@args
))
792 `(list 'condition
,(second values
))
793 `(lambda () (multiple-value-list ,values
)))
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
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
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
816 (add-source-path (condition)
817 (cons condition
(context-source-path))))
818 (apply #'checked-compile form
:condition-transform
#'add-source-path
821 ;;; Similar to CHECKED-COMPILE, but allow compilation failure and
822 ;;; warnings and only return source paths associated to those
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
830 (push-source-path (condition)
831 (declare (ignore condition
))
832 (push (context-source-path) source-paths
)))
833 (checked-compile form
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
857 (loop with start
= (get-internal-run-time)
859 for n
= 1 then
(* n
2)
860 for total-runs
= n then
(+ total-runs n
)
861 for gc-start
= *gc-run-time
*
864 (setf duration
(- (get-internal-run-time) start
865 (- *gc-run-time
* gc-start
)))
866 when
(> duration precision
)
867 return
(/ (float duration
)
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
)
887 (defun integer-sequence (n)
888 (loop for i below n collect i
))
890 (defun shuffle (sequence)
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
)))
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
))))
912 file
(parse-native-namestring dir nil
*default-pathname-defaults
*
914 (concatenate 'string
"/tmp/" file
))))
915 (defun scratch-file-name (&optional extension
)
916 (let ((a (make-array 10 :element-type
'character
)))
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
)))
926 file
(truename (parse-native-namestring dir nil
*default-pathname-defaults
*
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
)))
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???
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
*
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
)
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)))
962 `(call-with-test-directory (lambda (,test-directory-var
)
963 (declare (ignorable ,test-directory-var
))
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
))
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
)))
982 (apply #'sb-assem
:inst
* (car inst
) (cdr inst
)))
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
))
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
))
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
)))))
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.
1058 (1- (hash-table-count 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)
1066 `(lambda () (sb-kernel:%make-funcallable-instance
,n
)))))