1 ;;; ert.el --- Emacs Lisp Regression Testing
3 ;; Modified by Lennart Borgman 2008-07-13 to make all global symbols
4 ;; use the "ert-" prefix.
6 ;; Copyright (C) 2007, 2008 Christian M. Ohler
8 ;; Author: Christian M. Ohler
10 ;; Keywords: lisp, tools
12 ;; This file is NOT part of GNU Emacs.
14 ;; This program is free software: you can redistribute it and/or
15 ;; modify it under the terms of the GNU General Public License as
16 ;; published by the Free Software Foundation, either version 3 of the
17 ;; License, or (at your option) any later version.
19 ;; This program is distributed in the hope that it will be useful, but
20 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
21 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 ;; General Public License for more details.
24 ;; You should have received a copy of the GNU General Public License
25 ;; along with this program. If not, see `http://www.gnu.org/licenses/'.
29 ;; ERT is a tool for automated testing in Emacs Lisp. Its main
30 ;; features are facilities for defining and running test cases and
31 ;; reporting the results as well as for debugging test failures
34 ;; The main entry points are `ert-deftest', which is similar to
35 ;; `defun' but defines a test, and `ert-run-tests-interactively',
36 ;; which runs tests and offers an interactive interface for inspecting
37 ;; results and debugging. There is also `ert-run-tests-batch' for
38 ;; non-interactive use.
40 ;; The body of `ert-deftest' forms resembles a function body, but the
41 ;; additional operators `should', `should-not' and `should-error' are
42 ;; available. `should' is similar to cl's `assert', but signals a
43 ;; different error when its condition is violated that is caught and
44 ;; processed by ERT. In addition, it analyzes its argument form and
45 ;; records information that helps debugging (`assert' tries to do
46 ;; something similar when its second argument SHOW-ARGS is true, but
47 ;; `should' is more sophisticated). For information on `should-not'
48 ;; and `should-error', see their docstrings.
52 ;; ;; Define a test named `foo'.
53 ;; (ert-deftest foo ()
54 ;; (ert-should (= (+ 1 2) 4)))
57 ;; (ert-run-tests-interactively 'foo)
59 ;; generates the following output (in addition to some statistics) in
60 ;; the *ert* results buffer:
72 ;; This indicates that the test failed. The `should' form that failed
73 ;; was (ert-should (= (+ 1 2) 4)), because its inner form, after
74 ;; evaluation of its arguments, was the function call (= 3 4), which
77 ;; Obviously, this is a bug in the test case, not in the functions `+'
78 ;; or `='. In the results buffer, with point on the test result, the
79 ;; key "." can be used to jump to the definition of the test to modify
80 ;; it to correct the bug. After evaluating the modified definition
81 ;; and switching back to the results buffer, the key "r" will re-run
82 ;; the test and show the new result.
87 ;; Functions like `ert-run-tests-interactively' accept a test
88 ;; selector, which is a Lisp expression specifying a set of tests.
89 ;; Each test name is a selector that refers to that test, the selector
90 ;; `t' refers to all tests, and the selector `:failed' refers to all
91 ;; tests that failed; but more complex selectors are available. Test
92 ;; selector syntax is similar to cl's type specifier syntax. See the
93 ;; docstring of `ert-select-tests' for details.
96 ;; Comparison with other testing tools
98 ;; ERT allows test-driven development similar to *Unit frameworks for
99 ;; other languages. However, two common *Unit features are notably
100 ;; absent from ERT: fixtures and test suites.
102 ;; Fixtures, as used e.g. in SUnit or JUnit, have two main purposes:
103 ;; Setting up (and tearing down) an environment for a set of test
104 ;; cases, and making that environment accessible through object
105 ;; attributes that can be used like local variables.
107 ;; While fixtures are a great syntactic simplification in other
108 ;; languages, they are not very useful in Lisp, where higher-order
109 ;; functions and `unwind-protect' are available. One way to implement
110 ;; and use a fixture in ERT is
112 ;; (defun my-fixture (body)
114 ;; (progn ...set up...
118 ;; (ert-deftest my-test ()
121 ;; ...test code...)))
123 ;; (Another way would be a `with-my-fixture' macro.) This solves the
124 ;; set-up and tear-down part, and additionally allows any test case to
125 ;; use any combination of fixtures, so it is more general than what
126 ;; other tools typically allow.
128 ;; If the test case needs access to the environment the fixture sets
129 ;; up, the fixture can be modified to pass arguments to the body.
131 ;; These are standard Lisp idioms. Special syntax for them could be
132 ;; added easily enough, but would provide only a minor simplification.
134 ;; (Note that splitting set-up and tear-down into separate functions,
135 ;; like *Unit tools usually do, makes it impossible to establish
136 ;; dynamic `let' bindings as part of the fixture. So, blindly
137 ;; imitating the way fixtures are implemented in other languages would
138 ;; be counter-productive in Lisp.)
141 ;; The purpose of test suites is to group related test cases together.
142 ;; The most common use of this is to run just the tests for one
143 ;; particular module. Since symbol prefixes are the usual way of
144 ;; separating module namespaces in Emacs Lisp, test selectors already
145 ;; solve this by allowing regexp matching on test names; e.g., the
146 ;; selector "^ert-" selects ERT's self-tests.
148 ;; If test suites containing arbitrary sets of tests are found to be
149 ;; desirable, it would be easy to add a `define-test-selector'
150 ;; mechanism that introduces a new selector, defined in terms of
151 ;; existing ones; e.g.
153 ;; ;; Note that `define-test-selector' does not exist yet.
154 ;; (define-test-selector my-test-suite () `(member foo-test bar-test))
156 ;; would define a test suite named `my-test-suite' consisting of
157 ;; `foo-test' and `bar-test'. See also `deftype' in Common Lisp.
160 ;; TODO: Add `skip' feature for tests that can't run in current environment.
165 (eval-when-compile (require 'cl))
170 (defvar ert-debug-on-error nil
171 "Non-nil means enter debugger when a test fails or terminates with an error.")
174 ;;; Defining and locating tests.
176 ;; The data structure that represents a test case.
181 (most-recent-result nil)
182 (expected-result-type 'ert-test-passed))
184 (defun ert-test-boundp (symbol)
185 "Return non-nil if SYMBOL names a test."
186 (and (get symbol 'ert-test) t))
188 (defun ert-get-test (symbol)
189 "If SYMBOL names a test, return that. Signal an error otherwise."
190 (assert (ert-test-boundp symbol) t)
191 (get symbol 'ert-test))
193 (defun ert-set-test (symbol doc definition)
194 "Make SYMBOL name the test DEFINITION, and return DEFINITION."
196 (put symbol 'ert-test-documentation doc))
197 (put symbol 'ert-test definition)
200 (defun ert-make-test-unbound (symbol)
201 "Make SYMBOL name no test. Return SYMBOL."
202 (remprop symbol 'ert-test)
205 (defun ert-test-result-expected-p (test result)
206 "Return non-nil if RESULT matches the expected result type for TEST."
207 (typep result (ert-test-expected-result-type test)))
209 (defvar ert-find-test-regexp
210 (concat "^\\s-*(ert-deftest"
211 find-function-space-re
213 "The regexp the `find-function' mechanisms use for locating test definitions.")
216 (defun ert-parse-keys-and-body (docstr keys-and-body)
217 "Split KEYS-AND-BODY into keyword-and-value pairs and the remaining body.
219 KEYS-AND-BODY should have the form of a property list, with the
220 exception that only keywords are permitted as keys and that the
221 tail -- the body -- is a list of forms that does not start with a
224 Returns a two-element list containing the keys-and-values plist
226 (unless (stringp docstr)
228 (setq keys-and-body (cons docstr keys-and-body))
230 (let ((extracted-key-accu '())
231 (remaining keys-and-body))
232 (while (and (consp remaining) (keywordp (first remaining)))
233 (let ((keyword (pop remaining)))
234 (unless (consp remaining)
235 (error "Value expected after keyword %S in %S"
236 keyword keys-and-body))
237 (when (assoc keyword extracted-key-accu)
238 (warn "Keyword %S appears more than once in %S" keyword
240 (push (cons keyword (pop remaining)) extracted-key-accu)))
241 (setq extracted-key-accu (nreverse extracted-key-accu))
242 (list (loop for (key . value) in extracted-key-accu
248 (defvar ert-error-on-test-redefinition nil)
251 (defmacro* ert-deftest (name ()
254 "Define NAME (a symbol) as a test.
256 \(fn NAME () [:documentation DOCSTRING] [:expected-result TYPE] BODY...)"
257 ;; The :documentation would be unreadable. I have therefore added
258 ;; docstr that will look like documentation use to in Emacs. Maybe
259 ;; add function ert-describe-test?
261 (debug (&define :name test name sexp
262 [&optional [":documentation" stringp]]
263 [&optional [":expected-result" sexp]]
265 (destructuring-bind ((&key (expected-result nil expected-result-supplied-p)
266 (documentation nil documentation-supplied-p))
269 (ert-parse-keys-and-body docstr keys-and-body)
271 ;; Guard against missing/badly named tests:
272 (when (and ert-error-on-test-redefinition
274 (get ',name 'ert-test))
275 (with-output-to-temp-buffer "*Ert Error*"
276 (with-current-buffer "*Ert Error*"
279 " is already defined in "
280 (format "%s" (find-definition-noselect ',name 'ert-deftest))
282 "Tip: Use `ert-delete-all-tests' or `ert-delete-test' before redefining tests."
284 (if (y-or-n-p "Do you want to call ert-delete-all-tests and then continue? ")
285 ;; Fix-me: This does not work, why?
286 (ert-delete-all-tests)
287 (error "Test %s is already defined in %s"
289 (find-definition-noselect ',name 'ert-deftest))))
294 :body (lambda () ,@body)
295 ,@(when expected-result-supplied-p
296 `(:expected-result-type ,expected-result))
297 ,@(when documentation-supplied-p
298 `(:documentation ,documentation))))
299 ;; This hack allows `symbol-file' to associate `ert-deftest'
300 ;; forms with files, and therefore enables `find-function' to
301 ;; work with tests. However, it leads to warnings in
302 ;; `unload-feature', which doesn't know how to undefine tests
303 ;; and has no mechanism for extension.
304 (push '(ert-deftest . ,name) current-load-list)
307 (defun ert-read-test-name (prompt &optional default-value history)
308 "Read the name of a test and return it as a symbol.
309 Prompt with PROMPT. By default, return DEFAULT-VALUE."
310 (when (symbolp default-value) (setq default-value (symbol-name default-value)))
311 (intern (completing-read prompt obarray #'ert-test-boundp
312 t nil history default-value nil)))
314 (defun ert-find-test-other-window (test-name)
315 "Find, in another window, the definition of TEST-NAME."
316 (interactive (list (ert-read-test-name "Find test definition: ")))
317 (find-function-do-it test-name 'ert-deftest 'switch-to-buffer-other-window))
319 (defun ert-delete-test (test-name)
320 "An interactive interface to `ert-make-test-unbound'."
321 (interactive (list (let ((default (thing-at-point 'symbol)))
323 (set-text-properties 0 (length default) nil default)
324 (when (or (string= default "nil") (intern-soft default))
325 (setq default (intern default)))
326 (unless (ert-test-boundp default)
328 (completing-read (if (null default)
330 (format "Delete test (default %s): "
332 obarray #'ert-test-boundp
333 'really-require-match
334 nil nil default nil))))
335 (ert-make-test-unbound test-name))
337 (defun ert-delete-all-tests ()
338 "Make all symbols in `obarray' name no test."
340 (when (interactive-p)
341 (unless (y-or-n-p "Delete all tests? ")
343 (mapc #'ert-delete-test (mapcar #'ert-test-name (ert-select-tests t t)))
347 (defun ert-make-end-marker (buffer must-exist)
348 "Return a marker to the end of buffer BUFFER.
349 BUFFER may be a string or a buffer. If BUFFER does not exist
352 The buffer must exist if MUST-EXIST is non-nil.
355 `ert-end-of-messages'
356 `ert-end-of-warnings'"
357 (let ((buf (if must-exist
359 (get-buffer-create buffer))))
363 (with-current-buffer buf
366 (point-max-marker))))))
368 (defun ert-end-of-messages ()
369 "Return a marker to the end of *Messages* buffer."
370 (ert-make-end-marker "*Messages*" nil))
372 (defun ert-end-of-warnings ()
373 "Return a marker to the end of *Warnings* buffer."
374 (ert-make-end-marker "*Warnings*" nil))
376 (defun ert-search-after (after regexp)
377 "Search after marker in AFTER for regular expression REGEXP.
378 Return a alist of position and matches. AFTER should have been
379 created with `ert-make-end-marker'.
381 This is supposed to be used for messages and trace buffers.
385 (let ((buf (marker-buffer after)))
386 (with-current-buffer buf
391 (while (re-search-forward regexp nil t)
392 (setq res (cons (match-data) res))))
395 ;; fix-me: add a conventient way to look at the result of
396 ;; `ert-search-after'. Probably this means adding something more to
397 ;; the returned result.
399 (defvar ert-messages-mark)
400 (defun ert-get-messages (regexp)
401 "Search *Messages* buffer for regular expression REGEXP.
402 This should be used within `ert-deftest'. Search begins where
403 the buffer ended when test started.
408 (ert-search-after ert-messages-mark regexp))
410 (defvar ert-warnings-mark)
411 (defun ert-get-warnings (regexp)
412 "Search *Warnings* buffer for regular expression REGEXP.
413 See `ert-get-messages' for more information."
414 (ert-search-after ert-warnings-mark regexp))
419 (defun ert-select-tests (selector universe)
420 "Select, from UNIVERSE, a set of tests according to SELECTOR.
422 UNIVERSE should be a list of tests, or t, which refers to all
423 tests named by symbols in `obarray'.
425 Returns the set of tests as a list.
429 nil -- Selects the empty set.
430 t -- Selects UNIVERSE.
431 :new -- Selects all tests that have not been run yet.
432 :failed, :passed, :error -- Select tests according to their most recent result.
433 :expected, :unexpected -- Select tests according to their most recent result.
434 a string -- Selects all tests that have a name that matches the string, a regexp.
435 a test -- Selects that test.
436 a symbol -- Selects the test that the symbol names, errors if none.
437 \(member TESTS...\) -- Selects TESTS, a list of tests or symbols naming tests.
438 \(eql TEST\) -- Selects TEST, a test or a symbol naming a test.
439 \(and SELECTORS...\) -- Selects the tests that match all SELECTORS.
440 \(or SELECTORS...\) -- Selects the tests that match any SELECTOR.
441 \(not SELECTOR\) -- Selects all tests that do not match SELECTOR.
442 \(satisfies PREDICATE\) -- Selects all tests that satisfy PREDICATE.
444 Only selectors that require a superset of tests, such
445 as (satisfies ...), strings, :new, etc. make use of UNIVERSE.
446 Selectors that do not, such as \(member ...\), just return the
447 set implied by them without checking whether it is really
448 contained in UNIVERSE."
449 ;; This code needs to match the etypecase in
450 ;; `ert-insert-human-readable-selector'.
453 ((member t) (etypecase universe
455 ((member t) (ert-select-tests "" universe))))
456 ((member :new) (ert-select-tests
457 `(satisfies ,(lambda (test)
458 (typep (ert-test-most-recent-result test)
461 ((member :failed) (ert-select-tests
462 `(satisfies ,(lambda (test)
463 (typep (ert-test-most-recent-result test)
466 ((member :passed) (ert-select-tests
467 `(satisfies ,(lambda (test)
468 (typep (ert-test-most-recent-result test)
471 ((member :error) (ert-select-tests
472 `(satisfies ,(lambda (test)
473 (typep (ert-test-most-recent-result test)
476 ((member :expected) (ert-select-tests
479 (ert-test-result-expected-p
481 (ert-test-most-recent-result test))))
483 ((member :unexpected) (ert-select-tests `(not :expected) universe))
486 ((member t) (mapcar #'ert-get-test
487 (apropos-internal selector #'ert-test-boundp)))
488 (list (remove-if-not (lambda (test)
489 (and (ert-test-name test)
490 (string-match selector (ert-test-name test))))
492 (ert-test (list selector))
494 (assert (ert-test-boundp selector))
495 (list (ert-get-test selector)))
497 (destructuring-bind (operator &rest operands) selector
500 (mapcar (lambda (purported-test)
501 (etypecase purported-test
502 (symbol (assert (ert-test-boundp purported-test))
503 (ert-get-test purported-test))
504 (ert-test purported-test)))
507 (assert (eql (length operands) 1))
508 (ert-select-tests `(member ,@operands) universe))
510 ;; Do these definitions of AND, NOT and OR satisfy de
511 ;; Morgan's rules? Should they?
512 (case (length operands)
513 (0 (ert-select-tests 't universe))
514 (t (ert-select-tests `(and ,@(rest operands))
515 (ert-select-tests (first operands) universe)))))
517 (assert (eql (length operands) 1))
518 (set-difference (ert-select-tests 't universe)
519 (ert-select-tests (first operands) universe)))
521 (case (length operands)
522 (0 (ert-select-tests 'nil universe))
523 (t (union (ert-select-tests (first operands) universe)
524 (ert-select-tests `(or ,@(rest operands)) universe)))))
526 (assert (eql (length operands) 1))
527 (remove-if-not (first operands) (ert-select-tests 't universe))))))))
529 (defun ert-insert-human-readable-selector (selector)
530 "Insert a human-readable presentation of SELECTOR into the current buffer."
531 ;; This is needed to avoid printing the (huge) contents of the
532 ;; `backtrace' slot of the result objects in the
533 ;; `most-recent-result' slots of test case objects in (eql ...) or
534 ;; (member ...) selectors.
535 (labels ((rec (selector)
536 ;; This code needs to match the etypecase in `ert-select-tests'.
539 :new :failed :passed :error
540 :expected :unexpected)
545 (if (ert-test-name selector)
546 (make-symbol (format "<%S>" (ert-test-name selector)))
547 (make-symbol "<unnamed test>")))
549 (destructuring-bind (operator &rest operands) selector
551 ((member eql and not or)
552 `(,operator ,@(mapcar #'rec operands)))
555 (insert (format "%S" (rec selector)))))
560 (put 'ert-test-failed 'error-conditions '(error ert-test-failed))
561 (put 'ert-test-failed 'error-message "Test failed")
564 "Terminate the current test and mark it passed. Does not return."
565 (throw 'ert-pass nil))
567 (defun ert-fail (data)
568 "Terminate the current test and mark it failed. Does not return.
569 DATA is displayed to the user and should state the reason of the failure."
570 (signal 'ert-test-failed (list data)))
572 ;; The data structures that represent the result of running a test.
573 (defstruct ert-test-result
576 (defstruct (ert-test-passed (:include ert-test-result)))
577 (defstruct (ert-test-result-with-condition (:include ert-test-result))
578 (condition (assert nil))
579 (backtrace (assert nil)))
580 (defstruct (ert-test-error (:include ert-test-result-with-condition)))
581 (defstruct (ert-test-quit (:include ert-test-result-with-condition)))
582 (defstruct (ert-test-failed (:include ert-test-result-with-condition)))
583 (defstruct (ert-test-aborted-with-non-local-exit (:include ert-test-result)))
586 (defun ert-record-backtrace ()
587 "Record the current backtrace (as a list) and return it."
588 ;; Since the backtrace is stored in the result object, result
589 ;; objects must only be printed with appropriate limits
590 ;; (`print-level' and `print-length') in place. For interactive
591 ;; use, the cost of ensuring this possibly outweighs the advantage
592 ;; of storing the backtrace for
593 ;; `ert-results-pop-to-backtrace-for-test-at-point' given that we
594 ;; already have `ert-results-rerun-test-debugging-errors-at-point'.
595 ;; For batch use, however, printing the backtrace may be useful.
597 ;; 6 is the number of frames our own debugger adds (when
598 ;; compiled; more when interpreted). FIXME: Need to describe a
599 ;; procedure for determining this constant.
601 for frame = (backtrace-frame i)
605 ;; A container for the state of the execution of a single test and
606 ;; environment data needed during its execution.
607 (defstruct ert-test-execution-info
609 (result (assert nil))
610 ;; A thunk that may be called when RESULT has been set to its final
611 ;; value and test execution should be terminated. Should not
613 (exit-continuation (assert nil))
614 ;; The binding of `debugger' outside of the execution of the test.
616 ;; The binding of `ert-debug-on-error' that is in effect for the
617 ;; execution of the current test. We store it to avoid being
618 ;; affected by any new bindings the test itself may establish. (I
619 ;; don't remember whether this feature is important.)
622 (defun ert-run-test-debugger (info debugger-args)
623 "The function that `debugger' is bound to during the execution of tests.
625 Records failures and errors and either terminates the test
626 silently or calls the interactive debugger, as appropriate."
627 (destructuring-bind (first-debugger-arg &rest more-debugger-args) debugger-args
628 (ecase first-debugger-arg
629 ((lambda debug t exit nil)
630 (apply (ert-test-execution-info-next-debugger info) debugger-args))
632 (let* ((condition (first more-debugger-args))
633 (type (case (car condition)
635 ((ert-test-failed) 'failed)
637 (backtrace (ert-record-backtrace)))
638 (setf (ert-test-execution-info-result info)
641 (make-ert-test-quit :condition condition
642 :backtrace backtrace))
644 (make-ert-test-failed :condition condition
645 :backtrace backtrace))
647 (make-ert-test-error :condition condition
648 :backtrace backtrace))))
649 ;; Work around Emacs' heuristic (in eval.c) for detecting
650 ;; errors in the debugger.
651 (incf num-nonmacro-input-events)
652 ;; FIXME: We should probably implement more fine-grained
653 ;; control a la non-t `debug-on-error' here.
655 ((ert-test-execution-info-ert-debug-on-error info)
656 (apply (ert-test-execution-info-next-debugger info) debugger-args))
658 (funcall (ert-test-execution-info-exit-continuation info)))))))
660 (defun ert-run-test-internal (ert-test-execution-info)
661 (lexical-let ((info ert-test-execution-info))
662 (setf (ert-test-execution-info-next-debugger info) debugger
663 (ert-test-execution-info-ert-debug-on-error info) ert-debug-on-error)
665 ;; For now, each test gets its own temp buffer and its own
666 ;; window excursion, just to be safe. If this turns out to be
667 ;; too expensive, we can remove it.
669 (save-window-excursion
670 (let ((debugger (lambda (&rest debugger-args)
671 (ert-run-test-debugger info debugger-args)))
674 ;; FIXME: Do we need to store the old binding of this
675 ;; and consider it in `ert-run-test-debugger'?
676 (debug-ignored-errors nil)
677 (ert-messages-mark (ert-end-of-messages))
678 (ert-warnings-mark (ert-end-of-warnings)))
679 (funcall (ert-test-body (ert-test-execution-info-test info))))))
681 (setf (ert-test-execution-info-result info) (make-ert-test-passed)))
684 (defun ert-make-marker-in-messages-buffer ()
685 (with-current-buffer (get-buffer-create "*Messages*")
686 (set-marker (make-marker) (point-max))))
688 (defun ert-force-message-log-buffer-truncation ()
689 (with-current-buffer (get-buffer-create "*Messages*")
690 ;; This is a reimplementation of this part of message_dolog() in xdisp.c:
691 ;; if (NATNUMP (Vmessage_log_max))
693 ;; scan_newline (Z, Z_BYTE, BEG, BEG_BYTE,
694 ;; -XFASTINT (Vmessage_log_max) - 1, 0);
695 ;; del_range_both (BEG, BEG_BYTE, PT, PT_BYTE, 0);
697 (when (and (integerp message-log-max) (>= message-log-max 0))
698 (let ((begin (point-min))
700 (goto-char (point-max))
701 (forward-line (- message-log-max))
703 (delete-region begin end)))))
705 (defun ert-run-test (test)
706 "Run TEST. Return the result and store it in TEST's `most-recent-result' slot."
707 (setf (ert-test-most-recent-result test) nil)
709 (lexical-let* ((begin-marker (ert-make-marker-in-messages-buffer))
710 (info (make-ert-test-execution-info
712 :result (make-ert-test-aborted-with-non-local-exit)
713 :exit-continuation (lambda ()
714 (return-from error nil)))))
716 (let ((message-log-max t))
717 (ert-run-test-internal info))
718 (let ((result (ert-test-execution-info-result info)))
719 (setf (ert-test-result-messages result)
720 (with-current-buffer (get-buffer-create "*Messages*")
721 (buffer-substring begin-marker (point-max))))
722 (ert-force-message-log-buffer-truncation)
723 (setf (ert-test-most-recent-result test) result)))))
724 (ert-test-most-recent-result test))
727 ;;; The `should' macros.
730 (defun ert-special-operator-p (thing)
731 "Return non-nil if THING is a symbol naming a special operator."
733 (let ((definition (indirect-function thing t)))
734 (and (subrp definition)
735 (eql (cdr (subr-arity definition)) 'unevalled)))))
736 (defun ert-expand-should (whole form env inner-expander)
737 "Helper function for the `should' macro and its variants.
739 Analyzes FORM and produces an expression that has the same
740 semantics under evaluation but records additional debugging
741 information. INNER-EXPANDER adds the actual checks specific to
742 the particular variant of `should'."
743 (let ((form (macroexpand form env)))
744 ;; It's sort of a wart that `inner-expander' can't influence the
745 ;; value the expansion returns.
748 (funcall inner-expander form `(list ',whole :form ',form :value ,form)))
749 ((ert-special-operator-p (car form))
750 (let ((value (gensym "value-")))
751 `(let ((,value (make-symbol "ert-form-evaluation-aborted")))
752 ,(funcall inner-expander
754 `(list ',whole :form ',form :value ,value))
757 (let ((fn-name (car form))
758 (arg-forms (cdr form)))
759 (assert (or (symbolp fn-name)
761 (eql (car fn-name) 'lambda)
762 (listp (cdr fn-name)))))
763 (let ((fn (gensym "fn-"))
764 (args (gensym "args-"))
765 (value (gensym "value-"))
766 (default-value (gensym "ert-form-evaluation-aborted-")))
767 `(let ((,fn (function ,fn-name))
768 (,args (list ,@arg-forms)))
769 (let ((,value ',default-value))
770 ,(funcall inner-expander
771 `(setq ,value (apply ,fn ,args))
772 `(nconc (list ',whole)
773 (list :form `(,,fn ,@,args))
774 (unless (eql ,value ',default-value)
775 (list :value ,value))
777 (and (symbolp ',fn-name)
782 (apply -explainer- ,args))))))
785 (defmacro* ert-should (form &environment env)
786 "Evaluate FORM. If it returns nil, abort the current test as failed.
788 Returns the value of FORM."
789 (ert-expand-should `(ert-should ,form) form env
790 (lambda (inner-form form-description-form)
792 (ert-fail ,form-description-form)))))
794 (defmacro* ert-should-not (form &environment env)
795 "Evaluate FORM. If it returns non-nil, abort the current test as failed.
798 (ert-expand-should `(ert-should-not ,form) form env
799 (lambda (inner-form form-description-form)
800 `(unless (not ,inner-form)
801 (ert-fail ,form-description-form)))))
803 (defun ert-should-error-handle-error (form-description-fn
804 condition type exclude-subtypes test)
805 "Helper function for `should-error'.
807 Determines whether CONDITION matches TYPE, EXCLUDE-SUBTYPES and
808 TEST, and aborts the current test as failed if it doesn't."
809 (let ((signalled-conditions (get (car condition) 'error-conditions))
810 (handled-conditions (etypecase type
812 (symbol (list type)))))
813 (assert signalled-conditions)
814 (unless (intersection signalled-conditions handled-conditions)
816 (funcall form-description-fn)
819 :fail-reason (concat "the error signalled did not"
820 " have the expected type")))))
821 (when exclude-subtypes
822 (unless (member (car condition) handled-conditions)
824 (funcall form-description-fn)
827 :fail-reason (concat "the error signalled was a subtype"
828 " of the expected type"))))))
829 (unless (funcall test condition)
831 (funcall form-description-fn)
834 :fail-reason "the error signalled did not pass the test"))))))
836 ;; FIXME: The expansion will evaluate the keyword args (if any) in
837 ;; nonstandard order.
838 (defmacro* ert-should-error (form &rest keys &key type exclude-subtypes test
840 "Evaluate FORM. Unless it signals an error, abort the current test as failed.
842 The error signalled additionally needs to match TYPE and satisfy
843 TEST. TYPE should be a condition name or a list of condition
844 names. If EXCLUDE-SUBTYPES is nil, the error matches TYPE if one
845 of its condition names is an element of TYPE. If
846 EXCLUDE-SUBTYPES is non-nil, the error matches TYPE if it is an
847 element of TYPE. TEST should be a predicate."
848 ;; Returns a gensym named `ert-form-evaluation-aborted-XXX', but
849 ;; that's a wart, so let's not document it.
850 (unless type (setq type ''error))
851 (unless test (setq test '(lambda (condition) t)))
853 `(ert-should-error ,form ,@keys)
855 (lambda (inner-form form-description-form)
856 (let ((errorp (gensym "errorp"))
857 (form-description-fn (gensym "form-description-fn-")))
859 (,form-description-fn (lambda () ,form-description-form)))
860 (condition-case -condition-
862 ;; We can't use ,type here because we want to evaluate it.
865 (ert-should-error-handle-error ,form-description-fn
867 ,type ,exclude-subtypes ,test)
868 ;; It would make sense to have the `should-error' form
869 ;; return the error in this case, but `ert-expand-should'
870 ;; doesn't allow that at the moment.
874 (funcall ,form-description-fn)
876 :fail-reason "did not signal an error")))))))))
879 ;;; Explanation of `should' failures.
881 (defun ert-proper-list-p (x)
882 "Return non-nil if X is a proper list, nil otherwise."
884 for firstp = t then nil
885 for fast = x then (cddr fast)
886 for slow = x then (cdr slow) do
887 (when (null fast) (return t))
888 (when (not (consp fast)) (return nil))
889 (when (null (cdr fast)) (return t))
890 (when (not (consp (cdr fast))) (return nil))
891 (when (and (not firstp) (eq fast slow)) (return nil))))
893 (defun ert-explain-not-equal (a b)
894 "Return a programmer-readable explanation of why A and B are not `equal'.
896 Returns nil if they are equal."
897 (if (not (equal (type-of a) (type-of b)))
898 `(different-types ,a ,b)
901 (let ((a-proper-p (ert-proper-list-p a))
902 (b-proper-p (ert-proper-list-p b)))
903 (if (not (eql (not a-proper-p) (not b-proper-p)))
904 `(one-list-proper-one-improper ,a ,b)
906 (if (not (equal (length a) (length b)))
907 ;; This would be even more helpful if it showed
908 ;; something like what `set-difference' would
910 `(proper-lists-of-different-length ,a ,b)
914 for xi = (ert-explain-not-equal ai bi)
915 do (when xi (return `(list-elt ,i ,xi)))))
916 (let ((car-x (ert-explain-not-equal (car a) (car b))))
919 (let ((cdr-x (ert-explain-not-equal (cdr a) (cdr b))))
923 (array (if (not (equal (length a) (length b)))
924 `(arrays-of-different-length ,a ,b)
928 for xi = (ert-explain-not-equal ai bi)
929 do (when xi (return `(array-elt ,i ,xi))))))
930 (atom (if (not (equal a b))
931 `(different-atoms ,a ,b)
933 (put 'equal 'ert-explainer 'ert-explain-not-equal)
938 ;; The data structure that contains the set of tests being executed
939 ;; during one particular test run, their results, the state of the
940 ;; execution, and some statistics.
942 ;; The data about results and expected results of tests may seem
943 ;; redundant here, since the test objects also carry such information.
944 ;; However, the information in the test objects may be more recent, it
945 ;; may correspond to a different test run. We need the information
946 ;; that corresponds to this run in order to be able to update the
947 ;; statistics correctly when a test is re-run interactively and has a
948 ;; different result than before.
950 (selector (assert nil))
951 ;; The tests, in order.
952 (tests (assert nil) :type vector)
953 ;; A map of test names (or the test objects themselves for unnamed
954 ;; tests) to indices into the `tests' vector.
955 (test-map (assert nil) :type hash-table)
956 ;; The results of the tests during this run, in order.
957 (test-results (assert nil) :type vector)
958 ;; The expected result types of the tests, in order.
959 (test-results-expected (assert nil) :type vector)
962 (passed-unexpected 0)
964 (failed-unexpected 0)
967 (start-time (assert nil))
972 ;; An entry in the results buffer ewoc. There is one entry per test.
973 (defstruct ert-ewoc-entry
976 ;; If the result of this test was expected, its ewoc entry is hidden
978 (hidden-p (assert nil))
979 ;; An ewoc entry may be collapsed to hide details such as the error
982 ;; I'm not sure the ability to expand and collapse entries is still
985 ;; By default, the ewoc entry presents the error condition with
986 ;; certain limits on how much to print (`print-level',
987 ;; `print-length'). The user can interactively switch to a set of
989 (extended-printer-limits-p nil))
991 ;; Variables local to the results buffer.
994 (defvar ert-results-ewoc)
996 (defvar ert-results-stats)
997 ;; A string with one character per test. Each character represents
998 ;; the result of the corresponding test. The string is displayed near
999 ;; the top of the buffer and serves as a progress bar.
1000 (defvar ert-results-progress-bar-string)
1001 ;; The position where the progress bar button begins.
1002 (defvar ert-results-progress-bar-button-begin)
1003 ;; The test result listener that updates the buffer when tests are run.
1004 (defvar ert-results-listener)
1006 ;; The same as `ert-results-stats', but dynamically bound. Used for
1007 ;; the mode line progress indicator.
1008 (defvar ert-current-run-stats nil)
1010 (defun ert-format-time-iso8601 (time)
1011 "Format TIME in the particular variant of ISO 8601 used for timestamps in ERT."
1012 (format-time-string "%Y-%m-%d %T%z" time))
1014 (defun ert-insert-test-name-button (test-name)
1015 (insert-text-button (format "%S" test-name)
1016 :type 'ert-test-name-button
1017 'ert-test-name test-name))
1019 (defun ert-results-update-ewoc-hf (ewoc stats)
1020 "Update the header and footer of EWOC to show certain information from STATS.
1022 Also sets `ert-results-progress-bar-button-begin'."
1023 (let ((run-count (+ (ert-stats-passed-expected stats)
1024 (ert-stats-passed-unexpected stats)
1025 (ert-stats-failed-expected stats)
1026 (ert-stats-failed-unexpected stats)
1027 (ert-stats-error-expected stats)
1028 (ert-stats-error-unexpected stats)))
1029 (results-buffer (current-buffer)))
1034 (insert "Selector: ")
1035 (ert-insert-human-readable-selector (ert-stats-selector stats))
1038 (format (concat "Passed: %s (%s unexpected)\n"
1039 "Failed: %s (%s unexpected)\n"
1040 "Error: %s (%s unexpected)\n"
1042 (+ (ert-stats-passed-expected stats)
1043 (ert-stats-passed-unexpected stats))
1044 (ert-stats-passed-unexpected stats)
1045 (+ (ert-stats-failed-expected stats)
1046 (ert-stats-failed-unexpected stats))
1047 (ert-stats-failed-unexpected stats)
1048 (+ (ert-stats-error-expected stats)
1049 (ert-stats-error-unexpected stats))
1050 (ert-stats-error-unexpected stats)
1052 (ert-stats-total stats)))
1054 (format "Started at: %s\n"
1055 (ert-format-time-iso8601 (ert-stats-start-time stats))))
1056 ;; FIXME: This is ugly. Need to properly define invariants of
1057 ;; the `stats' data structure.
1058 (let ((state (cond ((ert-stats-aborted-p stats)
1060 ((ert-stats-current-test stats)
1062 ((ert-stats-end-time stats)
1070 (cond ((ert-stats-current-test stats)
1071 (insert "Aborted during test: ")
1072 (ert-insert-test-name-button
1073 (ert-test-name (ert-stats-current-test stats))))
1075 (insert "Aborted."))))
1077 (assert (ert-stats-current-test stats))
1078 (insert "Running test: ")
1079 (ert-insert-test-name-button (ert-test-name
1080 (ert-stats-current-test stats))))
1082 (assert (not (ert-stats-current-test stats)))
1083 (insert "Finished.")))
1085 (if (ert-stats-end-time stats)
1088 (if (ert-stats-aborted-p stats)
1091 (ert-format-time-iso8601 (ert-stats-end-time stats))))
1094 (let ((progress-bar-string (with-current-buffer results-buffer
1095 ert-results-progress-bar-string)))
1096 (let ((progress-bar-button-begin
1097 (insert-text-button (substring progress-bar-string 0 run-count)
1098 :type 'ert-results-progress-bar-button)))
1099 (with-current-buffer results-buffer
1100 (set (make-local-variable 'ert-results-progress-bar-button-begin)
1101 progress-bar-button-begin)))
1102 (insert (substring progress-bar-string run-count)))
1107 ;; We actually want an empty footer, but that would trigger a bug
1108 ;; in ewoc, sometimes clearing the entire buffer.
1111 (defun ert-results-update-stats-display (ewoc stats)
1112 "Update EWOC and the mode line to show data from STATS."
1113 (ert-results-update-ewoc-hf ewoc stats)
1114 (force-mode-line-update)
1117 (defun ert-char-for-test-result (result expectedp)
1118 "Return a character that represents the test result RESULT."
1121 (ert-test-passed ?.)
1122 (ert-test-failed ?f)
1125 (ert-test-aborted-with-non-local-exit ?a))))
1130 (defun ert-string-for-test-result (result expectedp)
1131 "Return a string that represents the test result RESULT."
1133 (ert-test-passed "passed")
1134 (ert-test-failed "failed")
1135 (ert-test-error "error")
1137 (ert-test-aborted-with-non-local-exit "aborted")))
1139 (defun ert-tests-running-mode-line-indicator ()
1140 (let* ((stats ert-current-run-stats)
1141 (tests-total (ert-stats-total stats))
1142 (tests-completed (+ (ert-stats-passed-expected stats)
1143 (ert-stats-passed-unexpected stats)
1144 (ert-stats-failed-expected stats)
1145 (ert-stats-failed-unexpected stats)
1146 (ert-stats-error-expected stats)
1147 (ert-stats-error-unexpected stats))))
1148 (if (>= tests-completed tests-total)
1149 (format " ERT(%s/%s,finished)" tests-completed tests-total)
1150 (format " ERT(%s/%s):%s"
1151 (1+ tests-completed)
1153 (if (null (ert-stats-current-test stats))
1156 (ert-test-name (ert-stats-current-test stats))))))))
1158 (defun ert-pp-with-indentation-and-newline (object)
1159 "Pretty-print OBJECT, indenting it to the current column of point.
1160 Ensures a final newline is inserted."
1161 (let ((begin (point)))
1162 (pp object (current-buffer))
1163 (unless (bolp) (insert "\n"))
1168 (defun ert-print-test-for-ewoc (entry)
1169 "The ewoc print function for ewoc test entries."
1170 (let* ((test (ert-ewoc-entry-test entry))
1171 (result (ert-ewoc-entry-result entry))
1172 (hiddenp (ert-ewoc-entry-hidden-p entry))
1173 (expandedp (ert-ewoc-entry-expanded-p entry))
1174 (extended-printer-limits-p (ert-ewoc-entry-extended-printer-limits-p
1178 (insert-text-button (format "%c"
1179 (ert-char-for-test-result
1181 (ert-test-result-expected-p test
1183 :type 'ert-results-expand-collapse-button)
1185 (ert-insert-test-name-button (ert-test-name test))
1187 (when (and expandedp (not (eql result 'nil)))
1190 (insert " passed\n")
1192 (ert-test-result-with-condition
1194 (let ((print-escape-newlines t)
1195 (print-level (if extended-printer-limits-p 10 5))
1196 (print-length (if extended-printer-limits-p 100 10)))
1197 (let ((begin (point)))
1198 (ert-pp-with-indentation-and-newline
1199 (ert-test-result-with-condition-condition result))
1201 (narrow-to-region begin (point))
1202 ;; Inhibit optimization in `debugger-make-xrefs'
1203 ;; that sometimes inserts unrelated backtrace
1204 ;; info into our buffer.
1205 (let ((debugger-previous-backtrace nil))
1206 (debugger-make-xrefs))))))
1207 (ert-test-aborted-with-non-local-exit
1208 (insert " aborted\n")))
1212 (defun ert-setup-results-buffer (stats listener buffer-name)
1213 "Set up a test results buffer."
1214 (unless buffer-name (setq buffer-name "*ert*"))
1215 (let ((buffer (let ((default-major-mode 'fundamental-mode))
1216 (get-buffer-create buffer-name))))
1217 (with-current-buffer buffer
1218 (setq buffer-read-only t)
1219 (let ((inhibit-read-only t))
1220 (buffer-disable-undo)
1223 (set (make-local-variable 'ert-results-ewoc)
1224 (ewoc-create 'ert-print-test-for-ewoc nil nil t))
1225 (set (make-local-variable 'ert-results-stats) stats)
1226 (set (make-local-variable 'ert-results-progress-bar-string)
1227 (make-string (ert-stats-total stats)
1228 (ert-char-for-test-result nil t)))
1229 (set (make-local-variable 'ert-results-listener) listener)
1230 (ert-results-update-ewoc-hf ert-results-ewoc ert-results-stats)
1231 (goto-char (1- (point-max)))
1234 (defun ert-run-or-rerun-test (stats test listener)
1235 "Run the single test TEST and record the result using STATS and LISTENER."
1236 (let ((ert-current-run-stats stats)
1237 (pos (ert-stats-test-index stats test))
1238 (results (ert-stats-test-results stats))
1239 (expected (ert-stats-test-results-expected stats)))
1240 ;; Adjust stats to remove previous result.
1241 (if (aref expected pos)
1242 (etypecase (aref results pos)
1243 (ert-test-passed (decf (ert-stats-passed-expected stats)))
1244 (ert-test-failed (decf (ert-stats-failed-expected stats)))
1245 (ert-test-error (decf (ert-stats-error-expected stats)))
1247 (ert-test-aborted-with-non-local-exit))
1248 (etypecase (aref results pos)
1249 (ert-test-passed (decf (ert-stats-passed-unexpected stats)))
1250 (ert-test-failed (decf (ert-stats-failed-unexpected stats)))
1251 (ert-test-error (decf (ert-stats-error-unexpected stats)))
1253 (ert-test-aborted-with-non-local-exit)))
1254 (setf (aref results pos) nil)
1255 ;; Call listener after setting/before resetting
1256 ;; (ert-stats-current-test stats); the listener might refresh the
1257 ;; mode line display, and if the value is not set yet/any more
1258 ;; during this refresh, the mode line will flicker unnecessarily.
1259 (setf (ert-stats-current-test stats) test)
1260 (funcall listener 'test-started stats test)
1261 (setf (ert-test-most-recent-result test) nil)
1264 (let* ((result (ert-test-most-recent-result test))
1265 (expectedp (typep result (ert-test-expected-result-type test))))
1266 ;; Adjust stats to add new result.
1269 (ert-test-passed (incf (ert-stats-passed-expected stats)))
1270 (ert-test-failed (incf (ert-stats-failed-expected stats)))
1271 (ert-test-error (incf (ert-stats-error-expected stats)))
1273 (ert-test-aborted-with-non-local-exit))
1275 (ert-test-passed (incf (ert-stats-passed-unexpected stats)))
1276 (ert-test-failed (incf (ert-stats-failed-unexpected stats)))
1277 (ert-test-error (incf (ert-stats-error-unexpected stats)))
1279 (ert-test-aborted-with-non-local-exit)))
1280 (setf (aref results pos) result
1281 (aref expected pos) expectedp)
1282 (funcall listener 'test-ended stats test result))
1283 (setf (ert-stats-current-test stats) nil))))
1285 (defun ert-run-tests (selector listener)
1286 "Run the tests specified by SELECTOR, sending progress updates to LISTENER."
1287 (let* ((tests (coerce (ert-select-tests selector t) 'vector))
1288 (map (let ((map (make-hash-table :size (length tests))))
1290 for test across tests
1291 for key = (or (ert-test-name test) test) do
1292 (assert (not (gethash key map)))
1293 (setf (gethash key map) i))
1295 (stats (make-ert-stats :selector selector
1298 :test-results (make-vector (length tests) nil)
1299 :test-results-expected (make-vector
1301 :total (length tests)
1302 :start-time (current-time))))
1303 (funcall listener 'run-started stats)
1305 (let ((ert-current-run-stats stats))
1306 (force-mode-line-update)
1309 (loop for test across tests do
1310 (ert-run-or-rerun-test stats test listener))
1311 (setq abortedp nil))
1312 (setf (ert-stats-aborted-p stats) abortedp)
1313 (setf (ert-stats-end-time stats) (current-time))
1314 (funcall listener 'run-ended stats abortedp)))
1317 (defun ert-stats-test-index (stats test)
1318 "Return the index of TEST in the run represented by STATS."
1319 (gethash (or (ert-test-name test) test) (ert-stats-test-map stats)))
1321 (defvar ert-selector-history nil
1322 "List of recent test selectors read from terminal.")
1324 ;; Fix-me: return (regep (list of matches))?
1325 ;; Fix-me: Add prompt parameter?
1326 (defun ert-read-test-selector ()
1327 "Read a regexp for test selection from minibuffer.
1328 The user can use TAB to see which tests match."
1330 (mapcar (lambda (rec) (format "%s" (elt rec 1)))
1331 (ert-select-tests "" t))
1332 ;;'("ert-group1-1" "ert-group1-2" "ert-other")
1338 (let* ((ret (save-match-data
1339 (mapcar (lambda (alt)
1340 (when (string-match regexp alt)
1343 (setq ret (delq nil ret))
1345 (setq all-tests (append all-tests
1347 ":failed" ":passed" ":error"
1350 (let ((mini-map (copy-keymap minibuffer-local-map)))
1351 (define-key mini-map [?\t]
1352 (lambda () (interactive)
1353 (with-output-to-temp-buffer "*Completions*"
1354 (display-completion-list
1356 (setq regexp (minibuffer-contents))
1357 (set-text-properties 0 (length regexp) nil regexp)
1358 (funcall get-completions))))))
1360 (let* ((sym-here (thing-at-point 'symbol))
1361 (test-here (when (and sym-here
1362 (memq sym-here all-tests))
1364 (default (if sym-here
1365 (substring-no-properties sym-here)
1366 (if ert-selector-history
1367 (first ert-selector-history)
1369 (read-from-minibuffer
1371 "Run tests, use TAB to see matches: "
1372 (format "Run tests, use TAB to see matches (default %s): "
1374 nil ;; initial-contents
1377 'ert-selector-history
1380 (when (string= "t" ret)
1384 ;; Should OUTPUT-BUFFER-NAME and MESSAGE-FN really be arguments here?
1385 ;; They are needed only for our automated self-tests at the moment.
1386 ;; Or should there be some other mechanism?
1388 (defun ert-run-tests-interactively (selector
1389 &optional output-buffer-name message-fn)
1390 "Run the tests specified by SELECTOR and display the results in a buffer."
1392 ;;; (list (let ((default (if ert-selector-history
1393 ;;; (first ert-selector-history)
1395 ;;; (read-from-minibuffer (if (null default)
1397 ;;; (format "Run tests (default %s): " default))
1398 ;;; ;;nil nil t 'ert-selector-history
1400 ;;; ;; fix-me: seems like I am misunderstanding Christians intent here.
1401 ;;; nil nil nil 'ert-selector-history
1404 (list (ert-read-test-selector)
1406 (unless message-fn (setq message-fn 'message))
1407 (lexical-let ((output-buffer-name output-buffer-name)
1410 (message-fn message-fn))
1412 (lambda (event-type &rest event-args)
1415 (destructuring-bind (stats) event-args
1416 (setq buffer (ert-setup-results-buffer stats
1418 output-buffer-name))
1419 (pop-to-buffer buffer)))
1421 (destructuring-bind (stats abortedp) event-args
1423 "%sRan %s tests, %s results were as expected%s"
1427 (ert-stats-total stats)
1428 (+ (ert-stats-passed-expected stats)
1429 (ert-stats-failed-expected stats)
1430 (ert-stats-error-expected stats))
1432 (+ (ert-stats-passed-unexpected stats)
1433 (ert-stats-failed-unexpected stats)
1434 (ert-stats-error-unexpected stats))))
1435 (if (zerop unexpected)
1437 (format ", %s unexpected" unexpected))))
1438 (ert-results-update-stats-display (with-current-buffer buffer
1442 (destructuring-bind (stats test) event-args
1443 (with-current-buffer buffer
1444 (let* ((ewoc ert-results-ewoc)
1445 (pos (ert-stats-test-index stats test))
1446 (node (ewoc-nth ewoc pos)))
1448 ;; FIXME: How expensive is this assertion?
1449 (assert (or (zerop pos) (ewoc-nth ewoc (1- pos)))
1451 (setq node (ewoc-enter-last
1453 (make-ert-ewoc-entry :test test
1455 (setf (ert-ewoc-entry-test (ewoc-data node)) test)
1456 (setf (ert-ewoc-entry-result (ewoc-data node)) nil)
1457 (aset ert-results-progress-bar-string pos
1458 (ert-char-for-test-result nil t))
1459 (ert-results-update-stats-display ewoc stats)
1460 (ewoc-invalidate ewoc node)))))
1462 (destructuring-bind (stats test result) event-args
1463 (with-current-buffer buffer
1464 (let* ((ewoc ert-results-ewoc)
1465 (pos (ert-stats-test-index stats test))
1466 (node (ewoc-nth ewoc pos)))
1467 (setf (ert-ewoc-entry-result (ewoc-data node)) result)
1468 (when (ert-ewoc-entry-hidden-p (ewoc-data node))
1469 (setf (ert-ewoc-entry-hidden-p (ewoc-data node))
1470 (ert-test-result-expected-p test result)))
1471 (aset ert-results-progress-bar-string pos
1472 (ert-char-for-test-result result
1473 (ert-test-result-expected-p
1475 (ert-results-update-stats-display ewoc stats)
1476 (ewoc-invalidate ewoc node))))))))
1481 (defvar ert-batch-backtrace-right-margin 70
1482 "*The maximum line length for printing backtraces in `ert-run-tests-batch'.")
1484 (defun ert-run-tests-batch (selector)
1485 "Run the tests specified by SELECTOR, printing results to the terminal.
1487 Returns the stats object."
1490 (lambda (event-type &rest event-args)
1493 (destructuring-bind (stats) event-args
1494 (message "Running %s tests (%s)"
1495 (length (ert-stats-tests stats))
1496 (ert-format-time-iso8601 (ert-stats-start-time stats)))))
1498 (destructuring-bind (stats abortedp) event-args
1499 (let ((unexpected (+ (ert-stats-passed-unexpected stats)
1500 (ert-stats-failed-unexpected stats)
1501 (ert-stats-error-unexpected stats))))
1502 (message "\n%sRan %s tests, %s results were as expected%s (%s)\n"
1506 (ert-stats-total stats)
1507 (+ (ert-stats-passed-expected stats)
1508 (ert-stats-failed-expected stats)
1509 (ert-stats-error-expected stats))
1510 (if (zerop unexpected)
1512 (format ", %s unexpected" unexpected))
1513 (ert-format-time-iso8601 (ert-stats-end-time stats)))
1514 (unless (zerop unexpected)
1515 (message "%s unexpected results:" unexpected)
1516 (loop for test across (ert-stats-tests stats)
1517 for result = (ert-test-most-recent-result test) do
1518 (when (not (ert-test-result-expected-p test result))
1520 (ert-string-for-test-result result nil)
1521 (ert-test-name test))))
1522 (message "%s" "")))))
1526 (destructuring-bind (stats test result) event-args
1529 (ert-test-result-with-condition
1530 (message "Test %S backtrace:" (ert-test-name test))
1532 (ert-print-backtrace (ert-test-result-with-condition-backtrace result))
1533 (goto-char (point-min))
1535 (let ((start (point))
1536 (end (progn (end-of-line) (point))))
1538 (+ start ert-batch-backtrace-right-margin)))
1539 (message "%s" (buffer-substring-no-properties
1544 (let ((print-escape-newlines t)
1547 (let ((begin (point)))
1548 (ert-pp-with-indentation-and-newline
1549 (ert-test-result-with-condition-condition result))))
1550 (goto-char (1- (point-max)))
1551 (assert (looking-at "\n"))
1553 (message "Test %S condition:" (ert-test-name test))
1554 (message "%s" (buffer-string))))
1555 (ert-test-aborted-with-non-local-exit))
1556 (let* ((max (prin1-to-string (length (ert-stats-tests stats))))
1557 (format-string (concat "%9s %"
1558 (prin1-to-string (length max))
1560 (message format-string
1561 (ert-string-for-test-result result
1562 (ert-test-result-expected-p
1564 (1+ (ert-stats-test-index stats test))
1565 (ert-test-name test)))))))))
1568 ;;; Commands and button actions for the results buffer.
1570 (define-derived-mode ert-results-mode fundamental-mode "ERT-Results"
1571 "Major mode for viewing results of ERT test runs.")
1573 (loop for (key binding) in
1574 '(("j" ert-results-jump-between-summary-and-result)
1575 ("." ert-results-find-test-at-point-other-window)
1576 ("r" ert-results-rerun-test-at-point)
1577 ("d" ert-results-rerun-test-at-point-debugging-errors)
1578 ("b" ert-results-pop-to-backtrace-for-test-at-point)
1579 ("m" ert-results-pop-to-messages-for-test-at-point)
1580 ("p" ert-results-toggle-printer-limits-for-test-at-point)
1581 ("D" ert-delete-test)
1582 ([?\t] forward-button)
1583 ([backtab] backward-button)
1586 (define-key ert-results-mode-map key binding))
1588 (define-button-type 'ert-results-progress-bar-button
1589 'action #'ert-results-progress-bar-button-action
1590 'help-echo "mouse-2, RET: Reveal test result")
1592 (define-button-type 'ert-test-name-button
1593 'action #'ert-test-name-button-action
1594 'help-echo "mouse-2, RET: Find test definition")
1596 (define-button-type 'ert-results-expand-collapse-button
1597 'action #'ert-results-expand-collapse-button-action
1598 'help-echo "mouse-2, RET: Expand/collapse test result")
1600 (defun ert-results-test-node-or-null-at-point ()
1601 "If point is on a valid ewoc node, return it; return nil otherwise.
1603 To be used in the ERT results buffer."
1604 (let* ((ewoc ert-results-ewoc)
1605 (node (ewoc-locate ewoc)))
1606 ;; `ewoc-locate' will return an arbitrary node when point is on
1607 ;; header or footer, or when all nodes are invisible. So we need
1608 ;; to validate its return value here.
1609 (if (and (>= (point) (ewoc-location node))
1610 (not (ert-ewoc-entry-hidden-p (ewoc-data node))))
1614 (defun ert-results-test-node-at-point ()
1615 "If point is on a valid ewoc node, return it; signal an error otherwise.
1617 To be used in the ERT results buffer."
1618 (or (ert-results-test-node-or-null-at-point)
1619 (error "No test at point")))
1621 (defun ert-results-expand-collapse-button-action (button)
1622 "Expand or collapse the test node BUTTON belongs to."
1623 (let* ((ewoc ert-results-ewoc)
1624 (node (save-excursion
1625 (goto-char (ert-button-action-position))
1626 (ert-results-test-node-at-point)))
1627 (entry (ewoc-data node)))
1628 (setf (ert-ewoc-entry-expanded-p entry)
1629 (not (ert-ewoc-entry-expanded-p entry)))
1630 (ewoc-invalidate ewoc node)))
1632 (defun ert-results-find-test-at-point-other-window ()
1633 "Find the definition of the test at point in another window.
1635 To be used in the ERT results buffer."
1637 (let* ((node (ert-results-test-node-at-point))
1638 (entry (ewoc-data node))
1639 (test (ert-ewoc-entry-test entry))
1640 (name (ert-test-name test)))
1641 (ert-find-test-other-window name)))
1643 (defun ert-test-name-button-action (button)
1644 "Find the definition of the test BUTTON belongs to, in another window."
1645 (let ((name (button-get button 'ert-test-name)))
1646 (ert-find-test-other-window name)))
1648 (defun ert-ewoc-position (ewoc node)
1649 "Return the position of NODE in EWOC, or nil if NODE is not in EWOC."
1651 for node-here = (ewoc-nth ewoc 0) then (ewoc-next ewoc node-here)
1652 do (when (eql node node-here)
1654 finally (return nil)))
1656 (defun ert-results-jump-between-summary-and-result ()
1657 "Jump back and forth between the test run summary and individual test results.
1659 From an ewoc node, jumps to the character that represents the
1660 same test in the progress bar, and vice versa.
1662 To be used in the ERT results buffer."
1663 ;; Maybe this command isn't actually needed much, but if it is, it
1664 ;; seems like an indication that the UI design is not optimal. If
1665 ;; jumping back and forth between a summary at the top of the buffer
1666 ;; and the error log in the remainder of the buffer is useful, then
1667 ;; the summary apparently needs to be easily accessible from the
1668 ;; error log, and perhaps it would be better to have it in a
1669 ;; separate buffer to keep it visible.
1671 (let ((ewoc ert-results-ewoc)
1672 (progress-bar-begin ert-results-progress-bar-button-begin))
1673 (cond ((ert-results-test-node-or-null-at-point)
1674 (let* ((node (ert-results-test-node-at-point))
1675 (pos (ert-ewoc-position ewoc node)))
1676 (goto-char (+ progress-bar-begin pos))))
1677 ((and (<= progress-bar-begin (point))
1678 (< (point) (button-end (button-at progress-bar-begin))))
1679 (let* ((node (ewoc-nth ewoc (- (point) progress-bar-begin)))
1680 (entry (ewoc-data node)))
1681 (when (ert-ewoc-entry-hidden-p entry)
1682 (setf (ert-ewoc-entry-hidden-p entry) nil)
1683 (ewoc-invalidate ewoc node))
1684 (ewoc-goto-node ewoc node)))
1686 (goto-char progress-bar-begin)))))
1688 (defun ert-button-action-position ()
1689 "The buffer position where the last button action was triggered."
1690 (cond ((integerp last-command-event)
1692 ((eventp last-command-event)
1693 (posn-point (event-start last-command-event)))
1696 (defun ert-results-progress-bar-button-action (button)
1697 "Find the ewoc node that represents the same test as the character clicked on."
1698 (goto-char (ert-button-action-position))
1699 (ert-results-jump-between-summary-and-result))
1701 (defun ert-results-rerun-test-at-point ()
1702 "Re-run the test at point.
1704 To be used in the ERT results buffer."
1706 (let* ((ewoc ert-results-ewoc)
1707 (node (ert-results-test-node-at-point))
1708 (entry (ewoc-data node))
1709 (old-test (ert-ewoc-entry-test entry))
1710 (test-name (ert-test-name old-test))
1711 ;; FIXME: Write a test for this lookup.
1713 (if (ert-test-boundp test-name)
1714 (ert-get-test test-name)
1715 (error "No such test: %S" test-name))
1717 (stats ert-results-stats)
1718 (pos (gethash test (ert-stats-test-map stats)))
1719 (progress-message (format "Running test %S" (ert-test-name test))))
1720 ;; Need to save and restore point manually here: When point is on
1721 ;; the first visible ewoc entry while the header is updated, point
1722 ;; moves to the top of the buffer. This is undesirable, and a
1723 ;; simple `save-excursion' doesn't prevent it.
1724 (let ((point (point)))
1728 (message "%s..." progress-message)
1729 (ert-run-or-rerun-test stats test
1730 ert-results-listener))
1731 (ert-results-update-stats-display ewoc stats)
1734 (let ((result (ert-test-most-recent-result test)))
1735 (ert-string-for-test-result
1736 result (ert-test-result-expected-p test result)))))
1737 (goto-char point)))))
1739 (defun ert-results-rerun-test-at-point-debugging-errors ()
1740 "Re-run the test at point with `ert-debug-on-error' bound to t.
1742 To be used in the ERT results buffer."
1744 (let ((ert-debug-on-error t))
1745 (ert-results-rerun-test-at-point)))
1747 (defun ert-print-backtrace (backtrace)
1748 "Format the backtrace BACKTRACE to the current buffer."
1749 ;; This is essentially a reimplementation of Fbacktrace
1750 ;; (src/eval.c), but for a saved backtrace, not the current one.
1751 (let ((print-escape-newlines t)
1754 (dolist (frame backtrace)
1755 (ecase (first frame)
1757 ;; Special operator.
1758 (destructuring-bind (special-operator &rest arg-forms)
1761 (format " %S\n" (list* special-operator arg-forms)))))
1764 (destructuring-bind (fn &rest args) (cdr frame)
1765 (insert (format " %S(" fn))
1766 (loop for firstp = t then nil
1770 (insert (format "%S" arg)))
1771 (insert ")\n")))))))
1773 (defun ert-results-pop-to-backtrace-for-test-at-point ()
1774 "Display the backtrace for the test at point.
1776 To be used in the ERT results buffer."
1778 (let* ((node (ert-results-test-node-at-point))
1779 (entry (ewoc-data node))
1780 (test (ert-ewoc-entry-test entry))
1781 (result (ert-ewoc-entry-result entry)))
1783 (ert-test-passed (error "Test passed, no backtrace available"))
1784 (ert-test-result-with-condition
1785 (let ((backtrace (ert-test-result-with-condition-backtrace result))
1787 (let ((default-major-mode 'fundamental-mode))
1788 (get-buffer-create "*ERT Backtrace*"))))
1789 (pop-to-buffer buffer)
1790 (setq buffer-read-only t)
1791 (let ((inhibit-read-only t))
1793 ;; Use unibyte because `debugger-setup-buffer' also does so.
1794 (set-buffer-multibyte nil)
1795 (setq truncate-lines t)
1796 (ert-print-backtrace backtrace)
1797 (debugger-make-xrefs)
1798 (goto-char (point-min))))))))
1800 (defun ert-results-pop-to-messages-for-test-at-point ()
1801 "Display the part of the *Messages* buffer generated during the test at point.
1803 To be used in the ERT results buffer."
1805 (let* ((node (ert-results-test-node-at-point))
1806 (entry (ewoc-data node))
1807 (test (ert-ewoc-entry-test entry))
1808 (result (ert-ewoc-entry-result entry)))
1810 (let ((default-major-mode 'fundamental-mode))
1811 (get-buffer-create "*ERT Messages*"))))
1812 (pop-to-buffer buffer)
1813 (setq buffer-read-only t)
1814 (let ((inhibit-read-only t))
1816 (insert (ert-test-result-messages result))
1817 (goto-char (point-min))
1818 (insert "Messages for test `")
1819 (ert-insert-test-name-button (ert-test-name test))
1822 (defun ert-results-toggle-printer-limits-for-test-at-point ()
1823 "Toggle how much of the condition to print for the test at point.
1825 To be used in the ERT results buffer."
1827 (let* ((ewoc ert-results-ewoc)
1828 (node (ert-results-test-node-at-point))
1829 (entry (ewoc-data node)))
1830 (setf (ert-ewoc-entry-extended-printer-limits-p entry)
1831 (not (ert-ewoc-entry-extended-printer-limits-p entry)))
1832 (ewoc-invalidate ewoc node)))
1834 (defun ert-activate-font-lock-keywords ()
1835 (font-lock-add-keywords
1837 '(("(\\(\\<ert-deftest\\)\\>\\s *\\(\\sw+\\)?"
1838 (1 font-lock-keyword-face nil t)
1839 (2 font-lock-function-name-face nil t)))))
1841 (defun* ert-remove-from-list (list-var element &key key test)
1842 "Remove ELEMENT from the value of LIST-VAR if present.
1844 This is an inverse of `add-to-list'."
1845 (unless key (setq key #'identity))
1846 (unless test (setq test #'equal))
1847 (setf (symbol-value list-var)
1849 (symbol-value list-var)
1854 ;;; Actions on load/unload.
1856 (add-to-list 'find-function-regexp-alist '(ert-deftest . ert-find-test-regexp))
1857 (add-to-list 'minor-mode-alist '(ert-current-run-stats
1859 (ert-tests-running-mode-line-indicator))))
1860 (add-to-list 'emacs-lisp-mode-hook 'ert-activate-font-lock-keywords)
1862 (defun ert-unload-function ()
1863 (ert-remove-from-list 'find-function-regexp-alist 'ert-deftest :key #'car)
1864 (ert-remove-from-list 'minor-mode-alist 'ert-current-run-stats :key #'car)
1865 (ert-remove-from-list 'emacs-lisp-mode-hook 'ert-activate-font-lock-keywords)
1868 (defvar ert-unload-hook '())
1869 (add-hook 'ert-unload-hook 'ert-unload-function)
1874 (ert-delete-all-tests)
1876 ;; Test that test bodies are actually run.
1877 (defvar ert-test-body-was-run)
1878 (ert-deftest ert-test-body-runs ()
1879 (setq ert-test-body-was-run t))
1882 ;; Test that nested test bodies run.
1883 (ert-deftest ert-nested-test-body-runs ()
1884 (lexical-let ((was-run nil))
1885 (let ((test (make-ert-test :body (lambda ()
1886 (setq was-run t)))))
1887 (assert (not was-run))
1892 ;; Test that pass/fail works.
1893 (ert-deftest ert-test-pass ()
1894 (let ((test (make-ert-test :body (lambda ()))))
1895 (let ((result (ert-run-test test)))
1896 (assert (typep result 'ert-test-passed)))))
1898 (ert-deftest ert-test-fail ()
1899 (let ((test (make-ert-test :body (lambda () (ert-fail "failure message")))))
1900 (let ((result (let ((ert-debug-on-error nil))
1901 (ert-run-test test))))
1902 (assert (typep result 'ert-test-failed) t)
1903 (assert (equal (ert-test-result-with-condition-condition result)
1904 '(ert-test-failed "failure message"))
1907 (ert-deftest ert-test-fail-debug-with-condition-case ()
1908 (let ((test (make-ert-test :body (lambda () (ert-fail "failure message")))))
1909 (condition-case condition
1911 (let ((ert-debug-on-error t))
1912 (ert-run-test test))
1915 (assert (equal condition '(ert-test-failed "failure message")) t)))))
1917 (ert-deftest ert-test-fail-debug-with-debugger-1 ()
1918 (let ((test (make-ert-test :body (lambda () (ert-fail "failure message")))))
1919 (let ((debugger (lambda (&rest debugger-args)
1921 (let ((ert-debug-on-error nil))
1922 (ert-run-test test)))))
1924 (ert-deftest ert-test-fail-debug-with-debugger-2 ()
1925 (let ((test (make-ert-test :body (lambda () (ert-fail "failure message")))))
1927 (let ((debugger (lambda (&rest debugger-args)
1928 (return-from nil nil))))
1929 (let ((ert-debug-on-error t))
1930 (ert-run-test test))
1933 (ert-deftest ert-test-fail-debug-nested-with-debugger ()
1934 (let ((test (make-ert-test :body (lambda ()
1935 (let ((ert-debug-on-error t))
1936 (ert-fail "failure message"))))))
1937 (let ((debugger (lambda (&rest debugger-args)
1938 (assert nil nil "Assertion a"))))
1939 (let ((ert-debug-on-error nil))
1940 (ert-run-test test))))
1941 (let ((test (make-ert-test :body (lambda ()
1942 (let ((ert-debug-on-error nil))
1943 (ert-fail "failure message"))))))
1945 (let ((debugger (lambda (&rest debugger-args)
1946 (return-from nil nil))))
1947 (let ((ert-debug-on-error t))
1948 (ert-run-test test))
1949 (assert nil nil "Assertion b")))))
1951 (ert-deftest ert-test-error ()
1952 (let ((test (make-ert-test :body (lambda () (error "error message")))))
1953 (let ((result (let ((ert-debug-on-error nil))
1954 (ert-run-test test))))
1955 (assert (typep result 'ert-test-error) t)
1956 (assert (equal (ert-test-result-with-condition-condition result)
1957 '(error "error message"))
1960 (ert-deftest ert-test-error-debug ()
1961 (let ((test (make-ert-test :body (lambda () (error "error message")))))
1962 (condition-case condition
1964 (let ((ert-debug-on-error t))
1965 (ert-run-test test))
1968 (assert (equal condition '(error "error message")) t)))))
1971 ;; Test that `should' works.
1972 (ert-deftest ert-test-should ()
1973 (let ((test (make-ert-test :body (lambda () (ert-should nil)))))
1974 (let ((result (let ((ert-debug-on-error nil))
1975 (ert-run-test test))))
1976 (assert (typep result 'ert-test-failed) t)
1977 (assert (equal (ert-test-result-with-condition-condition result)
1978 '(ert-test-failed ((ert-should nil) :form nil :value nil)))
1980 (let ((test (make-ert-test :body (lambda () (ert-should t)))))
1981 (let ((result (ert-run-test test)))
1982 (assert (typep result 'ert-test-passed) t))))
1984 (ert-deftest ert-test-should-value ()
1985 (ert-should (eql (ert-should 'foo) 'foo))
1986 (ert-should (eql (ert-should 'bar) 'bar)))
1988 (ert-deftest ert-test-should-not ()
1989 (let ((test (make-ert-test :body (lambda () (ert-should-not t)))))
1990 (let ((result (let ((ert-debug-on-error nil))
1991 (ert-run-test test))))
1992 (assert (typep result 'ert-test-failed) t)
1993 (assert (equal (ert-test-result-with-condition-condition result)
1994 '(ert-test-failed ((ert-should-not t) :form t :value t)))
1996 (let ((test (make-ert-test :body (lambda () (ert-should-not nil)))))
1997 (let ((result (ert-run-test test)))
1998 (assert (typep result 'ert-test-passed)))))
2001 (ert-deftest ert-test-should-error ()
2003 (let ((test (make-ert-test :body (lambda () (ert-should-error (progn))))))
2004 (let ((result (let ((ert-debug-on-error nil))
2005 (ert-run-test test))))
2006 (ert-should (typep result 'ert-test-failed))
2007 (ert-should (equal (ert-test-result-with-condition-condition result)
2009 ((ert-should-error (progn))
2012 :fail-reason "did not signal an error"))))))
2014 (let ((test (make-ert-test :body (lambda () (ert-should-error (error "foo"))))))
2015 (let ((result (ert-run-test test)))
2016 (ert-should (typep result 'ert-test-passed))))
2017 ;; Error of unexpected type, no test.
2018 (let ((test (make-ert-test :body (lambda ()
2019 (ert-should-error (error "foo")
2020 :type 'singularity-error)))))
2021 (let ((result (ert-run-test test)))
2022 (ert-should (typep result 'ert-test-failed))
2024 (ert-test-result-with-condition-condition result)
2026 ((ert-should-error (error "foo") :type 'singularity-error)
2028 :condition (error "foo")
2030 "the error signalled did not have the expected type"))))))
2031 ;; Error of the expected type, no test.
2032 (let ((test (make-ert-test :body (lambda ()
2033 (ert-should-error (signal 'singularity-error
2035 :type 'singularity-error)))))
2036 (let ((result (ert-run-test test)))
2037 (ert-should (typep result 'ert-test-passed))))
2038 ;; Error that fails the test, no type.
2039 (let ((test (make-ert-test :body (lambda ()
2042 :test (lambda (error) nil))))))
2043 (let ((result (ert-run-test test)))
2044 (ert-should (typep result 'ert-test-failed))
2045 (ert-should (equal (ert-test-result-with-condition-condition result)
2047 ((ert-should-error (error "foo") :test (lambda (error) nil))
2049 :condition (error "foo")
2051 "the error signalled did not pass the test"))))))
2052 ;; Error that passes the test, no type.
2053 (let ((test (make-ert-test :body (lambda ()
2054 (ert-should-error (error "foo")
2055 :test (lambda (error) t))))))
2056 (let ((result (ert-run-test test)))
2057 (ert-should (typep result 'ert-test-passed))))
2058 ;; Error that has the expected type but fails the test.
2059 (let ((test (make-ert-test :body (lambda ()
2061 (signal 'singularity-error nil)
2062 :type 'singularity-error
2063 :test (lambda (error) nil))))))
2064 (let ((result (ert-run-test test)))
2065 (ert-should (typep result 'ert-test-failed))
2066 (ert-should (equal (ert-test-result-with-condition-condition result)
2068 ((ert-should-error (signal 'singularity-error nil)
2069 :type 'singularity-error
2070 :test (lambda (error) nil))
2071 :form (signal singularity-error nil)
2072 :condition (singularity-error)
2074 "the error signalled did not pass the test"))))))
2075 ;; Error that has the expected type and passes the test.
2076 (let ((test (make-ert-test :body (lambda ()
2078 (signal 'singularity-error nil)
2079 :type 'singularity-error
2080 :test (lambda (error) t))))))
2081 (let ((result (ert-run-test test)))
2082 (ert-should (typep result 'ert-test-passed))))
2085 (ert-deftest ert-test-should-error-subtypes ()
2086 (let ((test (make-ert-test
2088 (ert-should-error (signal 'singularity-error nil)
2089 :type 'singularity-error
2090 :exclude-subtypes t)))))
2091 (let ((result (ert-run-test test)))
2092 (ert-should (typep result 'ert-test-passed))))
2093 (let ((test (make-ert-test
2095 (ert-should-error (signal 'arith-error nil)
2096 :type 'singularity-error)))))
2097 (let ((result (ert-run-test test)))
2098 (ert-should (typep result 'ert-test-failed))
2100 (ert-test-result-with-condition-condition result)
2102 ((ert-should-error (signal 'arith-error nil)
2103 :type 'singularity-error)
2104 :form (signal arith-error nil)
2105 :condition (arith-error)
2107 "the error signalled did not have the expected type"))))))
2108 (let ((test (make-ert-test
2110 (ert-should-error (signal 'arith-error nil)
2111 :type 'singularity-error
2112 :exclude-subtypes t)))))
2113 (let ((result (ert-run-test test)))
2114 (ert-should (typep result 'ert-test-failed))
2116 (ert-test-result-with-condition-condition result)
2118 ((ert-should-error (signal 'arith-error nil)
2119 :type 'singularity-error
2120 :exclude-subtypes t)
2121 :form (signal arith-error nil)
2122 :condition (arith-error)
2124 "the error signalled did not have the expected type"))))))
2125 (let ((test (make-ert-test
2127 (ert-should-error (signal 'singularity-error nil)
2129 :exclude-subtypes t)))))
2130 (let ((result (ert-run-test test)))
2131 (ert-should (typep result 'ert-test-failed))
2133 (ert-test-result-with-condition-condition result)
2135 ((ert-should-error (signal 'singularity-error nil)
2137 :exclude-subtypes t)
2138 :form (signal singularity-error nil)
2139 :condition (singularity-error)
2141 "the error signalled was a subtype of the expected type"))))))
2144 ;; Test that `should' errors contain the information we expect them to.
2145 (defmacro ert-test-my-list (&rest args)
2148 (ert-deftest ert-test-should-failure-debugging ()
2149 (loop for (body expected-condition) in
2150 `((,(lambda () (let ((x nil)) (ert-should x)))
2151 (ert-test-failed ((ert-should x) :form x :value nil)))
2152 (,(lambda () (let ((x t)) (ert-should-not x)))
2153 (ert-test-failed ((ert-should-not x) :form x :value t)))
2154 (,(lambda () (let ((x t)) (ert-should (not x))))
2155 (ert-test-failed ((ert-should (not x)) :form (not t) :value nil)))
2156 (,(lambda () (let ((x nil)) (ert-should-not (not x))))
2157 (ert-test-failed ((ert-should-not (not x)) :form (not nil) :value t)))
2158 (,(lambda () (let ((x t) (y nil)) (ert-should-not (ert-test-my-list x y))))
2160 ((ert-should-not (ert-test-my-list x y))
2163 (,(lambda () (let ((x t)) (ert-should (error "foo"))))
2166 (let ((test (make-ert-test :body body)))
2167 (condition-case actual-condition
2169 (let ((ert-debug-on-error t))
2170 (ert-run-test test))
2173 (ert-should (equal actual-condition expected-condition)))))))
2175 (ert-deftest ert-test-messages ()
2176 (let* ((message-string "Test message")
2177 (messages-buffer (get-buffer-create "*Messages*"))
2178 (test (make-ert-test :body (lambda () (message "%s" message-string)))))
2179 (with-current-buffer messages-buffer
2180 (let ((result (ert-run-test test)))
2181 (ert-should (equal (concat message-string "\n")
2182 (ert-test-result-messages result)))))))
2184 (defun ert-call-with-temporary-messages-buffer (thunk)
2185 (lexical-let ((new-buffer-name (generate-new-buffer-name
2186 "*Messages* orig buffer")))
2189 (with-current-buffer (get-buffer-create "*Messages*")
2190 (rename-buffer new-buffer-name))
2191 (get-buffer-create "*Messages*")
2193 (kill-buffer "*Messages*")
2194 (with-current-buffer new-buffer-name
2195 (rename-buffer "*Messages*")))))
2197 (ert-deftest ert-test-messages-on-log-truncation ()
2198 (let ((test (make-ert-test
2200 ;; Emacs would combine messages if we
2201 ;; generate the same message multiple
2208 (ert-call-with-temporary-messages-buffer
2210 (let ((message-log-max 2))
2211 (setq result (ert-run-test test)))
2212 (ert-should (equal (with-current-buffer "*Messages*"
2215 (ert-should (equal (ert-test-result-messages result) "a\nb\nc\nd\n")))))
2217 ;; Test `ert-select-tests'.
2218 (ert-deftest ert-test-select-regexp ()
2219 (ert-should (equal (ert-select-tests "^ert-test-select-regexp$" t)
2220 (list (ert-get-test 'ert-test-select-regexp)))))
2222 (ert-deftest ert-test-test-boundp ()
2223 (ert-should (ert-test-boundp 'ert-test-test-boundp))
2224 (ert-should-not (ert-test-boundp (make-symbol "ert-not-a-test"))))
2226 (ert-deftest ert-test-select-member ()
2227 (ert-should (equal (ert-select-tests '(member ert-test-select-member) t)
2228 (list (ert-get-test 'ert-test-select-member)))))
2230 (ert-deftest ert-test-select-test ()
2231 (ert-should (equal (ert-select-tests (ert-get-test 'ert-test-select-test) t)
2232 (list (ert-get-test 'ert-test-select-test)))))
2234 (ert-deftest ert-test-select-symbol ()
2235 (ert-should (equal (ert-select-tests 'ert-test-select-symbol t)
2236 (list (ert-get-test 'ert-test-select-symbol)))))
2238 (ert-deftest ert-test-select-and ()
2239 (let ((test (make-ert-test
2242 :most-recent-result (make-ert-test-failed
2245 (ert-should (equal (ert-select-tests `(and (member ,test) :failed) t)
2249 ;; Test utility functions.
2250 (ert-deftest ert-proper-list-p ()
2251 (ert-should (ert-proper-list-p '()))
2252 (ert-should (ert-proper-list-p '(1)))
2253 (ert-should (ert-proper-list-p '(1 2)))
2254 (ert-should (ert-proper-list-p '(1 2 3)))
2255 (ert-should (ert-proper-list-p '(1 2 3 4)))
2256 (ert-should (not (ert-proper-list-p 'a)))
2257 (ert-should (not (ert-proper-list-p '(1 . a))))
2258 (ert-should (not (ert-proper-list-p '(1 2 . a))))
2259 (ert-should (not (ert-proper-list-p '(1 2 3 . a))))
2260 (ert-should (not (ert-proper-list-p '(1 2 3 4 . a))))
2262 (setf (cdr (last a)) a)
2263 (ert-should (not (ert-proper-list-p a))))
2264 (let ((a (list 1 2)))
2265 (setf (cdr (last a)) a)
2266 (ert-should (not (ert-proper-list-p a))))
2267 (let ((a (list 1 2 3)))
2268 (setf (cdr (last a)) a)
2269 (ert-should (not (ert-proper-list-p a))))
2270 (let ((a (list 1 2 3 4)))
2271 (setf (cdr (last a)) a)
2272 (ert-should (not (ert-proper-list-p a))))
2273 (let ((a (list 1 2)))
2274 (setf (cdr (last a)) (cdr a))
2275 (ert-should (not (ert-proper-list-p a))))
2276 (let ((a (list 1 2 3)))
2277 (setf (cdr (last a)) (cdr a))
2278 (ert-should (not (ert-proper-list-p a))))
2279 (let ((a (list 1 2 3 4)))
2280 (setf (cdr (last a)) (cdr a))
2281 (ert-should (not (ert-proper-list-p a))))
2282 (let ((a (list 1 2 3)))
2283 (setf (cdr (last a)) (cddr a))
2284 (ert-should (not (ert-proper-list-p a))))
2285 (let ((a (list 1 2 3 4)))
2286 (setf (cdr (last a)) (cddr a))
2287 (ert-should (not (ert-proper-list-p a))))
2288 (let ((a (list 1 2 3 4)))
2289 (setf (cdr (last a)) (cdddr a))
2290 (ert-should (not (ert-proper-list-p a)))))
2292 (ert-deftest ert-parse-keys-and-body ()
2293 (ert-should (equal (ert-parse-keys-and-body "doc" '(foo))
2294 '(nil "doc" (foo))))
2295 (ert-should (equal (ert-parse-keys-and-body "doc" '(:bar foo))
2296 '((:bar foo) "doc" nil)))
2297 (ert-should (equal (ert-parse-keys-and-body nil '(:bar foo))
2298 '((:bar foo) nil nil)))
2299 (ert-should (equal (ert-parse-keys-and-body "doc" '(:bar foo))
2300 '((:bar foo) "doc" nil)))
2301 (ert-should (equal (ert-parse-keys-and-body nil '(:bar foo a (b)))
2302 '((:bar foo) nil (a (b)))))
2303 (ert-should (equal (ert-parse-keys-and-body nil '(:bar foo :a (b)))
2304 '((:bar foo :a (b)) nil nil)))
2305 (ert-should (equal (ert-parse-keys-and-body nil '(bar foo :a (b)))
2306 '(nil nil (bar foo :a (b)))))
2307 (ert-should-error (ert-parse-keys-and-body nil '(:bar foo :a))))
2311 ;; Test `ert-run-tests'.
2312 (ert-deftest ert-test-run-tests ()
2313 (let ((passing-test (make-ert-test :name 'passing-test
2314 :body (lambda () (ert-pass))))
2315 (failing-test (make-ert-test :name 'failing-test
2316 :body (lambda () (ert-fail
2317 "failure message"))))
2319 (let ((ert-debug-on-error nil))
2320 (let* ((buffer-name (generate-new-buffer-name " *ert-test-run-tests*"))
2323 (lambda (format-string &rest args)
2324 (push (apply #'format format-string args) messages))))
2325 (save-window-excursion
2327 (let ((case-fold-search nil))
2328 (ert-run-tests-interactively
2329 `(member ,passing-test ,failing-test) buffer-name
2331 (ert-should (equal messages `(,(concat
2332 "Ran 2 tests, 1 results were "
2333 "as expected, 1 unexpected"))))
2334 (with-current-buffer buffer-name
2335 (goto-char (point-min))
2337 (buffer-substring (point-min)
2342 "Selector: (member <passing-test> <failing-test>)\n"
2343 "Passed: 1 (0 unexpected)\n"
2344 "Failed: 1 (1 unexpected)\n"
2345 "Error: 0 (0 unexpected)\n"
2347 (when (get-buffer buffer-name)
2348 (kill-buffer buffer-name))))))))
2350 (ert-deftest ert-test-special-operator-p ()
2351 (ert-should (ert-special-operator-p 'if))
2352 (ert-should-not (ert-special-operator-p 'car))
2353 (ert-should-not (ert-special-operator-p 'ert-special-operator-p))
2355 (ert-should-not (ert-special-operator-p b))
2357 (ert-should (ert-special-operator-p b))))
2359 ;; This test attempts to demonstrate that there is no way to force
2360 ;; immediate truncation of the *Messages* buffer from Lisp (and hence
2361 ;; justifies the existence of
2362 ;; `ert-force-message-log-buffer-truncation'): The only way that came
2363 ;; to my mind was (message ""), which doesn't have the desired effect.
2364 (ert-deftest ert-test-builtin-message-log-flushing ()
2365 (ert-call-with-temporary-messages-buffer
2367 (with-current-buffer "*Messages*"
2368 (let ((message-log-max 2))
2369 (let ((message-log-max t))
2370 (loop for i below 4 do
2372 (ert-should (eql (count-lines (point-min) (point-max)) 4)))
2373 (ert-should (eql (count-lines (point-min) (point-max)) 4))
2375 (ert-should (eql (count-lines (point-min) (point-max)) 4))
2376 (message "Test message")
2377 (ert-should (eql (count-lines (point-min) (point-max)) 2)))))))
2379 (ert-deftest ert-test-force-message-log-buffer-truncation ()
2381 (loop for i below 5 do
2384 (ert-call-with-temporary-messages-buffer
2386 (let ((message-log-max x))
2388 (with-current-buffer "*Messages*"
2391 (ert-call-with-temporary-messages-buffer
2393 (let ((message-log-max t))
2395 (let ((message-log-max x))
2396 (ert-force-message-log-buffer-truncation))
2397 (with-current-buffer "*Messages*"
2398 (buffer-string))))))
2399 (loop for x in '(0 1 2 3 4 5 6 t) do
2400 (ert-should (equal (c x) (lisp x))))))
2402 (defun ert-run-self-tests ()
2403 ;; Run tests and make sure they actually ran.
2404 (let ((window-configuration (current-window-configuration)))
2405 (let ((ert-test-body-was-run nil))
2406 ;; The buffer name chosen here should not compete with the default
2407 ;; results buffer name for completion in `switch-to-buffer'.
2408 (let ((stats (ert-run-tests-interactively "^ert-" " *ert self-tests*")))
2409 (assert ert-test-body-was-run)
2410 (when (zerop (+ (ert-stats-passed-unexpected stats)
2411 (ert-stats-failed-unexpected stats)
2412 (ert-stats-error-unexpected stats)))
2413 ;; Hide results window only when everything went well.
2414 (set-window-configuration window-configuration))))))
2418 ;;; ert.el ends here