From 0d342f0aee3f2f800e486c0051dabe718a7b2841 Mon Sep 17 00:00:00 2001 From: Tom Willemsen Date: Wed, 23 Mar 2011 11:14:27 +0100 Subject: I don't like nxhtml --- emacs.d/nxhtml/tests/ert.el | 2418 ------------------------------------------- 1 file changed, 2418 deletions(-) delete mode 100644 emacs.d/nxhtml/tests/ert.el (limited to 'emacs.d/nxhtml/tests/ert.el') 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 ""))) - (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 - '(("(\\(\\\\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 )\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 -- cgit v1.2.3-54-g00ecf