Add skip-when macro to ert-deftest

This can help avoid some awkward test skip conditions.

For example, this triple negation:
    (skip-unless (not noninteractive))

Can be written as the simpler:
    (skip-when noninteractive)

* lisp/emacs-lisp/ert.el (ert-deftest): Add new 'skip-when' macro.
(ert--skip-when): New internal function.
* doc/misc/ert.texi (Tests and Their Environment): Document above
new macro.
* test/lisp/emacs-lisp/ert-tests.el (ert-test-skip-when): New test.
This commit is contained in:
Stefan Kangas 2023-09-03 18:45:16 +02:00
parent f9b43107ce
commit 62f7760e61
4 changed files with 67 additions and 16 deletions

View file

@ -658,11 +658,30 @@ versions, specific architectures, etc.:
@cindex skipping tests
@cindex test preconditions
@cindex preconditions of a test
@findex skip-when
@findex skip-unless
Sometimes, it doesn't make sense to run a test due to missing
preconditions. A required Emacs feature might not be compiled in, the
function to be tested could call an external binary which might not be
available on the test machine, you name it. In this case, the macro
@code{skip-unless} could be used to skip the test:
available on the test machine, you name it. In this case, the macros
@code{skip-when} or @code{skip-unless} could be used to skip the
test.@footnote{The @code{skip-when} macro was added in Emacs 30.1. If
you need your tests to be compatible with older versions of Emacs, use
@code{skip-unless} instead.}
@noindent
For example, this test is skipped on MS-Windows and macOS:
@lisp
(ert-deftest test-gnu-linux ()
"A test that is not relevant on MS-Windows and macOS."
(skip-when (memq system-type '(windows-nt ns))
...))
@end lisp
@noindent
This test is skipped if the feature @samp{dbusbind} is not present in
the running Emacs:
@lisp
(ert-deftest test-dbus ()

View file

@ -724,6 +724,13 @@ without specifying a file, like this:
*** New user option 'image-dired-thumb-naming'.
You can now configure how a thumbnail is named using this option.
** ERT
*** New macro `skip-when' to skip 'ert-deftest' tests.
This can help avoid some awkward skip conditions. For example
'(skip-unless (not noninteractive))' can be changed to the easier
to read '(skip-when noninteractive)'.
** checkdoc
---

View file

@ -34,17 +34,18 @@
;; `ert-run-tests-batch-and-exit' for non-interactive use.
;;
;; The body of `ert-deftest' forms resembles a function body, but the
;; additional operators `should', `should-not', `should-error' and
;; `skip-unless' are available. `should' is similar to cl's `assert',
;; but signals a different error when its condition is violated that
;; is caught and processed by ERT. In addition, it analyzes its
;; argument form and records information that helps debugging
;; (`cl-assert' tries to do something similar when its second argument
;; SHOW-ARGS is true, but `should' is more sophisticated). For
;; information on `should-not' and `should-error', see their
;; docstrings. `skip-unless' skips the test immediately without
;; processing further, this is useful for checking the test
;; environment (like availability of features, external binaries, etc).
;; additional operators `should', `should-not', `should-error',
;; `skip-when' and `skip-unless' are available. `should' is similar
;; to cl's `assert', but signals a different error when its condition
;; is violated that is caught and processed by ERT. In addition, it
;; analyzes its argument form and records information that helps
;; debugging (`cl-assert' tries to do something similar when its
;; second argument SHOW-ARGS is true, but `should' is more
;; sophisticated). For information on `should-not' and
;; `should-error', see their docstrings. The `skip-when' and
;; `skip-unless' forms skip the test immediately, which is useful for
;; checking the test environment (like availability of features,
;; external binaries, etc).
;;
;; See ERT's Info manual `(ert) Top' as well as the docstrings for
;; more details. To see some examples of tests written in ERT, see
@ -194,8 +195,8 @@ and the body."
BODY is evaluated as a `progn' when the test is run. It should
signal a condition on failure or just return if the test passes.
`should', `should-not', `should-error' and `skip-unless' are
useful for assertions in BODY.
`should', `should-not', `should-error', `skip-when', and
`skip-unless' are useful for assertions in BODY.
Use `ert' to run tests interactively.
@ -227,7 +228,8 @@ in batch mode, an error is signaled.
(tags nil tags-supplied-p))
body)
(ert--parse-keys-and-body docstring-keys-and-body)
`(cl-macrolet ((skip-unless (form) `(ert--skip-unless ,form)))
`(cl-macrolet ((skip-when (form) `(ert--skip-when ,form))
(skip-unless (form) `(ert--skip-unless ,form)))
(ert-set-test ',name
(make-ert-test
:name ',name
@ -464,6 +466,15 @@ failed."
(list
:fail-reason "did not signal an error")))))))))
(cl-defmacro ert--skip-when (form)
"Evaluate FORM. If it returns t, skip the current test.
Errors during evaluation are caught and handled like t."
(declare (debug t))
(ert--expand-should `(skip-when ,form) form
(lambda (inner-form form-description-form _value-var)
`(when (condition-case nil ,inner-form (t t))
(ert-skip ,form-description-form)))))
(cl-defmacro ert--skip-unless (form)
"Evaluate FORM. If it returns nil, skip the current test.
Errors during evaluation are caught and handled like nil."

View file

@ -304,6 +304,20 @@ failed or if there was a problem."
(cl-macrolet ((test () (error "Foo")))
(should-error (test))))
(ert-deftest ert-test-skip-when ()
;; Don't skip.
(let ((test (make-ert-test :body (lambda () (skip-when nil)))))
(let ((result (ert-run-test test)))
(should (ert-test-passed-p result))))
;; Skip.
(let ((test (make-ert-test :body (lambda () (skip-when t)))))
(let ((result (ert-run-test test)))
(should (ert-test-skipped-p result))))
;; Skip in case of error.
(let ((test (make-ert-test :body (lambda () (skip-when (error "Foo"))))))
(let ((result (ert-run-test test)))
(should (ert-test-skipped-p result)))))
(ert-deftest ert-test-skip-unless ()
;; Don't skip.
(let ((test (make-ert-test :body (lambda () (skip-unless t)))))