aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul Eggert2011-01-13 09:17:33 -0800
committerPaul Eggert2011-01-13 09:17:33 -0800
commit193770eec942defa96c7ea310773b40534f709d1 (patch)
tree3a1ed2de2b7ece4697999045da27e21fb174f21c
parentf737437b23c75bb6924021df14b4f740ce370b21 (diff)
parent821f936d1c04df2f9ccaf6307b220d7cbe0e76c7 (diff)
downloademacs-193770eec942defa96c7ea310773b40534f709d1.tar.gz
emacs-193770eec942defa96c7ea310773b40534f709d1.zip
Merge from mainline.
-rw-r--r--ChangeLog11
-rw-r--r--Makefile.in9
-rwxr-xr-xconfigure3
-rw-r--r--configure.in2
-rw-r--r--doc/misc/ChangeLog7
-rw-r--r--doc/misc/Makefile.in11
-rw-r--r--doc/misc/ert.texi830
-rw-r--r--doc/misc/makefile.w32-in13
-rw-r--r--etc/ChangeLog4
-rw-r--r--etc/NEWS9
-rw-r--r--etc/themes/tsdh-dark-theme.el6
-rw-r--r--lisp/ChangeLog45
-rw-r--r--lisp/cus-theme.el2
-rw-r--r--lisp/dired-x.el19
-rw-r--r--lisp/dired.el2
-rw-r--r--lisp/emacs-lisp/ert-x.el290
-rw-r--r--lisp/emacs-lisp/ert.el2544
-rw-r--r--lisp/emacs-lisp/package.el18
-rw-r--r--lisp/font-lock.el5
-rw-r--r--lisp/gnus/ChangeLog13
-rw-r--r--lisp/gnus/gnus-msg.el4
-rw-r--r--lisp/gnus/message.el42
-rw-r--r--lisp/ido.el12
-rw-r--r--lisp/mail/rmail.el72
-rw-r--r--lisp/mail/sendmail.el134
-rw-r--r--lisp/menu-bar.el20
-rw-r--r--lisp/mh-e/ChangeLog4
-rw-r--r--lisp/mh-e/mh-comp.el7
-rw-r--r--lisp/simple.el67
-rw-r--r--src/image.c3
-rw-r--r--test/ChangeLog8
-rw-r--r--test/automated/Makefile.in158
-rw-r--r--test/automated/ert-tests.el949
-rw-r--r--test/automated/ert-x-tests.el273
34 files changed, 5375 insertions, 221 deletions
diff --git a/ChangeLog b/ChangeLog
index 54b6958607e..789d54cfa60 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,4 +1,4 @@
12011-01-11 Paul Eggert <eggert@cs.ucla.edu> 12011-01-13 Paul Eggert <eggert@cs.ucla.edu>
2 2
3 * Makefile.in (GNULIB_MODULES): Change ftoastr to dtoastr. 3 * Makefile.in (GNULIB_MODULES): Change ftoastr to dtoastr.
4 This avoids building ftoastr and ldtoastr, which aren't needed. See 4 This avoids building ftoastr and ldtoastr, which aren't needed. See
@@ -106,6 +106,15 @@
106 * make-dist: Also put into the distribution aclocal.m4, 106 * make-dist: Also put into the distribution aclocal.m4,
107 compile, depcomp, missing, and the files under lib/. 107 compile, depcomp, missing, and the files under lib/.
108 108
1092011-01-13 Christian Ohler <ohler@gnu.org>
110
111 * Makefile.in (INFO_FILES): Add ERT.
112
113 * Makefile.in (check): Run tests in test/automated.
114
115 * Makefile.in:
116 * configure.in: Add test/automated/Makefile.
117
1092011-01-07 Paul Eggert <eggert@cs.ucla.edu> 1182011-01-07 Paul Eggert <eggert@cs.ucla.edu>
110 119
111 * install-sh, mkinstalldirs, move-if-change: Update from master 120 * install-sh, mkinstalldirs, move-if-change: Update from master
diff --git a/Makefile.in b/Makefile.in
index 72e642c79c0..b9237edac88 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -134,7 +134,7 @@ MAN_PAGES=ctags.1 ebrowse.1 emacs.1 emacsclient.1 etags.1 \
134infodir=@infodir@ 134infodir=@infodir@
135INFO_FILES=ada-mode auth autotype calc ccmode cl dbus dired-x ebrowse \ 135INFO_FILES=ada-mode auth autotype calc ccmode cl dbus dired-x ebrowse \
136 ede ediff edt eieio efaq eintr elisp emacs emacs-mime epa erc \ 136 ede ediff edt eieio efaq eintr elisp emacs emacs-mime epa erc \
137 eshell eudc flymake forms gnus idlwave info mairix-el \ 137 ert eshell eudc flymake forms gnus idlwave info mairix-el \
138 message mh-e newsticker nxml-mode org pcl-cvs pgg rcirc \ 138 message mh-e newsticker nxml-mode org pcl-cvs pgg rcirc \
139 reftex remember sasl sc semantic ses sieve smtpmail speedbar \ 139 reftex remember sasl sc semantic ses sieve smtpmail speedbar \
140 tramp url vip viper widget woman 140 tramp url vip viper widget woman
@@ -267,7 +267,7 @@ EMACSFULL = `echo emacs-${version}${EXEEXT} | sed '$(TRANSFORM)'`
267SUBDIR = lib lib-src src lisp 267SUBDIR = lib lib-src src lisp
268 268
269# The subdir makefiles created by config.status. 269# The subdir makefiles created by config.status.
270SUBDIR_MAKEFILES = lib/Makefile lib-src/Makefile doc/emacs/Makefile doc/misc/Makefile doc/lispref/Makefile doc/lispintro/Makefile src/Makefile oldXMenu/Makefile lwlib/Makefile leim/Makefile lisp/Makefile 270SUBDIR_MAKEFILES = lib/Makefile lib-src/Makefile doc/emacs/Makefile doc/misc/Makefile doc/lispref/Makefile doc/lispintro/Makefile src/Makefile oldXMenu/Makefile lwlib/Makefile leim/Makefile lisp/Makefile test/automated/Makefile
271 271
272# Subdirectories to install, and where they'll go. 272# Subdirectories to install, and where they'll go.
273# lib-src's makefile knows how to install it, so we don't do that here. 273# lib-src's makefile knows how to install it, so we don't do that here.
@@ -395,7 +395,8 @@ Makefile: config.status $(srcdir)/src/config.in \
395 $(srcdir)/oldXMenu/Makefile.in \ 395 $(srcdir)/oldXMenu/Makefile.in \
396 $(srcdir)/lwlib/Makefile.in \ 396 $(srcdir)/lwlib/Makefile.in \
397 $(srcdir)/leim/Makefile.in \ 397 $(srcdir)/leim/Makefile.in \
398 $(srcdir)/lisp/Makefile.in 398 $(srcdir)/lisp/Makefile.in \
399 $(srcdir)/test/automated/Makefile.in
399 ./config.status 400 ./config.status
400 401
401config.status: ${srcdir}/configure ${srcdir}/lisp/version.el 402config.status: ${srcdir}/configure ${srcdir}/lisp/version.el
@@ -855,7 +856,7 @@ TAGS tags: lib lib-src src
855 cd src; $(MAKE) tags 856 cd src; $(MAKE) tags
856 857
857check: 858check:
858 @echo "We don't have any tests for GNU Emacs yet." 859 cd test/automated; $(MAKE) check
859 860
860dist: 861dist:
861 cd ${srcdir}; ./make-dist 862 cd ${srcdir}; ./make-dist
diff --git a/configure b/configure
index 49dc050e7bc..9fa20d66244 100755
--- a/configure
+++ b/configure
@@ -17389,7 +17389,7 @@ test "${prefix}" != NONE &&
17389test "${exec_prefix}" != NONE && 17389test "${exec_prefix}" != NONE &&
17390 exec_prefix=`echo "${exec_prefix}" | sed 's,\([^/]\)/*$,\1,'` 17390 exec_prefix=`echo "${exec_prefix}" | sed 's,\([^/]\)/*$,\1,'`
17391 17391
17392ac_config_files="$ac_config_files Makefile lib/Makefile lib-src/Makefile oldXMenu/Makefile doc/emacs/Makefile doc/misc/Makefile doc/lispintro/Makefile doc/lispref/Makefile src/Makefile lwlib/Makefile lisp/Makefile leim/Makefile" 17392ac_config_files="$ac_config_files Makefile lib/Makefile lib-src/Makefile oldXMenu/Makefile doc/emacs/Makefile doc/misc/Makefile doc/lispintro/Makefile doc/lispref/Makefile src/Makefile lwlib/Makefile lisp/Makefile leim/Makefile test/automated/Makefile"
17393 17393
17394ac_config_commands="$ac_config_commands default" 17394ac_config_commands="$ac_config_commands default"
17395 17395
@@ -18174,6 +18174,7 @@ do
18174 "lwlib/Makefile") CONFIG_FILES="$CONFIG_FILES lwlib/Makefile" ;; 18174 "lwlib/Makefile") CONFIG_FILES="$CONFIG_FILES lwlib/Makefile" ;;
18175 "lisp/Makefile") CONFIG_FILES="$CONFIG_FILES lisp/Makefile" ;; 18175 "lisp/Makefile") CONFIG_FILES="$CONFIG_FILES lisp/Makefile" ;;
18176 "leim/Makefile") CONFIG_FILES="$CONFIG_FILES leim/Makefile" ;; 18176 "leim/Makefile") CONFIG_FILES="$CONFIG_FILES leim/Makefile" ;;
18177 "test/automated/Makefile") CONFIG_FILES="$CONFIG_FILES test/automated/Makefile" ;;
18177 "default") CONFIG_COMMANDS="$CONFIG_COMMANDS default" ;; 18178 "default") CONFIG_COMMANDS="$CONFIG_COMMANDS default" ;;
18178 18179
18179 *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; 18180 *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;;
diff --git a/configure.in b/configure.in
index 20e90faabbf..9fd9719c14e 100644
--- a/configure.in
+++ b/configure.in
@@ -3718,7 +3718,7 @@ dnl the use of force in the `epaths-force' rule in Makefile.in.
3718AC_OUTPUT(Makefile lib/Makefile lib-src/Makefile oldXMenu/Makefile \ 3718AC_OUTPUT(Makefile lib/Makefile lib-src/Makefile oldXMenu/Makefile \
3719 doc/emacs/Makefile doc/misc/Makefile doc/lispintro/Makefile \ 3719 doc/emacs/Makefile doc/misc/Makefile doc/lispintro/Makefile \
3720 doc/lispref/Makefile src/Makefile \ 3720 doc/lispref/Makefile src/Makefile \
3721 lwlib/Makefile lisp/Makefile leim/Makefile, [ 3721 lwlib/Makefile lisp/Makefile leim/Makefile test/automated/Makefile, [
3722 3722
3723### Make the necessary directories, if they don't exist. 3723### Make the necessary directories, if they don't exist.
3724for dir in etc lisp ; do 3724for dir in etc lisp ; do
diff --git a/doc/misc/ChangeLog b/doc/misc/ChangeLog
index ca43b7e06df..7fc944e523c 100644
--- a/doc/misc/ChangeLog
+++ b/doc/misc/ChangeLog
@@ -1,3 +1,10 @@
12011-01-13 Christian Ohler <ohler@gnu.org>
2
3 * ert.texi: New file.
4
5 * Makefile.in:
6 * makefile.w32-in: Add ert.texi.
7
12011-01-10 Jan Moringen <jan.moringen@uni-bielefeld.de> 82011-01-10 Jan Moringen <jan.moringen@uni-bielefeld.de>
2 9
3 * dbus.texi (Receiving Method Calls): New function 10 * dbus.texi (Receiving Method Calls): New function
diff --git a/doc/misc/Makefile.in b/doc/misc/Makefile.in
index deeafa2c1b2..7953d0cf596 100644
--- a/doc/misc/Makefile.in
+++ b/doc/misc/Makefile.in
@@ -62,6 +62,7 @@ INFO_TARGETS = \
62 $(infodir)/emacs-mime \ 62 $(infodir)/emacs-mime \
63 $(infodir)/epa \ 63 $(infodir)/epa \
64 $(infodir)/erc \ 64 $(infodir)/erc \
65 $(infodir)/ert \
65 $(infodir)/eshell \ 66 $(infodir)/eshell \
66 $(infodir)/eudc \ 67 $(infodir)/eudc \
67 $(infodir)/efaq \ 68 $(infodir)/efaq \
@@ -112,6 +113,7 @@ DVI_TARGETS = \
112 emacs-mime.dvi \ 113 emacs-mime.dvi \
113 epa.dvi \ 114 epa.dvi \
114 erc.dvi \ 115 erc.dvi \
116 ert.dvi \
115 eshell.dvi \ 117 eshell.dvi \
116 eudc.dvi \ 118 eudc.dvi \
117 faq.dvi \ 119 faq.dvi \
@@ -162,6 +164,7 @@ PDF_TARGETS = \
162 emacs-mime.pdf \ 164 emacs-mime.pdf \
163 epa.pdf \ 165 epa.pdf \
164 erc.pdf \ 166 erc.pdf \
167 ert.pdf \
165 eshell.pdf \ 168 eshell.pdf \
166 eudc.pdf \ 169 eudc.pdf \
167 faq.pdf \ 170 faq.pdf \
@@ -360,6 +363,14 @@ erc.dvi: ${srcdir}/erc.texi
360erc.pdf: ${srcdir}/erc.texi 363erc.pdf: ${srcdir}/erc.texi
361 $(ENVADD) $(TEXI2PDF) $< 364 $(ENVADD) $(TEXI2PDF) $<
362 365
366ert : $(infodir)/ert
367$(infodir)/ert: ert.texi $(infodir)
368 cd $(srcdir); $(MAKEINFO) ert.texi
369ert.dvi: ert.texi
370 $(ENVADD) $(TEXI2DVI) ${srcdir}/ert.texi
371ert.pdf: ert.texi
372 $(ENVADD) $(TEXI2PDF) ${srcdir}/ert.texi
373
363eshell : $(infodir)/eshell 374eshell : $(infodir)/eshell
364$(infodir)/eshell: eshell.texi 375$(infodir)/eshell: eshell.texi
365 $(mkinfodir) 376 $(mkinfodir)
diff --git a/doc/misc/ert.texi b/doc/misc/ert.texi
new file mode 100644
index 00000000000..e1a8cdc319c
--- /dev/null
+++ b/doc/misc/ert.texi
@@ -0,0 +1,830 @@
1\input texinfo
2@c %**start of header
3@setfilename ../../info/ert
4@settitle Emacs Lisp Regression Testing
5@c %**end of header
6
7@dircategory Emacs
8@direntry
9* ERT: (ert). Emacs Lisp Regression Testing.
10@end direntry
11
12@copying
13Copyright @copyright{} 2008, 2010, 2011 Free Software Foundation, Inc.
14
15@quotation
16Permission is granted to copy, distribute and/or modify this document
17under the terms of the GNU Free Documentation License, Version 1.2 or
18any later version published by the Free Software Foundation; with no
19Invariant Sections, with no Front-Cover Texts, and with no Back-Cover
20Texts.
21@end quotation
22@end copying
23
24@node Top, Introduction, (dir), (dir)
25@top ERT: Emacs Lisp Regression Testing
26
27ERT is a tool for automated testing in Emacs Lisp. Its main features
28are facilities for defining tests, running them and reporting the
29results, and for debugging test failures interactively.
30
31ERT is similar to tools for other environments such as JUnit, but has
32unique features that take advantage of the dynamic and interactive
33nature of Emacs. Despite its name, it works well both for test-driven
34development (see
35@url{http://en.wikipedia.org/wiki/Test-driven_development}) and for
36traditional software development methods.
37
38@menu
39* Introduction:: A simple example of an ERT test.
40* How to Run Tests:: Run tests in your Emacs or from the command line.
41* How to Write Tests:: How to add tests to your Emacs Lisp code.
42* How to Debug Tests:: What to do if a test fails.
43* Extending ERT:: ERT is extensible in several ways.
44* Other Testing Concepts:: Features not in ERT.
45
46@detailmenu
47 --- The Detailed Node Listing ---
48
49How to Run Tests
50
51* Running Tests Interactively:: Run tests in your current Emacs.
52* Running Tests in Batch Mode:: Run tests in emacs -Q.
53* Test Selectors:: Choose which tests to run.
54
55How to Write Tests
56
57* The @code{should} Macro:: A powerful way to express assertions.
58* Expected Failures:: Tests for known bugs.
59* Tests and Their Environment:: Don't depend on customizations; no side effects.
60* Useful Techniques:: Some examples.
61
62How to Debug Tests
63
64* Understanding Explanations:: How ERT gives details on why an assertion failed.
65* Interactive Debugging:: Tools available in the ERT results buffer.
66
67Extending ERT
68
69* Defining Explanation Functions:: Teach ERT about more predicates.
70* Low-Level Functions for Working with Tests:: Use ERT's data for your purposes.
71
72Other Testing Concepts
73
74* Mocks and Stubs:: Stubbing out code that is irrelevant to the test.
75* Fixtures and Test Suites:: How ERT differs from tools for other languages.
76
77@end detailmenu
78@end menu
79
80@node Introduction, How to Run Tests, Top, Top
81@chapter Introduction
82
83ERT allows you to define @emph{tests} in addition to functions,
84macros, variables, and the other usual Lisp constructs. Tests are
85simply Lisp code --- code that invokes other code and checks whether
86it behaves as expected.
87
88ERT keeps track of the tests that are defined and provides convenient
89commands to run them to verify whether the definitions that are
90currently loaded in Emacs pass the tests.
91
92Some Lisp files have comments like the following (adapted from the
93package @code{pp.el}):
94
95@lisp
96;; (pp-to-string '(quote quote)) ; expected: "'quote"
97;; (pp-to-string '((quote a) (quote b))) ; expected: "('a 'b)\n"
98;; (pp-to-string '('a 'b)) ; same as above
99@end lisp
100
101The code contained in these comments can be evaluated from time to
102time to compare the output with the expected output. ERT formalizes
103this and introduces a common convention, which simplifies Emacs
104development, since programmers no longer have to manually find and
105evaluate such comments.
106
107An ERT test definition equivalent to the above comments is this:
108
109@lisp
110(ert-deftest pp-test-quote ()
111 "Tests the rendering of `quote' symbols in `pp-to-string'."
112 (should (equal (pp-to-string '(quote quote)) "'quote"))
113 (should (equal (pp-to-string '((quote a) (quote b))) "('a 'b)\n"))
114 (should (equal (pp-to-string '('a 'b)) "('a 'b)\n")))
115@end lisp
116
117If you know @code{defun}, the syntax of @code{ert-deftest} should look
118familiar: This example defines a test named @code{pp-test-quote} that
119will pass if the three calls to @code{equal} all return true
120(non-nil).
121
122@code{should} is a macro with the same meaning as @code{assert} but
123better error reporting. @xref{The @code{should} Macro}.
124
125Each test should have a name that describes what functionality the
126test tests. Test names can be chosen arbitrarily --- they are in a
127namespace separate from functions and variables --- but should follow
128the usual Emacs Lisp convention of having a prefix that indicates
129which package they belong to. Test names are displayed by ERT when
130reporting failures and can be used when selecting which tests to run.
131
132The empty parentheses @code{()} in the first line don't currently have
133any meaning and are reserved for future extension. They also make
134@code{ert-deftest}'s syntax more similar to @code{defun}.
135
136The docstring describes what feature this test tests. When running
137tests interactively, the first line of the docstring is displayed for
138tests that fail, so it is good if the first line makes sense on its
139own.
140
141The body of a test can be arbitrary Lisp code. It should have as few
142side effects as possible; each test should be written to clean up
143after itself, leaving Emacs in the same state as it was before the
144test. Tests should clean up even if they fail. @xref{Tests and Their
145Environment}.
146
147
148@node How to Run Tests, How to Write Tests, Introduction, Top
149@chapter How to Run Tests
150
151You can run tests either in the Emacs you are working in, or on the
152command line in a separate Emacs process in batch mode (i.e., with no
153user interface). The former mode is convenient during interactive
154development, the latter is useful to make sure that tests pass
155independently of your customizations, allows tests to be invoked from
156makefiles and scripts to be written that run tests in several
157different Emacs versions.
158
159@menu
160* Running Tests Interactively:: Run tests in your current Emacs.
161* Running Tests in Batch Mode:: Run tests in emacs -Q.
162* Test Selectors:: Choose which tests to run.
163@end menu
164
165
166@node Running Tests Interactively, Running Tests in Batch Mode, How to Run Tests, How to Run Tests
167@section Running Tests Interactively
168
169You can run the tests that are currently defined in your Emacs with
170the command @kbd{@kbd{M-x} ert @kbd{RET} t @kbd{RET}}. ERT will pop
171up a new buffer, the ERT results buffer, showing the results of the
172tests run. It looks like this:
173
174@example
175Selector: t
176Passed: 31
177Failed: 2 (2 unexpected)
178Total: 33/33
179
180Started at: 2008-09-11 08:39:25-0700
181Finished.
182Finished at: 2008-09-11 08:39:27-0700
183
184FF...............................
185
186F addition-test
187 (ert-test-failed
188 ((should
189 (=
190 (+ 1 2)
191 4))
192 :form
193 (= 3 4)
194 :value nil))
195
196F list-test
197 (ert-test-failed
198 ((should
199 (equal
200 (list 'a 'b 'c)
201 '(a b d)))
202 :form
203 (equal
204 (a b c)
205 (a b d))
206 :value nil :explanation
207 (list-elt 2
208 (different-atoms c d))))
209@end example
210
211At the top, there is a summary of the results: We ran all tests in the
212current Emacs (@code{Selector: t}), 31 of them passed, and 2 failed
213unexpectedly. @xref{Expected Failures}, for an explanation of the
214term @emph{unexpected} in this context.
215
216The line of dots and @code{F}s is a progress bar where each character
217represents one test; it fills while the tests are running. A dot
218means that the test passed, an @code{F} means that it failed. Below
219the progress bar, ERT shows details about each test that had an
220unexpected result. In the example above, there are two failures, both
221due to failed @code{should} forms. @xref{Understanding Explanations},
222for more details.
223
224In the ERT results buffer, @kbd{TAB} and @kbd{S-TAB} cycle between
225buttons. Each name of a function or macro in this buffer is a button;
226moving point to it and typing @kbd{RET} jumps to its definition.
227
228Pressing @kbd{r} re-runs the test near point on its own. Pressing
229@kbd{d} re-runs it with the debugger enabled. @kbd{.} jumps to the
230definition of the test near point (@kbd{RET} has the same effect if
231point is on the name of the test). On a failed test, @kbd{b} shows
232the backtrace of the failure.
233
234@kbd{l} shows the list of @code{should} forms executed in the test.
235If any messages were generated (with the Lisp function @code{message})
236in a test or any of the code that it invoked, @kbd{m} will show them.
237
238By default, long expressions in the failure details are abbreviated
239using @code{print-length} and @code{print-level}. Pressing @kbd{L}
240while point is on a test failure will increase the limits to show more
241of the expression.
242
243
244@node Running Tests in Batch Mode, Test Selectors, Running Tests Interactively, How to Run Tests
245@section Running Tests in Batch Mode
246
247ERT supports automated invocations from the command line or from
248scripts or makefiles. There are two functions for this purpose,
249@code{ert-run-tests-batch} and @code{ert-run-tests-batch-and-exit}.
250They can be used like this:
251
252@example
253emacs -batch -L /path/to/ert -l ert.el -l my-tests.el -f ert-run-tests-batch-and-exit
254@end example
255
256This command will start up Emacs in batch mode, load ERT, load
257@code{my-tests.el}, and run all tests defined in it. It will exit
258with a zero exit status if all tests passed, or nonzero if any tests
259failed or if anything else went wrong. It will also print progress
260messages and error diagnostics to standard output.
261
262You may need additional @code{-L} flags to ensure that
263@code{my-tests.el} and all the files that it requires are on your
264@code{load-path}.
265
266
267@node Test Selectors, , Running Tests in Batch Mode, How to Run Tests
268@section Test Selectors
269
270Functions like @code{ert} accept a @emph{test selector}, a Lisp
271expression specifying a set of tests. Test selector syntax is similar
272to Common Lisp's type specifier syntax:
273
274@itemize
275@item @code{nil} selects no tests.
276@item @code{t} selects all tests.
277@item @code{:new} selects all tests that have not been run yet.
278@item @code{:failed} and @code{:passed} select tests according to their most recent result.
279@item @code{:expected}, @code{:unexpected} select tests according to their most recent result.
280@item A string selects all tests that have a name that matches the string, a regexp.
281@item A test selects that test.
282@item A symbol selects the test that the symbol names.
283@item @code{(member TESTS...)} selects TESTS, a list of tests or symbols naming tests.
284@item @code{(eql TEST)} selects TEST, a test or a symbol naming a test.
285@item @code{(and SELECTORS...)} selects the tests that match all SELECTORS.
286@item @code{(or SELECTORS...)} selects the tests that match any SELECTOR.
287@item @code{(not SELECTOR)} selects all tests that do not match SELECTOR.
288@item @code{(tag TAG)} selects all tests that have TAG on their tags list.
289@item @code{(satisfies PREDICATE)} Selects all tests that satisfy PREDICATE.
290@end itemize
291
292Selectors that are frequently useful when selecting tests to run
293include @code{t} to run all tests that are currently defined in Emacs,
294@code{"^foo-"} to run all tests in package @code{foo} --- this assumes
295that package @code{foo} uses the prefix @code{foo-} for its test names
296---, result-based selectors such as @code{(or :new :unexpected)} to
297run all tests that have either not run yet or that had an unexpected
298result in the last run, and tag-based selectors such as @code{(not
299(tag :causes-redisplay))} to run all tests that are not tagged
300@code{:causes-redisplay}.
301
302
303@node How to Write Tests, How to Debug Tests, How to Run Tests, Top
304@chapter How to Write Tests
305
306ERT lets you define tests in the same way you define functions. You
307can type @code{ert-deftest} forms in a buffer and evaluate them there
308with @code{eval-defun} or @code{compile-defun}, or you can save the
309file and load it, optionally byte-compiling it first.
310
311Just like @code{find-function} is only able to find where a function
312was defined if the function was loaded from a file, ERT is only able
313to find where a test was defined if the test was loaded from a file.
314
315
316@menu
317* The @code{should} Macro:: A powerful way to express assertions.
318* Expected Failures:: Tests for known bugs.
319* Tests and Their Environment:: Don't depend on customizations; no side effects.
320* Useful Techniques:: Some examples.
321@end menu
322
323@node The @code{should} Macro, Expected Failures, How to Write Tests, How to Write Tests
324@section The @code{should} Macro
325
326Test bodies can include arbitrary code; but to be useful, they need to
327have checks whether the code being tested (or @emph{code under test})
328does what it is supposed to do. The macro @code{should} is similar to
329@code{assert} from the cl package, but analyzes its argument form and
330records information that ERT can display to help debugging.
331
332This test definition
333
334@lisp
335(ert-deftest addition-test ()
336 (should (= (+ 1 2) 4)))
337@end lisp
338
339will produce this output when run via @kbd{M-x ert}:
340
341@example
342F addition-test
343 (ert-test-failed
344 ((should
345 (=
346 (+ 1 2)
347 4))
348 :form
349 (= 3 4)
350 :value nil))
351@end example
352
353In this example, @code{should} recorded the fact that (= (+ 1 2) 4)
354reduced to (= 3 4) before it reduced to nil. When debugging why the
355test failed, it helps to know that the function @code{+} returned 3
356here. ERT records the return value for any predicate called directly
357within @code{should}.
358
359In addition to @code{should}, ERT provides @code{should-not}, which
360checks that the predicate returns nil, and @code{should-error}, which
361checks that the form called within it signals an error. An example
362use of @code{should-error}:
363
364@lisp
365(ert-deftest test-divide-by-zero ()
366 (should-error (/ 1 0)
367 :type 'arith-error))
368@end lisp
369
370This checks that dividing one by zero signals an error of type
371@code{arith-error}. The @code{:type} argument to @code{should-error}
372is optional; if absent, any type of error is accepted.
373@code{should-error} returns an error description of the error that was
374signalled, to allow additional checks to be made. The error
375description has the format @code{(ERROR-SYMBOL . DATA)}.
376
377There is no @code{should-not-error} macro since tests that signal an
378error fail anyway, so @code{should-not-error} is effectively the
379default.
380
381@xref{Understanding Explanations}, for more details on what
382@code{should} reports.
383
384
385@node Expected Failures, Tests and Their Environment, The @code{should} Macro, How to Write Tests
386@section Expected Failures
387
388Some bugs are complicated to fix or not very important and are left as
389@emph{known bugs}. If there is a test case that triggers the bug and
390fails, ERT will alert you of this failure every time you run all
391tests. For known bugs, this alert is a distraction. The way to
392suppress it is to add @code{:expected-result :failed} to the test
393definition:
394
395@lisp
396(ert-deftest future-bug ()
397 "Test `time-forward' with negative arguments.
398Since this functionality isn't implemented yet, the test is known to fail."
399 :expected-result :failed
400 (time-forward -1))
401@end lisp
402
403ERT will still display a small @code{f} in the progress bar as a
404reminder that there is a known bug, and will count the test as failed,
405but it will be quiet about it otherwise.
406
407An alternative to marking the test as a known failure this way is to
408delete the test. This is a good idea if there is no intent to fix it,
409i.e., if the behavior that was formerly considered a bug has become an
410accepted feature.
411
412In general, however, it can be useful to keep tests that are known to
413fail. If someone wants to fix the bug, they will have a very good
414starting point: an automated test case that reproduces the bug. This
415makes it much easier to fix the bug, demonstrate that it is fixed, and
416prevent future regressions.
417
418ERT displays the same kind of alerts for tests that pass unexpectedly
419that it displays for unexpected failures. This way, if you make code
420changes that happen to fix a bug that you weren't aware of, you will
421know to remove the @code{:expected-result} clause of that test and
422close the corresponding bug report, if any.
423
424Since @code{:expected-result} evaluates its argument when the test is
425loaded, tests can be marked as known failures only on certain Emacs
426versions, specific architectures, etc.:
427
428@lisp
429(ert-deftest foo ()
430 "A test that is expected to fail on Emacs 23 but succeed elsewhere."
431 :expected-result (if (string-match "GNU Emacs 23[.]" (emacs-version))
432 :failed
433 :passed)
434 ...)
435@end lisp
436
437
438@node Tests and Their Environment, Useful Techniques, Expected Failures, How to Write Tests
439@section Tests and Their Environment
440
441The outcome of running a test should not depend on the current state
442of the environment, and each test should leave its environment in the
443same state it found it in. In particular, a test should not depend on
444any Emacs customization variables or hooks, and if it has to make any
445changes to Emacs' state or state external to Emacs such as the file
446system, it should undo these changes before it returns, regardless of
447whether it passed or failed.
448
449Tests should not depend on the environment because any such
450dependencies can make the test brittle or lead to failures that occur
451only under certain circumstances and are hard to reproduce. Of
452course, the code under test may have settings that affect its
453behavior. In that case, it is best to make the test @code{let}-bind
454all such settings variables to set up a specific configuration for the
455duration of the test. The test can also set up a number of different
456configurations and run the code under test with each.
457
458Tests that have side effects on their environment should restore it to
459its original state because any side effects that persist after the
460test can disrupt the workflow of the programmer running the tests. If
461the code under test has side effects on Emacs' current state, such as
462on the current buffer or window configuration, the test should create
463a temporary buffer for the code to manipulate (using
464@code{with-temp-buffer}), or save and restore the window configuration
465(using @code{save-window-excursion}), respectively. For aspects of
466the state that can not be preserved with such macros, cleanup should
467be performed with @code{unwind-protect}, to ensure that the cleanup
468occurs even if the test fails.
469
470An exception to this are messages that the code under test prints with
471@code{message} and similar logging; tests should not bother restoring
472the @code{*Message*} buffer to its original state.
473
474The above guidelines imply that tests should avoid calling highly
475customizable commands such as @code{find-file}, except, of course, if
476such commands are what they want to test. The exact behavior of
477@code{find-file} depends on many settings such as
478@code{find-file-wildcards}, @code{enable-local-variables}, and
479@code{auto-mode-alist}. It is difficult to write a meaningful test if
480its behavior can be affected by so many external factors. Also,
481@code{find-file} has side effects that are hard to predict and thus
482hard to undo: It may create a new buffer or may reuse an existing
483buffer if one is already visiting the requested file; and it runs
484@code{find-file-hook}, which can have arbitrary side effects.
485
486Instead, it is better to use lower-level mechanisms with simple and
487predictable semantics like @code{with-temp-buffer}, @code{insert} or
488@code{insert-file-contents-literally}, and activating the desired mode
489by calling the corresponding function directly --- after binding the
490hook variables to nil. This avoids the above problems.
491
492
493@node Useful Techniques, , Tests and Their Environment, How to Write Tests
494@section Useful Techniques when Writing Tests
495
496Testing simple functions that have no side effects and no dependencies
497on their environment is easy. Such tests often look like this:
498
499@lisp
500(ert-deftest ert-test-mismatch ()
501 (should (eql (ert--mismatch "" "") nil))
502 (should (eql (ert--mismatch "" "a") 0))
503 (should (eql (ert--mismatch "a" "a") nil))
504 (should (eql (ert--mismatch "ab" "a") 1))
505 (should (eql (ert--mismatch "Aa" "aA") 0))
506 (should (eql (ert--mismatch '(a b c) '(a b d)) 2)))
507@end lisp
508
509This test calls the function @code{ert--mismatch} several times with
510various combinations of arguments and compares the return value to the
511expected return value. (Some programmers prefer @code{(should (eql
512EXPECTED ACTUAL))} over the @code{(should (eql ACTUAL EXPECTED))}
513shown here. ERT works either way.)
514
515Here's a more complicated test:
516
517@lisp
518(ert-deftest ert-test-record-backtrace ()
519 (let ((test (make-ert-test :body (lambda () (ert-fail "foo")))))
520 (let ((result (ert-run-test test)))
521 (should (ert-test-failed-p result))
522 (with-temp-buffer
523 (ert--print-backtrace (ert-test-failed-backtrace result))
524 (goto-char (point-min))
525 (end-of-line)
526 (let ((first-line (buffer-substring-no-properties (point-min) (point))))
527 (should (equal first-line " signal(ert-test-failed (\"foo\"))")))))))
528@end lisp
529
530This test creates a test object using @code{make-ert-test} whose body
531will immediately signal failure. It then runs that test and asserts
532that it fails. Then, it creates a temporary buffer and invokes
533@code{ert--print-backtrace} to print the backtrace of the failed test
534to the current buffer. Finally, it extracts the first line from the
535buffer and asserts that it matches what we expect. It uses
536@code{buffer-substring-no-properties} and @code{equal} to ignore text
537properties; for a test that takes properties into account,
538@code{buffer-substring} and @code{ert-equal-including-properties}
539could be used instead.
540
541The reason why this test only checks the first line of the backtrace
542is that the remainder of the backtrace is dependent on ERT's internals
543as well as whether the code is running interpreted or compiled. By
544looking only at the first line, the test checks a useful property
545--- that the backtrace correctly captures the call to @code{signal} that
546results from the call to @code{ert-fail} --- without being brittle.
547
548This example also shows that writing tests is much easier if the code
549under test was structured with testing in mind.
550
551For example, if @code{ert-run-test} accepted only symbols that name
552tests rather than test objects, the test would need a name for the
553failing test, which would have to be a temporary symbol generated with
554@code{make-symbol}, to avoid side effects on Emacs' state. Choosing
555the right interface for @code{ert-run-tests} allows the test to be
556simpler.
557
558Similarly, if @code{ert--print-backtrace} printed the backtrace to a
559buffer with a fixed name rather than the current buffer, it would be
560much harder for the test to undo the side effect. Of course, some
561code somewhere needs to pick the buffer name. But that logic is
562independent of the logic that prints backtraces, and keeping them in
563separate functions allows us to test them independently.
564
565A lot of code that you will encounter in Emacs was not written with
566testing in mind. Sometimes, the easiest way to write tests for such
567code is to restructure the code slightly to provide better interfaces
568for testing. Usually, this makes the interfaces easier to use as
569well.
570
571
572@node How to Debug Tests, Extending ERT, How to Write Tests, Top
573@chapter How to Debug Tests
574
575This section describes how to use ERT's features to understand why
576a test failed.
577
578
579@menu
580* Understanding Explanations:: How ERT gives details on why an assertion failed.
581* Interactive Debugging:: Tools available in the ERT results buffer.
582@end menu
583
584
585@node Understanding Explanations, Interactive Debugging, How to Debug Tests, How to Debug Tests
586@section Understanding Explanations
587
588Failed @code{should} forms are reported like this:
589
590@example
591F addition-test
592 (ert-test-failed
593 ((should
594 (=
595 (+ 1 2)
596 4))
597 :form
598 (= 3 4)
599 :value nil))
600@end example
601
602ERT shows what the @code{should} expression looked like and what
603values its subexpressions had: The source code of the assertion was
604@code{(should (= (+ 1 2) 4))}, which applied the function @code{=} to
605the arguments @code{3} and @code{4}, resulting in the value
606@code{nil}. In this case, the test is wrong; it should expect 3
607rather than 4.
608
609If a predicate like @code{equal} is used with @code{should}, ERT
610provides a so-called @emph{explanation}:
611
612@example
613F list-test
614 (ert-test-failed
615 ((should
616 (equal
617 (list 'a 'b 'c)
618 '(a b d)))
619 :form
620 (equal
621 (a b c)
622 (a b d))
623 :value nil :explanation
624 (list-elt 2
625 (different-atoms c d))))
626@end example
627
628In this case, the function @code{equal} was applied to the arguments
629@code{(a b c)} and @code{(a b d)}. ERT's explanation shows that
630the item at index 2 differs between the two lists; in one list, it is
631the atom c, in the other, it is the atom d.
632
633In simple examples like the above, the explanation is unnecessary.
634But in cases where the difference is not immediately apparent, it can
635save time:
636
637@example
638F test1
639 (ert-test-failed
640 ((should
641 (equal x y))
642 :form
643 (equal a a)
644 :value nil :explanation
645 (different-symbols-with-the-same-name a a)))
646@end example
647
648ERT only provides explanations for predicates that have an explanation
649function registered. @xref{Defining Explanation Functions}.
650
651
652@node Interactive Debugging, , Understanding Explanations, How to Debug Tests
653@section Interactive Debugging
654
655Debugging failed tests works essentially the same way as debugging any
656other problems with Lisp code. Here are a few tricks specific to
657tests:
658
659@itemize
660@item Re-run the failed test a few times to see if it fails in the same way
661each time. It's good to find out whether the behavior is
662deterministic before spending any time looking for a cause. In the
663ERT results buffer, @kbd{r} re-runs the selected test.
664
665@item Use @kbd{.} to jump to the source code of the test to find out what
666exactly it does. Perhaps the test is broken rather than the code
667under test.
668
669@item If the test contains a series of @code{should} forms and you can't
670tell which one failed, use @kbd{l}, which shows you the list of all
671@code{should} forms executed during the test before it failed.
672
673@item Use @kbd{b} to view the backtrace. You can also use @kbd{d} to re-run
674the test with debugging enabled, this will enter the debugger and show
675the backtrace as well; but the top few frames shown there will not be
676relevant to you since they are ERT's own debugger hook. @kbd{b}
677strips them out, so it is more convenient.
678
679@item If the test or the code under testing prints messages using
680@code{message}, use @kbd{m} to see what messages it printed before it
681failed. This can be useful to figure out how far it got.
682
683@item You can instrument tests for debugging the same way you instrument
684@code{defun}s for debugging --- go to the source code of the test and
685type @kbd{@kbd{C-u} @kbd{C-M-x}}. Then, go back to the ERT buffer and
686re-run the test with @kbd{r} or @kbd{d}.
687
688@item If you have been editing and rearranging tests, it is possible that
689ERT remembers an old test that you have since renamed or removed ---
690renamings or removals of definitions in the source code leave around a
691stray definition under the old name in the running process, this is a
692common problem in Lisp. In such a situation, hit @kbd{D} to let ERT
693forget about the obsolete test.
694@end itemize
695
696
697@node Extending ERT, Other Testing Concepts, How to Debug Tests, Top
698@chapter Extending ERT
699
700There are several ways to add functionality to ERT.
701
702@menu
703* Defining Explanation Functions:: Teach ERT about more predicates.
704* Low-Level Functions for Working with Tests:: Use ERT's data for your purposes.
705@end menu
706
707
708@node Defining Explanation Functions, Low-Level Functions for Working with Tests, Extending ERT, Extending ERT
709@section Defining Explanation Functions
710
711The explanation function for a predicate is a function that takes the
712same arguments as the predicate and returns an @emph{explanation}.
713The explanation should explain why the predicate, when invoked with
714the arguments given to the explanation function, returns the value
715that it returns. The explanation can be any object but should have a
716comprehensible printed representation. If the return value of the
717predicate needs no explanation for a given list of arguments, the
718explanation function should return nil.
719
720To associate an explanation function with a predicate, add the
721property @code{ert-explainer} to the symbol that names the predicate.
722The value of the property should be the symbol that names the
723explanation function.
724
725
726@node Low-Level Functions for Working with Tests, , Defining Explanation Functions, Extending ERT
727@section Low-Level Functions for Working with Tests
728
729Both @code{ert-run-tests-interactively} and @code{ert-run-tests-batch}
730are implemented on top of the lower-level test handling code in the
731sections named ``Facilities for running a single test'', ``Test
732selectors'', and ``Facilities for running a whole set of tests''.
733
734If you want to write code that works with ERT tests, you should take a
735look at this lower-level code. Symbols that start with @code{ert--}
736are internal to ERT, those that start with @code{ert-} but not
737@code{ert--} are meant to be usable by other code. But there is no
738mature API yet.
739
740Contributions to ERT are welcome.
741
742
743@node Other Testing Concepts, , Extending ERT, Top
744@chapter Other Testing Concepts
745
746For information on mocks, stubs, fixtures, or test suites, see below.
747
748
749@menu
750* Mocks and Stubs:: Stubbing out code that is irrelevant to the test.
751* Fixtures and Test Suites:: How ERT differs from tools for other languages.
752@end menu
753
754@node Mocks and Stubs, Fixtures and Test Suites, Other Testing Concepts, Other Testing Concepts
755@section Other Tools for Emacs Lisp
756
757Stubbing out functions or using so-called @emph{mocks} can make it
758easier to write tests. See
759@url{http://en.wikipedia.org/wiki/Mock_object} for an explanation of
760the corresponding concepts in object-oriented languages.
761
762ERT does not have built-in support for mocks or stubs. The package
763@code{el-mock} (see @url{http://www.emacswiki.org/emacs/el-mock.el})
764offers mocks for Emacs Lisp and can be used in conjunction with ERT.
765
766
767@node Fixtures and Test Suites, , Mocks and Stubs, Other Testing Concepts
768@section Fixtures and Test Suites
769
770In many ways, ERT is similar to frameworks for other languages like
771SUnit or JUnit. However, two features commonly found in such
772frameworks are notably absent from ERT: fixtures and test suites.
773
774Fixtures, as used e.g. in SUnit or JUnit, are mainly used to provide
775an environment for a set of tests, and consist of set-up and tear-down
776functions.
777
778While fixtures are a useful syntactic simplification in other
779languages, this does not apply to Lisp, where higher-order functions
780and `unwind-protect' are available. One way to implement and use a
781fixture in ERT is
782
783@lisp
784(defun my-fixture (body)
785 (unwind-protect
786 (progn [set up]
787 (funcall body))
788 [tear down]))
789
790(ert-deftest my-test ()
791 (my-fixture
792 (lambda ()
793 [test code])))
794@end lisp
795
796(Another way would be a @code{with-my-fixture} macro.) This solves
797the set-up and tear-down part, and additionally allows any test
798to use any combination of fixtures, so it is more flexible than what
799other tools typically allow.
800
801If the test needs access to the environment the fixture sets up, the
802fixture can be modified to pass arguments to the body.
803
804These are well-known Lisp techniques. Special syntax for them could
805be added but would provide only a minor simplification.
806
807(If you are interested in such syntax, note that splitting set-up and
808tear-down into separate functions, like *Unit tools usually do, makes
809it impossible to establish dynamic `let' bindings as part of the
810fixture. So, blindly imitating the way fixtures are implemented in
811other languages would be counter-productive in Lisp.)
812
813The purpose of test suites is to group related tests together.
814
815The most common use of this is to run just the tests for one
816particular module. Since symbol prefixes are the usual way of
817separating module namespaces in Emacs Lisp, test selectors already
818solve this by allowing regexp matching on test names; e.g., the
819selector "^ert-" selects ERT's self-tests.
820
821Other uses include grouping tests by their expected execution time to
822run quick tests during interactive development and slow tests less
823frequently. This can be achieved with the @code{:tag} argument to
824@code{ert-deftest} and @code{tag} test selectors.
825
826@bye
827
828@c LocalWords: ERT Hagelberg Ohler JUnit namespace docstring ERT's
829@c LocalWords: backtrace makefiles workflow backtraces API SUnit
830@c LocalWords: subexpressions
diff --git a/doc/misc/makefile.w32-in b/doc/misc/makefile.w32-in
index fd3b1476b55..e5150e3b122 100644
--- a/doc/misc/makefile.w32-in
+++ b/doc/misc/makefile.w32-in
@@ -47,7 +47,8 @@ INFO_TARGETS = $(infodir)/ccmode \
47 $(infodir)/org $(infodir)/url $(infodir)/speedbar \ 47 $(infodir)/org $(infodir)/url $(infodir)/speedbar \
48 $(infodir)/tramp $(infodir)/ses $(infodir)/smtpmail \ 48 $(infodir)/tramp $(infodir)/ses $(infodir)/smtpmail \
49 $(infodir)/flymake $(infodir)/newsticker $(infodir)/rcirc \ 49 $(infodir)/flymake $(infodir)/newsticker $(infodir)/rcirc \
50 $(infodir)/erc $(infodir)/remember $(infodir)/nxml-mode \ 50 $(infodir)/erc $(infodir)/ert \
51 $(infodir)/remember $(infodir)/nxml-mode \
51 $(infodir)/epa $(infodir)/mairix-el $(infodir)/sasl \ 52 $(infodir)/epa $(infodir)/mairix-el $(infodir)/sasl \
52 $(infodir)/auth $(infodir)/eieio $(infodir)/ede \ 53 $(infodir)/auth $(infodir)/eieio $(infodir)/ede \
53 $(infodir)/semantic $(infodir)/edt 54 $(infodir)/semantic $(infodir)/edt
@@ -58,7 +59,8 @@ DVI_TARGETS = calc.dvi cc-mode.dvi cl.dvi dbus.dvi dired-x.dvi \
58 ada-mode.dvi autotype.dvi idlwave.dvi eudc.dvi ebrowse.dvi \ 59 ada-mode.dvi autotype.dvi idlwave.dvi eudc.dvi ebrowse.dvi \
59 pcl-cvs.dvi woman.dvi eshell.dvi org.dvi url.dvi \ 60 pcl-cvs.dvi woman.dvi eshell.dvi org.dvi url.dvi \
60 speedbar.dvi tramp.dvi ses.dvi smtpmail.dvi flymake.dvi \ 61 speedbar.dvi tramp.dvi ses.dvi smtpmail.dvi flymake.dvi \
61 newsticker.dvi rcirc.dvi erc.dvi remember.dvi nxml-mode.dvi \ 62 newsticker.dvi rcirc.dvi erc.dvi ert.dvi \
63 remember.dvi nxml-mode.dvi \
62 epa.dvi mairix-el.dvi sasl.dvi auth.dvi eieio.dvi ede.dvi \ 64 epa.dvi mairix-el.dvi sasl.dvi auth.dvi eieio.dvi ede.dvi \
63 semantic.dvi edt.dvi 65 semantic.dvi edt.dvi
64INFOSOURCES = info.texi 66INFOSOURCES = info.texi
@@ -305,6 +307,11 @@ $(infodir)/erc: erc.texi
305erc.dvi: erc.texi 307erc.dvi: erc.texi
306 $(ENVADD) $(TEXI2DVI) $(srcdir)/erc.texi 308 $(ENVADD) $(TEXI2DVI) $(srcdir)/erc.texi
307 309
310$(infodir)/ert: ert.texi
311 $(MAKEINFO) ert.texi
312ert.dvi: ert.texi
313 $(ENVADD) $(TEXI2DVI) $(srcdir)/ert.texi
314
308$(infodir)/epa: epa.texi 315$(infodir)/epa: epa.texi
309 $(MAKEINFO) epa.texi 316 $(MAKEINFO) epa.texi
310epa.dvi: epa.texi 317epa.dvi: epa.texi
@@ -362,7 +369,7 @@ clean: mostlyclean
362 $(infodir)/url* $(infodir)/org* \ 369 $(infodir)/url* $(infodir)/org* \
363 $(infodir)/flymake* $(infodir)/newsticker* \ 370 $(infodir)/flymake* $(infodir)/newsticker* \
364 $(infodir)/sieve* $(infodir)/pgg* \ 371 $(infodir)/sieve* $(infodir)/pgg* \
365 $(infodir)/erc* $(infodir)/rcirc* \ 372 $(infodir)/erc* $(infodir)/ert* $(infodir)/rcirc* \
366 $(infodir)/remember* $(infodir)/nxml-mode* \ 373 $(infodir)/remember* $(infodir)/nxml-mode* \
367 $(infodir)/epa* $(infodir)/sasl* \ 374 $(infodir)/epa* $(infodir)/sasl* \
368 $(infodir)/mairix-el* $(infodir)/auth* \ 375 $(infodir)/mairix-el* $(infodir)/auth* \
diff --git a/etc/ChangeLog b/etc/ChangeLog
index d690c963fba..d8dd5aa0ae0 100644
--- a/etc/ChangeLog
+++ b/etc/ChangeLog
@@ -1,3 +1,7 @@
12011-01-13 Christian Ohler <ohler@gnu.org>
2
3 * NEWS: Mention ERT.
4
12011-01-10 Jan Moringen <jan.moringen@uni-bielefeld.de> 52011-01-10 Jan Moringen <jan.moringen@uni-bielefeld.de>
2 6
3 * NEWS: Add new function dbus-register-service. 7 * NEWS: Add new function dbus-register-service.
diff --git a/etc/NEWS b/etc/NEWS
index 92d96fd1806..8f707331f81 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -210,6 +210,10 @@ automatically when Emacs starts up. To disable this, set
210`package-enable-at-startup' to nil. To change which packages are 210`package-enable-at-startup' to nil. To change which packages are
211loaded, customize `package-load-list'. 211loaded, customize `package-load-list'.
212 212
213** An Emacs Lisp testing tool is now included.
214Emacs Lisp developers can use this tool to write automated tests for
215their code. See the ERT info manual for details.
216
213** Custom Themes 217** Custom Themes
214 218
215*** `M-x customize-themes' lists Custom themes which can be enabled. 219*** `M-x customize-themes' lists Custom themes which can be enabled.
@@ -621,6 +625,11 @@ Notifications API. It requires D-Bus for communication.
621 625
622* Incompatible Lisp Changes in Emacs 24.1 626* Incompatible Lisp Changes in Emacs 24.1
623 627
628** `compose-mail' now accepts an optional 8th arg, RETURN-ACTION, and
629passes it to the mail user agent function. This argument specifies an
630action for returning to the caller after finishing with the mail.
631This is currently used by Rmail to delete a mail window.
632
624** For mouse click input events in the text area, the Y pixel 633** For mouse click input events in the text area, the Y pixel
625coordinate in the POSITION list now counts from the top of the text 634coordinate in the POSITION list now counts from the top of the text
626area, excluding any header line. Previously, it counted from the top 635area, excluding any header line. Previously, it counted from the top
diff --git a/etc/themes/tsdh-dark-theme.el b/etc/themes/tsdh-dark-theme.el
index 4fe86987d87..cc1e86b3306 100644
--- a/etc/themes/tsdh-dark-theme.el
+++ b/etc/themes/tsdh-dark-theme.el
@@ -25,12 +25,12 @@
25(custom-theme-set-faces 25(custom-theme-set-faces
26 'tsdh-dark 26 'tsdh-dark
27 '(default ((t (:background "gray20" :foreground "white smoke")))) 27 '(default ((t (:background "gray20" :foreground "white smoke"))))
28 '(diff-added ((t (:inherit diff-changed :background "light green")))) 28 '(diff-added ((t (:inherit diff-changed :background "dark green"))))
29 '(diff-changed ((t (:background "light steel blue")))) 29 '(diff-changed ((t (:background "midnight blue"))))
30 '(diff-indicator-added ((t (:inherit diff-indicator-changed)))) 30 '(diff-indicator-added ((t (:inherit diff-indicator-changed))))
31 '(diff-indicator-changed ((t (:weight bold)))) 31 '(diff-indicator-changed ((t (:weight bold))))
32 '(diff-indicator-removed ((t (:inherit diff-indicator-changed)))) 32 '(diff-indicator-removed ((t (:inherit diff-indicator-changed))))
33 '(diff-removed ((t (:inherit diff-changed :background "sandy brown")))) 33 '(diff-removed ((t (:inherit diff-changed :background "dark red"))))
34 '(dired-directory ((t (:inherit font-lock-function-name-face :weight bold)))) 34 '(dired-directory ((t (:inherit font-lock-function-name-face :weight bold))))
35 '(hl-line ((t (:background "grey28")))) 35 '(hl-line ((t (:background "grey28"))))
36 '(message-header-subject ((t (:foreground "SkyBlue")))) 36 '(message-header-subject ((t (:foreground "SkyBlue"))))
diff --git a/lisp/ChangeLog b/lisp/ChangeLog
index 0c3f09e157b..eafb096f499 100644
--- a/lisp/ChangeLog
+++ b/lisp/ChangeLog
@@ -1,3 +1,48 @@
12011-01-13 Kim F. Storm <storm@cua.dk>
2
3 * ido.el (ido-may-cache-directory): Move "too-big" check later.
4 (ido-next-match, ido-prev-match): Fix stray reordering of matching
5 items when cycling through the matches.
6
72011-01-13 Tassilo Horn <tassilo@member.fsf.org>
8
9 * dired-x.el (dired-omit-verbose): New defcustom that allows
10 disabling the omit messages.
11 (dired-omit-expunge): Use it.
12
132011-01-13 Christian Ohler <ohler@gnu.org>
14
15 * emacs-lisp/ert.el, emacs-lisp/ert-x.el: New files.
16
172011-01-13 Chong Yidong <cyd@stupidchicken.com>
18
19 * font-lock.el (font-lock-verbose): Default to nil.
20
212011-01-13 Chong Yidong <cyd@stupidchicken.com>
22
23 * simple.el (sendmail-user-agent-compose): Move to sendmail.el.
24 (compose-mail): New arg RETURN-ACTION.
25 (compose-mail-other-window, compose-mail-other-frame): Likewise.
26
27 * mail/sendmail.el (mail-return-action): New var.
28 (mail-mode): Make it buffer-local.
29 (mail-bury): Obey it. Move special Rmail window handling to
30 rmail-mail-return.
31 (mail, mail-setup): New arg RETURN-ACTION.
32 (sendmail-user-agent-compose): Move from simple.el.
33
34 * mail/rmail.el (rmail-mail-return): New function.
35 (rmail-start-mail): Pass it to compose-mail.
36
372011-01-12 Chong Yidong <cyd@stupidchicken.com>
38
39 * menu-bar.el (menu-bar-custom-menu): Tweak Mule and Customize
40 menus. Add menu item for customize-themes.
41
42 * cus-theme.el (customize-themes):
43 * emacs-lisp/package.el (package--list-packages): Use
44 switch-to-buffer.
45
12011-01-11 Johan Bockgård <bojohan@gnu.org> 462011-01-11 Johan Bockgård <bojohan@gnu.org>
2 47
3 * emacs-lisp/unsafep.el (unsafep): Handle backquoted forms. 48 * emacs-lisp/unsafep.el (unsafep): Handle backquoted forms.
diff --git a/lisp/cus-theme.el b/lisp/cus-theme.el
index e6e286f00fa..6e94b326e53 100644
--- a/lisp/cus-theme.el
+++ b/lisp/cus-theme.el
@@ -541,7 +541,7 @@ Do not call this mode function yourself. It is meant for internal use."
541When called from Lisp, BUFFER should be the buffer to use; if 541When called from Lisp, BUFFER should be the buffer to use; if
542omitted, a buffer named *Custom Themes* is used." 542omitted, a buffer named *Custom Themes* is used."
543 (interactive) 543 (interactive)
544 (pop-to-buffer (get-buffer-create (or buffer "*Custom Themes*"))) 544 (switch-to-buffer (get-buffer-create (or buffer "*Custom Themes*")))
545 (let ((inhibit-read-only t)) 545 (let ((inhibit-read-only t))
546 (erase-buffer)) 546 (erase-buffer))
547 (custom-theme-choose-mode) 547 (custom-theme-choose-mode)
diff --git a/lisp/dired-x.el b/lisp/dired-x.el
index 45fdda71356..a0b3bf38c03 100644
--- a/lisp/dired-x.el
+++ b/lisp/dired-x.el
@@ -189,6 +189,12 @@ files and lock files."
189 :type 'regexp 189 :type 'regexp
190 :group 'dired-x) 190 :group 'dired-x)
191 191
192(defcustom dired-omit-verbose t
193 "When non-nil, show messages when omitting files.
194When nil, don't show messages."
195 :type 'boolean
196 :group 'dired-x)
197
192(defcustom dired-find-subdir nil ; t is pretty near to DWIM... 198(defcustom dired-find-subdir nil ; t is pretty near to DWIM...
193 "If non-nil, Dired always finds a directory in a buffer of its own. 199 "If non-nil, Dired always finds a directory in a buffer of its own.
194If nil, Dired finds the directory as a subdirectory in some other buffer 200If nil, Dired finds the directory as a subdirectory in some other buffer
@@ -613,8 +619,9 @@ This functions works by temporarily binding `dired-marker-char' to
613 (not dired-omit-size-limit) 619 (not dired-omit-size-limit)
614 (< (buffer-size) dired-omit-size-limit) 620 (< (buffer-size) dired-omit-size-limit)
615 (progn 621 (progn
616 (message "Not omitting: directory larger than %d characters." 622 (when dired-omit-verbose
617 dired-omit-size-limit) 623 (message "Not omitting: directory larger than %d characters."
624 dired-omit-size-limit))
618 (setq dired-omit-mode nil) 625 (setq dired-omit-mode nil)
619 nil))) 626 nil)))
620 (let ((omit-re (or regexp (dired-omit-regexp))) 627 (let ((omit-re (or regexp (dired-omit-regexp)))
@@ -622,12 +629,14 @@ This functions works by temporarily binding `dired-marker-char' to
622 count) 629 count)
623 (or (string= omit-re "") 630 (or (string= omit-re "")
624 (let ((dired-marker-char dired-omit-marker-char)) 631 (let ((dired-marker-char dired-omit-marker-char))
625 (message "Omitting...") 632 (when dired-omit-verbose (message "Omitting..."))
626 (if (dired-mark-unmarked-files omit-re nil nil dired-omit-localp) 633 (if (dired-mark-unmarked-files omit-re nil nil dired-omit-localp)
627 (progn 634 (progn
628 (setq count (dired-do-kill-lines nil "Omitted %d line%s.")) 635 (setq count (dired-do-kill-lines
636 nil
637 (if dired-omit-verbose "Omitted %d line%s." "")))
629 (force-mode-line-update)) 638 (force-mode-line-update))
630 (message "(Nothing to omit)")))) 639 (when dired-omit-verbose (message "(Nothing to omit)")))))
631 ;; Try to preserve modified state of buffer. So `%*' doesn't appear 640 ;; Try to preserve modified state of buffer. So `%*' doesn't appear
632 ;; in mode-line of omitted buffers. 641 ;; in mode-line of omitted buffers.
633 (set-buffer-modified-p (and old-modified-p 642 (set-buffer-modified-p (and old-modified-p
diff --git a/lisp/dired.el b/lisp/dired.el
index e34340c15df..1f9c78a9db3 100644
--- a/lisp/dired.el
+++ b/lisp/dired.el
@@ -4021,7 +4021,7 @@ true then the type of the file linked to by FILE is printed instead.
4021;;;*** 4021;;;***
4022 4022
4023;;;### (autoloads (dired-do-relsymlink dired-jump) "dired-x" "dired-x.el" 4023;;;### (autoloads (dired-do-relsymlink dired-jump) "dired-x" "dired-x.el"
4024;;;;;; "27c312d6d5d40d8cb4ef8d62e30d5f4a") 4024;;;;;; "6181a5bcc2b61255676a7a41549b9f40")
4025;;; Generated autoloads from dired-x.el 4025;;; Generated autoloads from dired-x.el
4026 4026
4027(autoload 'dired-jump "dired-x" "\ 4027(autoload 'dired-jump "dired-x" "\
diff --git a/lisp/emacs-lisp/ert-x.el b/lisp/emacs-lisp/ert-x.el
new file mode 100644
index 00000000000..bb14b84117a
--- /dev/null
+++ b/lisp/emacs-lisp/ert-x.el
@@ -0,0 +1,290 @@
1;;; ert-x.el --- Staging area for experimental extensions to ERT
2
3;; Copyright (C) 2008, 2010, 2011 Free Software Foundation, Inc.
4
5;; Author: Lennart Borgman (lennart O borgman A gmail O com)
6;; Author: Christian Ohler <ohler@gnu.org>
7
8;; This file is part of GNU Emacs.
9
10;; This program is free software: you can redistribute it and/or
11;; modify it under the terms of the GNU General Public License as
12;; published by the Free Software Foundation, either version 3 of the
13;; License, or (at your option) any later version.
14;;
15;; This program is distributed in the hope that it will be useful, but
16;; WITHOUT ANY WARRANTY; without even the implied warranty of
17;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18;; General Public License for more details.
19;;
20;; You should have received a copy of the GNU General Public License
21;; along with this program. If not, see `http://www.gnu.org/licenses/'.
22
23;;; Commentary:
24
25;; This file includes some extra helper functions to use while writing
26;; automated tests with ERT. These have been proposed as extensions
27;; to ERT but are not mature yet and likely to change.
28
29;;; Code:
30
31(eval-when-compile
32 (require 'cl))
33(require 'ert)
34
35
36;;; Test buffers.
37
38(defun ert--text-button (string &rest properties)
39 "Return a string containing STRING as a text button with PROPERTIES.
40
41See `make-text-button'."
42 (with-temp-buffer
43 (insert string)
44 (apply #'make-text-button (point-min) (point-max) properties)
45 (buffer-string)))
46
47(defun ert--format-test-buffer-name (base-name)
48 "Compute a test buffer name based on BASE-NAME.
49
50Helper function for `ert--test-buffers'."
51 (format "*Test buffer (%s)%s*"
52 (or (and (ert-running-test)
53 (ert-test-name (ert-running-test)))
54 "<anonymous test>")
55 (if base-name
56 (format ": %s" base-name)
57 "")))
58
59(defvar ert--test-buffers (make-hash-table :weakness t)
60 "Table of all test buffers. Keys are the buffer objects, values are t.
61
62The main use of this table is for `ert-kill-all-test-buffers'.
63Not all buffers in this table are necessarily live, but all live
64test buffers are in this table.")
65
66(define-button-type 'ert--test-buffer-button
67 'action #'ert--test-buffer-button-action
68 'help-echo "mouse-2, RET: Pop to test buffer")
69
70(defun ert--test-buffer-button-action (button)
71 "Pop to the test buffer that BUTTON is associated with."
72 (pop-to-buffer (button-get button 'ert--test-buffer)))
73
74(defun ert--call-with-test-buffer (ert--base-name ert--thunk)
75 "Helper function for `ert-with-test-buffer'.
76
77Create a test buffer with a name based on ERT--BASE-NAME and run
78ERT--THUNK with that buffer as current."
79 (let* ((ert--buffer (generate-new-buffer
80 (ert--format-test-buffer-name ert--base-name)))
81 (ert--button (ert--text-button (buffer-name ert--buffer)
82 :type 'ert--test-buffer-button
83 'ert--test-buffer ert--buffer)))
84 (puthash ert--buffer 't ert--test-buffers)
85 ;; We don't use `unwind-protect' here since we want to kill the
86 ;; buffer only on success.
87 (prog1 (with-current-buffer ert--buffer
88 (ert-info (ert--button :prefix "Buffer: ")
89 (funcall ert--thunk)))
90 (kill-buffer ert--buffer)
91 (remhash ert--buffer ert--test-buffers))))
92
93(defmacro* ert-with-test-buffer ((&key ((:name name-form)))
94 &body body)
95 "Create a test buffer and run BODY in that buffer.
96
97To be used in ERT tests. If BODY finishes successfully, the test
98buffer is killed; if there is an error, the test buffer is kept
99around on error for further inspection. Its name is derived from
100the name of the test and the result of NAME-FORM."
101 (declare (debug ((form) body))
102 (indent 1))
103 `(ert--call-with-test-buffer ,name-form (lambda () ,@body)))
104
105;; We use these `put' forms in addition to the (declare (indent)) in
106;; the defmacro form since the `declare' alone does not lead to
107;; correct indentation before the .el/.elc file is loaded.
108;; Autoloading these `put' forms solves this.
109;;;###autoload
110(progn
111 ;; TODO(ohler): Figure out what these mean and make sure they are correct.
112 (put 'ert-with-test-buffer 'lisp-indent-function 1))
113
114;;;###autoload
115(defun ert-kill-all-test-buffers ()
116 "Kill all test buffers that are still live."
117 (interactive)
118 (let ((count 0))
119 (maphash (lambda (buffer dummy)
120 (when (or (not (buffer-live-p buffer))
121 (kill-buffer buffer))
122 (incf count)))
123 ert--test-buffers)
124 (message "%s out of %s test buffers killed"
125 count (hash-table-count ert--test-buffers)))
126 ;; It could be that some test buffers were actually kept alive
127 ;; (e.g., due to `kill-buffer-query-functions'). I'm not sure what
128 ;; to do about this. For now, let's just forget them.
129 (clrhash ert--test-buffers)
130 nil)
131
132
133;;; Simulate commands.
134
135(defun ert-simulate-command (command)
136 ;; FIXME: add unread-events
137 "Simulate calling COMMAND the way the Emacs command loop would call it.
138
139This effectively executes
140
141 \(apply (car COMMAND) (cdr COMMAND)\)
142
143and returns the same value, but additionally runs hooks like
144`pre-command-hook' and `post-command-hook', and sets variables
145like `this-command' and `last-command'.
146
147COMMAND should be a list where the car is the command symbol and
148the rest are arguments to the command.
149
150NOTE: Since the command is not called by `call-interactively'
151test for `called-interactively' in the command will fail."
152 (assert (listp command) t)
153 (assert (commandp (car command)) t)
154 (assert (not unread-command-events) t)
155 (let (return-value)
156 ;; For the order of things here see command_loop_1 in keyboard.c.
157 ;;
158 ;; The command loop will reset the command-related variables so
159 ;; there is no reason to let-bind them. They are set here,
160 ;; however, to be able to test several commands in a row and how
161 ;; they affect each other.
162 (setq deactivate-mark nil
163 this-original-command (car command)
164 ;; remap through active keymaps
165 this-command (or (command-remapping this-original-command)
166 this-original-command))
167 (run-hooks 'pre-command-hook)
168 (setq return-value (apply (car command) (cdr command)))
169 (run-hooks 'post-command-hook)
170 (when deferred-action-list
171 (run-hooks 'deferred-action-function))
172 (setq real-last-command (car command)
173 last-command this-command)
174 (when (boundp 'last-repeatable-command)
175 (setq last-repeatable-command real-last-command))
176 (when (and deactivate-mark transient-mark-mode) (deactivate-mark))
177 (assert (not unread-command-events) t)
178 return-value))
179
180(defun ert-run-idle-timers ()
181 "Run all idle timers (from `timer-idle-list')."
182 (dolist (timer (copy-sequence timer-idle-list))
183 (timer-event-handler timer)))
184
185
186;;; Miscellaneous utilities.
187
188(defun ert-filter-string (s &rest regexps)
189 "Return a copy of S with all matches of REGEXPS removed.
190
191Elements of REGEXPS may also be two-element lists \(REGEXP
192SUBEXP\), where SUBEXP is the number of a subexpression in
193REGEXP. In that case, only that subexpression will be removed
194rather than the entire match."
195 ;; Use a temporary buffer since replace-match copies strings, which
196 ;; would lead to N^2 runtime.
197 (with-temp-buffer
198 (insert s)
199 (dolist (x regexps)
200 (destructuring-bind (regexp subexp) (if (listp x) x `(,x nil))
201 (goto-char (point-min))
202 (while (re-search-forward regexp nil t)
203 (replace-match "" t t nil subexp))))
204 (buffer-string)))
205
206
207(defun ert-propertized-string (&rest args)
208 "Return a string with properties as specified by ARGS.
209
210ARGS is a list of strings and plists. The strings in ARGS are
211concatenated to produce an output string. In the output string,
212each string from ARGS will be have the preceding plist as its
213property list, or no properties if there is no plist before it.
214
215As a simple example,
216
217\(ert-propertized-string \"foo \" '(face italic) \"bar\" \" baz\" nil \
218\" quux\"\)
219
220would return the string \"foo bar baz quux\" where the substring
221\"bar baz\" has a `face' property with the value `italic'.
222
223None of the ARGS are modified, but the return value may share
224structure with the plists in ARGS."
225 (with-temp-buffer
226 (loop with current-plist = nil
227 for x in args do
228 (etypecase x
229 (string (let ((begin (point)))
230 (insert x)
231 (set-text-properties begin (point) current-plist)))
232 (list (unless (zerop (mod (length x) 2))
233 (error "Odd number of args in plist: %S" x))
234 (setq current-plist x))))
235 (buffer-string)))
236
237
238(defun ert-call-with-buffer-renamed (buffer-name thunk)
239 "Protect the buffer named BUFFER-NAME from side-effects and run THUNK.
240
241Renames the buffer BUFFER-NAME to a new temporary name, creates a
242new buffer named BUFFER-NAME, executes THUNK, kills the new
243buffer, and renames the original buffer back to BUFFER-NAME.
244
245This is useful if THUNK has undesirable side-effects on an Emacs
246buffer with a fixed name such as *Messages*."
247 (lexical-let ((new-buffer-name (generate-new-buffer-name
248 (format "%s orig buffer" buffer-name))))
249 (with-current-buffer (get-buffer-create buffer-name)
250 (rename-buffer new-buffer-name))
251 (unwind-protect
252 (progn
253 (get-buffer-create buffer-name)
254 (funcall thunk))
255 (when (get-buffer buffer-name)
256 (kill-buffer buffer-name))
257 (with-current-buffer new-buffer-name
258 (rename-buffer buffer-name)))))
259
260(defmacro* ert-with-buffer-renamed ((buffer-name-form) &body body)
261 "Protect the buffer named BUFFER-NAME from side-effects and run BODY.
262
263See `ert-call-with-buffer-renamed' for details."
264 (declare (indent 1))
265 `(ert-call-with-buffer-renamed ,buffer-name-form (lambda () ,@body)))
266
267
268(defun ert-buffer-string-reindented (&optional buffer)
269 "Return the contents of BUFFER after reindentation.
270
271BUFFER defaults to current buffer. Does not modify BUFFER."
272 (with-current-buffer (or buffer (current-buffer))
273 (let ((clone nil))
274 (unwind-protect
275 (progn
276 ;; `clone-buffer' doesn't work if `buffer-file-name' is non-nil.
277 (let ((buffer-file-name nil))
278 (setq clone (clone-buffer)))
279 (with-current-buffer clone
280 (let ((inhibit-read-only t))
281 (indent-region (point-min) (point-max)))
282 (buffer-string)))
283 (when clone
284 (let ((kill-buffer-query-functions nil))
285 (kill-buffer clone)))))))
286
287
288(provide 'ert-x)
289
290;;; ert-x.el ends here
diff --git a/lisp/emacs-lisp/ert.el b/lisp/emacs-lisp/ert.el
new file mode 100644
index 00000000000..6e543458df4
--- /dev/null
+++ b/lisp/emacs-lisp/ert.el
@@ -0,0 +1,2544 @@
1;;; ert.el --- Emacs Lisp Regression Testing
2
3;; Copyright (C) 2007, 2008, 2010, 2011 Free Software Foundation, Inc.
4
5;; Author: Christian Ohler <ohler@gnu.org>
6;; Keywords: lisp, tools
7
8;; This file is part of GNU Emacs.
9
10;; This program is free software: you can redistribute it and/or
11;; modify it under the terms of the GNU General Public License as
12;; published by the Free Software Foundation, either version 3 of the
13;; License, or (at your option) any later version.
14;;
15;; This program is distributed in the hope that it will be useful, but
16;; WITHOUT ANY WARRANTY; without even the implied warranty of
17;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18;; General Public License for more details.
19;;
20;; You should have received a copy of the GNU General Public License
21;; along with this program. If not, see `http://www.gnu.org/licenses/'.
22
23;;; Commentary:
24
25;; ERT is a tool for automated testing in Emacs Lisp. Its main
26;; features are facilities for defining and running test cases and
27;; reporting the results as well as for debugging test failures
28;; interactively.
29;;
30;; The main entry points are `ert-deftest', which is similar to
31;; `defun' but defines a test, and `ert-run-tests-interactively',
32;; which runs tests and offers an interactive interface for inspecting
33;; results and debugging. There is also
34;; `ert-run-tests-batch-and-exit' for non-interactive use.
35;;
36;; The body of `ert-deftest' forms resembles a function body, but the
37;; additional operators `should', `should-not' and `should-error' are
38;; available. `should' is similar to cl's `assert', but signals a
39;; different error when its condition is violated that is caught and
40;; processed by ERT. In addition, it analyzes its argument form and
41;; records information that helps debugging (`assert' tries to do
42;; something similar when its second argument SHOW-ARGS is true, but
43;; `should' is more sophisticated). For information on `should-not'
44;; and `should-error', see their docstrings.
45;;
46;; See ERT's info manual as well as the docstrings for more details.
47;; To compile the manual, run `makeinfo ert.texinfo' in the ERT
48;; directory, then C-u M-x info ert.info in Emacs to view it.
49;;
50;; To see some examples of tests written in ERT, see its self-tests in
51;; ert-tests.el. Some of these are tricky due to the bootstrapping
52;; problem of writing tests for a testing tool, others test simple
53;; functions and are straightforward.
54
55;;; Code:
56
57(eval-when-compile
58 (require 'cl))
59(require 'button)
60(require 'debug)
61(require 'easymenu)
62(require 'ewoc)
63(require 'find-func)
64(require 'help)
65
66
67;;; UI customization options.
68
69(defgroup ert ()
70 "ERT, the Emacs Lisp regression testing tool."
71 :prefix "ert-"
72 :group 'lisp)
73
74(defface ert-test-result-expected '((((class color) (background light))
75 :background "green1")
76 (((class color) (background dark))
77 :background "green3"))
78 "Face used for expected results in the ERT results buffer."
79 :group 'ert)
80
81(defface ert-test-result-unexpected '((((class color) (background light))
82 :background "red1")
83 (((class color) (background dark))
84 :background "red3"))
85 "Face used for unexpected results in the ERT results buffer."
86 :group 'ert)
87
88
89;;; Copies/reimplementations of cl functions.
90
91(defun ert--cl-do-remf (plist tag)
92 "Copy of `cl-do-remf'. Modify PLIST by removing TAG."
93 (let ((p (cdr plist)))
94 (while (and (cdr p) (not (eq (car (cdr p)) tag))) (setq p (cdr (cdr p))))
95 (and (cdr p) (progn (setcdr p (cdr (cdr (cdr p)))) t))))
96
97(defun ert--remprop (sym tag)
98 "Copy of `cl-remprop'. Modify SYM's plist by removing TAG."
99 (let ((plist (symbol-plist sym)))
100 (if (and plist (eq tag (car plist)))
101 (progn (setplist sym (cdr (cdr plist))) t)
102 (ert--cl-do-remf plist tag))))
103
104(defun ert--remove-if-not (ert-pred ert-list)
105 "A reimplementation of `remove-if-not'.
106
107ERT-PRED is a predicate, ERT-LIST is the input list."
108 (loop for ert-x in ert-list
109 if (funcall ert-pred ert-x)
110 collect ert-x))
111
112(defun ert--intersection (a b)
113 "A reimplementation of `intersection'. Intersect the sets A and B.
114
115Elements are compared using `eql'."
116 (loop for x in a
117 if (memql x b)
118 collect x))
119
120(defun ert--set-difference (a b)
121 "A reimplementation of `set-difference'. Subtract the set B from the set A.
122
123Elements are compared using `eql'."
124 (loop for x in a
125 unless (memql x b)
126 collect x))
127
128(defun ert--set-difference-eq (a b)
129 "A reimplementation of `set-difference'. Subtract the set B from the set A.
130
131Elements are compared using `eq'."
132 (loop for x in a
133 unless (memq x b)
134 collect x))
135
136(defun ert--union (a b)
137 "A reimplementation of `union'. Compute the union of the sets A and B.
138
139Elements are compared using `eql'."
140 (append a (ert--set-difference b a)))
141
142(eval-and-compile
143 (defvar ert--gensym-counter 0))
144
145(eval-and-compile
146 (defun ert--gensym (&optional prefix)
147 "Only allows string PREFIX, not compatible with CL."
148 (unless prefix (setq prefix "G"))
149 (make-symbol (format "%s%s"
150 prefix
151 (prog1 ert--gensym-counter
152 (incf ert--gensym-counter))))))
153
154(defun ert--coerce-to-vector (x)
155 "Coerce X to a vector."
156 (when (char-table-p x) (error "Not supported"))
157 (if (vectorp x)
158 x
159 (vconcat x)))
160
161(defun* ert--remove* (x list &key key test)
162 "Does not support all the keywords of remove*."
163 (unless key (setq key #'identity))
164 (unless test (setq test #'eql))
165 (loop for y in list
166 unless (funcall test x (funcall key y))
167 collect y))
168
169(defun ert--string-position (c s)
170 "Return the position of the first occurrence of C in S, or nil if none."
171 (loop for i from 0
172 for x across s
173 when (eql x c) return i))
174
175(defun ert--mismatch (a b)
176 "Return index of first element that differs between A and B.
177
178Like `mismatch'. Uses `equal' for comparison."
179 (cond ((or (listp a) (listp b))
180 (ert--mismatch (ert--coerce-to-vector a)
181 (ert--coerce-to-vector b)))
182 ((> (length a) (length b))
183 (ert--mismatch b a))
184 (t
185 (let ((la (length a))
186 (lb (length b)))
187 (assert (arrayp a) t)
188 (assert (arrayp b) t)
189 (assert (<= la lb) t)
190 (loop for i below la
191 when (not (equal (aref a i) (aref b i))) return i
192 finally (return (if (/= la lb)
193 la
194 (assert (equal a b) t)
195 nil)))))))
196
197(defun ert--subseq (seq start &optional end)
198 "Return a subsequence of SEQ from START to END."
199 (when (char-table-p seq) (error "Not supported"))
200 (let ((vector (substring (ert--coerce-to-vector seq) start end)))
201 (etypecase seq
202 (vector vector)
203 (string (concat vector))
204 (list (append vector nil))
205 (bool-vector (loop with result = (make-bool-vector (length vector) nil)
206 for i below (length vector) do
207 (setf (aref result i) (aref vector i))
208 finally (return result)))
209 (char-table (assert nil)))))
210
211(defun ert-equal-including-properties (a b)
212 "Return t if A and B have similar structure and contents.
213
214This is like `equal-including-properties' except that it compares
215the property values of text properties structurally (by
216recursing) rather than with `eq'. Perhaps this is what
217`equal-including-properties' should do in the first place; see
218Emacs bug 6581 at URL `http://debbugs.gnu.org/cgi/bugreport.cgi?bug=6581'."
219 ;; This implementation is inefficient. Rather than making it
220 ;; efficient, let's hope bug 6581 gets fixed so that we can delete
221 ;; it altogether.
222 (not (ert--explain-not-equal-including-properties a b)))
223
224
225;;; Defining and locating tests.
226
227;; The data structure that represents a test case.
228(defstruct ert-test
229 (name nil)
230 (documentation nil)
231 (body (assert nil))
232 (most-recent-result nil)
233 (expected-result-type ':passed)
234 (tags '()))
235
236(defun ert-test-boundp (symbol)
237 "Return non-nil if SYMBOL names a test."
238 (and (get symbol 'ert--test) t))
239
240(defun ert-get-test (symbol)
241 "If SYMBOL names a test, return that. Signal an error otherwise."
242 (unless (ert-test-boundp symbol) (error "No test named `%S'" symbol))
243 (get symbol 'ert--test))
244
245(defun ert-set-test (symbol definition)
246 "Make SYMBOL name the test DEFINITION, and return DEFINITION."
247 (when (eq symbol 'nil)
248 ;; We disallow nil since `ert-test-at-point' and related functions
249 ;; want to return a test name, but also need an out-of-band value
250 ;; on failure. Nil is the most natural out-of-band value; using 0
251 ;; or "" or signalling an error would be too awkward.
252 ;;
253 ;; Note that nil is still a valid value for the `name' slot in
254 ;; ert-test objects. It designates an anonymous test.
255 (error "Attempt to define a test named nil"))
256 (put symbol 'ert--test definition)
257 definition)
258
259(defun ert-make-test-unbound (symbol)
260 "Make SYMBOL name no test. Return SYMBOL."
261 (ert--remprop symbol 'ert--test)
262 symbol)
263
264(defun ert--parse-keys-and-body (keys-and-body)
265 "Split KEYS-AND-BODY into keyword-and-value pairs and the remaining body.
266
267KEYS-AND-BODY should have the form of a property list, with the
268exception that only keywords are permitted as keys and that the
269tail -- the body -- is a list of forms that does not start with a
270keyword.
271
272Returns a two-element list containing the keys-and-values plist
273and the body."
274 (let ((extracted-key-accu '())
275 (remaining keys-and-body))
276 (while (and (consp remaining) (keywordp (first remaining)))
277 (let ((keyword (pop remaining)))
278 (unless (consp remaining)
279 (error "Value expected after keyword %S in %S"
280 keyword keys-and-body))
281 (when (assoc keyword extracted-key-accu)
282 (warn "Keyword %S appears more than once in %S" keyword
283 keys-and-body))
284 (push (cons keyword (pop remaining)) extracted-key-accu)))
285 (setq extracted-key-accu (nreverse extracted-key-accu))
286 (list (loop for (key . value) in extracted-key-accu
287 collect key
288 collect value)
289 remaining)))
290
291;;;###autoload
292(defmacro* ert-deftest (name () &body docstring-keys-and-body)
293 "Define NAME (a symbol) as a test.
294
295BODY is evaluated as a `progn' when the test is run. It should
296signal a condition on failure or just return if the test passes.
297
298`should', `should-not' and `should-error' are useful for
299assertions in BODY.
300
301Use `ert' to run tests interactively.
302
303Tests that are expected to fail can be marked as such
304using :expected-result. See `ert-test-result-type-p' for a
305description of valid values for RESULT-TYPE.
306
307\(fn NAME () [DOCSTRING] [:expected-result RESULT-TYPE] \
308\[:tags '(TAG...)] BODY...)"
309 (declare (debug (&define :name test
310 name sexp [&optional stringp]
311 [&rest keywordp sexp] def-body))
312 (doc-string 3)
313 (indent 2))
314 (let ((documentation nil)
315 (documentation-supplied-p nil))
316 (when (stringp (first docstring-keys-and-body))
317 (setq documentation (pop docstring-keys-and-body)
318 documentation-supplied-p t))
319 (destructuring-bind ((&key (expected-result nil expected-result-supplied-p)
320 (tags nil tags-supplied-p))
321 body)
322 (ert--parse-keys-and-body docstring-keys-and-body)
323 `(progn
324 (ert-set-test ',name
325 (make-ert-test
326 :name ',name
327 ,@(when documentation-supplied-p
328 `(:documentation ,documentation))
329 ,@(when expected-result-supplied-p
330 `(:expected-result-type ,expected-result))
331 ,@(when tags-supplied-p
332 `(:tags ,tags))
333 :body (lambda () ,@body)))
334 ;; This hack allows `symbol-file' to associate `ert-deftest'
335 ;; forms with files, and therefore enables `find-function' to
336 ;; work with tests. However, it leads to warnings in
337 ;; `unload-feature', which doesn't know how to undefine tests
338 ;; and has no mechanism for extension.
339 (push '(ert-deftest . ,name) current-load-list)
340 ',name))))
341
342;; We use these `put' forms in addition to the (declare (indent)) in
343;; the defmacro form since the `declare' alone does not lead to
344;; correct indentation before the .el/.elc file is loaded.
345;; Autoloading these `put' forms solves this.
346;;;###autoload
347(progn
348 ;; TODO(ohler): Figure out what these mean and make sure they are correct.
349 (put 'ert-deftest 'lisp-indent-function 2)
350 (put 'ert-info 'lisp-indent-function 1))
351
352(defvar ert--find-test-regexp
353 (concat "^\\s-*(ert-deftest"
354 find-function-space-re
355 "%s\\(\\s-\\|$\\)")
356 "The regexp the `find-function' mechanisms use for finding test definitions.")
357
358
359(put 'ert-test-failed 'error-conditions '(error ert-test-failed))
360(put 'ert-test-failed 'error-message "Test failed")
361
362(defun ert-pass ()
363 "Terminate the current test and mark it passed. Does not return."
364 (throw 'ert--pass nil))
365
366(defun ert-fail (data)
367 "Terminate the current test and mark it failed. Does not return.
368DATA is displayed to the user and should state the reason of the failure."
369 (signal 'ert-test-failed (list data)))
370
371
372;;; The `should' macros.
373
374(defvar ert--should-execution-observer nil)
375
376(defun ert--signal-should-execution (form-description)
377 "Tell the current `should' form observer (if any) about FORM-DESCRIPTION."
378 (when ert--should-execution-observer
379 (funcall ert--should-execution-observer form-description)))
380
381(defun ert--special-operator-p (thing)
382 "Return non-nil if THING is a symbol naming a special operator."
383 (and (symbolp thing)
384 (let ((definition (indirect-function thing t)))
385 (and (subrp definition)
386 (eql (cdr (subr-arity definition)) 'unevalled)))))
387
388(defun ert--expand-should-1 (whole form inner-expander)
389 "Helper function for the `should' macro and its variants."
390 (let ((form
391 ;; If `cl-macroexpand' isn't bound, the code that we're
392 ;; compiling doesn't depend on cl and thus doesn't need an
393 ;; environment arg for `macroexpand'.
394 (if (fboundp 'cl-macroexpand)
395 ;; Suppress warning about run-time call to cl funtion: we
396 ;; only call it if it's fboundp.
397 (with-no-warnings
398 (cl-macroexpand form (and (boundp 'cl-macro-environment)
399 cl-macro-environment)))
400 (macroexpand form))))
401 (cond
402 ((or (atom form) (ert--special-operator-p (car form)))
403 (let ((value (ert--gensym "value-")))
404 `(let ((,value (ert--gensym "ert-form-evaluation-aborted-")))
405 ,(funcall inner-expander
406 `(setq ,value ,form)
407 `(list ',whole :form ',form :value ,value)
408 value)
409 ,value)))
410 (t
411 (let ((fn-name (car form))
412 (arg-forms (cdr form)))
413 (assert (or (symbolp fn-name)
414 (and (consp fn-name)
415 (eql (car fn-name) 'lambda)
416 (listp (cdr fn-name)))))
417 (let ((fn (ert--gensym "fn-"))
418 (args (ert--gensym "args-"))
419 (value (ert--gensym "value-"))
420 (default-value (ert--gensym "ert-form-evaluation-aborted-")))
421 `(let ((,fn (function ,fn-name))
422 (,args (list ,@arg-forms)))
423 (let ((,value ',default-value))
424 ,(funcall inner-expander
425 `(setq ,value (apply ,fn ,args))
426 `(nconc (list ',whole)
427 (list :form `(,,fn ,@,args))
428 (unless (eql ,value ',default-value)
429 (list :value ,value))
430 (let ((-explainer-
431 (and (symbolp ',fn-name)
432 (get ',fn-name 'ert-explainer))))
433 (when -explainer-
434 (list :explanation
435 (apply -explainer- ,args)))))
436 value)
437 ,value))))))))
438
439(defun ert--expand-should (whole form inner-expander)
440 "Helper function for the `should' macro and its variants.
441
442Analyzes FORM and returns an expression that has the same
443semantics under evaluation but records additional debugging
444information.
445
446INNER-EXPANDER should be a function and is called with two
447arguments: INNER-FORM and FORM-DESCRIPTION-FORM, where INNER-FORM
448is an expression equivalent to FORM, and FORM-DESCRIPTION-FORM is
449an expression that returns a description of FORM. INNER-EXPANDER
450should return code that calls INNER-FORM and performs the checks
451and error signalling specific to the particular variant of
452`should'. The code that INNER-EXPANDER returns must not call
453FORM-DESCRIPTION-FORM before it has called INNER-FORM."
454 (lexical-let ((inner-expander inner-expander))
455 (ert--expand-should-1
456 whole form
457 (lambda (inner-form form-description-form value-var)
458 (let ((form-description (ert--gensym "form-description-")))
459 `(let (,form-description)
460 ,(funcall inner-expander
461 `(unwind-protect
462 ,inner-form
463 (setq ,form-description ,form-description-form)
464 (ert--signal-should-execution ,form-description))
465 `,form-description
466 value-var)))))))
467
468(defmacro* should (form)
469 "Evaluate FORM. If it returns nil, abort the current test as failed.
470
471Returns the value of FORM."
472 (ert--expand-should `(should ,form) form
473 (lambda (inner-form form-description-form value-var)
474 `(unless ,inner-form
475 (ert-fail ,form-description-form)))))
476
477(defmacro* should-not (form)
478 "Evaluate FORM. If it returns non-nil, abort the current test as failed.
479
480Returns nil."
481 (ert--expand-should `(should-not ,form) form
482 (lambda (inner-form form-description-form value-var)
483 `(unless (not ,inner-form)
484 (ert-fail ,form-description-form)))))
485
486(defun ert--should-error-handle-error (form-description-fn
487 condition type exclude-subtypes)
488 "Helper function for `should-error'.
489
490Determines whether CONDITION matches TYPE and EXCLUDE-SUBTYPES,
491and aborts the current test as failed if it doesn't."
492 (let ((signalled-conditions (get (car condition) 'error-conditions))
493 (handled-conditions (etypecase type
494 (list type)
495 (symbol (list type)))))
496 (assert signalled-conditions)
497 (unless (ert--intersection signalled-conditions handled-conditions)
498 (ert-fail (append
499 (funcall form-description-fn)
500 (list
501 :condition condition
502 :fail-reason (concat "the error signalled did not"
503 " have the expected type")))))
504 (when exclude-subtypes
505 (unless (member (car condition) handled-conditions)
506 (ert-fail (append
507 (funcall form-description-fn)
508 (list
509 :condition condition
510 :fail-reason (concat "the error signalled was a subtype"
511 " of the expected type"))))))))
512
513;; FIXME: The expansion will evaluate the keyword args (if any) in
514;; nonstandard order.
515(defmacro* should-error (form &rest keys &key type exclude-subtypes)
516 "Evaluate FORM and check that it signals an error.
517
518The error signalled needs to match TYPE. TYPE should be a list
519of condition names. (It can also be a non-nil symbol, which is
520equivalent to a singleton list containing that symbol.) If
521EXCLUDE-SUBTYPES is nil, the error matches TYPE if one of its
522condition names is an element of TYPE. If EXCLUDE-SUBTYPES is
523non-nil, the error matches TYPE if it is an element of TYPE.
524
525If the error matches, returns (ERROR-SYMBOL . DATA) from the
526error. If not, or if no error was signalled, abort the test as
527failed."
528 (unless type (setq type ''error))
529 (ert--expand-should
530 `(should-error ,form ,@keys)
531 form
532 (lambda (inner-form form-description-form value-var)
533 (let ((errorp (ert--gensym "errorp"))
534 (form-description-fn (ert--gensym "form-description-fn-")))
535 `(let ((,errorp nil)
536 (,form-description-fn (lambda () ,form-description-form)))
537 (condition-case -condition-
538 ,inner-form
539 ;; We can't use ,type here because we want to evaluate it.
540 (error
541 (setq ,errorp t)
542 (ert--should-error-handle-error ,form-description-fn
543 -condition-
544 ,type ,exclude-subtypes)
545 (setq ,value-var -condition-)))
546 (unless ,errorp
547 (ert-fail (append
548 (funcall ,form-description-fn)
549 (list
550 :fail-reason "did not signal an error")))))))))
551
552
553;;; Explanation of `should' failures.
554
555;; TODO(ohler): Rework explanations so that they are displayed in a
556;; similar way to `ert-info' messages; in particular, allow text
557;; buttons in explanations that give more detail or open an ediff
558;; buffer. Perhaps explanations should be reported through `ert-info'
559;; rather than as part of the condition.
560
561(defun ert--proper-list-p (x)
562 "Return non-nil if X is a proper list, nil otherwise."
563 (loop
564 for firstp = t then nil
565 for fast = x then (cddr fast)
566 for slow = x then (cdr slow) do
567 (when (null fast) (return t))
568 (when (not (consp fast)) (return nil))
569 (when (null (cdr fast)) (return t))
570 (when (not (consp (cdr fast))) (return nil))
571 (when (and (not firstp) (eq fast slow)) (return nil))))
572
573(defun ert--explain-format-atom (x)
574 "Format the atom X for `ert--explain-not-equal'."
575 (typecase x
576 (fixnum (list x (format "#x%x" x) (format "?%c" x)))
577 (t x)))
578
579(defun ert--explain-not-equal (a b)
580 "Explainer function for `equal'.
581
582Returns a programmer-readable explanation of why A and B are not
583`equal', or nil if they are."
584 (if (not (equal (type-of a) (type-of b)))
585 `(different-types ,a ,b)
586 (etypecase a
587 (cons
588 (let ((a-proper-p (ert--proper-list-p a))
589 (b-proper-p (ert--proper-list-p b)))
590 (if (not (eql (not a-proper-p) (not b-proper-p)))
591 `(one-list-proper-one-improper ,a ,b)
592 (if a-proper-p
593 (if (not (equal (length a) (length b)))
594 `(proper-lists-of-different-length ,(length a) ,(length b)
595 ,a ,b
596 first-mismatch-at
597 ,(ert--mismatch a b))
598 (loop for i from 0
599 for ai in a
600 for bi in b
601 for xi = (ert--explain-not-equal ai bi)
602 do (when xi (return `(list-elt ,i ,xi)))
603 finally (assert (equal a b) t)))
604 (let ((car-x (ert--explain-not-equal (car a) (car b))))
605 (if car-x
606 `(car ,car-x)
607 (let ((cdr-x (ert--explain-not-equal (cdr a) (cdr b))))
608 (if cdr-x
609 `(cdr ,cdr-x)
610 (assert (equal a b) t)
611 nil))))))))
612 (array (if (not (equal (length a) (length b)))
613 `(arrays-of-different-length ,(length a) ,(length b)
614 ,a ,b
615 ,@(unless (char-table-p a)
616 `(first-mismatch-at
617 ,(ert--mismatch a b))))
618 (loop for i from 0
619 for ai across a
620 for bi across b
621 for xi = (ert--explain-not-equal ai bi)
622 do (when xi (return `(array-elt ,i ,xi)))
623 finally (assert (equal a b) t))))
624 (atom (if (not (equal a b))
625 (if (and (symbolp a) (symbolp b) (string= a b))
626 `(different-symbols-with-the-same-name ,a ,b)
627 `(different-atoms ,(ert--explain-format-atom a)
628 ,(ert--explain-format-atom b)))
629 nil)))))
630(put 'equal 'ert-explainer 'ert--explain-not-equal)
631
632(defun ert--significant-plist-keys (plist)
633 "Return the keys of PLIST that have non-null values, in order."
634 (assert (zerop (mod (length plist) 2)) t)
635 (loop for (key value . rest) on plist by #'cddr
636 unless (or (null value) (memq key accu)) collect key into accu
637 finally (return accu)))
638
639(defun ert--plist-difference-explanation (a b)
640 "Return a programmer-readable explanation of why A and B are different plists.
641
642Returns nil if they are equivalent, i.e., have the same value for
643each key, where absent values are treated as nil. The order of
644key/value pairs in each list does not matter."
645 (assert (zerop (mod (length a) 2)) t)
646 (assert (zerop (mod (length b) 2)) t)
647 ;; Normalizing the plists would be another way to do this but it
648 ;; requires a total ordering on all lisp objects (since any object
649 ;; is valid as a text property key). Perhaps defining such an
650 ;; ordering is useful in other contexts, too, but it's a lot of
651 ;; work, so let's punt on it for now.
652 (let* ((keys-a (ert--significant-plist-keys a))
653 (keys-b (ert--significant-plist-keys b))
654 (keys-in-a-not-in-b (ert--set-difference-eq keys-a keys-b))
655 (keys-in-b-not-in-a (ert--set-difference-eq keys-b keys-a)))
656 (flet ((explain-with-key (key)
657 (let ((value-a (plist-get a key))
658 (value-b (plist-get b key)))
659 (assert (not (equal value-a value-b)) t)
660 `(different-properties-for-key
661 ,key ,(ert--explain-not-equal-including-properties value-a
662 value-b)))))
663 (cond (keys-in-a-not-in-b
664 (explain-with-key (first keys-in-a-not-in-b)))
665 (keys-in-b-not-in-a
666 (explain-with-key (first keys-in-b-not-in-a)))
667 (t
668 (loop for key in keys-a
669 when (not (equal (plist-get a key) (plist-get b key)))
670 return (explain-with-key key)))))))
671
672(defun ert--abbreviate-string (s len suffixp)
673 "Shorten string S to at most LEN chars.
674
675If SUFFIXP is non-nil, returns a suffix of S, otherwise a prefix."
676 (let ((n (length s)))
677 (cond ((< n len)
678 s)
679 (suffixp
680 (substring s (- n len)))
681 (t
682 (substring s 0 len)))))
683
684(defun ert--explain-not-equal-including-properties (a b)
685 "Explainer function for `ert-equal-including-properties'.
686
687Returns a programmer-readable explanation of why A and B are not
688`ert-equal-including-properties', or nil if they are."
689 (if (not (equal a b))
690 (ert--explain-not-equal a b)
691 (assert (stringp a) t)
692 (assert (stringp b) t)
693 (assert (eql (length a) (length b)) t)
694 (loop for i from 0 to (length a)
695 for props-a = (text-properties-at i a)
696 for props-b = (text-properties-at i b)
697 for difference = (ert--plist-difference-explanation props-a props-b)
698 do (when difference
699 (return `(char ,i ,(substring-no-properties a i (1+ i))
700 ,difference
701 context-before
702 ,(ert--abbreviate-string
703 (substring-no-properties a 0 i)
704 10 t)
705 context-after
706 ,(ert--abbreviate-string
707 (substring-no-properties a (1+ i))
708 10 nil))))
709 ;; TODO(ohler): Get `equal-including-properties' fixed in
710 ;; Emacs, delete `ert-equal-including-properties', and
711 ;; re-enable this assertion.
712 ;;finally (assert (equal-including-properties a b) t)
713 )))
714(put 'ert-equal-including-properties
715 'ert-explainer
716 'ert--explain-not-equal-including-properties)
717
718
719;;; Implementation of `ert-info'.
720
721;; TODO(ohler): The name `info' clashes with
722;; `ert--test-execution-info'. One or both should be renamed.
723(defvar ert--infos '()
724 "The stack of `ert-info' infos that currently apply.
725
726Bound dynamically. This is a list of (PREFIX . MESSAGE) pairs.")
727
728(defmacro* ert-info ((message-form &key ((:prefix prefix-form) "Info: "))
729 &body body)
730 "Evaluate MESSAGE-FORM and BODY, and report the message if BODY fails.
731
732To be used within ERT tests. MESSAGE-FORM should evaluate to a
733string that will be displayed together with the test result if
734the test fails. PREFIX-FORM should evaluate to a string as well
735and is displayed in front of the value of MESSAGE-FORM."
736 (declare (debug ((form &rest [sexp form]) body))
737 (indent 1))
738 `(let ((ert--infos (cons (cons ,prefix-form ,message-form) ert--infos)))
739 ,@body))
740
741
742
743;;; Facilities for running a single test.
744
745(defvar ert-debug-on-error nil
746 "Non-nil means enter debugger when a test fails or terminates with an error.")
747
748;; The data structures that represent the result of running a test.
749(defstruct ert-test-result
750 (messages nil)
751 (should-forms nil)
752 )
753(defstruct (ert-test-passed (:include ert-test-result)))
754(defstruct (ert-test-result-with-condition (:include ert-test-result))
755 (condition (assert nil))
756 (backtrace (assert nil))
757 (infos (assert nil)))
758(defstruct (ert-test-quit (:include ert-test-result-with-condition)))
759(defstruct (ert-test-failed (:include ert-test-result-with-condition)))
760(defstruct (ert-test-aborted-with-non-local-exit (:include ert-test-result)))
761
762
763(defun ert--record-backtrace ()
764 "Record the current backtrace (as a list) and return it."
765 ;; Since the backtrace is stored in the result object, result
766 ;; objects must only be printed with appropriate limits
767 ;; (`print-level' and `print-length') in place. For interactive
768 ;; use, the cost of ensuring this possibly outweighs the advantage
769 ;; of storing the backtrace for
770 ;; `ert-results-pop-to-backtrace-for-test-at-point' given that we
771 ;; already have `ert-results-rerun-test-debugging-errors-at-point'.
772 ;; For batch use, however, printing the backtrace may be useful.
773 (loop
774 ;; 6 is the number of frames our own debugger adds (when
775 ;; compiled; more when interpreted). FIXME: Need to describe a
776 ;; procedure for determining this constant.
777 for i from 6
778 for frame = (backtrace-frame i)
779 while frame
780 collect frame))
781
782(defun ert--print-backtrace (backtrace)
783 "Format the backtrace BACKTRACE to the current buffer."
784 ;; This is essentially a reimplementation of Fbacktrace
785 ;; (src/eval.c), but for a saved backtrace, not the current one.
786 (let ((print-escape-newlines t)
787 (print-level 8)
788 (print-length 50))
789 (dolist (frame backtrace)
790 (ecase (first frame)
791 ((nil)
792 ;; Special operator.
793 (destructuring-bind (special-operator &rest arg-forms)
794 (cdr frame)
795 (insert
796 (format " %S\n" (list* special-operator arg-forms)))))
797 ((t)
798 ;; Function call.
799 (destructuring-bind (fn &rest args) (cdr frame)
800 (insert (format " %S(" fn))
801 (loop for firstp = t then nil
802 for arg in args do
803 (unless firstp
804 (insert " "))
805 (insert (format "%S" arg)))
806 (insert ")\n")))))))
807
808;; A container for the state of the execution of a single test and
809;; environment data needed during its execution.
810(defstruct ert--test-execution-info
811 (test (assert nil))
812 (result (assert nil))
813 ;; A thunk that may be called when RESULT has been set to its final
814 ;; value and test execution should be terminated. Should not
815 ;; return.
816 (exit-continuation (assert nil))
817 ;; The binding of `debugger' outside of the execution of the test.
818 next-debugger
819 ;; The binding of `ert-debug-on-error' that is in effect for the
820 ;; execution of the current test. We store it to avoid being
821 ;; affected by any new bindings the test itself may establish. (I
822 ;; don't remember whether this feature is important.)
823 ert-debug-on-error)
824
825(defun ert--run-test-debugger (info debugger-args)
826 "During a test run, `debugger' is bound to a closure that calls this function.
827
828This function records failures and errors and either terminates
829the test silently or calls the interactive debugger, as
830appropriate.
831
832INFO is the ert--test-execution-info corresponding to this test
833run. DEBUGGER-ARGS are the arguments to `debugger'."
834 (destructuring-bind (first-debugger-arg &rest more-debugger-args)
835 debugger-args
836 (ecase first-debugger-arg
837 ((lambda debug t exit nil)
838 (apply (ert--test-execution-info-next-debugger info) debugger-args))
839 (error
840 (let* ((condition (first more-debugger-args))
841 (type (case (car condition)
842 ((quit) 'quit)
843 (otherwise 'failed)))
844 (backtrace (ert--record-backtrace))
845 (infos (reverse ert--infos)))
846 (setf (ert--test-execution-info-result info)
847 (ecase type
848 (quit
849 (make-ert-test-quit :condition condition
850 :backtrace backtrace
851 :infos infos))
852 (failed
853 (make-ert-test-failed :condition condition
854 :backtrace backtrace
855 :infos infos))))
856 ;; Work around Emacs' heuristic (in eval.c) for detecting
857 ;; errors in the debugger.
858 (incf num-nonmacro-input-events)
859 ;; FIXME: We should probably implement more fine-grained
860 ;; control a la non-t `debug-on-error' here.
861 (cond
862 ((ert--test-execution-info-ert-debug-on-error info)
863 (apply (ert--test-execution-info-next-debugger info) debugger-args))
864 (t))
865 (funcall (ert--test-execution-info-exit-continuation info)))))))
866
867(defun ert--run-test-internal (ert-test-execution-info)
868 "Low-level function to run a test according to ERT-TEST-EXECUTION-INFO.
869
870This mainly sets up debugger-related bindings."
871 (lexical-let ((info ert-test-execution-info))
872 (setf (ert--test-execution-info-next-debugger info) debugger
873 (ert--test-execution-info-ert-debug-on-error info) ert-debug-on-error)
874 (catch 'ert--pass
875 ;; For now, each test gets its own temp buffer and its own
876 ;; window excursion, just to be safe. If this turns out to be
877 ;; too expensive, we can remove it.
878 (with-temp-buffer
879 (save-window-excursion
880 (let ((debugger (lambda (&rest debugger-args)
881 (ert--run-test-debugger info debugger-args)))
882 (debug-on-error t)
883 (debug-on-quit t)
884 ;; FIXME: Do we need to store the old binding of this
885 ;; and consider it in `ert--run-test-debugger'?
886 (debug-ignored-errors nil)
887 (ert--infos '()))
888 (funcall (ert-test-body (ert--test-execution-info-test info))))))
889 (ert-pass))
890 (setf (ert--test-execution-info-result info) (make-ert-test-passed)))
891 nil)
892
893(defun ert--force-message-log-buffer-truncation ()
894 "Immediately truncate *Messages* buffer according to `message-log-max'.
895
896This can be useful after reducing the value of `message-log-max'."
897 (with-current-buffer (get-buffer-create "*Messages*")
898 ;; This is a reimplementation of this part of message_dolog() in xdisp.c:
899 ;; if (NATNUMP (Vmessage_log_max))
900 ;; {
901 ;; scan_newline (Z, Z_BYTE, BEG, BEG_BYTE,
902 ;; -XFASTINT (Vmessage_log_max) - 1, 0);
903 ;; del_range_both (BEG, BEG_BYTE, PT, PT_BYTE, 0);
904 ;; }
905 (when (and (integerp message-log-max) (>= message-log-max 0))
906 (let ((begin (point-min))
907 (end (save-excursion
908 (goto-char (point-max))
909 (forward-line (- message-log-max))
910 (point))))
911 (delete-region begin end)))))
912
913(defvar ert--running-tests nil
914 "List of tests that are currently in execution.
915
916This list is empty while no test is running, has one element
917while a test is running, two elements while a test run from
918inside a test is running, etc. The list is in order of nesting,
919innermost test first.
920
921The elements are of type `ert-test'.")
922
923(defun ert-run-test (ert-test)
924 "Run ERT-TEST.
925
926Returns the result and stores it in ERT-TEST's `most-recent-result' slot."
927 (setf (ert-test-most-recent-result ert-test) nil)
928 (block error
929 (lexical-let ((begin-marker
930 (with-current-buffer (get-buffer-create "*Messages*")
931 (set-marker (make-marker) (point-max)))))
932 (unwind-protect
933 (lexical-let ((info (make-ert--test-execution-info
934 :test ert-test
935 :result
936 (make-ert-test-aborted-with-non-local-exit)
937 :exit-continuation (lambda ()
938 (return-from error nil))))
939 (should-form-accu (list)))
940 (unwind-protect
941 (let ((ert--should-execution-observer
942 (lambda (form-description)
943 (push form-description should-form-accu)))
944 (message-log-max t)
945 (ert--running-tests (cons ert-test ert--running-tests)))
946 (ert--run-test-internal info))
947 (let ((result (ert--test-execution-info-result info)))
948 (setf (ert-test-result-messages result)
949 (with-current-buffer (get-buffer-create "*Messages*")
950 (buffer-substring begin-marker (point-max))))
951 (ert--force-message-log-buffer-truncation)
952 (setq should-form-accu (nreverse should-form-accu))
953 (setf (ert-test-result-should-forms result)
954 should-form-accu)
955 (setf (ert-test-most-recent-result ert-test) result))))
956 (set-marker begin-marker nil))))
957 (ert-test-most-recent-result ert-test))
958
959(defun ert-running-test ()
960 "Return the top-level test currently executing."
961 (car (last ert--running-tests)))
962
963
964;;; Test selectors.
965
966(defun ert-test-result-type-p (result result-type)
967 "Return non-nil if RESULT matches type RESULT-TYPE.
968
969Valid result types:
970
971nil -- Never matches.
972t -- Always matches.
973:failed, :passed -- Matches corresponding results.
974\(and TYPES...\) -- Matches if all TYPES match.
975\(or TYPES...\) -- Matches if some TYPES match.
976\(not TYPE\) -- Matches if TYPE does not match.
977\(satisfies PREDICATE\) -- Matches if PREDICATE returns true when called with
978 RESULT."
979 ;; It would be easy to add `member' and `eql' types etc., but I
980 ;; haven't bothered yet.
981 (etypecase result-type
982 ((member nil) nil)
983 ((member t) t)
984 ((member :failed) (ert-test-failed-p result))
985 ((member :passed) (ert-test-passed-p result))
986 (cons
987 (destructuring-bind (operator &rest operands) result-type
988 (ecase operator
989 (and
990 (case (length operands)
991 (0 t)
992 (t
993 (and (ert-test-result-type-p result (first operands))
994 (ert-test-result-type-p result `(and ,@(rest operands)))))))
995 (or
996 (case (length operands)
997 (0 nil)
998 (t
999 (or (ert-test-result-type-p result (first operands))
1000 (ert-test-result-type-p result `(or ,@(rest operands)))))))
1001 (not
1002 (assert (eql (length operands) 1))
1003 (not (ert-test-result-type-p result (first operands))))
1004 (satisfies
1005 (assert (eql (length operands) 1))
1006 (funcall (first operands) result)))))))
1007
1008(defun ert-test-result-expected-p (test result)
1009 "Return non-nil if TEST's expected result type matches RESULT."
1010 (ert-test-result-type-p result (ert-test-expected-result-type test)))
1011
1012(defun ert-select-tests (selector universe)
1013 "Return the tests that match SELECTOR.
1014
1015UNIVERSE specifies the set of tests to select from; it should be
1016a list of tests, or t, which refers to all tests named by symbols
1017in `obarray'.
1018
1019Returns the set of tests as a list.
1020
1021Valid selectors:
1022
1023nil -- Selects the empty set.
1024t -- Selects UNIVERSE.
1025:new -- Selects all tests that have not been run yet.
1026:failed, :passed -- Select tests according to their most recent result.
1027:expected, :unexpected -- Select tests according to their most recent result.
1028a string -- Selects all tests that have a name that matches the string,
1029 a regexp.
1030a test -- Selects that test.
1031a symbol -- Selects the test that the symbol names, errors if none.
1032\(member TESTS...\) -- Selects TESTS, a list of tests or symbols naming tests.
1033\(eql TEST\) -- Selects TEST, a test or a symbol naming a test.
1034\(and SELECTORS...\) -- Selects the tests that match all SELECTORS.
1035\(or SELECTORS...\) -- Selects the tests that match any SELECTOR.
1036\(not SELECTOR\) -- Selects all tests that do not match SELECTOR.
1037\(tag TAG) -- Selects all tests that have TAG on their tags list.
1038\(satisfies PREDICATE\) -- Selects all tests that satisfy PREDICATE.
1039
1040Only selectors that require a superset of tests, such
1041as (satisfies ...), strings, :new, etc. make use of UNIVERSE.
1042Selectors that do not, such as \(member ...\), just return the
1043set implied by them without checking whether it is really
1044contained in UNIVERSE."
1045 ;; This code needs to match the etypecase in
1046 ;; `ert-insert-human-readable-selector'.
1047 (etypecase selector
1048 ((member nil) nil)
1049 ((member t) (etypecase universe
1050 (list universe)
1051 ((member t) (ert-select-tests "" universe))))
1052 ((member :new) (ert-select-tests
1053 `(satisfies ,(lambda (test)
1054 (null (ert-test-most-recent-result test))))
1055 universe))
1056 ((member :failed) (ert-select-tests
1057 `(satisfies ,(lambda (test)
1058 (ert-test-result-type-p
1059 (ert-test-most-recent-result test)
1060 ':failed)))
1061 universe))
1062 ((member :passed) (ert-select-tests
1063 `(satisfies ,(lambda (test)
1064 (ert-test-result-type-p
1065 (ert-test-most-recent-result test)
1066 ':passed)))
1067 universe))
1068 ((member :expected) (ert-select-tests
1069 `(satisfies
1070 ,(lambda (test)
1071 (ert-test-result-expected-p
1072 test
1073 (ert-test-most-recent-result test))))
1074 universe))
1075 ((member :unexpected) (ert-select-tests `(not :expected) universe))
1076 (string
1077 (etypecase universe
1078 ((member t) (mapcar #'ert-get-test
1079 (apropos-internal selector #'ert-test-boundp)))
1080 (list (ert--remove-if-not (lambda (test)
1081 (and (ert-test-name test)
1082 (string-match selector
1083 (ert-test-name test))))
1084 universe))))
1085 (ert-test (list selector))
1086 (symbol
1087 (assert (ert-test-boundp selector))
1088 (list (ert-get-test selector)))
1089 (cons
1090 (destructuring-bind (operator &rest operands) selector
1091 (ecase operator
1092 (member
1093 (mapcar (lambda (purported-test)
1094 (etypecase purported-test
1095 (symbol (assert (ert-test-boundp purported-test))
1096 (ert-get-test purported-test))
1097 (ert-test purported-test)))
1098 operands))
1099 (eql
1100 (assert (eql (length operands) 1))
1101 (ert-select-tests `(member ,@operands) universe))
1102 (and
1103 ;; Do these definitions of AND, NOT and OR satisfy de
1104 ;; Morgan's laws? Should they?
1105 (case (length operands)
1106 (0 (ert-select-tests 't universe))
1107 (t (ert-select-tests `(and ,@(rest operands))
1108 (ert-select-tests (first operands)
1109 universe)))))
1110 (not
1111 (assert (eql (length operands) 1))
1112 (let ((all-tests (ert-select-tests 't universe)))
1113 (ert--set-difference all-tests
1114 (ert-select-tests (first operands)
1115 all-tests))))
1116 (or
1117 (case (length operands)
1118 (0 (ert-select-tests 'nil universe))
1119 (t (ert--union (ert-select-tests (first operands) universe)
1120 (ert-select-tests `(or ,@(rest operands))
1121 universe)))))
1122 (tag
1123 (assert (eql (length operands) 1))
1124 (let ((tag (first operands)))
1125 (ert-select-tests `(satisfies
1126 ,(lambda (test)
1127 (member tag (ert-test-tags test))))
1128 universe)))
1129 (satisfies
1130 (assert (eql (length operands) 1))
1131 (ert--remove-if-not (first operands)
1132 (ert-select-tests 't universe))))))))
1133
1134(defun ert--insert-human-readable-selector (selector)
1135 "Insert a human-readable presentation of SELECTOR into the current buffer."
1136 ;; This is needed to avoid printing the (huge) contents of the
1137 ;; `backtrace' slot of the result objects in the
1138 ;; `most-recent-result' slots of test case objects in (eql ...) or
1139 ;; (member ...) selectors.
1140 (labels ((rec (selector)
1141 ;; This code needs to match the etypecase in `ert-select-tests'.
1142 (etypecase selector
1143 ((or (member nil t
1144 :new :failed :passed
1145 :expected :unexpected)
1146 string
1147 symbol)
1148 selector)
1149 (ert-test
1150 (if (ert-test-name selector)
1151 (make-symbol (format "<%S>" (ert-test-name selector)))
1152 (make-symbol "<unnamed test>")))
1153 (cons
1154 (destructuring-bind (operator &rest operands) selector
1155 (ecase operator
1156 ((member eql and not or)
1157 `(,operator ,@(mapcar #'rec operands)))
1158 ((member tag satisfies)
1159 selector)))))))
1160 (insert (format "%S" (rec selector)))))
1161
1162
1163;;; Facilities for running a whole set of tests.
1164
1165;; The data structure that contains the set of tests being executed
1166;; during one particular test run, their results, the state of the
1167;; execution, and some statistics.
1168;;
1169;; The data about results and expected results of tests may seem
1170;; redundant here, since the test objects also carry such information.
1171;; However, the information in the test objects may be more recent, it
1172;; may correspond to a different test run. We need the information
1173;; that corresponds to this run in order to be able to update the
1174;; statistics correctly when a test is re-run interactively and has a
1175;; different result than before.
1176(defstruct ert--stats
1177 (selector (assert nil))
1178 ;; The tests, in order.
1179 (tests (assert nil) :type vector)
1180 ;; A map of test names (or the test objects themselves for unnamed
1181 ;; tests) to indices into the `tests' vector.
1182 (test-map (assert nil) :type hash-table)
1183 ;; The results of the tests during this run, in order.
1184 (test-results (assert nil) :type vector)
1185 ;; The start times of the tests, in order, as reported by
1186 ;; `current-time'.
1187 (test-start-times (assert nil) :type vector)
1188 ;; The end times of the tests, in order, as reported by
1189 ;; `current-time'.
1190 (test-end-times (assert nil) :type vector)
1191 (passed-expected 0)
1192 (passed-unexpected 0)
1193 (failed-expected 0)
1194 (failed-unexpected 0)
1195 (start-time nil)
1196 (end-time nil)
1197 (aborted-p nil)
1198 (current-test nil)
1199 ;; The time at or after which the next redisplay should occur, as a
1200 ;; float.
1201 (next-redisplay 0.0))
1202
1203(defun ert-stats-completed-expected (stats)
1204 "Return the number of tests in STATS that had expected results."
1205 (+ (ert--stats-passed-expected stats)
1206 (ert--stats-failed-expected stats)))
1207
1208(defun ert-stats-completed-unexpected (stats)
1209 "Return the number of tests in STATS that had unexpected results."
1210 (+ (ert--stats-passed-unexpected stats)
1211 (ert--stats-failed-unexpected stats)))
1212
1213(defun ert-stats-completed (stats)
1214 "Number of tests in STATS that have run so far."
1215 (+ (ert-stats-completed-expected stats)
1216 (ert-stats-completed-unexpected stats)))
1217
1218(defun ert-stats-total (stats)
1219 "Number of tests in STATS, regardless of whether they have run yet."
1220 (length (ert--stats-tests stats)))
1221
1222;; The stats object of the current run, dynamically bound. This is
1223;; used for the mode line progress indicator.
1224(defvar ert--current-run-stats nil)
1225
1226(defun ert--stats-test-key (test)
1227 "Return the key used for TEST in the test map of ert--stats objects.
1228
1229Returns the name of TEST if it has one, or TEST itself otherwise."
1230 (or (ert-test-name test) test))
1231
1232(defun ert--stats-set-test-and-result (stats pos test result)
1233 "Change STATS by replacing the test at position POS with TEST and RESULT.
1234
1235Also changes the counters in STATS to match."
1236 (let* ((tests (ert--stats-tests stats))
1237 (results (ert--stats-test-results stats))
1238 (old-test (aref tests pos))
1239 (map (ert--stats-test-map stats)))
1240 (flet ((update (d)
1241 (if (ert-test-result-expected-p (aref tests pos)
1242 (aref results pos))
1243 (etypecase (aref results pos)
1244 (ert-test-passed (incf (ert--stats-passed-expected stats) d))
1245 (ert-test-failed (incf (ert--stats-failed-expected stats) d))
1246 (null)
1247 (ert-test-aborted-with-non-local-exit))
1248 (etypecase (aref results pos)
1249 (ert-test-passed (incf (ert--stats-passed-unexpected stats) d))
1250 (ert-test-failed (incf (ert--stats-failed-unexpected stats) d))
1251 (null)
1252 (ert-test-aborted-with-non-local-exit)))))
1253 ;; Adjust counters to remove the result that is currently in stats.
1254 (update -1)
1255 ;; Put new test and result into stats.
1256 (setf (aref tests pos) test
1257 (aref results pos) result)
1258 (remhash (ert--stats-test-key old-test) map)
1259 (setf (gethash (ert--stats-test-key test) map) pos)
1260 ;; Adjust counters to match new result.
1261 (update +1)
1262 nil)))
1263
1264(defun ert--make-stats (tests selector)
1265 "Create a new `ert--stats' object for running TESTS.
1266
1267SELECTOR is the selector that was used to select TESTS."
1268 (setq tests (ert--coerce-to-vector tests))
1269 (let ((map (make-hash-table :size (length tests))))
1270 (loop for i from 0
1271 for test across tests
1272 for key = (ert--stats-test-key test) do
1273 (assert (not (gethash key map)))
1274 (setf (gethash key map) i))
1275 (make-ert--stats :selector selector
1276 :tests tests
1277 :test-map map
1278 :test-results (make-vector (length tests) nil)
1279 :test-start-times (make-vector (length tests) nil)
1280 :test-end-times (make-vector (length tests) nil))))
1281
1282(defun ert-run-or-rerun-test (stats test listener)
1283 ;; checkdoc-order: nil
1284 "Run the single test TEST and record the result using STATS and LISTENER."
1285 (let ((ert--current-run-stats stats)
1286 (pos (ert--stats-test-pos stats test)))
1287 (ert--stats-set-test-and-result stats pos test nil)
1288 ;; Call listener after setting/before resetting
1289 ;; (ert--stats-current-test stats); the listener might refresh the
1290 ;; mode line display, and if the value is not set yet/any more
1291 ;; during this refresh, the mode line will flicker unnecessarily.
1292 (setf (ert--stats-current-test stats) test)
1293 (funcall listener 'test-started stats test)
1294 (setf (ert-test-most-recent-result test) nil)
1295 (setf (aref (ert--stats-test-start-times stats) pos) (current-time))
1296 (unwind-protect
1297 (ert-run-test test)
1298 (setf (aref (ert--stats-test-end-times stats) pos) (current-time))
1299 (let ((result (ert-test-most-recent-result test)))
1300 (ert--stats-set-test-and-result stats pos test result)
1301 (funcall listener 'test-ended stats test result))
1302 (setf (ert--stats-current-test stats) nil))))
1303
1304(defun ert-run-tests (selector listener)
1305 "Run the tests specified by SELECTOR, sending progress updates to LISTENER."
1306 (let* ((tests (ert-select-tests selector t))
1307 (stats (ert--make-stats tests selector)))
1308 (setf (ert--stats-start-time stats) (current-time))
1309 (funcall listener 'run-started stats)
1310 (let ((abortedp t))
1311 (unwind-protect
1312 (let ((ert--current-run-stats stats))
1313 (force-mode-line-update)
1314 (unwind-protect
1315 (progn
1316 (loop for test in tests do
1317 (ert-run-or-rerun-test stats test listener))
1318 (setq abortedp nil))
1319 (setf (ert--stats-aborted-p stats) abortedp)
1320 (setf (ert--stats-end-time stats) (current-time))
1321 (funcall listener 'run-ended stats abortedp)))
1322 (force-mode-line-update))
1323 stats)))
1324
1325(defun ert--stats-test-pos (stats test)
1326 ;; checkdoc-order: nil
1327 "Return the position (index) of TEST in the run represented by STATS."
1328 (gethash (ert--stats-test-key test) (ert--stats-test-map stats)))
1329
1330
1331;;; Formatting functions shared across UIs.
1332
1333(defun ert--format-time-iso8601 (time)
1334 "Format TIME in the variant of ISO 8601 used for timestamps in ERT."
1335 (format-time-string "%Y-%m-%d %T%z" time))
1336
1337(defun ert-char-for-test-result (result expectedp)
1338 "Return a character that represents the test result RESULT.
1339
1340EXPECTEDP specifies whether the result was expected."
1341 (let ((s (etypecase result
1342 (ert-test-passed ".P")
1343 (ert-test-failed "fF")
1344 (null "--")
1345 (ert-test-aborted-with-non-local-exit "aA"))))
1346 (elt s (if expectedp 0 1))))
1347
1348(defun ert-string-for-test-result (result expectedp)
1349 "Return a string that represents the test result RESULT.
1350
1351EXPECTEDP specifies whether the result was expected."
1352 (let ((s (etypecase result
1353 (ert-test-passed '("passed" "PASSED"))
1354 (ert-test-failed '("failed" "FAILED"))
1355 (null '("unknown" "UNKNOWN"))
1356 (ert-test-aborted-with-non-local-exit '("aborted" "ABORTED")))))
1357 (elt s (if expectedp 0 1))))
1358
1359(defun ert--pp-with-indentation-and-newline (object)
1360 "Pretty-print OBJECT, indenting it to the current column of point.
1361Ensures a final newline is inserted."
1362 (let ((begin (point)))
1363 (pp object (current-buffer))
1364 (unless (bolp) (insert "\n"))
1365 (save-excursion
1366 (goto-char begin)
1367 (indent-sexp))))
1368
1369(defun ert--insert-infos (result)
1370 "Insert `ert-info' infos from RESULT into current buffer.
1371
1372RESULT must be an `ert-test-result-with-condition'."
1373 (check-type result ert-test-result-with-condition)
1374 (dolist (info (ert-test-result-with-condition-infos result))
1375 (destructuring-bind (prefix . message) info
1376 (let ((begin (point))
1377 (indentation (make-string (+ (length prefix) 4) ?\s))
1378 (end nil))
1379 (unwind-protect
1380 (progn
1381 (insert message "\n")
1382 (setq end (copy-marker (point)))
1383 (goto-char begin)
1384 (insert " " prefix)
1385 (forward-line 1)
1386 (while (< (point) end)
1387 (insert indentation)
1388 (forward-line 1)))
1389 (when end (set-marker end nil)))))))
1390
1391
1392;;; Running tests in batch mode.
1393
1394(defvar ert-batch-backtrace-right-margin 70
1395 "*The maximum line length for printing backtraces in `ert-run-tests-batch'.")
1396
1397;;;###autoload
1398(defun ert-run-tests-batch (&optional selector)
1399 "Run the tests specified by SELECTOR, printing results to the terminal.
1400
1401SELECTOR works as described in `ert-select-tests', except if
1402SELECTOR is nil, in which case all tests rather than none will be
1403run; this makes the command line \"emacs -batch -l my-tests.el -f
1404ert-run-tests-batch-and-exit\" useful.
1405
1406Returns the stats object."
1407 (unless selector (setq selector 't))
1408 (ert-run-tests
1409 selector
1410 (lambda (event-type &rest event-args)
1411 (ecase event-type
1412 (run-started
1413 (destructuring-bind (stats) event-args
1414 (message "Running %s tests (%s)"
1415 (length (ert--stats-tests stats))
1416 (ert--format-time-iso8601 (ert--stats-start-time stats)))))
1417 (run-ended
1418 (destructuring-bind (stats abortedp) event-args
1419 (let ((unexpected (ert-stats-completed-unexpected stats))
1420 (expected-failures (ert--stats-failed-expected stats)))
1421 (message "\n%sRan %s tests, %s results as expected%s (%s)%s\n"
1422 (if (not abortedp)
1423 ""
1424 "Aborted: ")
1425 (ert-stats-total stats)
1426 (ert-stats-completed-expected stats)
1427 (if (zerop unexpected)
1428 ""
1429 (format ", %s unexpected" unexpected))
1430 (ert--format-time-iso8601 (ert--stats-end-time stats))
1431 (if (zerop expected-failures)
1432 ""
1433 (format "\n%s expected failures" expected-failures)))
1434 (unless (zerop unexpected)
1435 (message "%s unexpected results:" unexpected)
1436 (loop for test across (ert--stats-tests stats)
1437 for result = (ert-test-most-recent-result test) do
1438 (when (not (ert-test-result-expected-p test result))
1439 (message "%9s %S"
1440 (ert-string-for-test-result result nil)
1441 (ert-test-name test))))
1442 (message "%s" "")))))
1443 (test-started
1444 )
1445 (test-ended
1446 (destructuring-bind (stats test result) event-args
1447 (unless (ert-test-result-expected-p test result)
1448 (etypecase result
1449 (ert-test-passed
1450 (message "Test %S passed unexpectedly" (ert-test-name test)))
1451 (ert-test-result-with-condition
1452 (message "Test %S backtrace:" (ert-test-name test))
1453 (with-temp-buffer
1454 (ert--print-backtrace (ert-test-result-with-condition-backtrace
1455 result))
1456 (goto-char (point-min))
1457 (while (not (eobp))
1458 (let ((start (point))
1459 (end (progn (end-of-line) (point))))
1460 (setq end (min end
1461 (+ start ert-batch-backtrace-right-margin)))
1462 (message "%s" (buffer-substring-no-properties
1463 start end)))
1464 (forward-line 1)))
1465 (with-temp-buffer
1466 (ert--insert-infos result)
1467 (insert " ")
1468 (let ((print-escape-newlines t)
1469 (print-level 5)
1470 (print-length 10))
1471 (let ((begin (point)))
1472 (ert--pp-with-indentation-and-newline
1473 (ert-test-result-with-condition-condition result))))
1474 (goto-char (1- (point-max)))
1475 (assert (looking-at "\n"))
1476 (delete-char 1)
1477 (message "Test %S condition:" (ert-test-name test))
1478 (message "%s" (buffer-string))))
1479 (ert-test-aborted-with-non-local-exit
1480 (message "Test %S aborted with non-local exit"
1481 (ert-test-name test)))))
1482 (let* ((max (prin1-to-string (length (ert--stats-tests stats))))
1483 (format-string (concat "%9s %"
1484 (prin1-to-string (length max))
1485 "s/" max " %S")))
1486 (message format-string
1487 (ert-string-for-test-result result
1488 (ert-test-result-expected-p
1489 test result))
1490 (1+ (ert--stats-test-pos stats test))
1491 (ert-test-name test)))))))))
1492
1493;;;###autoload
1494(defun ert-run-tests-batch-and-exit (&optional selector)
1495 "Like `ert-run-tests-batch', but exits Emacs when done.
1496
1497The exit status will be 0 if all test results were as expected, 1
1498on unexpected results, or 2 if the tool detected an error outside
1499of the tests (e.g. invalid SELECTOR or bug in the code that runs
1500the tests)."
1501 (unwind-protect
1502 (let ((stats (ert-run-tests-batch selector)))
1503 (kill-emacs (if (zerop (ert-stats-completed-unexpected stats)) 0 1)))
1504 (unwind-protect
1505 (progn
1506 (message "Error running tests")
1507 (backtrace))
1508 (kill-emacs 2))))
1509
1510
1511;;; Utility functions for load/unload actions.
1512
1513(defun ert--activate-font-lock-keywords ()
1514 "Activate font-lock keywords for some of ERT's symbols."
1515 (font-lock-add-keywords
1516 nil
1517 '(("(\\(\\<ert-deftest\\)\\>\\s *\\(\\sw+\\)?"
1518 (1 font-lock-keyword-face nil t)
1519 (2 font-lock-function-name-face nil t)))))
1520
1521(defun* ert--remove-from-list (list-var element &key key test)
1522 "Remove ELEMENT from the value of LIST-VAR if present.
1523
1524This can be used as an inverse of `add-to-list'."
1525 (unless key (setq key #'identity))
1526 (unless test (setq test #'equal))
1527 (setf (symbol-value list-var)
1528 (ert--remove* element
1529 (symbol-value list-var)
1530 :key key
1531 :test test)))
1532
1533
1534;;; Some basic interactive functions.
1535
1536(defun ert-read-test-name (prompt &optional default history
1537 add-default-to-prompt)
1538 "Read the name of a test and return it as a symbol.
1539
1540Prompt with PROMPT. If DEFAULT is a valid test name, use it as a
1541default. HISTORY is the history to use; see `completing-read'.
1542If ADD-DEFAULT-TO-PROMPT is non-nil, PROMPT will be modified to
1543include the default, if any.
1544
1545Signals an error if no test name was read."
1546 (etypecase default
1547 (string (let ((symbol (intern-soft default)))
1548 (unless (and symbol (ert-test-boundp symbol))
1549 (setq default nil))))
1550 (symbol (setq default
1551 (if (ert-test-boundp default)
1552 (symbol-name default)
1553 nil)))
1554 (ert-test (setq default (ert-test-name default))))
1555 (when add-default-to-prompt
1556 (setq prompt (if (null default)
1557 (format "%s: " prompt)
1558 (format "%s (default %s): " prompt default))))
1559 (let ((input (completing-read prompt obarray #'ert-test-boundp
1560 t nil history default nil)))
1561 ;; completing-read returns an empty string if default was nil and
1562 ;; the user just hit enter.
1563 (let ((sym (intern-soft input)))
1564 (if (ert-test-boundp sym)
1565 sym
1566 (error "Input does not name a test")))))
1567
1568(defun ert-read-test-name-at-point (prompt)
1569 "Read the name of a test and return it as a symbol.
1570As a default, use the symbol at point, or the test at point if in
1571the ERT results buffer. Prompt with PROMPT, augmented with the
1572default (if any)."
1573 (ert-read-test-name prompt (ert-test-at-point) nil t))
1574
1575(defun ert-find-test-other-window (test-name)
1576 "Find, in another window, the definition of TEST-NAME."
1577 (interactive (list (ert-read-test-name-at-point "Find test definition: ")))
1578 (find-function-do-it test-name 'ert-deftest 'switch-to-buffer-other-window))
1579
1580(defun ert-delete-test (test-name)
1581 "Make the test TEST-NAME unbound.
1582
1583Nothing more than an interactive interface to `ert-make-test-unbound'."
1584 (interactive (list (ert-read-test-name-at-point "Delete test")))
1585 (ert-make-test-unbound test-name))
1586
1587(defun ert-delete-all-tests ()
1588 "Make all symbols in `obarray' name no test."
1589 (interactive)
1590 (when (interactive-p)
1591 (unless (y-or-n-p "Delete all tests? ")
1592 (error "Aborted")))
1593 ;; We can't use `ert-select-tests' here since that gives us only
1594 ;; test objects, and going from them back to the test name symbols
1595 ;; can fail if the `ert-test' defstruct has been redefined.
1596 (mapc #'ert-make-test-unbound (apropos-internal "" #'ert-test-boundp))
1597 t)
1598
1599
1600;;; Display of test progress and results.
1601
1602;; An entry in the results buffer ewoc. There is one entry per test.
1603(defstruct ert--ewoc-entry
1604 (test (assert nil))
1605 ;; If the result of this test was expected, its ewoc entry is hidden
1606 ;; initially.
1607 (hidden-p (assert nil))
1608 ;; An ewoc entry may be collapsed to hide details such as the error
1609 ;; condition.
1610 ;;
1611 ;; I'm not sure the ability to expand and collapse entries is still
1612 ;; a useful feature.
1613 (expanded-p t)
1614 ;; By default, the ewoc entry presents the error condition with
1615 ;; certain limits on how much to print (`print-level',
1616 ;; `print-length'). The user can interactively switch to a set of
1617 ;; higher limits.
1618 (extended-printer-limits-p nil))
1619
1620;; Variables local to the results buffer.
1621
1622;; The ewoc.
1623(defvar ert--results-ewoc)
1624;; The stats object.
1625(defvar ert--results-stats)
1626;; A string with one character per test. Each character represents
1627;; the result of the corresponding test. The string is displayed near
1628;; the top of the buffer and serves as a progress bar.
1629(defvar ert--results-progress-bar-string)
1630;; The position where the progress bar button begins.
1631(defvar ert--results-progress-bar-button-begin)
1632;; The test result listener that updates the buffer when tests are run.
1633(defvar ert--results-listener)
1634
1635(defun ert-insert-test-name-button (test-name)
1636 "Insert a button that links to TEST-NAME."
1637 (insert-text-button (format "%S" test-name)
1638 :type 'ert--test-name-button
1639 'ert-test-name test-name))
1640
1641(defun ert--results-format-expected-unexpected (expected unexpected)
1642 "Return a string indicating EXPECTED expected results, UNEXPECTED unexpected."
1643 (if (zerop unexpected)
1644 (format "%s" expected)
1645 (format "%s (%s unexpected)" (+ expected unexpected) unexpected)))
1646
1647(defun ert--results-update-ewoc-hf (ewoc stats)
1648 "Update the header and footer of EWOC to show certain information from STATS.
1649
1650Also sets `ert--results-progress-bar-button-begin'."
1651 (let ((run-count (ert-stats-completed stats))
1652 (results-buffer (current-buffer))
1653 ;; Need to save buffer-local value.
1654 (font-lock font-lock-mode))
1655 (ewoc-set-hf
1656 ewoc
1657 ;; header
1658 (with-temp-buffer
1659 (insert "Selector: ")
1660 (ert--insert-human-readable-selector (ert--stats-selector stats))
1661 (insert "\n")
1662 (insert
1663 (format (concat "Passed: %s\n"
1664 "Failed: %s\n"
1665 "Total: %s/%s\n\n")
1666 (ert--results-format-expected-unexpected
1667 (ert--stats-passed-expected stats)
1668 (ert--stats-passed-unexpected stats))
1669 (ert--results-format-expected-unexpected
1670 (ert--stats-failed-expected stats)
1671 (ert--stats-failed-unexpected stats))
1672 run-count
1673 (ert-stats-total stats)))
1674 (insert
1675 (format "Started at: %s\n"
1676 (ert--format-time-iso8601 (ert--stats-start-time stats))))
1677 ;; FIXME: This is ugly. Need to properly define invariants of
1678 ;; the `stats' data structure.
1679 (let ((state (cond ((ert--stats-aborted-p stats) 'aborted)
1680 ((ert--stats-current-test stats) 'running)
1681 ((ert--stats-end-time stats) 'finished)
1682 (t 'preparing))))
1683 (ecase state
1684 (preparing
1685 (insert ""))
1686 (aborted
1687 (cond ((ert--stats-current-test stats)
1688 (insert "Aborted during test: ")
1689 (ert-insert-test-name-button
1690 (ert-test-name (ert--stats-current-test stats))))
1691 (t
1692 (insert "Aborted."))))
1693 (running
1694 (assert (ert--stats-current-test stats))
1695 (insert "Running test: ")
1696 (ert-insert-test-name-button (ert-test-name
1697 (ert--stats-current-test stats))))
1698 (finished
1699 (assert (not (ert--stats-current-test stats)))
1700 (insert "Finished.")))
1701 (insert "\n")
1702 (if (ert--stats-end-time stats)
1703 (insert
1704 (format "%s%s\n"
1705 (if (ert--stats-aborted-p stats)
1706 "Aborted at: "
1707 "Finished at: ")
1708 (ert--format-time-iso8601 (ert--stats-end-time stats))))
1709 (insert "\n"))
1710 (insert "\n"))
1711 (let ((progress-bar-string (with-current-buffer results-buffer
1712 ert--results-progress-bar-string)))
1713 (let ((progress-bar-button-begin
1714 (insert-text-button progress-bar-string
1715 :type 'ert--results-progress-bar-button
1716 'face (or (and font-lock
1717 (ert-face-for-stats stats))
1718 'button))))
1719 ;; The header gets copied verbatim to the results buffer,
1720 ;; and all positions remain the same, so
1721 ;; `progress-bar-button-begin' will be the right position
1722 ;; even in the results buffer.
1723 (with-current-buffer results-buffer
1724 (set (make-local-variable 'ert--results-progress-bar-button-begin)
1725 progress-bar-button-begin))))
1726 (insert "\n\n")
1727 (buffer-string))
1728 ;; footer
1729 ;;
1730 ;; We actually want an empty footer, but that would trigger a bug
1731 ;; in ewoc, sometimes clearing the entire buffer. (It's possible
1732 ;; that this bug has been fixed since this has been tested; we
1733 ;; should test it again.)
1734 "\n")))
1735
1736
1737(defvar ert-test-run-redisplay-interval-secs .1
1738 "How many seconds ERT should wait between redisplays while running tests.
1739
1740While running tests, ERT shows the current progress, and this variable
1741determines how frequently the progress display is updated.")
1742
1743(defun ert--results-update-stats-display (ewoc stats)
1744 "Update EWOC and the mode line to show data from STATS."
1745 ;; TODO(ohler): investigate using `make-progress-reporter'.
1746 (ert--results-update-ewoc-hf ewoc stats)
1747 (force-mode-line-update)
1748 (redisplay t)
1749 (setf (ert--stats-next-redisplay stats)
1750 (+ (float-time) ert-test-run-redisplay-interval-secs)))
1751
1752(defun ert--results-update-stats-display-maybe (ewoc stats)
1753 "Call `ert--results-update-stats-display' if not called recently.
1754
1755EWOC and STATS are arguments for `ert--results-update-stats-display'."
1756 (when (>= (float-time) (ert--stats-next-redisplay stats))
1757 (ert--results-update-stats-display ewoc stats)))
1758
1759(defun ert--tests-running-mode-line-indicator ()
1760 "Return a string for the mode line that shows the test run progress."
1761 (let* ((stats ert--current-run-stats)
1762 (tests-total (ert-stats-total stats))
1763 (tests-completed (ert-stats-completed stats)))
1764 (if (>= tests-completed tests-total)
1765 (format " ERT(%s/%s,finished)" tests-completed tests-total)
1766 (format " ERT(%s/%s):%s"
1767 (1+ tests-completed)
1768 tests-total
1769 (if (null (ert--stats-current-test stats))
1770 "?"
1771 (format "%S"
1772 (ert-test-name (ert--stats-current-test stats))))))))
1773
1774(defun ert--make-xrefs-region (begin end)
1775 "Attach cross-references to function names between BEGIN and END.
1776
1777BEGIN and END specify a region in the current buffer."
1778 (save-excursion
1779 (save-restriction
1780 (narrow-to-region begin (point))
1781 ;; Inhibit optimization in `debugger-make-xrefs' that would
1782 ;; sometimes insert unrelated backtrace info into our buffer.
1783 (let ((debugger-previous-backtrace nil))
1784 (debugger-make-xrefs)))))
1785
1786(defun ert--string-first-line (s)
1787 "Return the first line of S, or S if it contains no newlines.
1788
1789The return value does not include the line terminator."
1790 (substring s 0 (ert--string-position ?\n s)))
1791
1792(defun ert-face-for-test-result (expectedp)
1793 "Return a face that shows whether a test result was expected or unexpected.
1794
1795If EXPECTEDP is nil, returns the face for unexpected results; if
1796non-nil, returns the face for expected results.."
1797 (if expectedp 'ert-test-result-expected 'ert-test-result-unexpected))
1798
1799(defun ert-face-for-stats (stats)
1800 "Return a face that represents STATS."
1801 (cond ((ert--stats-aborted-p stats) 'nil)
1802 ((plusp (ert-stats-completed-unexpected stats))
1803 (ert-face-for-test-result nil))
1804 ((eql (ert-stats-completed-expected stats) (ert-stats-total stats))
1805 (ert-face-for-test-result t))
1806 (t 'nil)))
1807
1808(defun ert--print-test-for-ewoc (entry)
1809 "The ewoc print function for ewoc test entries. ENTRY is the entry to print."
1810 (let* ((test (ert--ewoc-entry-test entry))
1811 (stats ert--results-stats)
1812 (result (let ((pos (ert--stats-test-pos stats test)))
1813 (assert pos)
1814 (aref (ert--stats-test-results stats) pos)))
1815 (hiddenp (ert--ewoc-entry-hidden-p entry))
1816 (expandedp (ert--ewoc-entry-expanded-p entry))
1817 (extended-printer-limits-p (ert--ewoc-entry-extended-printer-limits-p
1818 entry)))
1819 (cond (hiddenp)
1820 (t
1821 (let ((expectedp (ert-test-result-expected-p test result)))
1822 (insert-text-button (format "%c" (ert-char-for-test-result
1823 result expectedp))
1824 :type 'ert--results-expand-collapse-button
1825 'face (or (and font-lock-mode
1826 (ert-face-for-test-result
1827 expectedp))
1828 'button)))
1829 (insert " ")
1830 (ert-insert-test-name-button (ert-test-name test))
1831 (insert "\n")
1832 (when (and expandedp (not (eql result 'nil)))
1833 (when (ert-test-documentation test)
1834 (insert " "
1835 (propertize
1836 (ert--string-first-line (ert-test-documentation test))
1837 'font-lock-face 'font-lock-doc-face)
1838 "\n"))
1839 (etypecase result
1840 (ert-test-passed
1841 (if (ert-test-result-expected-p test result)
1842 (insert " passed\n")
1843 (insert " passed unexpectedly\n"))
1844 (insert ""))
1845 (ert-test-result-with-condition
1846 (ert--insert-infos result)
1847 (let ((print-escape-newlines t)
1848 (print-level (if extended-printer-limits-p 12 6))
1849 (print-length (if extended-printer-limits-p 100 10)))
1850 (insert " ")
1851 (let ((begin (point)))
1852 (ert--pp-with-indentation-and-newline
1853 (ert-test-result-with-condition-condition result))
1854 (ert--make-xrefs-region begin (point)))))
1855 (ert-test-aborted-with-non-local-exit
1856 (insert " aborted\n")))
1857 (insert "\n")))))
1858 nil)
1859
1860(defun ert--results-font-lock-function (enabledp)
1861 "Redraw the ERT results buffer after font-lock-mode was switched on or off.
1862
1863ENABLEDP is true if font-lock-mode is switched on, false
1864otherwise."
1865 (ert--results-update-ewoc-hf ert--results-ewoc ert--results-stats)
1866 (ewoc-refresh ert--results-ewoc)
1867 (font-lock-default-function enabledp))
1868
1869(defun ert--setup-results-buffer (stats listener buffer-name)
1870 "Set up a test results buffer.
1871
1872STATS is the stats object; LISTENER is the results listener;
1873BUFFER-NAME, if non-nil, is the buffer name to use."
1874 (unless buffer-name (setq buffer-name "*ert*"))
1875 (let ((buffer (get-buffer-create buffer-name)))
1876 (with-current-buffer buffer
1877 (setq buffer-read-only t)
1878 (let ((inhibit-read-only t))
1879 (buffer-disable-undo)
1880 (erase-buffer)
1881 (ert-results-mode)
1882 ;; Erase buffer again in case switching out of the previous
1883 ;; mode inserted anything. (This happens e.g. when switching
1884 ;; from ert-results-mode to ert-results-mode when
1885 ;; font-lock-mode turns itself off in change-major-mode-hook.)
1886 (erase-buffer)
1887 (set (make-local-variable 'font-lock-function)
1888 'ert--results-font-lock-function)
1889 (let ((ewoc (ewoc-create 'ert--print-test-for-ewoc nil nil t)))
1890 (set (make-local-variable 'ert--results-ewoc) ewoc)
1891 (set (make-local-variable 'ert--results-stats) stats)
1892 (set (make-local-variable 'ert--results-progress-bar-string)
1893 (make-string (ert-stats-total stats)
1894 (ert-char-for-test-result nil t)))
1895 (set (make-local-variable 'ert--results-listener) listener)
1896 (loop for test across (ert--stats-tests stats) do
1897 (ewoc-enter-last ewoc
1898 (make-ert--ewoc-entry :test test :hidden-p t)))
1899 (ert--results-update-ewoc-hf ert--results-ewoc ert--results-stats)
1900 (goto-char (1- (point-max)))
1901 buffer)))))
1902
1903
1904(defvar ert--selector-history nil
1905 "List of recent test selectors read from terminal.")
1906
1907;; Should OUTPUT-BUFFER-NAME and MESSAGE-FN really be arguments here?
1908;; They are needed only for our automated self-tests at the moment.
1909;; Or should there be some other mechanism?
1910;;;###autoload
1911(defun ert-run-tests-interactively (selector
1912 &optional output-buffer-name message-fn)
1913 "Run the tests specified by SELECTOR and display the results in a buffer.
1914
1915SELECTOR works as described in `ert-select-tests'.
1916OUTPUT-BUFFER-NAME and MESSAGE-FN should normally be nil; they
1917are used for automated self-tests and specify which buffer to use
1918and how to display message."
1919 (interactive
1920 (list (let ((default (if ert--selector-history
1921 ;; Can't use `first' here as this form is
1922 ;; not compiled, and `first' is not
1923 ;; defined without cl.
1924 (car ert--selector-history)
1925 "t")))
1926 (read-from-minibuffer (if (null default)
1927 "Run tests: "
1928 (format "Run tests (default %s): " default))
1929 nil nil t 'ert--selector-history
1930 default nil))
1931 nil))
1932 (unless message-fn (setq message-fn 'message))
1933 (lexical-let ((output-buffer-name output-buffer-name)
1934 buffer
1935 listener
1936 (message-fn message-fn))
1937 (setq listener
1938 (lambda (event-type &rest event-args)
1939 (ecase event-type
1940 (run-started
1941 (destructuring-bind (stats) event-args
1942 (setq buffer (ert--setup-results-buffer stats
1943 listener
1944 output-buffer-name))
1945 (pop-to-buffer buffer)))
1946 (run-ended
1947 (destructuring-bind (stats abortedp) event-args
1948 (funcall message-fn
1949 "%sRan %s tests, %s results were as expected%s"
1950 (if (not abortedp)
1951 ""
1952 "Aborted: ")
1953 (ert-stats-total stats)
1954 (ert-stats-completed-expected stats)
1955 (let ((unexpected
1956 (ert-stats-completed-unexpected stats)))
1957 (if (zerop unexpected)
1958 ""
1959 (format ", %s unexpected" unexpected))))
1960 (ert--results-update-stats-display (with-current-buffer buffer
1961 ert--results-ewoc)
1962 stats)))
1963 (test-started
1964 (destructuring-bind (stats test) event-args
1965 (with-current-buffer buffer
1966 (let* ((ewoc ert--results-ewoc)
1967 (pos (ert--stats-test-pos stats test))
1968 (node (ewoc-nth ewoc pos)))
1969 (assert node)
1970 (setf (ert--ewoc-entry-test (ewoc-data node)) test)
1971 (aset ert--results-progress-bar-string pos
1972 (ert-char-for-test-result nil t))
1973 (ert--results-update-stats-display-maybe ewoc stats)
1974 (ewoc-invalidate ewoc node)))))
1975 (test-ended
1976 (destructuring-bind (stats test result) event-args
1977 (with-current-buffer buffer
1978 (let* ((ewoc ert--results-ewoc)
1979 (pos (ert--stats-test-pos stats test))
1980 (node (ewoc-nth ewoc pos)))
1981 (when (ert--ewoc-entry-hidden-p (ewoc-data node))
1982 (setf (ert--ewoc-entry-hidden-p (ewoc-data node))
1983 (ert-test-result-expected-p test result)))
1984 (aset ert--results-progress-bar-string pos
1985 (ert-char-for-test-result result
1986 (ert-test-result-expected-p
1987 test result)))
1988 (ert--results-update-stats-display-maybe ewoc stats)
1989 (ewoc-invalidate ewoc node))))))))
1990 (ert-run-tests
1991 selector
1992 listener)))
1993;;;###autoload
1994(defalias 'ert 'ert-run-tests-interactively)
1995
1996
1997;;; Simple view mode for auxiliary information like stack traces or
1998;;; messages. Mainly binds "q" for quit.
1999
2000(define-derived-mode ert-simple-view-mode fundamental-mode "ERT-View"
2001 "Major mode for viewing auxiliary information in ERT.")
2002
2003(loop for (key binding) in
2004 '(("q" quit-window)
2005 )
2006 do
2007 (define-key ert-simple-view-mode-map key binding))
2008
2009
2010;;; Commands and button actions for the results buffer.
2011
2012(define-derived-mode ert-results-mode fundamental-mode "ERT-Results"
2013 "Major mode for viewing results of ERT test runs.")
2014
2015(loop for (key binding) in
2016 '(;; Stuff that's not in the menu.
2017 ("\t" forward-button)
2018 ([backtab] backward-button)
2019 ("j" ert-results-jump-between-summary-and-result)
2020 ("q" quit-window)
2021 ("L" ert-results-toggle-printer-limits-for-test-at-point)
2022 ("n" ert-results-next-test)
2023 ("p" ert-results-previous-test)
2024 ;; Stuff that is in the menu.
2025 ("R" ert-results-rerun-all-tests)
2026 ("r" ert-results-rerun-test-at-point)
2027 ("d" ert-results-rerun-test-at-point-debugging-errors)
2028 ("." ert-results-find-test-at-point-other-window)
2029 ("b" ert-results-pop-to-backtrace-for-test-at-point)
2030 ("m" ert-results-pop-to-messages-for-test-at-point)
2031 ("l" ert-results-pop-to-should-forms-for-test-at-point)
2032 ("h" ert-results-describe-test-at-point)
2033 ("D" ert-delete-test)
2034 ("T" ert-results-pop-to-timings)
2035 )
2036 do
2037 (define-key ert-results-mode-map key binding))
2038
2039(easy-menu-define ert-results-mode-menu ert-results-mode-map
2040 "Menu for `ert-results-mode'."
2041 '("ERT Results"
2042 ["Re-run all tests" ert-results-rerun-all-tests]
2043 "--"
2044 ["Re-run test" ert-results-rerun-test-at-point]
2045 ["Debug test" ert-results-rerun-test-at-point-debugging-errors]
2046 ["Show test definition" ert-results-find-test-at-point-other-window]
2047 "--"
2048 ["Show backtrace" ert-results-pop-to-backtrace-for-test-at-point]
2049 ["Show messages" ert-results-pop-to-messages-for-test-at-point]
2050 ["Show `should' forms" ert-results-pop-to-should-forms-for-test-at-point]
2051 ["Describe test" ert-results-describe-test-at-point]
2052 "--"
2053 ["Delete test" ert-delete-test]
2054 "--"
2055 ["Show execution time of each test" ert-results-pop-to-timings]
2056 ))
2057
2058(define-button-type 'ert--results-progress-bar-button
2059 'action #'ert--results-progress-bar-button-action
2060 'help-echo "mouse-2, RET: Reveal test result")
2061
2062(define-button-type 'ert--test-name-button
2063 'action #'ert--test-name-button-action
2064 'help-echo "mouse-2, RET: Find test definition")
2065
2066(define-button-type 'ert--results-expand-collapse-button
2067 'action #'ert--results-expand-collapse-button-action
2068 'help-echo "mouse-2, RET: Expand/collapse test result")
2069
2070(defun ert--results-test-node-or-null-at-point ()
2071 "If point is on a valid ewoc node, return it; return nil otherwise.
2072
2073To be used in the ERT results buffer."
2074 (let* ((ewoc ert--results-ewoc)
2075 (node (ewoc-locate ewoc)))
2076 ;; `ewoc-locate' will return an arbitrary node when point is on
2077 ;; header or footer, or when all nodes are invisible. So we need
2078 ;; to validate its return value here.
2079 ;;
2080 ;; Update: I'm seeing nil being returned in some cases now,
2081 ;; perhaps this has been changed?
2082 (if (and node
2083 (>= (point) (ewoc-location node))
2084 (not (ert--ewoc-entry-hidden-p (ewoc-data node))))
2085 node
2086 nil)))
2087
2088(defun ert--results-test-node-at-point ()
2089 "If point is on a valid ewoc node, return it; signal an error otherwise.
2090
2091To be used in the ERT results buffer."
2092 (or (ert--results-test-node-or-null-at-point)
2093 (error "No test at point")))
2094
2095(defun ert-results-next-test ()
2096 "Move point to the next test.
2097
2098To be used in the ERT results buffer."
2099 (interactive)
2100 (ert--results-move (ewoc-locate ert--results-ewoc) 'ewoc-next
2101 "No tests below"))
2102
2103(defun ert-results-previous-test ()
2104 "Move point to the previous test.
2105
2106To be used in the ERT results buffer."
2107 (interactive)
2108 (ert--results-move (ewoc-locate ert--results-ewoc) 'ewoc-prev
2109 "No tests above"))
2110
2111(defun ert--results-move (node ewoc-fn error-message)
2112 "Move point from NODE to the previous or next node.
2113
2114EWOC-FN specifies the direction and should be either `ewoc-prev'
2115or `ewoc-next'. If there are no more nodes in that direction, an
2116error is signalled with the message ERROR-MESSAGE."
2117 (loop
2118 (setq node (funcall ewoc-fn ert--results-ewoc node))
2119 (when (null node)
2120 (error "%s" error-message))
2121 (unless (ert--ewoc-entry-hidden-p (ewoc-data node))
2122 (goto-char (ewoc-location node))
2123 (return))))
2124
2125(defun ert--results-expand-collapse-button-action (button)
2126 "Expand or collapse the test node BUTTON belongs to."
2127 (let* ((ewoc ert--results-ewoc)
2128 (node (save-excursion
2129 (goto-char (ert--button-action-position))
2130 (ert--results-test-node-at-point)))
2131 (entry (ewoc-data node)))
2132 (setf (ert--ewoc-entry-expanded-p entry)
2133 (not (ert--ewoc-entry-expanded-p entry)))
2134 (ewoc-invalidate ewoc node)))
2135
2136(defun ert-results-find-test-at-point-other-window ()
2137 "Find the definition of the test at point in another window.
2138
2139To be used in the ERT results buffer."
2140 (interactive)
2141 (let ((name (ert-test-at-point)))
2142 (unless name
2143 (error "No test at point"))
2144 (ert-find-test-other-window name)))
2145
2146(defun ert--test-name-button-action (button)
2147 "Find the definition of the test BUTTON belongs to, in another window."
2148 (let ((name (button-get button 'ert-test-name)))
2149 (ert-find-test-other-window name)))
2150
2151(defun ert--ewoc-position (ewoc node)
2152 ;; checkdoc-order: nil
2153 "Return the position of NODE in EWOC, or nil if NODE is not in EWOC."
2154 (loop for i from 0
2155 for node-here = (ewoc-nth ewoc 0) then (ewoc-next ewoc node-here)
2156 do (when (eql node node-here)
2157 (return i))
2158 finally (return nil)))
2159
2160(defun ert-results-jump-between-summary-and-result ()
2161 "Jump back and forth between the test run summary and individual test results.
2162
2163From an ewoc node, jumps to the character that represents the
2164same test in the progress bar, and vice versa.
2165
2166To be used in the ERT results buffer."
2167 ;; Maybe this command isn't actually needed much, but if it is, it
2168 ;; seems like an indication that the UI design is not optimal. If
2169 ;; jumping back and forth between a summary at the top of the buffer
2170 ;; and the error log in the remainder of the buffer is useful, then
2171 ;; the summary apparently needs to be easily accessible from the
2172 ;; error log, and perhaps it would be better to have it in a
2173 ;; separate buffer to keep it visible.
2174 (interactive)
2175 (let ((ewoc ert--results-ewoc)
2176 (progress-bar-begin ert--results-progress-bar-button-begin))
2177 (cond ((ert--results-test-node-or-null-at-point)
2178 (let* ((node (ert--results-test-node-at-point))
2179 (pos (ert--ewoc-position ewoc node)))
2180 (goto-char (+ progress-bar-begin pos))))
2181 ((and (<= progress-bar-begin (point))
2182 (< (point) (button-end (button-at progress-bar-begin))))
2183 (let* ((node (ewoc-nth ewoc (- (point) progress-bar-begin)))
2184 (entry (ewoc-data node)))
2185 (when (ert--ewoc-entry-hidden-p entry)
2186 (setf (ert--ewoc-entry-hidden-p entry) nil)
2187 (ewoc-invalidate ewoc node))
2188 (ewoc-goto-node ewoc node)))
2189 (t
2190 (goto-char progress-bar-begin)))))
2191
2192(defun ert-test-at-point ()
2193 "Return the name of the test at point as a symbol, or nil if none."
2194 (or (and (eql major-mode 'ert-results-mode)
2195 (let ((test (ert--results-test-at-point-no-redefinition)))
2196 (and test (ert-test-name test))))
2197 (let* ((thing (thing-at-point 'symbol))
2198 (sym (intern-soft thing)))
2199 (and (ert-test-boundp sym)
2200 sym))))
2201
2202(defun ert--results-test-at-point-no-redefinition ()
2203 "Return the test at point, or nil.
2204
2205To be used in the ERT results buffer."
2206 (assert (eql major-mode 'ert-results-mode))
2207 (if (ert--results-test-node-or-null-at-point)
2208 (let* ((node (ert--results-test-node-at-point))
2209 (test (ert--ewoc-entry-test (ewoc-data node))))
2210 test)
2211 (let ((progress-bar-begin ert--results-progress-bar-button-begin))
2212 (when (and (<= progress-bar-begin (point))
2213 (< (point) (button-end (button-at progress-bar-begin))))
2214 (let* ((test-index (- (point) progress-bar-begin))
2215 (test (aref (ert--stats-tests ert--results-stats)
2216 test-index)))
2217 test)))))
2218
2219(defun ert--results-test-at-point-allow-redefinition ()
2220 "Look up the test at point, and check whether it has been redefined.
2221
2222To be used in the ERT results buffer.
2223
2224Returns a list of two elements: the test (or nil) and a symbol
2225specifying whether the test has been redefined.
2226
2227If a new test has been defined with the same name as the test at
2228point, replaces the test at point with the new test, and returns
2229the new test and the symbol `redefined'.
2230
2231If the test has been deleted, returns the old test and the symbol
2232`deleted'.
2233
2234If the test is still current, returns the test and the symbol nil.
2235
2236If there is no test at point, returns a list with two nils."
2237 (let ((test (ert--results-test-at-point-no-redefinition)))
2238 (cond ((null test)
2239 `(nil nil))
2240 ((null (ert-test-name test))
2241 `(,test nil))
2242 (t
2243 (let* ((name (ert-test-name test))
2244 (new-test (and (ert-test-boundp name)
2245 (ert-get-test name))))
2246 (cond ((eql test new-test)
2247 `(,test nil))
2248 ((null new-test)
2249 `(,test deleted))
2250 (t
2251 (ert--results-update-after-test-redefinition
2252 (ert--stats-test-pos ert--results-stats test)
2253 new-test)
2254 `(,new-test redefined))))))))
2255
2256(defun ert--results-update-after-test-redefinition (pos new-test)
2257 "Update results buffer after the test at pos POS has been redefined.
2258
2259Also updates the stats object. NEW-TEST is the new test
2260definition."
2261 (let* ((stats ert--results-stats)
2262 (ewoc ert--results-ewoc)
2263 (node (ewoc-nth ewoc pos))
2264 (entry (ewoc-data node)))
2265 (ert--stats-set-test-and-result stats pos new-test nil)
2266 (setf (ert--ewoc-entry-test entry) new-test
2267 (aref ert--results-progress-bar-string pos) (ert-char-for-test-result
2268 nil t))
2269 (ewoc-invalidate ewoc node))
2270 nil)
2271
2272(defun ert--button-action-position ()
2273 "The buffer position where the last button action was triggered."
2274 (cond ((integerp last-command-event)
2275 (point))
2276 ((eventp last-command-event)
2277 (posn-point (event-start last-command-event)))
2278 (t (assert nil))))
2279
2280(defun ert--results-progress-bar-button-action (button)
2281 "Jump to details for the test represented by the character clicked in BUTTON."
2282 (goto-char (ert--button-action-position))
2283 (ert-results-jump-between-summary-and-result))
2284
2285(defun ert-results-rerun-all-tests ()
2286 "Re-run all tests, using the same selector.
2287
2288To be used in the ERT results buffer."
2289 (interactive)
2290 (assert (eql major-mode 'ert-results-mode))
2291 (let ((selector (ert--stats-selector ert--results-stats)))
2292 (ert-run-tests-interactively selector (buffer-name))))
2293
2294(defun ert-results-rerun-test-at-point ()
2295 "Re-run the test at point.
2296
2297To be used in the ERT results buffer."
2298 (interactive)
2299 (destructuring-bind (test redefinition-state)
2300 (ert--results-test-at-point-allow-redefinition)
2301 (when (null test)
2302 (error "No test at point"))
2303 (let* ((stats ert--results-stats)
2304 (progress-message (format "Running %stest %S"
2305 (ecase redefinition-state
2306 ((nil) "")
2307 (redefined "new definition of ")
2308 (deleted "deleted "))
2309 (ert-test-name test))))
2310 ;; Need to save and restore point manually here: When point is on
2311 ;; the first visible ewoc entry while the header is updated, point
2312 ;; moves to the top of the buffer. This is undesirable, and a
2313 ;; simple `save-excursion' doesn't prevent it.
2314 (let ((point (point)))
2315 (unwind-protect
2316 (unwind-protect
2317 (progn
2318 (message "%s..." progress-message)
2319 (ert-run-or-rerun-test stats test
2320 ert--results-listener))
2321 (ert--results-update-stats-display ert--results-ewoc stats)
2322 (message "%s...%s"
2323 progress-message
2324 (let ((result (ert-test-most-recent-result test)))
2325 (ert-string-for-test-result
2326 result (ert-test-result-expected-p test result)))))
2327 (goto-char point))))))
2328
2329(defun ert-results-rerun-test-at-point-debugging-errors ()
2330 "Re-run the test at point with `ert-debug-on-error' bound to t.
2331
2332To be used in the ERT results buffer."
2333 (interactive)
2334 (let ((ert-debug-on-error t))
2335 (ert-results-rerun-test-at-point)))
2336
2337(defun ert-results-pop-to-backtrace-for-test-at-point ()
2338 "Display the backtrace for the test at point.
2339
2340To be used in the ERT results buffer."
2341 (interactive)
2342 (let* ((test (ert--results-test-at-point-no-redefinition))
2343 (stats ert--results-stats)
2344 (pos (ert--stats-test-pos stats test))
2345 (result (aref (ert--stats-test-results stats) pos)))
2346 (etypecase result
2347 (ert-test-passed (error "Test passed, no backtrace available"))
2348 (ert-test-result-with-condition
2349 (let ((backtrace (ert-test-result-with-condition-backtrace result))
2350 (buffer (get-buffer-create "*ERT Backtrace*")))
2351 (pop-to-buffer buffer)
2352 (setq buffer-read-only t)
2353 (let ((inhibit-read-only t))
2354 (buffer-disable-undo)
2355 (erase-buffer)
2356 (ert-simple-view-mode)
2357 ;; Use unibyte because `debugger-setup-buffer' also does so.
2358 (set-buffer-multibyte nil)
2359 (setq truncate-lines t)
2360 (ert--print-backtrace backtrace)
2361 (debugger-make-xrefs)
2362 (goto-char (point-min))
2363 (insert "Backtrace for test `")
2364 (ert-insert-test-name-button (ert-test-name test))
2365 (insert "':\n")))))))
2366
2367(defun ert-results-pop-to-messages-for-test-at-point ()
2368 "Display the part of the *Messages* buffer generated during the test at point.
2369
2370To be used in the ERT results buffer."
2371 (interactive)
2372 (let* ((test (ert--results-test-at-point-no-redefinition))
2373 (stats ert--results-stats)
2374 (pos (ert--stats-test-pos stats test))
2375 (result (aref (ert--stats-test-results stats) pos)))
2376 (let ((buffer (get-buffer-create "*ERT Messages*")))
2377 (pop-to-buffer buffer)
2378 (setq buffer-read-only t)
2379 (let ((inhibit-read-only t))
2380 (buffer-disable-undo)
2381 (erase-buffer)
2382 (ert-simple-view-mode)
2383 (insert (ert-test-result-messages result))
2384 (goto-char (point-min))
2385 (insert "Messages for test `")
2386 (ert-insert-test-name-button (ert-test-name test))
2387 (insert "':\n")))))
2388
2389(defun ert-results-pop-to-should-forms-for-test-at-point ()
2390 "Display the list of `should' forms executed during the test at point.
2391
2392To be used in the ERT results buffer."
2393 (interactive)
2394 (let* ((test (ert--results-test-at-point-no-redefinition))
2395 (stats ert--results-stats)
2396 (pos (ert--stats-test-pos stats test))
2397 (result (aref (ert--stats-test-results stats) pos)))
2398 (let ((buffer (get-buffer-create "*ERT list of should forms*")))
2399 (pop-to-buffer buffer)
2400 (setq buffer-read-only t)
2401 (let ((inhibit-read-only t))
2402 (buffer-disable-undo)
2403 (erase-buffer)
2404 (ert-simple-view-mode)
2405 (if (null (ert-test-result-should-forms result))
2406 (insert "\n(No should forms during this test.)\n")
2407 (loop for form-description in (ert-test-result-should-forms result)
2408 for i from 1 do
2409 (insert "\n")
2410 (insert (format "%s: " i))
2411 (let ((begin (point)))
2412 (ert--pp-with-indentation-and-newline form-description)
2413 (ert--make-xrefs-region begin (point)))))
2414 (goto-char (point-min))
2415 (insert "`should' forms executed during test `")
2416 (ert-insert-test-name-button (ert-test-name test))
2417 (insert "':\n")
2418 (insert "\n")
2419 (insert (concat "(Values are shallow copies and may have "
2420 "looked different during the test if they\n"
2421 "have been modified destructively.)\n"))
2422 (forward-line 1)))))
2423
2424(defun ert-results-toggle-printer-limits-for-test-at-point ()
2425 "Toggle how much of the condition to print for the test at point.
2426
2427To be used in the ERT results buffer."
2428 (interactive)
2429 (let* ((ewoc ert--results-ewoc)
2430 (node (ert--results-test-node-at-point))
2431 (entry (ewoc-data node)))
2432 (setf (ert--ewoc-entry-extended-printer-limits-p entry)
2433 (not (ert--ewoc-entry-extended-printer-limits-p entry)))
2434 (ewoc-invalidate ewoc node)))
2435
2436(defun ert-results-pop-to-timings ()
2437 "Display test timings for the last run.
2438
2439To be used in the ERT results buffer."
2440 (interactive)
2441 (let* ((stats ert--results-stats)
2442 (start-times (ert--stats-test-start-times stats))
2443 (end-times (ert--stats-test-end-times stats))
2444 (buffer (get-buffer-create "*ERT timings*"))
2445 (data (loop for test across (ert--stats-tests stats)
2446 for start-time across (ert--stats-test-start-times stats)
2447 for end-time across (ert--stats-test-end-times stats)
2448 collect (list test
2449 (float-time (subtract-time end-time
2450 start-time))))))
2451 (setq data (sort data (lambda (a b)
2452 (> (second a) (second b)))))
2453 (pop-to-buffer buffer)
2454 (setq buffer-read-only t)
2455 (let ((inhibit-read-only t))
2456 (buffer-disable-undo)
2457 (erase-buffer)
2458 (ert-simple-view-mode)
2459 (if (null data)
2460 (insert "(No data)\n")
2461 (insert (format "%-3s %8s %8s\n" "" "time" "cumul"))
2462 (loop for (test time) in data
2463 for cumul-time = time then (+ cumul-time time)
2464 for i from 1 do
2465 (let ((begin (point)))
2466 (insert (format "%3s: %8.3f %8.3f " i time cumul-time))
2467 (ert-insert-test-name-button (ert-test-name test))
2468 (insert "\n"))))
2469 (goto-char (point-min))
2470 (insert "Tests by run time (seconds):\n\n")
2471 (forward-line 1))))
2472
2473;;;###autoload
2474(defun ert-describe-test (test-or-test-name)
2475 "Display the documentation for TEST-OR-TEST-NAME (a symbol or ert-test)."
2476 (interactive (list (ert-read-test-name-at-point "Describe test")))
2477 (when (< emacs-major-version 24)
2478 (error "Requires Emacs 24"))
2479 (let (test-name
2480 test-definition)
2481 (etypecase test-or-test-name
2482 (symbol (setq test-name test-or-test-name
2483 test-definition (ert-get-test test-or-test-name)))
2484 (ert-test (setq test-name (ert-test-name test-or-test-name)
2485 test-definition test-or-test-name)))
2486 (help-setup-xref (list #'ert-describe-test test-or-test-name)
2487 (called-interactively-p 'interactive))
2488 (save-excursion
2489 (with-help-window (help-buffer)
2490 (with-current-buffer (help-buffer)
2491 (insert (if test-name (format "%S" test-name) "<anonymous test>"))
2492 (insert " is a test")
2493 (let ((file-name (and test-name
2494 (symbol-file test-name 'ert-deftest))))
2495 (when file-name
2496 (insert " defined in `" (file-name-nondirectory file-name) "'")
2497 (save-excursion
2498 (re-search-backward "`\\([^`']+\\)'" nil t)
2499 (help-xref-button 1 'help-function-def test-name file-name)))
2500 (insert ".")
2501 (fill-region-as-paragraph (point-min) (point))
2502 (insert "\n\n")
2503 (unless (and (ert-test-boundp test-name)
2504 (eql (ert-get-test test-name) test-definition))
2505 (let ((begin (point)))
2506 (insert "Note: This test has been redefined or deleted, "
2507 "this documentation refers to an old definition.")
2508 (fill-region-as-paragraph begin (point)))
2509 (insert "\n\n"))
2510 (insert (or (ert-test-documentation test-definition)
2511 "It is not documented.")
2512 "\n")))))))
2513
2514(defun ert-results-describe-test-at-point ()
2515 "Display the documentation of the test at point.
2516
2517To be used in the ERT results buffer."
2518 (interactive)
2519 (ert-describe-test (ert--results-test-at-point-no-redefinition)))
2520
2521
2522;;; Actions on load/unload.
2523
2524(add-to-list 'find-function-regexp-alist '(ert-deftest . ert--find-test-regexp))
2525(add-to-list 'minor-mode-alist '(ert--current-run-stats
2526 (:eval
2527 (ert--tests-running-mode-line-indicator))))
2528(add-to-list 'emacs-lisp-mode-hook 'ert--activate-font-lock-keywords)
2529
2530(defun ert--unload-function ()
2531 "Unload function to undo the side-effects of loading ert.el."
2532 (ert--remove-from-list 'find-function-regexp-alist 'ert-deftest :key #'car)
2533 (ert--remove-from-list 'minor-mode-alist 'ert--current-run-stats :key #'car)
2534 (ert--remove-from-list 'emacs-lisp-mode-hook
2535 'ert--activate-font-lock-keywords)
2536 nil)
2537
2538(defvar ert-unload-hook '())
2539(add-hook 'ert-unload-hook 'ert--unload-function)
2540
2541
2542(provide 'ert)
2543
2544;;; ert.el ends here
diff --git a/lisp/emacs-lisp/package.el b/lisp/emacs-lisp/package.el
index fecddcf16ed..dfe1a4309ca 100644
--- a/lisp/emacs-lisp/package.el
+++ b/lisp/emacs-lisp/package.el
@@ -1663,15 +1663,15 @@ A value of nil means to display all packages.")
1663Optional PACKAGES is a list of names of packages (symbols) to 1663Optional PACKAGES is a list of names of packages (symbols) to
1664list; the default is to display everything in `package-alist'." 1664list; the default is to display everything in `package-alist'."
1665 (require 'finder-inf nil t) 1665 (require 'finder-inf nil t)
1666 (with-current-buffer (get-buffer-create "*Packages*") 1666 (let ((buf (get-buffer-create "*Packages*")))
1667 (package-menu-mode) 1667 (with-current-buffer buf
1668 (set (make-local-variable 'package-menu-package-list) packages) 1668 (package-menu-mode)
1669 (set (make-local-variable 'package-menu-sort-key) nil) 1669 (set (make-local-variable 'package-menu-package-list) packages)
1670 (package--generate-package-list) 1670 (set (make-local-variable 'package-menu-sort-key) nil)
1671 ;; It's okay to use pop-to-buffer here. The package menu buffer 1671 (package--generate-package-list))
1672 ;; has keybindings, and the user just typed `M-x list-packages', 1672 ;; The package menu buffer has keybindings. If the user types
1673 ;; suggesting that they might want to use them. 1673 ;; `M-x list-packages', that suggests it should become current.
1674 (pop-to-buffer (current-buffer)))) 1674 (switch-to-buffer buf)))
1675 1675
1676;;;###autoload 1676;;;###autoload
1677(defun list-packages () 1677(defun list-packages ()
diff --git a/lisp/font-lock.el b/lisp/font-lock.el
index fe873297dc2..b10c3d8b9ee 100644
--- a/lisp/font-lock.el
+++ b/lisp/font-lock.el
@@ -276,13 +276,14 @@ decoration for buffers in C++ mode, and level 1 decoration otherwise."
276 (integer :tag "level" 1))))) 276 (integer :tag "level" 1)))))
277 :group 'font-lock) 277 :group 'font-lock)
278 278
279(defcustom font-lock-verbose 0 279(defcustom font-lock-verbose nil
280 "If non-nil, means show status messages for buffer fontification. 280 "If non-nil, means show status messages for buffer fontification.
281If a number, only buffers greater than this size have fontification messages." 281If a number, only buffers greater than this size have fontification messages."
282 :type '(choice (const :tag "never" nil) 282 :type '(choice (const :tag "never" nil)
283 (other :tag "always" t) 283 (other :tag "always" t)
284 (integer :tag "size")) 284 (integer :tag "size"))
285 :group 'font-lock) 285 :group 'font-lock
286 :version "24.1")
286 287
287 288
288;; Originally these variable values were face names such as `bold' etc. 289;; Originally these variable values were face names such as `bold' etc.
diff --git a/lisp/gnus/ChangeLog b/lisp/gnus/ChangeLog
index c0c6533d531..dcb2353208c 100644
--- a/lisp/gnus/ChangeLog
+++ b/lisp/gnus/ChangeLog
@@ -1,3 +1,16 @@
12011-01-13 Chong Yidong <cyd@stupidchicken.com>
2
3 * message.el (message-tool-bar-gnome): Tweak tool-bar items. Add
4 :vert-only tags.
5 (message-mail): New arg RETURN-ACTION.
6 (message-return-action): New var.
7 (message-bury): Use it.
8 (message-mode): Make it buffer-local.
9 (message-send-and-exit): Always call message-bury.
10
11 * gnus-msg.el (gnus-msg-mail): New arg RETURN-ACTION. Pass it to
12 message-mail.
13
12011-01-11 Lars Magne Ingebrigtsen <larsi@gnus.org> 142011-01-11 Lars Magne Ingebrigtsen <larsi@gnus.org>
2 15
3 * nnimap.el (nnimap-convert-partial-article): Protect against 16 * nnimap.el (nnimap-convert-partial-article): Protect against
diff --git a/lisp/gnus/gnus-msg.el b/lisp/gnus/gnus-msg.el
index d66e1692a80..e352ffacef8 100644
--- a/lisp/gnus/gnus-msg.el
+++ b/lisp/gnus/gnus-msg.el
@@ -477,7 +477,7 @@ Thank you for your help in stamping out bugs.
477 477
478;;;###autoload 478;;;###autoload
479(defun gnus-msg-mail (&optional to subject other-headers continue 479(defun gnus-msg-mail (&optional to subject other-headers continue
480 switch-action yank-action send-actions) 480 switch-action yank-action send-actions return-action)
481 "Start editing a mail message to be sent. 481 "Start editing a mail message to be sent.
482Like `message-mail', but with Gnus paraphernalia, particularly the 482Like `message-mail', but with Gnus paraphernalia, particularly the
483Gcc: header for archiving purposes." 483Gcc: header for archiving purposes."
@@ -486,7 +486,7 @@ Gcc: header for archiving purposes."
486 mail-buf) 486 mail-buf)
487 (gnus-setup-message 'message 487 (gnus-setup-message 'message
488 (message-mail to subject other-headers continue 488 (message-mail to subject other-headers continue
489 nil yank-action send-actions)) 489 nil yank-action send-actions return-action))
490 (when switch-action 490 (when switch-action
491 (setq mail-buf (current-buffer)) 491 (setq mail-buf (current-buffer))
492 (switch-to-buffer buf) 492 (switch-to-buffer buf)
diff --git a/lisp/gnus/message.el b/lisp/gnus/message.el
index 78652fb2ee0..e8b6d141720 100644
--- a/lisp/gnus/message.el
+++ b/lisp/gnus/message.el
@@ -1120,6 +1120,8 @@ It is a vector of the following headers:
1120(defvar message-checksum nil) 1120(defvar message-checksum nil)
1121(defvar message-send-actions nil 1121(defvar message-send-actions nil
1122 "A list of actions to be performed upon successful sending of a message.") 1122 "A list of actions to be performed upon successful sending of a message.")
1123(defvar message-return-action nil
1124 "Action to return to the caller after sending or postphoning a message.")
1123(defvar message-exit-actions nil 1125(defvar message-exit-actions nil
1124 "A list of actions to be performed upon exiting after sending a message.") 1126 "A list of actions to be performed upon exiting after sending a message.")
1125(defvar message-kill-actions nil 1127(defvar message-kill-actions nil
@@ -2863,6 +2865,7 @@ M-RET `message-newline-and-reformat' (break the line and reformat)."
2863 (set (make-local-variable 'message-reply-buffer) nil) 2865 (set (make-local-variable 'message-reply-buffer) nil)
2864 (set (make-local-variable 'message-inserted-headers) nil) 2866 (set (make-local-variable 'message-inserted-headers) nil)
2865 (set (make-local-variable 'message-send-actions) nil) 2867 (set (make-local-variable 'message-send-actions) nil)
2868 (set (make-local-variable 'message-return-action) nil)
2866 (set (make-local-variable 'message-exit-actions) nil) 2869 (set (make-local-variable 'message-exit-actions) nil)
2867 (set (make-local-variable 'message-kill-actions) nil) 2870 (set (make-local-variable 'message-kill-actions) nil)
2868 (set (make-local-variable 'message-postpone-actions) nil) 2871 (set (make-local-variable 'message-postpone-actions) nil)
@@ -3955,11 +3958,9 @@ The text will also be indented the normal way."
3955 (actions message-exit-actions)) 3958 (actions message-exit-actions))
3956 (when (and (message-send arg) 3959 (when (and (message-send arg)
3957 (buffer-name buf)) 3960 (buffer-name buf))
3961 (message-bury buf)
3958 (if message-kill-buffer-on-exit 3962 (if message-kill-buffer-on-exit
3959 (kill-buffer buf) 3963 (kill-buffer buf))
3960 (bury-buffer buf)
3961 (when (eq buf (current-buffer))
3962 (message-bury buf)))
3963 (message-do-actions actions) 3964 (message-do-actions actions)
3964 t))) 3965 t)))
3965 3966
@@ -4009,9 +4010,8 @@ Instead, just auto-save the buffer and then bury it."
4009 "Bury this mail BUFFER." 4010 "Bury this mail BUFFER."
4010 (let ((newbuf (other-buffer buffer))) 4011 (let ((newbuf (other-buffer buffer)))
4011 (bury-buffer buffer) 4012 (bury-buffer buffer)
4012 (if (and (window-dedicated-p (selected-window)) 4013 (if message-return-action
4013 (not (null (delq (selected-frame) (visible-frame-list))))) 4014 (apply (car message-return-action) (cdr message-return-action))
4014 (delete-frame (selected-frame))
4015 (switch-to-buffer newbuf)))) 4015 (switch-to-buffer newbuf))))
4016 4016
4017(defun message-send (&optional arg) 4017(defun message-send (&optional arg)
@@ -6304,11 +6304,11 @@ between beginning of field and beginning of line."
6304;; YANK-ACTION, if non-nil, can be a buffer or a yank action of the 6304;; YANK-ACTION, if non-nil, can be a buffer or a yank action of the
6305;; form (FUNCTION . ARGS). 6305;; form (FUNCTION . ARGS).
6306(defun message-setup (headers &optional yank-action actions 6306(defun message-setup (headers &optional yank-action actions
6307 continue switch-function) 6307 continue switch-function return-action)
6308 (let ((mua (message-mail-user-agent)) 6308 (let ((mua (message-mail-user-agent))
6309 subject to field) 6309 subject to field)
6310 (if (not (and message-this-is-mail mua)) 6310 (if (not (and message-this-is-mail mua))
6311 (message-setup-1 headers yank-action actions) 6311 (message-setup-1 headers yank-action actions return-action)
6312 (setq headers (copy-sequence headers)) 6312 (setq headers (copy-sequence headers))
6313 (setq field (assq 'Subject headers)) 6313 (setq field (assq 'Subject headers))
6314 (when field 6314 (when field
@@ -6356,11 +6356,12 @@ are not included."
6356 (push header result))) 6356 (push header result)))
6357 (nreverse result))) 6357 (nreverse result)))
6358 6358
6359(defun message-setup-1 (headers &optional yank-action actions) 6359(defun message-setup-1 (headers &optional yank-action actions return-action)
6360 (dolist (action actions) 6360 (dolist (action actions)
6361 (condition-case nil 6361 (condition-case nil
6362 (add-to-list 'message-send-actions 6362 (add-to-list 'message-send-actions
6363 `(apply ',(car action) ',(cdr action))))) 6363 `(apply ',(car action) ',(cdr action)))))
6364 (setq message-return-action return-action)
6364 (setq message-reply-buffer 6365 (setq message-reply-buffer
6365 (if (and (consp yank-action) 6366 (if (and (consp yank-action)
6366 (eq (car yank-action) 'insert-buffer)) 6367 (eq (car yank-action) 'insert-buffer))
@@ -6489,9 +6490,9 @@ are not included."
6489;;; 6490;;;
6490 6491
6491;;;###autoload 6492;;;###autoload
6492(defun message-mail (&optional to subject 6493(defun message-mail (&optional to subject other-headers continue
6493 other-headers continue switch-function 6494 switch-function yank-action send-actions
6494 yank-action send-actions) 6495 return-action &rest ignored)
6495 "Start editing a mail message to be sent. 6496 "Start editing a mail message to be sent.
6496OTHER-HEADERS is an alist of header/value pairs. CONTINUE says whether 6497OTHER-HEADERS is an alist of header/value pairs. CONTINUE says whether
6497to continue editing a message already being composed. SWITCH-FUNCTION 6498to continue editing a message already being composed. SWITCH-FUNCTION
@@ -6512,7 +6513,8 @@ is a function used to switch to and display the mail buffer."
6512 (nconc 6513 (nconc
6513 `((To . ,(or to "")) (Subject . ,(or subject ""))) 6514 `((To . ,(or to "")) (Subject . ,(or subject "")))
6514 (when other-headers other-headers)) 6515 (when other-headers other-headers))
6515 yank-action send-actions continue switch-function) 6516 yank-action send-actions continue switch-function
6517 return-action)
6516 ;; FIXME: Should return nil if failure. 6518 ;; FIXME: Should return nil if failure.
6517 t)) 6519 t))
6518 6520
@@ -7642,24 +7644,22 @@ Pre-defined symbols include `message-tool-bar-gnome' and
7642 7644
7643(defcustom message-tool-bar-gnome 7645(defcustom message-tool-bar-gnome
7644 '((ispell-message "spell" nil 7646 '((ispell-message "spell" nil
7647 :vert-only t
7645 :visible (or (not (boundp 'flyspell-mode)) 7648 :visible (or (not (boundp 'flyspell-mode))
7646 (not flyspell-mode))) 7649 (not flyspell-mode)))
7647 (flyspell-buffer "spell" t 7650 (flyspell-buffer "spell" t
7651 :vert-only t
7648 :visible (and (boundp 'flyspell-mode) 7652 :visible (and (boundp 'flyspell-mode)
7649 flyspell-mode) 7653 flyspell-mode)
7650 :help "Flyspell whole buffer") 7654 :help "Flyspell whole buffer")
7651 (gmm-ignore "separator") 7655 (message-send-and-exit "mail/send" t :label "Send")
7652 (message-send-and-exit "mail/send")
7653 (message-dont-send "mail/save-draft") 7656 (message-dont-send "mail/save-draft")
7654 (message-kill-buffer "close") ;; stock_cancel 7657 (mml-attach-file "attach" mml-mode-map :vert-only t)
7655 (mml-attach-file "attach" mml-mode-map)
7656 (mml-preview "mail/preview" mml-mode-map) 7658 (mml-preview "mail/preview" mml-mode-map)
7657 (mml-secure-message-sign-encrypt "lock" mml-mode-map :visible nil) 7659 (mml-secure-message-sign-encrypt "lock" mml-mode-map :visible nil)
7658 (message-insert-importance-high "important" nil :visible nil) 7660 (message-insert-importance-high "important" nil :visible nil)
7659 (message-insert-importance-low "unimportant" nil :visible nil) 7661 (message-insert-importance-low "unimportant" nil :visible nil)
7660 (message-insert-disposition-notification-to "receipt" nil :visible nil) 7662 (message-insert-disposition-notification-to "receipt" nil :visible nil))
7661 (gmm-customize-mode "preferences" t :help "Edit mode preferences")
7662 (message-info "help" t :help "Message manual"))
7663 "List of items for the message tool bar (GNOME style). 7663 "List of items for the message tool bar (GNOME style).
7664 7664
7665See `gmm-tool-bar-from-list' for details on the format of the list." 7665See `gmm-tool-bar-from-list' for details on the format of the list."
diff --git a/lisp/ido.el b/lisp/ido.el
index 84ae93142b4..502dd39e327 100644
--- a/lisp/ido.el
+++ b/lisp/ido.el
@@ -1289,8 +1289,6 @@ Only used if `ido-use-virtual-buffers' is non-nil.")
1289(defun ido-may-cache-directory (&optional dir) 1289(defun ido-may-cache-directory (&optional dir)
1290 (setq dir (or dir ido-current-directory)) 1290 (setq dir (or dir ido-current-directory))
1291 (cond 1291 (cond
1292 ((ido-directory-too-big-p dir)
1293 nil)
1294 ((and (ido-is-root-directory dir) 1292 ((and (ido-is-root-directory dir)
1295 (or ido-enable-tramp-completion 1293 (or ido-enable-tramp-completion
1296 (memq system-type '(windows-nt ms-dos)))) 1294 (memq system-type '(windows-nt ms-dos))))
@@ -1299,6 +1297,8 @@ Only used if `ido-use-virtual-buffers' is non-nil.")
1299 (ido-cache-unc-valid)) 1297 (ido-cache-unc-valid))
1300 ((ido-is-ftp-directory dir) 1298 ((ido-is-ftp-directory dir)
1301 (ido-cache-ftp-valid)) 1299 (ido-cache-ftp-valid))
1300 ((ido-directory-too-big-p dir)
1301 nil)
1302 (t t))) 1302 (t t)))
1303 1303
1304(defun ido-pp (list &optional sep) 1304(defun ido-pp (list &optional sep)
@@ -3072,8 +3072,8 @@ If repeated, insert text from buffer instead."
3072 (if ido-matches 3072 (if ido-matches
3073 (let ((next (cadr ido-matches))) 3073 (let ((next (cadr ido-matches)))
3074 (setq ido-cur-list (ido-chop ido-cur-list next)) 3074 (setq ido-cur-list (ido-chop ido-cur-list next))
3075 (setq ido-rescan t) 3075 (setq ido-matches (ido-chop ido-matches next))
3076 (setq ido-rotate t)))) 3076 (setq ido-rescan nil))))
3077 3077
3078(defun ido-prev-match () 3078(defun ido-prev-match ()
3079 "Put last element of `ido-matches' at the front of the list." 3079 "Put last element of `ido-matches' at the front of the list."
@@ -3081,8 +3081,8 @@ If repeated, insert text from buffer instead."
3081 (if ido-matches 3081 (if ido-matches
3082 (let ((prev (car (last ido-matches)))) 3082 (let ((prev (car (last ido-matches))))
3083 (setq ido-cur-list (ido-chop ido-cur-list prev)) 3083 (setq ido-cur-list (ido-chop ido-cur-list prev))
3084 (setq ido-rescan t) 3084 (setq ido-matches (ido-chop ido-matches prev))
3085 (setq ido-rotate t)))) 3085 (setq ido-rescan nil))))
3086 3086
3087(defun ido-next-match-dir () 3087(defun ido-next-match-dir ()
3088 "Find next directory in match list. 3088 "Find next directory in match list.
diff --git a/lisp/mail/rmail.el b/lisp/mail/rmail.el
index 250481c20b5..1697bce91a5 100644
--- a/lisp/mail/rmail.el
+++ b/lisp/mail/rmail.el
@@ -3441,30 +3441,62 @@ does not pop any summary buffer."
3441;;;; *** Rmail Mailing Commands *** 3441;;;; *** Rmail Mailing Commands ***
3442 3442
3443(defun rmail-start-mail (&optional noerase to subject in-reply-to cc 3443(defun rmail-start-mail (&optional noerase to subject in-reply-to cc
3444 replybuffer sendactions same-window others) 3444 replybuffer sendactions same-window
3445 (let (yank-action) 3445 other-headers)
3446 (let ((switch-function
3447 (cond (same-window nil)
3448 (rmail-mail-new-frame 'switch-to-buffer-other-frame)
3449 (t 'switch-to-buffer-other-window)))
3450 yank-action)
3446 (if replybuffer 3451 (if replybuffer
3447 ;; The function used here must behave like insert-buffer wrt 3452 ;; The function used here must behave like insert-buffer wrt
3448 ;; point and mark (see doc of sc-cite-original). 3453 ;; point and mark (see doc of sc-cite-original).
3449 (setq yank-action (list 'insert-buffer replybuffer))) 3454 (setq yank-action (list 'insert-buffer replybuffer)))
3450 (setq others (cons (cons "cc" cc) others)) 3455 (push (cons "cc" cc) other-headers)
3451 (setq others (cons (cons "in-reply-to" in-reply-to) others)) 3456 (push (cons "in-reply-to" in-reply-to) other-headers)
3452 (if same-window 3457 (prog1
3453 (compose-mail to subject others 3458 (compose-mail to subject other-headers noerase
3454 noerase nil 3459 switch-function yank-action sendactions
3455 yank-action sendactions) 3460 '(rmail-mail-return))
3456 (if rmail-mail-new-frame 3461 (if (eq switch-function 'switch-to-buffer-other-frame)
3457 (prog1 3462 ;; This is not a standard frame parameter; nothing except
3458 (compose-mail to subject others 3463 ;; sendmail.el looks at it.
3459 noerase 'switch-to-buffer-other-frame 3464 (modify-frame-parameters (selected-frame)
3460 yank-action sendactions) 3465 '((mail-dedicated-frame . t)))))))
3461 ;; This is not a standard frame parameter; 3466
3462 ;; nothing except sendmail.el looks at it. 3467(defun rmail-mail-return ()
3463 (modify-frame-parameters (selected-frame) 3468 (cond
3464 '((mail-dedicated-frame . t)))) 3469 ;; If there is only one visible frame with no special handling,
3465 (compose-mail to subject others 3470 ;; consider deleting the mail window to return to Rmail.
3466 noerase 'switch-to-buffer-other-window 3471 ((or (null (delq (selected-frame) (visible-frame-list)))
3467 yank-action sendactions))))) 3472 (not (or (window-dedicated-p (frame-selected-window))
3473 (and pop-up-frames (one-window-p))
3474 (cdr (assq 'mail-dedicated-frame
3475 (frame-parameters))))))
3476 (let (rmail-flag summary-buffer)
3477 (and (not (one-window-p))
3478 (with-current-buffer
3479 (window-buffer (next-window (selected-window) 'not))
3480 (setq rmail-flag (eq major-mode 'rmail-mode))
3481 (setq summary-buffer
3482 (and (boundp 'mail-bury-selects-summary)
3483 mail-bury-selects-summary
3484 (boundp 'rmail-summary-buffer)
3485 rmail-summary-buffer
3486 (buffer-name rmail-summary-buffer)
3487 (not (get-buffer-window rmail-summary-buffer))
3488 rmail-summary-buffer))))
3489 (if rmail-flag
3490 ;; If the Rmail buffer has a summary, show that.
3491 (if summary-buffer (switch-to-buffer summary-buffer)
3492 (delete-window)))))
3493 ;; If the frame was probably made for this buffer, the user
3494 ;; probably wants to delete it now.
3495 ((display-multi-frame-p)
3496 (delete-frame (selected-frame)))
3497 ;; The previous frame is where normally they have the Rmail buffer
3498 ;; displayed.
3499 (t (other-frame -1))))
3468 3500
3469(defun rmail-mail () 3501(defun rmail-mail ()
3470 "Send mail in another window. 3502 "Send mail in another window.
diff --git a/lisp/mail/sendmail.el b/lisp/mail/sendmail.el
index ca91d9512ee..4fa513089bc 100644
--- a/lisp/mail/sendmail.el
+++ b/lisp/mail/sendmail.el
@@ -419,8 +419,7 @@ in `message-auto-save-directory'."
419(defvar mail-reply-action nil) 419(defvar mail-reply-action nil)
420(defvar mail-send-actions nil 420(defvar mail-send-actions nil
421 "A list of actions to be performed upon successful sending of a message.") 421 "A list of actions to be performed upon successful sending of a message.")
422(put 'mail-reply-action 'permanent-local t) 422(defvar mail-return-action nil)
423(put 'mail-send-actions 'permanent-local t)
424 423
425;;;###autoload 424;;;###autoload
426(defcustom mail-default-headers nil 425(defcustom mail-default-headers nil
@@ -521,7 +520,46 @@ by Emacs.)")
521 (setq mail-alias-modtime modtime 520 (setq mail-alias-modtime modtime
522 mail-aliases t))))) 521 mail-aliases t)))))
523 522
524(defun mail-setup (to subject in-reply-to cc replybuffer actions) 523
524;;;###autoload
525(define-mail-user-agent 'sendmail-user-agent
526 'sendmail-user-agent-compose
527 'mail-send-and-exit)
528
529;;;###autoload
530(defun sendmail-user-agent-compose (&optional to subject other-headers
531 continue switch-function yank-action
532 send-actions return-action
533 &rest ignored)
534 (if switch-function
535 (let ((special-display-buffer-names nil)
536 (special-display-regexps nil)
537 (same-window-buffer-names nil)
538 (same-window-regexps nil))
539 (funcall switch-function "*mail*")))
540 (let ((cc (cdr (assoc-string "cc" other-headers t)))
541 (in-reply-to (cdr (assoc-string "in-reply-to" other-headers t)))
542 (body (cdr (assoc-string "body" other-headers t))))
543 (or (mail continue to subject in-reply-to cc yank-action
544 send-actions return-action)
545 continue
546 (error "Message aborted"))
547 (save-excursion
548 (rfc822-goto-eoh)
549 (while other-headers
550 (unless (member-ignore-case (car (car other-headers))
551 '("in-reply-to" "cc" "body"))
552 (insert (car (car other-headers)) ": "
553 (cdr (car other-headers))
554 (if use-hard-newlines hard-newline "\n")))
555 (setq other-headers (cdr other-headers)))
556 (when body
557 (forward-line 1)
558 (insert body))
559 t)))
560
561(defun mail-setup (to subject in-reply-to cc replybuffer
562 actions return-action)
525 (or mail-default-reply-to 563 (or mail-default-reply-to
526 (setq mail-default-reply-to (getenv "REPLYTO"))) 564 (setq mail-default-reply-to (getenv "REPLYTO")))
527 (sendmail-sync-aliases) 565 (sendmail-sync-aliases)
@@ -537,8 +575,12 @@ by Emacs.)")
537 (set-buffer-multibyte (default-value 'enable-multibyte-characters)) 575 (set-buffer-multibyte (default-value 'enable-multibyte-characters))
538 (if current-input-method 576 (if current-input-method
539 (inactivate-input-method)) 577 (inactivate-input-method))
578
579 ;; Local variables for Mail mode.
540 (setq mail-send-actions actions) 580 (setq mail-send-actions actions)
541 (setq mail-reply-action replybuffer) 581 (setq mail-reply-action replybuffer)
582 (setq mail-return-action return-action)
583
542 (goto-char (point-min)) 584 (goto-char (point-min))
543 (if mail-setup-with-from 585 (if mail-setup-with-from
544 (mail-insert-from-field)) 586 (mail-insert-from-field))
@@ -629,6 +671,7 @@ Turning on Mail mode runs the normal hooks `text-mode-hook' and
629`mail-mode-hook' (in that order)." 671`mail-mode-hook' (in that order)."
630 (make-local-variable 'mail-reply-action) 672 (make-local-variable 'mail-reply-action)
631 (make-local-variable 'mail-send-actions) 673 (make-local-variable 'mail-send-actions)
674 (make-local-variable 'mail-return-action)
632 (setq buffer-offer-save t) 675 (setq buffer-offer-save t)
633 (make-local-variable 'font-lock-defaults) 676 (make-local-variable 'font-lock-defaults)
634 (setq font-lock-defaults '(mail-font-lock-keywords t t)) 677 (setq font-lock-defaults '(mail-font-lock-keywords t t))
@@ -762,39 +805,9 @@ Prefix arg means don't delete this window."
762 "Bury this mail buffer." 805 "Bury this mail buffer."
763 (let ((newbuf (other-buffer (current-buffer)))) 806 (let ((newbuf (other-buffer (current-buffer))))
764 (bury-buffer (current-buffer)) 807 (bury-buffer (current-buffer))
765 (if (and (or nil 808 (if (and (null arg) mail-return-action)
766 ;; In this case, we need to go to a different frame. 809 (apply (car mail-return-action) (cdr mail-return-action))
767 (window-dedicated-p (frame-selected-window)) 810 (switch-to-buffer newbuf))))
768 ;; In this mode of operation, the frame was probably
769 ;; made for this buffer, so the user probably wants
770 ;; to delete it now.
771 (and pop-up-frames (one-window-p))
772 (cdr (assq 'mail-dedicated-frame (frame-parameters))))
773 (not (null (delq (selected-frame) (visible-frame-list)))))
774 (progn
775 (if (display-multi-frame-p)
776 (delete-frame (selected-frame))
777 ;; The previous frame is where normally they have the
778 ;; Rmail buffer displayed.
779 (other-frame -1)))
780 (let (rmail-flag summary-buffer)
781 (and (not arg)
782 (not (one-window-p))
783 (with-current-buffer
784 (window-buffer (next-window (selected-window) 'not))
785 (setq rmail-flag (eq major-mode 'rmail-mode))
786 (setq summary-buffer
787 (and mail-bury-selects-summary
788 (boundp 'rmail-summary-buffer)
789 rmail-summary-buffer
790 (buffer-name rmail-summary-buffer)
791 (not (get-buffer-window rmail-summary-buffer))
792 rmail-summary-buffer))))
793 (if rmail-flag
794 ;; If the Rmail buffer has a summary, show that.
795 (if summary-buffer (switch-to-buffer summary-buffer)
796 (delete-window))
797 (switch-to-buffer newbuf))))))
798 811
799(defcustom mail-send-hook nil 812(defcustom mail-send-hook nil
800 "Hook run just before sending a message." 813 "Hook run just before sending a message."
@@ -1643,7 +1656,8 @@ If the current line has `mail-yank-prefix', insert it on the new line."
1643;;;###autoload (add-hook 'same-window-buffer-names (purecopy "*unsent mail*")) 1656;;;###autoload (add-hook 'same-window-buffer-names (purecopy "*unsent mail*"))
1644 1657
1645;;;###autoload 1658;;;###autoload
1646(defun mail (&optional noerase to subject in-reply-to cc replybuffer actions) 1659(defun mail (&optional noerase to subject in-reply-to cc replybuffer
1660 actions return-action)
1647 "Edit a message to be sent. Prefix arg means resume editing (don't erase). 1661 "Edit a message to be sent. Prefix arg means resume editing (don't erase).
1648When this function returns, the buffer `*mail*' is selected. 1662When this function returns, the buffer `*mail*' is selected.
1649The value is t if the message was newly initialized; otherwise, nil. 1663The value is t if the message was newly initialized; otherwise, nil.
@@ -1691,49 +1705,6 @@ The seventh argument ACTIONS is a list of actions to take
1691 when the message is sent, we apply FUNCTION to ARGS. 1705 when the message is sent, we apply FUNCTION to ARGS.
1692 This is how Rmail arranges to mark messages `answered'." 1706 This is how Rmail arranges to mark messages `answered'."
1693 (interactive "P") 1707 (interactive "P")
1694 ;; This is commented out because I found it was confusing in practice.
1695 ;; It is easy enough to rename *mail* by hand with rename-buffer
1696 ;; if you want to have multiple mail buffers.
1697 ;; And then you can control which messages to save. --rms.
1698 ;; (let ((index 1)
1699 ;; buffer)
1700 ;; ;; If requested, look for a mail buffer that is modified and go to it.
1701 ;; (if noerase
1702 ;; (progn
1703 ;; (while (and (setq buffer
1704 ;; (get-buffer (if (= 1 index) "*mail*"
1705 ;; (format "*mail*<%d>" index))))
1706 ;; (not (buffer-modified-p buffer)))
1707 ;; (setq index (1+ index)))
1708 ;; (if buffer (switch-to-buffer buffer)
1709 ;; ;; If none exists, start a new message.
1710 ;; ;; This will never re-use an existing unmodified mail buffer
1711 ;; ;; (since index is not 1 anymore). Perhaps it should.
1712 ;; (setq noerase nil))))
1713 ;; ;; Unless we found a modified message and are happy, start a new message.
1714 ;; (if (not noerase)
1715 ;; (progn
1716 ;; ;; Look for existing unmodified mail buffer.
1717 ;; (while (and (setq buffer
1718 ;; (get-buffer (if (= 1 index) "*mail*"
1719 ;; (format "*mail*<%d>" index))))
1720 ;; (buffer-modified-p buffer))
1721 ;; (setq index (1+ index)))
1722 ;; ;; If none, make a new one.
1723 ;; (or buffer
1724 ;; (setq buffer (generate-new-buffer "*mail*")))
1725 ;; ;; Go there and initialize it.
1726 ;; (switch-to-buffer buffer)
1727 ;; (erase-buffer)
1728 ;; (setq default-directory (expand-file-name "~/"))
1729 ;; (auto-save-mode auto-save-default)
1730 ;; (mail-mode)
1731 ;; (mail-setup to subject in-reply-to cc replybuffer actions)
1732 ;; (if (and buffer-auto-save-file-name
1733 ;; (file-exists-p buffer-auto-save-file-name))
1734 ;; (message "Auto save file for draft message exists; consider M-x mail-recover"))
1735 ;; t))
1736
1737 (if (eq noerase 'new) 1708 (if (eq noerase 'new)
1738 (pop-to-buffer (generate-new-buffer "*mail*")) 1709 (pop-to-buffer (generate-new-buffer "*mail*"))
1739 (and noerase 1710 (and noerase
@@ -1772,7 +1743,8 @@ The seventh argument ACTIONS is a list of actions to take
1772 t)) 1743 t))
1773 (let ((inhibit-read-only t)) 1744 (let ((inhibit-read-only t))
1774 (erase-buffer) 1745 (erase-buffer)
1775 (mail-setup to subject in-reply-to cc replybuffer actions) 1746 (mail-setup to subject in-reply-to cc replybuffer actions
1747 return-action)
1776 (setq initialized t))) 1748 (setq initialized t)))
1777 (if (and buffer-auto-save-file-name 1749 (if (and buffer-auto-save-file-name
1778 (file-exists-p buffer-auto-save-file-name)) 1750 (file-exists-p buffer-auto-save-file-name))
diff --git a/lisp/menu-bar.el b/lisp/menu-bar.el
index 7babd41b69d..2fe5f94ac7f 100644
--- a/lisp/menu-bar.el
+++ b/lisp/menu-bar.el
@@ -584,18 +584,15 @@ Do the same for the keys of the same name."
584 584
585(defvar menu-bar-custom-menu (make-sparse-keymap "Customize")) 585(defvar menu-bar-custom-menu (make-sparse-keymap "Customize"))
586 586
587(define-key menu-bar-custom-menu [customize-apropos-groups]
588 `(menu-item ,(purecopy "Groups Matching Regexp...") customize-apropos-groups
589 :help ,(purecopy "Browse groups whose names match regexp")))
590(define-key menu-bar-custom-menu [customize-apropos-faces] 587(define-key menu-bar-custom-menu [customize-apropos-faces]
591 `(menu-item ,(purecopy "Faces Matching Regexp...") customize-apropos-faces 588 `(menu-item ,(purecopy "Faces Matching...") customize-apropos-faces
592 :help ,(purecopy "Browse faces whose names match regexp"))) 589 :help ,(purecopy "Browse faces matching a regexp or word list")))
593(define-key menu-bar-custom-menu [customize-apropos-options] 590(define-key menu-bar-custom-menu [customize-apropos-options]
594 `(menu-item ,(purecopy "Options Matching Regexp...") customize-apropos-options 591 `(menu-item ,(purecopy "Options Matching...") customize-apropos-options
595 :help ,(purecopy "Browse options whose names match regexp"))) 592 :help ,(purecopy "Browse options matching a regexp or word list")))
596(define-key menu-bar-custom-menu [customize-apropos] 593(define-key menu-bar-custom-menu [customize-apropos]
597 `(menu-item ,(purecopy "Settings Matching Regexp...") customize-apropos 594 `(menu-item ,(purecopy "All Settings Matching...") customize-apropos
598 :help ,(purecopy "Browse customizable settings whose names match regexp"))) 595 :help ,(purecopy "Browse customizable settings matching a regexp or word list")))
599(define-key menu-bar-custom-menu [separator-1] 596(define-key menu-bar-custom-menu [separator-1]
600 menu-bar-separator) 597 menu-bar-separator)
601(define-key menu-bar-custom-menu [customize-group] 598(define-key menu-bar-custom-menu [customize-group]
@@ -623,6 +620,9 @@ Do the same for the keys of the same name."
623(define-key menu-bar-custom-menu [customize] 620(define-key menu-bar-custom-menu [customize]
624 `(menu-item ,(purecopy "Top-level Customization Group") customize 621 `(menu-item ,(purecopy "Top-level Customization Group") customize
625 :help ,(purecopy "The master group called `Emacs'"))) 622 :help ,(purecopy "The master group called `Emacs'")))
623(define-key menu-bar-custom-menu [customize-themes]
624 `(menu-item ,(purecopy "Custom Themes") customize-themes
625 :help ,(purecopy "Choose a pre-defined customization theme")))
626 626
627;(defvar menu-bar-preferences-menu (make-sparse-keymap "Preferences")) 627;(defvar menu-bar-preferences-menu (make-sparse-keymap "Preferences"))
628 628
@@ -1144,7 +1144,7 @@ mail status in mode line"))
1144 ;; It is better not to use backquote here, 1144 ;; It is better not to use backquote here,
1145 ;; because that makes a bootstrapping problem 1145 ;; because that makes a bootstrapping problem
1146 ;; if you need to recompile all the Lisp files using interpreted code. 1146 ;; if you need to recompile all the Lisp files using interpreted code.
1147 `(menu-item ,(purecopy "Mule (Multilingual Environment)") ,mule-menu-keymap 1147 `(menu-item ,(purecopy "Multilingual Environment") ,mule-menu-keymap
1148;; Most of the MULE menu actually does make sense in unibyte mode, 1148;; Most of the MULE menu actually does make sense in unibyte mode,
1149;; e.g. language selection. 1149;; e.g. language selection.
1150;;; :visible '(default-value 'enable-multibyte-characters) 1150;;; :visible '(default-value 'enable-multibyte-characters)
diff --git a/lisp/mh-e/ChangeLog b/lisp/mh-e/ChangeLog
index 336fd0100c1..d6cc987bf41 100644
--- a/lisp/mh-e/ChangeLog
+++ b/lisp/mh-e/ChangeLog
@@ -1,3 +1,7 @@
12011-01-13 Chong Yidong <cyd@stupidchicken.com>
2
3 * mh-comp.el (mh-user-agent-compose): New arg RETURN-ACTION.
4
12010-11-07 Glenn Morris <rgm@gnu.org> 52010-11-07 Glenn Morris <rgm@gnu.org>
2 6
3 * mh-seq.el (mh-read-msg-list): Use point-at-eol. 7 * mh-seq.el (mh-read-msg-list): Use point-at-eol.
diff --git a/lisp/mh-e/mh-comp.el b/lisp/mh-e/mh-comp.el
index 762aad86080..b2de7ab706e 100644
--- a/lisp/mh-e/mh-comp.el
+++ b/lisp/mh-e/mh-comp.el
@@ -199,7 +199,8 @@ applications should use `mh-user-agent-compose'."
199;;;###autoload 199;;;###autoload
200(defun mh-user-agent-compose (&optional to subject other-headers continue 200(defun mh-user-agent-compose (&optional to subject other-headers continue
201 switch-function yank-action 201 switch-function yank-action
202 send-actions) 202 send-actions return-action
203 &rest ignored)
203 "Set up mail composition draft with the MH mail system. 204 "Set up mail composition draft with the MH mail system.
204This is the `mail-user-agent' entry point to MH-E. This function 205This is the `mail-user-agent' entry point to MH-E. This function
205conforms to the contract specified by `define-mail-user-agent' 206conforms to the contract specified by `define-mail-user-agent'
@@ -213,8 +214,8 @@ OTHER-HEADERS is an alist specifying additional header fields.
213Elements look like (HEADER . VALUE) where both HEADER and VALUE 214Elements look like (HEADER . VALUE) where both HEADER and VALUE
214are strings. 215are strings.
215 216
216CONTINUE, SWITCH-FUNCTION, YANK-ACTION and SEND-ACTIONS are 217CONTINUE, SWITCH-FUNCTION, YANK-ACTION, SEND-ACTIONS, and
217ignored." 218RETURN-ACTION are ignored."
218 (mh-find-path) 219 (mh-find-path)
219 (let ((mh-error-if-no-draft t)) 220 (let ((mh-error-if-no-draft t))
220 (mh-send to "" subject) 221 (mh-send to "" subject)
diff --git a/lisp/simple.el b/lisp/simple.el
index ca365e9f854..57ef84882d0 100644
--- a/lisp/simple.el
+++ b/lisp/simple.el
@@ -5712,10 +5712,6 @@ appears to have customizations applying to the old default,
5712 :version "23.2" 5712 :version "23.2"
5713 :group 'mail) 5713 :group 'mail)
5714 5714
5715(define-mail-user-agent 'sendmail-user-agent
5716 'sendmail-user-agent-compose
5717 'mail-send-and-exit)
5718
5719(defun rfc822-goto-eoh () 5715(defun rfc822-goto-eoh ()
5720 ;; Go to header delimiter line in a mail message, following RFC822 rules 5716 ;; Go to header delimiter line in a mail message, following RFC822 rules
5721 (goto-char (point-min)) 5717 (goto-char (point-min))
@@ -5723,37 +5719,9 @@ appears to have customizations applying to the old default,
5723 "^\\([:\n]\\|[^: \t\n]+[ \t\n]\\)" nil 'move) 5719 "^\\([:\n]\\|[^: \t\n]+[ \t\n]\\)" nil 'move)
5724 (goto-char (match-beginning 0)))) 5720 (goto-char (match-beginning 0))))
5725 5721
5726(defun sendmail-user-agent-compose (&optional to subject other-headers continue
5727 switch-function yank-action
5728 send-actions)
5729 (if switch-function
5730 (let ((special-display-buffer-names nil)
5731 (special-display-regexps nil)
5732 (same-window-buffer-names nil)
5733 (same-window-regexps nil))
5734 (funcall switch-function "*mail*")))
5735 (let ((cc (cdr (assoc-string "cc" other-headers t)))
5736 (in-reply-to (cdr (assoc-string "in-reply-to" other-headers t)))
5737 (body (cdr (assoc-string "body" other-headers t))))
5738 (or (mail continue to subject in-reply-to cc yank-action send-actions)
5739 continue
5740 (error "Message aborted"))
5741 (save-excursion
5742 (rfc822-goto-eoh)
5743 (while other-headers
5744 (unless (member-ignore-case (car (car other-headers))
5745 '("in-reply-to" "cc" "body"))
5746 (insert (car (car other-headers)) ": "
5747 (cdr (car other-headers))
5748 (if use-hard-newlines hard-newline "\n")))
5749 (setq other-headers (cdr other-headers)))
5750 (when body
5751 (forward-line 1)
5752 (insert body))
5753 t)))
5754
5755(defun compose-mail (&optional to subject other-headers continue 5722(defun compose-mail (&optional to subject other-headers continue
5756 switch-function yank-action send-actions) 5723 switch-function yank-action send-actions
5724 return-action)
5757 "Start composing a mail message to send. 5725 "Start composing a mail message to send.
5758This uses the user's chosen mail composition package 5726This uses the user's chosen mail composition package
5759as selected with the variable `mail-user-agent'. 5727as selected with the variable `mail-user-agent'.
@@ -5778,7 +5746,12 @@ FUNCTION to ARGS, to insert the raw text of the original message.
5778original text has been inserted in this way.) 5746original text has been inserted in this way.)
5779 5747
5780SEND-ACTIONS is a list of actions to call when the message is sent. 5748SEND-ACTIONS is a list of actions to call when the message is sent.
5781Each action has the form (FUNCTION . ARGS)." 5749Each action has the form (FUNCTION . ARGS).
5750
5751RETURN-ACTION, if non-nil, is an action for returning to the
5752caller. It has the form (FUNCTION . ARGS). The function is
5753called after the mail has been sent or put aside, and the mail
5754buffer buried."
5782 (interactive 5755 (interactive
5783 (list nil nil nil current-prefix-arg)) 5756 (list nil nil nil current-prefix-arg))
5784 5757
@@ -5808,25 +5781,27 @@ To disable this warning, set `compose-mail-user-agent-warnings' to nil."
5808 warn-vars " ")))))) 5781 warn-vars " "))))))
5809 5782
5810 (let ((function (get mail-user-agent 'composefunc))) 5783 (let ((function (get mail-user-agent 'composefunc)))
5811 (funcall function to subject other-headers continue 5784 (funcall function to subject other-headers continue switch-function
5812 switch-function yank-action send-actions))) 5785 yank-action send-actions return-action)))
5813 5786
5814(defun compose-mail-other-window (&optional to subject other-headers continue 5787(defun compose-mail-other-window (&optional to subject other-headers continue
5815 yank-action send-actions) 5788 yank-action send-actions
5789 return-action)
5816 "Like \\[compose-mail], but edit the outgoing message in another window." 5790 "Like \\[compose-mail], but edit the outgoing message in another window."
5817 (interactive 5791 (interactive (list nil nil nil current-prefix-arg))
5818 (list nil nil nil current-prefix-arg))
5819 (compose-mail to subject other-headers continue 5792 (compose-mail to subject other-headers continue
5820 'switch-to-buffer-other-window yank-action send-actions)) 5793 'switch-to-buffer-other-window yank-action send-actions
5821 5794 return-action))
5822 5795
5823(defun compose-mail-other-frame (&optional to subject other-headers continue 5796(defun compose-mail-other-frame (&optional to subject other-headers continue
5824 yank-action send-actions) 5797 yank-action send-actions
5798 return-action)
5825 "Like \\[compose-mail], but edit the outgoing message in another frame." 5799 "Like \\[compose-mail], but edit the outgoing message in another frame."
5826 (interactive 5800 (interactive (list nil nil nil current-prefix-arg))
5827 (list nil nil nil current-prefix-arg))
5828 (compose-mail to subject other-headers continue 5801 (compose-mail to subject other-headers continue
5829 'switch-to-buffer-other-frame yank-action send-actions)) 5802 'switch-to-buffer-other-frame yank-action send-actions
5803 return-action))
5804
5830 5805
5831(defvar set-variable-value-history nil 5806(defvar set-variable-value-history nil
5832 "History of values entered with `set-variable'. 5807 "History of values entered with `set-variable'.
diff --git a/src/image.c b/src/image.c
index 1125309a9f7..023c9763abd 100644
--- a/src/image.c
+++ b/src/image.c
@@ -7519,7 +7519,7 @@ imagemagick_load_image (/* Pointer to emacs frame structure. */
7519 image to see how many sub-images it contains. Pinging is faster 7519 image to see how many sub-images it contains. Pinging is faster
7520 than loading the image to find out things about it. */ 7520 than loading the image to find out things about it. */
7521 7521
7522 /* MagickWandGenesis() initializes the imagemagick library. */ 7522 /* `MagickWandGenesis' initializes the imagemagick environment. */
7523 MagickWandGenesis (); 7523 MagickWandGenesis ();
7524 image = image_spec_value (img->spec, QCindex, NULL); 7524 image = image_spec_value (img->spec, QCindex, NULL);
7525 ino = INTEGERP (image) ? XFASTINT (image) : 0; 7525 ino = INTEGERP (image) ? XFASTINT (image) : 0;
@@ -7807,6 +7807,7 @@ imagemagick_load_image (/* Pointer to emacs frame structure. */
7807 7807
7808 /* Final cleanup. image_wand should be the only resource left. */ 7808 /* Final cleanup. image_wand should be the only resource left. */
7809 DestroyMagickWand (image_wand); 7809 DestroyMagickWand (image_wand);
7810 /* `MagickWandTerminus' terminates the imagemagick environment. */
7810 MagickWandTerminus (); 7811 MagickWandTerminus ();
7811 7812
7812 return 1; 7813 return 1;
diff --git a/test/ChangeLog b/test/ChangeLog
index 3b1921c5987..695a51b7f4f 100644
--- a/test/ChangeLog
+++ b/test/ChangeLog
@@ -1,3 +1,11 @@
12011-01-13 Christian Ohler <ohler@gnu.org>
2
3 * automated: New directory for automated tests.
4
5 * automated/ert-tests.el, automated/ert-x-tests.el: New files.
6
7 * automated/Makefile.in: New file.
8
12010-11-11 Stefan Monnier <monnier@iro.umontreal.ca> 92010-11-11 Stefan Monnier <monnier@iro.umontreal.ca>
2 10
3 * indent/modula2.mod: New file. 11 * indent/modula2.mod: New file.
diff --git a/test/automated/Makefile.in b/test/automated/Makefile.in
new file mode 100644
index 00000000000..80a853056b1
--- /dev/null
+++ b/test/automated/Makefile.in
@@ -0,0 +1,158 @@
1# Maintenance productions for the automated test directory
2# Copyright (C) 2010, 2011 Free Software Foundation, Inc.
3
4# This file is part of GNU Emacs.
5
6# GNU Emacs is free software: you can redistribute it and/or modify
7# it under the terms of the GNU General Public License as published by
8# the Free Software Foundation, either version 3 of the License, or
9# (at your option) any later version.
10
11# GNU Emacs is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14# GNU General Public License for more details.
15
16# You should have received a copy of the GNU General Public License
17# along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
18
19SHELL = /bin/sh
20
21srcdir = @srcdir@
22top_srcdir = @top_srcdir@
23abs_top_builddir = @abs_top_builddir@
24test = $(srcdir)
25VPATH = $(srcdir)
26lispsrc = $(top_srcdir)/lisp
27lisp = ${abs_top_builddir}/lisp
28
29# You can specify a different executable on the make command line,
30# e.g. "make EMACS=../src/emacs ...".
31
32# We sometimes change directory before running Emacs (typically when
33# building out-of-tree, we chdir to the source directory), so we need
34# to use an absolute file name.
35EMACS = ${abs_top_builddir}/src/emacs
36
37# Command line flags for Emacs.
38
39EMACSOPT = -batch --no-site-file --no-site-lisp
40
41# Extra flags to pass to the byte compiler
42BYTE_COMPILE_EXTRA_FLAGS =
43# For example to not display the undefined function warnings you can use this:
44# BYTE_COMPILE_EXTRA_FLAGS = --eval '(setq byte-compile-warnings (quote (not unresolved)))'
45# The example above is just for developers, it should not be used by default.
46
47# The actual Emacs command run in the targets below.
48emacs = EMACSLOADPATH=$(lispsrc):$(test) LC_ALL=C $(EMACS) $(EMACSOPT)
49
50# Common command to find subdirectories
51setwins=subdirs=`(find . -type d -print)`; \
52 for file in $$subdirs; do \
53 case $$file in */.* | */.*/* | */=* ) ;; \
54 *) wins="$$wins $$file" ;; \
55 esac; \
56 done
57
58all: test
59
60doit:
61
62
63# Files MUST be compiled one by one. If we compile several files in a
64# row (i.e., in the same instance of Emacs) we can't make sure that
65# the compilation environment is clean. We also set the load-path of
66# the Emacs used for compilation to the current directory and its
67# subdirectories, to make sure require's and load's in the files being
68# compiled find the right files.
69
70.SUFFIXES: .elc .el
71
72# An old-fashioned suffix rule, which, according to the GNU Make manual,
73# cannot have prerequisites.
74.el.elc:
75 @echo Compiling $<
76 @$(emacs) $(BYTE_COMPILE_EXTRA_FLAGS) -f batch-byte-compile $<
77
78.PHONY: lisp-compile compile-main compile compile-always
79
80lisp-compile:
81 cd $(lisp); $(MAKE) $(MFLAGS) compile EMACS=$(EMACS)
82
83# In `compile-main' we could directly do
84# ... | xargs $(MAKE) $(MFLAGS) EMACS="$(EMACS)"
85# and it works, but it generates a lot of messages like
86# make[2]: « gnus/gnus-mlspl.elc » is up to date.
87# so instead, we use "xargs echo" to split the list of file into manageable
88# chunks and then use an intermediate `compile-targets' target so the
89# actual targets (the .elc files) are not mentioned as targets on the
90# make command line.
91
92
93.PHONY: compile-targets
94# TARGETS is set dynamically in the recursive call from `compile-main'.
95compile-targets: $(TARGETS)
96
97# Compile all the Elisp files that need it. Beware: it approximates
98# `no-byte-compile', so watch out for false-positives!
99compile-main: compile-clean lisp-compile
100 @(cd $(test); $(setwins); \
101 els=`echo "$$wins " | sed -e 's|/\./|/|g' -e 's|/\. | |g' -e 's| |/*.el |g'`; \
102 for el in $$els; do \
103 test -f $$el || continue; \
104 test ! -f $${el}c && GREP_OPTIONS= grep '^;.*no-byte-compile: t' $$el > /dev/null && continue; \
105 echo "$${el}c"; \
106 done | xargs echo) | \
107 while read chunk; do \
108 $(MAKE) $(MFLAGS) compile-targets EMACS="$(EMACS)" TARGETS="$$chunk"; \
109 done
110
111.PHONY: compile-clean
112# Erase left-over .elc files that do not have a corresponding .el file.
113compile-clean:
114 @cd $(test); $(setwins); \
115 elcs=`echo "$$wins " | sed -e 's|/\./|/|g' -e 's|/\. | |g' -e 's| |/*.elc |g'`; \
116 for el in $$(echo $$elcs | sed -e 's/\.elc/\.el/g'); do \
117 if test -f "$$el" -o \! -f "$${el}c"; then :; else \
118 echo rm "$${el}c"; \
119 rm "$${el}c"; \
120 fi \
121 done
122
123# Compile all Lisp files, but don't recompile those that are up to
124# date. Some .el files don't get compiled because they set the
125# local variable no-byte-compile.
126# Calling make recursively because suffix rule cannot have prerequisites.
127# Explicitly pass EMACS (sometimes ../src/bootstrap-emacs) to those
128# sub-makes that run rules that use it, for the sake of some non-GNU makes.
129compile: $(LOADDEFS) autoloads compile-first
130 $(MAKE) $(MFLAGS) compile-main EMACS=$(EMACS)
131
132# Compile all Lisp files. This is like `compile' but compiles files
133# unconditionally. Some files don't actually get compiled because they
134# set the local variable no-byte-compile.
135compile-always: doit
136 cd $(test); rm -f *.elc */*.elc */*/*.elc */*/*/*.elc
137 $(MAKE) $(MFLAGS) compile EMACS=$(EMACS)
138
139bootstrap-clean:
140 cd $(test); rm -f *.elc */*.elc */*/*.elc */*/*/*.elc
141
142distclean:
143 -rm -f ./Makefile
144
145maintainer-clean: distclean bootstrap-clean
146
147check: compile-main
148 @(cd $(test); $(setwins); \
149 pattern=`echo "$$wins " | sed -e 's|/\./|/|g' -e 's|/\. | |g' -e 's| |/*.el |g'`; \
150 for el in $$pattern; do \
151 test -f $$el || continue; \
152 args="$$args -l $$el"; \
153 els="$$els $$el"; \
154 done; \
155 echo Testing $$els; \
156 $(emacs) $$args -f ert-run-tests-batch-and-exit)
157
158# Makefile ends here.
diff --git a/test/automated/ert-tests.el b/test/automated/ert-tests.el
new file mode 100644
index 00000000000..3c9e2fef0c7
--- /dev/null
+++ b/test/automated/ert-tests.el
@@ -0,0 +1,949 @@
1;;; ert-tests.el --- ERT's self-tests
2
3;; Copyright (C) 2007, 2008, 2010, 2011 Free Software Foundation, Inc.
4
5;; Author: Christian Ohler <ohler@gnu.org>
6
7;; This file is part of GNU Emacs.
8
9;; This program is free software: you can redistribute it and/or
10;; modify it under the terms of the GNU General Public License as
11;; published by the Free Software Foundation, either version 3 of the
12;; License, or (at your option) any later version.
13;;
14;; This program is distributed in the hope that it will be useful, but
15;; WITHOUT ANY WARRANTY; without even the implied warranty of
16;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17;; General Public License for more details.
18;;
19;; You should have received a copy of the GNU General Public License
20;; along with this program. If not, see `http://www.gnu.org/licenses/'.
21
22;;; Commentary:
23
24;; This file is part of ERT, the Emacs Lisp Regression Testing tool.
25;; See ert.el or the texinfo manual for more details.
26
27;;; Code:
28
29(eval-when-compile
30 (require 'cl))
31(require 'ert)
32
33
34;;; Self-test that doesn't rely on ERT, for bootstrapping.
35
36;; This is used to test that bodies actually run.
37(defvar ert--test-body-was-run)
38(ert-deftest ert-test-body-runs ()
39 (setq ert--test-body-was-run t))
40
41(defun ert-self-test ()
42 "Run ERT's self-tests and make sure they actually ran."
43 (let ((window-configuration (current-window-configuration)))
44 (let ((ert--test-body-was-run nil))
45 ;; The buffer name chosen here should not compete with the default
46 ;; results buffer name for completion in `switch-to-buffer'.
47 (let ((stats (ert-run-tests-interactively "^ert-" " *ert self-tests*")))
48 (assert ert--test-body-was-run)
49 (if (zerop (ert-stats-completed-unexpected stats))
50 ;; Hide results window only when everything went well.
51 (set-window-configuration window-configuration)
52 (error "ERT self-test failed"))))))
53
54(defun ert-self-test-and-exit ()
55 "Run ERT's self-tests and exit Emacs.
56
57The exit code will be zero if the tests passed, nonzero if they
58failed or if there was a problem."
59 (unwind-protect
60 (progn
61 (ert-self-test)
62 (kill-emacs 0))
63 (unwind-protect
64 (progn
65 (message "Error running tests")
66 (backtrace))
67 (kill-emacs 1))))
68
69
70;;; Further tests are defined using ERT.
71
72(ert-deftest ert-test-nested-test-body-runs ()
73 "Test that nested test bodies run."
74 (lexical-let ((was-run nil))
75 (let ((test (make-ert-test :body (lambda ()
76 (setq was-run t)))))
77 (assert (not was-run))
78 (ert-run-test test)
79 (assert was-run))))
80
81
82;;; Test that pass/fail works.
83(ert-deftest ert-test-pass ()
84 (let ((test (make-ert-test :body (lambda ()))))
85 (let ((result (ert-run-test test)))
86 (assert (ert-test-passed-p result)))))
87
88(ert-deftest ert-test-fail ()
89 (let ((test (make-ert-test :body (lambda () (ert-fail "failure message")))))
90 (let ((result (let ((ert-debug-on-error nil))
91 (ert-run-test test))))
92 (assert (ert-test-failed-p result) t)
93 (assert (equal (ert-test-result-with-condition-condition result)
94 '(ert-test-failed "failure message"))
95 t))))
96
97(ert-deftest ert-test-fail-debug-with-condition-case ()
98 (let ((test (make-ert-test :body (lambda () (ert-fail "failure message")))))
99 (condition-case condition
100 (progn
101 (let ((ert-debug-on-error t))
102 (ert-run-test test))
103 (assert nil))
104 ((error)
105 (assert (equal condition '(ert-test-failed "failure message")) t)))))
106
107(ert-deftest ert-test-fail-debug-with-debugger-1 ()
108 (let ((test (make-ert-test :body (lambda () (ert-fail "failure message")))))
109 (let ((debugger (lambda (&rest debugger-args)
110 (assert nil))))
111 (let ((ert-debug-on-error nil))
112 (ert-run-test test)))))
113
114(ert-deftest ert-test-fail-debug-with-debugger-2 ()
115 (let ((test (make-ert-test :body (lambda () (ert-fail "failure message")))))
116 (block nil
117 (let ((debugger (lambda (&rest debugger-args)
118 (return-from nil nil))))
119 (let ((ert-debug-on-error t))
120 (ert-run-test test))
121 (assert nil)))))
122
123(ert-deftest ert-test-fail-debug-nested-with-debugger ()
124 (let ((test (make-ert-test :body (lambda ()
125 (let ((ert-debug-on-error t))
126 (ert-fail "failure message"))))))
127 (let ((debugger (lambda (&rest debugger-args)
128 (assert nil nil "Assertion a"))))
129 (let ((ert-debug-on-error nil))
130 (ert-run-test test))))
131 (let ((test (make-ert-test :body (lambda ()
132 (let ((ert-debug-on-error nil))
133 (ert-fail "failure message"))))))
134 (block nil
135 (let ((debugger (lambda (&rest debugger-args)
136 (return-from nil nil))))
137 (let ((ert-debug-on-error t))
138 (ert-run-test test))
139 (assert nil nil "Assertion b")))))
140
141(ert-deftest ert-test-error ()
142 (let ((test (make-ert-test :body (lambda () (error "Error message")))))
143 (let ((result (let ((ert-debug-on-error nil))
144 (ert-run-test test))))
145 (assert (ert-test-failed-p result) t)
146 (assert (equal (ert-test-result-with-condition-condition result)
147 '(error "Error message"))
148 t))))
149
150(ert-deftest ert-test-error-debug ()
151 (let ((test (make-ert-test :body (lambda () (error "Error message")))))
152 (condition-case condition
153 (progn
154 (let ((ert-debug-on-error t))
155 (ert-run-test test))
156 (assert nil))
157 ((error)
158 (assert (equal condition '(error "Error message")) t)))))
159
160
161;;; Test that `should' works.
162(ert-deftest ert-test-should ()
163 (let ((test (make-ert-test :body (lambda () (should nil)))))
164 (let ((result (let ((ert-debug-on-error nil))
165 (ert-run-test test))))
166 (assert (ert-test-failed-p result) t)
167 (assert (equal (ert-test-result-with-condition-condition result)
168 '(ert-test-failed ((should nil) :form nil :value nil)))
169 t)))
170 (let ((test (make-ert-test :body (lambda () (should t)))))
171 (let ((result (ert-run-test test)))
172 (assert (ert-test-passed-p result) t))))
173
174(ert-deftest ert-test-should-value ()
175 (should (eql (should 'foo) 'foo))
176 (should (eql (should 'bar) 'bar)))
177
178(ert-deftest ert-test-should-not ()
179 (let ((test (make-ert-test :body (lambda () (should-not t)))))
180 (let ((result (let ((ert-debug-on-error nil))
181 (ert-run-test test))))
182 (assert (ert-test-failed-p result) t)
183 (assert (equal (ert-test-result-with-condition-condition result)
184 '(ert-test-failed ((should-not t) :form t :value t)))
185 t)))
186 (let ((test (make-ert-test :body (lambda () (should-not nil)))))
187 (let ((result (ert-run-test test)))
188 (assert (ert-test-passed-p result)))))
189
190(ert-deftest ert-test-should-with-macrolet ()
191 (let ((test (make-ert-test :body (lambda ()
192 (macrolet ((foo () `(progn t nil)))
193 (should (foo)))))))
194 (let ((result (let ((ert-debug-on-error nil))
195 (ert-run-test test))))
196 (should (ert-test-failed-p result))
197 (should (equal
198 (ert-test-result-with-condition-condition result)
199 '(ert-test-failed ((should (foo))
200 :form (progn t nil)
201 :value nil)))))))
202
203(ert-deftest ert-test-should-error ()
204 ;; No error.
205 (let ((test (make-ert-test :body (lambda () (should-error (progn))))))
206 (let ((result (let ((ert-debug-on-error nil))
207 (ert-run-test test))))
208 (should (ert-test-failed-p result))
209 (should (equal (ert-test-result-with-condition-condition result)
210 '(ert-test-failed
211 ((should-error (progn))
212 :form (progn)
213 :value nil
214 :fail-reason "did not signal an error"))))))
215 ;; A simple error.
216 (should (equal (should-error (error "Foo"))
217 '(error "Foo")))
218 ;; Error of unexpected type.
219 (let ((test (make-ert-test :body (lambda ()
220 (should-error (error "Foo")
221 :type 'singularity-error)))))
222 (let ((result (ert-run-test test)))
223 (should (ert-test-failed-p result))
224 (should (equal
225 (ert-test-result-with-condition-condition result)
226 '(ert-test-failed
227 ((should-error (error "Foo") :type 'singularity-error)
228 :form (error "Foo")
229 :condition (error "Foo")
230 :fail-reason
231 "the error signalled did not have the expected type"))))))
232 ;; Error of the expected type.
233 (let* ((error nil)
234 (test (make-ert-test
235 :body (lambda ()
236 (setq error
237 (should-error (signal 'singularity-error nil)
238 :type 'singularity-error))))))
239 (let ((result (ert-run-test test)))
240 (should (ert-test-passed-p result))
241 (should (equal error '(singularity-error))))))
242
243(ert-deftest ert-test-should-error-subtypes ()
244 (should-error (signal 'singularity-error nil)
245 :type 'singularity-error
246 :exclude-subtypes t)
247 (let ((test (make-ert-test
248 :body (lambda ()
249 (should-error (signal 'arith-error nil)
250 :type 'singularity-error)))))
251 (let ((result (ert-run-test test)))
252 (should (ert-test-failed-p result))
253 (should (equal
254 (ert-test-result-with-condition-condition result)
255 '(ert-test-failed
256 ((should-error (signal 'arith-error nil)
257 :type 'singularity-error)
258 :form (signal arith-error nil)
259 :condition (arith-error)
260 :fail-reason
261 "the error signalled did not have the expected type"))))))
262 (let ((test (make-ert-test
263 :body (lambda ()
264 (should-error (signal 'arith-error nil)
265 :type 'singularity-error
266 :exclude-subtypes t)))))
267 (let ((result (ert-run-test test)))
268 (should (ert-test-failed-p result))
269 (should (equal
270 (ert-test-result-with-condition-condition result)
271 '(ert-test-failed
272 ((should-error (signal 'arith-error nil)
273 :type 'singularity-error
274 :exclude-subtypes t)
275 :form (signal arith-error nil)
276 :condition (arith-error)
277 :fail-reason
278 "the error signalled did not have the expected type"))))))
279 (let ((test (make-ert-test
280 :body (lambda ()
281 (should-error (signal 'singularity-error nil)
282 :type 'arith-error
283 :exclude-subtypes t)))))
284 (let ((result (ert-run-test test)))
285 (should (ert-test-failed-p result))
286 (should (equal
287 (ert-test-result-with-condition-condition result)
288 '(ert-test-failed
289 ((should-error (signal 'singularity-error nil)
290 :type 'arith-error
291 :exclude-subtypes t)
292 :form (signal singularity-error nil)
293 :condition (singularity-error)
294 :fail-reason
295 "the error signalled was a subtype of the expected type")))))
296 ))
297
298(defmacro ert--test-my-list (&rest args)
299 "Don't use this. Instead, call `list' with ARGS, it does the same thing.
300
301This macro is used to test if macroexpansion in `should' works."
302 `(list ,@args))
303
304(ert-deftest ert-test-should-failure-debugging ()
305 "Test that `should' errors contain the information we expect them to."
306 (loop for (body expected-condition) in
307 `((,(lambda () (let ((x nil)) (should x)))
308 (ert-test-failed ((should x) :form x :value nil)))
309 (,(lambda () (let ((x t)) (should-not x)))
310 (ert-test-failed ((should-not x) :form x :value t)))
311 (,(lambda () (let ((x t)) (should (not x))))
312 (ert-test-failed ((should (not x)) :form (not t) :value nil)))
313 (,(lambda () (let ((x nil)) (should-not (not x))))
314 (ert-test-failed ((should-not (not x)) :form (not nil) :value t)))
315 (,(lambda () (let ((x t) (y nil)) (should-not
316 (ert--test-my-list x y))))
317 (ert-test-failed
318 ((should-not (ert--test-my-list x y))
319 :form (list t nil)
320 :value (t nil))))
321 (,(lambda () (let ((x t)) (should (error "Foo"))))
322 (error "Foo")))
323 do
324 (let ((test (make-ert-test :body body)))
325 (condition-case actual-condition
326 (progn
327 (let ((ert-debug-on-error t))
328 (ert-run-test test))
329 (assert nil))
330 ((error)
331 (should (equal actual-condition expected-condition)))))))
332
333(ert-deftest ert-test-deftest ()
334 (should (equal (macroexpand '(ert-deftest abc () "foo" :tags '(bar)))
335 '(progn
336 (ert-set-test 'abc
337 (make-ert-test :name 'abc
338 :documentation "foo"
339 :tags '(bar)
340 :body (lambda ())))
341 (push '(ert-deftest . abc) current-load-list)
342 'abc)))
343 (should (equal (macroexpand '(ert-deftest def ()
344 :expected-result ':passed))
345 '(progn
346 (ert-set-test 'def
347 (make-ert-test :name 'def
348 :expected-result-type ':passed
349 :body (lambda ())))
350 (push '(ert-deftest . def) current-load-list)
351 'def)))
352 ;; :documentation keyword is forbidden
353 (should-error (macroexpand '(ert-deftest ghi ()
354 :documentation "foo"))))
355
356(ert-deftest ert-test-record-backtrace ()
357 (let ((test (make-ert-test :body (lambda () (ert-fail "foo")))))
358 (let ((result (ert-run-test test)))
359 (should (ert-test-failed-p result))
360 (with-temp-buffer
361 (ert--print-backtrace (ert-test-failed-backtrace result))
362 (goto-char (point-min))
363 (end-of-line)
364 (let ((first-line (buffer-substring-no-properties (point-min) (point))))
365 (should (equal first-line " signal(ert-test-failed (\"foo\"))")))))))
366
367(ert-deftest ert-test-messages ()
368 :tags '(:causes-redisplay)
369 (let* ((message-string "Test message")
370 (messages-buffer (get-buffer-create "*Messages*"))
371 (test (make-ert-test :body (lambda () (message "%s" message-string)))))
372 (with-current-buffer messages-buffer
373 (let ((result (ert-run-test test)))
374 (should (equal (concat message-string "\n")
375 (ert-test-result-messages result)))))))
376
377(ert-deftest ert-test-running-tests ()
378 (let ((outer-test (ert-get-test 'ert-test-running-tests)))
379 (should (equal (ert-running-test) outer-test))
380 (let (test1 test2 test3)
381 (setq test1 (make-ert-test
382 :name "1"
383 :body (lambda ()
384 (should (equal (ert-running-test) outer-test))
385 (should (equal ert--running-tests
386 (list test1 test2 test3
387 outer-test)))))
388 test2 (make-ert-test
389 :name "2"
390 :body (lambda ()
391 (should (equal (ert-running-test) outer-test))
392 (should (equal ert--running-tests
393 (list test3 test2 outer-test)))
394 (ert-run-test test1)))
395 test3 (make-ert-test
396 :name "3"
397 :body (lambda ()
398 (should (equal (ert-running-test) outer-test))
399 (should (equal ert--running-tests
400 (list test3 outer-test)))
401 (ert-run-test test2))))
402 (should (ert-test-passed-p (ert-run-test test3))))))
403
404(ert-deftest ert-test-test-result-expected-p ()
405 "Test `ert-test-result-expected-p' and (implicitly) `ert-test-result-type-p'."
406 ;; passing test
407 (let ((test (make-ert-test :body (lambda ()))))
408 (should (ert-test-result-expected-p test (ert-run-test test))))
409 ;; unexpected failure
410 (let ((test (make-ert-test :body (lambda () (ert-fail "failed")))))
411 (should-not (ert-test-result-expected-p test (ert-run-test test))))
412 ;; expected failure
413 (let ((test (make-ert-test :body (lambda () (ert-fail "failed"))
414 :expected-result-type ':failed)))
415 (should (ert-test-result-expected-p test (ert-run-test test))))
416 ;; `not' expected type
417 (let ((test (make-ert-test :body (lambda ())
418 :expected-result-type '(not :failed))))
419 (should (ert-test-result-expected-p test (ert-run-test test))))
420 (let ((test (make-ert-test :body (lambda ())
421 :expected-result-type '(not :passed))))
422 (should-not (ert-test-result-expected-p test (ert-run-test test))))
423 ;; `and' expected type
424 (let ((test (make-ert-test :body (lambda ())
425 :expected-result-type '(and :passed :failed))))
426 (should-not (ert-test-result-expected-p test (ert-run-test test))))
427 (let ((test (make-ert-test :body (lambda ())
428 :expected-result-type '(and :passed
429 (not :failed)))))
430 (should (ert-test-result-expected-p test (ert-run-test test))))
431 ;; `or' expected type
432 (let ((test (make-ert-test :body (lambda ())
433 :expected-result-type '(or (and :passed :failed)
434 :passed))))
435 (should (ert-test-result-expected-p test (ert-run-test test))))
436 (let ((test (make-ert-test :body (lambda ())
437 :expected-result-type '(or (and :passed :failed)
438 nil (not t)))))
439 (should-not (ert-test-result-expected-p test (ert-run-test test)))))
440
441;;; Test `ert-select-tests'.
442(ert-deftest ert-test-select-regexp ()
443 (should (equal (ert-select-tests "^ert-test-select-regexp$" t)
444 (list (ert-get-test 'ert-test-select-regexp)))))
445
446(ert-deftest ert-test-test-boundp ()
447 (should (ert-test-boundp 'ert-test-test-boundp))
448 (should-not (ert-test-boundp (make-symbol "ert-not-a-test"))))
449
450(ert-deftest ert-test-select-member ()
451 (should (equal (ert-select-tests '(member ert-test-select-member) t)
452 (list (ert-get-test 'ert-test-select-member)))))
453
454(ert-deftest ert-test-select-test ()
455 (should (equal (ert-select-tests (ert-get-test 'ert-test-select-test) t)
456 (list (ert-get-test 'ert-test-select-test)))))
457
458(ert-deftest ert-test-select-symbol ()
459 (should (equal (ert-select-tests 'ert-test-select-symbol t)
460 (list (ert-get-test 'ert-test-select-symbol)))))
461
462(ert-deftest ert-test-select-and ()
463 (let ((test (make-ert-test
464 :name nil
465 :body nil
466 :most-recent-result (make-ert-test-failed
467 :condition nil
468 :backtrace nil
469 :infos nil))))
470 (should (equal (ert-select-tests `(and (member ,test) :failed) t)
471 (list test)))))
472
473(ert-deftest ert-test-select-tag ()
474 (let ((test (make-ert-test
475 :name nil
476 :body nil
477 :tags '(a b))))
478 (should (equal (ert-select-tests `(tag a) (list test)) (list test)))
479 (should (equal (ert-select-tests `(tag b) (list test)) (list test)))
480 (should (equal (ert-select-tests `(tag c) (list test)) '()))))
481
482
483;;; Tests for utility functions.
484(ert-deftest ert-test-proper-list-p ()
485 (should (ert--proper-list-p '()))
486 (should (ert--proper-list-p '(1)))
487 (should (ert--proper-list-p '(1 2)))
488 (should (ert--proper-list-p '(1 2 3)))
489 (should (ert--proper-list-p '(1 2 3 4)))
490 (should (not (ert--proper-list-p 'a)))
491 (should (not (ert--proper-list-p '(1 . a))))
492 (should (not (ert--proper-list-p '(1 2 . a))))
493 (should (not (ert--proper-list-p '(1 2 3 . a))))
494 (should (not (ert--proper-list-p '(1 2 3 4 . a))))
495 (let ((a (list 1)))
496 (setf (cdr (last a)) a)
497 (should (not (ert--proper-list-p a))))
498 (let ((a (list 1 2)))
499 (setf (cdr (last a)) a)
500 (should (not (ert--proper-list-p a))))
501 (let ((a (list 1 2 3)))
502 (setf (cdr (last a)) a)
503 (should (not (ert--proper-list-p a))))
504 (let ((a (list 1 2 3 4)))
505 (setf (cdr (last a)) a)
506 (should (not (ert--proper-list-p a))))
507 (let ((a (list 1 2)))
508 (setf (cdr (last a)) (cdr a))
509 (should (not (ert--proper-list-p a))))
510 (let ((a (list 1 2 3)))
511 (setf (cdr (last a)) (cdr a))
512 (should (not (ert--proper-list-p a))))
513 (let ((a (list 1 2 3 4)))
514 (setf (cdr (last a)) (cdr a))
515 (should (not (ert--proper-list-p a))))
516 (let ((a (list 1 2 3)))
517 (setf (cdr (last a)) (cddr a))
518 (should (not (ert--proper-list-p a))))
519 (let ((a (list 1 2 3 4)))
520 (setf (cdr (last a)) (cddr a))
521 (should (not (ert--proper-list-p a))))
522 (let ((a (list 1 2 3 4)))
523 (setf (cdr (last a)) (cdddr a))
524 (should (not (ert--proper-list-p a)))))
525
526(ert-deftest ert-test-parse-keys-and-body ()
527 (should (equal (ert--parse-keys-and-body '(foo)) '(nil (foo))))
528 (should (equal (ert--parse-keys-and-body '(:bar foo)) '((:bar foo) nil)))
529 (should (equal (ert--parse-keys-and-body '(:bar foo a (b)))
530 '((:bar foo) (a (b)))))
531 (should (equal (ert--parse-keys-and-body '(:bar foo :a (b)))
532 '((:bar foo :a (b)) nil)))
533 (should (equal (ert--parse-keys-and-body '(bar foo :a (b)))
534 '(nil (bar foo :a (b)))))
535 (should-error (ert--parse-keys-and-body '(:bar foo :a))))
536
537
538(ert-deftest ert-test-run-tests-interactively ()
539 :tags '(:causes-redisplay)
540 (let ((passing-test (make-ert-test :name 'passing-test
541 :body (lambda () (ert-pass))))
542 (failing-test (make-ert-test :name 'failing-test
543 :body (lambda () (ert-fail
544 "failure message")))))
545 (let ((ert-debug-on-error nil))
546 (let* ((buffer-name (generate-new-buffer-name " *ert-test-run-tests*"))
547 (messages nil)
548 (mock-message-fn
549 (lambda (format-string &rest args)
550 (push (apply #'format format-string args) messages))))
551 (save-window-excursion
552 (unwind-protect
553 (let ((case-fold-search nil))
554 (ert-run-tests-interactively
555 `(member ,passing-test ,failing-test) buffer-name
556 mock-message-fn)
557 (should (equal messages `(,(concat
558 "Ran 2 tests, 1 results were "
559 "as expected, 1 unexpected"))))
560 (with-current-buffer buffer-name
561 (goto-char (point-min))
562 (should (equal
563 (buffer-substring (point-min)
564 (save-excursion
565 (forward-line 4)
566 (point)))
567 (concat
568 "Selector: (member <passing-test> <failing-test>)\n"
569 "Passed: 1\n"
570 "Failed: 1 (1 unexpected)\n"
571 "Total: 2/2\n")))))
572 (when (get-buffer buffer-name)
573 (kill-buffer buffer-name))))))))
574
575(ert-deftest ert-test-special-operator-p ()
576 (should (ert--special-operator-p 'if))
577 (should-not (ert--special-operator-p 'car))
578 (should-not (ert--special-operator-p 'ert--special-operator-p))
579 (let ((b (ert--gensym)))
580 (should-not (ert--special-operator-p b))
581 (fset b 'if)
582 (should (ert--special-operator-p b))))
583
584(ert-deftest ert-test-list-of-should-forms ()
585 (let ((test (make-ert-test :body (lambda ()
586 (should t)
587 (should (null '()))
588 (should nil)
589 (should t)))))
590 (let ((result (let ((ert-debug-on-error nil))
591 (ert-run-test test))))
592 (should (equal (ert-test-result-should-forms result)
593 '(((should t) :form t :value t)
594 ((should (null '())) :form (null nil) :value t)
595 ((should nil) :form nil :value nil)))))))
596
597(ert-deftest ert-test-list-of-should-forms-observers-should-not-stack ()
598 (let ((test (make-ert-test
599 :body (lambda ()
600 (let ((test2 (make-ert-test
601 :body (lambda ()
602 (should t)))))
603 (let ((result (ert-run-test test2)))
604 (should (ert-test-passed-p result))))))))
605 (let ((result (let ((ert-debug-on-error nil))
606 (ert-run-test test))))
607 (should (ert-test-passed-p result))
608 (should (eql (length (ert-test-result-should-forms result))
609 1)))))
610
611(ert-deftest ert-test-list-of-should-forms-no-deep-copy ()
612 (let ((test (make-ert-test :body (lambda ()
613 (let ((obj (list 'a)))
614 (should (equal obj '(a)))
615 (setf (car obj) 'b)
616 (should (equal obj '(b))))))))
617 (let ((result (let ((ert-debug-on-error nil))
618 (ert-run-test test))))
619 (should (ert-test-passed-p result))
620 (should (equal (ert-test-result-should-forms result)
621 '(((should (equal obj '(a))) :form (equal (b) (a)) :value t
622 :explanation nil)
623 ((should (equal obj '(b))) :form (equal (b) (b)) :value t
624 :explanation nil)
625 ))))))
626
627(ert-deftest ert-test-remprop ()
628 (let ((x (ert--gensym)))
629 (should (equal (symbol-plist x) '()))
630 ;; Remove nonexistent property on empty plist.
631 (ert--remprop x 'b)
632 (should (equal (symbol-plist x) '()))
633 (put x 'a 1)
634 (should (equal (symbol-plist x) '(a 1)))
635 ;; Remove nonexistent property on nonempty plist.
636 (ert--remprop x 'b)
637 (should (equal (symbol-plist x) '(a 1)))
638 (put x 'b 2)
639 (put x 'c 3)
640 (put x 'd 4)
641 (should (equal (symbol-plist x) '(a 1 b 2 c 3 d 4)))
642 ;; Remove property that is neither first nor last.
643 (ert--remprop x 'c)
644 (should (equal (symbol-plist x) '(a 1 b 2 d 4)))
645 ;; Remove last property from a plist of length >1.
646 (ert--remprop x 'd)
647 (should (equal (symbol-plist x) '(a 1 b 2)))
648 ;; Remove first property from a plist of length >1.
649 (ert--remprop x 'a)
650 (should (equal (symbol-plist x) '(b 2)))
651 ;; Remove property when there is only one.
652 (ert--remprop x 'b)
653 (should (equal (symbol-plist x) '()))))
654
655(ert-deftest ert-test-remove-if-not ()
656 (let ((list (list 'a 'b 'c 'd))
657 (i 0))
658 (let ((result (ert--remove-if-not (lambda (x)
659 (should (eql x (nth i list)))
660 (incf i)
661 (member i '(2 3)))
662 list)))
663 (should (equal i 4))
664 (should (equal result '(b c)))
665 (should (equal list '(a b c d)))))
666 (should (equal '()
667 (ert--remove-if-not (lambda (x) (should nil)) '()))))
668
669(ert-deftest ert-test-remove* ()
670 (let ((list (list 'a 'b 'c 'd))
671 (key-index 0)
672 (test-index 0))
673 (let ((result
674 (ert--remove* 'foo list
675 :key (lambda (x)
676 (should (eql x (nth key-index list)))
677 (prog1
678 (list key-index x)
679 (incf key-index)))
680 :test
681 (lambda (a b)
682 (should (eql a 'foo))
683 (should (equal b (list test-index
684 (nth test-index list))))
685 (incf test-index)
686 (member test-index '(2 3))))))
687 (should (equal key-index 4))
688 (should (equal test-index 4))
689 (should (equal result '(a d)))
690 (should (equal list '(a b c d)))))
691 (let ((x (cons nil nil))
692 (y (cons nil nil)))
693 (should (equal (ert--remove* x (list x y))
694 ;; or (list x), since we use `equal' -- the
695 ;; important thing is that only one element got
696 ;; removed, this proves that the default test is
697 ;; `eql', not `equal'
698 (list y)))))
699
700
701(ert-deftest ert-test-set-functions ()
702 (let ((c1 (cons nil nil))
703 (c2 (cons nil nil))
704 (sym (make-symbol "a")))
705 (let ((e '())
706 (a (list 'a 'b sym nil "" "x" c1 c2))
707 (b (list c1 'y 'b sym 'x)))
708 (should (equal (ert--set-difference e e) e))
709 (should (equal (ert--set-difference a e) a))
710 (should (equal (ert--set-difference e a) e))
711 (should (equal (ert--set-difference a a) e))
712 (should (equal (ert--set-difference b e) b))
713 (should (equal (ert--set-difference e b) e))
714 (should (equal (ert--set-difference b b) e))
715 (should (equal (ert--set-difference a b) (list 'a nil "" "x" c2)))
716 (should (equal (ert--set-difference b a) (list 'y 'x)))
717
718 ;; We aren't testing whether this is really using `eq' rather than `eql'.
719 (should (equal (ert--set-difference-eq e e) e))
720 (should (equal (ert--set-difference-eq a e) a))
721 (should (equal (ert--set-difference-eq e a) e))
722 (should (equal (ert--set-difference-eq a a) e))
723 (should (equal (ert--set-difference-eq b e) b))
724 (should (equal (ert--set-difference-eq e b) e))
725 (should (equal (ert--set-difference-eq b b) e))
726 (should (equal (ert--set-difference-eq a b) (list 'a nil "" "x" c2)))
727 (should (equal (ert--set-difference-eq b a) (list 'y 'x)))
728
729 (should (equal (ert--union e e) e))
730 (should (equal (ert--union a e) a))
731 (should (equal (ert--union e a) a))
732 (should (equal (ert--union a a) a))
733 (should (equal (ert--union b e) b))
734 (should (equal (ert--union e b) b))
735 (should (equal (ert--union b b) b))
736 (should (equal (ert--union a b) (list 'a 'b sym nil "" "x" c1 c2 'y 'x)))
737 (should (equal (ert--union b a) (list c1 'y 'b sym 'x 'a nil "" "x" c2)))
738
739 (should (equal (ert--intersection e e) e))
740 (should (equal (ert--intersection a e) e))
741 (should (equal (ert--intersection e a) e))
742 (should (equal (ert--intersection a a) a))
743 (should (equal (ert--intersection b e) e))
744 (should (equal (ert--intersection e b) e))
745 (should (equal (ert--intersection b b) b))
746 (should (equal (ert--intersection a b) (list 'b sym c1)))
747 (should (equal (ert--intersection b a) (list c1 'b sym))))))
748
749(ert-deftest ert-test-gensym ()
750 ;; Since the expansion of `should' calls `ert--gensym' and thus has a
751 ;; side-effect on `ert--gensym-counter', we have to make sure all
752 ;; macros in our test body are expanded before we rebind
753 ;; `ert--gensym-counter' and run the body. Otherwise, the test would
754 ;; fail if run interpreted.
755 (let ((body (byte-compile
756 '(lambda ()
757 (should (equal (symbol-name (ert--gensym)) "G0"))
758 (should (equal (symbol-name (ert--gensym)) "G1"))
759 (should (equal (symbol-name (ert--gensym)) "G2"))
760 (should (equal (symbol-name (ert--gensym "foo")) "foo3"))
761 (should (equal (symbol-name (ert--gensym "bar")) "bar4"))
762 (should (equal ert--gensym-counter 5))))))
763 (let ((ert--gensym-counter 0))
764 (funcall body))))
765
766(ert-deftest ert-test-coerce-to-vector ()
767 (let* ((a (vector))
768 (b (vector 1 a 3))
769 (c (list))
770 (d (list b a)))
771 (should (eql (ert--coerce-to-vector a) a))
772 (should (eql (ert--coerce-to-vector b) b))
773 (should (equal (ert--coerce-to-vector c) (vector)))
774 (should (equal (ert--coerce-to-vector d) (vector b a)))))
775
776(ert-deftest ert-test-string-position ()
777 (should (eql (ert--string-position ?x "") nil))
778 (should (eql (ert--string-position ?a "abc") 0))
779 (should (eql (ert--string-position ?b "abc") 1))
780 (should (eql (ert--string-position ?c "abc") 2))
781 (should (eql (ert--string-position ?d "abc") nil))
782 (should (eql (ert--string-position ?A "abc") nil)))
783
784(ert-deftest ert-test-mismatch ()
785 (should (eql (ert--mismatch "" "") nil))
786 (should (eql (ert--mismatch "" "a") 0))
787 (should (eql (ert--mismatch "a" "a") nil))
788 (should (eql (ert--mismatch "ab" "a") 1))
789 (should (eql (ert--mismatch "Aa" "aA") 0))
790 (should (eql (ert--mismatch '(a b c) '(a b d)) 2)))
791
792(ert-deftest ert-test-string-first-line ()
793 (should (equal (ert--string-first-line "") ""))
794 (should (equal (ert--string-first-line "abc") "abc"))
795 (should (equal (ert--string-first-line "abc\n") "abc"))
796 (should (equal (ert--string-first-line "foo\nbar") "foo"))
797 (should (equal (ert--string-first-line " foo\nbar\nbaz\n") " foo")))
798
799(ert-deftest ert-test-explain-not-equal ()
800 (should (equal (ert--explain-not-equal nil 'foo)
801 '(different-atoms nil foo)))
802 (should (equal (ert--explain-not-equal '(a a) '(a b))
803 '(list-elt 1 (different-atoms a b))))
804 (should (equal (ert--explain-not-equal '(1 48) '(1 49))
805 '(list-elt 1 (different-atoms (48 "#x30" "?0")
806 (49 "#x31" "?1")))))
807 (should (equal (ert--explain-not-equal 'nil '(a))
808 '(different-types nil (a))))
809 (should (equal (ert--explain-not-equal '(a b c) '(a b c d))
810 '(proper-lists-of-different-length 3 4 (a b c) (a b c d)
811 first-mismatch-at 3)))
812 (let ((sym (make-symbol "a")))
813 (should (equal (ert--explain-not-equal 'a sym)
814 `(different-symbols-with-the-same-name a ,sym)))))
815
816(ert-deftest ert-test-explain-not-equal-improper-list ()
817 (should (equal (ert--explain-not-equal '(a . b) '(a . c))
818 '(cdr (different-atoms b c)))))
819
820(ert-deftest ert-test-significant-plist-keys ()
821 (should (equal (ert--significant-plist-keys '()) '()))
822 (should (equal (ert--significant-plist-keys '(a b c d e f c g p q r nil s t))
823 '(a c e p s))))
824
825(ert-deftest ert-test-plist-difference-explanation ()
826 (should (equal (ert--plist-difference-explanation
827 '(a b c nil) '(a b))
828 nil))
829 (should (equal (ert--plist-difference-explanation
830 '(a b c t) '(a b))
831 '(different-properties-for-key c (different-atoms t nil))))
832 (should (equal (ert--plist-difference-explanation
833 '(a b c t) '(c nil a b))
834 '(different-properties-for-key c (different-atoms t nil))))
835 (should (equal (ert--plist-difference-explanation
836 '(a b c (foo . bar)) '(c (foo . baz) a b))
837 '(different-properties-for-key c
838 (cdr
839 (different-atoms bar baz))))))
840
841(ert-deftest ert-test-abbreviate-string ()
842 (should (equal (ert--abbreviate-string "foo" 4 nil) "foo"))
843 (should (equal (ert--abbreviate-string "foo" 3 nil) "foo"))
844 (should (equal (ert--abbreviate-string "foo" 3 nil) "foo"))
845 (should (equal (ert--abbreviate-string "foo" 2 nil) "fo"))
846 (should (equal (ert--abbreviate-string "foo" 1 nil) "f"))
847 (should (equal (ert--abbreviate-string "foo" 0 nil) ""))
848 (should (equal (ert--abbreviate-string "bar" 4 t) "bar"))
849 (should (equal (ert--abbreviate-string "bar" 3 t) "bar"))
850 (should (equal (ert--abbreviate-string "bar" 3 t) "bar"))
851 (should (equal (ert--abbreviate-string "bar" 2 t) "ar"))
852 (should (equal (ert--abbreviate-string "bar" 1 t) "r"))
853 (should (equal (ert--abbreviate-string "bar" 0 t) "")))
854
855(ert-deftest ert-test-explain-not-equal-string-properties ()
856 (should
857 (equal (ert--explain-not-equal-including-properties #("foo" 0 1 (a b))
858 "foo")
859 '(char 0 "f"
860 (different-properties-for-key a (different-atoms b nil))
861 context-before ""
862 context-after "oo")))
863 (should (equal (ert--explain-not-equal-including-properties
864 #("foo" 1 3 (a b))
865 #("goo" 0 1 (c d)))
866 '(array-elt 0 (different-atoms (?f "#x66" "?f")
867 (?g "#x67" "?g")))))
868 (should
869 (equal (ert--explain-not-equal-including-properties
870 #("foo" 0 1 (a b c d) 1 3 (a b))
871 #("foo" 0 1 (c d a b) 1 2 (a foo)))
872 '(char 1 "o" (different-properties-for-key a (different-atoms b foo))
873 context-before "f" context-after "o"))))
874
875(ert-deftest ert-test-equal-including-properties ()
876 (should (equal-including-properties "foo" "foo"))
877 (should (ert-equal-including-properties "foo" "foo"))
878
879 (should (equal-including-properties #("foo" 0 3 (a b))
880 (propertize "foo" 'a 'b)))
881 (should (ert-equal-including-properties #("foo" 0 3 (a b))
882 (propertize "foo" 'a 'b)))
883
884 (should (equal-including-properties #("foo" 0 3 (a b c d))
885 (propertize "foo" 'a 'b 'c 'd)))
886 (should (ert-equal-including-properties #("foo" 0 3 (a b c d))
887 (propertize "foo" 'a 'b 'c 'd)))
888
889 (should-not (equal-including-properties #("foo" 0 3 (a b c e))
890 (propertize "foo" 'a 'b 'c 'd)))
891 (should-not (ert-equal-including-properties #("foo" 0 3 (a b c e))
892 (propertize "foo" 'a 'b 'c 'd)))
893
894 ;; This is bug 6581.
895 (should-not (equal-including-properties #("foo" 0 3 (a (t)))
896 (propertize "foo" 'a (list t))))
897 (should (ert-equal-including-properties #("foo" 0 3 (a (t)))
898 (propertize "foo" 'a (list t)))))
899
900(ert-deftest ert-test-stats-set-test-and-result ()
901 (let* ((test-1 (make-ert-test :name 'test-1
902 :body (lambda () nil)))
903 (test-2 (make-ert-test :name 'test-2
904 :body (lambda () nil)))
905 (test-3 (make-ert-test :name 'test-2
906 :body (lambda () nil)))
907 (stats (ert--make-stats (list test-1 test-2) 't))
908 (failed (make-ert-test-failed :condition nil
909 :backtrace nil
910 :infos nil)))
911 (should (eql 2 (ert-stats-total stats)))
912 (should (eql 0 (ert-stats-completed stats)))
913 (should (eql 0 (ert-stats-completed-expected stats)))
914 (should (eql 0 (ert-stats-completed-unexpected stats)))
915 (ert--stats-set-test-and-result stats 0 test-1 (make-ert-test-passed))
916 (should (eql 2 (ert-stats-total stats)))
917 (should (eql 1 (ert-stats-completed stats)))
918 (should (eql 1 (ert-stats-completed-expected stats)))
919 (should (eql 0 (ert-stats-completed-unexpected stats)))
920 (ert--stats-set-test-and-result stats 0 test-1 failed)
921 (should (eql 2 (ert-stats-total stats)))
922 (should (eql 1 (ert-stats-completed stats)))
923 (should (eql 0 (ert-stats-completed-expected stats)))
924 (should (eql 1 (ert-stats-completed-unexpected stats)))
925 (ert--stats-set-test-and-result stats 0 test-1 nil)
926 (should (eql 2 (ert-stats-total stats)))
927 (should (eql 0 (ert-stats-completed stats)))
928 (should (eql 0 (ert-stats-completed-expected stats)))
929 (should (eql 0 (ert-stats-completed-unexpected stats)))
930 (ert--stats-set-test-and-result stats 0 test-3 failed)
931 (should (eql 2 (ert-stats-total stats)))
932 (should (eql 1 (ert-stats-completed stats)))
933 (should (eql 0 (ert-stats-completed-expected stats)))
934 (should (eql 1 (ert-stats-completed-unexpected stats)))
935 (ert--stats-set-test-and-result stats 1 test-2 (make-ert-test-passed))
936 (should (eql 2 (ert-stats-total stats)))
937 (should (eql 2 (ert-stats-completed stats)))
938 (should (eql 1 (ert-stats-completed-expected stats)))
939 (should (eql 1 (ert-stats-completed-unexpected stats)))
940 (ert--stats-set-test-and-result stats 0 test-1 (make-ert-test-passed))
941 (should (eql 2 (ert-stats-total stats)))
942 (should (eql 2 (ert-stats-completed stats)))
943 (should (eql 2 (ert-stats-completed-expected stats)))
944 (should (eql 0 (ert-stats-completed-unexpected stats)))))
945
946
947(provide 'ert-tests)
948
949;;; ert-tests.el ends here
diff --git a/test/automated/ert-x-tests.el b/test/automated/ert-x-tests.el
new file mode 100644
index 00000000000..80fff40d86a
--- /dev/null
+++ b/test/automated/ert-x-tests.el
@@ -0,0 +1,273 @@
1;;; ert-x-tests.el --- Tests for ert-x.el
2
3;; Copyright (C) 2008, 2010, 2011 Free Software Foundation, Inc.
4
5;; Author: Phil Hagelberg
6;; Author: Christian Ohler <ohler@gnu.org>
7
8;; This file is part of GNU Emacs.
9
10;; This program is free software: you can redistribute it and/or
11;; modify it under the terms of the GNU General Public License as
12;; published by the Free Software Foundation, either version 3 of the
13;; License, or (at your option) any later version.
14;;
15;; This program is distributed in the hope that it will be useful, but
16;; WITHOUT ANY WARRANTY; without even the implied warranty of
17;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18;; General Public License for more details.
19;;
20;; You should have received a copy of the GNU General Public License
21;; along with this program. If not, see `http://www.gnu.org/licenses/'.
22
23;;; Commentary:
24
25;; This file is part of ERT, the Emacs Lisp Regression Testing tool.
26;; See ert.el or the texinfo manual for more details.
27
28;;; Code:
29
30(eval-when-compile
31 (require 'cl))
32(require 'ert)
33(require 'ert-x)
34
35;;; Utilities
36
37(ert-deftest ert-test-buffer-string-reindented ()
38 (ert-with-test-buffer (:name "well-indented")
39 (insert (concat "(hello (world\n"
40 " 'elisp)\n"))
41 (emacs-lisp-mode)
42 (should (equal (ert-buffer-string-reindented) (buffer-string))))
43 (ert-with-test-buffer (:name "badly-indented")
44 (insert (concat "(hello\n"
45 " world)"))
46 (emacs-lisp-mode)
47 (should-not (equal (ert-buffer-string-reindented) (buffer-string)))))
48
49(defun ert--hash-table-to-alist (table)
50 (let ((accu nil))
51 (maphash (lambda (key value)
52 (push (cons key value) accu))
53 table)
54 (nreverse accu)))
55
56(ert-deftest ert-test-test-buffers ()
57 (let (buffer-1
58 buffer-2)
59 (let ((test-1
60 (make-ert-test
61 :name 'test-1
62 :body (lambda ()
63 (ert-with-test-buffer (:name "foo")
64 (should (string-match
65 "[*]Test buffer (ert-test-test-buffers): foo[*]"
66 (buffer-name)))
67 (setq buffer-1 (current-buffer))))))
68 (test-2
69 (make-ert-test
70 :name 'test-2
71 :body (lambda ()
72 (ert-with-test-buffer (:name "bar")
73 (should (string-match
74 "[*]Test buffer (ert-test-test-buffers): bar[*]"
75 (buffer-name)))
76 (setq buffer-2 (current-buffer))
77 (ert-fail "fail for test"))))))
78 (let ((ert--test-buffers (make-hash-table :weakness t)))
79 (ert-run-tests `(member ,test-1 ,test-2) #'ignore)
80 (should (equal (ert--hash-table-to-alist ert--test-buffers)
81 `((,buffer-2 . t))))
82 (should-not (buffer-live-p buffer-1))
83 (should (buffer-live-p buffer-2))))))
84
85
86(ert-deftest ert-filter-string ()
87 (should (equal (ert-filter-string "foo bar baz" "quux")
88 "foo bar baz"))
89 (should (equal (ert-filter-string "foo bar baz" "bar")
90 "foo baz")))
91
92(ert-deftest ert-propertized-string ()
93 (should (ert-equal-including-properties
94 (ert-propertized-string "a" '(a b) "b" '(c t) "cd")
95 #("abcd" 1 2 (a b) 2 4 (c t))))
96 (should (ert-equal-including-properties
97 (ert-propertized-string "foo " '(face italic) "bar" " baz" nil
98 " quux")
99 #("foo bar baz quux" 4 11 (face italic)))))
100
101
102;;; Tests for ERT itself that require test features from ert-x.el.
103
104(ert-deftest ert-test-run-tests-interactively-2 ()
105 :tags '(:causes-redisplay)
106 (let ((passing-test (make-ert-test :name 'passing-test
107 :body (lambda () (ert-pass))))
108 (failing-test (make-ert-test :name 'failing-test
109 :body (lambda ()
110 (ert-info ((propertize "foo\nbar"
111 'a 'b))
112 (ert-fail
113 "failure message"))))))
114 (let ((ert-debug-on-error nil))
115 (let* ((buffer-name (generate-new-buffer-name "*ert-test-run-tests*"))
116 (messages nil)
117 (mock-message-fn
118 (lambda (format-string &rest args)
119 (push (apply #'format format-string args) messages))))
120 (flet ((expected-string (with-font-lock-p)
121 (ert-propertized-string
122 "Selector: (member <passing-test> <failing-test>)\n"
123 "Passed: 1\n"
124 "Failed: 1 (1 unexpected)\n"
125 "Total: 2/2\n\n"
126 "Started at:\n"
127 "Finished.\n"
128 "Finished at:\n\n"
129 `(category ,(button-category-symbol
130 'ert--results-progress-bar-button)
131 button (t)
132 face ,(if with-font-lock-p
133 'ert-test-result-unexpected
134 'button))
135 ".F" nil "\n\n"
136 `(category ,(button-category-symbol
137 'ert--results-expand-collapse-button)
138 button (t)
139 face ,(if with-font-lock-p
140 'ert-test-result-unexpected
141 'button))
142 "F" nil " "
143 `(category ,(button-category-symbol
144 'ert--test-name-button)
145 button (t)
146 ert-test-name failing-test)
147 "failing-test"
148 nil "\n Info: " '(a b) "foo\n"
149 nil " " '(a b) "bar"
150 nil "\n (ert-test-failed \"failure message\")\n\n\n"
151 )))
152 (save-window-excursion
153 (unwind-protect
154 (let ((case-fold-search nil))
155 (ert-run-tests-interactively
156 `(member ,passing-test ,failing-test) buffer-name
157 mock-message-fn)
158 (should (equal messages `(,(concat
159 "Ran 2 tests, 1 results were "
160 "as expected, 1 unexpected"))))
161 (with-current-buffer buffer-name
162 (font-lock-mode 0)
163 (should (ert-equal-including-properties
164 (ert-filter-string (buffer-string)
165 '("Started at:\\(.*\\)$" 1)
166 '("Finished at:\\(.*\\)$" 1))
167 (expected-string nil)))
168 ;; `font-lock-mode' only works if interactive, so
169 ;; pretend we are.
170 (let ((noninteractive nil))
171 (font-lock-mode 1))
172 (should (ert-equal-including-properties
173 (ert-filter-string (buffer-string)
174 '("Started at:\\(.*\\)$" 1)
175 '("Finished at:\\(.*\\)$" 1))
176 (expected-string t)))))
177 (when (get-buffer buffer-name)
178 (kill-buffer buffer-name)))))))))
179
180(ert-deftest ert-test-describe-test ()
181 "Tests `ert-describe-test'."
182 (save-window-excursion
183 (ert-with-buffer-renamed ("*Help*")
184 (if (< emacs-major-version 24)
185 (should (equal (should-error (ert-describe-test 'ert-describe-test))
186 '(error "Requires Emacs 24")))
187 (ert-describe-test 'ert-test-describe-test)
188 (with-current-buffer "*Help*"
189 (let ((case-fold-search nil))
190 (should (string-match (concat
191 "\\`ert-test-describe-test is a test"
192 " defined in `ert-x-tests.elc?'\\.\n\n"
193 "Tests `ert-describe-test'\\.\n\\'")
194 (buffer-string)))))))))
195
196(ert-deftest ert-test-message-log-truncation ()
197 :tags '(:causes-redisplay)
198 (let ((test (make-ert-test
199 :body (lambda ()
200 ;; Emacs would combine messages if we
201 ;; generate the same message multiple
202 ;; times.
203 (message "a")
204 (message "b")
205 (message "c")
206 (message "d")))))
207 (let (result)
208 (ert-with-buffer-renamed ("*Messages*")
209 (let ((message-log-max 2))
210 (setq result (ert-run-test test)))
211 (should (equal (with-current-buffer "*Messages*"
212 (buffer-string))
213 "c\nd\n")))
214 (should (equal (ert-test-result-messages result) "a\nb\nc\nd\n")))))
215
216(ert-deftest ert-test-builtin-message-log-flushing ()
217 "This test attempts to demonstrate that there is no way to
218force immediate truncation of the *Messages* buffer from Lisp
219\(and hence justifies the existence of
220`ert--force-message-log-buffer-truncation'\): The only way that
221came to my mind was \(message \"\"\), which doesn't have the
222desired effect."
223 :tags '(:causes-redisplay)
224 (ert-with-buffer-renamed ("*Messages*")
225 (with-current-buffer "*Messages*"
226 (should (equal (buffer-string) ""))
227 ;; We used to get sporadic failures in this test that involved
228 ;; a spurious newline at the beginning of the buffer, before
229 ;; the first message. Below, we print a message and erase the
230 ;; buffer since this seems to eliminate the sporadic failures.
231 (message "foo")
232 (erase-buffer)
233 (should (equal (buffer-string) ""))
234 (let ((message-log-max 2))
235 (let ((message-log-max t))
236 (loop for i below 4 do
237 (message "%s" i))
238 (should (equal (buffer-string) "0\n1\n2\n3\n")))
239 (should (equal (buffer-string) "0\n1\n2\n3\n"))
240 (message "")
241 (should (equal (buffer-string) "0\n1\n2\n3\n"))
242 (message "Test message")
243 (should (equal (buffer-string) "3\nTest message\n"))))))
244
245(ert-deftest ert-test-force-message-log-buffer-truncation ()
246 :tags '(:causes-redisplay)
247 (labels ((body ()
248 (loop for i below 3 do
249 (message "%s" i)))
250 ;; Uses the implicit messages buffer truncation implemented
251 ;; in Emacs' C core.
252 (c (x)
253 (ert-with-buffer-renamed ("*Messages*")
254 (let ((message-log-max x))
255 (body))
256 (with-current-buffer "*Messages*"
257 (buffer-string))))
258 ;; Uses our lisp reimplementation.
259 (lisp (x)
260 (ert-with-buffer-renamed ("*Messages*")
261 (let ((message-log-max t))
262 (body))
263 (let ((message-log-max x))
264 (ert--force-message-log-buffer-truncation))
265 (with-current-buffer "*Messages*"
266 (buffer-string)))))
267 (loop for x in '(0 1 2 3 4 t) do
268 (should (equal (c x) (lisp x))))))
269
270
271(provide 'ert-x-tests)
272
273;;; ert-x-tests.el ends here