;;;; miscellaneous side-effectful tests of LOAD ;;;; This software is part of the SBCL system. See the README file for ;;;; more information. ;;;; ;;;; While most of SBCL is derived from the CMU CL system, the test ;;;; files (like this one) were written from scratch after the fork ;;;; from CMU CL. ;;;; ;;;; This software is in the public domain and is provided with ;;;; absolutely no warranty. See the COPYING and CREDITS files for ;;;; more information. (defvar *tmp-filename* "load-test.tmp") ;;; Bug reported by Sean Ross: FASL loader set fill pointer to loaded ;;; simple arrays. (defvar *array*) (progn (with-open-file (s *tmp-filename* :direction :output :if-exists :supersede :if-does-not-exist :create) (print '(setq *array* #3a(((1 2) (2 1)) ((3 4) (4 3)))) s)) (let (tmp-fasl) (unwind-protect (progn (setq tmp-fasl (compile-file *tmp-filename*)) (let ((*array* nil)) (load tmp-fasl) (assert (arrayp *array*)) (assert (= (array-rank *array*) 3)) (assert (not (array-has-fill-pointer-p *array*))))) (when tmp-fasl (delete-file tmp-fasl)) (delete-file *tmp-filename*)))) ;;; rudimentary external-format test (dolist (ef '(:default :ascii :latin-1 :utf-8)) (with-open-file (s *tmp-filename* :direction :output :if-exists :supersede :if-does-not-exist :create) (print '(defun foo (x) (1+ x)) s)) (fmakunbound 'foo) (let (tmp-fasl) (unwind-protect (progn (setq tmp-fasl (compile-file *tmp-filename* :external-format ef)) (load tmp-fasl) (assert (= (foo 1) 2))) (when tmp-fasl (delete-file tmp-fasl)) (delete-file *tmp-filename*)))) ;;; As reported by David Tolpin *LOAD-PATHNAME* was not merged. (progn (defvar *saved-load-pathname*) (with-open-file (s *tmp-filename* :direction :output :if-exists :supersede :if-does-not-exist :create) (print '(setq *saved-load-pathname* *load-pathname*) s)) (let (tmp-fasl) (unwind-protect (progn (load *tmp-filename*) (assert (equal (merge-pathnames *tmp-filename*) *saved-load-pathname*))) (delete-file *tmp-filename*)))) ;;; Test many, many variations on LOAD. (defparameter *counter* 0) (defparameter *loaded-pathname* nil) (defparameter *loaded-truename* nil) (defparameter *test-program-string* (format nil "~ (incf *counter*) (setf *loaded-pathname* *load-pathname*) (setf *loaded-truename* *load-truename*)")) (defmacro load-and-assert (load-argument pathname truename) (let ((before (gensym))) `(let ((,before *counter*) *loaded-pathname* *loaded-truename*) (load ,load-argument :print t :verbose t) (assert (and (= (1+ ,before) *counter*) (equal ,(if pathname `(merge-pathnames ,pathname)) *loaded-pathname*) (equal ,(if pathname `(merge-pathnames ,truename)) *loaded-truename*)))))) (defmacro with-test-program (source fasl &body body) (let ((src (gensym)) (fsl (gensym))) `(let ((,src ,source) (,fsl ,fasl)) (with-open-file (*standard-output* ,src :direction :output :if-exists :supersede) (princ *test-program-string*)) (when ,fsl (compile-file ,src :output-file ,fsl)) (unwind-protect (progn ,@body) (when (probe-file ,src) (delete-file ,src)) (when (and ,fsl (probe-file ,fsl)) (delete-file ,fsl)))))) ;;; Loading from streams. ;; string-stream (with-input-from-string (s *test-program-string*) (load-and-assert s nil nil)) ;; file-stream associated with a source file (let ((source (pathname "load-impure-test.lisp"))) (with-test-program source nil (with-open-file (stream source) (load-and-assert stream source source)))) ;; file-stream associated with a fasl file (let* ((source (pathname "load-impure-test.lisp")) (fasl (compile-file-pathname source))) (with-test-program source fasl (with-open-file (stream fasl :element-type 'unsigned-byte) (load-and-assert fasl fasl fasl)))) ;; Develop a simple Gray stream to test loading from. (defclass load-impure-gray-stream (fundamental-character-input-stream) ((pointer :initform 0 :accessor load-impure-gray-stream-pointer))) (defmethod stream-read-char ((stream load-impure-gray-stream)) (with-accessors ((pointer load-impure-gray-stream-pointer)) stream (prog1 (if (>= pointer (length *test-program-string*)) :eof (char *test-program-string* pointer)) (incf pointer)))) (defmethod stream-unread-char ((stream load-impure-gray-stream) char) (with-accessors ((pointer load-impure-gray-stream-pointer)) stream (if (<= pointer 0) (error "fibber! you never read from this stream ~S" stream) (decf pointer))) nil) (with-open-stream (stream (make-instance 'load-impure-gray-stream)) (load-and-assert stream nil nil)) ;;; Loading from things named by pathname designators. ;; Test loading a source file by supplying a complete pathname. (let ((source (pathname "load-impure-test.lisp"))) (with-test-program source nil (load-and-assert source source source))) ;; Test loading a source file when supplying a partial pathname. (let ((source (pathname "load-impure-test.lisp")) (partial (pathname "load-impure-test"))) (with-test-program source nil (load-and-assert partial source source))) ;; Test loading a source file whose name lacks a type when supplying a ;; partial pathname. (let ((source (make-pathname :type :unspecific :defaults (pathname "load-impure-test"))) (partial (pathname "load-impure-test"))) (with-test-program source nil (load-and-assert partial partial partial))) ;; Test loading a fasl (let* ((source (pathname "load-impure-test.lisp")) (fasl (compile-file-pathname source))) (with-test-program source fasl (load-and-assert fasl fasl fasl))) ;; Test loading a fasl when supplying a partial pathname. (let* ((source (pathname "load-impure-test.lisp")) (fasl (compile-file-pathname source)) (partial (pathname "load-impure-test"))) (with-test-program source fasl (load-and-assert partial fasl fasl))) ;; Test loading a fasl whose name lacks a type when supplying a ;; partial pathname. (let* ((source (pathname "load-impure-test.lisp")) (fasl (make-pathname :type :unspecific :defaults (compile-file-pathname source))) (partial (pathname "load-impure-test"))) (with-test-program source fasl (load-and-assert partial partial partial))) ;; Test loading a fasl with a strange type (let* ((source (pathname "load-impure-test.lisp")) (fasl (make-pathname :defaults (compile-file-pathname source) :type "compiled-lisp"))) (with-test-program source fasl (load-and-assert fasl fasl fasl))) ;;; Errors ;; Ensure that loading a fasl specified with a type checks for the ;; header. (let* ((source (pathname "load-impure-test.lisp")) (fasl (compile-file-pathname source))) (with-test-program source fasl (with-open-file (f fasl :direction :io :if-exists :overwrite :element-type '(unsigned-byte 8)) (write-byte 0 f)) (handler-case (load fasl) (sb-fasl::fasl-header-missing () :ok)))) ;; Ensure that loading a fasl specified without a type checks for the ;; header. Note: this wasn't the behavior in ;; src/code/target-load.lisp v1.40 and earlier (SBCL version 1.0.12.35 ;; or so). If target-load.lisp is reverted to that state eventually, ;; this test should be removed (or that definition of LOAD altered). (let* ((source (pathname "load-impure-test.lisp")) (fasl (compile-file-pathname source)) (fasl-spec (make-pathname :type nil :defaults (compile-file-pathname source)))) (with-test-program source fasl (with-open-file (f fasl :direction :io :if-exists :overwrite :element-type '(unsigned-byte 8)) (write-byte 0 f)) (handler-case (load fasl-spec) (sb-fasl::fasl-header-missing () :ok)))) ;; Ensure that we get an error when the source file is newer than the ;; fasl and the supplied argument is an incomplete pathname. (let* ((source (pathname "load-impure-test.lisp")) (fasl (compile-file-pathname source)) (spec (make-pathname :type nil :defaults source))) (with-test-program source fasl (sleep 1) (with-open-file (*standard-output* source :direction :output :if-exists :append) (write-line ";;comment")) (handler-case (load spec) ;; IWBNI the error signalled here were more specific than ;; SIMPLE-ERROR. (error () :|well, we got an error!|)))) ;; Ensure that we can invoke the restart SOURCE in the above case. (let* ((source (pathname "load-impure-test.lisp")) (fasl (compile-file-pathname source)) (spec (make-pathname :type nil :defaults source))) (with-test-program source fasl (sleep 1) (with-open-file (*standard-output* source :direction :output :if-exists :append) (write-line ";;comment")) (handler-bind ((error (lambda (error) (declare (ignore error)) (when (find-restart 'sb-fasl::source) (invoke-restart 'sb-fasl::source))))) (load-and-assert spec source source)))) ;; Ensure that we can invoke the restart OBJECT in the above case. (let* ((source (pathname "load-impure-test.lisp")) (fasl (compile-file-pathname source)) (spec (make-pathname :type nil :defaults source))) (with-test-program source fasl (sleep 1) (with-open-file (*standard-output* source :direction :output :if-exists :append) (write-line ";;comment")) (handler-bind ((error (lambda (error) (declare (ignore error)) (when (find-restart 'sb-fasl::object) (invoke-restart 'sb-fasl::object))))) (load-and-assert spec fasl fasl))))