diff options
| author | Paul Eggert | 2011-01-13 09:17:33 -0800 |
|---|---|---|
| committer | Paul Eggert | 2011-01-13 09:17:33 -0800 |
| commit | 193770eec942defa96c7ea310773b40534f709d1 (patch) | |
| tree | 3a1ed2de2b7ece4697999045da27e21fb174f21c | |
| parent | f737437b23c75bb6924021df14b4f740ce370b21 (diff) | |
| parent | 821f936d1c04df2f9ccaf6307b220d7cbe0e76c7 (diff) | |
| download | emacs-193770eec942defa96c7ea310773b40534f709d1.tar.gz emacs-193770eec942defa96c7ea310773b40534f709d1.zip | |
Merge from mainline.
34 files changed, 5375 insertions, 221 deletions
| @@ -1,4 +1,4 @@ | |||
| 1 | 2011-01-11 Paul Eggert <eggert@cs.ucla.edu> | 1 | 2011-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 | ||
| 109 | 2011-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 | |||
| 109 | 2011-01-07 Paul Eggert <eggert@cs.ucla.edu> | 118 | 2011-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 \ | |||
| 134 | infodir=@infodir@ | 134 | infodir=@infodir@ |
| 135 | INFO_FILES=ada-mode auth autotype calc ccmode cl dbus dired-x ebrowse \ | 135 | INFO_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)'` | |||
| 267 | SUBDIR = lib lib-src src lisp | 267 | SUBDIR = lib lib-src src lisp |
| 268 | 268 | ||
| 269 | # The subdir makefiles created by config.status. | 269 | # The subdir makefiles created by config.status. |
| 270 | SUBDIR_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 | 270 | SUBDIR_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 | ||
| 401 | config.status: ${srcdir}/configure ${srcdir}/lisp/version.el | 402 | config.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 | ||
| 857 | check: | 858 | check: |
| 858 | @echo "We don't have any tests for GNU Emacs yet." | 859 | cd test/automated; $(MAKE) check |
| 859 | 860 | ||
| 860 | dist: | 861 | dist: |
| 861 | cd ${srcdir}; ./make-dist | 862 | cd ${srcdir}; ./make-dist |
| @@ -17389,7 +17389,7 @@ test "${prefix}" != NONE && | |||
| 17389 | test "${exec_prefix}" != NONE && | 17389 | test "${exec_prefix}" != NONE && |
| 17390 | exec_prefix=`echo "${exec_prefix}" | sed 's,\([^/]\)/*$,\1,'` | 17390 | exec_prefix=`echo "${exec_prefix}" | sed 's,\([^/]\)/*$,\1,'` |
| 17391 | 17391 | ||
| 17392 | ac_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" | 17392 | ac_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 | ||
| 17394 | ac_config_commands="$ac_config_commands default" | 17394 | ac_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. | |||
| 3718 | AC_OUTPUT(Makefile lib/Makefile lib-src/Makefile oldXMenu/Makefile \ | 3718 | AC_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. |
| 3724 | for dir in etc lisp ; do | 3724 | for 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 @@ | |||
| 1 | 2011-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 | |||
| 1 | 2011-01-10 Jan Moringen <jan.moringen@uni-bielefeld.de> | 8 | 2011-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 | |||
| 360 | erc.pdf: ${srcdir}/erc.texi | 363 | erc.pdf: ${srcdir}/erc.texi |
| 361 | $(ENVADD) $(TEXI2PDF) $< | 364 | $(ENVADD) $(TEXI2PDF) $< |
| 362 | 365 | ||
| 366 | ert : $(infodir)/ert | ||
| 367 | $(infodir)/ert: ert.texi $(infodir) | ||
| 368 | cd $(srcdir); $(MAKEINFO) ert.texi | ||
| 369 | ert.dvi: ert.texi | ||
| 370 | $(ENVADD) $(TEXI2DVI) ${srcdir}/ert.texi | ||
| 371 | ert.pdf: ert.texi | ||
| 372 | $(ENVADD) $(TEXI2PDF) ${srcdir}/ert.texi | ||
| 373 | |||
| 363 | eshell : $(infodir)/eshell | 374 | eshell : $(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 | ||
| 13 | Copyright @copyright{} 2008, 2010, 2011 Free Software Foundation, Inc. | ||
| 14 | |||
| 15 | @quotation | ||
| 16 | Permission is granted to copy, distribute and/or modify this document | ||
| 17 | under the terms of the GNU Free Documentation License, Version 1.2 or | ||
| 18 | any later version published by the Free Software Foundation; with no | ||
| 19 | Invariant Sections, with no Front-Cover Texts, and with no Back-Cover | ||
| 20 | Texts. | ||
| 21 | @end quotation | ||
| 22 | @end copying | ||
| 23 | |||
| 24 | @node Top, Introduction, (dir), (dir) | ||
| 25 | @top ERT: Emacs Lisp Regression Testing | ||
| 26 | |||
| 27 | ERT is a tool for automated testing in Emacs Lisp. Its main features | ||
| 28 | are facilities for defining tests, running them and reporting the | ||
| 29 | results, and for debugging test failures interactively. | ||
| 30 | |||
| 31 | ERT is similar to tools for other environments such as JUnit, but has | ||
| 32 | unique features that take advantage of the dynamic and interactive | ||
| 33 | nature of Emacs. Despite its name, it works well both for test-driven | ||
| 34 | development (see | ||
| 35 | @url{http://en.wikipedia.org/wiki/Test-driven_development}) and for | ||
| 36 | traditional 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 | |||
| 49 | How 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 | |||
| 55 | How 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 | |||
| 62 | How 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 | |||
| 67 | Extending 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 | |||
| 72 | Other 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 | |||
| 83 | ERT allows you to define @emph{tests} in addition to functions, | ||
| 84 | macros, variables, and the other usual Lisp constructs. Tests are | ||
| 85 | simply Lisp code --- code that invokes other code and checks whether | ||
| 86 | it behaves as expected. | ||
| 87 | |||
| 88 | ERT keeps track of the tests that are defined and provides convenient | ||
| 89 | commands to run them to verify whether the definitions that are | ||
| 90 | currently loaded in Emacs pass the tests. | ||
| 91 | |||
| 92 | Some Lisp files have comments like the following (adapted from the | ||
| 93 | package @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 | |||
| 101 | The code contained in these comments can be evaluated from time to | ||
| 102 | time to compare the output with the expected output. ERT formalizes | ||
| 103 | this and introduces a common convention, which simplifies Emacs | ||
| 104 | development, since programmers no longer have to manually find and | ||
| 105 | evaluate such comments. | ||
| 106 | |||
| 107 | An 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 | |||
| 117 | If you know @code{defun}, the syntax of @code{ert-deftest} should look | ||
| 118 | familiar: This example defines a test named @code{pp-test-quote} that | ||
| 119 | will 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 | ||
| 123 | better error reporting. @xref{The @code{should} Macro}. | ||
| 124 | |||
| 125 | Each test should have a name that describes what functionality the | ||
| 126 | test tests. Test names can be chosen arbitrarily --- they are in a | ||
| 127 | namespace separate from functions and variables --- but should follow | ||
| 128 | the usual Emacs Lisp convention of having a prefix that indicates | ||
| 129 | which package they belong to. Test names are displayed by ERT when | ||
| 130 | reporting failures and can be used when selecting which tests to run. | ||
| 131 | |||
| 132 | The empty parentheses @code{()} in the first line don't currently have | ||
| 133 | any meaning and are reserved for future extension. They also make | ||
| 134 | @code{ert-deftest}'s syntax more similar to @code{defun}. | ||
| 135 | |||
| 136 | The docstring describes what feature this test tests. When running | ||
| 137 | tests interactively, the first line of the docstring is displayed for | ||
| 138 | tests that fail, so it is good if the first line makes sense on its | ||
| 139 | own. | ||
| 140 | |||
| 141 | The body of a test can be arbitrary Lisp code. It should have as few | ||
| 142 | side effects as possible; each test should be written to clean up | ||
| 143 | after itself, leaving Emacs in the same state as it was before the | ||
| 144 | test. Tests should clean up even if they fail. @xref{Tests and Their | ||
| 145 | Environment}. | ||
| 146 | |||
| 147 | |||
| 148 | @node How to Run Tests, How to Write Tests, Introduction, Top | ||
| 149 | @chapter How to Run Tests | ||
| 150 | |||
| 151 | You can run tests either in the Emacs you are working in, or on the | ||
| 152 | command line in a separate Emacs process in batch mode (i.e., with no | ||
| 153 | user interface). The former mode is convenient during interactive | ||
| 154 | development, the latter is useful to make sure that tests pass | ||
| 155 | independently of your customizations, allows tests to be invoked from | ||
| 156 | makefiles and scripts to be written that run tests in several | ||
| 157 | different 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 | |||
| 169 | You can run the tests that are currently defined in your Emacs with | ||
| 170 | the command @kbd{@kbd{M-x} ert @kbd{RET} t @kbd{RET}}. ERT will pop | ||
| 171 | up a new buffer, the ERT results buffer, showing the results of the | ||
| 172 | tests run. It looks like this: | ||
| 173 | |||
| 174 | @example | ||
| 175 | Selector: t | ||
| 176 | Passed: 31 | ||
| 177 | Failed: 2 (2 unexpected) | ||
| 178 | Total: 33/33 | ||
| 179 | |||
| 180 | Started at: 2008-09-11 08:39:25-0700 | ||
| 181 | Finished. | ||
| 182 | Finished at: 2008-09-11 08:39:27-0700 | ||
| 183 | |||
| 184 | FF............................... | ||
| 185 | |||
| 186 | F addition-test | ||
| 187 | (ert-test-failed | ||
| 188 | ((should | ||
| 189 | (= | ||
| 190 | (+ 1 2) | ||
| 191 | 4)) | ||
| 192 | :form | ||
| 193 | (= 3 4) | ||
| 194 | :value nil)) | ||
| 195 | |||
| 196 | F 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 | |||
| 211 | At the top, there is a summary of the results: We ran all tests in the | ||
| 212 | current Emacs (@code{Selector: t}), 31 of them passed, and 2 failed | ||
| 213 | unexpectedly. @xref{Expected Failures}, for an explanation of the | ||
| 214 | term @emph{unexpected} in this context. | ||
| 215 | |||
| 216 | The line of dots and @code{F}s is a progress bar where each character | ||
| 217 | represents one test; it fills while the tests are running. A dot | ||
| 218 | means that the test passed, an @code{F} means that it failed. Below | ||
| 219 | the progress bar, ERT shows details about each test that had an | ||
| 220 | unexpected result. In the example above, there are two failures, both | ||
| 221 | due to failed @code{should} forms. @xref{Understanding Explanations}, | ||
| 222 | for more details. | ||
| 223 | |||
| 224 | In the ERT results buffer, @kbd{TAB} and @kbd{S-TAB} cycle between | ||
| 225 | buttons. Each name of a function or macro in this buffer is a button; | ||
| 226 | moving point to it and typing @kbd{RET} jumps to its definition. | ||
| 227 | |||
| 228 | Pressing @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 | ||
| 230 | definition of the test near point (@kbd{RET} has the same effect if | ||
| 231 | point is on the name of the test). On a failed test, @kbd{b} shows | ||
| 232 | the backtrace of the failure. | ||
| 233 | |||
| 234 | @kbd{l} shows the list of @code{should} forms executed in the test. | ||
| 235 | If any messages were generated (with the Lisp function @code{message}) | ||
| 236 | in a test or any of the code that it invoked, @kbd{m} will show them. | ||
| 237 | |||
| 238 | By default, long expressions in the failure details are abbreviated | ||
| 239 | using @code{print-length} and @code{print-level}. Pressing @kbd{L} | ||
| 240 | while point is on a test failure will increase the limits to show more | ||
| 241 | of 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 | |||
| 247 | ERT supports automated invocations from the command line or from | ||
| 248 | scripts or makefiles. There are two functions for this purpose, | ||
| 249 | @code{ert-run-tests-batch} and @code{ert-run-tests-batch-and-exit}. | ||
| 250 | They can be used like this: | ||
| 251 | |||
| 252 | @example | ||
| 253 | emacs -batch -L /path/to/ert -l ert.el -l my-tests.el -f ert-run-tests-batch-and-exit | ||
| 254 | @end example | ||
| 255 | |||
| 256 | This 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 | ||
| 258 | with a zero exit status if all tests passed, or nonzero if any tests | ||
| 259 | failed or if anything else went wrong. It will also print progress | ||
| 260 | messages and error diagnostics to standard output. | ||
| 261 | |||
| 262 | You 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 | |||
| 270 | Functions like @code{ert} accept a @emph{test selector}, a Lisp | ||
| 271 | expression specifying a set of tests. Test selector syntax is similar | ||
| 272 | to 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 | |||
| 292 | Selectors that are frequently useful when selecting tests to run | ||
| 293 | include @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 | ||
| 295 | that package @code{foo} uses the prefix @code{foo-} for its test names | ||
| 296 | ---, result-based selectors such as @code{(or :new :unexpected)} to | ||
| 297 | run all tests that have either not run yet or that had an unexpected | ||
| 298 | result 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 | |||
| 306 | ERT lets you define tests in the same way you define functions. You | ||
| 307 | can type @code{ert-deftest} forms in a buffer and evaluate them there | ||
| 308 | with @code{eval-defun} or @code{compile-defun}, or you can save the | ||
| 309 | file and load it, optionally byte-compiling it first. | ||
| 310 | |||
| 311 | Just like @code{find-function} is only able to find where a function | ||
| 312 | was defined if the function was loaded from a file, ERT is only able | ||
| 313 | to 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 | |||
| 326 | Test bodies can include arbitrary code; but to be useful, they need to | ||
| 327 | have checks whether the code being tested (or @emph{code under test}) | ||
| 328 | does 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 | ||
| 330 | records information that ERT can display to help debugging. | ||
| 331 | |||
| 332 | This test definition | ||
| 333 | |||
| 334 | @lisp | ||
| 335 | (ert-deftest addition-test () | ||
| 336 | (should (= (+ 1 2) 4))) | ||
| 337 | @end lisp | ||
| 338 | |||
| 339 | will produce this output when run via @kbd{M-x ert}: | ||
| 340 | |||
| 341 | @example | ||
| 342 | F 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 | |||
| 353 | In this example, @code{should} recorded the fact that (= (+ 1 2) 4) | ||
| 354 | reduced to (= 3 4) before it reduced to nil. When debugging why the | ||
| 355 | test failed, it helps to know that the function @code{+} returned 3 | ||
| 356 | here. ERT records the return value for any predicate called directly | ||
| 357 | within @code{should}. | ||
| 358 | |||
| 359 | In addition to @code{should}, ERT provides @code{should-not}, which | ||
| 360 | checks that the predicate returns nil, and @code{should-error}, which | ||
| 361 | checks that the form called within it signals an error. An example | ||
| 362 | use 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 | |||
| 370 | This checks that dividing one by zero signals an error of type | ||
| 371 | @code{arith-error}. The @code{:type} argument to @code{should-error} | ||
| 372 | is optional; if absent, any type of error is accepted. | ||
| 373 | @code{should-error} returns an error description of the error that was | ||
| 374 | signalled, to allow additional checks to be made. The error | ||
| 375 | description has the format @code{(ERROR-SYMBOL . DATA)}. | ||
| 376 | |||
| 377 | There is no @code{should-not-error} macro since tests that signal an | ||
| 378 | error fail anyway, so @code{should-not-error} is effectively the | ||
| 379 | default. | ||
| 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 | |||
| 388 | Some 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 | ||
| 390 | fails, ERT will alert you of this failure every time you run all | ||
| 391 | tests. For known bugs, this alert is a distraction. The way to | ||
| 392 | suppress it is to add @code{:expected-result :failed} to the test | ||
| 393 | definition: | ||
| 394 | |||
| 395 | @lisp | ||
| 396 | (ert-deftest future-bug () | ||
| 397 | "Test `time-forward' with negative arguments. | ||
| 398 | Since this functionality isn't implemented yet, the test is known to fail." | ||
| 399 | :expected-result :failed | ||
| 400 | (time-forward -1)) | ||
| 401 | @end lisp | ||
| 402 | |||
| 403 | ERT will still display a small @code{f} in the progress bar as a | ||
| 404 | reminder that there is a known bug, and will count the test as failed, | ||
| 405 | but it will be quiet about it otherwise. | ||
| 406 | |||
| 407 | An alternative to marking the test as a known failure this way is to | ||
| 408 | delete the test. This is a good idea if there is no intent to fix it, | ||
| 409 | i.e., if the behavior that was formerly considered a bug has become an | ||
| 410 | accepted feature. | ||
| 411 | |||
| 412 | In general, however, it can be useful to keep tests that are known to | ||
| 413 | fail. If someone wants to fix the bug, they will have a very good | ||
| 414 | starting point: an automated test case that reproduces the bug. This | ||
| 415 | makes it much easier to fix the bug, demonstrate that it is fixed, and | ||
| 416 | prevent future regressions. | ||
| 417 | |||
| 418 | ERT displays the same kind of alerts for tests that pass unexpectedly | ||
| 419 | that it displays for unexpected failures. This way, if you make code | ||
| 420 | changes that happen to fix a bug that you weren't aware of, you will | ||
| 421 | know to remove the @code{:expected-result} clause of that test and | ||
| 422 | close the corresponding bug report, if any. | ||
| 423 | |||
| 424 | Since @code{:expected-result} evaluates its argument when the test is | ||
| 425 | loaded, tests can be marked as known failures only on certain Emacs | ||
| 426 | versions, 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 | |||
| 441 | The outcome of running a test should not depend on the current state | ||
| 442 | of the environment, and each test should leave its environment in the | ||
| 443 | same state it found it in. In particular, a test should not depend on | ||
| 444 | any Emacs customization variables or hooks, and if it has to make any | ||
| 445 | changes to Emacs' state or state external to Emacs such as the file | ||
| 446 | system, it should undo these changes before it returns, regardless of | ||
| 447 | whether it passed or failed. | ||
| 448 | |||
| 449 | Tests should not depend on the environment because any such | ||
| 450 | dependencies can make the test brittle or lead to failures that occur | ||
| 451 | only under certain circumstances and are hard to reproduce. Of | ||
| 452 | course, the code under test may have settings that affect its | ||
| 453 | behavior. In that case, it is best to make the test @code{let}-bind | ||
| 454 | all such settings variables to set up a specific configuration for the | ||
| 455 | duration of the test. The test can also set up a number of different | ||
| 456 | configurations and run the code under test with each. | ||
| 457 | |||
| 458 | Tests that have side effects on their environment should restore it to | ||
| 459 | its original state because any side effects that persist after the | ||
| 460 | test can disrupt the workflow of the programmer running the tests. If | ||
| 461 | the code under test has side effects on Emacs' current state, such as | ||
| 462 | on the current buffer or window configuration, the test should create | ||
| 463 | a 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 | ||
| 466 | the state that can not be preserved with such macros, cleanup should | ||
| 467 | be performed with @code{unwind-protect}, to ensure that the cleanup | ||
| 468 | occurs even if the test fails. | ||
| 469 | |||
| 470 | An exception to this are messages that the code under test prints with | ||
| 471 | @code{message} and similar logging; tests should not bother restoring | ||
| 472 | the @code{*Message*} buffer to its original state. | ||
| 473 | |||
| 474 | The above guidelines imply that tests should avoid calling highly | ||
| 475 | customizable commands such as @code{find-file}, except, of course, if | ||
| 476 | such 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 | ||
| 480 | its behavior can be affected by so many external factors. Also, | ||
| 481 | @code{find-file} has side effects that are hard to predict and thus | ||
| 482 | hard to undo: It may create a new buffer or may reuse an existing | ||
| 483 | buffer if one is already visiting the requested file; and it runs | ||
| 484 | @code{find-file-hook}, which can have arbitrary side effects. | ||
| 485 | |||
| 486 | Instead, it is better to use lower-level mechanisms with simple and | ||
| 487 | predictable semantics like @code{with-temp-buffer}, @code{insert} or | ||
| 488 | @code{insert-file-contents-literally}, and activating the desired mode | ||
| 489 | by calling the corresponding function directly --- after binding the | ||
| 490 | hook 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 | |||
| 496 | Testing simple functions that have no side effects and no dependencies | ||
| 497 | on 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 | |||
| 509 | This test calls the function @code{ert--mismatch} several times with | ||
| 510 | various combinations of arguments and compares the return value to the | ||
| 511 | expected return value. (Some programmers prefer @code{(should (eql | ||
| 512 | EXPECTED ACTUAL))} over the @code{(should (eql ACTUAL EXPECTED))} | ||
| 513 | shown here. ERT works either way.) | ||
| 514 | |||
| 515 | Here'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 | |||
| 530 | This test creates a test object using @code{make-ert-test} whose body | ||
| 531 | will immediately signal failure. It then runs that test and asserts | ||
| 532 | that it fails. Then, it creates a temporary buffer and invokes | ||
| 533 | @code{ert--print-backtrace} to print the backtrace of the failed test | ||
| 534 | to the current buffer. Finally, it extracts the first line from the | ||
| 535 | buffer and asserts that it matches what we expect. It uses | ||
| 536 | @code{buffer-substring-no-properties} and @code{equal} to ignore text | ||
| 537 | properties; for a test that takes properties into account, | ||
| 538 | @code{buffer-substring} and @code{ert-equal-including-properties} | ||
| 539 | could be used instead. | ||
| 540 | |||
| 541 | The reason why this test only checks the first line of the backtrace | ||
| 542 | is that the remainder of the backtrace is dependent on ERT's internals | ||
| 543 | as well as whether the code is running interpreted or compiled. By | ||
| 544 | looking only at the first line, the test checks a useful property | ||
| 545 | --- that the backtrace correctly captures the call to @code{signal} that | ||
| 546 | results from the call to @code{ert-fail} --- without being brittle. | ||
| 547 | |||
| 548 | This example also shows that writing tests is much easier if the code | ||
| 549 | under test was structured with testing in mind. | ||
| 550 | |||
| 551 | For example, if @code{ert-run-test} accepted only symbols that name | ||
| 552 | tests rather than test objects, the test would need a name for the | ||
| 553 | failing test, which would have to be a temporary symbol generated with | ||
| 554 | @code{make-symbol}, to avoid side effects on Emacs' state. Choosing | ||
| 555 | the right interface for @code{ert-run-tests} allows the test to be | ||
| 556 | simpler. | ||
| 557 | |||
| 558 | Similarly, if @code{ert--print-backtrace} printed the backtrace to a | ||
| 559 | buffer with a fixed name rather than the current buffer, it would be | ||
| 560 | much harder for the test to undo the side effect. Of course, some | ||
| 561 | code somewhere needs to pick the buffer name. But that logic is | ||
| 562 | independent of the logic that prints backtraces, and keeping them in | ||
| 563 | separate functions allows us to test them independently. | ||
| 564 | |||
| 565 | A lot of code that you will encounter in Emacs was not written with | ||
| 566 | testing in mind. Sometimes, the easiest way to write tests for such | ||
| 567 | code is to restructure the code slightly to provide better interfaces | ||
| 568 | for testing. Usually, this makes the interfaces easier to use as | ||
| 569 | well. | ||
| 570 | |||
| 571 | |||
| 572 | @node How to Debug Tests, Extending ERT, How to Write Tests, Top | ||
| 573 | @chapter How to Debug Tests | ||
| 574 | |||
| 575 | This section describes how to use ERT's features to understand why | ||
| 576 | a 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 | |||
| 588 | Failed @code{should} forms are reported like this: | ||
| 589 | |||
| 590 | @example | ||
| 591 | F 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 | |||
| 602 | ERT shows what the @code{should} expression looked like and what | ||
| 603 | values its subexpressions had: The source code of the assertion was | ||
| 604 | @code{(should (= (+ 1 2) 4))}, which applied the function @code{=} to | ||
| 605 | the arguments @code{3} and @code{4}, resulting in the value | ||
| 606 | @code{nil}. In this case, the test is wrong; it should expect 3 | ||
| 607 | rather than 4. | ||
| 608 | |||
| 609 | If a predicate like @code{equal} is used with @code{should}, ERT | ||
| 610 | provides a so-called @emph{explanation}: | ||
| 611 | |||
| 612 | @example | ||
| 613 | F 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 | |||
| 628 | In 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 | ||
| 630 | the item at index 2 differs between the two lists; in one list, it is | ||
| 631 | the atom c, in the other, it is the atom d. | ||
| 632 | |||
| 633 | In simple examples like the above, the explanation is unnecessary. | ||
| 634 | But in cases where the difference is not immediately apparent, it can | ||
| 635 | save time: | ||
| 636 | |||
| 637 | @example | ||
| 638 | F 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 | |||
| 648 | ERT only provides explanations for predicates that have an explanation | ||
| 649 | function registered. @xref{Defining Explanation Functions}. | ||
| 650 | |||
| 651 | |||
| 652 | @node Interactive Debugging, , Understanding Explanations, How to Debug Tests | ||
| 653 | @section Interactive Debugging | ||
| 654 | |||
| 655 | Debugging failed tests works essentially the same way as debugging any | ||
| 656 | other problems with Lisp code. Here are a few tricks specific to | ||
| 657 | tests: | ||
| 658 | |||
| 659 | @itemize | ||
| 660 | @item Re-run the failed test a few times to see if it fails in the same way | ||
| 661 | each time. It's good to find out whether the behavior is | ||
| 662 | deterministic before spending any time looking for a cause. In the | ||
| 663 | ERT 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 | ||
| 666 | exactly it does. Perhaps the test is broken rather than the code | ||
| 667 | under test. | ||
| 668 | |||
| 669 | @item If the test contains a series of @code{should} forms and you can't | ||
| 670 | tell 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 | ||
| 674 | the test with debugging enabled, this will enter the debugger and show | ||
| 675 | the backtrace as well; but the top few frames shown there will not be | ||
| 676 | relevant to you since they are ERT's own debugger hook. @kbd{b} | ||
| 677 | strips 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 | ||
| 681 | failed. 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 | ||
| 685 | type @kbd{@kbd{C-u} @kbd{C-M-x}}. Then, go back to the ERT buffer and | ||
| 686 | re-run the test with @kbd{r} or @kbd{d}. | ||
| 687 | |||
| 688 | @item If you have been editing and rearranging tests, it is possible that | ||
| 689 | ERT remembers an old test that you have since renamed or removed --- | ||
| 690 | renamings or removals of definitions in the source code leave around a | ||
| 691 | stray definition under the old name in the running process, this is a | ||
| 692 | common problem in Lisp. In such a situation, hit @kbd{D} to let ERT | ||
| 693 | forget 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 | |||
| 700 | There 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 | |||
| 711 | The explanation function for a predicate is a function that takes the | ||
| 712 | same arguments as the predicate and returns an @emph{explanation}. | ||
| 713 | The explanation should explain why the predicate, when invoked with | ||
| 714 | the arguments given to the explanation function, returns the value | ||
| 715 | that it returns. The explanation can be any object but should have a | ||
| 716 | comprehensible printed representation. If the return value of the | ||
| 717 | predicate needs no explanation for a given list of arguments, the | ||
| 718 | explanation function should return nil. | ||
| 719 | |||
| 720 | To associate an explanation function with a predicate, add the | ||
| 721 | property @code{ert-explainer} to the symbol that names the predicate. | ||
| 722 | The value of the property should be the symbol that names the | ||
| 723 | explanation 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 | |||
| 729 | Both @code{ert-run-tests-interactively} and @code{ert-run-tests-batch} | ||
| 730 | are implemented on top of the lower-level test handling code in the | ||
| 731 | sections named ``Facilities for running a single test'', ``Test | ||
| 732 | selectors'', and ``Facilities for running a whole set of tests''. | ||
| 733 | |||
| 734 | If you want to write code that works with ERT tests, you should take a | ||
| 735 | look at this lower-level code. Symbols that start with @code{ert--} | ||
| 736 | are 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 | ||
| 738 | mature API yet. | ||
| 739 | |||
| 740 | Contributions to ERT are welcome. | ||
| 741 | |||
| 742 | |||
| 743 | @node Other Testing Concepts, , Extending ERT, Top | ||
| 744 | @chapter Other Testing Concepts | ||
| 745 | |||
| 746 | For 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 | |||
| 757 | Stubbing out functions or using so-called @emph{mocks} can make it | ||
| 758 | easier to write tests. See | ||
| 759 | @url{http://en.wikipedia.org/wiki/Mock_object} for an explanation of | ||
| 760 | the corresponding concepts in object-oriented languages. | ||
| 761 | |||
| 762 | ERT 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}) | ||
| 764 | offers 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 | |||
| 770 | In many ways, ERT is similar to frameworks for other languages like | ||
| 771 | SUnit or JUnit. However, two features commonly found in such | ||
| 772 | frameworks are notably absent from ERT: fixtures and test suites. | ||
| 773 | |||
| 774 | Fixtures, as used e.g. in SUnit or JUnit, are mainly used to provide | ||
| 775 | an environment for a set of tests, and consist of set-up and tear-down | ||
| 776 | functions. | ||
| 777 | |||
| 778 | While fixtures are a useful syntactic simplification in other | ||
| 779 | languages, this does not apply to Lisp, where higher-order functions | ||
| 780 | and `unwind-protect' are available. One way to implement and use a | ||
| 781 | fixture 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 | ||
| 797 | the set-up and tear-down part, and additionally allows any test | ||
| 798 | to use any combination of fixtures, so it is more flexible than what | ||
| 799 | other tools typically allow. | ||
| 800 | |||
| 801 | If the test needs access to the environment the fixture sets up, the | ||
| 802 | fixture can be modified to pass arguments to the body. | ||
| 803 | |||
| 804 | These are well-known Lisp techniques. Special syntax for them could | ||
| 805 | be added but would provide only a minor simplification. | ||
| 806 | |||
| 807 | (If you are interested in such syntax, note that splitting set-up and | ||
| 808 | tear-down into separate functions, like *Unit tools usually do, makes | ||
| 809 | it impossible to establish dynamic `let' bindings as part of the | ||
| 810 | fixture. So, blindly imitating the way fixtures are implemented in | ||
| 811 | other languages would be counter-productive in Lisp.) | ||
| 812 | |||
| 813 | The purpose of test suites is to group related tests together. | ||
| 814 | |||
| 815 | The most common use of this is to run just the tests for one | ||
| 816 | particular module. Since symbol prefixes are the usual way of | ||
| 817 | separating module namespaces in Emacs Lisp, test selectors already | ||
| 818 | solve this by allowing regexp matching on test names; e.g., the | ||
| 819 | selector "^ert-" selects ERT's self-tests. | ||
| 820 | |||
| 821 | Other uses include grouping tests by their expected execution time to | ||
| 822 | run quick tests during interactive development and slow tests less | ||
| 823 | frequently. 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 |
| 64 | INFOSOURCES = info.texi | 66 | INFOSOURCES = info.texi |
| @@ -305,6 +307,11 @@ $(infodir)/erc: erc.texi | |||
| 305 | erc.dvi: erc.texi | 307 | erc.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 | ||
| 312 | ert.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 |
| 310 | epa.dvi: epa.texi | 317 | epa.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 @@ | |||
| 1 | 2011-01-13 Christian Ohler <ohler@gnu.org> | ||
| 2 | |||
| 3 | * NEWS: Mention ERT. | ||
| 4 | |||
| 1 | 2011-01-10 Jan Moringen <jan.moringen@uni-bielefeld.de> | 5 | 2011-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. |
| @@ -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 |
| 211 | loaded, customize `package-load-list'. | 211 | loaded, customize `package-load-list'. |
| 212 | 212 | ||
| 213 | ** An Emacs Lisp testing tool is now included. | ||
| 214 | Emacs Lisp developers can use this tool to write automated tests for | ||
| 215 | their 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 | ||
| 629 | passes it to the mail user agent function. This argument specifies an | ||
| 630 | action for returning to the caller after finishing with the mail. | ||
| 631 | This 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 |
| 625 | coordinate in the POSITION list now counts from the top of the text | 634 | coordinate in the POSITION list now counts from the top of the text |
| 626 | area, excluding any header line. Previously, it counted from the top | 635 | area, 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 @@ | |||
| 1 | 2011-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 | |||
| 7 | 2011-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 | |||
| 13 | 2011-01-13 Christian Ohler <ohler@gnu.org> | ||
| 14 | |||
| 15 | * emacs-lisp/ert.el, emacs-lisp/ert-x.el: New files. | ||
| 16 | |||
| 17 | 2011-01-13 Chong Yidong <cyd@stupidchicken.com> | ||
| 18 | |||
| 19 | * font-lock.el (font-lock-verbose): Default to nil. | ||
| 20 | |||
| 21 | 2011-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 | |||
| 37 | 2011-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 | |||
| 1 | 2011-01-11 Johan Bockgård <bojohan@gnu.org> | 46 | 2011-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." | |||
| 541 | When called from Lisp, BUFFER should be the buffer to use; if | 541 | When called from Lisp, BUFFER should be the buffer to use; if |
| 542 | omitted, a buffer named *Custom Themes* is used." | 542 | omitted, 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. | ||
| 194 | When 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. |
| 194 | If nil, Dired finds the directory as a subdirectory in some other buffer | 200 | If 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 | |||
| 41 | See `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 | |||
| 50 | Helper 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 | |||
| 62 | The main use of this table is for `ert-kill-all-test-buffers'. | ||
| 63 | Not all buffers in this table are necessarily live, but all live | ||
| 64 | test 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 | |||
| 77 | Create a test buffer with a name based on ERT--BASE-NAME and run | ||
| 78 | ERT--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 | |||
| 97 | To be used in ERT tests. If BODY finishes successfully, the test | ||
| 98 | buffer is killed; if there is an error, the test buffer is kept | ||
| 99 | around on error for further inspection. Its name is derived from | ||
| 100 | the 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 | |||
| 139 | This effectively executes | ||
| 140 | |||
| 141 | \(apply (car COMMAND) (cdr COMMAND)\) | ||
| 142 | |||
| 143 | and returns the same value, but additionally runs hooks like | ||
| 144 | `pre-command-hook' and `post-command-hook', and sets variables | ||
| 145 | like `this-command' and `last-command'. | ||
| 146 | |||
| 147 | COMMAND should be a list where the car is the command symbol and | ||
| 148 | the rest are arguments to the command. | ||
| 149 | |||
| 150 | NOTE: Since the command is not called by `call-interactively' | ||
| 151 | test 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 | |||
| 191 | Elements of REGEXPS may also be two-element lists \(REGEXP | ||
| 192 | SUBEXP\), where SUBEXP is the number of a subexpression in | ||
| 193 | REGEXP. In that case, only that subexpression will be removed | ||
| 194 | rather 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 | |||
| 210 | ARGS is a list of strings and plists. The strings in ARGS are | ||
| 211 | concatenated to produce an output string. In the output string, | ||
| 212 | each string from ARGS will be have the preceding plist as its | ||
| 213 | property list, or no properties if there is no plist before it. | ||
| 214 | |||
| 215 | As a simple example, | ||
| 216 | |||
| 217 | \(ert-propertized-string \"foo \" '(face italic) \"bar\" \" baz\" nil \ | ||
| 218 | \" quux\"\) | ||
| 219 | |||
| 220 | would return the string \"foo bar baz quux\" where the substring | ||
| 221 | \"bar baz\" has a `face' property with the value `italic'. | ||
| 222 | |||
| 223 | None of the ARGS are modified, but the return value may share | ||
| 224 | structure 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 | |||
| 241 | Renames the buffer BUFFER-NAME to a new temporary name, creates a | ||
| 242 | new buffer named BUFFER-NAME, executes THUNK, kills the new | ||
| 243 | buffer, and renames the original buffer back to BUFFER-NAME. | ||
| 244 | |||
| 245 | This is useful if THUNK has undesirable side-effects on an Emacs | ||
| 246 | buffer 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 | |||
| 263 | See `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 | |||
| 271 | BUFFER 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 | |||
| 107 | ERT-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 | |||
| 115 | Elements 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 | |||
| 123 | Elements 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 | |||
| 131 | Elements 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 | |||
| 139 | Elements 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 | |||
| 178 | Like `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 | |||
| 214 | This is like `equal-including-properties' except that it compares | ||
| 215 | the property values of text properties structurally (by | ||
| 216 | recursing) rather than with `eq'. Perhaps this is what | ||
| 217 | `equal-including-properties' should do in the first place; see | ||
| 218 | Emacs 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 | |||
| 267 | KEYS-AND-BODY should have the form of a property list, with the | ||
| 268 | exception that only keywords are permitted as keys and that the | ||
| 269 | tail -- the body -- is a list of forms that does not start with a | ||
| 270 | keyword. | ||
| 271 | |||
| 272 | Returns a two-element list containing the keys-and-values plist | ||
| 273 | and 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 | |||
| 295 | BODY is evaluated as a `progn' when the test is run. It should | ||
| 296 | signal a condition on failure or just return if the test passes. | ||
| 297 | |||
| 298 | `should', `should-not' and `should-error' are useful for | ||
| 299 | assertions in BODY. | ||
| 300 | |||
| 301 | Use `ert' to run tests interactively. | ||
| 302 | |||
| 303 | Tests that are expected to fail can be marked as such | ||
| 304 | using :expected-result. See `ert-test-result-type-p' for a | ||
| 305 | description 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. | ||
| 368 | DATA 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 | |||
| 442 | Analyzes FORM and returns an expression that has the same | ||
| 443 | semantics under evaluation but records additional debugging | ||
| 444 | information. | ||
| 445 | |||
| 446 | INNER-EXPANDER should be a function and is called with two | ||
| 447 | arguments: INNER-FORM and FORM-DESCRIPTION-FORM, where INNER-FORM | ||
| 448 | is an expression equivalent to FORM, and FORM-DESCRIPTION-FORM is | ||
| 449 | an expression that returns a description of FORM. INNER-EXPANDER | ||
| 450 | should return code that calls INNER-FORM and performs the checks | ||
| 451 | and error signalling specific to the particular variant of | ||
| 452 | `should'. The code that INNER-EXPANDER returns must not call | ||
| 453 | FORM-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 | |||
| 471 | Returns 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 | |||
| 480 | Returns 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 | |||
| 490 | Determines whether CONDITION matches TYPE and EXCLUDE-SUBTYPES, | ||
| 491 | and 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 | |||
| 518 | The error signalled needs to match TYPE. TYPE should be a list | ||
| 519 | of condition names. (It can also be a non-nil symbol, which is | ||
| 520 | equivalent to a singleton list containing that symbol.) If | ||
| 521 | EXCLUDE-SUBTYPES is nil, the error matches TYPE if one of its | ||
| 522 | condition names is an element of TYPE. If EXCLUDE-SUBTYPES is | ||
| 523 | non-nil, the error matches TYPE if it is an element of TYPE. | ||
| 524 | |||
| 525 | If the error matches, returns (ERROR-SYMBOL . DATA) from the | ||
| 526 | error. If not, or if no error was signalled, abort the test as | ||
| 527 | failed." | ||
| 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 | |||
| 582 | Returns 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 | |||
| 642 | Returns nil if they are equivalent, i.e., have the same value for | ||
| 643 | each key, where absent values are treated as nil. The order of | ||
| 644 | key/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 | |||
| 675 | If 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 | |||
| 687 | Returns 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 | |||
| 726 | Bound 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 | |||
| 732 | To be used within ERT tests. MESSAGE-FORM should evaluate to a | ||
| 733 | string that will be displayed together with the test result if | ||
| 734 | the test fails. PREFIX-FORM should evaluate to a string as well | ||
| 735 | and 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 | |||
| 828 | This function records failures and errors and either terminates | ||
| 829 | the test silently or calls the interactive debugger, as | ||
| 830 | appropriate. | ||
| 831 | |||
| 832 | INFO is the ert--test-execution-info corresponding to this test | ||
| 833 | run. 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 | |||
| 870 | This 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 | |||
| 896 | This 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 | |||
| 916 | This list is empty while no test is running, has one element | ||
| 917 | while a test is running, two elements while a test run from | ||
| 918 | inside a test is running, etc. The list is in order of nesting, | ||
| 919 | innermost test first. | ||
| 920 | |||
| 921 | The elements are of type `ert-test'.") | ||
| 922 | |||
| 923 | (defun ert-run-test (ert-test) | ||
| 924 | "Run ERT-TEST. | ||
| 925 | |||
| 926 | Returns 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 | |||
| 969 | Valid result types: | ||
| 970 | |||
| 971 | nil -- Never matches. | ||
| 972 | t -- 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 | |||
| 1015 | UNIVERSE specifies the set of tests to select from; it should be | ||
| 1016 | a list of tests, or t, which refers to all tests named by symbols | ||
| 1017 | in `obarray'. | ||
| 1018 | |||
| 1019 | Returns the set of tests as a list. | ||
| 1020 | |||
| 1021 | Valid selectors: | ||
| 1022 | |||
| 1023 | nil -- Selects the empty set. | ||
| 1024 | t -- 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. | ||
| 1028 | a string -- Selects all tests that have a name that matches the string, | ||
| 1029 | a regexp. | ||
| 1030 | a test -- Selects that test. | ||
| 1031 | a 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 | |||
| 1040 | Only selectors that require a superset of tests, such | ||
| 1041 | as (satisfies ...), strings, :new, etc. make use of UNIVERSE. | ||
| 1042 | Selectors that do not, such as \(member ...\), just return the | ||
| 1043 | set implied by them without checking whether it is really | ||
| 1044 | contained 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 | |||
| 1229 | Returns 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 | |||
| 1235 | Also 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 | |||
| 1267 | SELECTOR 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 | |||
| 1340 | EXPECTEDP 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 | |||
| 1351 | EXPECTEDP 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. | ||
| 1361 | Ensures 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 | |||
| 1372 | RESULT 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 | |||
| 1401 | SELECTOR works as described in `ert-select-tests', except if | ||
| 1402 | SELECTOR is nil, in which case all tests rather than none will be | ||
| 1403 | run; this makes the command line \"emacs -batch -l my-tests.el -f | ||
| 1404 | ert-run-tests-batch-and-exit\" useful. | ||
| 1405 | |||
| 1406 | Returns 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 | |||
| 1497 | The exit status will be 0 if all test results were as expected, 1 | ||
| 1498 | on unexpected results, or 2 if the tool detected an error outside | ||
| 1499 | of the tests (e.g. invalid SELECTOR or bug in the code that runs | ||
| 1500 | the 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 | |||
| 1524 | This 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 | |||
| 1540 | Prompt with PROMPT. If DEFAULT is a valid test name, use it as a | ||
| 1541 | default. HISTORY is the history to use; see `completing-read'. | ||
| 1542 | If ADD-DEFAULT-TO-PROMPT is non-nil, PROMPT will be modified to | ||
| 1543 | include the default, if any. | ||
| 1544 | |||
| 1545 | Signals 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. | ||
| 1570 | As a default, use the symbol at point, or the test at point if in | ||
| 1571 | the ERT results buffer. Prompt with PROMPT, augmented with the | ||
| 1572 | default (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 | |||
| 1583 | Nothing 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 | |||
| 1650 | Also 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 | |||
| 1740 | While running tests, ERT shows the current progress, and this variable | ||
| 1741 | determines 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 | |||
| 1755 | EWOC 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 | |||
| 1777 | BEGIN 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 | |||
| 1789 | The 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 | |||
| 1795 | If EXPECTEDP is nil, returns the face for unexpected results; if | ||
| 1796 | non-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 | |||
| 1863 | ENABLEDP is true if font-lock-mode is switched on, false | ||
| 1864 | otherwise." | ||
| 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 | |||
| 1872 | STATS is the stats object; LISTENER is the results listener; | ||
| 1873 | BUFFER-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 | |||
| 1915 | SELECTOR works as described in `ert-select-tests'. | ||
| 1916 | OUTPUT-BUFFER-NAME and MESSAGE-FN should normally be nil; they | ||
| 1917 | are used for automated self-tests and specify which buffer to use | ||
| 1918 | and 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 | |||
| 2073 | To 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 | |||
| 2091 | To 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 | |||
| 2098 | To 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 | |||
| 2106 | To 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 | |||
| 2114 | EWOC-FN specifies the direction and should be either `ewoc-prev' | ||
| 2115 | or `ewoc-next'. If there are no more nodes in that direction, an | ||
| 2116 | error 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 | |||
| 2139 | To 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 | |||
| 2163 | From an ewoc node, jumps to the character that represents the | ||
| 2164 | same test in the progress bar, and vice versa. | ||
| 2165 | |||
| 2166 | To 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 | |||
| 2205 | To 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 | |||
| 2222 | To be used in the ERT results buffer. | ||
| 2223 | |||
| 2224 | Returns a list of two elements: the test (or nil) and a symbol | ||
| 2225 | specifying whether the test has been redefined. | ||
| 2226 | |||
| 2227 | If a new test has been defined with the same name as the test at | ||
| 2228 | point, replaces the test at point with the new test, and returns | ||
| 2229 | the new test and the symbol `redefined'. | ||
| 2230 | |||
| 2231 | If the test has been deleted, returns the old test and the symbol | ||
| 2232 | `deleted'. | ||
| 2233 | |||
| 2234 | If the test is still current, returns the test and the symbol nil. | ||
| 2235 | |||
| 2236 | If 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 | |||
| 2259 | Also updates the stats object. NEW-TEST is the new test | ||
| 2260 | definition." | ||
| 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 | |||
| 2288 | To 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 | |||
| 2297 | To 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 | |||
| 2332 | To 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 | |||
| 2340 | To 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 | |||
| 2370 | To 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 | |||
| 2392 | To 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 | |||
| 2427 | To 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 | |||
| 2439 | To 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 | |||
| 2517 | To 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.") | |||
| 1663 | Optional PACKAGES is a list of names of packages (symbols) to | 1663 | Optional PACKAGES is a list of names of packages (symbols) to |
| 1664 | list; the default is to display everything in `package-alist'." | 1664 | list; 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. |
| 281 | If a number, only buffers greater than this size have fontification messages." | 281 | If 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 @@ | |||
| 1 | 2011-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 | |||
| 1 | 2011-01-11 Lars Magne Ingebrigtsen <larsi@gnus.org> | 14 | 2011-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. |
| 482 | Like `message-mail', but with Gnus paraphernalia, particularly the | 482 | Like `message-mail', but with Gnus paraphernalia, particularly the |
| 483 | Gcc: header for archiving purposes." | 483 | Gcc: 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. |
| 6496 | OTHER-HEADERS is an alist of header/value pairs. CONTINUE says whether | 6497 | OTHER-HEADERS is an alist of header/value pairs. CONTINUE says whether |
| 6497 | to continue editing a message already being composed. SWITCH-FUNCTION | 6498 | to 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 | ||
| 7665 | See `gmm-tool-bar-from-list' for details on the format of the list." | 7665 | See `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). |
| 1648 | When this function returns, the buffer `*mail*' is selected. | 1662 | When this function returns, the buffer `*mail*' is selected. |
| 1649 | The value is t if the message was newly initialized; otherwise, nil. | 1663 | The 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 @@ | |||
| 1 | 2011-01-13 Chong Yidong <cyd@stupidchicken.com> | ||
| 2 | |||
| 3 | * mh-comp.el (mh-user-agent-compose): New arg RETURN-ACTION. | ||
| 4 | |||
| 1 | 2010-11-07 Glenn Morris <rgm@gnu.org> | 5 | 2010-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. |
| 204 | This is the `mail-user-agent' entry point to MH-E. This function | 205 | This is the `mail-user-agent' entry point to MH-E. This function |
| 205 | conforms to the contract specified by `define-mail-user-agent' | 206 | conforms to the contract specified by `define-mail-user-agent' |
| @@ -213,8 +214,8 @@ OTHER-HEADERS is an alist specifying additional header fields. | |||
| 213 | Elements look like (HEADER . VALUE) where both HEADER and VALUE | 214 | Elements look like (HEADER . VALUE) where both HEADER and VALUE |
| 214 | are strings. | 215 | are strings. |
| 215 | 216 | ||
| 216 | CONTINUE, SWITCH-FUNCTION, YANK-ACTION and SEND-ACTIONS are | 217 | CONTINUE, SWITCH-FUNCTION, YANK-ACTION, SEND-ACTIONS, and |
| 217 | ignored." | 218 | RETURN-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. |
| 5758 | This uses the user's chosen mail composition package | 5726 | This uses the user's chosen mail composition package |
| 5759 | as selected with the variable `mail-user-agent'. | 5727 | as selected with the variable `mail-user-agent'. |
| @@ -5778,7 +5746,12 @@ FUNCTION to ARGS, to insert the raw text of the original message. | |||
| 5778 | original text has been inserted in this way.) | 5746 | original text has been inserted in this way.) |
| 5779 | 5747 | ||
| 5780 | SEND-ACTIONS is a list of actions to call when the message is sent. | 5748 | SEND-ACTIONS is a list of actions to call when the message is sent. |
| 5781 | Each action has the form (FUNCTION . ARGS)." | 5749 | Each action has the form (FUNCTION . ARGS). |
| 5750 | |||
| 5751 | RETURN-ACTION, if non-nil, is an action for returning to the | ||
| 5752 | caller. It has the form (FUNCTION . ARGS). The function is | ||
| 5753 | called after the mail has been sent or put aside, and the mail | ||
| 5754 | buffer 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 @@ | |||
| 1 | 2011-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 | |||
| 1 | 2010-11-11 Stefan Monnier <monnier@iro.umontreal.ca> | 9 | 2010-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 | |||
| 19 | SHELL = /bin/sh | ||
| 20 | |||
| 21 | srcdir = @srcdir@ | ||
| 22 | top_srcdir = @top_srcdir@ | ||
| 23 | abs_top_builddir = @abs_top_builddir@ | ||
| 24 | test = $(srcdir) | ||
| 25 | VPATH = $(srcdir) | ||
| 26 | lispsrc = $(top_srcdir)/lisp | ||
| 27 | lisp = ${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. | ||
| 35 | EMACS = ${abs_top_builddir}/src/emacs | ||
| 36 | |||
| 37 | # Command line flags for Emacs. | ||
| 38 | |||
| 39 | EMACSOPT = -batch --no-site-file --no-site-lisp | ||
| 40 | |||
| 41 | # Extra flags to pass to the byte compiler | ||
| 42 | BYTE_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. | ||
| 48 | emacs = EMACSLOADPATH=$(lispsrc):$(test) LC_ALL=C $(EMACS) $(EMACSOPT) | ||
| 49 | |||
| 50 | # Common command to find subdirectories | ||
| 51 | setwins=subdirs=`(find . -type d -print)`; \ | ||
| 52 | for file in $$subdirs; do \ | ||
| 53 | case $$file in */.* | */.*/* | */=* ) ;; \ | ||
| 54 | *) wins="$$wins $$file" ;; \ | ||
| 55 | esac; \ | ||
| 56 | done | ||
| 57 | |||
| 58 | all: test | ||
| 59 | |||
| 60 | doit: | ||
| 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 | |||
| 80 | lisp-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'. | ||
| 95 | compile-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! | ||
| 99 | compile-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. | ||
| 113 | compile-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. | ||
| 129 | compile: $(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. | ||
| 135 | compile-always: doit | ||
| 136 | cd $(test); rm -f *.elc */*.elc */*/*.elc */*/*/*.elc | ||
| 137 | $(MAKE) $(MFLAGS) compile EMACS=$(EMACS) | ||
| 138 | |||
| 139 | bootstrap-clean: | ||
| 140 | cd $(test); rm -f *.elc */*.elc */*/*.elc */*/*/*.elc | ||
| 141 | |||
| 142 | distclean: | ||
| 143 | -rm -f ./Makefile | ||
| 144 | |||
| 145 | maintainer-clean: distclean bootstrap-clean | ||
| 146 | |||
| 147 | check: 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 | |||
| 57 | The exit code will be zero if the tests passed, nonzero if they | ||
| 58 | failed 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 | |||
| 301 | This 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 | ||
| 218 | force 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 | ||
| 221 | came to my mind was \(message \"\"\), which doesn't have the | ||
| 222 | desired 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 | ||