summaryrefslogtreecommitdiffstats
path: root/emacs.d/nxhtml/tests/ert.el
diff options
context:
space:
mode:
Diffstat (limited to 'emacs.d/nxhtml/tests/ert.el')
-rw-r--r--emacs.d/nxhtml/tests/ert.el2418
1 files changed, 0 insertions, 2418 deletions
diff --git a/emacs.d/nxhtml/tests/ert.el b/emacs.d/nxhtml/tests/ert.el
deleted file mode 100644
index 491d79f..0000000
--- a/emacs.d/nxhtml/tests/ert.el
+++ /dev/null
@@ -1,2418 +0,0 @@
-;;; ert.el --- Emacs Lisp Regression Testing
-
-;; Modified by Lennart Borgman 2008-07-13 to make all global symbols
-;; use the "ert-" prefix.
-
-;; Copyright (C) 2007, 2008 Christian M. Ohler
-
-;; Author: Christian M. Ohler
-;; Version: 0.2
-;; Keywords: lisp, tools
-
-;; This file is NOT part of GNU Emacs.
-
-;; This program is free software: you can redistribute it and/or
-;; modify it under the terms of the GNU General Public License as
-;; published by the Free Software Foundation, either version 3 of the
-;; License, or (at your option) any later version.
-;;
-;; This program is distributed in the hope that it will be useful, but
-;; WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;; General Public License for more details.
-;;
-;; You should have received a copy of the GNU General Public License
-;; along with this program. If not, see `http://www.gnu.org/licenses/'.
-
-;;; Commentary:
-
-;; ERT is a tool for automated testing in Emacs Lisp. Its main
-;; features are facilities for defining and running test cases and
-;; reporting the results as well as for debugging test failures
-;; interactively.
-;;
-;; The main entry points are `ert-deftest', which is similar to
-;; `defun' but defines a test, and `ert-run-tests-interactively',
-;; which runs tests and offers an interactive interface for inspecting
-;; results and debugging. There is also `ert-run-tests-batch' for
-;; non-interactive use.
-;;
-;; The body of `ert-deftest' forms resembles a function body, but the
-;; additional operators `should', `should-not' and `should-error' are
-;; available. `should' is similar to cl's `assert', but signals a
-;; different error when its condition is violated that is caught and
-;; processed by ERT. In addition, it analyzes its argument form and
-;; records information that helps debugging (`assert' tries to do
-;; something similar when its second argument SHOW-ARGS is true, but
-;; `should' is more sophisticated). For information on `should-not'
-;; and `should-error', see their docstrings.
-;;
-;; For example,
-;;
-;; ;; Define a test named `foo'.
-;; (ert-deftest foo ()
-;; (ert-should (= (+ 1 2) 4)))
-;;
-;; ;; Run it.
-;; (ert-run-tests-interactively 'foo)
-;;
-;; generates the following output (in addition to some statistics) in
-;; the *ert* results buffer:
-;;
-;; F foo
-;; (ert-test-failed
-;; ((ert-should
-;; (=
-;; (+ 1 2)
-;; 4))
-;; :form
-;; (= 3 4)
-;; :value nil))
-;;
-;; This indicates that the test failed. The `should' form that failed
-;; was (ert-should (= (+ 1 2) 4)), because its inner form, after
-;; evaluation of its arguments, was the function call (= 3 4), which
-;; returned nil.
-;;
-;; Obviously, this is a bug in the test case, not in the functions `+'
-;; or `='. In the results buffer, with point on the test result, the
-;; key "." can be used to jump to the definition of the test to modify
-;; it to correct the bug. After evaluating the modified definition
-;; and switching back to the results buffer, the key "r" will re-run
-;; the test and show the new result.
-
-
-;; Test selectors
-;;
-;; Functions like `ert-run-tests-interactively' accept a test
-;; selector, which is a Lisp expression specifying a set of tests.
-;; Each test name is a selector that refers to that test, the selector
-;; `t' refers to all tests, and the selector `:failed' refers to all
-;; tests that failed; but more complex selectors are available. Test
-;; selector syntax is similar to cl's type specifier syntax. See the
-;; docstring of `ert-select-tests' for details.
-
-
-;; Comparison with other testing tools
-;;
-;; ERT allows test-driven development similar to *Unit frameworks for
-;; other languages. However, two common *Unit features are notably
-;; absent from ERT: fixtures and test suites.
-;;
-;; Fixtures, as used e.g. in SUnit or JUnit, have two main purposes:
-;; Setting up (and tearing down) an environment for a set of test
-;; cases, and making that environment accessible through object
-;; attributes that can be used like local variables.
-;;
-;; While fixtures are a great syntactic simplification in other
-;; languages, they are not very useful in Lisp, where higher-order
-;; functions and `unwind-protect' are available. One way to implement
-;; and use a fixture in ERT is
-;;
-;; (defun my-fixture (body)
-;; (unwind-protect
-;; (progn ...set up...
-;; (funcall body))
-;; ...tear down...))
-;;
-;; (ert-deftest my-test ()
-;; (my-fixture
-;; (lambda ()
-;; ...test code...)))
-;;
-;; (Another way would be a `with-my-fixture' macro.) This solves the
-;; set-up and tear-down part, and additionally allows any test case to
-;; use any combination of fixtures, so it is more general than what
-;; other tools typically allow.
-;;
-;; If the test case needs access to the environment the fixture sets
-;; up, the fixture can be modified to pass arguments to the body.
-;;
-;; These are standard Lisp idioms. Special syntax for them could be
-;; added easily enough, but would provide only a minor simplification.
-;;
-;; (Note that splitting set-up and tear-down into separate functions,
-;; like *Unit tools usually do, makes it impossible to establish
-;; dynamic `let' bindings as part of the fixture. So, blindly
-;; imitating the way fixtures are implemented in other languages would
-;; be counter-productive in Lisp.)
-;;
-;;
-;; The purpose of test suites is to group related test cases together.
-;; The most common use of this is to run just the tests for one
-;; particular module. Since symbol prefixes are the usual way of
-;; separating module namespaces in Emacs Lisp, test selectors already
-;; solve this by allowing regexp matching on test names; e.g., the
-;; selector "^ert-" selects ERT's self-tests.
-;;
-;; If test suites containing arbitrary sets of tests are found to be
-;; desirable, it would be easy to add a `define-test-selector'
-;; mechanism that introduces a new selector, defined in terms of
-;; existing ones; e.g.
-;;
-;; ;; Note that `define-test-selector' does not exist yet.
-;; (define-test-selector my-test-suite () `(member foo-test bar-test))
-;;
-;; would define a test suite named `my-test-suite' consisting of
-;; `foo-test' and `bar-test'. See also `deftype' in Common Lisp.
-
-
-;; TODO: Add `skip' feature for tests that can't run in current environment.
-
-
-;;; Code:
-
-(eval-when-compile (require 'cl))
-(require 'ewoc)
-(require 'find-func)
-(require 'debug)
-
-(defvar ert-debug-on-error nil
- "Non-nil means enter debugger when a test fails or terminates with an error.")
-
-
-;;; Defining and locating tests.
-
-;; The data structure that represents a test case.
-(defstruct ert-test
- (name nil)
- (documentation nil)
- (body (assert nil))
- (most-recent-result nil)
- (expected-result-type 'ert-test-passed))
-
-(defun ert-test-boundp (symbol)
- "Return non-nil if SYMBOL names a test."
- (and (get symbol 'ert-test) t))
-
-(defun ert-get-test (symbol)
- "If SYMBOL names a test, return that. Signal an error otherwise."
- (assert (ert-test-boundp symbol) t)
- (get symbol 'ert-test))
-
-(defun ert-set-test (symbol doc definition)
- "Make SYMBOL name the test DEFINITION, and return DEFINITION."
- (when doc
- (put symbol 'ert-test-documentation doc))
- (put symbol 'ert-test definition)
- definition)
-
-(defun ert-make-test-unbound (symbol)
- "Make SYMBOL name no test. Return SYMBOL."
- (remprop symbol 'ert-test)
- symbol)
-
-(defun ert-test-result-expected-p (test result)
- "Return non-nil if RESULT matches the expected result type for TEST."
- (typep result (ert-test-expected-result-type test)))
-
-(defvar ert-find-test-regexp
- (concat "^\\s-*(ert-deftest"
- find-function-space-re
- "%s\\(\\s-\\|$\\)")
- "The regexp the `find-function' mechanisms use for locating test definitions.")
-
-(eval-and-compile
- (defun ert-parse-keys-and-body (docstr keys-and-body)
- "Split KEYS-AND-BODY into keyword-and-value pairs and the remaining body.
-
-KEYS-AND-BODY should have the form of a property list, with the
-exception that only keywords are permitted as keys and that the
-tail -- the body -- is a list of forms that does not start with a
-keyword.
-
-Returns a two-element list containing the keys-and-values plist
-and the body."
- (unless (stringp docstr)
- (when docstr
- (setq keys-and-body (cons docstr keys-and-body))
- (setq docstr nil)))
- (let ((extracted-key-accu '())
- (remaining keys-and-body))
- (while (and (consp remaining) (keywordp (first remaining)))
- (let ((keyword (pop remaining)))
- (unless (consp remaining)
- (error "Value expected after keyword %S in %S"
- keyword keys-and-body))
- (when (assoc keyword extracted-key-accu)
- (warn "Keyword %S appears more than once in %S" keyword
- keys-and-body))
- (push (cons keyword (pop remaining)) extracted-key-accu)))
- (setq extracted-key-accu (nreverse extracted-key-accu))
- (list (loop for (key . value) in extracted-key-accu
- collect key
- collect value)
- docstr
- remaining))))
-
-(defvar ert-error-on-test-redefinition nil)
-
-;;;###autoload
-(defmacro* ert-deftest (name ()
- &optional docstr
- &body keys-and-body)
- "Define NAME (a symbol) as a test.
-
-\(fn NAME () [:documentation DOCSTRING] [:expected-result TYPE] BODY...)"
- ;; The :documentation would be unreadable. I have therefore added
- ;; docstr that will look like documentation use to in Emacs. Maybe
- ;; add function ert-describe-test?
- (declare (indent 2)
- (debug (&define :name test name sexp
- [&optional [":documentation" stringp]]
- [&optional [":expected-result" sexp]]
- def-body)))
- (destructuring-bind ((&key (expected-result nil expected-result-supplied-p)
- (documentation nil documentation-supplied-p))
- doc
- body)
- (ert-parse-keys-and-body docstr keys-and-body)
- `(progn
- ;; Guard against missing/badly named tests:
- (when (and ert-error-on-test-redefinition
- (symbolp ',name)
- (get ',name 'ert-test))
- (with-output-to-temp-buffer "*Ert Error*"
- (with-current-buffer "*Ert Error*"
- (insert "Test "
- (format "%s" ',name)
- " is already defined in "
- (format "%s" (find-definition-noselect ',name 'ert-deftest))
- "\n\n"
- "Tip: Use `ert-delete-all-tests' or `ert-delete-test' before redefining tests."
- )))
- (if (y-or-n-p "Do you want to call ert-delete-all-tests and then continue? ")
- ;; Fix-me: This does not work, why?
- (ert-delete-all-tests)
- (error "Test %s is already defined in %s"
- ',name
- (find-definition-noselect ',name 'ert-deftest))))
- (ert-set-test ',name
- nil ;;doc
- (make-ert-test
- :name ',name
- :body (lambda () ,@body)
- ,@(when expected-result-supplied-p
- `(:expected-result-type ,expected-result))
- ,@(when documentation-supplied-p
- `(:documentation ,documentation))))
- ;; This hack allows `symbol-file' to associate `ert-deftest'
- ;; forms with files, and therefore enables `find-function' to
- ;; work with tests. However, it leads to warnings in
- ;; `unload-feature', which doesn't know how to undefine tests
- ;; and has no mechanism for extension.
- (push '(ert-deftest . ,name) current-load-list)
- ',name)))
-
-(defun ert-read-test-name (prompt &optional default-value history)
- "Read the name of a test and return it as a symbol.
-Prompt with PROMPT. By default, return DEFAULT-VALUE."
- (when (symbolp default-value) (setq default-value (symbol-name default-value)))
- (intern (completing-read prompt obarray #'ert-test-boundp
- t nil history default-value nil)))
-
-(defun ert-find-test-other-window (test-name)
- "Find, in another window, the definition of TEST-NAME."
- (interactive (list (ert-read-test-name "Find test definition: ")))
- (find-function-do-it test-name 'ert-deftest 'switch-to-buffer-other-window))
-
-(defun ert-delete-test (test-name)
- "An interactive interface to `ert-make-test-unbound'."
- (interactive (list (let ((default (thing-at-point 'symbol)))
- (when default
- (set-text-properties 0 (length default) nil default)
- (when (or (string= default "nil") (intern-soft default))
- (setq default (intern default)))
- (unless (ert-test-boundp default)
- (setq default nil)))
- (completing-read (if (null default)
- "Delete test: "
- (format "Delete test (default %s): "
- default))
- obarray #'ert-test-boundp
- 'really-require-match
- nil nil default nil))))
- (ert-make-test-unbound test-name))
-
-(defun ert-delete-all-tests ()
- "Make all symbols in `obarray' name no test."
- (interactive)
- (when (interactive-p)
- (unless (y-or-n-p "Delete all tests? ")
- (error "Aborted")))
- (mapc #'ert-delete-test (mapcar #'ert-test-name (ert-select-tests t t)))
- t)
-
-
-(defun ert-make-end-marker (buffer must-exist)
- "Return a marker to the end of buffer BUFFER.
-BUFFER may be a string or a buffer. If BUFFER does not exist
-return nil.
-
-The buffer must exist if MUST-EXIST is non-nil.
-
-See also:
- `ert-end-of-messages'
- `ert-end-of-warnings'"
- (let ((buf (if must-exist
- (get-buffer buffer)
- (get-buffer-create buffer))))
- (when (and buf
- (bufferp buf)
- (buffer-live-p buf))
- (with-current-buffer buf
- (save-restriction
- (widen)
- (point-max-marker))))))
-
-(defun ert-end-of-messages ()
- "Return a marker to the end of *Messages* buffer."
- (ert-make-end-marker "*Messages*" nil))
-
-(defun ert-end-of-warnings ()
- "Return a marker to the end of *Warnings* buffer."
- (ert-make-end-marker "*Warnings*" nil))
-
-(defun ert-search-after (after regexp)
- "Search after marker in AFTER for regular expression REGEXP.
-Return a alist of position and matches. AFTER should have been
-created with `ert-make-end-marker'.
-
-This is supposed to be used for messages and trace buffers.
-
-See also
- `ert-get-messages'"
- (let ((buf (marker-buffer after)))
- (with-current-buffer buf
- (let ((here (point))
- res)
- (goto-char after)
- (save-match-data
- (while (re-search-forward regexp nil t)
- (setq res (cons (match-data) res))))
- (goto-char here)
- (reverse res)))))
-;; fix-me: add a conventient way to look at the result of
-;; `ert-search-after'. Probably this means adding something more to
-;; the returned result.
-
-(defvar ert-messages-mark)
-(defun ert-get-messages (regexp)
- "Search *Messages* buffer for regular expression REGEXP.
-This should be used within `ert-deftest'. Search begins where
-the buffer ended when test started.
-
-See also:
- `ert-get-warnings'
- `ert-search-after'"
- (ert-search-after ert-messages-mark regexp))
-
-(defvar ert-warnings-mark)
-(defun ert-get-warnings (regexp)
- "Search *Warnings* buffer for regular expression REGEXP.
-See `ert-get-messages' for more information."
- (ert-search-after ert-warnings-mark regexp))
-
-
-;;; Test selectors.
-
-(defun ert-select-tests (selector universe)
- "Select, from UNIVERSE, a set of tests according to SELECTOR.
-
-UNIVERSE should be a list of tests, or t, which refers to all
-tests named by symbols in `obarray'.
-
-Returns the set of tests as a list.
-
-Valid selectors:
-
-nil -- Selects the empty set.
-t -- Selects UNIVERSE.
-:new -- Selects all tests that have not been run yet.
-:failed, :passed, :error -- Select tests according to their most recent result.
-:expected, :unexpected -- Select tests according to their most recent result.
-a string -- Selects all tests that have a name that matches the string, a regexp.
-a test -- Selects that test.
-a symbol -- Selects the test that the symbol names, errors if none.
-\(member TESTS...\) -- Selects TESTS, a list of tests or symbols naming tests.
-\(eql TEST\) -- Selects TEST, a test or a symbol naming a test.
-\(and SELECTORS...\) -- Selects the tests that match all SELECTORS.
-\(or SELECTORS...\) -- Selects the tests that match any SELECTOR.
-\(not SELECTOR\) -- Selects all tests that do not match SELECTOR.
-\(satisfies PREDICATE\) -- Selects all tests that satisfy PREDICATE.
-
-Only selectors that require a superset of tests, such
-as (satisfies ...), strings, :new, etc. make use of UNIVERSE.
-Selectors that do not, such as \(member ...\), just return the
-set implied by them without checking whether it is really
-contained in UNIVERSE."
- ;; This code needs to match the etypecase in
- ;; `ert-insert-human-readable-selector'.
- (etypecase selector
- ((member nil) nil)
- ((member t) (etypecase universe
- (list universe)
- ((member t) (ert-select-tests "" universe))))
- ((member :new) (ert-select-tests
- `(satisfies ,(lambda (test)
- (typep (ert-test-most-recent-result test)
- 'null)))
- universe))
- ((member :failed) (ert-select-tests
- `(satisfies ,(lambda (test)
- (typep (ert-test-most-recent-result test)
- 'ert-test-failed)))
- universe))
- ((member :passed) (ert-select-tests
- `(satisfies ,(lambda (test)
- (typep (ert-test-most-recent-result test)
- 'ert-test-passed)))
- universe))
- ((member :error) (ert-select-tests
- `(satisfies ,(lambda (test)
- (typep (ert-test-most-recent-result test)
- 'ert-test-error)))
- universe))
- ((member :expected) (ert-select-tests
- `(satisfies
- ,(lambda (test)
- (ert-test-result-expected-p
- test
- (ert-test-most-recent-result test))))
- universe))
- ((member :unexpected) (ert-select-tests `(not :expected) universe))
- (string
- (etypecase universe
- ((member t) (mapcar #'ert-get-test
- (apropos-internal selector #'ert-test-boundp)))
- (list (remove-if-not (lambda (test)
- (and (ert-test-name test)
- (string-match selector (ert-test-name test))))
- universe))))
- (ert-test (list selector))
- (symbol
- (assert (ert-test-boundp selector))
- (list (ert-get-test selector)))
- (cons
- (destructuring-bind (operator &rest operands) selector
- (ecase operator
- (member
- (mapcar (lambda (purported-test)
- (etypecase purported-test
- (symbol (assert (ert-test-boundp purported-test))
- (ert-get-test purported-test))
- (ert-test purported-test)))
- operands))
- (eql
- (assert (eql (length operands) 1))
- (ert-select-tests `(member ,@operands) universe))
- (and
- ;; Do these definitions of AND, NOT and OR satisfy de
- ;; Morgan's rules? Should they?
- (case (length operands)
- (0 (ert-select-tests 't universe))
- (t (ert-select-tests `(and ,@(rest operands))
- (ert-select-tests (first operands) universe)))))
- (not
- (assert (eql (length operands) 1))
- (set-difference (ert-select-tests 't universe)
- (ert-select-tests (first operands) universe)))
- (or
- (case (length operands)
- (0 (ert-select-tests 'nil universe))
- (t (union (ert-select-tests (first operands) universe)
- (ert-select-tests `(or ,@(rest operands)) universe)))))
- (satisfies
- (assert (eql (length operands) 1))
- (remove-if-not (first operands) (ert-select-tests 't universe))))))))
-
-(defun ert-insert-human-readable-selector (selector)
- "Insert a human-readable presentation of SELECTOR into the current buffer."
- ;; This is needed to avoid printing the (huge) contents of the
- ;; `backtrace' slot of the result objects in the
- ;; `most-recent-result' slots of test case objects in (eql ...) or
- ;; (member ...) selectors.
- (labels ((rec (selector)
- ;; This code needs to match the etypecase in `ert-select-tests'.
- (etypecase selector
- ((or (member nil t
- :new :failed :passed :error
- :expected :unexpected)
- string
- symbol)
- selector)
- (ert-test
- (if (ert-test-name selector)
- (make-symbol (format "<%S>" (ert-test-name selector)))
- (make-symbol "<unnamed test>")))
- (cons
- (destructuring-bind (operator &rest operands) selector
- (ecase operator
- ((member eql and not or)
- `(,operator ,@(mapcar #'rec operands)))
- (satisfies
- selector)))))))
- (insert (format "%S" (rec selector)))))
-
-
-;;; Running tests.
-
-(put 'ert-test-failed 'error-conditions '(error ert-test-failed))
-(put 'ert-test-failed 'error-message "Test failed")
-
-(defun ert-pass ()
- "Terminate the current test and mark it passed. Does not return."
- (throw 'ert-pass nil))
-
-(defun ert-fail (data)
- "Terminate the current test and mark it failed. Does not return.
-DATA is displayed to the user and should state the reason of the failure."
- (signal 'ert-test-failed (list data)))
-
-;; The data structures that represent the result of running a test.
-(defstruct ert-test-result
- (messages nil)
- )
-(defstruct (ert-test-passed (:include ert-test-result)))
-(defstruct (ert-test-result-with-condition (:include ert-test-result))
- (condition (assert nil))
- (backtrace (assert nil)))
-(defstruct (ert-test-error (:include ert-test-result-with-condition)))
-(defstruct (ert-test-quit (:include ert-test-result-with-condition)))
-(defstruct (ert-test-failed (:include ert-test-result-with-condition)))
-(defstruct (ert-test-aborted-with-non-local-exit (:include ert-test-result)))
-
-
-(defun ert-record-backtrace ()
- "Record the current backtrace (as a list) and return it."
- ;; Since the backtrace is stored in the result object, result
- ;; objects must only be printed with appropriate limits
- ;; (`print-level' and `print-length') in place. For interactive
- ;; use, the cost of ensuring this possibly outweighs the advantage
- ;; of storing the backtrace for
- ;; `ert-results-pop-to-backtrace-for-test-at-point' given that we
- ;; already have `ert-results-rerun-test-debugging-errors-at-point'.
- ;; For batch use, however, printing the backtrace may be useful.
- (loop
- ;; 6 is the number of frames our own debugger adds (when
- ;; compiled; more when interpreted). FIXME: Need to describe a
- ;; procedure for determining this constant.
- for i from 6
- for frame = (backtrace-frame i)
- while frame
- collect frame))
-
-;; A container for the state of the execution of a single test and
-;; environment data needed during its execution.
-(defstruct ert-test-execution-info
- (test (assert nil))
- (result (assert nil))
- ;; A thunk that may be called when RESULT has been set to its final
- ;; value and test execution should be terminated. Should not
- ;; return.
- (exit-continuation (assert nil))
- ;; The binding of `debugger' outside of the execution of the test.
- next-debugger
- ;; The binding of `ert-debug-on-error' that is in effect for the
- ;; execution of the current test. We store it to avoid being
- ;; affected by any new bindings the test itself may establish. (I
- ;; don't remember whether this feature is important.)
- ert-debug-on-error)
-
-(defun ert-run-test-debugger (info debugger-args)
- "The function that `debugger' is bound to during the execution of tests.
-
-Records failures and errors and either terminates the test
-silently or calls the interactive debugger, as appropriate."
- (destructuring-bind (first-debugger-arg &rest more-debugger-args) debugger-args
- (ecase first-debugger-arg
- ((lambda debug t exit nil)
- (apply (ert-test-execution-info-next-debugger info) debugger-args))
- (error
- (let* ((condition (first more-debugger-args))
- (type (case (car condition)
- ((quit) 'quit)
- ((ert-test-failed) 'failed)
- (otherwise 'error)))
- (backtrace (ert-record-backtrace)))
- (setf (ert-test-execution-info-result info)
- (ecase type
- (quit
- (make-ert-test-quit :condition condition
- :backtrace backtrace))
- (failed
- (make-ert-test-failed :condition condition
- :backtrace backtrace))
- (error
- (make-ert-test-error :condition condition
- :backtrace backtrace))))
- ;; Work around Emacs' heuristic (in eval.c) for detecting
- ;; errors in the debugger.
- (incf num-nonmacro-input-events)
- ;; FIXME: We should probably implement more fine-grained
- ;; control a la non-t `debug-on-error' here.
- (cond
- ((ert-test-execution-info-ert-debug-on-error info)
- (apply (ert-test-execution-info-next-debugger info) debugger-args))
- (t))
- (funcall (ert-test-execution-info-exit-continuation info)))))))
-
-(defun ert-run-test-internal (ert-test-execution-info)
- (lexical-let ((info ert-test-execution-info))
- (setf (ert-test-execution-info-next-debugger info) debugger
- (ert-test-execution-info-ert-debug-on-error info) ert-debug-on-error)
- (catch 'ert-pass
- ;; For now, each test gets its own temp buffer and its own
- ;; window excursion, just to be safe. If this turns out to be
- ;; too expensive, we can remove it.
- (with-temp-buffer
- (save-window-excursion
- (let ((debugger (lambda (&rest debugger-args)
- (ert-run-test-debugger info debugger-args)))
- (debug-on-error t)
- (debug-on-quit t)
- ;; FIXME: Do we need to store the old binding of this
- ;; and consider it in `ert-run-test-debugger'?
- (debug-ignored-errors nil)
- (ert-messages-mark (ert-end-of-messages))
- (ert-warnings-mark (ert-end-of-warnings)))
- (funcall (ert-test-body (ert-test-execution-info-test info))))))
- (ert-pass))
- (setf (ert-test-execution-info-result info) (make-ert-test-passed)))
- nil)
-
-(defun ert-make-marker-in-messages-buffer ()
- (with-current-buffer (get-buffer-create "*Messages*")
- (set-marker (make-marker) (point-max))))
-
-(defun ert-force-message-log-buffer-truncation ()
- (with-current-buffer (get-buffer-create "*Messages*")
- ;; This is a reimplementation of this part of message_dolog() in xdisp.c:
- ;; if (NATNUMP (Vmessage_log_max))
- ;; {
- ;; scan_newline (Z, Z_BYTE, BEG, BEG_BYTE,
- ;; -XFASTINT (Vmessage_log_max) - 1, 0);
- ;; del_range_both (BEG, BEG_BYTE, PT, PT_BYTE, 0);
- ;; }
- (when (and (integerp message-log-max) (>= message-log-max 0))
- (let ((begin (point-min))
- (end (save-excursion
- (goto-char (point-max))
- (forward-line (- message-log-max))
- (point))))
- (delete-region begin end)))))
-
-(defun ert-run-test (test)
- "Run TEST. Return the result and store it in TEST's `most-recent-result' slot."
- (setf (ert-test-most-recent-result test) nil)
- (block error
- (lexical-let* ((begin-marker (ert-make-marker-in-messages-buffer))
- (info (make-ert-test-execution-info
- :test test
- :result (make-ert-test-aborted-with-non-local-exit)
- :exit-continuation (lambda ()
- (return-from error nil)))))
- (unwind-protect
- (let ((message-log-max t))
- (ert-run-test-internal info))
- (let ((result (ert-test-execution-info-result info)))
- (setf (ert-test-result-messages result)
- (with-current-buffer (get-buffer-create "*Messages*")
- (buffer-substring begin-marker (point-max))))
- (ert-force-message-log-buffer-truncation)
- (setf (ert-test-most-recent-result test) result)))))
- (ert-test-most-recent-result test))
-
-
-;;; The `should' macros.
-
-(eval-and-compile
- (defun ert-special-operator-p (thing)
- "Return non-nil if THING is a symbol naming a special operator."
- (and (symbolp thing)
- (let ((definition (indirect-function thing t)))
- (and (subrp definition)
- (eql (cdr (subr-arity definition)) 'unevalled)))))
- (defun ert-expand-should (whole form env inner-expander)
- "Helper function for the `should' macro and its variants.
-
-Analyzes FORM and produces an expression that has the same
-semantics under evaluation but records additional debugging
-information. INNER-EXPANDER adds the actual checks specific to
-the particular variant of `should'."
- (let ((form (macroexpand form env)))
- ;; It's sort of a wart that `inner-expander' can't influence the
- ;; value the expansion returns.
- (cond
- ((atom form)
- (funcall inner-expander form `(list ',whole :form ',form :value ,form)))
- ((ert-special-operator-p (car form))
- (let ((value (gensym "value-")))
- `(let ((,value (make-symbol "ert-form-evaluation-aborted")))
- ,(funcall inner-expander
- `(setq ,value ,form)
- `(list ',whole :form ',form :value ,value))
- ,value)))
- (t
- (let ((fn-name (car form))
- (arg-forms (cdr form)))
- (assert (or (symbolp fn-name)
- (and (consp fn-name)
- (eql (car fn-name) 'lambda)
- (listp (cdr fn-name)))))
- (let ((fn (gensym "fn-"))
- (args (gensym "args-"))
- (value (gensym "value-"))
- (default-value (gensym "ert-form-evaluation-aborted-")))
- `(let ((,fn (function ,fn-name))
- (,args (list ,@arg-forms)))
- (let ((,value ',default-value))
- ,(funcall inner-expander
- `(setq ,value (apply ,fn ,args))
- `(nconc (list ',whole)
- (list :form `(,,fn ,@,args))
- (unless (eql ,value ',default-value)
- (list :value ,value))
- (let ((-explainer-
- (and (symbolp ',fn-name)
- (get ',fn-name
- 'ert-explainer))))
- (when -explainer-
- (list :explanation
- (apply -explainer- ,args))))))
- ,value)))))))))
-
-(defmacro* ert-should (form &environment env)
- "Evaluate FORM. If it returns nil, abort the current test as failed.
-
-Returns the value of FORM."
- (ert-expand-should `(ert-should ,form) form env
- (lambda (inner-form form-description-form)
- `(unless ,inner-form
- (ert-fail ,form-description-form)))))
-
-(defmacro* ert-should-not (form &environment env)
- "Evaluate FORM. If it returns non-nil, abort the current test as failed.
-
-Returns nil."
- (ert-expand-should `(ert-should-not ,form) form env
- (lambda (inner-form form-description-form)
- `(unless (not ,inner-form)
- (ert-fail ,form-description-form)))))
-
-(defun ert-should-error-handle-error (form-description-fn
- condition type exclude-subtypes test)
- "Helper function for `should-error'.
-
-Determines whether CONDITION matches TYPE, EXCLUDE-SUBTYPES and
-TEST, and aborts the current test as failed if it doesn't."
- (let ((signalled-conditions (get (car condition) 'error-conditions))
- (handled-conditions (etypecase type
- (list type)
- (symbol (list type)))))
- (assert signalled-conditions)
- (unless (intersection signalled-conditions handled-conditions)
- (ert-fail (append
- (funcall form-description-fn)
- (list
- :condition condition
- :fail-reason (concat "the error signalled did not"
- " have the expected type")))))
- (when exclude-subtypes
- (unless (member (car condition) handled-conditions)
- (ert-fail (append
- (funcall form-description-fn)
- (list
- :condition condition
- :fail-reason (concat "the error signalled was a subtype"
- " of the expected type"))))))
- (unless (funcall test condition)
- (ert-fail (append
- (funcall form-description-fn)
- (list
- :condition condition
- :fail-reason "the error signalled did not pass the test"))))))
-
-;; FIXME: The expansion will evaluate the keyword args (if any) in
-;; nonstandard order.
-(defmacro* ert-should-error (form &rest keys &key type exclude-subtypes test
- &environment env)
- "Evaluate FORM. Unless it signals an error, abort the current test as failed.
-
-The error signalled additionally needs to match TYPE and satisfy
-TEST. TYPE should be a condition name or a list of condition
-names. If EXCLUDE-SUBTYPES is nil, the error matches TYPE if one
-of its condition names is an element of TYPE. If
-EXCLUDE-SUBTYPES is non-nil, the error matches TYPE if it is an
-element of TYPE. TEST should be a predicate."
- ;; Returns a gensym named `ert-form-evaluation-aborted-XXX', but
- ;; that's a wart, so let's not document it.
- (unless type (setq type ''error))
- (unless test (setq test '(lambda (condition) t)))
- (ert-expand-should
- `(ert-should-error ,form ,@keys)
- form env
- (lambda (inner-form form-description-form)
- (let ((errorp (gensym "errorp"))
- (form-description-fn (gensym "form-description-fn-")))
- `(let ((,errorp nil)
- (,form-description-fn (lambda () ,form-description-form)))
- (condition-case -condition-
- ,inner-form
- ;; We can't use ,type here because we want to evaluate it.
- (error
- (setq ,errorp t)
- (ert-should-error-handle-error ,form-description-fn
- -condition-
- ,type ,exclude-subtypes ,test)
- ;; It would make sense to have the `should-error' form
- ;; return the error in this case, but `ert-expand-should'
- ;; doesn't allow that at the moment.
- ))
- (unless ,errorp
- (ert-fail (append
- (funcall ,form-description-fn)
- (list
- :fail-reason "did not signal an error")))))))))
-
-
-;;; Explanation of `should' failures.
-
-(defun ert-proper-list-p (x)
- "Return non-nil if X is a proper list, nil otherwise."
- (loop
- for firstp = t then nil
- for fast = x then (cddr fast)
- for slow = x then (cdr slow) do
- (when (null fast) (return t))
- (when (not (consp fast)) (return nil))
- (when (null (cdr fast)) (return t))
- (when (not (consp (cdr fast))) (return nil))
- (when (and (not firstp) (eq fast slow)) (return nil))))
-
-(defun ert-explain-not-equal (a b)
- "Return a programmer-readable explanation of why A and B are not `equal'.
-
-Returns nil if they are equal."
- (if (not (equal (type-of a) (type-of b)))
- `(different-types ,a ,b)
- (etypecase a
- (cons
- (let ((a-proper-p (ert-proper-list-p a))
- (b-proper-p (ert-proper-list-p b)))
- (if (not (eql (not a-proper-p) (not b-proper-p)))
- `(one-list-proper-one-improper ,a ,b)
- (if a-proper-p
- (if (not (equal (length a) (length b)))
- ;; This would be even more helpful if it showed
- ;; something like what `set-difference' would
- ;; return.
- `(proper-lists-of-different-length ,a ,b)
- (loop for i from 0
- for ai in a
- for bi in b
- for xi = (ert-explain-not-equal ai bi)
- do (when xi (return `(list-elt ,i ,xi)))))
- (let ((car-x (ert-explain-not-equal (car a) (car b))))
- (if car-x
- `(car ,car-x)
- (let ((cdr-x (ert-explain-not-equal (cdr a) (cdr b))))
- (if cdr-x
- `(cdr ,cdr-x))
- nil)))))))
- (array (if (not (equal (length a) (length b)))
- `(arrays-of-different-length ,a ,b)
- (loop for i from 0
- for ai across a
- for bi across b
- for xi = (ert-explain-not-equal ai bi)
- do (when xi (return `(array-elt ,i ,xi))))))
- (atom (if (not (equal a b))
- `(different-atoms ,a ,b)
- nil)))))
-(put 'equal 'ert-explainer 'ert-explain-not-equal)
-
-
-;;; Results display.
-
-;; The data structure that contains the set of tests being executed
-;; during one particular test run, their results, the state of the
-;; execution, and some statistics.
-;;
-;; The data about results and expected results of tests may seem
-;; redundant here, since the test objects also carry such information.
-;; However, the information in the test objects may be more recent, it
-;; may correspond to a different test run. We need the information
-;; that corresponds to this run in order to be able to update the
-;; statistics correctly when a test is re-run interactively and has a
-;; different result than before.
-(defstruct ert-stats
- (selector (assert nil))
- ;; The tests, in order.
- (tests (assert nil) :type vector)
- ;; A map of test names (or the test objects themselves for unnamed
- ;; tests) to indices into the `tests' vector.
- (test-map (assert nil) :type hash-table)
- ;; The results of the tests during this run, in order.
- (test-results (assert nil) :type vector)
- ;; The expected result types of the tests, in order.
- (test-results-expected (assert nil) :type vector)
- (total (assert nil))
- (passed-expected 0)
- (passed-unexpected 0)
- (failed-expected 0)
- (failed-unexpected 0)
- (error-expected 0)
- (error-unexpected 0)
- (start-time (assert nil))
- (end-time nil)
- (aborted-p nil)
- (current-test nil))
-
-;; An entry in the results buffer ewoc. There is one entry per test.
-(defstruct ert-ewoc-entry
- (test (assert nil))
- (result nil)
- ;; If the result of this test was expected, its ewoc entry is hidden
- ;; initially.
- (hidden-p (assert nil))
- ;; An ewoc entry may be collapsed to hide details such as the error
- ;; condition.
- ;;
- ;; I'm not sure the ability to expand and collapse entries is still
- ;; a useful feature.
- (expanded-p t)
- ;; By default, the ewoc entry presents the error condition with
- ;; certain limits on how much to print (`print-level',
- ;; `print-length'). The user can interactively switch to a set of
- ;; higher limits.
- (extended-printer-limits-p nil))
-
-;; Variables local to the results buffer.
-
-;; The ewoc.
-(defvar ert-results-ewoc)
-;; The stats object.
-(defvar ert-results-stats)
-;; A string with one character per test. Each character represents
-;; the result of the corresponding test. The string is displayed near
-;; the top of the buffer and serves as a progress bar.
-(defvar ert-results-progress-bar-string)
-;; The position where the progress bar button begins.
-(defvar ert-results-progress-bar-button-begin)
-;; The test result listener that updates the buffer when tests are run.
-(defvar ert-results-listener)
-
-;; The same as `ert-results-stats', but dynamically bound. Used for
-;; the mode line progress indicator.
-(defvar ert-current-run-stats nil)
-
-(defun ert-format-time-iso8601 (time)
- "Format TIME in the particular variant of ISO 8601 used for timestamps in ERT."
- (format-time-string "%Y-%m-%d %T%z" time))
-
-(defun ert-insert-test-name-button (test-name)
- (insert-text-button (format "%S" test-name)
- :type 'ert-test-name-button
- 'ert-test-name test-name))
-
-(defun ert-results-update-ewoc-hf (ewoc stats)
- "Update the header and footer of EWOC to show certain information from STATS.
-
-Also sets `ert-results-progress-bar-button-begin'."
- (let ((run-count (+ (ert-stats-passed-expected stats)
- (ert-stats-passed-unexpected stats)
- (ert-stats-failed-expected stats)
- (ert-stats-failed-unexpected stats)
- (ert-stats-error-expected stats)
- (ert-stats-error-unexpected stats)))
- (results-buffer (current-buffer)))
- (ewoc-set-hf
- ewoc
- ;; header
- (with-temp-buffer
- (insert "Selector: ")
- (ert-insert-human-readable-selector (ert-stats-selector stats))
- (insert "\n")
- (insert
- (format (concat "Passed: %s (%s unexpected)\n"
- "Failed: %s (%s unexpected)\n"
- "Error: %s (%s unexpected)\n"
- "Total: %s/%s\n\n")
- (+ (ert-stats-passed-expected stats)
- (ert-stats-passed-unexpected stats))
- (ert-stats-passed-unexpected stats)
- (+ (ert-stats-failed-expected stats)
- (ert-stats-failed-unexpected stats))
- (ert-stats-failed-unexpected stats)
- (+ (ert-stats-error-expected stats)
- (ert-stats-error-unexpected stats))
- (ert-stats-error-unexpected stats)
- run-count
- (ert-stats-total stats)))
- (insert
- (format "Started at: %s\n"
- (ert-format-time-iso8601 (ert-stats-start-time stats))))
- ;; FIXME: This is ugly. Need to properly define invariants of
- ;; the `stats' data structure.
- (let ((state (cond ((ert-stats-aborted-p stats)
- 'aborted)
- ((ert-stats-current-test stats)
- 'running)
- ((ert-stats-end-time stats)
- 'finished)
- (t
- 'preparing))))
- (ecase state
- (preparing
- (insert ""))
- (aborted
- (cond ((ert-stats-current-test stats)
- (insert "Aborted during test: ")
- (ert-insert-test-name-button
- (ert-test-name (ert-stats-current-test stats))))
- (t
- (insert "Aborted."))))
- (running
- (assert (ert-stats-current-test stats))
- (insert "Running test: ")
- (ert-insert-test-name-button (ert-test-name
- (ert-stats-current-test stats))))
- (finished
- (assert (not (ert-stats-current-test stats)))
- (insert "Finished.")))
- (insert "\n")
- (if (ert-stats-end-time stats)
- (insert
- (format "%s%s\n"
- (if (ert-stats-aborted-p stats)
- "Aborted at: "
- "Finished at: ")
- (ert-format-time-iso8601 (ert-stats-end-time stats))))
- (insert "\n"))
- (insert "\n"))
- (let ((progress-bar-string (with-current-buffer results-buffer
- ert-results-progress-bar-string)))
- (let ((progress-bar-button-begin
- (insert-text-button (substring progress-bar-string 0 run-count)
- :type 'ert-results-progress-bar-button)))
- (with-current-buffer results-buffer
- (set (make-local-variable 'ert-results-progress-bar-button-begin)
- progress-bar-button-begin)))
- (insert (substring progress-bar-string run-count)))
- (insert "\n\n")
- (buffer-string))
- ;; footer
- ;;
- ;; We actually want an empty footer, but that would trigger a bug
- ;; in ewoc, sometimes clearing the entire buffer.
- "\n")))
-
-(defun ert-results-update-stats-display (ewoc stats)
- "Update EWOC and the mode line to show data from STATS."
- (ert-results-update-ewoc-hf ewoc stats)
- (force-mode-line-update)
- (redisplay t))
-
-(defun ert-char-for-test-result (result expectedp)
- "Return a character that represents the test result RESULT."
- (let ((char
- (etypecase result
- (ert-test-passed ?.)
- (ert-test-failed ?f)
- (ert-test-error ?e)
- (null ?-)
- (ert-test-aborted-with-non-local-exit ?a))))
- (if expectedp
- char
- (upcase char))))
-
-(defun ert-string-for-test-result (result expectedp)
- "Return a string that represents the test result RESULT."
- (etypecase result
- (ert-test-passed "passed")
- (ert-test-failed "failed")
- (ert-test-error "error")
- (null "unknown")
- (ert-test-aborted-with-non-local-exit "aborted")))
-
-(defun ert-tests-running-mode-line-indicator ()
- (let* ((stats ert-current-run-stats)
- (tests-total (ert-stats-total stats))
- (tests-completed (+ (ert-stats-passed-expected stats)
- (ert-stats-passed-unexpected stats)
- (ert-stats-failed-expected stats)
- (ert-stats-failed-unexpected stats)
- (ert-stats-error-expected stats)
- (ert-stats-error-unexpected stats))))
- (if (>= tests-completed tests-total)
- (format " ERT(%s/%s,finished)" tests-completed tests-total)
- (format " ERT(%s/%s):%s"
- (1+ tests-completed)
- tests-total
- (if (null (ert-stats-current-test stats))
- "?"
- (format "%S"
- (ert-test-name (ert-stats-current-test stats))))))))
-
-(defun ert-pp-with-indentation-and-newline (object)
- "Pretty-print OBJECT, indenting it to the current column of point.
-Ensures a final newline is inserted."
- (let ((begin (point)))
- (pp object (current-buffer))
- (unless (bolp) (insert "\n"))
- (save-excursion
- (goto-char begin)
- (indent-sexp))))
-
-(defun ert-print-test-for-ewoc (entry)
- "The ewoc print function for ewoc test entries."
- (let* ((test (ert-ewoc-entry-test entry))
- (result (ert-ewoc-entry-result entry))
- (hiddenp (ert-ewoc-entry-hidden-p entry))
- (expandedp (ert-ewoc-entry-expanded-p entry))
- (extended-printer-limits-p (ert-ewoc-entry-extended-printer-limits-p
- entry)))
- (cond (hiddenp)
- (t
- (insert-text-button (format "%c"
- (ert-char-for-test-result
- result
- (ert-test-result-expected-p test
- result)))
- :type 'ert-results-expand-collapse-button)
- (insert " ")
- (ert-insert-test-name-button (ert-test-name test))
- (insert "\n")
- (when (and expandedp (not (eql result 'nil)))
- (etypecase result
- (ert-test-passed
- (insert " passed\n")
- (insert ""))
- (ert-test-result-with-condition
- (insert " ")
- (let ((print-escape-newlines t)
- (print-level (if extended-printer-limits-p 10 5))
- (print-length (if extended-printer-limits-p 100 10)))
- (let ((begin (point)))
- (ert-pp-with-indentation-and-newline
- (ert-test-result-with-condition-condition result))
- (save-restriction
- (narrow-to-region begin (point))
- ;; Inhibit optimization in `debugger-make-xrefs'
- ;; that sometimes inserts unrelated backtrace
- ;; info into our buffer.
- (let ((debugger-previous-backtrace nil))
- (debugger-make-xrefs))))))
- (ert-test-aborted-with-non-local-exit
- (insert " aborted\n")))
- (insert "\n")))))
- nil)
-
-(defun ert-setup-results-buffer (stats listener buffer-name)
- "Set up a test results buffer."
- (unless buffer-name (setq buffer-name "*ert*"))
- (let ((buffer (let ((default-major-mode 'fundamental-mode))
- (get-buffer-create buffer-name))))
- (with-current-buffer buffer
- (setq buffer-read-only t)
- (let ((inhibit-read-only t))
- (buffer-disable-undo)
- (erase-buffer)
- (ert-results-mode)
- (set (make-local-variable 'ert-results-ewoc)
- (ewoc-create 'ert-print-test-for-ewoc nil nil t))
- (set (make-local-variable 'ert-results-stats) stats)
- (set (make-local-variable 'ert-results-progress-bar-string)
- (make-string (ert-stats-total stats)
- (ert-char-for-test-result nil t)))
- (set (make-local-variable 'ert-results-listener) listener)
- (ert-results-update-ewoc-hf ert-results-ewoc ert-results-stats)
- (goto-char (1- (point-max)))
- buffer))))
-
-(defun ert-run-or-rerun-test (stats test listener)
- "Run the single test TEST and record the result using STATS and LISTENER."
- (let ((ert-current-run-stats stats)
- (pos (ert-stats-test-index stats test))
- (results (ert-stats-test-results stats))
- (expected (ert-stats-test-results-expected stats)))
- ;; Adjust stats to remove previous result.
- (if (aref expected pos)
- (etypecase (aref results pos)
- (ert-test-passed (decf (ert-stats-passed-expected stats)))
- (ert-test-failed (decf (ert-stats-failed-expected stats)))
- (ert-test-error (decf (ert-stats-error-expected stats)))
- (null)
- (ert-test-aborted-with-non-local-exit))
- (etypecase (aref results pos)
- (ert-test-passed (decf (ert-stats-passed-unexpected stats)))
- (ert-test-failed (decf (ert-stats-failed-unexpected stats)))
- (ert-test-error (decf (ert-stats-error-unexpected stats)))
- (null)
- (ert-test-aborted-with-non-local-exit)))
- (setf (aref results pos) nil)
- ;; Call listener after setting/before resetting
- ;; (ert-stats-current-test stats); the listener might refresh the
- ;; mode line display, and if the value is not set yet/any more
- ;; during this refresh, the mode line will flicker unnecessarily.
- (setf (ert-stats-current-test stats) test)
- (funcall listener 'test-started stats test)
- (setf (ert-test-most-recent-result test) nil)
- (unwind-protect
- (ert-run-test test)
- (let* ((result (ert-test-most-recent-result test))
- (expectedp (typep result (ert-test-expected-result-type test))))
- ;; Adjust stats to add new result.
- (if expectedp
- (etypecase result
- (ert-test-passed (incf (ert-stats-passed-expected stats)))
- (ert-test-failed (incf (ert-stats-failed-expected stats)))
- (ert-test-error (incf (ert-stats-error-expected stats)))
- (null)
- (ert-test-aborted-with-non-local-exit))
- (etypecase result
- (ert-test-passed (incf (ert-stats-passed-unexpected stats)))
- (ert-test-failed (incf (ert-stats-failed-unexpected stats)))
- (ert-test-error (incf (ert-stats-error-unexpected stats)))
- (null)
- (ert-test-aborted-with-non-local-exit)))
- (setf (aref results pos) result
- (aref expected pos) expectedp)
- (funcall listener 'test-ended stats test result))
- (setf (ert-stats-current-test stats) nil))))
-
-(defun ert-run-tests (selector listener)
- "Run the tests specified by SELECTOR, sending progress updates to LISTENER."
- (let* ((tests (coerce (ert-select-tests selector t) 'vector))
- (map (let ((map (make-hash-table :size (length tests))))
- (loop for i from 0
- for test across tests
- for key = (or (ert-test-name test) test) do
- (assert (not (gethash key map)))
- (setf (gethash key map) i))
- map))
- (stats (make-ert-stats :selector selector
- :tests tests
- :test-map map
- :test-results (make-vector (length tests) nil)
- :test-results-expected (make-vector
- (length tests) nil)
- :total (length tests)
- :start-time (current-time))))
- (funcall listener 'run-started stats)
- (let ((abortedp t))
- (let ((ert-current-run-stats stats))
- (force-mode-line-update)
- (unwind-protect
- (progn
- (loop for test across tests do
- (ert-run-or-rerun-test stats test listener))
- (setq abortedp nil))
- (setf (ert-stats-aborted-p stats) abortedp)
- (setf (ert-stats-end-time stats) (current-time))
- (funcall listener 'run-ended stats abortedp)))
- stats)))
-
-(defun ert-stats-test-index (stats test)
- "Return the index of TEST in the run represented by STATS."
- (gethash (or (ert-test-name test) test) (ert-stats-test-map stats)))
-
-(defvar ert-selector-history nil
- "List of recent test selectors read from terminal.")
-
-;; Fix-me: return (regep (list of matches))?
-;; Fix-me: Add prompt parameter?
-(defun ert-read-test-selector ()
- "Read a regexp for test selection from minibuffer.
-The user can use TAB to see which tests match."
- (let* ((all-tests
- (mapcar (lambda (rec) (format "%s" (elt rec 1)))
- (ert-select-tests "" t))
- ;;'("ert-group1-1" "ert-group1-2" "ert-other")
- )
- regexp
- ret
- (get-completions
- (lambda ()
- (let* ((ret (save-match-data
- (mapcar (lambda (alt)
- (when (string-match regexp alt)
- alt))
- all-tests))))
- (setq ret (delq nil ret))
- ret))))
- (setq all-tests (append all-tests
- '(":new"
- ":failed" ":passed" ":error"
- )
- nil))
- (let ((mini-map (copy-keymap minibuffer-local-map)))
- (define-key mini-map [?\t]
- (lambda () (interactive)
- (with-output-to-temp-buffer "*Completions*"
- (display-completion-list
- (progn
- (setq regexp (minibuffer-contents))
- (set-text-properties 0 (length regexp) nil regexp)
- (funcall get-completions))))))
- (setq regexp
- (let* ((sym-here (thing-at-point 'symbol))
- (test-here (when (and sym-here
- (memq sym-here all-tests))
- sym-here))
- (default (if sym-here
- (substring-no-properties sym-here)
- (if ert-selector-history
- (first ert-selector-history)
- "t"))))
- (read-from-minibuffer
- (if (null default)
- "Run tests, use TAB to see matches: "
- (format "Run tests, use TAB to see matches (default %s): "
- default))
- nil ;; initial-contents
- mini-map ;; keymap
- nil ;; read
- 'ert-selector-history
- default nil))))
- (setq ret regexp)
- (when (string= "t" ret)
- (setq ret t))
- ret))
-
-;; Should OUTPUT-BUFFER-NAME and MESSAGE-FN really be arguments here?
-;; They are needed only for our automated self-tests at the moment.
-;; Or should there be some other mechanism?
-;;;###autoload
-(defun ert-run-tests-interactively (selector
- &optional output-buffer-name message-fn)
- "Run the tests specified by SELECTOR and display the results in a buffer."
- (interactive
-;;; (list (let ((default (if ert-selector-history
-;;; (first ert-selector-history)
-;;; "t")))
-;;; (read-from-minibuffer (if (null default)
-;;; "Run tests: "
-;;; (format "Run tests (default %s): " default))
-;;; ;;nil nil t 'ert-selector-history
-;;; ;;
-;;; ;; fix-me: seems like I am misunderstanding Christians intent here.
-;;; nil nil nil 'ert-selector-history
-;;; default nil))
-;;; nil nil))
- (list (ert-read-test-selector)
- nil nil))
- (unless message-fn (setq message-fn 'message))
- (lexical-let ((output-buffer-name output-buffer-name)
- buffer
- listener
- (message-fn message-fn))
- (setq listener
- (lambda (event-type &rest event-args)
- (ecase event-type
- (run-started
- (destructuring-bind (stats) event-args
- (setq buffer (ert-setup-results-buffer stats
- listener
- output-buffer-name))
- (pop-to-buffer buffer)))
- (run-ended
- (destructuring-bind (stats abortedp) event-args
- (funcall message-fn
- "%sRan %s tests, %s results were as expected%s"
- (if (not abortedp)
- ""
- "Aborted: ")
- (ert-stats-total stats)
- (+ (ert-stats-passed-expected stats)
- (ert-stats-failed-expected stats)
- (ert-stats-error-expected stats))
- (let ((unexpected
- (+ (ert-stats-passed-unexpected stats)
- (ert-stats-failed-unexpected stats)
- (ert-stats-error-unexpected stats))))
- (if (zerop unexpected)
- ""
- (format ", %s unexpected" unexpected))))
- (ert-results-update-stats-display (with-current-buffer buffer
- ert-results-ewoc)
- stats)))
- (test-started
- (destructuring-bind (stats test) event-args
- (with-current-buffer buffer
- (let* ((ewoc ert-results-ewoc)
- (pos (ert-stats-test-index stats test))
- (node (ewoc-nth ewoc pos)))
- (unless node
- ;; FIXME: How expensive is this assertion?
- (assert (or (zerop pos) (ewoc-nth ewoc (1- pos)))
- t)
- (setq node (ewoc-enter-last
- ewoc
- (make-ert-ewoc-entry :test test
- :hidden-p t))))
- (setf (ert-ewoc-entry-test (ewoc-data node)) test)
- (setf (ert-ewoc-entry-result (ewoc-data node)) nil)
- (aset ert-results-progress-bar-string pos
- (ert-char-for-test-result nil t))
- (ert-results-update-stats-display ewoc stats)
- (ewoc-invalidate ewoc node)))))
- (test-ended
- (destructuring-bind (stats test result) event-args
- (with-current-buffer buffer
- (let* ((ewoc ert-results-ewoc)
- (pos (ert-stats-test-index stats test))
- (node (ewoc-nth ewoc pos)))
- (setf (ert-ewoc-entry-result (ewoc-data node)) result)
- (when (ert-ewoc-entry-hidden-p (ewoc-data node))
- (setf (ert-ewoc-entry-hidden-p (ewoc-data node))
- (ert-test-result-expected-p test result)))
- (aset ert-results-progress-bar-string pos
- (ert-char-for-test-result result
- (ert-test-result-expected-p
- test result)))
- (ert-results-update-stats-display ewoc stats)
- (ewoc-invalidate ewoc node))))))))
- (ert-run-tests
- selector
- listener)))
-
-(defvar ert-batch-backtrace-right-margin 70
- "*The maximum line length for printing backtraces in `ert-run-tests-batch'.")
-
-(defun ert-run-tests-batch (selector)
- "Run the tests specified by SELECTOR, printing results to the terminal.
-
-Returns the stats object."
- (ert-run-tests
- selector
- (lambda (event-type &rest event-args)
- (ecase event-type
- (run-started
- (destructuring-bind (stats) event-args
- (message "Running %s tests (%s)"
- (length (ert-stats-tests stats))
- (ert-format-time-iso8601 (ert-stats-start-time stats)))))
- (run-ended
- (destructuring-bind (stats abortedp) event-args
- (let ((unexpected (+ (ert-stats-passed-unexpected stats)
- (ert-stats-failed-unexpected stats)
- (ert-stats-error-unexpected stats))))
- (message "\n%sRan %s tests, %s results were as expected%s (%s)\n"
- (if (not abortedp)
- ""
- "Aborted: ")
- (ert-stats-total stats)
- (+ (ert-stats-passed-expected stats)
- (ert-stats-failed-expected stats)
- (ert-stats-error-expected stats))
- (if (zerop unexpected)
- ""
- (format ", %s unexpected" unexpected))
- (ert-format-time-iso8601 (ert-stats-end-time stats)))
- (unless (zerop unexpected)
- (message "%s unexpected results:" unexpected)
- (loop for test across (ert-stats-tests stats)
- for result = (ert-test-most-recent-result test) do
- (when (not (ert-test-result-expected-p test result))
- (message "%9s %S"
- (ert-string-for-test-result result nil)
- (ert-test-name test))))
- (message "%s" "")))))
- (test-started
- )
- (test-ended
- (destructuring-bind (stats test result) event-args
- (etypecase result
- (ert-test-passed)
- (ert-test-result-with-condition
- (message "Test %S backtrace:" (ert-test-name test))
- (with-temp-buffer
- (ert-print-backtrace (ert-test-result-with-condition-backtrace result))
- (goto-char (point-min))
- (while (not (eobp))
- (let ((start (point))
- (end (progn (end-of-line) (point))))
- (setq end (min end
- (+ start ert-batch-backtrace-right-margin)))
- (message "%s" (buffer-substring-no-properties
- start end)))
- (forward-line 1)))
- (with-temp-buffer
- (insert " ")
- (let ((print-escape-newlines t)
- (print-level 5)
- (print-length 10))
- (let ((begin (point)))
- (ert-pp-with-indentation-and-newline
- (ert-test-result-with-condition-condition result))))
- (goto-char (1- (point-max)))
- (assert (looking-at "\n"))
- (delete-char 1)
- (message "Test %S condition:" (ert-test-name test))
- (message "%s" (buffer-string))))
- (ert-test-aborted-with-non-local-exit))
- (let* ((max (prin1-to-string (length (ert-stats-tests stats))))
- (format-string (concat "%9s %"
- (prin1-to-string (length max))
- "s/" max " %S")))
- (message format-string
- (ert-string-for-test-result result
- (ert-test-result-expected-p
- test result))
- (1+ (ert-stats-test-index stats test))
- (ert-test-name test)))))))))
-
-
-;;; Commands and button actions for the results buffer.
-
-(define-derived-mode ert-results-mode fundamental-mode "ERT-Results"
- "Major mode for viewing results of ERT test runs.")
-
-(loop for (key binding) in
- '(("j" ert-results-jump-between-summary-and-result)
- ("." ert-results-find-test-at-point-other-window)
- ("r" ert-results-rerun-test-at-point)
- ("d" ert-results-rerun-test-at-point-debugging-errors)
- ("b" ert-results-pop-to-backtrace-for-test-at-point)
- ("m" ert-results-pop-to-messages-for-test-at-point)
- ("p" ert-results-toggle-printer-limits-for-test-at-point)
- ("D" ert-delete-test)
- ([?\t] forward-button)
- ([backtab] backward-button)
- )
- do
- (define-key ert-results-mode-map key binding))
-
-(define-button-type 'ert-results-progress-bar-button
- 'action #'ert-results-progress-bar-button-action
- 'help-echo "mouse-2, RET: Reveal test result")
-
-(define-button-type 'ert-test-name-button
- 'action #'ert-test-name-button-action
- 'help-echo "mouse-2, RET: Find test definition")
-
-(define-button-type 'ert-results-expand-collapse-button
- 'action #'ert-results-expand-collapse-button-action
- 'help-echo "mouse-2, RET: Expand/collapse test result")
-
-(defun ert-results-test-node-or-null-at-point ()
- "If point is on a valid ewoc node, return it; return nil otherwise.
-
-To be used in the ERT results buffer."
- (let* ((ewoc ert-results-ewoc)
- (node (ewoc-locate ewoc)))
- ;; `ewoc-locate' will return an arbitrary node when point is on
- ;; header or footer, or when all nodes are invisible. So we need
- ;; to validate its return value here.
- (if (and (>= (point) (ewoc-location node))
- (not (ert-ewoc-entry-hidden-p (ewoc-data node))))
- node
- nil)))
-
-(defun ert-results-test-node-at-point ()
- "If point is on a valid ewoc node, return it; signal an error otherwise.
-
-To be used in the ERT results buffer."
- (or (ert-results-test-node-or-null-at-point)
- (error "No test at point")))
-
-(defun ert-results-expand-collapse-button-action (button)
- "Expand or collapse the test node BUTTON belongs to."
- (let* ((ewoc ert-results-ewoc)
- (node (save-excursion
- (goto-char (ert-button-action-position))
- (ert-results-test-node-at-point)))
- (entry (ewoc-data node)))
- (setf (ert-ewoc-entry-expanded-p entry)
- (not (ert-ewoc-entry-expanded-p entry)))
- (ewoc-invalidate ewoc node)))
-
-(defun ert-results-find-test-at-point-other-window ()
- "Find the definition of the test at point in another window.
-
-To be used in the ERT results buffer."
- (interactive)
- (let* ((node (ert-results-test-node-at-point))
- (entry (ewoc-data node))
- (test (ert-ewoc-entry-test entry))
- (name (ert-test-name test)))
- (ert-find-test-other-window name)))
-
-(defun ert-test-name-button-action (button)
- "Find the definition of the test BUTTON belongs to, in another window."
- (let ((name (button-get button 'ert-test-name)))
- (ert-find-test-other-window name)))
-
-(defun ert-ewoc-position (ewoc node)
- "Return the position of NODE in EWOC, or nil if NODE is not in EWOC."
- (loop for i from 0
- for node-here = (ewoc-nth ewoc 0) then (ewoc-next ewoc node-here)
- do (when (eql node node-here)
- (return i))
- finally (return nil)))
-
-(defun ert-results-jump-between-summary-and-result ()
- "Jump back and forth between the test run summary and individual test results.
-
-From an ewoc node, jumps to the character that represents the
-same test in the progress bar, and vice versa.
-
-To be used in the ERT results buffer."
- ;; Maybe this command isn't actually needed much, but if it is, it
- ;; seems like an indication that the UI design is not optimal. If
- ;; jumping back and forth between a summary at the top of the buffer
- ;; and the error log in the remainder of the buffer is useful, then
- ;; the summary apparently needs to be easily accessible from the
- ;; error log, and perhaps it would be better to have it in a
- ;; separate buffer to keep it visible.
- (interactive)
- (let ((ewoc ert-results-ewoc)
- (progress-bar-begin ert-results-progress-bar-button-begin))
- (cond ((ert-results-test-node-or-null-at-point)
- (let* ((node (ert-results-test-node-at-point))
- (pos (ert-ewoc-position ewoc node)))
- (goto-char (+ progress-bar-begin pos))))
- ((and (<= progress-bar-begin (point))
- (< (point) (button-end (button-at progress-bar-begin))))
- (let* ((node (ewoc-nth ewoc (- (point) progress-bar-begin)))
- (entry (ewoc-data node)))
- (when (ert-ewoc-entry-hidden-p entry)
- (setf (ert-ewoc-entry-hidden-p entry) nil)
- (ewoc-invalidate ewoc node))
- (ewoc-goto-node ewoc node)))
- (t
- (goto-char progress-bar-begin)))))
-
-(defun ert-button-action-position ()
- "The buffer position where the last button action was triggered."
- (cond ((integerp last-command-event)
- (point))
- ((eventp last-command-event)
- (posn-point (event-start last-command-event)))
- (t (assert nil))))
-
-(defun ert-results-progress-bar-button-action (button)
- "Find the ewoc node that represents the same test as the character clicked on."
- (goto-char (ert-button-action-position))
- (ert-results-jump-between-summary-and-result))
-
-(defun ert-results-rerun-test-at-point ()
- "Re-run the test at point.
-
-To be used in the ERT results buffer."
- (interactive)
- (let* ((ewoc ert-results-ewoc)
- (node (ert-results-test-node-at-point))
- (entry (ewoc-data node))
- (old-test (ert-ewoc-entry-test entry))
- (test-name (ert-test-name old-test))
- ;; FIXME: Write a test for this lookup.
- (test (if test-name
- (if (ert-test-boundp test-name)
- (ert-get-test test-name)
- (error "No such test: %S" test-name))
- old-test))
- (stats ert-results-stats)
- (pos (gethash test (ert-stats-test-map stats)))
- (progress-message (format "Running test %S" (ert-test-name test))))
- ;; Need to save and restore point manually here: When point is on
- ;; the first visible ewoc entry while the header is updated, point
- ;; moves to the top of the buffer. This is undesirable, and a
- ;; simple `save-excursion' doesn't prevent it.
- (let ((point (point)))
- (unwind-protect
- (unwind-protect
- (progn
- (message "%s..." progress-message)
- (ert-run-or-rerun-test stats test
- ert-results-listener))
- (ert-results-update-stats-display ewoc stats)
- (message "%s...%s"
- progress-message
- (let ((result (ert-test-most-recent-result test)))
- (ert-string-for-test-result
- result (ert-test-result-expected-p test result)))))
- (goto-char point)))))
-
-(defun ert-results-rerun-test-at-point-debugging-errors ()
- "Re-run the test at point with `ert-debug-on-error' bound to t.
-
-To be used in the ERT results buffer."
- (interactive)
- (let ((ert-debug-on-error t))
- (ert-results-rerun-test-at-point)))
-
-(defun ert-print-backtrace (backtrace)
- "Format the backtrace BACKTRACE to the current buffer."
- ;; This is essentially a reimplementation of Fbacktrace
- ;; (src/eval.c), but for a saved backtrace, not the current one.
- (let ((print-escape-newlines t)
- (print-level 8)
- (print-length 50))
- (dolist (frame backtrace)
- (ecase (first frame)
- ((nil)
- ;; Special operator.
- (destructuring-bind (special-operator &rest arg-forms)
- (cdr frame)
- (insert
- (format " %S\n" (list* special-operator arg-forms)))))
- ((t)
- ;; Function call.
- (destructuring-bind (fn &rest args) (cdr frame)
- (insert (format " %S(" fn))
- (loop for firstp = t then nil
- for arg in args do
- (unless firstp
- (insert " "))
- (insert (format "%S" arg)))
- (insert ")\n")))))))
-
-(defun ert-results-pop-to-backtrace-for-test-at-point ()
- "Display the backtrace for the test at point.
-
-To be used in the ERT results buffer."
- (interactive)
- (let* ((node (ert-results-test-node-at-point))
- (entry (ewoc-data node))
- (test (ert-ewoc-entry-test entry))
- (result (ert-ewoc-entry-result entry)))
- (etypecase result
- (ert-test-passed (error "Test passed, no backtrace available"))
- (ert-test-result-with-condition
- (let ((backtrace (ert-test-result-with-condition-backtrace result))
- (buffer
- (let ((default-major-mode 'fundamental-mode))
- (get-buffer-create "*ERT Backtrace*"))))
- (pop-to-buffer buffer)
- (setq buffer-read-only t)
- (let ((inhibit-read-only t))
- (erase-buffer)
- ;; Use unibyte because `debugger-setup-buffer' also does so.
- (set-buffer-multibyte nil)
- (setq truncate-lines t)
- (ert-print-backtrace backtrace)
- (debugger-make-xrefs)
- (goto-char (point-min))))))))
-
-(defun ert-results-pop-to-messages-for-test-at-point ()
- "Display the part of the *Messages* buffer generated during the test at point.
-
-To be used in the ERT results buffer."
- (interactive)
- (let* ((node (ert-results-test-node-at-point))
- (entry (ewoc-data node))
- (test (ert-ewoc-entry-test entry))
- (result (ert-ewoc-entry-result entry)))
- (let ((buffer
- (let ((default-major-mode 'fundamental-mode))
- (get-buffer-create "*ERT Messages*"))))
- (pop-to-buffer buffer)
- (setq buffer-read-only t)
- (let ((inhibit-read-only t))
- (erase-buffer)
- (insert (ert-test-result-messages result))
- (goto-char (point-min))
- (insert "Messages for test `")
- (ert-insert-test-name-button (ert-test-name test))
- (insert "':\n")))))
-
-(defun ert-results-toggle-printer-limits-for-test-at-point ()
- "Toggle how much of the condition to print for the test at point.
-
-To be used in the ERT results buffer."
- (interactive)
- (let* ((ewoc ert-results-ewoc)
- (node (ert-results-test-node-at-point))
- (entry (ewoc-data node)))
- (setf (ert-ewoc-entry-extended-printer-limits-p entry)
- (not (ert-ewoc-entry-extended-printer-limits-p entry)))
- (ewoc-invalidate ewoc node)))
-
-(defun ert-activate-font-lock-keywords ()
- (font-lock-add-keywords
- nil
- '(("(\\(\\<ert-deftest\\)\\>\\s *\\(\\sw+\\)?"
- (1 font-lock-keyword-face nil t)
- (2 font-lock-function-name-face nil t)))))
-
-(defun* ert-remove-from-list (list-var element &key key test)
- "Remove ELEMENT from the value of LIST-VAR if present.
-
-This is an inverse of `add-to-list'."
- (unless key (setq key #'identity))
- (unless test (setq test #'equal))
- (setf (symbol-value list-var)
- (remove* element
- (symbol-value list-var)
- :key key
- :test test)))
-
-
-;;; Actions on load/unload.
-
-(add-to-list 'find-function-regexp-alist '(ert-deftest . ert-find-test-regexp))
-(add-to-list 'minor-mode-alist '(ert-current-run-stats
- (:eval
- (ert-tests-running-mode-line-indicator))))
-(add-to-list 'emacs-lisp-mode-hook 'ert-activate-font-lock-keywords)
-
-(defun ert-unload-function ()
- (ert-remove-from-list 'find-function-regexp-alist 'ert-deftest :key #'car)
- (ert-remove-from-list 'minor-mode-alist 'ert-current-run-stats :key #'car)
- (ert-remove-from-list 'emacs-lisp-mode-hook 'ert-activate-font-lock-keywords)
- nil)
-
-(defvar ert-unload-hook '())
-(add-hook 'ert-unload-hook 'ert-unload-function)
-
-
-;;; Self-tests.
-
-(ert-delete-all-tests)
-
-;; Test that test bodies are actually run.
-(defvar ert-test-body-was-run)
-(ert-deftest ert-test-body-runs ()
- (setq ert-test-body-was-run t))
-
-
-;; Test that nested test bodies run.
-(ert-deftest ert-nested-test-body-runs ()
- (lexical-let ((was-run nil))
- (let ((test (make-ert-test :body (lambda ()
- (setq was-run t)))))
- (assert (not was-run))
- (ert-run-test test)
- (assert was-run))))
-
-
-;; Test that pass/fail works.
-(ert-deftest ert-test-pass ()
- (let ((test (make-ert-test :body (lambda ()))))
- (let ((result (ert-run-test test)))
- (assert (typep result 'ert-test-passed)))))
-
-(ert-deftest ert-test-fail ()
- (let ((test (make-ert-test :body (lambda () (ert-fail "failure message")))))
- (let ((result (let ((ert-debug-on-error nil))
- (ert-run-test test))))
- (assert (typep result 'ert-test-failed) t)
- (assert (equal (ert-test-result-with-condition-condition result)
- '(ert-test-failed "failure message"))
- t))))
-
-(ert-deftest ert-test-fail-debug-with-condition-case ()
- (let ((test (make-ert-test :body (lambda () (ert-fail "failure message")))))
- (condition-case condition
- (progn
- (let ((ert-debug-on-error t))
- (ert-run-test test))
- (assert nil))
- ((error)
- (assert (equal condition '(ert-test-failed "failure message")) t)))))
-
-(ert-deftest ert-test-fail-debug-with-debugger-1 ()
- (let ((test (make-ert-test :body (lambda () (ert-fail "failure message")))))
- (let ((debugger (lambda (&rest debugger-args)
- (assert nil))))
- (let ((ert-debug-on-error nil))
- (ert-run-test test)))))
-
-(ert-deftest ert-test-fail-debug-with-debugger-2 ()
- (let ((test (make-ert-test :body (lambda () (ert-fail "failure message")))))
- (block nil
- (let ((debugger (lambda (&rest debugger-args)
- (return-from nil nil))))
- (let ((ert-debug-on-error t))
- (ert-run-test test))
- (assert nil)))))
-
-(ert-deftest ert-test-fail-debug-nested-with-debugger ()
- (let ((test (make-ert-test :body (lambda ()
- (let ((ert-debug-on-error t))
- (ert-fail "failure message"))))))
- (let ((debugger (lambda (&rest debugger-args)
- (assert nil nil "Assertion a"))))
- (let ((ert-debug-on-error nil))
- (ert-run-test test))))
- (let ((test (make-ert-test :body (lambda ()
- (let ((ert-debug-on-error nil))
- (ert-fail "failure message"))))))
- (block nil
- (let ((debugger (lambda (&rest debugger-args)
- (return-from nil nil))))
- (let ((ert-debug-on-error t))
- (ert-run-test test))
- (assert nil nil "Assertion b")))))
-
-(ert-deftest ert-test-error ()
- (let ((test (make-ert-test :body (lambda () (error "error message")))))
- (let ((result (let ((ert-debug-on-error nil))
- (ert-run-test test))))
- (assert (typep result 'ert-test-error) t)
- (assert (equal (ert-test-result-with-condition-condition result)
- '(error "error message"))
- t))))
-
-(ert-deftest ert-test-error-debug ()
- (let ((test (make-ert-test :body (lambda () (error "error message")))))
- (condition-case condition
- (progn
- (let ((ert-debug-on-error t))
- (ert-run-test test))
- (assert nil))
- ((error)
- (assert (equal condition '(error "error message")) t)))))
-
-
-;; Test that `should' works.
-(ert-deftest ert-test-should ()
- (let ((test (make-ert-test :body (lambda () (ert-should nil)))))
- (let ((result (let ((ert-debug-on-error nil))
- (ert-run-test test))))
- (assert (typep result 'ert-test-failed) t)
- (assert (equal (ert-test-result-with-condition-condition result)
- '(ert-test-failed ((ert-should nil) :form nil :value nil)))
- t)))
- (let ((test (make-ert-test :body (lambda () (ert-should t)))))
- (let ((result (ert-run-test test)))
- (assert (typep result 'ert-test-passed) t))))
-
-(ert-deftest ert-test-should-value ()
- (ert-should (eql (ert-should 'foo) 'foo))
- (ert-should (eql (ert-should 'bar) 'bar)))
-
-(ert-deftest ert-test-should-not ()
- (let ((test (make-ert-test :body (lambda () (ert-should-not t)))))
- (let ((result (let ((ert-debug-on-error nil))
- (ert-run-test test))))
- (assert (typep result 'ert-test-failed) t)
- (assert (equal (ert-test-result-with-condition-condition result)
- '(ert-test-failed ((ert-should-not t) :form t :value t)))
- t)))
- (let ((test (make-ert-test :body (lambda () (ert-should-not nil)))))
- (let ((result (ert-run-test test)))
- (assert (typep result 'ert-test-passed)))))
-
-
-(ert-deftest ert-test-should-error ()
- ;; No error.
- (let ((test (make-ert-test :body (lambda () (ert-should-error (progn))))))
- (let ((result (let ((ert-debug-on-error nil))
- (ert-run-test test))))
- (ert-should (typep result 'ert-test-failed))
- (ert-should (equal (ert-test-result-with-condition-condition result)
- '(ert-test-failed
- ((ert-should-error (progn))
- :form (progn)
- :value nil
- :fail-reason "did not signal an error"))))))
- ;; A simple error.
- (let ((test (make-ert-test :body (lambda () (ert-should-error (error "foo"))))))
- (let ((result (ert-run-test test)))
- (ert-should (typep result 'ert-test-passed))))
- ;; Error of unexpected type, no test.
- (let ((test (make-ert-test :body (lambda ()
- (ert-should-error (error "foo")
- :type 'singularity-error)))))
- (let ((result (ert-run-test test)))
- (ert-should (typep result 'ert-test-failed))
- (ert-should (equal
- (ert-test-result-with-condition-condition result)
- '(ert-test-failed
- ((ert-should-error (error "foo") :type 'singularity-error)
- :form (error "foo")
- :condition (error "foo")
- :fail-reason
- "the error signalled did not have the expected type"))))))
- ;; Error of the expected type, no test.
- (let ((test (make-ert-test :body (lambda ()
- (ert-should-error (signal 'singularity-error
- nil)
- :type 'singularity-error)))))
- (let ((result (ert-run-test test)))
- (ert-should (typep result 'ert-test-passed))))
- ;; Error that fails the test, no type.
- (let ((test (make-ert-test :body (lambda ()
- (ert-should-error
- (error "foo")
- :test (lambda (error) nil))))))
- (let ((result (ert-run-test test)))
- (ert-should (typep result 'ert-test-failed))
- (ert-should (equal (ert-test-result-with-condition-condition result)
- '(ert-test-failed
- ((ert-should-error (error "foo") :test (lambda (error) nil))
- :form (error "foo")
- :condition (error "foo")
- :fail-reason
- "the error signalled did not pass the test"))))))
- ;; Error that passes the test, no type.
- (let ((test (make-ert-test :body (lambda ()
- (ert-should-error (error "foo")
- :test (lambda (error) t))))))
- (let ((result (ert-run-test test)))
- (ert-should (typep result 'ert-test-passed))))
- ;; Error that has the expected type but fails the test.
- (let ((test (make-ert-test :body (lambda ()
- (ert-should-error
- (signal 'singularity-error nil)
- :type 'singularity-error
- :test (lambda (error) nil))))))
- (let ((result (ert-run-test test)))
- (ert-should (typep result 'ert-test-failed))
- (ert-should (equal (ert-test-result-with-condition-condition result)
- '(ert-test-failed
- ((ert-should-error (signal 'singularity-error nil)
- :type 'singularity-error
- :test (lambda (error) nil))
- :form (signal singularity-error nil)
- :condition (singularity-error)
- :fail-reason
- "the error signalled did not pass the test"))))))
- ;; Error that has the expected type and passes the test.
- (let ((test (make-ert-test :body (lambda ()
- (ert-should-error
- (signal 'singularity-error nil)
- :type 'singularity-error
- :test (lambda (error) t))))))
- (let ((result (ert-run-test test)))
- (ert-should (typep result 'ert-test-passed))))
- )
-
-(ert-deftest ert-test-should-error-subtypes ()
- (let ((test (make-ert-test
- :body (lambda ()
- (ert-should-error (signal 'singularity-error nil)
- :type 'singularity-error
- :exclude-subtypes t)))))
- (let ((result (ert-run-test test)))
- (ert-should (typep result 'ert-test-passed))))
- (let ((test (make-ert-test
- :body (lambda ()
- (ert-should-error (signal 'arith-error nil)
- :type 'singularity-error)))))
- (let ((result (ert-run-test test)))
- (ert-should (typep result 'ert-test-failed))
- (ert-should (equal
- (ert-test-result-with-condition-condition result)
- '(ert-test-failed
- ((ert-should-error (signal 'arith-error nil)
- :type 'singularity-error)
- :form (signal arith-error nil)
- :condition (arith-error)
- :fail-reason
- "the error signalled did not have the expected type"))))))
- (let ((test (make-ert-test
- :body (lambda ()
- (ert-should-error (signal 'arith-error nil)
- :type 'singularity-error
- :exclude-subtypes t)))))
- (let ((result (ert-run-test test)))
- (ert-should (typep result 'ert-test-failed))
- (ert-should (equal
- (ert-test-result-with-condition-condition result)
- '(ert-test-failed
- ((ert-should-error (signal 'arith-error nil)
- :type 'singularity-error
- :exclude-subtypes t)
- :form (signal arith-error nil)
- :condition (arith-error)
- :fail-reason
- "the error signalled did not have the expected type"))))))
- (let ((test (make-ert-test
- :body (lambda ()
- (ert-should-error (signal 'singularity-error nil)
- :type 'arith-error
- :exclude-subtypes t)))))
- (let ((result (ert-run-test test)))
- (ert-should (typep result 'ert-test-failed))
- (ert-should (equal
- (ert-test-result-with-condition-condition result)
- '(ert-test-failed
- ((ert-should-error (signal 'singularity-error nil)
- :type 'arith-error
- :exclude-subtypes t)
- :form (signal singularity-error nil)
- :condition (singularity-error)
- :fail-reason
- "the error signalled was a subtype of the expected type"))))))
- )
-
-;; Test that `should' errors contain the information we expect them to.
-(defmacro ert-test-my-list (&rest args)
- `(list ,@args))
-
-(ert-deftest ert-test-should-failure-debugging ()
- (loop for (body expected-condition) in
- `((,(lambda () (let ((x nil)) (ert-should x)))
- (ert-test-failed ((ert-should x) :form x :value nil)))
- (,(lambda () (let ((x t)) (ert-should-not x)))
- (ert-test-failed ((ert-should-not x) :form x :value t)))
- (,(lambda () (let ((x t)) (ert-should (not x))))
- (ert-test-failed ((ert-should (not x)) :form (not t) :value nil)))
- (,(lambda () (let ((x nil)) (ert-should-not (not x))))
- (ert-test-failed ((ert-should-not (not x)) :form (not nil) :value t)))
- (,(lambda () (let ((x t) (y nil)) (ert-should-not (ert-test-my-list x y))))
- (ert-test-failed
- ((ert-should-not (ert-test-my-list x y))
- :form (list t nil)
- :value (t nil))))
- (,(lambda () (let ((x t)) (ert-should (error "foo"))))
- (error "foo")))
- do
- (let ((test (make-ert-test :body body)))
- (condition-case actual-condition
- (progn
- (let ((ert-debug-on-error t))
- (ert-run-test test))
- (assert nil))
- ((error)
- (ert-should (equal actual-condition expected-condition)))))))
-
-(ert-deftest ert-test-messages ()
- (let* ((message-string "Test message")
- (messages-buffer (get-buffer-create "*Messages*"))
- (test (make-ert-test :body (lambda () (message "%s" message-string)))))
- (with-current-buffer messages-buffer
- (let ((result (ert-run-test test)))
- (ert-should (equal (concat message-string "\n")
- (ert-test-result-messages result)))))))
-
-(defun ert-call-with-temporary-messages-buffer (thunk)
- (lexical-let ((new-buffer-name (generate-new-buffer-name
- "*Messages* orig buffer")))
- (unwind-protect
- (progn
- (with-current-buffer (get-buffer-create "*Messages*")
- (rename-buffer new-buffer-name))
- (get-buffer-create "*Messages*")
- (funcall thunk))
- (kill-buffer "*Messages*")
- (with-current-buffer new-buffer-name
- (rename-buffer "*Messages*")))))
-
-(ert-deftest ert-test-messages-on-log-truncation ()
- (let ((test (make-ert-test
- :body (lambda ()
- ;; Emacs would combine messages if we
- ;; generate the same message multiple
- ;; times.
- (message "a")
- (message "b")
- (message "c")
- (message "d")))))
- (let (result)
- (ert-call-with-temporary-messages-buffer
- (lambda ()
- (let ((message-log-max 2))
- (setq result (ert-run-test test)))
- (ert-should (equal (with-current-buffer "*Messages*"
- (buffer-string))
- "c\nd\n"))))
- (ert-should (equal (ert-test-result-messages result) "a\nb\nc\nd\n")))))
-
-;; Test `ert-select-tests'.
-(ert-deftest ert-test-select-regexp ()
- (ert-should (equal (ert-select-tests "^ert-test-select-regexp$" t)
- (list (ert-get-test 'ert-test-select-regexp)))))
-
-(ert-deftest ert-test-test-boundp ()
- (ert-should (ert-test-boundp 'ert-test-test-boundp))
- (ert-should-not (ert-test-boundp (make-symbol "ert-not-a-test"))))
-
-(ert-deftest ert-test-select-member ()
- (ert-should (equal (ert-select-tests '(member ert-test-select-member) t)
- (list (ert-get-test 'ert-test-select-member)))))
-
-(ert-deftest ert-test-select-test ()
- (ert-should (equal (ert-select-tests (ert-get-test 'ert-test-select-test) t)
- (list (ert-get-test 'ert-test-select-test)))))
-
-(ert-deftest ert-test-select-symbol ()
- (ert-should (equal (ert-select-tests 'ert-test-select-symbol t)
- (list (ert-get-test 'ert-test-select-symbol)))))
-
-(ert-deftest ert-test-select-and ()
- (let ((test (make-ert-test
- :name nil
- :body nil
- :most-recent-result (make-ert-test-failed
- :condition nil
- :backtrace nil))))
- (ert-should (equal (ert-select-tests `(and (member ,test) :failed) t)
- (list test)))))
-
-
-;; Test utility functions.
-(ert-deftest ert-proper-list-p ()
- (ert-should (ert-proper-list-p '()))
- (ert-should (ert-proper-list-p '(1)))
- (ert-should (ert-proper-list-p '(1 2)))
- (ert-should (ert-proper-list-p '(1 2 3)))
- (ert-should (ert-proper-list-p '(1 2 3 4)))
- (ert-should (not (ert-proper-list-p 'a)))
- (ert-should (not (ert-proper-list-p '(1 . a))))
- (ert-should (not (ert-proper-list-p '(1 2 . a))))
- (ert-should (not (ert-proper-list-p '(1 2 3 . a))))
- (ert-should (not (ert-proper-list-p '(1 2 3 4 . a))))
- (let ((a (list 1)))
- (setf (cdr (last a)) a)
- (ert-should (not (ert-proper-list-p a))))
- (let ((a (list 1 2)))
- (setf (cdr (last a)) a)
- (ert-should (not (ert-proper-list-p a))))
- (let ((a (list 1 2 3)))
- (setf (cdr (last a)) a)
- (ert-should (not (ert-proper-list-p a))))
- (let ((a (list 1 2 3 4)))
- (setf (cdr (last a)) a)
- (ert-should (not (ert-proper-list-p a))))
- (let ((a (list 1 2)))
- (setf (cdr (last a)) (cdr a))
- (ert-should (not (ert-proper-list-p a))))
- (let ((a (list 1 2 3)))
- (setf (cdr (last a)) (cdr a))
- (ert-should (not (ert-proper-list-p a))))
- (let ((a (list 1 2 3 4)))
- (setf (cdr (last a)) (cdr a))
- (ert-should (not (ert-proper-list-p a))))
- (let ((a (list 1 2 3)))
- (setf (cdr (last a)) (cddr a))
- (ert-should (not (ert-proper-list-p a))))
- (let ((a (list 1 2 3 4)))
- (setf (cdr (last a)) (cddr a))
- (ert-should (not (ert-proper-list-p a))))
- (let ((a (list 1 2 3 4)))
- (setf (cdr (last a)) (cdddr a))
- (ert-should (not (ert-proper-list-p a)))))
-
-(ert-deftest ert-parse-keys-and-body ()
- (ert-should (equal (ert-parse-keys-and-body "doc" '(foo))
- '(nil "doc" (foo))))
- (ert-should (equal (ert-parse-keys-and-body "doc" '(:bar foo))
- '((:bar foo) "doc" nil)))
- (ert-should (equal (ert-parse-keys-and-body nil '(:bar foo))
- '((:bar foo) nil nil)))
- (ert-should (equal (ert-parse-keys-and-body "doc" '(:bar foo))
- '((:bar foo) "doc" nil)))
- (ert-should (equal (ert-parse-keys-and-body nil '(:bar foo a (b)))
- '((:bar foo) nil (a (b)))))
- (ert-should (equal (ert-parse-keys-and-body nil '(:bar foo :a (b)))
- '((:bar foo :a (b)) nil nil)))
- (ert-should (equal (ert-parse-keys-and-body nil '(bar foo :a (b)))
- '(nil nil (bar foo :a (b)))))
- (ert-should-error (ert-parse-keys-and-body nil '(:bar foo :a))))
-
-
-
-;; Test `ert-run-tests'.
-(ert-deftest ert-test-run-tests ()
- (let ((passing-test (make-ert-test :name 'passing-test
- :body (lambda () (ert-pass))))
- (failing-test (make-ert-test :name 'failing-test
- :body (lambda () (ert-fail
- "failure message"))))
- )
- (let ((ert-debug-on-error nil))
- (let* ((buffer-name (generate-new-buffer-name " *ert-test-run-tests*"))
- (messages nil)
- (mock-message-fn
- (lambda (format-string &rest args)
- (push (apply #'format format-string args) messages))))
- (save-window-excursion
- (unwind-protect
- (let ((case-fold-search nil))
- (ert-run-tests-interactively
- `(member ,passing-test ,failing-test) buffer-name
- mock-message-fn)
- (ert-should (equal messages `(,(concat
- "Ran 2 tests, 1 results were "
- "as expected, 1 unexpected"))))
- (with-current-buffer buffer-name
- (goto-char (point-min))
- (ert-should (equal
- (buffer-substring (point-min)
- (save-excursion
- (forward-line 5)
- (point)))
- (concat
- "Selector: (member <passing-test> <failing-test>)\n"
- "Passed: 1 (0 unexpected)\n"
- "Failed: 1 (1 unexpected)\n"
- "Error: 0 (0 unexpected)\n"
- "Total: 2/2\n")))))
- (when (get-buffer buffer-name)
- (kill-buffer buffer-name))))))))
-
-(ert-deftest ert-test-special-operator-p ()
- (ert-should (ert-special-operator-p 'if))
- (ert-should-not (ert-special-operator-p 'car))
- (ert-should-not (ert-special-operator-p 'ert-special-operator-p))
- (let ((b (gensym)))
- (ert-should-not (ert-special-operator-p b))
- (fset b 'if)
- (ert-should (ert-special-operator-p b))))
-
-;; This test attempts to demonstrate that there is no way to force
-;; immediate truncation of the *Messages* buffer from Lisp (and hence
-;; justifies the existence of
-;; `ert-force-message-log-buffer-truncation'): The only way that came
-;; to my mind was (message ""), which doesn't have the desired effect.
-(ert-deftest ert-test-builtin-message-log-flushing ()
- (ert-call-with-temporary-messages-buffer
- (lambda ()
- (with-current-buffer "*Messages*"
- (let ((message-log-max 2))
- (let ((message-log-max t))
- (loop for i below 4 do
- (message "%s" i))
- (ert-should (eql (count-lines (point-min) (point-max)) 4)))
- (ert-should (eql (count-lines (point-min) (point-max)) 4))
- (message "")
- (ert-should (eql (count-lines (point-min) (point-max)) 4))
- (message "Test message")
- (ert-should (eql (count-lines (point-min) (point-max)) 2)))))))
-
-(ert-deftest ert-test-force-message-log-buffer-truncation ()
- (labels ((body ()
- (loop for i below 5 do
- (message "%s" i)))
- (c (x)
- (ert-call-with-temporary-messages-buffer
- (lambda ()
- (let ((message-log-max x))
- (body))
- (with-current-buffer "*Messages*"
- (buffer-string)))))
- (lisp (x)
- (ert-call-with-temporary-messages-buffer
- (lambda ()
- (let ((message-log-max t))
- (body))
- (let ((message-log-max x))
- (ert-force-message-log-buffer-truncation))
- (with-current-buffer "*Messages*"
- (buffer-string))))))
- (loop for x in '(0 1 2 3 4 5 6 t) do
- (ert-should (equal (c x) (lisp x))))))
-
-(defun ert-run-self-tests ()
- ;; Run tests and make sure they actually ran.
- (let ((window-configuration (current-window-configuration)))
- (let ((ert-test-body-was-run nil))
- ;; The buffer name chosen here should not compete with the default
- ;; results buffer name for completion in `switch-to-buffer'.
- (let ((stats (ert-run-tests-interactively "^ert-" " *ert self-tests*")))
- (assert ert-test-body-was-run)
- (when (zerop (+ (ert-stats-passed-unexpected stats)
- (ert-stats-failed-unexpected stats)
- (ert-stats-error-unexpected stats)))
- ;; Hide results window only when everything went well.
- (set-window-configuration window-configuration))))))
-
-(provide 'ert)
-
-;;; ert.el ends here