Commit d221e780 authored by Christian Ohler's avatar Christian Ohler
Browse files

Add ERT, a tool for automated testing in Emacs Lisp.

* Makefile.in, configure.in, doc/misc/Makefile.in, doc/misc/makefile.w32-in:
Add ERT.  Make "make check" run tests in test/automated.

* doc/misc/ert.texi, lisp/emacs-lisp/ert.el, lisp/emacs-lisp/ert-x.el:
New files.

* test/automated: New directory.
parent 03d32f1b
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
......
......@@ -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-src src lisp
# The subdir makefiles created by config.status.
SUBDIR_MAKEFILES = 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-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.
......@@ -368,7 +368,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
......@@ -810,7 +811,7 @@ TAGS tags: 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
......
......@@ -3750,7 +3750,7 @@ dnl the use of force in the `epaths-force' rule in Makefile.in.
AC_OUTPUT(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
......
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
......
......@@ -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)
......
This diff is collapsed.
......@@ -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* \
......
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.
......
......@@ -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.
......
2011-01-13 Christian Ohler <ohler@gnu.org>
* emacs-lisp/ert.el, emacs-lisp/ert-x.el: New files.
2011-01-11 Johan Bockgård <bojohan@gnu.org>
* emacs-lisp/unsafep.el (unsafep): Handle backquoted forms.
......
;;; 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
This diff is collapsed.
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.
......
# 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