1
0
mirror of https://git.savannah.gnu.org/git/emacs.git synced 2024-12-24 10:38:38 +00:00

Merge from mainline.

This commit is contained in:
Paul Eggert 2011-01-13 09:17:33 -08:00
commit 193770eec9
34 changed files with 5375 additions and 221 deletions

View File

@ -1,4 +1,4 @@
2011-01-11 Paul Eggert <eggert@cs.ucla.edu>
2011-01-13 Paul Eggert <eggert@cs.ucla.edu>
* Makefile.in (GNULIB_MODULES): Change ftoastr to dtoastr.
This avoids building ftoastr and ldtoastr, which aren't needed. See
@ -106,6 +106,15 @@
* make-dist: Also put into the distribution aclocal.m4,
compile, depcomp, missing, and the files under lib/.
2011-01-13 Christian Ohler <ohler@gnu.org>
* Makefile.in (INFO_FILES): Add ERT.
* Makefile.in (check): Run tests in test/automated.
* Makefile.in:
* configure.in: Add test/automated/Makefile.
2011-01-07 Paul Eggert <eggert@cs.ucla.edu>
* install-sh, mkinstalldirs, move-if-change: Update from master

View File

@ -134,7 +134,7 @@ MAN_PAGES=ctags.1 ebrowse.1 emacs.1 emacsclient.1 etags.1 \
infodir=@infodir@
INFO_FILES=ada-mode auth autotype calc ccmode cl dbus dired-x ebrowse \
ede ediff edt eieio efaq eintr elisp emacs emacs-mime epa erc \
eshell eudc flymake forms gnus idlwave info mairix-el \
ert eshell eudc flymake forms gnus idlwave info mairix-el \
message mh-e newsticker nxml-mode org pcl-cvs pgg rcirc \
reftex remember sasl sc semantic ses sieve smtpmail speedbar \
tramp url vip viper widget woman
@ -267,7 +267,7 @@ EMACSFULL = `echo emacs-${version}${EXEEXT} | sed '$(TRANSFORM)'`
SUBDIR = lib lib-src src lisp
# The subdir makefiles created by config.status.
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
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
# Subdirectories to install, and where they'll go.
# 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 \
$(srcdir)/oldXMenu/Makefile.in \
$(srcdir)/lwlib/Makefile.in \
$(srcdir)/leim/Makefile.in \
$(srcdir)/lisp/Makefile.in
$(srcdir)/lisp/Makefile.in \
$(srcdir)/test/automated/Makefile.in
./config.status
config.status: ${srcdir}/configure ${srcdir}/lisp/version.el
@ -855,7 +856,7 @@ TAGS tags: lib lib-src src
cd src; $(MAKE) tags
check:
@echo "We don't have any tests for GNU Emacs yet."
cd test/automated; $(MAKE) check
dist:
cd ${srcdir}; ./make-dist

3
configure vendored
View File

@ -17389,7 +17389,7 @@ test "${prefix}" != NONE &&
test "${exec_prefix}" != NONE &&
exec_prefix=`echo "${exec_prefix}" | sed 's,\([^/]\)/*$,\1,'`
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"
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"
ac_config_commands="$ac_config_commands default"
@ -18174,6 +18174,7 @@ do
"lwlib/Makefile") CONFIG_FILES="$CONFIG_FILES lwlib/Makefile" ;;
"lisp/Makefile") CONFIG_FILES="$CONFIG_FILES lisp/Makefile" ;;
"leim/Makefile") CONFIG_FILES="$CONFIG_FILES leim/Makefile" ;;
"test/automated/Makefile") CONFIG_FILES="$CONFIG_FILES test/automated/Makefile" ;;
"default") CONFIG_COMMANDS="$CONFIG_COMMANDS default" ;;
*) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;;

View File

@ -3718,7 +3718,7 @@ dnl the use of force in the `epaths-force' rule in Makefile.in.
AC_OUTPUT(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, [
lwlib/Makefile lisp/Makefile leim/Makefile test/automated/Makefile, [
### Make the necessary directories, if they don't exist.
for dir in etc lisp ; do

View File

@ -1,3 +1,10 @@
2011-01-13 Christian Ohler <ohler@gnu.org>
* ert.texi: New file.
* Makefile.in:
* makefile.w32-in: Add ert.texi.
2011-01-10 Jan Moringen <jan.moringen@uni-bielefeld.de>
* dbus.texi (Receiving Method Calls): New function

View File

@ -62,6 +62,7 @@ INFO_TARGETS = \
$(infodir)/emacs-mime \
$(infodir)/epa \
$(infodir)/erc \
$(infodir)/ert \
$(infodir)/eshell \
$(infodir)/eudc \
$(infodir)/efaq \
@ -112,6 +113,7 @@ DVI_TARGETS = \
emacs-mime.dvi \
epa.dvi \
erc.dvi \
ert.dvi \
eshell.dvi \
eudc.dvi \
faq.dvi \
@ -162,6 +164,7 @@ PDF_TARGETS = \
emacs-mime.pdf \
epa.pdf \
erc.pdf \
ert.pdf \
eshell.pdf \
eudc.pdf \
faq.pdf \
@ -360,6 +363,14 @@ erc.dvi: ${srcdir}/erc.texi
erc.pdf: ${srcdir}/erc.texi
$(ENVADD) $(TEXI2PDF) $<
ert : $(infodir)/ert
$(infodir)/ert: ert.texi $(infodir)
cd $(srcdir); $(MAKEINFO) ert.texi
ert.dvi: ert.texi
$(ENVADD) $(TEXI2DVI) ${srcdir}/ert.texi
ert.pdf: ert.texi
$(ENVADD) $(TEXI2PDF) ${srcdir}/ert.texi
eshell : $(infodir)/eshell
$(infodir)/eshell: eshell.texi
$(mkinfodir)

830
doc/misc/ert.texi Normal file
View File

@ -0,0 +1,830 @@
\input texinfo
@c %**start of header
@setfilename ../../info/ert
@settitle Emacs Lisp Regression Testing
@c %**end of header
@dircategory Emacs
@direntry
* ERT: (ert). Emacs Lisp Regression Testing.
@end direntry
@copying
Copyright @copyright{} 2008, 2010, 2011 Free Software Foundation, Inc.
@quotation
Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.2 or
any later version published by the Free Software Foundation; with no
Invariant Sections, with no Front-Cover Texts, and with no Back-Cover
Texts.
@end quotation
@end copying
@node Top, Introduction, (dir), (dir)
@top ERT: Emacs Lisp Regression Testing
ERT is a tool for automated testing in Emacs Lisp. Its main features
are facilities for defining tests, running them and reporting the
results, and for debugging test failures interactively.
ERT is similar to tools for other environments such as JUnit, but has
unique features that take advantage of the dynamic and interactive
nature of Emacs. Despite its name, it works well both for test-driven
development (see
@url{http://en.wikipedia.org/wiki/Test-driven_development}) and for
traditional software development methods.
@menu
* Introduction:: A simple example of an ERT test.
* How to Run Tests:: Run tests in your Emacs or from the command line.
* How to Write Tests:: How to add tests to your Emacs Lisp code.
* How to Debug Tests:: What to do if a test fails.
* Extending ERT:: ERT is extensible in several ways.
* Other Testing Concepts:: Features not in ERT.
@detailmenu
--- The Detailed Node Listing ---
How to Run Tests
* Running Tests Interactively:: Run tests in your current Emacs.
* Running Tests in Batch Mode:: Run tests in emacs -Q.
* Test Selectors:: Choose which tests to run.
How to Write Tests
* The @code{should} Macro:: A powerful way to express assertions.
* Expected Failures:: Tests for known bugs.
* Tests and Their Environment:: Don't depend on customizations; no side effects.
* Useful Techniques:: Some examples.
How to Debug Tests
* Understanding Explanations:: How ERT gives details on why an assertion failed.
* Interactive Debugging:: Tools available in the ERT results buffer.
Extending ERT
* Defining Explanation Functions:: Teach ERT about more predicates.
* Low-Level Functions for Working with Tests:: Use ERT's data for your purposes.
Other Testing Concepts
* Mocks and Stubs:: Stubbing out code that is irrelevant to the test.
* Fixtures and Test Suites:: How ERT differs from tools for other languages.
@end detailmenu
@end menu
@node Introduction, How to Run Tests, Top, Top
@chapter Introduction
ERT allows you to define @emph{tests} in addition to functions,
macros, variables, and the other usual Lisp constructs. Tests are
simply Lisp code --- code that invokes other code and checks whether
it behaves as expected.
ERT keeps track of the tests that are defined and provides convenient
commands to run them to verify whether the definitions that are
currently loaded in Emacs pass the tests.
Some Lisp files have comments like the following (adapted from the
package @code{pp.el}):
@lisp
;; (pp-to-string '(quote quote)) ; expected: "'quote"
;; (pp-to-string '((quote a) (quote b))) ; expected: "('a 'b)\n"
;; (pp-to-string '('a 'b)) ; same as above
@end lisp
The code contained in these comments can be evaluated from time to
time to compare the output with the expected output. ERT formalizes
this and introduces a common convention, which simplifies Emacs
development, since programmers no longer have to manually find and
evaluate such comments.
An ERT test definition equivalent to the above comments is this:
@lisp
(ert-deftest pp-test-quote ()
"Tests the rendering of `quote' symbols in `pp-to-string'."
(should (equal (pp-to-string '(quote quote)) "'quote"))
(should (equal (pp-to-string '((quote a) (quote b))) "('a 'b)\n"))
(should (equal (pp-to-string '('a 'b)) "('a 'b)\n")))
@end lisp
If you know @code{defun}, the syntax of @code{ert-deftest} should look
familiar: This example defines a test named @code{pp-test-quote} that
will pass if the three calls to @code{equal} all return true
(non-nil).
@code{should} is a macro with the same meaning as @code{assert} but
better error reporting. @xref{The @code{should} Macro}.
Each test should have a name that describes what functionality the
test tests. Test names can be chosen arbitrarily --- they are in a
namespace separate from functions and variables --- but should follow
the usual Emacs Lisp convention of having a prefix that indicates
which package they belong to. Test names are displayed by ERT when
reporting failures and can be used when selecting which tests to run.
The empty parentheses @code{()} in the first line don't currently have
any meaning and are reserved for future extension. They also make
@code{ert-deftest}'s syntax more similar to @code{defun}.
The docstring describes what feature this test tests. When running
tests interactively, the first line of the docstring is displayed for
tests that fail, so it is good if the first line makes sense on its
own.
The body of a test can be arbitrary Lisp code. It should have as few
side effects as possible; each test should be written to clean up
after itself, leaving Emacs in the same state as it was before the
test. Tests should clean up even if they fail. @xref{Tests and Their
Environment}.
@node How to Run Tests, How to Write Tests, Introduction, Top
@chapter How to Run Tests
You can run tests either in the Emacs you are working in, or on the
command line in a separate Emacs process in batch mode (i.e., with no
user interface). The former mode is convenient during interactive
development, the latter is useful to make sure that tests pass
independently of your customizations, allows tests to be invoked from
makefiles and scripts to be written that run tests in several
different Emacs versions.
@menu
* Running Tests Interactively:: Run tests in your current Emacs.
* Running Tests in Batch Mode:: Run tests in emacs -Q.
* Test Selectors:: Choose which tests to run.
@end menu
@node Running Tests Interactively, Running Tests in Batch Mode, How to Run Tests, How to Run Tests
@section Running Tests Interactively
You can run the tests that are currently defined in your Emacs with
the command @kbd{@kbd{M-x} ert @kbd{RET} t @kbd{RET}}. ERT will pop
up a new buffer, the ERT results buffer, showing the results of the
tests run. It looks like this:
@example
Selector: t
Passed: 31
Failed: 2 (2 unexpected)
Total: 33/33
Started at: 2008-09-11 08:39:25-0700
Finished.
Finished at: 2008-09-11 08:39:27-0700
FF...............................
F addition-test
(ert-test-failed
((should
(=
(+ 1 2)
4))
:form
(= 3 4)
:value nil))
F list-test
(ert-test-failed
((should
(equal
(list 'a 'b 'c)
'(a b d)))
:form
(equal
(a b c)
(a b d))
:value nil :explanation
(list-elt 2
(different-atoms c d))))
@end example
At the top, there is a summary of the results: We ran all tests in the
current Emacs (@code{Selector: t}), 31 of them passed, and 2 failed
unexpectedly. @xref{Expected Failures}, for an explanation of the
term @emph{unexpected} in this context.
The line of dots and @code{F}s is a progress bar where each character
represents one test; it fills while the tests are running. A dot
means that the test passed, an @code{F} means that it failed. Below
the progress bar, ERT shows details about each test that had an
unexpected result. In the example above, there are two failures, both
due to failed @code{should} forms. @xref{Understanding Explanations},
for more details.
In the ERT results buffer, @kbd{TAB} and @kbd{S-TAB} cycle between
buttons. Each name of a function or macro in this buffer is a button;
moving point to it and typing @kbd{RET} jumps to its definition.
Pressing @kbd{r} re-runs the test near point on its own. Pressing
@kbd{d} re-runs it with the debugger enabled. @kbd{.} jumps to the
definition of the test near point (@kbd{RET} has the same effect if
point is on the name of the test). On a failed test, @kbd{b} shows
the backtrace of the failure.
@kbd{l} shows the list of @code{should} forms executed in the test.
If any messages were generated (with the Lisp function @code{message})
in a test or any of the code that it invoked, @kbd{m} will show them.
By default, long expressions in the failure details are abbreviated
using @code{print-length} and @code{print-level}. Pressing @kbd{L}
while point is on a test failure will increase the limits to show more
of the expression.
@node Running Tests in Batch Mode, Test Selectors, Running Tests Interactively, How to Run Tests
@section Running Tests in Batch Mode
ERT supports automated invocations from the command line or from
scripts or makefiles. There are two functions for this purpose,
@code{ert-run-tests-batch} and @code{ert-run-tests-batch-and-exit}.
They can be used like this:
@example
emacs -batch -L /path/to/ert -l ert.el -l my-tests.el -f ert-run-tests-batch-and-exit
@end example
This command will start up Emacs in batch mode, load ERT, load
@code{my-tests.el}, and run all tests defined in it. It will exit
with a zero exit status if all tests passed, or nonzero if any tests
failed or if anything else went wrong. It will also print progress
messages and error diagnostics to standard output.
You may need additional @code{-L} flags to ensure that
@code{my-tests.el} and all the files that it requires are on your
@code{load-path}.
@node Test Selectors, , Running Tests in Batch Mode, How to Run Tests
@section Test Selectors
Functions like @code{ert} accept a @emph{test selector}, a Lisp
expression specifying a set of tests. Test selector syntax is similar
to Common Lisp's type specifier syntax:
@itemize
@item @code{nil} selects no tests.
@item @code{t} selects all tests.
@item @code{:new} selects all tests that have not been run yet.
@item @code{:failed} and @code{:passed} select tests according to their most recent result.
@item @code{:expected}, @code{:unexpected} select tests according to their most recent result.
@item A string selects all tests that have a name that matches the string, a regexp.
@item A test selects that test.
@item A symbol selects the test that the symbol names.
@item @code{(member TESTS...)} selects TESTS, a list of tests or symbols naming tests.
@item @code{(eql TEST)} selects TEST, a test or a symbol naming a test.
@item @code{(and SELECTORS...)} selects the tests that match all SELECTORS.
@item @code{(or SELECTORS...)} selects the tests that match any SELECTOR.
@item @code{(not SELECTOR)} selects all tests that do not match SELECTOR.
@item @code{(tag TAG)} selects all tests that have TAG on their tags list.
@item @code{(satisfies PREDICATE)} Selects all tests that satisfy PREDICATE.
@end itemize
Selectors that are frequently useful when selecting tests to run
include @code{t} to run all tests that are currently defined in Emacs,
@code{"^foo-"} to run all tests in package @code{foo} --- this assumes
that package @code{foo} uses the prefix @code{foo-} for its test names
---, result-based selectors such as @code{(or :new :unexpected)} to
run all tests that have either not run yet or that had an unexpected
result in the last run, and tag-based selectors such as @code{(not
(tag :causes-redisplay))} to run all tests that are not tagged
@code{:causes-redisplay}.
@node How to Write Tests, How to Debug Tests, How to Run Tests, Top
@chapter How to Write Tests
ERT lets you define tests in the same way you define functions. You
can type @code{ert-deftest} forms in a buffer and evaluate them there
with @code{eval-defun} or @code{compile-defun}, or you can save the
file and load it, optionally byte-compiling it first.
Just like @code{find-function} is only able to find where a function
was defined if the function was loaded from a file, ERT is only able
to find where a test was defined if the test was loaded from a file.
@menu
* The @code{should} Macro:: A powerful way to express assertions.
* Expected Failures:: Tests for known bugs.
* Tests and Their Environment:: Don't depend on customizations; no side effects.
* Useful Techniques:: Some examples.
@end menu
@node The @code{should} Macro, Expected Failures, How to Write Tests, How to Write Tests
@section The @code{should} Macro
Test bodies can include arbitrary code; but to be useful, they need to
have checks whether the code being tested (or @emph{code under test})
does what it is supposed to do. The macro @code{should} is similar to
@code{assert} from the cl package, but analyzes its argument form and
records information that ERT can display to help debugging.
This test definition
@lisp
(ert-deftest addition-test ()
(should (= (+ 1 2) 4)))
@end lisp
will produce this output when run via @kbd{M-x ert}:
@example
F addition-test
(ert-test-failed
((should
(=
(+ 1 2)
4))
:form
(= 3 4)
:value nil))
@end example
In this example, @code{should} recorded the fact that (= (+ 1 2) 4)
reduced to (= 3 4) before it reduced to nil. When debugging why the
test failed, it helps to know that the function @code{+} returned 3
here. ERT records the return value for any predicate called directly
within @code{should}.
In addition to @code{should}, ERT provides @code{should-not}, which
checks that the predicate returns nil, and @code{should-error}, which
checks that the form called within it signals an error. An example
use of @code{should-error}:
@lisp
(ert-deftest test-divide-by-zero ()
(should-error (/ 1 0)
:type 'arith-error))
@end lisp
This checks that dividing one by zero signals an error of type
@code{arith-error}. The @code{:type} argument to @code{should-error}
is optional; if absent, any type of error is accepted.
@code{should-error} returns an error description of the error that was
signalled, to allow additional checks to be made. The error
description has the format @code{(ERROR-SYMBOL . DATA)}.
There is no @code{should-not-error} macro since tests that signal an
error fail anyway, so @code{should-not-error} is effectively the
default.
@xref{Understanding Explanations}, for more details on what
@code{should} reports.
@node Expected Failures, Tests and Their Environment, The @code{should} Macro, How to Write Tests
@section Expected Failures
Some bugs are complicated to fix or not very important and are left as
@emph{known bugs}. If there is a test case that triggers the bug and
fails, ERT will alert you of this failure every time you run all
tests. For known bugs, this alert is a distraction. The way to
suppress it is to add @code{:expected-result :failed} to the test
definition:
@lisp
(ert-deftest future-bug ()
"Test `time-forward' with negative arguments.
Since this functionality isn't implemented yet, the test is known to fail."
:expected-result :failed
(time-forward -1))
@end lisp
ERT will still display a small @code{f} in the progress bar as a
reminder that there is a known bug, and will count the test as failed,
but it will be quiet about it otherwise.
An alternative to marking the test as a known failure this way is to
delete the test. This is a good idea if there is no intent to fix it,
i.e., if the behavior that was formerly considered a bug has become an
accepted feature.
In general, however, it can be useful to keep tests that are known to
fail. If someone wants to fix the bug, they will have a very good
starting point: an automated test case that reproduces the bug. This
makes it much easier to fix the bug, demonstrate that it is fixed, and
prevent future regressions.
ERT displays the same kind of alerts for tests that pass unexpectedly
that it displays for unexpected failures. This way, if you make code
changes that happen to fix a bug that you weren't aware of, you will
know to remove the @code{:expected-result} clause of that test and
close the corresponding bug report, if any.
Since @code{:expected-result} evaluates its argument when the test is
loaded, tests can be marked as known failures only on certain Emacs
versions, specific architectures, etc.:
@lisp
(ert-deftest foo ()
"A test that is expected to fail on Emacs 23 but succeed elsewhere."
:expected-result (if (string-match "GNU Emacs 23[.]" (emacs-version))
:failed
:passed)
...)
@end lisp
@node Tests and Their Environment, Useful Techniques, Expected Failures, How to Write Tests
@section Tests and Their Environment
The outcome of running a test should not depend on the current state
of the environment, and each test should leave its environment in the
same state it found it in. In particular, a test should not depend on
any Emacs customization variables or hooks, and if it has to make any
changes to Emacs' state or state external to Emacs such as the file
system, it should undo these changes before it returns, regardless of
whether it passed or failed.
Tests should not depend on the environment because any such
dependencies can make the test brittle or lead to failures that occur
only under certain circumstances and are hard to reproduce. Of
course, the code under test may have settings that affect its
behavior. In that case, it is best to make the test @code{let}-bind
all such settings variables to set up a specific configuration for the
duration of the test. The test can also set up a number of different
configurations and run the code under test with each.
Tests that have side effects on their environment should restore it to
its original state because any side effects that persist after the
test can disrupt the workflow of the programmer running the tests. If
the code under test has side effects on Emacs' current state, such as
on the current buffer or window configuration, the test should create
a temporary buffer for the code to manipulate (using
@code{with-temp-buffer}), or save and restore the window configuration
(using @code{save-window-excursion}), respectively. For aspects of
the state that can not be preserved with such macros, cleanup should
be performed with @code{unwind-protect}, to ensure that the cleanup
occurs even if the test fails.
An exception to this are messages that the code under test prints with
@code{message} and similar logging; tests should not bother restoring
the @code{*Message*} buffer to its original state.
The above guidelines imply that tests should avoid calling highly
customizable commands such as @code{find-file}, except, of course, if
such commands are what they want to test. The exact behavior of
@code{find-file} depends on many settings such as
@code{find-file-wildcards}, @code{enable-local-variables}, and
@code{auto-mode-alist}. It is difficult to write a meaningful test if
its behavior can be affected by so many external factors. Also,
@code{find-file} has side effects that are hard to predict and thus
hard to undo: It may create a new buffer or may reuse an existing
buffer if one is already visiting the requested file; and it runs
@code{find-file-hook}, which can have arbitrary side effects.
Instead, it is better to use lower-level mechanisms with simple and
predictable semantics like @code{with-temp-buffer}, @code{insert} or
@code{insert-file-contents-literally}, and activating the desired mode
by calling the corresponding function directly --- after binding the
hook variables to nil. This avoids the above problems.
@node Useful Techniques, , Tests and Their Environment, How to Write Tests
@section Useful Techniques when Writing Tests
Testing simple functions that have no side effects and no dependencies
on their environment is easy. Such tests often look like this:
@lisp
(ert-deftest ert-test-mismatch ()
(should (eql (ert--mismatch "" "") nil))
(should (eql (ert--mismatch "" "a") 0))
(should (eql (ert--mismatch "a" "a") nil))
(should (eql (ert--mismatch "ab" "a") 1))
(should (eql (ert--mismatch "Aa" "aA") 0))
(should (eql (ert--mismatch '(a b c) '(a b d)) 2)))
@end lisp
This test calls the function @code{ert--mismatch} several times with
various combinations of arguments and compares the return value to the
expected return value. (Some programmers prefer @code{(should (eql
EXPECTED ACTUAL))} over the @code{(should (eql ACTUAL EXPECTED))}
shown here. ERT works either way.)
Here's a more complicated test:
@lisp
(ert-deftest ert-test-record-backtrace ()
(let ((test (make-ert-test :body (lambda () (ert-fail "foo")))))
(let ((result (ert-run-test test)))
(should (ert-test-failed-p result))
(with-temp-buffer
(ert--print-backtrace (ert-test-failed-backtrace result))
(goto-char (point-min))
(end-of-line)
(let ((first-line (buffer-substring-no-properties (point-min) (point))))
(should (equal first-line " signal(ert-test-failed (\"foo\"))")))))))
@end lisp
This test creates a test object using @code{make-ert-test} whose body
will immediately signal failure. It then runs that test and asserts
that it fails. Then, it creates a temporary buffer and invokes
@code{ert--print-backtrace} to print the backtrace of the failed test
to the current buffer. Finally, it extracts the first line from the
buffer and asserts that it matches what we expect. It uses
@code{buffer-substring-no-properties} and @code{equal} to ignore text
properties; for a test that takes properties into account,
@code{buffer-substring} and @code{ert-equal-including-properties}
could be used instead.
The reason why this test only checks the first line of the backtrace
is that the remainder of the backtrace is dependent on ERT's internals
as well as whether the code is running interpreted or compiled. By
looking only at the first line, the test checks a useful property
--- that the backtrace correctly captures the call to @code{signal} that
results from the call to @code{ert-fail} --- without being brittle.
This example also shows that writing tests is much easier if the code
under test was structured with testing in mind.
For example, if @code{ert-run-test} accepted only symbols that name
tests rather than test objects, the test would need a name for the
failing test, which would have to be a temporary symbol generated with
@code{make-symbol}, to avoid side effects on Emacs' state. Choosing
the right interface for @code{ert-run-tests} allows the test to be
simpler.
Similarly, if @code{ert--print-backtrace} printed the backtrace to a
buffer with a fixed name rather than the current buffer, it would be
much harder for the test to undo the side effect. Of course, some
code somewhere needs to pick the buffer name. But that logic is
independent of the logic that prints backtraces, and keeping them in
separate functions allows us to test them independently.
A lot of code that you will encounter in Emacs was not written with
testing in mind. Sometimes, the easiest way to write tests for such
code is to restructure the code slightly to provide better interfaces
for testing. Usually, this makes the interfaces easier to use as
well.
@node How to Debug Tests, Extending ERT, How to Write Tests, Top
@chapter How to Debug Tests
This section describes how to use ERT's features to understand why
a test failed.
@menu
* Understanding Explanations:: How ERT gives details on why an assertion failed.
* Interactive Debugging:: Tools available in the ERT results buffer.
@end menu
@node Understanding Explanations, Interactive Debugging, How to Debug Tests, How to Debug Tests
@section Understanding Explanations
Failed @code{should} forms are reported like this:
@example
F addition-test
(ert-test-failed
((should
(=
(+ 1 2)
4))
:form
(= 3 4)
:value nil))
@end example
ERT shows what the @code{should} expression looked like and what
values its subexpressions had: The source code of the assertion was
@code{(should (= (+ 1 2) 4))}, which applied the function @code{=} to
the arguments @code{3} and @code{4}, resulting in the value
@code{nil}. In this case, the test is wrong; it should expect 3
rather than 4.
If a predicate like @code{equal} is used with @code{should}, ERT
provides a so-called @emph{explanation}:
@example
F list-test
(ert-test-failed
((should
(equal
(list 'a 'b 'c)
'(a b d)))
:form
(equal
(a b c)
(a b d))
:value nil :explanation
(list-elt 2
(different-atoms c d))))
@end example
In this case, the function @code{equal} was applied to the arguments
@code{(a b c)} and @code{(a b d)}. ERT's explanation shows that
the item at index 2 differs between the two lists; in one list, it is
the atom c, in the other, it is the atom d.
In simple examples like the above, the explanation is unnecessary.
But in cases where the difference is not immediately apparent, it can
save time:
@example
F test1
(ert-test-failed
((should
(equal x y))
:form
(equal a a)
:value nil :explanation
(different-symbols-with-the-same-name a a)))
@end example
ERT only provides explanations for predicates that have an explanation
function registered. @xref{Defining Explanation Functions}.
@node Interactive Debugging, , Understanding Explanations, How to Debug Tests
@section Interactive Debugging
Debugging failed tests works essentially the same way as debugging any
other problems with Lisp code. Here are a few tricks specific to
tests:
@itemize
@item Re-run the failed test a few times to see if it fails in the same way
each time. It's good to find out whether the behavior is
deterministic before spending any time looking for a cause. In the
ERT results buffer, @kbd{r} re-runs the selected test.
@item Use @kbd{.} to jump to the source code of the test to find out what
exactly it does. Perhaps the test is broken rather than the code
under test.
@item If the test contains a series of @code{should} forms and you can't
tell which one failed, use @kbd{l}, which shows you the list of all
@code{should} forms executed during the test before it failed.
@item Use @kbd{b} to view the backtrace. You can also use @kbd{d} to re-run
the test with debugging enabled, this will enter the debugger and show
the backtrace as well; but the top few frames shown there will not be
relevant to you since they are ERT's own debugger hook. @kbd{b}
strips them out, so it is more convenient.
@item If the test or the code under testing prints messages using
@code{message}, use @kbd{m} to see what messages it printed before it
failed. This can be useful to figure out how far it got.
@item You can instrument tests for debugging the same way you instrument
@code{defun}s for debugging --- go to the source code of the test and
type @kbd{@kbd{C-u} @kbd{C-M-x}}. Then, go back to the ERT buffer and
re-run the test with @kbd{r} or @kbd{d}.
@item If you have been editing and rearranging tests, it is possible that
ERT remembers an old test that you have since renamed or removed ---
renamings or removals of definitions in the source code leave around a
stray definition under the old name in the running process, this is a
common problem in Lisp. In such a situation, hit @kbd{D} to let ERT
forget about the obsolete test.
@end itemize
@node Extending ERT, Other Testing Concepts, How to Debug Tests, Top
@chapter Extending ERT
There are several ways to add functionality to ERT.
@menu
* Defining Explanation Functions:: Teach ERT about more predicates.
* Low-Level Functions for Working with Tests:: Use ERT's data for your purposes.
@end menu
@node Defining Explanation Functions, Low-Level Functions for Working with Tests, Extending ERT, Extending ERT
@section Defining Explanation Functions
The explanation function for a predicate is a function that takes the
same arguments as the predicate and returns an @emph{explanation}.
The explanation should explain why the predicate, when invoked with
the arguments given to the explanation function, returns the value
that it returns. The explanation can be any object but should have a
comprehensible printed representation. If the return value of the
predicate needs no explanation for a given list of arguments, the
explanation function should return nil.
To associate an explanation function with a predicate, add the
property @code{ert-explainer} to the symbol that names the predicate.
The value of the property should be the symbol that names the
explanation function.
@node Low-Level Functions for Working with Tests, , Defining Explanation Functions, Extending ERT
@section Low-Level Functions for Working with Tests
Both @code{ert-run-tests-interactively} and @code{ert-run-tests-batch}
are implemented on top of the lower-level test handling code in the
sections named ``Facilities for running a single test'', ``Test
selectors'', and ``Facilities for running a whole set of tests''.
If you want to write code that works with ERT tests, you should take a
look at this lower-level code. Symbols that start with @code{ert--}
are internal to ERT, those that start with @code{ert-} but not
@code{ert--} are meant to be usable by other code. But there is no
mature API yet.
Contributions to ERT are welcome.
@node Other Testing Concepts, , Extending ERT, Top
@chapter Other Testing Concepts
For information on mocks, stubs, fixtures, or test suites, see below.
@menu
* Mocks and Stubs:: Stubbing out code that is irrelevant to the test.
* Fixtures and Test Suites:: How ERT differs from tools for other languages.
@end menu
@node Mocks and Stubs, Fixtures and Test Suites, Other Testing Concepts, Other Testing Concepts
@section Other Tools for Emacs Lisp
Stubbing out functions or using so-called @emph{mocks} can make it
easier to write tests. See
@url{http://en.wikipedia.org/wiki/Mock_object} for an explanation of
the corresponding concepts in object-oriented languages.
ERT does not have built-in support for mocks or stubs. The package
@code{el-mock} (see @url{http://www.emacswiki.org/emacs/el-mock.el})
offers mocks for Emacs Lisp and can be used in conjunction with ERT.
@node Fixtures and Test Suites, , Mocks and Stubs, Other Testing Concepts
@section Fixtures and Test Suites
In many ways, ERT is similar to frameworks for other languages like
SUnit or JUnit. However, two features commonly found in such
frameworks are notably absent from ERT: fixtures and test suites.
Fixtures, as used e.g. in SUnit or JUnit, are mainly used to provide
an environment for a set of tests, and consist of set-up and tear-down
functions.
While fixtures are a useful syntactic simplification in other
languages, this does not apply to Lisp, where higher-order functions
and `unwind-protect' are available. One way to implement and use a
fixture in ERT is
@lisp
(defun my-fixture (body)
(unwind-protect
(progn [set up]
(funcall body))
[tear down]))
(ert-deftest my-test ()
(my-fixture
(lambda ()
[test code])))
@end lisp
(Another way would be a @code{with-my-fixture} macro.) This solves
the set-up and tear-down part, and additionally allows any test
to use any combination of fixtures, so it is more flexible than what
other tools typically allow.
If the test needs access to the environment the fixture sets up, the
fixture can be modified to pass arguments to the body.
These are well-known Lisp techniques. Special syntax for them could
be added but would provide only a minor simplification.
(If you are interested in such syntax, note that splitting set-up and
tear-down into separate functions, like *Unit tools usually do, makes
it impossible to establish dynamic `let' bindings as part of the
fixture. So, blindly imitating the way fixtures are implemented in
other languages would be counter-productive in Lisp.)
The purpose of test suites is to group related tests together.
The most common use of this is to run just the tests for one
particular module. Since symbol prefixes are the usual way of
separating module namespaces in Emacs Lisp, test selectors already
solve this by allowing regexp matching on test names; e.g., the
selector "^ert-" selects ERT's self-tests.
Other uses include grouping tests by their expected execution time to
run quick tests during interactive development and slow tests less
frequently. This can be achieved with the @code{:tag} argument to
@code{ert-deftest} and @code{tag} test selectors.
@bye
@c LocalWords: ERT Hagelberg Ohler JUnit namespace docstring ERT's
@c LocalWords: backtrace makefiles workflow backtraces API SUnit
@c LocalWords: subexpressions

View File

@ -47,7 +47,8 @@ INFO_TARGETS = $(infodir)/ccmode \
$(infodir)/org $(infodir)/url $(infodir)/speedbar \
$(infodir)/tramp $(infodir)/ses $(infodir)/smtpmail \
$(infodir)/flymake $(infodir)/newsticker $(infodir)/rcirc \
$(infodir)/erc $(infodir)/remember $(infodir)/nxml-mode \
$(infodir)/erc $(infodir)/ert \
$(infodir)/remember $(infodir)/nxml-mode \
$(infodir)/epa $(infodir)/mairix-el $(infodir)/sasl \
$(infodir)/auth $(infodir)/eieio $(infodir)/ede \
$(infodir)/semantic $(infodir)/edt
@ -58,7 +59,8 @@ DVI_TARGETS = calc.dvi cc-mode.dvi cl.dvi dbus.dvi dired-x.dvi \
ada-mode.dvi autotype.dvi idlwave.dvi eudc.dvi ebrowse.dvi \
pcl-cvs.dvi woman.dvi eshell.dvi org.dvi url.dvi \
speedbar.dvi tramp.dvi ses.dvi smtpmail.dvi flymake.dvi \
newsticker.dvi rcirc.dvi erc.dvi remember.dvi nxml-mode.dvi \
newsticker.dvi rcirc.dvi erc.dvi ert.dvi \
remember.dvi nxml-mode.dvi \
epa.dvi mairix-el.dvi sasl.dvi auth.dvi eieio.dvi ede.dvi \
semantic.dvi edt.dvi
INFOSOURCES = info.texi
@ -305,6 +307,11 @@ $(infodir)/erc: erc.texi
erc.dvi: erc.texi
$(ENVADD) $(TEXI2DVI) $(srcdir)/erc.texi
$(infodir)/ert: ert.texi
$(MAKEINFO) ert.texi
ert.dvi: ert.texi
$(ENVADD) $(TEXI2DVI) $(srcdir)/ert.texi
$(infodir)/epa: epa.texi
$(MAKEINFO) epa.texi
epa.dvi: epa.texi
@ -362,7 +369,7 @@ clean: mostlyclean
$(infodir)/url* $(infodir)/org* \
$(infodir)/flymake* $(infodir)/newsticker* \
$(infodir)/sieve* $(infodir)/pgg* \
$(infodir)/erc* $(infodir)/rcirc* \
$(infodir)/erc* $(infodir)/ert* $(infodir)/rcirc* \
$(infodir)/remember* $(infodir)/nxml-mode* \
$(infodir)/epa* $(infodir)/sasl* \
$(infodir)/mairix-el* $(infodir)/auth* \

View File

@ -1,3 +1,7 @@
2011-01-13 Christian Ohler <ohler@gnu.org>
* NEWS: Mention ERT.
2011-01-10 Jan Moringen <jan.moringen@uni-bielefeld.de>
* NEWS: Add new function dbus-register-service.

View File

@ -210,6 +210,10 @@ automatically when Emacs starts up. To disable this, set
`package-enable-at-startup' to nil. To change which packages are
loaded, customize `package-load-list'.
** An Emacs Lisp testing tool is now included.
Emacs Lisp developers can use this tool to write automated tests for
their code. See the ERT info manual for details.
** Custom Themes
*** `M-x customize-themes' lists Custom themes which can be enabled.
@ -621,6 +625,11 @@ Notifications API. It requires D-Bus for communication.
* Incompatible Lisp Changes in Emacs 24.1
** `compose-mail' now accepts an optional 8th arg, RETURN-ACTION, and
passes it to the mail user agent function. This argument specifies an
action for returning to the caller after finishing with the mail.
This is currently used by Rmail to delete a mail window.
** For mouse click input events in the text area, the Y pixel
coordinate in the POSITION list now counts from the top of the text
area, excluding any header line. Previously, it counted from the top

View File

@ -25,12 +25,12 @@
(custom-theme-set-faces
'tsdh-dark
'(default ((t (:background "gray20" :foreground "white smoke"))))
'(diff-added ((t (:inherit diff-changed :background "light green"))))
'(diff-changed ((t (:background "light steel blue"))))
'(diff-added ((t (:inherit diff-changed :background "dark green"))))
'(diff-changed ((t (:background "midnight blue"))))
'(diff-indicator-added ((t (:inherit diff-indicator-changed))))
'(diff-indicator-changed ((t (:weight bold))))
'(diff-indicator-removed ((t (:inherit diff-indicator-changed))))
'(diff-removed ((t (:inherit diff-changed :background "sandy brown"))))
'(diff-removed ((t (:inherit diff-changed :background "dark red"))))
'(dired-directory ((t (:inherit font-lock-function-name-face :weight bold))))
'(hl-line ((t (:background "grey28"))))
'(message-header-subject ((t (:foreground "SkyBlue"))))

View File

@ -1,3 +1,48 @@
2011-01-13 Kim F. Storm <storm@cua.dk>
* ido.el (ido-may-cache-directory): Move "too-big" check later.
(ido-next-match, ido-prev-match): Fix stray reordering of matching
items when cycling through the matches.
2011-01-13 Tassilo Horn <tassilo@member.fsf.org>
* dired-x.el (dired-omit-verbose): New defcustom that allows
disabling the omit messages.
(dired-omit-expunge): Use it.
2011-01-13 Christian Ohler <ohler@gnu.org>
* emacs-lisp/ert.el, emacs-lisp/ert-x.el: New files.
2011-01-13 Chong Yidong <cyd@stupidchicken.com>
* font-lock.el (font-lock-verbose): Default to nil.
2011-01-13 Chong Yidong <cyd@stupidchicken.com>
* simple.el (sendmail-user-agent-compose): Move to sendmail.el.
(compose-mail): New arg RETURN-ACTION.
(compose-mail-other-window, compose-mail-other-frame): Likewise.
* mail/sendmail.el (mail-return-action): New var.
(mail-mode): Make it buffer-local.
(mail-bury): Obey it. Move special Rmail window handling to
rmail-mail-return.
(mail, mail-setup): New arg RETURN-ACTION.
(sendmail-user-agent-compose): Move from simple.el.
* mail/rmail.el (rmail-mail-return): New function.
(rmail-start-mail): Pass it to compose-mail.
2011-01-12 Chong Yidong <cyd@stupidchicken.com>
* menu-bar.el (menu-bar-custom-menu): Tweak Mule and Customize
menus. Add menu item for customize-themes.
* cus-theme.el (customize-themes):
* emacs-lisp/package.el (package--list-packages): Use
switch-to-buffer.
2011-01-11 Johan Bockgård <bojohan@gnu.org>
* emacs-lisp/unsafep.el (unsafep): Handle backquoted forms.

View File

@ -541,7 +541,7 @@ Do not call this mode function yourself. It is meant for internal use."
When called from Lisp, BUFFER should be the buffer to use; if
omitted, a buffer named *Custom Themes* is used."
(interactive)
(pop-to-buffer (get-buffer-create (or buffer "*Custom Themes*")))
(switch-to-buffer (get-buffer-create (or buffer "*Custom Themes*")))
(let ((inhibit-read-only t))
(erase-buffer))
(custom-theme-choose-mode)

View File

@ -189,6 +189,12 @@ files and lock files."
:type 'regexp
:group 'dired-x)
(defcustom dired-omit-verbose t
"When non-nil, show messages when omitting files.
When nil, don't show messages."
:type 'boolean
:group 'dired-x)
(defcustom dired-find-subdir nil ; t is pretty near to DWIM...
"If non-nil, Dired always finds a directory in a buffer of its own.
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
(not dired-omit-size-limit)
(< (buffer-size) dired-omit-size-limit)
(progn
(message "Not omitting: directory larger than %d characters."
dired-omit-size-limit)
(when dired-omit-verbose
(message "Not omitting: directory larger than %d characters."
dired-omit-size-limit))
(setq dired-omit-mode nil)
nil)))
(let ((omit-re (or regexp (dired-omit-regexp)))
@ -622,12 +629,14 @@ This functions works by temporarily binding `dired-marker-char' to
count)
(or (string= omit-re "")
(let ((dired-marker-char dired-omit-marker-char))
(message "Omitting...")
(when dired-omit-verbose (message "Omitting..."))
(if (dired-mark-unmarked-files omit-re nil nil dired-omit-localp)
(progn
(setq count (dired-do-kill-lines nil "Omitted %d line%s."))
(setq count (dired-do-kill-lines
nil
(if dired-omit-verbose "Omitted %d line%s." "")))
(force-mode-line-update))
(message "(Nothing to omit)"))))
(when dired-omit-verbose (message "(Nothing to omit)")))))
;; Try to preserve modified state of buffer. So `%*' doesn't appear
;; in mode-line of omitted buffers.
(set-buffer-modified-p (and old-modified-p

View File

@ -4021,7 +4021,7 @@ true then the type of the file linked to by FILE is printed instead.
;;;***
;;;### (autoloads (dired-do-relsymlink dired-jump) "dired-x" "dired-x.el"
;;;;;; "27c312d6d5d40d8cb4ef8d62e30d5f4a")
;;;;;; "6181a5bcc2b61255676a7a41549b9f40")
;;; Generated autoloads from dired-x.el
(autoload 'dired-jump "dired-x" "\

290
lisp/emacs-lisp/ert-x.el Normal file
View File

@ -0,0 +1,290 @@
;;; ert-x.el --- Staging area for experimental extensions to ERT
;; Copyright (C) 2008, 2010, 2011 Free Software Foundation, Inc.
;; Author: Lennart Borgman (lennart O borgman A gmail O com)
;; Author: Christian Ohler <ohler@gnu.org>
;; This file is part of GNU Emacs.
;; This program is free software: you can redistribute it and/or
;; modify it under the terms of the GNU General Public License as
;; published by the Free Software Foundation, either version 3 of the
;; License, or (at your option) any later version.
;;
;; This program is distributed in the hope that it will be useful, but
;; WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
;; General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see `http://www.gnu.org/licenses/'.
;;; Commentary:
;; This file includes some extra helper functions to use while writing
;; automated tests with ERT. These have been proposed as extensions
;; to ERT but are not mature yet and likely to change.
;;; Code:
(eval-when-compile
(require 'cl))
(require 'ert)
;;; Test buffers.
(defun ert--text-button (string &rest properties)
"Return a string containing STRING as a text button with PROPERTIES.
See `make-text-button'."
(with-temp-buffer
(insert string)
(apply #'make-text-button (point-min) (point-max) properties)
(buffer-string)))
(defun ert--format-test-buffer-name (base-name)
"Compute a test buffer name based on BASE-NAME.
Helper function for `ert--test-buffers'."
(format "*Test buffer (%s)%s*"
(or (and (ert-running-test)
(ert-test-name (ert-running-test)))
"<anonymous test>")
(if base-name
(format ": %s" base-name)
"")))
(defvar ert--test-buffers (make-hash-table :weakness t)
"Table of all test buffers. Keys are the buffer objects, values are t.
The main use of this table is for `ert-kill-all-test-buffers'.
Not all buffers in this table are necessarily live, but all live
test buffers are in this table.")
(define-button-type 'ert--test-buffer-button
'action #'ert--test-buffer-button-action
'help-echo "mouse-2, RET: Pop to test buffer")
(defun ert--test-buffer-button-action (button)
"Pop to the test buffer that BUTTON is associated with."
(pop-to-buffer (button-get button 'ert--test-buffer)))
(defun ert--call-with-test-buffer (ert--base-name ert--thunk)
"Helper function for `ert-with-test-buffer'.
Create a test buffer with a name based on ERT--BASE-NAME and run
ERT--THUNK with that buffer as current."
(let* ((ert--buffer (generate-new-buffer
(ert--format-test-buffer-name ert--base-name)))
(ert--button (ert--text-button (buffer-name ert--buffer)
:type 'ert--test-buffer-button
'ert--test-buffer ert--buffer)))
(puthash ert--buffer 't ert--test-buffers)
;; We don't use `unwind-protect' here since we want to kill the
;; buffer only on success.
(prog1 (with-current-buffer ert--buffer
(ert-info (ert--button :prefix "Buffer: ")
(funcall ert--thunk)))
(kill-buffer ert--buffer)
(remhash ert--buffer ert--test-buffers))))
(defmacro* ert-with-test-buffer ((&key ((:name name-form)))
&body body)
"Create a test buffer and run BODY in that buffer.
To be used in ERT tests. If BODY finishes successfully, the test
buffer is killed; if there is an error, the test buffer is kept
around on error for further inspection. Its name is derived from
the name of the test and the result of NAME-FORM."
(declare (debug ((form) body))
(indent 1))
`(ert--call-with-test-buffer ,name-form (lambda () ,@body)))
;; We use these `put' forms in addition to the (declare (indent)) in
;; the defmacro form since the `declare' alone does not lead to
;; correct indentation before the .el/.elc file is loaded.
;; Autoloading these `put' forms solves this.
;;;###autoload
(progn
;; TODO(ohler): Figure out what these mean and make sure they are correct.
(put 'ert-with-test-buffer 'lisp-indent-function 1))
;;;###autoload
(defun ert-kill-all-test-buffers ()
"Kill all test buffers that are still live."
(interactive)
(let ((count 0))
(maphash (lambda (buffer dummy)
(when (or (not (buffer-live-p buffer))
(kill-buffer buffer))
(incf count)))
ert--test-buffers)
(message "%s out of %s test buffers killed"
count (hash-table-count ert--test-buffers)))
;; It could be that some test buffers were actually kept alive
;; (e.g., due to `kill-buffer-query-functions'). I'm not sure what
;; to do about this. For now, let's just forget them.
(clrhash ert--test-buffers)
nil)
;;; Simulate commands.
(defun ert-simulate-command (command)
;; FIXME: add unread-events
"Simulate calling COMMAND the way the Emacs command loop would call it.
This effectively executes
\(apply (car COMMAND) (cdr COMMAND)\)
and returns the same value, but additionally runs hooks like
`pre-command-hook' and `post-command-hook', and sets variables
like `this-command' and `last-command'.
COMMAND should be a list where the car is the command symbol and
the rest are arguments to the command.
NOTE: Since the command is not called by `call-interactively'
test for `called-interactively' in the command will fail."
(assert (listp command) t)
(assert (commandp (car command)) t)
(assert (not unread-command-events) t)
(let (return-value)
;; For the order of things here see command_loop_1 in keyboard.c.
;;
;; The command loop will reset the command-related variables so
;; there is no reason to let-bind them. They are set here,
;; however, to be able to test several commands in a row and how
;; they affect each other.
(setq deactivate-mark nil
this-original-command (car command)
;; remap through active keymaps
this-command (or (command-remapping this-original-command)
this-original-command))
(run-hooks 'pre-command-hook)
(setq return-value (apply (car command) (cdr command)))
(run-hooks 'post-command-hook)
(when deferred-action-list
(run-hooks 'deferred-action-function))
(setq real-last-command (car command)
last-command this-command)
(when (boundp 'last-repeatable-command)
(setq last-repeatable-command real-last-command))
(when (and deactivate-mark transient-mark-mode) (deactivate-mark))
(assert (not unread-command-events) t)
return-value))
(defun ert-run-idle-timers ()
"Run all idle timers (from `timer-idle-list')."
(dolist (timer (copy-sequence timer-idle-list))
(timer-event-handler timer)))
;;; Miscellaneous utilities.
(defun ert-filter-string (s &rest regexps)
"Return a copy of S with all matches of REGEXPS removed.
Elements of REGEXPS may also be two-element lists \(REGEXP
SUBEXP\), where SUBEXP is the number of a subexpression in
REGEXP. In that case, only that subexpression will be removed
rather than the entire match."
;; Use a temporary buffer since replace-match copies strings, which
;; would lead to N^2 runtime.
(with-temp-buffer
(insert s)
(dolist (x regexps)
(destructuring-bind (regexp subexp) (if (listp x) x `(,x nil))
(goto-char (point-min))
(while (re-search-forward regexp nil t)
(replace-match "" t t nil subexp))))
(buffer-string)))
(defun ert-propertized-string (&rest args)
"Return a string with properties as specified by ARGS.
ARGS is a list of strings and plists. The strings in ARGS are
concatenated to produce an output string. In the output string,
each string from ARGS will be have the preceding plist as its
property list, or no properties if there is no plist before it.
As a simple example,
\(ert-propertized-string \"foo \" '(face italic) \"bar\" \" baz\" nil \
\" quux\"\)
would return the string \"foo bar baz quux\" where the substring
\"bar baz\" has a `face' property with the value `italic'.
None of the ARGS are modified, but the return value may share
structure with the plists in ARGS."
(with-temp-buffer
(loop with current-plist = nil
for x in args do
(etypecase x
(string (let ((begin (point)))
(insert x)
(set-text-properties begin (point) current-plist)))
(list (unless (zerop (mod (length x) 2))
(error "Odd number of args in plist: %S" x))
(setq current-plist x))))
(buffer-string)))
(defun ert-call-with-buffer-renamed (buffer-name thunk)
"Protect the buffer named BUFFER-NAME from side-effects and run THUNK.
Renames the buffer BUFFER-NAME to a new temporary name, creates a
new buffer named BUFFER-NAME, executes THUNK, kills the new
buffer, and renames the original buffer back to BUFFER-NAME.
This is useful if THUNK has undesirable side-effects on an Emacs
buffer with a fixed name such as *Messages*."
(lexical-let ((new-buffer-name (generate-new-buffer-name
(format "%s orig buffer" buffer-name))))
(with-current-buffer (get-buffer-create buffer-name)
(rename-buffer new-buffer-name))
(unwind-protect
(progn
(get-buffer-create buffer-name)
(funcall thunk))
(when (get-buffer buffer-name)
(kill-buffer buffer-name))
(with-current-buffer new-buffer-name
(rename-buffer buffer-name)))))
(defmacro* ert-with-buffer-renamed ((buffer-name-form) &body body)
"Protect the buffer named BUFFER-NAME from side-effects and run BODY.
See `ert-call-with-buffer-renamed' for details."
(declare (indent 1))
`(ert-call-with-buffer-renamed ,buffer-name-form (lambda () ,@body)))
(defun ert-buffer-string-reindented (&optional buffer)
"Return the contents of BUFFER after reindentation.
BUFFER defaults to current buffer. Does not modify BUFFER."
(with-current-buffer (or buffer (current-buffer))
(let ((clone nil))
(unwind-protect
(progn
;; `clone-buffer' doesn't work if `buffer-file-name' is non-nil.
(let ((buffer-file-name nil))
(setq clone (clone-buffer)))
(with-current-buffer clone
(let ((inhibit-read-only t))
(indent-region (point-min) (point-max)))
(buffer-string)))
(when clone
(let ((kill-buffer-query-functions nil))
(kill-buffer clone)))))))
(provide 'ert-x)
;;; ert-x.el ends here

2544
lisp/emacs-lisp/ert.el Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1663,15 +1663,15 @@ A value of nil means to display all packages.")
Optional PACKAGES is a list of names of packages (symbols) to
list; the default is to display everything in `package-alist'."
(require 'finder-inf nil t)
(with-current-buffer (get-buffer-create "*Packages*")
(package-menu-mode)
(set (make-local-variable 'package-menu-package-list) packages)
(set (make-local-variable 'package-menu-sort-key) nil)
(package--generate-package-list)
;; It's okay to use pop-to-buffer here. The package menu buffer
;; has keybindings, and the user just typed `M-x list-packages',
;; suggesting that they might want to use them.
(pop-to-buffer (current-buffer))))
(let ((buf (get-buffer-create "*Packages*")))
(with-current-buffer buf
(package-menu-mode)
(set (make-local-variable 'package-menu-package-list) packages)
(set (make-local-variable 'package-menu-sort-key) nil)
(package--generate-package-list))
;; The package menu buffer has keybindings. If the user types
;; `M-x list-packages', that suggests it should become current.
(switch-to-buffer buf)))
;;;###autoload
(defun list-packages ()

View File

@ -276,13 +276,14 @@ decoration for buffers in C++ mode, and level 1 decoration otherwise."
(integer :tag "level" 1)))))
:group 'font-lock)
(defcustom font-lock-verbose 0
(defcustom font-lock-verbose nil
"If non-nil, means show status messages for buffer fontification.
If a number, only buffers greater than this size have fontification messages."
:type '(choice (const :tag "never" nil)
(other :tag "always" t)
(integer :tag "size"))
:group 'font-lock)
:group 'font-lock
:version "24.1")
;; Originally these variable values were face names such as `bold' etc.

View File

@ -1,3 +1,16 @@
2011-01-13 Chong Yidong <cyd@stupidchicken.com>
* message.el (message-tool-bar-gnome): Tweak tool-bar items. Add
:vert-only tags.
(message-mail): New arg RETURN-ACTION.
(message-return-action): New var.
(message-bury): Use it.
(message-mode): Make it buffer-local.
(message-send-and-exit): Always call message-bury.
* gnus-msg.el (gnus-msg-mail): New arg RETURN-ACTION. Pass it to
message-mail.
2011-01-11 Lars Magne Ingebrigtsen <larsi@gnus.org>
* nnimap.el (nnimap-convert-partial-article): Protect against

View File

@ -477,7 +477,7 @@ Thank you for your help in stamping out bugs.
;;;###autoload
(defun gnus-msg-mail (&optional to subject other-headers continue
switch-action yank-action send-actions)
switch-action yank-action send-actions return-action)
"Start editing a mail message to be sent.
Like `message-mail', but with Gnus paraphernalia, particularly the
Gcc: header for archiving purposes."
@ -486,7 +486,7 @@ Gcc: header for archiving purposes."
mail-buf)
(gnus-setup-message 'message
(message-mail to subject other-headers continue
nil yank-action send-actions))
nil yank-action send-actions return-action))
(when switch-action
(setq mail-buf (current-buffer))
(switch-to-buffer buf)

View File

@ -1120,6 +1120,8 @@ It is a vector of the following headers:
(defvar message-checksum nil)
(defvar message-send-actions nil
"A list of actions to be performed upon successful sending of a message.")
(defvar message-return-action nil
"Action to return to the caller after sending or postphoning a message.")
(defvar message-exit-actions nil
"A list of actions to be performed upon exiting after sending a message.")
(defvar message-kill-actions nil
@ -2863,6 +2865,7 @@ M-RET `message-newline-and-reformat' (break the line and reformat)."
(set (make-local-variable 'message-reply-buffer) nil)
(set (make-local-variable 'message-inserted-headers) nil)
(set (make-local-variable 'message-send-actions) nil)
(set (make-local-variable 'message-return-action) nil)
(set (make-local-variable 'message-exit-actions) nil)
(set (make-local-variable 'message-kill-actions) nil)
(set (make-local-variable 'message-postpone-actions) nil)
@ -3955,11 +3958,9 @@ The text will also be indented the normal way."
(actions message-exit-actions))
(when (and (message-send arg)
(buffer-name buf))
(message-bury buf)
(if message-kill-buffer-on-exit
(kill-buffer buf)
(bury-buffer buf)
(when (eq buf (current-buffer))
(message-bury buf)))
(kill-buffer buf))
(message-do-actions actions)
t)))
@ -4009,9 +4010,8 @@ Instead, just auto-save the buffer and then bury it."
"Bury this mail BUFFER."
(let ((newbuf (other-buffer buffer)))
(bury-buffer buffer)
(if (and (window-dedicated-p (selected-window))
(not (null (delq (selected-frame) (visible-frame-list)))))
(delete-frame (selected-frame))
(if message-return-action
(apply (car message-return-action) (cdr message-return-action))
(switch-to-buffer newbuf))))
(defun message-send (&optional arg)
@ -6304,11 +6304,11 @@ between beginning of field and beginning of line."
;; YANK-ACTION, if non-nil, can be a buffer or a yank action of the
;; form (FUNCTION . ARGS).
(defun message-setup (headers &optional yank-action actions
continue switch-function)
continue switch-function return-action)
(let ((mua (message-mail-user-agent))
subject to field)
(if (not (and message-this-is-mail mua))
(message-setup-1 headers yank-action actions)
(message-setup-1 headers yank-action actions return-action)
(setq headers (copy-sequence headers))
(setq field (assq 'Subject headers))
(when field
@ -6356,11 +6356,12 @@ are not included."
(push header result)))
(nreverse result)))
(defun message-setup-1 (headers &optional yank-action actions)
(defun message-setup-1 (headers &optional yank-action actions return-action)
(dolist (action actions)
(condition-case nil
(add-to-list 'message-send-actions
`(apply ',(car action) ',(cdr action)))))
(setq message-return-action return-action)
(setq message-reply-buffer
(if (and (consp yank-action)
(eq (car yank-action) 'insert-buffer))
@ -6489,9 +6490,9 @@ are not included."
;;;
;;;###autoload
(defun message-mail (&optional to subject
other-headers continue switch-function
yank-action send-actions)
(defun message-mail (&optional to subject other-headers continue
switch-function yank-action send-actions
return-action &rest ignored)
"Start editing a mail message to be sent.
OTHER-HEADERS is an alist of header/value pairs. CONTINUE says whether
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."
(nconc
`((To . ,(or to "")) (Subject . ,(or subject "")))
(when other-headers other-headers))
yank-action send-actions continue switch-function)
yank-action send-actions continue switch-function
return-action)
;; FIXME: Should return nil if failure.
t))
@ -7642,24 +7644,22 @@ Pre-defined symbols include `message-tool-bar-gnome' and
(defcustom message-tool-bar-gnome
'((ispell-message "spell" nil
:vert-only t
:visible (or (not (boundp 'flyspell-mode))
(not flyspell-mode)))
(flyspell-buffer "spell" t
:vert-only t
:visible (and (boundp 'flyspell-mode)
flyspell-mode)
:help "Flyspell whole buffer")
(gmm-ignore "separator")
(message-send-and-exit "mail/send")
(message-send-and-exit "mail/send" t :label "Send")
(message-dont-send "mail/save-draft")
(message-kill-buffer "close") ;; stock_cancel
(mml-attach-file "attach" mml-mode-map)
(mml-attach-file "attach" mml-mode-map :vert-only t)
(mml-preview "mail/preview" mml-mode-map)
(mml-secure-message-sign-encrypt "lock" mml-mode-map :visible nil)
(message-insert-importance-high "important" nil :visible nil)
(message-insert-importance-low "unimportant" nil :visible nil)
(message-insert-disposition-notification-to "receipt" nil :visible nil)
(gmm-customize-mode "preferences" t :help "Edit mode preferences")
(message-info "help" t :help "Message manual"))
(message-insert-disposition-notification-to "receipt" nil :visible nil))
"List of items for the message tool bar (GNOME style).
See `gmm-tool-bar-from-list' for details on the format of the list."

View File

@ -1289,8 +1289,6 @@ Only used if `ido-use-virtual-buffers' is non-nil.")
(defun ido-may-cache-directory (&optional dir)
(setq dir (or dir ido-current-directory))
(cond
((ido-directory-too-big-p dir)
nil)
((and (ido-is-root-directory dir)
(or ido-enable-tramp-completion
(memq system-type '(windows-nt ms-dos))))
@ -1299,6 +1297,8 @@ Only used if `ido-use-virtual-buffers' is non-nil.")
(ido-cache-unc-valid))
((ido-is-ftp-directory dir)
(ido-cache-ftp-valid))
((ido-directory-too-big-p dir)
nil)
(t t)))
(defun ido-pp (list &optional sep)
@ -3072,8 +3072,8 @@ If repeated, insert text from buffer instead."
(if ido-matches
(let ((next (cadr ido-matches)))
(setq ido-cur-list (ido-chop ido-cur-list next))
(setq ido-rescan t)
(setq ido-rotate t))))
(setq ido-matches (ido-chop ido-matches next))
(setq ido-rescan nil))))
(defun ido-prev-match ()
"Put last element of `ido-matches' at the front of the list."
@ -3081,8 +3081,8 @@ If repeated, insert text from buffer instead."
(if ido-matches
(let ((prev (car (last ido-matches))))
(setq ido-cur-list (ido-chop ido-cur-list prev))
(setq ido-rescan t)
(setq ido-rotate t))))
(setq ido-matches (ido-chop ido-matches prev))
(setq ido-rescan nil))))
(defun ido-next-match-dir ()
"Find next directory in match list.

View File

@ -3441,30 +3441,62 @@ does not pop any summary buffer."
;;;; *** Rmail Mailing Commands ***
(defun rmail-start-mail (&optional noerase to subject in-reply-to cc
replybuffer sendactions same-window others)
(let (yank-action)
replybuffer sendactions same-window
other-headers)
(let ((switch-function
(cond (same-window nil)
(rmail-mail-new-frame 'switch-to-buffer-other-frame)
(t 'switch-to-buffer-other-window)))
yank-action)
(if replybuffer
;; The function used here must behave like insert-buffer wrt
;; point and mark (see doc of sc-cite-original).
(setq yank-action (list 'insert-buffer replybuffer)))
(setq others (cons (cons "cc" cc) others))
(setq others (cons (cons "in-reply-to" in-reply-to) others))
(if same-window
(compose-mail to subject others
noerase nil
yank-action sendactions)
(if rmail-mail-new-frame
(prog1
(compose-mail to subject others
noerase 'switch-to-buffer-other-frame
yank-action sendactions)
;; This is not a standard frame parameter;
;; nothing except sendmail.el looks at it.
(modify-frame-parameters (selected-frame)
'((mail-dedicated-frame . t))))
(compose-mail to subject others
noerase 'switch-to-buffer-other-window
yank-action sendactions)))))
(push (cons "cc" cc) other-headers)
(push (cons "in-reply-to" in-reply-to) other-headers)
(prog1
(compose-mail to subject other-headers noerase
switch-function yank-action sendactions
'(rmail-mail-return))
(if (eq switch-function 'switch-to-buffer-other-frame)
;; This is not a standard frame parameter; nothing except
;; sendmail.el looks at it.
(modify-frame-parameters (selected-frame)
'((mail-dedicated-frame . t)))))))
(defun rmail-mail-return ()
(cond
;; If there is only one visible frame with no special handling,
;; consider deleting the mail window to return to Rmail.
((or (null (delq (selected-frame) (visible-frame-list)))
(not (or (window-dedicated-p (frame-selected-window))
(and pop-up-frames (one-window-p))
(cdr (assq 'mail-dedicated-frame
(frame-parameters))))))
(let (rmail-flag summary-buffer)
(and (not (one-window-p))
(with-current-buffer
(window-buffer (next-window (selected-window) 'not))
(setq rmail-flag (eq major-mode 'rmail-mode))
(setq summary-buffer
(and (boundp 'mail-bury-selects-summary)
mail-bury-selects-summary
(boundp 'rmail-summary-buffer)
rmail-summary-buffer
(buffer-name rmail-summary-buffer)
(not (get-buffer-window rmail-summary-buffer))
rmail-summary-buffer))))
(if rmail-flag
;; If the Rmail buffer has a summary, show that.
(if summary-buffer (switch-to-buffer summary-buffer)
(delete-window)))))
;; If the frame was probably made for this buffer, the user
;; probably wants to delete it now.
((display-multi-frame-p)
(delete-frame (selected-frame)))
;; The previous frame is where normally they have the Rmail buffer
;; displayed.
(t (other-frame -1))))
(defun rmail-mail ()
"Send mail in another window.

View File

@ -419,8 +419,7 @@ in `message-auto-save-directory'."
(defvar mail-reply-action nil)
(defvar mail-send-actions nil
"A list of actions to be performed upon successful sending of a message.")
(put 'mail-reply-action 'permanent-local t)
(put 'mail-send-actions 'permanent-local t)
(defvar mail-return-action nil)
;;;###autoload
(defcustom mail-default-headers nil
@ -521,7 +520,46 @@ by Emacs.)")
(setq mail-alias-modtime modtime
mail-aliases t)))))
(defun mail-setup (to subject in-reply-to cc replybuffer actions)
;;;###autoload
(define-mail-user-agent 'sendmail-user-agent
'sendmail-user-agent-compose
'mail-send-and-exit)
;;;###autoload
(defun sendmail-user-agent-compose (&optional to subject other-headers
continue switch-function yank-action
send-actions return-action
&rest ignored)
(if switch-function
(let ((special-display-buffer-names nil)
(special-display-regexps nil)
(same-window-buffer-names nil)
(same-window-regexps nil))
(funcall switch-function "*mail*")))
(let ((cc (cdr (assoc-string "cc" other-headers t)))
(in-reply-to (cdr (assoc-string "in-reply-to" other-headers t)))
(body (cdr (assoc-string "body" other-headers t))))
(or (mail continue to subject in-reply-to cc yank-action
send-actions return-action)
continue
(error "Message aborted"))
(save-excursion
(rfc822-goto-eoh)
(while other-headers
(unless (member-ignore-case (car (car other-headers))
'("in-reply-to" "cc" "body"))
(insert (car (car other-headers)) ": "
(cdr (car other-headers))
(if use-hard-newlines hard-newline "\n")))
(setq other-headers (cdr other-headers)))
(when body
(forward-line 1)
(insert body))
t)))
(defun mail-setup (to subject in-reply-to cc replybuffer
actions return-action)
(or mail-default-reply-to
(setq mail-default-reply-to (getenv "REPLYTO")))
(sendmail-sync-aliases)
@ -537,8 +575,12 @@ by Emacs.)")
(set-buffer-multibyte (default-value 'enable-multibyte-characters))
(if current-input-method
(inactivate-input-method))
;; Local variables for Mail mode.
(setq mail-send-actions actions)
(setq mail-reply-action replybuffer)
(setq mail-return-action return-action)
(goto-char (point-min))
(if mail-setup-with-from
(mail-insert-from-field))
@ -629,6 +671,7 @@ Turning on Mail mode runs the normal hooks `text-mode-hook' and
`mail-mode-hook' (in that order)."
(make-local-variable 'mail-reply-action)
(make-local-variable 'mail-send-actions)
(make-local-variable 'mail-return-action)
(setq buffer-offer-save t)
(make-local-variable 'font-lock-defaults)
(setq font-lock-defaults '(mail-font-lock-keywords t t))
@ -762,39 +805,9 @@ Prefix arg means don't delete this window."
"Bury this mail buffer."
(let ((newbuf (other-buffer (current-buffer))))
(bury-buffer (current-buffer))
(if (and (or nil
;; In this case, we need to go to a different frame.
(window-dedicated-p (frame-selected-window))
;; In this mode of operation, the frame was probably
;; made for this buffer, so the user probably wants
;; to delete it now.
(and pop-up-frames (one-window-p))
(cdr (assq 'mail-dedicated-frame (frame-parameters))))
(not (null (delq (selected-frame) (visible-frame-list)))))
(progn
(if (display-multi-frame-p)
(delete-frame (selected-frame))
;; The previous frame is where normally they have the
;; Rmail buffer displayed.
(other-frame -1)))
(let (rmail-flag summary-buffer)
(and (not arg)
(not (one-window-p))
(with-current-buffer
(window-buffer (next-window (selected-window) 'not))
(setq rmail-flag (eq major-mode 'rmail-mode))
(setq summary-buffer
(and mail-bury-selects-summary
(boundp 'rmail-summary-buffer)
rmail-summary-buffer
(buffer-name rmail-summary-buffer)
(not (get-buffer-window rmail-summary-buffer))
rmail-summary-buffer))))
(if rmail-flag
;; If the Rmail buffer has a summary, show that.
(if summary-buffer (switch-to-buffer summary-buffer)
(delete-window))
(switch-to-buffer newbuf))))))
(if (and (null arg) mail-return-action)
(apply (car mail-return-action) (cdr mail-return-action))
(switch-to-buffer newbuf))))
(defcustom mail-send-hook nil
"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."
;;;###autoload (add-hook 'same-window-buffer-names (purecopy "*unsent mail*"))
;;;###autoload
(defun mail (&optional noerase to subject in-reply-to cc replybuffer actions)
(defun mail (&optional noerase to subject in-reply-to cc replybuffer
actions return-action)
"Edit a message to be sent. Prefix arg means resume editing (don't erase).
When this function returns, the buffer `*mail*' is selected.
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
when the message is sent, we apply FUNCTION to ARGS.
This is how Rmail arranges to mark messages `answered'."
(interactive "P")
;; This is commented out because I found it was confusing in practice.
;; It is easy enough to rename *mail* by hand with rename-buffer
;; if you want to have multiple mail buffers.
;; And then you can control which messages to save. --rms.
;; (let ((index 1)
;; buffer)
;; ;; If requested, look for a mail buffer that is modified and go to it.
;; (if noerase
;; (progn
;; (while (and (setq buffer
;; (get-buffer (if (= 1 index) "*mail*"
;; (format "*mail*<%d>" index))))
;; (not (buffer-modified-p buffer)))
;; (setq index (1+ index)))
;; (if buffer (switch-to-buffer buffer)
;; ;; If none exists, start a new message.
;; ;; This will never re-use an existing unmodified mail buffer
;; ;; (since index is not 1 anymore). Perhaps it should.
;; (setq noerase nil))))
;; ;; Unless we found a modified message and are happy, start a new message.
;; (if (not noerase)
;; (progn
;; ;; Look for existing unmodified mail buffer.
;; (while (and (setq buffer
;; (get-buffer (if (= 1 index) "*mail*"
;; (format "*mail*<%d>" index))))
;; (buffer-modified-p buffer))
;; (setq index (1+ index)))
;; ;; If none, make a new one.
;; (or buffer
;; (setq buffer (generate-new-buffer "*mail*")))
;; ;; Go there and initialize it.
;; (switch-to-buffer buffer)
;; (erase-buffer)
;; (setq default-directory (expand-file-name "~/"))
;; (auto-save-mode auto-save-default)
;; (mail-mode)
;; (mail-setup to subject in-reply-to cc replybuffer actions)
;; (if (and buffer-auto-save-file-name
;; (file-exists-p buffer-auto-save-file-name))
;; (message "Auto save file for draft message exists; consider M-x mail-recover"))
;; t))
(if (eq noerase 'new)
(pop-to-buffer (generate-new-buffer "*mail*"))
(and noerase
@ -1772,7 +1743,8 @@ The seventh argument ACTIONS is a list of actions to take
t))
(let ((inhibit-read-only t))
(erase-buffer)
(mail-setup to subject in-reply-to cc replybuffer actions)
(mail-setup to subject in-reply-to cc replybuffer actions
return-action)
(setq initialized t)))
(if (and buffer-auto-save-file-name
(file-exists-p buffer-auto-save-file-name))

View File

@ -584,18 +584,15 @@ Do the same for the keys of the same name."
(defvar menu-bar-custom-menu (make-sparse-keymap "Customize"))
(define-key menu-bar-custom-menu [customize-apropos-groups]
`(menu-item ,(purecopy "Groups Matching Regexp...") customize-apropos-groups
:help ,(purecopy "Browse groups whose names match regexp")))
(define-key menu-bar-custom-menu [customize-apropos-faces]
`(menu-item ,(purecopy "Faces Matching Regexp...") customize-apropos-faces
:help ,(purecopy "Browse faces whose names match regexp")))
`(menu-item ,(purecopy "Faces Matching...") customize-apropos-faces
:help ,(purecopy "Browse faces matching a regexp or word list")))
(define-key menu-bar-custom-menu [customize-apropos-options]
`(menu-item ,(purecopy "Options Matching Regexp...") customize-apropos-options
:help ,(purecopy "Browse options whose names match regexp")))
`(menu-item ,(purecopy "Options Matching...") customize-apropos-options
:help ,(purecopy "Browse options matching a regexp or word list")))
(define-key menu-bar-custom-menu [customize-apropos]
`(menu-item ,(purecopy "Settings Matching Regexp...") customize-apropos
:help ,(purecopy "Browse customizable settings whose names match regexp")))
`(menu-item ,(purecopy "All Settings Matching...") customize-apropos
:help ,(purecopy "Browse customizable settings matching a regexp or word list")))
(define-key menu-bar-custom-menu [separator-1]
menu-bar-separator)
(define-key menu-bar-custom-menu [customize-group]
@ -623,6 +620,9 @@ Do the same for the keys of the same name."
(define-key menu-bar-custom-menu [customize]
`(menu-item ,(purecopy "Top-level Customization Group") customize
:help ,(purecopy "The master group called `Emacs'")))
(define-key menu-bar-custom-menu [customize-themes]
`(menu-item ,(purecopy "Custom Themes") customize-themes
:help ,(purecopy "Choose a pre-defined customization theme")))
;(defvar menu-bar-preferences-menu (make-sparse-keymap "Preferences"))
@ -1144,7 +1144,7 @@ mail status in mode line"))
;; It is better not to use backquote here,
;; because that makes a bootstrapping problem
;; if you need to recompile all the Lisp files using interpreted code.
`(menu-item ,(purecopy "Mule (Multilingual Environment)") ,mule-menu-keymap
`(menu-item ,(purecopy "Multilingual Environment") ,mule-menu-keymap
;; Most of the MULE menu actually does make sense in unibyte mode,
;; e.g. language selection.
;;; :visible '(default-value 'enable-multibyte-characters)

View File

@ -1,3 +1,7 @@
2011-01-13 Chong Yidong <cyd@stupidchicken.com>
* mh-comp.el (mh-user-agent-compose): New arg RETURN-ACTION.
2010-11-07 Glenn Morris <rgm@gnu.org>
* mh-seq.el (mh-read-msg-list): Use point-at-eol.

View File

@ -199,7 +199,8 @@ applications should use `mh-user-agent-compose'."
;;;###autoload
(defun mh-user-agent-compose (&optional to subject other-headers continue
switch-function yank-action
send-actions)
send-actions return-action
&rest ignored)
"Set up mail composition draft with the MH mail system.
This is the `mail-user-agent' entry point to MH-E. This function
conforms to the contract specified by `define-mail-user-agent'
@ -213,8 +214,8 @@ OTHER-HEADERS is an alist specifying additional header fields.
Elements look like (HEADER . VALUE) where both HEADER and VALUE
are strings.
CONTINUE, SWITCH-FUNCTION, YANK-ACTION and SEND-ACTIONS are
ignored."
CONTINUE, SWITCH-FUNCTION, YANK-ACTION, SEND-ACTIONS, and
RETURN-ACTION are ignored."
(mh-find-path)
(let ((mh-error-if-no-draft t))
(mh-send to "" subject)

View File

@ -5712,10 +5712,6 @@ appears to have customizations applying to the old default,
:version "23.2"
:group 'mail)
(define-mail-user-agent 'sendmail-user-agent
'sendmail-user-agent-compose
'mail-send-and-exit)
(defun rfc822-goto-eoh ()
;; Go to header delimiter line in a mail message, following RFC822 rules
(goto-char (point-min))
@ -5723,37 +5719,9 @@ appears to have customizations applying to the old default,
"^\\([:\n]\\|[^: \t\n]+[ \t\n]\\)" nil 'move)
(goto-char (match-beginning 0))))
(defun sendmail-user-agent-compose (&optional to subject other-headers continue
switch-function yank-action
send-actions)
(if switch-function
(let ((special-display-buffer-names nil)
(special-display-regexps nil)
(same-window-buffer-names nil)
(same-window-regexps nil))
(funcall switch-function "*mail*")))
(let ((cc (cdr (assoc-string "cc" other-headers t)))
(in-reply-to (cdr (assoc-string "in-reply-to" other-headers t)))
(body (cdr (assoc-string "body" other-headers t))))
(or (mail continue to subject in-reply-to cc yank-action send-actions)
continue
(error "Message aborted"))
(save-excursion
(rfc822-goto-eoh)
(while other-headers
(unless (member-ignore-case (car (car other-headers))
'("in-reply-to" "cc" "body"))
(insert (car (car other-headers)) ": "
(cdr (car other-headers))
(if use-hard-newlines hard-newline "\n")))
(setq other-headers (cdr other-headers)))
(when body
(forward-line 1)
(insert body))
t)))
(defun compose-mail (&optional to subject other-headers continue
switch-function yank-action send-actions)
switch-function yank-action send-actions
return-action)
"Start composing a mail message to send.
This uses the user's chosen mail composition package
as selected with the variable `mail-user-agent'.
@ -5778,7 +5746,12 @@ FUNCTION to ARGS, to insert the raw text of the original message.
original text has been inserted in this way.)
SEND-ACTIONS is a list of actions to call when the message is sent.
Each action has the form (FUNCTION . ARGS)."
Each action has the form (FUNCTION . ARGS).
RETURN-ACTION, if non-nil, is an action for returning to the
caller. It has the form (FUNCTION . ARGS). The function is
called after the mail has been sent or put aside, and the mail
buffer buried."
(interactive
(list nil nil nil current-prefix-arg))
@ -5808,25 +5781,27 @@ To disable this warning, set `compose-mail-user-agent-warnings' to nil."
warn-vars " "))))))
(let ((function (get mail-user-agent 'composefunc)))
(funcall function to subject other-headers continue
switch-function yank-action send-actions)))
(funcall function to subject other-headers continue switch-function
yank-action send-actions return-action)))
(defun compose-mail-other-window (&optional to subject other-headers continue
yank-action send-actions)
yank-action send-actions
return-action)
"Like \\[compose-mail], but edit the outgoing message in another window."
(interactive
(list nil nil nil current-prefix-arg))
(interactive (list nil nil nil current-prefix-arg))
(compose-mail to subject other-headers continue
'switch-to-buffer-other-window yank-action send-actions))
'switch-to-buffer-other-window yank-action send-actions
return-action))
(defun compose-mail-other-frame (&optional to subject other-headers continue
yank-action send-actions)
yank-action send-actions
return-action)
"Like \\[compose-mail], but edit the outgoing message in another frame."
(interactive
(list nil nil nil current-prefix-arg))
(interactive (list nil nil nil current-prefix-arg))
(compose-mail to subject other-headers continue
'switch-to-buffer-other-frame yank-action send-actions))
'switch-to-buffer-other-frame yank-action send-actions
return-action))
(defvar set-variable-value-history nil
"History of values entered with `set-variable'.

View File

@ -7519,7 +7519,7 @@ imagemagick_load_image (/* Pointer to emacs frame structure. */
image to see how many sub-images it contains. Pinging is faster
than loading the image to find out things about it. */
/* MagickWandGenesis() initializes the imagemagick library. */
/* `MagickWandGenesis' initializes the imagemagick environment. */
MagickWandGenesis ();
image = image_spec_value (img->spec, QCindex, NULL);
ino = INTEGERP (image) ? XFASTINT (image) : 0;
@ -7807,6 +7807,7 @@ imagemagick_load_image (/* Pointer to emacs frame structure. */
/* Final cleanup. image_wand should be the only resource left. */
DestroyMagickWand (image_wand);
/* `MagickWandTerminus' terminates the imagemagick environment. */
MagickWandTerminus ();
return 1;

View File

@ -1,3 +1,11 @@
2011-01-13 Christian Ohler <ohler@gnu.org>
* automated: New directory for automated tests.
* automated/ert-tests.el, automated/ert-x-tests.el: New files.
* automated/Makefile.in: New file.
2010-11-11 Stefan Monnier <monnier@iro.umontreal.ca>
* indent/modula2.mod: New file.

158
test/automated/Makefile.in Normal file
View File

@ -0,0 +1,158 @@
# Maintenance productions for the automated test directory
# Copyright (C) 2010, 2011 Free Software Foundation, Inc.
# This file is part of GNU Emacs.
# GNU Emacs is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# GNU Emacs is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
SHELL = /bin/sh
srcdir = @srcdir@
top_srcdir = @top_srcdir@
abs_top_builddir = @abs_top_builddir@
test = $(srcdir)
VPATH = $(srcdir)
lispsrc = $(top_srcdir)/lisp
lisp = ${abs_top_builddir}/lisp
# You can specify a different executable on the make command line,
# e.g. "make EMACS=../src/emacs ...".
# We sometimes change directory before running Emacs (typically when
# building out-of-tree, we chdir to the source directory), so we need
# to use an absolute file name.
EMACS = ${abs_top_builddir}/src/emacs
# Command line flags for Emacs.
EMACSOPT = -batch --no-site-file --no-site-lisp
# Extra flags to pass to the byte compiler
BYTE_COMPILE_EXTRA_FLAGS =
# For example to not display the undefined function warnings you can use this:
# BYTE_COMPILE_EXTRA_FLAGS = --eval '(setq byte-compile-warnings (quote (not unresolved)))'
# The example above is just for developers, it should not be used by default.
# The actual Emacs command run in the targets below.
emacs = EMACSLOADPATH=$(lispsrc):$(test) LC_ALL=C $(EMACS) $(EMACSOPT)
# Common command to find subdirectories
setwins=subdirs=`(find . -type d -print)`; \
for file in $$subdirs; do \
case $$file in */.* | */.*/* | */=* ) ;; \
*) wins="$$wins $$file" ;; \
esac; \
done
all: test
doit:
# Files MUST be compiled one by one. If we compile several files in a
# row (i.e., in the same instance of Emacs) we can't make sure that
# the compilation environment is clean. We also set the load-path of
# the Emacs used for compilation to the current directory and its
# subdirectories, to make sure require's and load's in the files being
# compiled find the right files.
.SUFFIXES: .elc .el
# An old-fashioned suffix rule, which, according to the GNU Make manual,
# cannot have prerequisites.
.el.elc:
@echo Compiling $<
@$(emacs) $(BYTE_COMPILE_EXTRA_FLAGS) -f batch-byte-compile $<
.PHONY: lisp-compile compile-main compile compile-always
lisp-compile:
cd $(lisp); $(MAKE) $(MFLAGS) compile EMACS=$(EMACS)
# In `compile-main' we could directly do
# ... | xargs $(MAKE) $(MFLAGS) EMACS="$(EMACS)"
# and it works, but it generates a lot of messages like
# make[2]: « gnus/gnus-mlspl.elc » is up to date.
# so instead, we use "xargs echo" to split the list of file into manageable
# chunks and then use an intermediate `compile-targets' target so the
# actual targets (the .elc files) are not mentioned as targets on the
# make command line.
.PHONY: compile-targets
# TARGETS is set dynamically in the recursive call from `compile-main'.
compile-targets: $(TARGETS)
# Compile all the Elisp files that need it. Beware: it approximates
# `no-byte-compile', so watch out for false-positives!
compile-main: compile-clean lisp-compile
@(cd $(test); $(setwins); \
els=`echo "$$wins " | sed -e 's|/\./|/|g' -e 's|/\. | |g' -e 's| |/*.el |g'`; \
for el in $$els; do \
test -f $$el || continue; \
test ! -f $${el}c && GREP_OPTIONS= grep '^;.*no-byte-compile: t' $$el > /dev/null && continue; \
echo "$${el}c"; \
done | xargs echo) | \
while read chunk; do \
$(MAKE) $(MFLAGS) compile-targets EMACS="$(EMACS)" TARGETS="$$chunk"; \
done
.PHONY: compile-clean
# Erase left-over .elc files that do not have a corresponding .el file.
compile-clean:
@cd $(test); $(setwins); \
elcs=`echo "$$wins " | sed -e 's|/\./|/|g' -e 's|/\. | |g' -e 's| |/*.elc |g'`; \
for el in $$(echo $$elcs | sed -e 's/\.elc/\.el/g'); do \
if test -f "$$el" -o \! -f "$${el}c"; then :; else \
echo rm "$${el}c"; \
rm "$${el}c"; \
fi \
done
# Compile all Lisp files, but don't recompile those that are up to
# date. Some .el files don't get compiled because they set the
# local variable no-byte-compile.
# Calling make recursively because suffix rule cannot have prerequisites.
# Explicitly pass EMACS (sometimes ../src/bootstrap-emacs) to those
# sub-makes that run rules that use it, for the sake of some non-GNU makes.
compile: $(LOADDEFS) autoloads compile-first
$(MAKE) $(MFLAGS) compile-main EMACS=$(EMACS)
# Compile all Lisp files. This is like `compile' but compiles files
# unconditionally. Some files don't actually get compiled because they
# set the local variable no-byte-compile.
compile-always: doit
cd $(test); rm -f *.elc */*.elc */*/*.elc */*/*/*.elc
$(MAKE) $(MFLAGS) compile EMACS=$(EMACS)
bootstrap-clean:
cd $(test); rm -f *.elc */*.elc */*/*.elc */*/*/*.elc
distclean:
-rm -f ./Makefile
maintainer-clean: distclean bootstrap-clean
check: compile-main
@(cd $(test); $(setwins); \
pattern=`echo "$$wins " | sed -e 's|/\./|/|g' -e 's|/\. | |g' -e 's| |/*.el |g'`; \
for el in $$pattern; do \
test -f $$el || continue; \
args="$$args -l $$el"; \
els="$$els $$el"; \
done; \
echo Testing $$els; \
$(emacs) $$args -f ert-run-tests-batch-and-exit)
# Makefile ends here.

949
test/automated/ert-tests.el Normal file
View File

@ -0,0 +1,949 @@
;;; ert-tests.el --- ERT's self-tests
;; Copyright (C) 2007, 2008, 2010, 2011 Free Software Foundation, Inc.
;; Author: Christian Ohler <ohler@gnu.org>
;; This file is part of GNU Emacs.
;; This program is free software: you can redistribute it and/or
;; modify it under the terms of the GNU General Public License as
;; published by the Free Software Foundation, either version 3 of the
;; License, or (at your option) any later version.
;;
;; This program is distributed in the hope that it will be useful, but
;; WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
;; General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see `http://www.gnu.org/licenses/'.
;;; Commentary:
;; This file is part of ERT, the Emacs Lisp Regression Testing tool.
;; See ert.el or the texinfo manual for more details.
;;; Code:
(eval-when-compile
(require 'cl))
(require 'ert)
;;; Self-test that doesn't rely on ERT, for bootstrapping.
;; This is used to test that bodies actually run.
(defvar ert--test-body-was-run)
(ert-deftest ert-test-body-runs ()
(setq ert--test-body-was-run t))
(defun ert-self-test ()
"Run ERT's self-tests and make sure they actually ran."
(let ((window-configuration (current-window-configuration)))
(let ((ert--test-body-was-run nil))
;; The buffer name chosen here should not compete with the default
;; results buffer name for completion in `switch-to-buffer'.
(let ((stats (ert-run-tests-interactively "^ert-" " *ert self-tests*")))
(assert ert--test-body-was-run)
(if (zerop (ert-stats-completed-unexpected stats))
;; Hide results window only when everything went well.
(set-window-configuration window-configuration)
(error "ERT self-test failed"))))))
(defun ert-self-test-and-exit ()
"Run ERT's self-tests and exit Emacs.
The exit code will be zero if the tests passed, nonzero if they
failed or if there was a problem."
(unwind-protect
(progn
(ert-self-test)
(kill-emacs 0))
(unwind-protect
(progn
(message "Error running tests")
(backtrace))
(kill-emacs 1))))
;;; Further tests are defined using ERT.
(ert-deftest ert-test-nested-test-body-runs ()
"Test that nested test bodies run."
(lexical-let ((was-run nil))
(let ((test (make-ert-test :body (lambda ()
(setq was-run t)))))
(assert (not was-run))
(ert-run-test test)
(assert was-run))))
;;; Test that pass/fail works.
(ert-deftest ert-test-pass ()
(let ((test (make-ert-test :body (lambda ()))))
(let ((result (ert-run-test test)))
(assert (ert-test-passed-p result)))))
(ert-deftest ert-test-fail ()
(let ((test (make-ert-test :body (lambda () (ert-fail "failure message")))))
(let ((result (let ((ert-debug-on-error nil))
(ert-run-test test))))
(assert (ert-test-failed-p result) t)
(assert (equal (ert-test-result-with-condition-condition result)
'(ert-test-failed "failure message"))
t))))
(ert-deftest ert-test-fail-debug-with-condition-case ()
(let ((test (make-ert-test :body (lambda () (ert-fail "failure message")))))
(condition-case condition
(progn
(let ((ert-debug-on-error t))
(ert-run-test test))
(assert nil))
((error)
(assert (equal condition '(ert-test-failed "failure message")) t)))))
(ert-deftest ert-test-fail-debug-with-debugger-1 ()
(let ((test (make-ert-test :body (lambda () (ert-fail "failure message")))))
(let ((debugger (lambda (&rest debugger-args)
(assert nil))))
(let ((ert-debug-on-error nil))
(ert-run-test test)))))
(ert-deftest ert-test-fail-debug-with-debugger-2 ()
(let ((test (make-ert-test :body (lambda () (ert-fail "failure message")))))
(block nil
(let ((debugger (lambda (&rest debugger-args)
(return-from nil nil))))
(let ((ert-debug-on-error t))
(ert-run-test test))
(assert nil)))))
(ert-deftest ert-test-fail-debug-nested-with-debugger ()
(let ((test (make-ert-test :body (lambda ()
(let ((ert-debug-on-error t))
(ert-fail "failure message"))))))
(let ((debugger (lambda (&rest debugger-args)
(assert nil nil "Assertion a"))))
(let ((ert-debug-on-error nil))
(ert-run-test test))))
(let ((test (make-ert-test :body (lambda ()
(let ((ert-debug-on-error nil))
(ert-fail "failure message"))))))
(block nil
(let ((debugger (lambda (&rest debugger-args)
(return-from nil nil))))
(let ((ert-debug-on-error t))
(ert-run-test test))
(assert nil nil "Assertion b")))))
(ert-deftest ert-test-error ()
(let ((test (make-ert-test :body (lambda () (error "Error message")))))
(let ((result (let ((ert-debug-on-error nil))
(ert-run-test test))))
(assert (ert-test-failed-p result) t)
(assert (equal (ert-test-result-with-condition-condition result)
'(error "Error message"))
t))))
(ert-deftest ert-test-error-debug ()
(let ((test (make-ert-test :body (lambda () (error "Error message")))))
(condition-case condition
(progn
(let ((ert-debug-on-error t))
(ert-run-test test))
(assert nil))
((error)
(assert (equal condition '(error "Error message")) t)))))
;;; Test that `should' works.
(ert-deftest ert-test-should ()
(let ((test (make-ert-test :body (lambda () (should nil)))))
(let ((result (let ((ert-debug-on-error nil))
(ert-run-test test))))
(assert (ert-test-failed-p result) t)
(assert (equal (ert-test-result-with-condition-condition result)
'(ert-test-failed ((should nil) :form nil :value nil)))
t)))
(let ((test (make-ert-test :body (lambda () (should t)))))
(let ((result (ert-run-test test)))
(assert (ert-test-passed-p result) t))))
(ert-deftest ert-test-should-value ()
(should (eql (should 'foo) 'foo))
(should (eql (should 'bar) 'bar)))
(ert-deftest ert-test-should-not ()
(let ((test (make-ert-test :body (lambda () (should-not t)))))
(let ((result (let ((ert-debug-on-error nil))
(ert-run-test test))))
(assert (ert-test-failed-p result) t)
(assert (equal (ert-test-result-with-condition-condition result)
'(ert-test-failed ((should-not t) :form t :value t)))
t)))
(let ((test (make-ert-test :body (lambda () (should-not nil)))))
(let ((result (ert-run-test test)))
(assert (ert-test-passed-p result)))))
(ert-deftest ert-test-should-with-macrolet ()
(let ((test (make-ert-test :body (lambda ()
(macrolet ((foo () `(progn t nil)))
(should (foo)))))))
(let ((result (let ((ert-debug-on-error nil))
(ert-run-test test))))
(should (ert-test-failed-p result))
(should (equal
(ert-test-result-with-condition-condition result)
'(ert-test-failed ((should (foo))
:form (progn t nil)
:value nil)))))))
(ert-deftest ert-test-should-error ()
;; No error.
(let ((test (make-ert-test :body (lambda () (should-error (progn))))))
(let ((result (let ((ert-debug-on-error nil))
(ert-run-test test))))
(should (ert-test-failed-p result))
(should (equal (ert-test-result-with-condition-condition result)
'(ert-test-failed
((should-error (progn))
:form (progn)
:value nil
:fail-reason "did not signal an error"))))))
;; A simple error.
(should (equal (should-error (error "Foo"))
'(error "Foo")))
;; Error of unexpected type.
(let ((test (make-ert-test :body (lambda ()
(should-error (error "Foo")
:type 'singularity-error)))))
(let ((result (ert-run-test test)))
(should (ert-test-failed-p result))
(should (equal
(ert-test-result-with-condition-condition result)
'(ert-test-failed
((should-error (error "Foo") :type 'singularity-error)
:form (error "Foo")
:condition (error "Foo")
:fail-reason
"the error signalled did not have the expected type"))))))
;; Error of the expected type.
(let* ((error nil)
(test (make-ert-test
:body (lambda ()
(setq error
(should-error (signal 'singularity-error nil)
:type 'singularity-error))))))
(let ((result (ert-run-test test)))
(should (ert-test-passed-p result))
(should (equal error '(singularity-error))))))
(ert-deftest ert-test-should-error-subtypes ()
(should-error (signal 'singularity-error nil)
:type 'singularity-error
:exclude-subtypes t)
(let ((test (make-ert-test
:body (lambda ()
(should-error (signal 'arith-error nil)
:type 'singularity-error)))))
(let ((result (ert-run-test test)))
(should (ert-test-failed-p result))
(should (equal
(ert-test-result-with-condition-condition result)
'(ert-test-failed
((should-error (signal 'arith-error nil)
:type 'singularity-error)
:form (signal arith-error nil)
:condition (arith-error)
:fail-reason
"the error signalled did not have the expected type"))))))
(let ((test (make-ert-test
:body (lambda ()
(should-error (signal 'arith-error nil)
:type 'singularity-error
:exclude-subtypes t)))))
(let ((result (ert-run-test test)))
(should (ert-test-failed-p result))
(should (equal
(ert-test-result-with-condition-condition result)
'(ert-test-failed
((should-error (signal 'arith-error nil)
:type 'singularity-error
:exclude-subtypes t)
:form (signal arith-error nil)
:condition (arith-error)
:fail-reason
"the error signalled did not have the expected type"))))))
(let ((test (make-ert-test
:body (lambda ()
(should-error (signal 'singularity-error nil)
:type 'arith-error
:exclude-subtypes t)))))
(let ((result (ert-run-test test)))
(should (ert-test-failed-p result))
(should (equal
(ert-test-result-with-condition-condition result)
'(ert-test-failed
((should-error (signal 'singularity-error nil)
:type 'arith-error
:exclude-subtypes t)
:form (signal singularity-error nil)
:condition (singularity-error)
:fail-reason
"the error signalled was a subtype of the expected type")))))
))
(defmacro ert--test-my-list (&rest args)
"Don't use this. Instead, call `list' with ARGS, it does the same thing.
This macro is used to test if macroexpansion in `should' works."
`(list ,@args))
(ert-deftest ert-test-should-failure-debugging ()
"Test that `should' errors contain the information we expect them to."
(loop for (body expected-condition) in
`((,(lambda () (let ((x nil)) (should x)))
(ert-test-failed ((should x) :form x :value nil)))
(,(lambda () (let ((x t)) (should-not x)))
(ert-test-failed ((should-not x) :form x :value t)))
(,(lambda () (let ((x t)) (should (not x))))
(ert-test-failed ((should (not x)) :form (not t) :value nil)))
(,(lambda () (let ((x nil)) (should-not (not x))))
(ert-test-failed ((should-not (not x)) :form (not nil) :value t)))
(,(lambda () (let ((x t) (y nil)) (should-not
(ert--test-my-list x y))))
(ert-test-failed
((should-not (ert--test-my-list x y))
:form (list t nil)
:value (t nil))))
(,(lambda () (let ((x t)) (should (error "Foo"))))
(error "Foo")))
do
(let ((test (make-ert-test :body body)))
(condition-case actual-condition
(progn
(let ((ert-debug-on-error t))
(ert-run-test test))
(assert nil))
((error)
(should (equal actual-condition expected-condition)))))))
(ert-deftest ert-test-deftest ()
(should (equal (macroexpand '(ert-deftest abc () "foo" :tags '(bar)))
'(progn
(ert-set-test 'abc
(make-ert-test :name 'abc
:documentation "foo"
:tags '(bar)
:body (lambda ())))
(push '(ert-deftest . abc) current-load-list)
'abc)))
(should (equal (macroexpand '(ert-deftest def ()
:expected-result ':passed))
'(progn
(ert-set-test 'def
(make-ert-test :name 'def
:expected-result-type ':passed
:body (lambda ())))
(push '(ert-deftest . def) current-load-list)
'def)))
;; :documentation keyword is forbidden
(should-error (macroexpand '(ert-deftest ghi ()
:documentation "foo"))))
(ert-deftest ert-test-record-backtrace ()
(let ((test (make-ert-test :body (lambda () (ert-fail "foo")))))
(let ((result (ert-run-test test)))
(should (ert-test-failed-p result))
(with-temp-buffer
(ert--print-backtrace (ert-test-failed-backtrace result))
(goto-char (point-min))
(end-of-line)
(let ((first-line (buffer-substring-no-properties (point-min) (point))))
(should (equal first-line " signal(ert-test-failed (\"foo\"))")))))))
(ert-deftest ert-test-messages ()
:tags '(:causes-redisplay)
(let* ((message-string "Test message")
(messages-buffer (get-buffer-create "*Messages*"))
(test (make-ert-test :body (lambda () (message "%s" message-string)))))
(with-current-buffer messages-buffer
(let ((result (ert-run-test test)))
(should (equal (concat message-string "\n")
(ert-test-result-messages result)))))))
(ert-deftest ert-test-running-tests ()
(let ((outer-test (ert-get-test 'ert-test-running-tests)))
(should (equal (ert-running-test) outer-test))
(let (test1 test2 test3)
(setq test1 (make-ert-test
:name "1"
:body (lambda ()
(should (equal (ert-running-test) outer-test))
(should (equal ert--running-tests
(list test1 test2 test3
outer-test)))))
test2 (make-ert-test
:name "2"
:body (lambda ()
(should (equal (ert-running-test) outer-test))
(should (equal ert--running-tests
(list test3 test2 outer-test)))
(ert-run-test test1)))
test3 (make-ert-test
:name "3"
:body (lambda ()
(should (equal (ert-running-test) outer-test))
(should (equal ert--running-tests
(list test3 outer-test)))
(ert-run-test test2))))
(should (ert-test-passed-p (ert-run-test test3))))))
(ert-deftest ert-test-test-result-expected-p ()
"Test `ert-test-result-expected-p' and (implicitly) `ert-test-result-type-p'."
;; passing test
(let ((test (make-ert-test :body (lambda ()))))
(should (ert-test-result-expected-p test (ert-run-test test))))
;; unexpected failure
(let ((test (make-ert-test :body (lambda () (ert-fail "failed")))))
(should-not (ert-test-result-expected-p test (ert-run-test test))))
;; expected failure
(let ((test (make-ert-test :body (lambda () (ert-fail "failed"))
:expected-result-type ':failed)))
(should (ert-test-result-expected-p test (ert-run-test test))))
;; `not' expected type
(let ((test (make-ert-test :body (lambda ())
:expected-result-type '(not :failed))))
(should (ert-test-result-expected-p test (ert-run-test test))))
(let ((test (make-ert-test :body (lambda ())
:expected-result-type '(not :passed))))
(should-not (ert-test-result-expected-p test (ert-run-test test))))
;; `and' expected type
(let ((test (make-ert-test :body (lambda ())
:expected-result-type '(and :passed :failed))))
(should-not (ert-test-result-expected-p test (ert-run-test test))))
(let ((test (make-ert-test :body (lambda ())
:expected-result-type '(and :passed
(not :failed)))))
(should (ert-test-result-expected-p test (ert-run-test test))))
;; `or' expected type
(let ((test (make-ert-test :body (lambda ())
:expected-result-type '(or (and :passed :failed)
:passed))))
(should (ert-test-result-expected-p test (ert-run-test test))))
(let ((test (make-ert-test :body (lambda ())
:expected-result-type '(or (and :passed :failed)
nil (not t)))))
(should-not (ert-test-result-expected-p test (ert-run-test test)))))
;;; Test `ert-select-tests'.
(ert-deftest ert-test-select-regexp ()
(should (equal (ert-select-tests "^ert-test-select-regexp$" t)
(list (ert-get-test 'ert-test-select-regexp)))))
(ert-deftest ert-test-test-boundp ()
(should (ert-test-boundp 'ert-test-test-boundp))
(should-not (ert-test-boundp (make-symbol "ert-not-a-test"))))
(ert-deftest ert-test-select-member ()
(should (equal (ert-select-tests '(member ert-test-select-member) t)
(list (ert-get-test 'ert-test-select-member)))))
(ert-deftest ert-test-select-test ()
(should (equal (ert-select-tests (ert-get-test 'ert-test-select-test) t)
(list (ert-get-test 'ert-test-select-test)))))
(ert-deftest ert-test-select-symbol ()
(should (equal (ert-select-tests 'ert-test-select-symbol t)
(list (ert-get-test 'ert-test-select-symbol)))))
(ert-deftest ert-test-select-and ()
(let ((test (make-ert-test
:name nil
:body nil
:most-recent-result (make-ert-test-failed
:condition nil
:backtrace nil
:infos nil))))
(should (equal (ert-select-tests `(and (member ,test) :failed) t)
(list test)))))
(ert-deftest ert-test-select-tag ()
(let ((test (make-ert-test
:name nil
:body nil
:tags '(a b))))
(should (equal (ert-select-tests `(tag a) (list test)) (list test)))
(should (equal (ert-select-tests `(tag b) (list test)) (list test)))
(should (equal (ert-select-tests `(tag c) (list test)) '()))))
;;; Tests for utility functions.
(ert-deftest ert-test-proper-list-p ()
(should (ert--proper-list-p '()))
(should (ert--proper-list-p '(1)))
(should (ert--proper-list-p '(1 2)))
(should (ert--proper-list-p '(1 2 3)))
(should (ert--proper-list-p '(1 2 3 4)))
(should (not (ert--proper-list-p 'a)))
(should (not (ert--proper-list-p '(1 . a))))
(should (not (ert--proper-list-p '(1 2 . a))))
(should (not (ert--proper-list-p '(1 2 3 . a))))
(should (not (ert--proper-list-p '(1 2 3 4 . a))))
(let ((a (list 1)))
(setf (cdr (last a)) a)
(should (not (ert--proper-list-p a))))
(let ((a (list 1 2)))
(setf (cdr (last a)) a)
(should (not (ert--proper-list-p a))))
(let ((a (list 1 2 3)))
(setf (cdr (last a)) a)
(should (not (ert--proper-list-p a))))
(let ((a (list 1 2 3 4)))
(setf (cdr (last a)) a)
(should (not (ert--proper-list-p a))))
(let ((a (list 1 2)))
(setf (cdr (last a)) (cdr a))
(should (not (ert--proper-list-p a))))
(let ((a (list 1 2 3)))
(setf (cdr (last a)) (cdr a))
(should (not (ert--proper-list-p a))))
(let ((a (list 1 2 3 4)))
(setf (cdr (last a)) (cdr a))
(should (not (ert--proper-list-p a))))
(let ((a (list 1 2 3)))
(setf (cdr (last a)) (cddr a))
(should (not (ert--proper-list-p a))))
(let ((a (list 1 2 3 4)))
(setf (cdr (last a)) (cddr a))
(should (not (ert--proper-list-p a))))
(let ((a (list 1 2 3 4)))
(setf (cdr (last a)) (cdddr a))
(should (not (ert--proper-list-p a)))))
(ert-deftest ert-test-parse-keys-and-body ()
(should (equal (ert--parse-keys-and-body '(foo)) '(nil (foo))))
(should (equal (ert--parse-keys-and-body '(:bar foo)) '((:bar foo) nil)))
(should (equal (ert--parse-keys-and-body '(:bar foo a (b)))
'((:bar foo) (a (b)))))
(should (equal (ert--parse-keys-and-body '(:bar foo :a (b)))
'((:bar foo :a (b)) nil)))
(should (equal (ert--parse-keys-and-body '(bar foo :a (b)))
'(nil (bar foo :a (b)))))
(should-error (ert--parse-keys-and-body '(:bar foo :a))))
(ert-deftest ert-test-run-tests-interactively ()
:tags '(:causes-redisplay)
(let ((passing-test (make-ert-test :name 'passing-test
:body (lambda () (ert-pass))))
(failing-test (make-ert-test :name 'failing-test
:body (lambda () (ert-fail
"failure message")))))
(let ((ert-debug-on-error nil))
(let* ((buffer-name (generate-new-buffer-name " *ert-test-run-tests*"))
(messages nil)
(mock-message-fn
(lambda (format-string &rest args)
(push (apply #'format format-string args) messages))))
(save-window-excursion
(unwind-protect
(let ((case-fold-search nil))
(ert-run-tests-interactively
`(member ,passing-test ,failing-test) buffer-name
mock-message-fn)
(should (equal messages `(,(concat
"Ran 2 tests, 1 results were "
"as expected, 1 unexpected"))))
(with-current-buffer buffer-name
(goto-char (point-min))
(should (equal
(buffer-substring (point-min)
(save-excursion
(forward-line 4)
(point)))
(concat
"Selector: (member <passing-test> <failing-test>)\n"
"Passed: 1\n"
"Failed: 1 (1 unexpected)\n"
"Total: 2/2\n")))))
(when (get-buffer buffer-name)
(kill-buffer buffer-name))))))))
(ert-deftest ert-test-special-operator-p ()
(should (ert--special-operator-p 'if))
(should-not (ert--special-operator-p 'car))
(should-not (ert--special-operator-p 'ert--special-operator-p))
(let ((b (ert--gensym)))
(should-not (ert--special-operator-p b))
(fset b 'if)
(should (ert--special-operator-p b))))
(ert-deftest ert-test-list-of-should-forms ()
(let ((test (make-ert-test :body (lambda ()
(should t)
(should (null '()))
(should nil)
(should t)))))
(let ((result (let ((ert-debug-on-error nil))
(ert-run-test test))))
(should (equal (ert-test-result-should-forms result)
'(((should t) :form t :value t)
((should (null '())) :form (null nil) :value t)
((should nil) :form nil :value nil)))))))
(ert-deftest ert-test-list-of-should-forms-observers-should-not-stack ()
(let ((test (make-ert-test
:body (lambda ()
(let ((test2 (make-ert-test
:body (lambda ()
(should t)))))
(let ((result (ert-run-test test2)))
(should (ert-test-passed-p result))))))))
(let ((result (let ((ert-debug-on-error nil))
(ert-run-test test))))
(should (ert-test-passed-p result))
(should (eql (length (ert-test-result-should-forms result))
1)))))
(ert-deftest ert-test-list-of-should-forms-no-deep-copy ()
(let ((test (make-ert-test :body (lambda ()
(let ((obj (list 'a)))
(should (equal obj '(a)))
(setf (car obj) 'b)
(should (equal obj '(b))))))))
(let ((result (let ((ert-debug-on-error nil))
(ert-run-test test))))
(should (ert-test-passed-p result))
(should (equal (ert-test-result-should-forms result)
'(((should (equal obj '(a))) :form (equal (b) (a)) :value t
:explanation nil)
((should (equal obj '(b))) :form (equal (b) (b)) :value t
:explanation nil)
))))))
(ert-deftest ert-test-remprop ()
(let ((x (ert--gensym)))
(should (equal (symbol-plist x) '()))
;; Remove nonexistent property on empty plist.
(ert--remprop x 'b)
(should (equal (symbol-plist x) '()))
(put x 'a 1)
(should (equal (symbol-plist x) '(a 1)))
;; Remove nonexistent property on nonempty plist.
(ert--remprop x 'b)
(should (equal (symbol-plist x) '(a 1)))
(put x 'b 2)
(put x 'c 3)
(put x 'd 4)
(should (equal (symbol-plist x) '(a 1 b 2 c 3 d 4)))
;; Remove property that is neither first nor last.
(ert--remprop x 'c)
(should (equal (symbol-plist x) '(a 1 b 2 d 4)))
;; Remove last property from a plist of length >1.
(ert--remprop x 'd)
(should (equal (symbol-plist x) '(a 1 b 2)))
;; Remove first property from a plist of length >1.
(ert--remprop x 'a)
(should (equal (symbol-plist x) '(b 2)))
;; Remove property when there is only one.
(ert--remprop x 'b)
(should (equal (symbol-plist x) '()))))
(ert-deftest ert-test-remove-if-not ()
(let ((list (list 'a 'b 'c 'd))
(i 0))
(let ((result (ert--remove-if-not (lambda (x)
(should (eql x (nth i list)))
(incf i)
(member i '(2 3)))
list)))
(should (equal i 4))
(should (equal result '(b c)))
(should (equal list '(a b c d)))))
(should (equal '()
(ert--remove-if-not (lambda (x) (should nil)) '()))))
(ert-deftest ert-test-remove* ()
(let ((list (list 'a 'b 'c 'd))
(key-index 0)
(test-index 0))
(let ((result
(ert--remove* 'foo list
:key (lambda (x)
(should (eql x (nth key-index list)))
(prog1
(list key-index x)
(incf key-index)))
:test
(lambda (a b)
(should (eql a 'foo))
(should (equal b (list test-index
(nth test-index list))))
(incf test-index)
(member test-index '(2 3))))))
(should (equal key-index 4))
(should (equal test-index 4))
(should (equal result '(a d)))
(should (equal list '(a b c d)))))
(let ((x (cons nil nil))
(y (cons nil nil)))
(should (equal (ert--remove* x (list x y))
;; or (list x), since we use `equal' -- the
;; important thing is that only one element got
;; removed, this proves that the default test is
;; `eql', not `equal'
(list y)))))
(ert-deftest ert-test-set-functions ()
(let ((c1 (cons nil nil))
(c2 (cons nil nil))
(sym (make-symbol "a")))
(let ((e '())
(a (list 'a 'b sym nil "" "x" c1 c2))
(b (list c1 'y 'b sym 'x)))
(should (equal (ert--set-difference e e) e))
(should (equal (ert--set-difference a e) a))
(should (equal (ert--set-difference e a) e))
(should (equal (ert--set-difference a a) e))
(should (equal (ert--set-difference b e) b))
(should (equal (ert--set-difference e b) e))
(should (equal (ert--set-difference b b) e))
(should (equal (ert--set-difference a b) (list 'a nil "" "x" c2)))
(should (equal (ert--set-difference b a) (list 'y 'x)))
;; We aren't testing whether this is really using `eq' rather than `eql'.
(should (equal (ert--set-difference-eq e e) e))
(should (equal (ert--set-difference-eq a e) a))
(should (equal (ert--set-difference-eq e a) e))
(should (equal (ert--set-difference-eq a a) e))
(should (equal (ert--set-difference-eq b e) b))
(should (equal (ert--set-difference-eq e b) e))
(should (equal (ert--set-difference-eq b b) e))
(should (equal (ert--set-difference-eq a b) (list 'a nil "" "x" c2)))
(should (equal (ert--set-difference-eq b a) (list 'y 'x)))
(should (equal (ert--union e e) e))
(should (equal (ert--union a e) a))
(should (equal (ert--union e a) a))
(should (equal (ert--union a a) a))
(should (equal (ert--union b e) b))
(should (equal (ert--union e b) b))
(should (equal (ert--union b b) b))
(should (equal (ert--union a b) (list 'a 'b sym nil "" "x" c1 c2 'y 'x)))
(should (equal (ert--union b a) (list c1 'y 'b sym 'x 'a nil "" "x" c2)))
(should (equal (ert--intersection e e) e))
(should (equal (ert--intersection a e) e))
(should (equal (ert--intersection e a) e))
(should (equal (ert--intersection a a) a))
(should (equal (ert--intersection b e) e))
(should (equal (ert--intersection e b) e))
(should (equal (ert--intersection b b) b))
(should (equal (ert--intersection a b) (list 'b sym c1)))
(should (equal (ert--intersection b a) (list c1 'b sym))))))
(ert-deftest ert-test-gensym ()
;; Since the expansion of `should' calls `ert--gensym' and thus has a
;; side-effect on `ert--gensym-counter', we have to make sure all
;; macros in our test body are expanded before we rebind
;; `ert--gensym-counter' and run the body. Otherwise, the test would
;; fail if run interpreted.
(let ((body (byte-compile
'(lambda ()
(should (equal (symbol-name (ert--gensym)) "G0"))
(should (equal (symbol-name (ert--gensym)) "G1"))
(should (equal (symbol-name (ert--gensym)) "G2"))
(should (equal (symbol-name (ert--gensym "foo")) "foo3"))
(should (equal (symbol-name (ert--gensym "bar")) "bar4"))
(should (equal ert--gensym-counter 5))))))
(let ((ert--gensym-counter 0))
(funcall body))))
(ert-deftest ert-test-coerce-to-vector ()
(let* ((a (vector))
(b (vector 1 a 3))
(c (list))
(d (list b a)))
(should (eql (ert--coerce-to-vector a) a))
(should (eql (ert--coerce-to-vector b) b))
(should (equal (ert--coerce-to-vector c) (vector)))
(should (equal (ert--coerce-to-vector d) (vector b a)))))
(ert-deftest ert-test-string-position ()
(should (eql (ert--string-position ?x "") nil))
(should (eql (ert--string-position ?a "abc") 0))
(should (eql (ert--string-position ?b "abc") 1))
(should (eql (ert--string-position ?c "abc") 2))
(should (eql (ert--string-position ?d "abc") nil))
(should (eql (ert--string-position ?A "abc") nil)))
(ert-deftest ert-test-mismatch ()
(should (eql (ert--mismatch "" "") nil))
(should (eql (ert--mismatch "" "a") 0))
(should (eql (ert--mismatch "a" "a") nil))
(should (eql (ert--mismatch "ab" "a") 1))
(should (eql (ert--mismatch "Aa" "aA") 0))
(should (eql (ert--mismatch '(a b c) '(a b d)) 2)))
(ert-deftest ert-test-string-first-line ()
(should (equal (ert--string-first-line "") ""))
(should (equal (ert--string-first-line "abc") "abc"))
(should (equal (ert--string-first-line "abc\n") "abc"))
(should (equal (ert--string-first-line "foo\nbar") "foo"))
(should (equal (ert--string-first-line " foo\nbar\nbaz\n") " foo")))
(ert-deftest ert-test-explain-not-equal ()
(should (equal (ert--explain-not-equal nil 'foo)
'(different-atoms nil foo)))
(should (equal (ert--explain-not-equal '(a a) '(a b))
'(list-elt 1 (different-atoms a b))))
(should (equal (ert--explain-not-equal '(1 48) '(1 49))
'(list-elt 1 (different-atoms (48 "#x30" "?0")
(49 "#x31" "?1")))))
(should (equal (ert--explain-not-equal 'nil '(a))
'(different-types nil (a))))
(should (equal (ert--explain-not-equal '(a b c) '(a b c d))
'(proper-lists-of-different-length 3 4 (a b c) (a b c d)
first-mismatch-at 3)))
(let ((sym (make-symbol "a")))
(should (equal (ert--explain-not-equal 'a sym)
`(different-symbols-with-the-same-name a ,sym)))))
(ert-deftest ert-test-explain-not-equal-improper-list ()
(should (equal (ert--explain-not-equal '(a . b) '(a . c))
'(cdr (different-atoms b c)))))
(ert-deftest ert-test-significant-plist-keys ()
(should (equal (ert--significant-plist-keys '()) '()))
(should (equal (ert--significant-plist-keys '(a b c d e f c g p q r nil s t))
'(a c e p s))))
(ert-deftest ert-test-plist-difference-explanation ()
(should (equal (ert--plist-difference-explanation
'(a b c nil) '(a b))
nil))
(should (equal (ert--plist-difference-explanation
'(a b c t) '(a b))
'(different-properties-for-key c (different-atoms t nil))))
(should (equal (ert--plist-difference-explanation
'(a b c t) '(c nil a b))
'(different-properties-for-key c (different-atoms t nil))))
(should (equal (ert--plist-difference-explanation
'(a b c (foo . bar)) '(c (foo . baz) a b))
'(different-properties-for-key c
(cdr
(different-atoms bar baz))))))
(ert-deftest ert-test-abbreviate-string ()
(should (equal (ert--abbreviate-string "foo" 4 nil) "foo"))
(should (equal (ert--abbreviate-string "foo" 3 nil) "foo"))
(should (equal (ert--abbreviate-string "foo" 3 nil) "foo"))
(should (equal (ert--abbreviate-string "foo" 2 nil) "fo"))
(should (equal (ert--abbreviate-string "foo" 1 nil) "f"))
(should (equal (ert--abbreviate-string "foo" 0 nil) ""))
(should (equal (ert--abbreviate-string "bar" 4 t) "bar"))
(should (equal (ert--abbreviate-string "bar" 3 t) "bar"))
(should (equal (ert--abbreviate-string "bar" 3 t) "bar"))
(should (equal (ert--abbreviate-string "bar" 2 t) "ar"))
(should (equal (ert--abbreviate-string "bar" 1 t) "r"))
(should (equal (ert--abbreviate-string "bar" 0 t) "")))
(ert-deftest ert-test-explain-not-equal-string-properties ()
(should
(equal (ert--explain-not-equal-including-properties #("foo" 0 1 (a b))
"foo")
'(char 0 "f"
(different-properties-for-key a (different-atoms b nil))
context-before ""
context-after "oo")))
(should (equal (ert--explain-not-equal-including-properties
#("foo" 1 3 (a b))
#("goo" 0 1 (c d)))
'(array-elt 0 (different-atoms (?f "#x66" "?f")
(?g "#x67" "?g")))))
(should
(equal (ert--explain-not-equal-including-properties
#("foo" 0 1 (a b c d) 1 3 (a b))
#("foo" 0 1 (c d a b) 1 2 (a foo)))
'(char 1 "o" (different-properties-for-key a (different-atoms b foo))
context-before "f" context-after "o"))))
(ert-deftest ert-test-equal-including-properties ()
(should (equal-including-properties "foo" "foo"))
(should (ert-equal-including-properties "foo" "foo"))
(should (equal-including-properties #("foo" 0 3 (a b))
(propertize "foo" 'a 'b)))
(should (ert-equal-including-properties #("foo" 0 3 (a b))
(propertize "foo" 'a 'b)))
(should (equal-including-properties #("foo" 0 3 (a b c d))
(propertize "foo" 'a 'b 'c 'd)))
(should (ert-equal-including-properties #("foo" 0 3 (a b c d))
(propertize "foo" 'a 'b 'c 'd)))
(should-not (equal-including-properties #("foo" 0 3 (a b c e))
(propertize "foo" 'a 'b 'c 'd)))
(should-not (ert-equal-including-properties #("foo" 0 3 (a b c e))
(propertize "foo" 'a 'b 'c 'd)))
;; This is bug 6581.
(should-not (equal-including-properties #("foo" 0 3 (a (t)))
(propertize "foo" 'a (list t))))
(should (ert-equal-including-properties #("foo" 0 3 (a (t)))
(propertize "foo" 'a (list t)))))
(ert-deftest ert-test-stats-set-test-and-result ()
(let* ((test-1 (make-ert-test :name 'test-1
:body (lambda () nil)))
(test-2 (make-ert-test :name 'test-2
:body (lambda () nil)))
(test-3 (make-ert-test :name 'test-2
:body (lambda () nil)))
(stats (ert--make-stats (list test-1 test-2) 't))
(failed (make-ert-test-failed :condition nil
:backtrace nil
:infos nil)))
(should (eql 2 (ert-stats-total stats)))
(should (eql 0 (ert-stats-completed stats)))
(should (eql 0 (ert-stats-completed-expected stats)))
(should (eql 0 (ert-stats-completed-unexpected stats)))
(ert--stats-set-test-and-result stats 0 test-1 (make-ert-test-passed))
(should (eql 2 (ert-stats-total stats)))
(should (eql 1 (ert-stats-completed stats)))
(should (eql 1 (ert-stats-completed-expected stats)))
(should (eql 0 (ert-stats-completed-unexpected stats)))
(ert--stats-set-test-and-result stats 0 test-1 failed)
(should (eql 2 (ert-stats-total stats)))
(should (eql 1 (ert-stats-completed stats)))
(should (eql 0 (ert-stats-completed-expected stats)))
(should (eql 1 (ert-stats-completed-unexpected stats)))
(ert--stats-set-test-and-result stats 0 test-1 nil)
(should (eql 2 (ert-stats-total stats)))
(should (eql 0 (ert-stats-completed stats)))
(should (eql 0 (ert-stats-completed-expected stats)))
(should (eql 0 (ert-stats-completed-unexpected stats)))
(ert--stats-set-test-and-result stats 0 test-3 failed)
(should (eql 2 (ert-stats-total stats)))
(should (eql 1 (ert-stats-completed stats)))
(should (eql 0 (ert-stats-completed-expected stats)))
(should (eql 1 (ert-stats-completed-unexpected stats)))
(ert--stats-set-test-and-result stats 1 test-2 (make-ert-test-passed))
(should (eql 2 (ert-stats-total stats)))
(should (eql 2 (ert-stats-completed stats)))
(should (eql 1 (ert-stats-completed-expected stats)))
(should (eql 1 (ert-stats-completed-unexpected stats)))
(ert--stats-set-test-and-result stats 0 test-1 (make-ert-test-passed))
(should (eql 2 (ert-stats-total stats)))
(should (eql 2 (ert-stats-completed stats)))
(should (eql 2 (ert-stats-completed-expected stats)))
(should (eql 0 (ert-stats-completed-unexpected stats)))))
(provide 'ert-tests)
;;; ert-tests.el ends here

View File

@ -0,0 +1,273 @@
;;; ert-x-tests.el --- Tests for ert-x.el
;; Copyright (C) 2008, 2010, 2011 Free Software Foundation, Inc.
;; Author: Phil Hagelberg
;; Author: Christian Ohler <ohler@gnu.org>
;; This file is part of GNU Emacs.
;; This program is free software: you can redistribute it and/or
;; modify it under the terms of the GNU General Public License as
;; published by the Free Software Foundation, either version 3 of the
;; License, or (at your option) any later version.
;;
;; This program is distributed in the hope that it will be useful, but
;; WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
;; General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see `http://www.gnu.org/licenses/'.
;;; Commentary:
;; This file is part of ERT, the Emacs Lisp Regression Testing tool.
;; See ert.el or the texinfo manual for more details.
;;; Code:
(eval-when-compile
(require 'cl))
(require 'ert)
(require 'ert-x)
;;; Utilities
(ert-deftest ert-test-buffer-string-reindented ()
(ert-with-test-buffer (:name "well-indented")
(insert (concat "(hello (world\n"
" 'elisp)\n"))
(emacs-lisp-mode)
(should (equal (ert-buffer-string-reindented) (buffer-string))))
(ert-with-test-buffer (:name "badly-indented")
(insert (concat "(hello\n"
" world)"))
(emacs-lisp-mode)
(should-not (equal (ert-buffer-string-reindented) (buffer-string)))))
(defun ert--hash-table-to-alist (table)
(let ((accu nil))
(maphash (lambda (key value)
(push (cons key value) accu))
table)
(nreverse accu)))
(ert-deftest ert-test-test-buffers ()
(let (buffer-1
buffer-2)
(let ((test-1
(make-ert-test
:name 'test-1
:body (lambda ()
(ert-with-test-buffer (:name "foo")
(should (string-match
"[*]Test buffer (ert-test-test-buffers): foo[*]"
(buffer-name)))
(setq buffer-1 (current-buffer))))))
(test-2
(make-ert-test
:name 'test-2
:body (lambda ()
(ert-with-test-buffer (:name "bar")
(should (string-match
"[*]Test buffer (ert-test-test-buffers): bar[*]"
(buffer-name)))
(setq buffer-2 (current-buffer))
(ert-fail "fail for test"))))))
(let ((ert--test-buffers (make-hash-table :weakness t)))
(ert-run-tests `(member ,test-1 ,test-2) #'ignore)
(should (equal (ert--hash-table-to-alist ert--test-buffers)
`((,buffer-2 . t))))
(should-not (buffer-live-p buffer-1))
(should (buffer-live-p buffer-2))))))
(ert-deftest ert-filter-string ()
(should (equal (ert-filter-string "foo bar baz" "quux")
"foo bar baz"))
(should (equal (ert-filter-string "foo bar baz" "bar")
"foo baz")))
(ert-deftest ert-propertized-string ()
(should (ert-equal-including-properties
(ert-propertized-string "a" '(a b) "b" '(c t) "cd")
#("abcd" 1 2 (a b) 2 4 (c t))))
(should (ert-equal-including-properties
(ert-propertized-string "foo " '(face italic) "bar" " baz" nil
" quux")
#("foo bar baz quux" 4 11 (face italic)))))
;;; Tests for ERT itself that require test features from ert-x.el.
(ert-deftest ert-test-run-tests-interactively-2 ()
:tags '(:causes-redisplay)
(let ((passing-test (make-ert-test :name 'passing-test
:body (lambda () (ert-pass))))
(failing-test (make-ert-test :name 'failing-test
:body (lambda ()
(ert-info ((propertize "foo\nbar"
'a 'b))
(ert-fail
"failure message"))))))
(let ((ert-debug-on-error nil))
(let* ((buffer-name (generate-new-buffer-name "*ert-test-run-tests*"))
(messages nil)
(mock-message-fn
(lambda (format-string &rest args)
(push (apply #'format format-string args) messages))))
(flet ((expected-string (with-font-lock-p)
(ert-propertized-string
"Selector: (member <passing-test> <failing-test>)\n"
"Passed: 1\n"
"Failed: 1 (1 unexpected)\n"
"Total: 2/2\n\n"
"Started at:\n"
"Finished.\n"
"Finished at:\n\n"
`(category ,(button-category-symbol
'ert--results-progress-bar-button)
button (t)
face ,(if with-font-lock-p
'ert-test-result-unexpected
'button))
".F" nil "\n\n"
`(category ,(button-category-symbol
'ert--results-expand-collapse-button)
button (t)
face ,(if with-font-lock-p
'ert-test-result-unexpected
'button))
"F" nil " "
`(category ,(button-category-symbol
'ert--test-name-button)
button (t)
ert-test-name failing-test)
"failing-test"
nil "\n Info: " '(a b) "foo\n"
nil " " '(a b) "bar"
nil "\n (ert-test-failed \"failure message\")\n\n\n"
)))
(save-window-excursion
(unwind-protect
(let ((case-fold-search nil))
(ert-run-tests-interactively
`(member ,passing-test ,failing-test) buffer-name
mock-message-fn)
(should (equal messages `(,(concat
"Ran 2 tests, 1 results were "
"as expected, 1 unexpected"))))
(with-current-buffer buffer-name
(font-lock-mode 0)
(should (ert-equal-including-properties
(ert-filter-string (buffer-string)
'("Started at:\\(.*\\)$" 1)
'("Finished at:\\(.*\\)$" 1))
(expected-string nil)))
;; `font-lock-mode' only works if interactive, so
;; pretend we are.
(let ((noninteractive nil))
(font-lock-mode 1))
(should (ert-equal-including-properties
(ert-filter-string (buffer-string)
'("Started at:\\(.*\\)$" 1)
'("Finished at:\\(.*\\)$" 1))
(expected-string t)))))
(when (get-buffer buffer-name)
(kill-buffer buffer-name)))))))))
(ert-deftest ert-test-describe-test ()
"Tests `ert-describe-test'."
(save-window-excursion
(ert-with-buffer-renamed ("*Help*")
(if (< emacs-major-version 24)
(should (equal (should-error (ert-describe-test 'ert-describe-test))
'(error "Requires Emacs 24")))
(ert-describe-test 'ert-test-describe-test)
(with-current-buffer "*Help*"
(let ((case-fold-search nil))
(should (string-match (concat
"\\`ert-test-describe-test is a test"
" defined in `ert-x-tests.elc?'\\.\n\n"
"Tests `ert-describe-test'\\.\n\\'")
(buffer-string)))))))))
(ert-deftest ert-test-message-log-truncation ()
:tags '(:causes-redisplay)
(let ((test (make-ert-test
:body (lambda ()
;; Emacs would combine messages if we
;; generate the same message multiple
;; times.
(message "a")
(message "b")
(message "c")
(message "d")))))
(let (result)
(ert-with-buffer-renamed ("*Messages*")
(let ((message-log-max 2))
(setq result (ert-run-test test)))
(should (equal (with-current-buffer "*Messages*"
(buffer-string))
"c\nd\n")))
(should (equal (ert-test-result-messages result) "a\nb\nc\nd\n")))))
(ert-deftest ert-test-builtin-message-log-flushing ()
"This test attempts to demonstrate that there is no way to
force immediate truncation of the *Messages* buffer from Lisp
\(and hence justifies the existence of
`ert--force-message-log-buffer-truncation'\): The only way that
came to my mind was \(message \"\"\), which doesn't have the
desired effect."
:tags '(:causes-redisplay)
(ert-with-buffer-renamed ("*Messages*")
(with-current-buffer "*Messages*"
(should (equal (buffer-string) ""))
;; We used to get sporadic failures in this test that involved
;; a spurious newline at the beginning of the buffer, before
;; the first message. Below, we print a message and erase the
;; buffer since this seems to eliminate the sporadic failures.
(message "foo")
(erase-buffer)
(should (equal (buffer-string) ""))
(let ((message-log-max 2))
(let ((message-log-max t))
(loop for i below 4 do
(message "%s" i))
(should (equal (buffer-string) "0\n1\n2\n3\n")))
(should (equal (buffer-string) "0\n1\n2\n3\n"))
(message "")
(should (equal (buffer-string) "0\n1\n2\n3\n"))
(message "Test message")
(should (equal (buffer-string) "3\nTest message\n"))))))
(ert-deftest ert-test-force-message-log-buffer-truncation ()
:tags '(:causes-redisplay)
(labels ((body ()
(loop for i below 3 do
(message "%s" i)))
;; Uses the implicit messages buffer truncation implemented
;; in Emacs' C core.
(c (x)
(ert-with-buffer-renamed ("*Messages*")
(let ((message-log-max x))
(body))
(with-current-buffer "*Messages*"
(buffer-string))))
;; Uses our lisp reimplementation.
(lisp (x)
(ert-with-buffer-renamed ("*Messages*")
(let ((message-log-max t))
(body))
(let ((message-log-max x))
(ert--force-message-log-buffer-truncation))
(with-current-buffer "*Messages*"
(buffer-string)))))
(loop for x in '(0 1 2 3 4 t) do
(should (equal (c x) (lisp x))))))
(provide 'ert-x-tests)
;;; ert-x-tests.el ends here