summaryrefslogtreecommitdiff
path: root/test/lisp
diff options
context:
space:
mode:
Diffstat (limited to 'test/lisp')
-rw-r--r--test/lisp/abbrev-tests.el3
-rw-r--r--test/lisp/autorevert-tests.el170
-rw-r--r--test/lisp/emacs-lisp/cl-seq-tests.el6
-rw-r--r--test/lisp/emacs-lisp/let-alist-tests.el5
-rw-r--r--test/lisp/emacs-lisp/testcover-resources/testcases.el493
-rw-r--r--test/lisp/emacs-lisp/testcover-tests.el186
-rw-r--r--test/lisp/faces-tests.el9
-rw-r--r--test/lisp/ffap-tests.el2
-rw-r--r--test/lisp/filenotify-tests.el70
-rw-r--r--test/lisp/htmlfontify-tests.el12
-rw-r--r--test/lisp/ibuffer-tests.el9
-rw-r--r--test/lisp/kmacro-tests.el890
-rw-r--r--test/lisp/minibuffer-tests.el2
-rw-r--r--test/lisp/net/dbus-tests.el3
-rw-r--r--test/lisp/progmodes/js-tests.el14
-rw-r--r--test/lisp/progmodes/python-tests.el23
-rw-r--r--test/lisp/simple-tests.el6
-rw-r--r--test/lisp/textmodes/css-mode-tests.el15
-rw-r--r--test/lisp/textmodes/tildify-tests.el2
-rw-r--r--test/lisp/vc/diff-mode-tests.el203
-rw-r--r--test/lisp/xml-tests.el15
21 files changed, 1998 insertions, 140 deletions
diff --git a/test/lisp/abbrev-tests.el b/test/lisp/abbrev-tests.el
index a454471ae3b..1ffcd6ac0d0 100644
--- a/test/lisp/abbrev-tests.el
+++ b/test/lisp/abbrev-tests.el
@@ -45,8 +45,7 @@
(should-not (abbrev-table-p []))
;; Missing :abbrev-table-modiff counter:
(should-not (abbrev-table-p (obarray-make)))
- (let* ((table (obarray-make)))
- (should (abbrev-table-empty-p (make-abbrev-table)))))
+ (should (abbrev-table-empty-p (make-abbrev-table))))
(ert-deftest abbrev-make-abbrev-table-test ()
;; Table without properties:
diff --git a/test/lisp/autorevert-tests.el b/test/lisp/autorevert-tests.el
index aea855ae02f..c6f103321c6 100644
--- a/test/lisp/autorevert-tests.el
+++ b/test/lisp/autorevert-tests.el
@@ -24,24 +24,29 @@
;;; Code:
(require 'ert)
+(require 'ert-x)
(require 'autorevert)
(setq auto-revert-notify-exclude-dir-regexp "nothing-to-be-excluded"
auto-revert-stop-on-user-input nil)
(defconst auto-revert--timeout 10
- "Time to wait until a message appears in the *Messages* buffer.")
+ "Time to wait for a message.")
+
+(defvar auto-revert--messages nil
+ "Used to collect messages issued during a section of a test.")
(defun auto-revert--wait-for-revert (buffer)
- "Wait until the *Messages* buffer reports reversion of BUFFER."
+ "Wait until a message reports reversion of BUFFER.
+This expects `auto-revert--messages' to be bound by
+`ert-with-message-capture' before calling."
(with-timeout (auto-revert--timeout nil)
- (with-current-buffer "*Messages*"
- (while
- (null (string-match
- (format-message "Reverting buffer `%s'." (buffer-name buffer))
- (buffer-string)))
- (if (with-current-buffer buffer auto-revert-use-notify)
- (read-event nil nil 0.1)
- (sleep-for 0.1))))))
+ (while
+ (null (string-match
+ (format-message "Reverting buffer `%s'." (buffer-name buffer))
+ auto-revert--messages))
+ (if (with-current-buffer buffer auto-revert-use-notify)
+ (read-event nil nil 0.1)
+ (sleep-for 0.1)))))
(ert-deftest auto-revert-test00-auto-revert-mode ()
"Check autorevert for a file."
@@ -51,41 +56,38 @@
buf)
(unwind-protect
(progn
- (with-current-buffer (get-buffer-create "*Messages*")
- (narrow-to-region (point-max) (point-max)))
- (write-region "any text" nil tmpfile nil 'no-message)
+ (write-region "any text" nil tmpfile nil 'no-message)
(setq buf (find-file-noselect tmpfile))
- (with-current-buffer buf
- (should (string-equal (buffer-string) "any text"))
- ;; `buffer-stale--default-function' checks for
- ;; `verify-visited-file-modtime'. We must ensure that it
- ;; returns nil.
- (sleep-for 1)
- (auto-revert-mode 1)
- (should auto-revert-mode)
+ (with-current-buffer buf
+ (ert-with-message-capture auto-revert--messages
+ (should (string-equal (buffer-string) "any text"))
+ ;; `buffer-stale--default-function' checks for
+ ;; `verify-visited-file-modtime'. We must ensure that it
+ ;; returns nil.
+ (sleep-for 1)
+ (auto-revert-mode 1)
+ (should auto-revert-mode)
- ;; Modify file. We wait for a second, in order to have
- ;; another timestamp.
- (sleep-for 1)
- (write-region "another text" nil tmpfile nil 'no-message)
+ ;; Modify file. We wait for a second, in order to have
+ ;; another timestamp.
+ (sleep-for 1)
+ (write-region "another text" nil tmpfile nil 'no-message)
- ;; Check, that the buffer has been reverted.
- (auto-revert--wait-for-revert buf)
+ ;; Check, that the buffer has been reverted.
+ (auto-revert--wait-for-revert buf))
(should (string-match "another text" (buffer-string)))
;; When the buffer is modified, it shall not be reverted.
- (with-current-buffer (get-buffer-create "*Messages*")
- (narrow-to-region (point-max) (point-max)))
- (set-buffer-modified-p t)
- (sleep-for 1)
- (write-region "any text" nil tmpfile nil 'no-message)
+ (ert-with-message-capture auto-revert--messages
+ (set-buffer-modified-p t)
+ (sleep-for 1)
+ (write-region "any text" nil tmpfile nil 'no-message)
- ;; Check, that the buffer hasn't been reverted.
- (auto-revert--wait-for-revert buf)
+ ;; Check, that the buffer hasn't been reverted.
+ (auto-revert--wait-for-revert buf))
(should-not (string-match "any text" (buffer-string)))))
;; Exit.
- (with-current-buffer "*Messages*" (widen))
(ignore-errors
(with-current-buffer buf (set-buffer-modified-p nil))
(kill-buffer buf))
@@ -106,13 +108,11 @@
(make-temp-file (expand-file-name "auto-revert-test" tmpdir1)))
buf1 buf2)
(unwind-protect
- (progn
- (with-current-buffer (get-buffer-create "*Messages*")
- (narrow-to-region (point-max) (point-max)))
- (write-region "any text" nil tmpfile1 nil 'no-message)
- (setq buf1 (find-file-noselect tmpfile1))
- (write-region "any text" nil tmpfile2 nil 'no-message)
- (setq buf2 (find-file-noselect tmpfile2))
+ (ert-with-message-capture auto-revert--messages
+ (write-region "any text" nil tmpfile1 nil 'no-message)
+ (setq buf1 (find-file-noselect tmpfile1))
+ (write-region "any text" nil tmpfile2 nil 'no-message)
+ (setq buf2 (find-file-noselect tmpfile2))
(dolist (buf (list buf1 buf2))
(with-current-buffer buf
@@ -148,7 +148,6 @@
(should (string-match "another text" (buffer-string))))))
;; Exit.
- (with-current-buffer "*Messages*" (widen))
(ignore-errors
(dolist (buf (list buf1 buf2))
(with-current-buffer buf (set-buffer-modified-p nil))
@@ -165,8 +164,6 @@
buf)
(unwind-protect
(progn
- (with-current-buffer (get-buffer-create "*Messages*")
- (narrow-to-region (point-max) (point-max)))
(write-region "any text" nil tmpfile nil 'no-message)
(setq buf (find-file-noselect tmpfile))
(with-current-buffer buf
@@ -184,42 +181,38 @@
'before-revert-hook
(lambda () (delete-file buffer-file-name))
nil t)
- (with-current-buffer (get-buffer-create "*Messages*")
- (narrow-to-region (point-max) (point-max)))
- (sleep-for 1)
- (write-region "another text" nil tmpfile nil 'no-message)
- ;; Check, that the buffer hasn't been reverted. File
- ;; notification should be disabled, falling back to
- ;; polling.
- (auto-revert--wait-for-revert buf)
+ (ert-with-message-capture auto-revert--messages
+ (sleep-for 1)
+ (write-region "another text" nil tmpfile nil 'no-message)
+ (auto-revert--wait-for-revert buf))
+ ;; Check, that the buffer hasn't been reverted. File
+ ;; notification should be disabled, falling back to
+ ;; polling.
(should (string-match "any text" (buffer-string)))
- (should-not auto-revert-use-notify)
+ ;; With w32notify, the 'stopped' events are not sent.
+ (or (eq file-notify--library 'w32notify)
+ (should-not auto-revert-use-notify))
;; Once the file has been recreated, the buffer shall be
;; reverted.
(kill-local-variable 'before-revert-hook)
- (with-current-buffer (get-buffer-create "*Messages*")
- (narrow-to-region (point-max) (point-max)))
- (sleep-for 1)
- (write-region "another text" nil tmpfile nil 'no-message)
-
- ;; Check, that the buffer has been reverted.
- (auto-revert--wait-for-revert buf)
+ (ert-with-message-capture auto-revert--messages
+ (sleep-for 1)
+ (write-region "another text" nil tmpfile nil 'no-message)
+ (auto-revert--wait-for-revert buf))
+ ;; Check, that the buffer has been reverted.
(should (string-match "another text" (buffer-string)))
;; An empty file shall still be reverted.
- (with-current-buffer (get-buffer-create "*Messages*")
- (narrow-to-region (point-max) (point-max)))
- (sleep-for 1)
- (write-region "" nil tmpfile nil 'no-message)
-
- ;; Check, that the buffer has been reverted.
- (auto-revert--wait-for-revert buf)
+ (ert-with-message-capture auto-revert--messages
+ (sleep-for 1)
+ (write-region "" nil tmpfile nil 'no-message)
+ (auto-revert--wait-for-revert buf))
+ ;; Check, that the buffer has been reverted.
(should (string-equal "" (buffer-string)))))
;; Exit.
- (with-current-buffer "*Messages*" (widen))
(ignore-errors
(with-current-buffer buf (set-buffer-modified-p nil))
(kill-buffer buf))
@@ -232,9 +225,7 @@
(let ((tmpfile (make-temp-file "auto-revert-test"))
buf)
(unwind-protect
- (progn
- (with-current-buffer (get-buffer-create "*Messages*")
- (narrow-to-region (point-max) (point-max)))
+ (ert-with-message-capture auto-revert--messages
(write-region "any text" nil tmpfile nil 'no-message)
(setq buf (find-file-noselect tmpfile))
(with-current-buffer buf
@@ -259,7 +250,6 @@
(string-match "modified text\nanother text" (buffer-string)))))
;; Exit.
- (with-current-buffer "*Messages*" (widen))
(ignore-errors (kill-buffer buf))
(ignore-errors (delete-file tmpfile)))))
@@ -283,33 +273,29 @@
(should
(string-match name (substring-no-properties (buffer-string))))
- ;; Delete file. We wait for a second, in order to have
- ;; another timestamp.
- (with-current-buffer (get-buffer-create "*Messages*")
- (narrow-to-region (point-max) (point-max)))
- (sleep-for 1)
- (delete-file tmpfile)
-
- ;; Check, that the buffer has been reverted.
- (auto-revert--wait-for-revert buf)
+ (ert-with-message-capture auto-revert--messages
+ ;; Delete file. We wait for a second, in order to have
+ ;; another timestamp.
+ (sleep-for 1)
+ (delete-file tmpfile)
+ (auto-revert--wait-for-revert buf))
+ ;; Check, that the buffer has been reverted.
(should-not
(string-match name (substring-no-properties (buffer-string))))
- ;; Make dired buffer modified. Check, that the buffer has
- ;; been still reverted.
- (with-current-buffer (get-buffer-create "*Messages*")
- (narrow-to-region (point-max) (point-max)))
- (set-buffer-modified-p t)
- (sleep-for 1)
- (write-region "any text" nil tmpfile nil 'no-message)
+ (ert-with-message-capture auto-revert--messages
+ ;; Make dired buffer modified. Check, that the buffer has
+ ;; been still reverted.
+ (set-buffer-modified-p t)
+ (sleep-for 1)
+ (write-region "any text" nil tmpfile nil 'no-message)
- ;; Check, that the buffer has been reverted.
- (auto-revert--wait-for-revert buf)
+ (auto-revert--wait-for-revert buf))
+ ;; Check, that the buffer has been reverted.
(should
(string-match name (substring-no-properties (buffer-string))))))
;; Exit.
- (with-current-buffer "*Messages*" (widen))
(ignore-errors
(with-current-buffer buf (set-buffer-modified-p nil))
(kill-buffer buf))
diff --git a/test/lisp/emacs-lisp/cl-seq-tests.el b/test/lisp/emacs-lisp/cl-seq-tests.el
index 3740b5c1836..61e3d720331 100644
--- a/test/lisp/emacs-lisp/cl-seq-tests.el
+++ b/test/lisp/emacs-lisp/cl-seq-tests.el
@@ -250,9 +250,9 @@ Body are forms defining the test."
(should (= 0 (cl-count -5 list)))
(should (= 0 (cl-count 2 list :start 2 :end 4)))
(should (= 4 (cl-count 'foo list :key (lambda (x) (and (cl-evenp x) 'foo)))))
- (should (= 4 (cl-count 'foo list :test (lambda (a b) (cl-evenp b)))))
- (should (equal (cl-count 'foo list :test (lambda (a b) (cl-oddp b)))
- (cl-count 'foo list :test-not (lambda (a b) (cl-evenp b)))))))
+ (should (= 4 (cl-count 'foo list :test (lambda (_a b) (cl-evenp b)))))
+ (should (equal (cl-count 'foo list :test (lambda (_a b) (cl-oddp b)))
+ (cl-count 'foo list :test-not (lambda (_a b) (cl-evenp b)))))))
;; keywords supported: :test :test-not :key :start1 :end1 :start2 :end2 :from-end
(ert-deftest cl-seq-mismatch-test ()
diff --git a/test/lisp/emacs-lisp/let-alist-tests.el b/test/lisp/emacs-lisp/let-alist-tests.el
index fbcde4e3cbf..d04645709e4 100644
--- a/test/lisp/emacs-lisp/let-alist-tests.el
+++ b/test/lisp/emacs-lisp/let-alist-tests.el
@@ -31,7 +31,7 @@
(.test-two (cdr (assq 'test-two symbol))))
(list .test-one .test-two
.test-two .test-two)))
- (cl-letf (((symbol-function #'make-symbol) (lambda (x) 'symbol)))
+ (cl-letf (((symbol-function #'make-symbol) (lambda (_x) 'symbol)))
(macroexpand
'(let-alist data (list .test-one .test-two
.test-two .test-two))))))
@@ -51,8 +51,7 @@
(ert-deftest let-alist-cons ()
(should
(equal
- (let ((.external "ext")
- (.external.too "et"))
+ (let ((.external "ext"))
(let-alist '((test-two . 0)
(test-three . 1)
(sublist . ((foo . 2)
diff --git a/test/lisp/emacs-lisp/testcover-resources/testcases.el b/test/lisp/emacs-lisp/testcover-resources/testcases.el
new file mode 100644
index 00000000000..1eb791a993c
--- /dev/null
+++ b/test/lisp/emacs-lisp/testcover-resources/testcases.el
@@ -0,0 +1,493 @@
+;;;; testcases.el -- Test cases for testcover-tests.el
+
+;; Copyright (C) 2017 Free Software Foundation, Inc.
+
+;; Author: Gemini Lasswell
+
+;; 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 should not be loaded directly. It is meant to be read
+;; by `testcover-tests-build-test-cases'.
+;;
+;; * Test cases begin with ;; ==== name ====. The symbol name between
+;; the ===='s is used to create the name of the test.
+;;
+;; * Following the beginning comment place the test docstring and
+;; any tags or keywords for ERT. These will be spliced into the
+;; ert-deftest for the test.
+;;
+;; * To separate the above from the test case code, use another
+;; comment: ;; ====
+;;
+;; * These special comments should start at the beginning of a line.
+;;
+;; * `testcover-tests-skeleton' will prompt you for a test name and
+;; insert the special comments.
+;;
+;; * The test case code should be annotated with %%% at the end of
+;; each form where a tan splotch is expected, and !!! at the end
+;; of each form where a red mark is expected.
+;;
+;; * If Testcover is working correctly on your code sample, using
+;; `testcover-tests-markup-region' and
+;; `testcover-tests-unmarkup-region' can make creating test cases
+;; easier.
+
+;;; Code:
+;;; Test Cases:
+
+;; ==== constants-bug-25316 ====
+"Testcover doesn't splotch constants."
+:expected-result :failed
+;; ====
+(defconst testcover-testcase-const "apples")
+(defun testcover-testcase-zero () 0)
+(defun testcover-testcase-list-consts ()
+ (list
+ emacs-version 10
+ "hello"
+ `(a b c ,testcover-testcase-const)
+ '(1 2 3)
+ testcover-testcase-const
+ (testcover-testcase-zero)
+ nil))
+
+(defun testcover-testcase-add-to-const-list (arg)
+ (cons arg%%% (testcover-testcase-list-consts))%%%)
+
+(should (equal (testcover-testcase-add-to-const-list 'a)
+ `(a ,emacs-version 10 "hello" (a b c "apples") (1 2 3)
+ "apples" 0 nil)))
+
+;; ==== customize-defcustom-bug-25326 ====
+"Testcover doesn't prevent testing of defcustom values."
+:expected-result :failed
+;; ====
+(defgroup testcover-testcase nil
+ "Test case for testcover"
+ :group 'lisp
+ :prefix "testcover-testcase-"
+ :version "26.0")
+(defcustom testcover-testcase-flag t
+ "Test value used by testcover-tests.el"
+ :type 'boolean
+ :group 'testcover-testcase)
+(defun testcover-testcase-get-flag ()
+ testcover-testcase-flag)
+
+(testcover-testcase-get-flag)
+(setq testcover-testcase-flag (not testcover-testcase-flag))
+(testcover-testcase-get-flag)
+
+;; ==== no-returns ====
+"Testcover doesn't splotch functions which don't return."
+;; ====
+(defun testcover-testcase-play-ball (retval)
+ (catch 'ball
+ (throw 'ball retval%%%))%%%) ; catch gets marked but not throw
+
+(defun testcover-testcase-not-my-favorite-error-message ()
+ (signal 'wrong-type-argument (list 'consp nil)))
+
+(should (testcover-testcase-play-ball t))
+(condition-case nil
+ (testcover-testcase-not-my-favorite-error-message)
+ (error nil))
+
+;; ==== noreturn-symbol ====
+"Wrapping a form with noreturn prevents splotching."
+;; ====
+(defun testcover-testcase-cancel (spacecraft)
+ (error "no destination for %s" spacecraft))
+(defun testcover-testcase-launch (spacecraft planet)
+ (if (null planet)
+ (noreturn (testcover-testcase-cancel spacecraft%%%))
+ (list spacecraft%%% planet%%%)%%%)%%%)
+(defun testcover-testcase-launch-2 (spacecraft planet)
+ (if (null planet%%%)%%%
+ (testcover-testcase-cancel spacecraft%%%)!!!
+ (list spacecraft!!! planet!!!)!!!)!!!)
+(should (equal (testcover-testcase-launch "Curiosity" "Mars") '("Curiosity" "Mars")))
+(condition-case err
+ (testcover-testcase-launch "Voyager" nil)
+ (error err))
+(condition-case err
+ (testcover-testcase-launch-2 "Voyager II" nil)
+ (error err))
+
+(should-error (testcover-testcase-launch "Voyager" nil))
+(should-error (testcover-testcase-launch-2 "Voyager II" nil))
+
+;; ==== 1-value-symbol-bug-25316 ====
+"Wrapping a form with 1value prevents splotching."
+:expected-result :failed
+;; ====
+(defun testcover-testcase-always-zero (num)
+ (- num%%% num%%%)%%%)
+(defun testcover-testcase-still-always-zero (num)
+ (1value (- num%%% num%%% (- num%%% num%%%)%%%)))
+(defun testcover-testcase-never-called (num)
+ (1value (/ num!!! num!!!)!!!)!!!)
+(should (eql 0 (testcover-testcase-always-zero 3)))
+(should (eql 0 (testcover-testcase-still-always-zero 5)))
+
+;; ==== dotimes-dolist ====
+"Dolist and dotimes with a 1valued return value are 1valued."
+;; ====
+(defun testcover-testcase-do-over (things)
+ (dolist (thing things%%%)
+ (list thing))
+ (dolist (thing things%%% 42)
+ (list thing))
+ (dolist (thing things%%% things%%%)
+ (list thing))%%%)
+(defun testcover-testcase-do-more (count)
+ (dotimes (num count%%%)
+ (+ num num))
+ (dotimes (num count%%% count%%%)
+ (+ num num))%%%
+ (dotimes (num count%%% 0)
+ (+ num num)))
+(should (equal '(a b c) (testcover-testcase-do-over '(a b c))))
+(should (eql 0 (testcover-testcase-do-more 2)))
+
+;; ==== let-last-form ====
+"A let form is 1valued if its last form is 1valued."
+;; ====
+(defun testcover-testcase-double (num)
+ (let ((double (* num%%% 2)%%%))
+ double%%%)%%%)
+(defun testcover-testcase-nullbody-let (num)
+ (let* ((square (* num%%% num%%%)%%%)
+ (double (* 2 num%%%)%%%))))
+(defun testcover-testcase-answer ()
+ (let ((num 100))
+ 42))
+(should-not (testcover-testcase-nullbody-let 3))
+(should (eql (testcover-testcase-answer) 42))
+(should (eql (testcover-testcase-double 10) 20))
+
+;; ==== if-with-1value-clauses ====
+"An if is 1valued if both then and else are 1valued."
+;; ====
+(defun testcover-testcase-describe (val)
+ (if (zerop val%%%)%%%
+ "a number"
+ "a different number"))
+(defun testcover-testcase-describe-2 (val)
+ (if (zerop val)
+ "zero"
+ "not zero"))
+(defun testcover-testcase-describe-3 (val)
+ (if (zerop val%%%)%%%
+ "zero"
+ (format "%d" val%%%)%%%)%%%)
+(should (equal (testcover-testcase-describe 0) "a number"))
+(should (equal (testcover-testcase-describe-2 0) "zero"))
+(should (equal (testcover-testcase-describe-2 1) "not zero"))
+(should (equal (testcover-testcase-describe-3 1) "1"))
+
+;; ==== cond-with-1value-clauses ====
+"A cond form is marked 1valued if all clauses are 1valued."
+;; ====
+(defun testcover-testcase-cond (num)
+ (cond
+ ((eql num%%% 0)%%% 'a)
+ ((eql num%%% 1)%%% 'b)
+ ((eql num!!! 2)!!! 'c)))
+(defun testcover-testcase-cond-2 (num)
+ (cond
+ ((eql num%%% 0)%%% (cons 'a 0)!!!)
+ ((eql num%%% 1)%%% 'b))%%%)
+(should (eql (testcover-testcase-cond 1) 'b))
+(should (eql (testcover-testcase-cond-2 1) 'b))
+
+;; ==== condition-case-with-1value-components ====
+"A condition-case is marked 1valued if its body and handlers are."
+;; ====
+(defun testcover-testcase-cc (arg)
+ (condition-case nil
+ (if (null arg%%%)%%%
+ (error "foo")
+ "0")!!!
+ (error nil)))
+(should-not (testcover-testcase-cc nil))
+
+;; ==== quotes-within-backquotes-bug-25316 ====
+"Forms to instrument are found within quotes within backquotes."
+:expected-result :failed
+;; ====
+(defun testcover-testcase-make-list ()
+ (list 'defun 'defvar))
+(defmacro testcover-testcase-bq-macro (arg)
+ (declare (debug t))
+ `(memq ,arg%%% '(defconst ,@(testcover-testcase-make-list)))%%%)
+(defun testcover-testcase-use-bq-macro (arg)
+ (testcover-testcase-bq-macro arg%%%)%%%)
+(should (equal '(defun defvar) (testcover-testcase-use-bq-macro 'defun)))
+
+;; ==== progn-functions ====
+"Some forms are 1value if their last argument is 1value."
+;; ====
+(defun testcover-testcase-one (arg)
+ (progn
+ (setq arg (1- arg%%%)%%%)%%%)%%%
+ (progn
+ (setq arg (1+ arg%%%)%%%)%%%
+ 1))
+
+(should (eql 1 (testcover-testcase-one 0)))
+;; ==== prog1-functions ====
+"Some forms are 1value if their first argument is 1value."
+;; ====
+(defun testcover-testcase-unwinder (arg)
+ (unwind-protect
+ (if ( > arg%%% 0)%%%
+ 1
+ 0)
+ (format "unwinding %s!" arg%%%)%%%))
+(defun testcover-testcase-divider (arg)
+ (unwind-protect
+ (/ 100 arg%%%)%%%
+ (format "unwinding! %s" arg%%%)%%%)%%%)
+
+(should (eq 0 (testcover-testcase-unwinder 0)))
+(should (eq 1 (testcover-testcase-divider 100)))
+
+;; ==== compose-functions ====
+"Some functions are 1value if all their arguments are 1value."
+;; ====
+(defconst testcover-testcase-count 3)
+(defun testcover-testcase-number ()
+ (+ 1 testcover-testcase-count))
+(defun testcover-testcase-more ()
+ (+ 1 (testcover-testcase-number) testcover-testcase-count))
+
+(should (equal (testcover-testcase-more) 8))
+
+;; ==== apply-quoted-symbol ====
+"Apply with a quoted function symbol treated as 1value if function is."
+;; ====
+(defun testcover-testcase-numlist (flag)
+ (if flag%%%
+ '(1 2 3)
+ '(4 5 6)))
+(defun testcover-testcase-sum (flag)
+ (apply '+ (testcover-testcase-numlist flag%%%)))
+(defun testcover-testcase-label ()
+ (apply 'message "edebug uses: %s %s" (list 1 2)!!!)!!!)
+
+(should (equal 6 (testcover-testcase-sum t)))
+
+;; ==== backquote-1value-bug-24509 ====
+"Commas within backquotes are recognized as non-1value."
+:expected-result :failed
+;; ====
+(defmacro testcover-testcase-lambda (&rest body)
+ `(lambda () ,@body))
+
+(defun testcover-testcase-example ()
+ (let ((lambda-1 (testcover-testcase-lambda (format "lambda-%d" 1))%%%)
+ (lambda-2 (testcover-testcase-lambda (format "lambda-%d" 2))%%%))
+ (concat (funcall lambda-1%%%)%%% " "
+ (funcall lambda-2%%%)%%%)%%%)%%%)
+
+(defmacro testcover-testcase-message-symbol (name)
+ `(message "%s" ',name))
+
+(defun testcover-testcase-example-2 ()
+ (concat
+ (testcover-testcase-message-symbol foo)%%%
+ (testcover-testcase-message-symbol bar)%%%)%%%)
+
+(should (equal "lambda-1 lambda-2" (testcover-testcase-example)))
+(should (equal "foobar" (testcover-testcase-example-2)))
+
+;; ==== pcase-bug-24688 ====
+"Testcover copes with condition-case within backquoted list."
+:expected-result :failed
+;; ====
+(defun testcover-testcase-pcase (form)
+ (pcase form%%%
+ (`(condition-case ,var ,protected-form . ,handlers)
+ (list var%%% protected-form%%% handlers%%%)%%%)
+ (_ nil))%%%)
+
+(should (equal (testcover-testcase-pcase '(condition-case a
+ (/ 5 a)
+ (error 0)))
+ '(a (/ 5 a) ((error 0)))))
+
+;; ==== defun-in-backquote-bug-11307-and-24743 ====
+"Testcover handles defun forms within backquoted list."
+:expected-result :failed
+;; ====
+(defmacro testcover-testcase-defun (name &rest body)
+ (declare (debug (symbolp def-body)))
+ `(defun ,name () ,@body))
+
+(testcover-testcase-defun foo (+ 1 2))
+(testcover-testcase-defun bar (+ 3 4))
+(should (eql (foo) 3))
+(should (eql (bar) 7))
+
+;; ==== closure-1value-bug ====
+"Testcover does not mark closures as 1value."
+:expected-result :failed
+;; ====
+;; -*- lexical-binding:t -*-
+(setq testcover-testcase-foo nil)
+(setq testcover-testcase-bar 0)
+
+(defun testcover-testcase-baz (arg)
+ (setq testcover-testcase-foo
+ (lambda () (+ arg testcover-testcase-bar%%%))))
+
+(testcover-testcase-baz 2)
+(should (equal 2 (funcall testcover-testcase-foo)))
+(testcover-testcase-baz 3)
+(should (equal 3 (funcall testcover-testcase-foo)))
+
+;; ==== by-value-vs-by-reference-bug-25351 ====
+"An object created by a 1value expression may be modified by other code."
+:expected-result :failed
+;; ====
+(defun testcover-testcase-ab ()
+ (list 'a 'b))
+(defun testcover-testcase-change-it (arg)
+ (setf (cadr arg%%%)%%% 'c)%%%
+ arg%%%)
+
+(should (equal (testcover-testcase-change-it (testcover-testcase-ab)) '(a c)))
+(should (equal (testcover-testcase-ab) '(a b)))
+
+;; ==== 1value-error-test ====
+"Forms wrapped by `1value' should always return the same value."
+;; ====
+(defun testcover-testcase-thing (arg)
+ (1value (list 1 arg 3)))
+
+(should (equal '(1 2 3) (testcover-testcase-thing 2)))
+(should-error (testcover-testcase-thing 3))
+
+;; ==== dotted-backquote ====
+"Testcover correctly instruments dotted backquoted lists."
+;; ====
+(defun testcover-testcase-dotted-bq (flag extras)
+ (let* ((bq
+ `(a b c . ,(and flag extras%%%))))
+ bq))
+
+(should (equal '(a b c) (testcover-testcase-dotted-bq nil '(d e))))
+(should (equal '(a b c d e) (testcover-testcase-dotted-bq t '(d e))))
+
+;; ==== backquoted-vector-bug-25316 ====
+"Testcover reinstruments within backquoted vectors."
+:expected-result :failed
+;; ====
+(defun testcover-testcase-vec (a b c)
+ `[,a%%% ,(list b%%% c%%%)%%%]%%%)
+
+(defun testcover-testcase-vec-in-list (d e f)
+ `([[,d%%% ,e%%%] ,f%%%])%%%)
+
+(defun testcover-testcase-vec-arg (num)
+ (list `[,num%%%]%%%)%%%)
+
+(should (equal [1 (2 3)] (testcover-testcase-vec 1 2 3)))
+(should (equal '([[4 5] 6]) (testcover-testcase-vec-in-list 4 5 6)))
+(should (equal '([100]) (testcover-testcase-vec-arg 100)))
+
+;; ==== vector-in-macro-spec-bug-25316 ====
+"Testcover reinstruments within vectors."
+:expected-result :failed
+;; ====
+(defmacro testcover-testcase-nth-case (arg vec)
+ (declare (indent 1)
+ (debug (form (vector &rest form))))
+ `(eval (aref ,vec%%% ,arg%%%))%%%)
+
+(defun testcover-testcase-use-nth-case (choice val)
+ (testcover-testcase-nth-case choice
+ [(+ 1 val!!!)!!!
+ (- 1 val%%%)%%%
+ (* 7 val)
+ (/ 4 val!!!)!!!]))
+
+(should (eql 42 (testcover-testcase-use-nth-case 2 6)))
+(should (eql 49 (testcover-testcase-use-nth-case 2 7)))
+(should (eql 0 (testcover-testcase-use-nth-case 1 1 )))
+
+;; ==== mapcar-is-not-compose ====
+"Mapcar with 1value arguments is not 1value."
+:expected-result :failed
+;; ====
+(defvar testcover-testcase-num 0)
+(defun testcover-testcase-add-num (n)
+ (+ testcover-testcase-num n))
+(defun testcover-testcase-mapcar-sides ()
+ (mapcar 'testcover-testcase-add-num '(1 2 3)))
+
+(setq testcover-testcase-num 1)
+(should (equal (testcover-testcase-mapcar-sides) '(2 3 4)))
+(setq testcover-testcase-num 2)
+(should (equal (testcover-testcase-mapcar-sides) '(3 4 5)))
+
+;; ==== function-with-edebug-spec-bug-25316 ====
+"Functions can have edebug specs too.
+See c-make-font-lock-search-function for an example in the Emacs
+sources. The other issue is that it's ok to use quote in an
+edebug spec, so testcover needs to cope with that."
+:expected-result :failed
+;; ====
+(defun testcover-testcase-make-function (forms)
+ `(lambda (flag) (if flag 0 ,@forms%%%))%%%)
+
+(def-edebug-spec testcover-testcase-make-function
+ (("quote" (&rest def-form))))
+
+(defun testcover-testcase-thing ()
+ (testcover-testcase-make-function '((+ 1 (+ 2 (+ 3 (+ 4 5))))))%%%)
+
+(defun testcover-testcase-use-thing ()
+ (funcall (testcover-testcase-thing)%%% nil)%%%)
+
+(should (equal (testcover-testcase-use-thing) 15))
+
+;; ==== backquoted-dotted-alist ====
+"Testcover can instrument a dotted alist constructed with backquote."
+;; ====
+(defun testcover-testcase-make-alist (expr entries)
+ `((0 . ,expr%%%) . ,entries%%%)%%%)
+
+(should (equal (testcover-testcase-make-alist "foo" '((1 . "bar") (2 . "baz")))
+ '((0 . "foo") (1 . "bar") (2 . "baz"))))
+
+;; ==== coverage-of-the-unknown-symbol-bug-25471 ====
+"Testcover correctly records coverage of code which uses `unknown'"
+:expected-result :failed
+;; ====
+(defun testcover-testcase-how-do-i-know-you (name)
+ (let ((val 'unknown))
+ (when (equal name%%% "Bob")%%%
+ (setq val 'known)!!!)
+ val%%%)%%%)
+
+(should (eq (testcover-testcase-how-do-i-know-you "Liz") 'unknown))
+
+;; testcases.el ends here.
diff --git a/test/lisp/emacs-lisp/testcover-tests.el b/test/lisp/emacs-lisp/testcover-tests.el
new file mode 100644
index 00000000000..d31379c3aa2
--- /dev/null
+++ b/test/lisp/emacs-lisp/testcover-tests.el
@@ -0,0 +1,186 @@
+;;; testcover-tests.el --- Testcover test suite -*- lexical-binding:t -*-
+
+;; Copyright (C) 2017 Free Software Foundation, Inc.
+
+;; Author: Gemini Lasswell
+
+;; 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:
+
+;; Testcover test suite.
+;; * All the test cases are in testcover-resources/testcover-cases.el.
+;; See that file for an explanation of the test case format.
+;; * `testcover-tests-define-tests', which is run when this file is
+;; loaded, reads testcover-resources/testcover-cases.el and defines
+;; ERT tests for each test case.
+
+;;; Code:
+
+(require 'ert)
+(require 'testcover)
+(require 'skeleton)
+
+;; Use `eval-and-compile' around all these definitions because they're
+;; used by the macro `testcover-tests-define-tests'.
+
+(eval-and-compile
+ (defvar testcover-tests-file-dir
+ (expand-file-name
+ "testcover-resources/"
+ (file-name-directory (or (bound-and-true-p byte-compile-current-file)
+ load-file-name
+ buffer-file-name)))
+ "Directory of the \"testcover-tests.el\" file."))
+
+(eval-and-compile
+ (defvar testcover-tests-test-cases
+ (expand-file-name "testcases.el" testcover-tests-file-dir)
+ "File containing marked up code to instrument and check."))
+
+;; Convert Testcover's overlays to plain text.
+
+(eval-and-compile
+ (defun testcover-tests-markup-region (beg end &rest optargs)
+ "Mark up test code within region between BEG and END.
+Convert Testcover's tan and red splotches to %%% and !!! for
+testcases.el. This can be used to create test cases if Testcover
+is working correctly on a code sample. OPTARGS are optional
+arguments for `testcover-start'."
+ (interactive "r")
+ (let ((tempfile (make-temp-file "testcover-tests-" nil ".el"))
+ (code (buffer-substring beg end))
+ (marked-up-code))
+ (unwind-protect
+ (progn
+ (with-temp-file tempfile
+ (insert code))
+ (save-current-buffer
+ (let ((buf (find-file-noselect tempfile)))
+ (set-buffer buf)
+ (apply 'testcover-start (cons tempfile optargs))
+ (testcover-mark-all buf)
+ (dolist (overlay (overlays-in (point-min) (point-max)))
+ (let ((ov-face (overlay-get overlay 'face)))
+ (goto-char (overlay-end overlay))
+ (cond
+ ((eq ov-face 'testcover-nohits) (insert "!!!"))
+ ((eq ov-face 'testcover-1value) (insert "%%%"))
+ (t nil))))
+ (setq marked-up-code (buffer-string)))
+ (set-buffer-modified-p nil)))
+ (ignore-errors (kill-buffer (find-file-noselect tempfile)))
+ (ignore-errors (delete-file tempfile)))
+
+ ;; Now replace the original code with the marked up code.
+ (delete-region beg end)
+ (insert marked-up-code))))
+
+(eval-and-compile
+ (defun testcover-tests-unmarkup-region (beg end)
+ "Remove the markup used in testcases.el between BEG and END."
+ (interactive "r")
+ (save-excursion
+ (save-restriction
+ (narrow-to-region beg end)
+ (goto-char (point-min))
+ (while (re-search-forward "!!!\\|%%%" nil t)
+ (replace-match ""))))))
+
+(define-skeleton testcover-tests-skeleton
+ "Write a testcase for testcover-tests.el."
+ "Enter name of test: "
+ ";; ==== " str " ====\n"
+ "\"docstring\"\n"
+ ";; Directives for ERT should go here, if any.\n"
+ ";; ====\n"
+ ";; Replace this line with annotated test code.\n")
+
+;; Check a test case.
+
+(eval-and-compile
+ (defun testcover-tests-run-test-case (marked-up-code)
+ "Test the operation of Testcover on the string MARKED-UP-CODE."
+ (let ((tempfile (make-temp-file "testcover-tests-" nil ".el")))
+ (unwind-protect
+ (progn
+ (with-temp-file tempfile
+ (insert marked-up-code))
+ ;; Remove the marks and mark the code up again. The original
+ ;; and recreated versions should match.
+ (save-current-buffer
+ (set-buffer (find-file-noselect tempfile))
+ ;; Fail the test if the debugger tries to become active,
+ ;; which will happen if Testcover's reinstrumentation
+ ;; leaves an edebug-enter in the code. This will also
+ ;; prevent debugging these tests using Edebug.
+ (cl-letf (((symbol-function #'edebug-enter)
+ (lambda (&rest _args)
+ (ert-fail
+ (concat "Debugger invoked during test run "
+ "(possible edebug-enter not replaced)")))))
+ (dolist (byte-compile '(t nil))
+ (testcover-tests-unmarkup-region (point-min) (point-max))
+ (unwind-protect
+ (testcover-tests-markup-region (point-min) (point-max) byte-compile)
+ (set-buffer-modified-p nil))
+ (should (string= marked-up-code
+ (buffer-string)))))))
+ (ignore-errors (kill-buffer (find-file-noselect tempfile)))
+ (ignore-errors (delete-file tempfile))))))
+
+;; Convert test case file to ert-defmethod.
+
+(eval-and-compile
+ (defun testcover-tests-build-test-cases ()
+ "Parse the test case file and return a list of ERT test definitions.
+Construct and return a list of `ert-deftest' forms. See testcases.el
+for documentation of the test definition format."
+ (let (results)
+ (with-temp-buffer
+ (insert-file-contents testcover-tests-test-cases)
+ (goto-char (point-min))
+ (while (re-search-forward
+ (concat "^;; ==== \\([^ ]+?\\) ====\n"
+ "\\(\\(?:.*\n\\)*?\\)"
+ ";; ====\n"
+ "\\(\\(?:.*\n\\)*?\\)"
+ "\\(\\'\\|;; ====\\)")
+ nil t)
+ (let ((name (match-string 1))
+ (splice (car (read-from-string
+ (format "(%s)" (match-string 2)))))
+ (code (match-string 3)))
+ (push
+ `(ert-deftest ,(intern (concat "testcover-tests-" name)) ()
+ ,@splice
+ (testcover-tests-run-test-case ,code))
+ results))
+ (beginning-of-line)))
+ results)))
+
+;; Define all the tests.
+
+(defmacro testcover-tests-define-tests ()
+ "Construct and define ERT test methods using the test case file."
+ (let* ((test-cases (testcover-tests-build-test-cases)))
+ `(progn ,@test-cases)))
+
+(testcover-tests-define-tests)
+
+(provide 'testcover-tests)
+
+;;; testcover-tests.el ends here
diff --git a/test/lisp/faces-tests.el b/test/lisp/faces-tests.el
index a30ba25f8f0..2b3456d47f6 100644
--- a/test/lisp/faces-tests.el
+++ b/test/lisp/faces-tests.el
@@ -23,13 +23,18 @@
(require 'ert)
(require 'faces)
+(defgroup faces--test nil ""
+ :group 'faces--test)
+
(defface faces--test1
'((t :background "black" :foreground "black"))
- "")
+ ""
+ :group 'faces--test)
(defface faces--test2
'((t :box 1))
- "")
+ ""
+ :group 'faces--test)
(ert-deftest faces--test-color-at-point ()
(with-temp-buffer
diff --git a/test/lisp/ffap-tests.el b/test/lisp/ffap-tests.el
index a3fe3502461..827d751be69 100644
--- a/test/lisp/ffap-tests.el
+++ b/test/lisp/ffap-tests.el
@@ -44,7 +44,7 @@ index 3d7cebadcf..ad4b70d737 100644
str
(make-string ffap-max-region-length #xa)
(format "%s ENDS HERE" file)))
- (mark-whole-buffer)
+ (call-interactively 'mark-whole-buffer)
(should (equal "" (ffap-string-at-point)))
(should (equal '(1 1) ffap-string-at-point-region)))))
(and (file-exists-p file) (delete-file file)))))
diff --git a/test/lisp/filenotify-tests.el b/test/lisp/filenotify-tests.el
index d237d0cc06e..27434bcef20 100644
--- a/test/lisp/filenotify-tests.el
+++ b/test/lisp/filenotify-tests.el
@@ -36,6 +36,7 @@
;;; Code:
(require 'ert)
+(require 'ert-x)
(require 'filenotify)
(require 'tramp)
@@ -703,21 +704,19 @@ delivered."
(should auto-revert-notify-watch-descriptor)
;; Modify file. We wait for a second, in order to have
- ;; another timestamp.
- (with-current-buffer (get-buffer-create "*Messages*")
- (narrow-to-region (point-max) (point-max)))
- (sleep-for 1)
- (write-region
- "another text" nil file-notify--test-tmpfile nil 'no-message)
-
- ;; Check, that the buffer has been reverted.
- (with-current-buffer (get-buffer-create "*Messages*")
- (file-notify--wait-for-events
- timeout
- (string-match
+ ;; another timestamp.
+ (ert-with-message-capture captured-messages
+ (sleep-for 1)
+ (write-region
+ "another text" nil file-notify--test-tmpfile nil 'no-message)
+
+ ;; Check, that the buffer has been reverted.
+ (file-notify--wait-for-events
+ timeout
+ (string-match
(format-message "Reverting buffer `%s'." (buffer-name buf))
- (buffer-string))))
- (should (string-match "another text" (buffer-string)))
+ captured-messages))
+ (should (string-match "another text" (buffer-string))))
;; Stop file notification. Autorevert shall still work via polling.
(file-notify-rm-watch auto-revert-notify-watch-descriptor)
@@ -728,27 +727,24 @@ delivered."
;; Modify file. We wait for two seconds, in order to
;; have another timestamp. One second seems to be too
- ;; short.
- (with-current-buffer (get-buffer-create "*Messages*")
- (narrow-to-region (point-max) (point-max)))
- (sleep-for 2)
- (write-region
- "foo bla" nil file-notify--test-tmpfile nil 'no-message)
-
- ;; Check, that the buffer has been reverted.
- (with-current-buffer (get-buffer-create "*Messages*")
- (file-notify--wait-for-events
- timeout
- (string-match
- (format-message "Reverting buffer `%s'." (buffer-name buf))
- (buffer-string))))
- (should (string-match "foo bla" (buffer-string))))
+ ;; short.
+ (ert-with-message-capture captured-messages
+ (sleep-for 2)
+ (write-region
+ "foo bla" nil file-notify--test-tmpfile nil 'no-message)
+
+ ;; Check, that the buffer has been reverted.
+ (file-notify--wait-for-events
+ timeout
+ (string-match
+ (format-message "Reverting buffer `%s'." (buffer-name buf))
+ captured-messages))
+ (should (string-match "foo bla" (buffer-string)))))
;; The environment shall be cleaned up.
(file-notify--test-cleanup-p))
;; Cleanup.
- (with-current-buffer "*Messages*" (widen))
(ignore-errors (kill-buffer buf))
(file-notify--test-cleanup))))
@@ -850,6 +846,13 @@ delivered."
;; After deleting the parent directory, the descriptor must
;; not be valid anymore.
(should-not (file-notify-valid-p file-notify--test-desc))
+ ;; w32notify doesn't generate 'stopped' events when the parent
+ ;; directory is deleted, which doesn't provide a chance for
+ ;; filenotify.el to remove the descriptor from the internal
+ ;; hash table it maintains. So we must remove the descriptor
+ ;; manually.
+ (if (string-equal (file-notify--test-library) "w32notify")
+ (file-notify--rm-descriptor file-notify--test-desc))
;; The environment shall be cleaned up.
(file-notify--test-cleanup-p))
@@ -906,6 +909,8 @@ delivered."
(file-notify--test-timeout)
(not (file-notify-valid-p file-notify--test-desc)))
(should-not (file-notify-valid-p file-notify--test-desc))
+ (if (string-equal (file-notify--test-library) "w32notify")
+ (file-notify--rm-descriptor file-notify--test-desc))
;; The environment shall be cleaned up.
(file-notify--test-cleanup-p))
@@ -975,6 +980,8 @@ delivered."
(file-notify--test-read-event)
(delete-file file)))
(delete-directory file-notify--test-tmpfile)
+ (if (string-equal (file-notify--test-library) "w32notify")
+ (file-notify--rm-descriptor file-notify--test-desc))
;; The environment shall be cleaned up.
(file-notify--test-cleanup-p))
@@ -1184,6 +1191,9 @@ the file watch."
(delete-directory file-notify--test-tmpfile 'recursive))
(should-not (file-notify-valid-p file-notify--test-desc1))
(should-not (file-notify-valid-p file-notify--test-desc2))
+ (when (string-equal (file-notify--test-library) "w32notify")
+ (file-notify--rm-descriptor file-notify--test-desc1)
+ (file-notify--rm-descriptor file-notify--test-desc2))
;; The environment shall be cleaned up.
(file-notify--test-cleanup-p))
diff --git a/test/lisp/htmlfontify-tests.el b/test/lisp/htmlfontify-tests.el
index 15eb7c170c9..4a1d566e96c 100644
--- a/test/lisp/htmlfontify-tests.el
+++ b/test/lisp/htmlfontify-tests.el
@@ -30,5 +30,17 @@
(symbol-function
'htmlfontify-load-rgb-file))))
+(ert-deftest htmlfontify-bug25468 ()
+ "Tests that htmlfontify can be loaded even if no shell is
+available (Bug#25468)."
+ (should (equal (let ((process-environment
+ (cons "SHELL=/does/not/exist" process-environment)))
+ (call-process
+ (expand-file-name (invocation-name) (invocation-directory))
+ nil nil nil
+ "--quick" "--batch"
+ (concat "--load=" (locate-library "htmlfontify"))))
+ 0)))
+
(provide 'htmlfontify-tests)
;; htmlfontify-tests.el ends here
diff --git a/test/lisp/ibuffer-tests.el b/test/lisp/ibuffer-tests.el
index fb632e2073d..b9f7fe7cde8 100644
--- a/test/lisp/ibuffer-tests.el
+++ b/test/lisp/ibuffer-tests.el
@@ -23,6 +23,15 @@
(eval-when-compile
(require 'ibuf-macs))
+(defvar ibuffer-filter-groups)
+(defvar ibuffer-filtering-alist)
+(defvar ibuffer-filtering-qualifiers)
+(defvar ibuffer-save-with-custom)
+(defvar ibuffer-saved-filter-groups)
+(defvar ibuffer-saved-filters)
+(declare-function ibuffer-format-qualifier "ibuf-ext" (qualifier))
+(declare-function ibuffer-unary-operand "ibuf-ext" (filter))
+
(ert-deftest ibuffer-autoload ()
"Tests to see whether ibuffer has been autoloaded"
(skip-unless (not (featurep 'ibuf-ext)))
diff --git a/test/lisp/kmacro-tests.el b/test/lisp/kmacro-tests.el
new file mode 100644
index 00000000000..5124cbbf962
--- /dev/null
+++ b/test/lisp/kmacro-tests.el
@@ -0,0 +1,890 @@
+;;; kmacro-tests.el --- Tests for kmacro.el -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2017 Free Software Foundation, Inc.
+
+;; Author: Gemini Lasswell <gazally@runbox.com>
+
+;; 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/>.
+
+;;; Commentary:
+
+;;; Code:
+
+(require 'kmacro)
+(require 'ert)
+(require 'ert-x)
+
+;;; Test fixtures:
+
+(defmacro kmacro-tests-with-kmacro-clean-slate (&rest body)
+ "Create a clean environment for a kmacro test BODY to run in."
+ (declare (debug (body)))
+ `(cl-letf* ((kmacro-execute-before-append t)
+ (kmacro-ring-max 8)
+ (kmacro-repeat-no-prefix t)
+ (kmacro-call-repeat-key nil)
+ (kmacro-call-repeat-with-arg nil)
+
+ (kbd-macro-termination-hook nil)
+ (defining-kbd-macro nil)
+ (executing-kbd-macro nil)
+ (executing-kbd-macro-index 0)
+ (last-kbd-macro nil)
+
+ (kmacro-ring nil)
+
+ (kmacro-counter 0)
+ (kmacro-default-counter-format "%d")
+ (kmacro-counter-format "%d")
+ (kmacro-counter-format-start "%d")
+ (kmacro-counter-value-start 0)
+ (kmacro-last-counter 0)
+ (kmacro-initial-counter-value nil)
+
+ (kmacro-tests-macros nil)
+ (kmacro-tests-events nil)
+ (kmacro-tests-sequences nil))
+ (advice-add 'end-kbd-macro :after #'kmacro-tests-end-macro-advice)
+ (advice-add 'read-event :around #'kmacro-tests-read-event-advice )
+ (advice-add 'read-key-sequence :around #'kmacro-tests-read-key-sequence-advice)
+ (unwind-protect
+ (ert-with-test-buffer (:name "")
+ (switch-to-buffer (current-buffer))
+ ,@body)
+ (advice-remove 'read-key-sequence #'kmacro-tests-read-key-sequence-advice)
+ (advice-remove 'read-event #'kmacro-tests-read-event-advice)
+ (advice-remove 'end-kbd-macro #'kmacro-tests-end-macro-advice))))
+
+(defmacro kmacro-tests-deftest (name _args docstring &rest keys-and-body)
+ "Define a kmacro unit test.
+NAME is the name of the test, _ARGS should be nil, and DOCSTRING
+is required. To avoid having to duplicate ert's keyword parsing
+here, its keywords and values (if any) must be inside a list
+after the docstring, preceding the body, here combined with the
+body in KEYS-AND-BODY."
+ (declare (debug (&define name sexp stringp
+ [&optional (&rest &or [keywordp sexp])]
+ def-body))
+ (doc-string 3)
+ (indent 2))
+
+ (let* ((keys (when (and (listp (car keys-and-body))
+ (keywordp (caar keys-and-body)))
+ (car keys-and-body)))
+ (body (if keys (cdr keys-and-body)
+ keys-and-body)))
+ `(ert-deftest ,name ()
+ ,docstring ,@keys
+ (kmacro-tests-with-kmacro-clean-slate ,@body))))
+
+(defvar kmacro-tests-keymap
+ (let ((map (make-sparse-keymap)))
+ (dotimes (i 26)
+ (define-key map (string (+ ?a i)) 'self-insert-command))
+ (dotimes (i 10)
+ (define-key map (string (+ ?0 i)) 'self-insert-command))
+ ;; Define a few key sequences of different lengths.
+ (dolist (item '(("\C-a" . beginning-of-line)
+ ("\C-b" . backward-char)
+ ("\C-e" . end-of-line)
+ ("\C-f" . forward-char)
+ ("\C-r" . isearch-backward)
+ ("\C-u" . universal-argument)
+ ("\C-w" . kill-region)
+ ("\C-SPC" . set-mark-command)
+ ("\M-w" . kill-ring-save)
+ ("\M-x" . execute-extended-command)
+ ("\C-cd" . downcase-word)
+ ("\C-cxu" . upcase-word)
+ ("\C-cxq" . quoted-insert)
+ ("\C-cxi" . kmacro-insert-counter)
+ ("\C-x\C-k" . kmacro-keymap)))
+ (define-key map (car item) (cdr item)))
+ map)
+ "Keymap to use for testing keyboard macros.
+This is used to obtain consistent results even if tests are run
+in an environment with rebound keys.")
+
+(defvar kmacro-tests-events nil
+ "Input events used by the kmacro test in progress.")
+
+(defun kmacro-tests-read-event-advice (orig-func &rest args)
+ "Pop and return an event from `kmacro-tests-events'.
+Return the result of calling ORIG-FUNC with ARGS if
+`kmacro-tests-events' is empty, or if a keyboard macro is
+running."
+ (if (or executing-kbd-macro (null kmacro-tests-events))
+ (apply orig-func args)
+ (pop kmacro-tests-events)))
+
+(defvar kmacro-tests-sequences nil
+ "Input sequences used by the kmacro test in progress.")
+
+(defun kmacro-tests-read-key-sequence-advice (orig-func &rest args)
+ "Pop and return a string from `kmacro-tests-sequences'.
+Return the result of calling ORIG-FUNC with ARGS if
+`kmacro-tests-sequences' is empty, or if a keyboard macro is
+running."
+ (if (or executing-kbd-macro (null kmacro-tests-sequences))
+ (apply orig-func args)
+ (pop kmacro-tests-sequences)))
+
+(defvar kmacro-tests-macros nil
+ "Keyboard macros (in vector form) used by the kmacro test in progress.")
+
+(defun kmacro-tests-end-macro-advice (&rest _args)
+ "Pop a macro from `kmacro-tests-macros' and assign it to `last-kbd-macro'.
+If `kmacro-tests-macros' is empty, do nothing."
+ (when kmacro-tests-macros
+ (setq last-kbd-macro (pop kmacro-tests-macros))))
+
+;;; Some more powerful expectations:
+
+(defmacro kmacro-tests-should-insert (value &rest body)
+ "Verify that VALUE is inserted by the execution of BODY.
+Execute BODY, then check that the string VALUE was inserted
+into the current buffer at point."
+ (declare (debug (stringp body))
+ (indent 1))
+ (let ((g-p (cl-gensym))
+ (g-bsize (cl-gensym)))
+ `(let ((,g-p (point))
+ (,g-bsize (buffer-size)))
+ ,@body
+ (should (equal (buffer-substring ,g-p (point)) ,value))
+ (should (equal (- (buffer-size) ,g-bsize) (length ,value))))))
+
+(defmacro kmacro-tests-should-match-message (value &rest body)
+ "Verify that a message matching VALUE is issued while executing BODY.
+Execute BODY, and then if there is not a regexp match between
+VALUE and any text written to *Messages* during the execution,
+cause the current test to fail."
+ (declare (debug (form body))
+ (indent 1))
+ (let ((g-captured-messages (cl-gensym)))
+ `(ert-with-message-capture ,g-captured-messages
+ ,@body
+ (should (string-match-p ,value ,g-captured-messages)))))
+
+;;; Tests:
+
+(kmacro-tests-deftest kmacro-tests-test-insert-counter-01-nil ()
+ "`kmacro-insert-counter' adds one to macro counter with nil arg."
+ (kmacro-tests-should-insert "0"
+ (kmacro-tests-simulate-command '(kmacro-insert-counter nil)))
+ (kmacro-tests-should-insert "1"
+ (kmacro-tests-simulate-command '(kmacro-insert-counter nil))))
+
+(kmacro-tests-deftest kmacro-tests-test-insert-counter-02-int ()
+ "`kmacro-insert-counter' increments by value of list argument."
+ (kmacro-tests-should-insert "0"
+ (kmacro-tests-simulate-command '(kmacro-insert-counter 2)))
+ (kmacro-tests-should-insert "2"
+ (kmacro-tests-simulate-command '(kmacro-insert-counter 3)))
+ (kmacro-tests-should-insert "5"
+ (kmacro-tests-simulate-command '(kmacro-insert-counter nil))))
+
+(kmacro-tests-deftest kmacro-tests-test-insert-counter-03-list ()
+ "`kmacro-insert-counter' doesn't increment when given universal argument."
+ (kmacro-tests-should-insert "0"
+ (kmacro-tests-simulate-command '(kmacro-insert-counter (16))))
+ (kmacro-tests-should-insert "0"
+ (kmacro-tests-simulate-command '(kmacro-insert-counter (4)))))
+
+(kmacro-tests-deftest kmacro-tests-test-insert-counter-04-neg ()
+ "`kmacro-insert-counter' decrements with '- prefix argument"
+ (kmacro-tests-should-insert "0"
+ (kmacro-tests-simulate-command '(kmacro-insert-counter -)))
+ (kmacro-tests-should-insert "-1"
+ (kmacro-tests-simulate-command '(kmacro-insert-counter nil))))
+
+(kmacro-tests-deftest kmacro-tests-test-start-format-counter ()
+ "`kmacro-insert-counter' uses start value and format."
+ (kmacro-tests-simulate-command '(kmacro-set-counter 10))
+ (kmacro-tests-should-insert "10"
+ (kmacro-tests-simulate-command '(kmacro-insert-counter nil)))
+ (kmacro-tests-should-insert "11"
+ (kmacro-tests-simulate-command '(kmacro-insert-counter nil)))
+ (kmacro-set-format "c=%s")
+ (kmacro-tests-simulate-command '(kmacro-set-counter 50))
+ (kmacro-tests-should-insert "c=50"
+ (kmacro-tests-simulate-command '(kmacro-insert-counter nil))))
+
+(kmacro-tests-deftest kmacro-tests-test-start-macro-when-defining-macro ()
+ "Starting a macro while defining a macro does not start a second macro."
+ (kmacro-tests-simulate-command '(kmacro-start-macro nil))
+ ;; We should now be in the macro-recording state.
+ (should defining-kbd-macro)
+ (should-not last-kbd-macro)
+ ;; Calling it again should leave us in the same state.
+ (kmacro-tests-simulate-command '(kmacro-start-macro nil))
+ (should defining-kbd-macro)
+ (should-not last-kbd-macro))
+
+
+(kmacro-tests-deftest kmacro-tests-set-macro-counter-while-defining ()
+ "Use of the prefix arg with kmacro-start sets kmacro-counter."
+ ;; Give kmacro-start-macro an argument.
+ (kmacro-tests-simulate-command '(kmacro-start-macro 5))
+ (should defining-kbd-macro)
+ ;; Verify that the counter is set to that value.
+ (kmacro-tests-should-insert "5"
+ (kmacro-tests-simulate-command '(kmacro-insert-counter nil)))
+ ;; Change it while defining a macro.
+ (kmacro-tests-simulate-command '(kmacro-set-counter 1))
+ (kmacro-tests-should-insert "1"
+ (kmacro-tests-simulate-command '(kmacro-insert-counter nil)))
+ ;; Using universal arg to to set counter should reset to starting value.
+ (kmacro-tests-simulate-command '(kmacro-set-counter (4)) '(4))
+ (kmacro-tests-should-insert "5"
+ (kmacro-tests-simulate-command '(kmacro-insert-counter nil))))
+
+
+(kmacro-tests-deftest kmacro-tests-start-insert-counter-appends-to-macro ()
+ "Use of the universal arg appends to the previous macro."
+ (let ((kmacro-tests-macros (list (string-to-vector "hello"))))
+ ;; Start recording a macro.
+ (kmacro-tests-simulate-command '(kmacro-start-macro-or-insert-counter nil))
+ ;; Make sure we are recording.
+ (should defining-kbd-macro)
+ ;; Call it again and it should insert the counter.
+ (kmacro-tests-should-insert "0"
+ (kmacro-tests-simulate-command '(kmacro-start-macro-or-insert-counter nil)))
+ ;; We should still be in the recording state.
+ (should defining-kbd-macro)
+ ;; End recording with repeat count.
+ (kmacro-tests-simulate-command '(kmacro-end-or-call-macro 3))
+ ;; Recording should be finished.
+ (should-not defining-kbd-macro)
+ ;; Now use prefix arg to append to the previous macro.
+ ;; This should run the previous macro first.
+ (kmacro-tests-should-insert "hello"
+ (kmacro-tests-simulate-command
+ '(kmacro-start-macro-or-insert-counter (4))))
+ ;; Verify that the recording state has changed.
+ (should (equal defining-kbd-macro 'append))))
+
+(kmacro-tests-deftest kmacro-tests-end-call-macro-prefix-args ()
+ "kmacro-end-call-macro changes behavior based on prefix arg."
+ ;; "Record" two macros.
+ (dotimes (i 2)
+ (kmacro-tests-define-macro (vconcat (format "macro #%d" (1+ i)))))
+ ;; With no prefix arg, it should call the second macro.
+ (kmacro-tests-should-insert "macro #2"
+ (kmacro-tests-simulate-command '(kmacro-end-or-call-macro nil)))
+ ;; With universal arg, it should call the first one.
+ (kmacro-tests-should-insert "macro #1"
+ (kmacro-tests-simulate-command '(kmacro-end-or-call-macro (4)))))
+
+(kmacro-tests-deftest kmacro-tests-end-and-call-macro ()
+ "Keyboard command to end and call macro works under various conditions."
+ ;; First, try it with no macro to record.
+ (setq kmacro-tests-macros '(""))
+ (kmacro-tests-simulate-command '(kmacro-start-macro nil))
+ (condition-case err
+ (kmacro-tests-simulate-command '(kmacro-end-and-call-macro 2) 2)
+ (error (should (string= (cadr err)
+ "No kbd macro has been defined"))))
+
+ ;; Check that it stopped defining and that no macro was recorded.
+ (should-not defining-kbd-macro)
+ (should-not last-kbd-macro)
+
+ ;; Now try it while not recording, but first record a non-nil macro.
+ (kmacro-tests-define-macro "macro")
+ (kmacro-tests-should-insert "macro"
+ (kmacro-tests-simulate-command '(kmacro-end-and-call-macro nil))))
+
+(kmacro-tests-deftest kmacro-tests-end-and-call-macro-mouse ()
+ "Commands to end and call macro work under various conditions.
+This is a regression test for Bug#24992."
+ (:expected-result :failed)
+ (cl-letf (((symbol-function #'mouse-set-point) #'ignore))
+ ;; First, try it with no macro to record.
+ (setq kmacro-tests-macros '(""))
+ (kmacro-tests-simulate-command '(kmacro-start-macro nil))
+ (condition-case err
+ (kmacro-tests-simulate-command '(kmacro-end-call-mouse 2) 2)
+ (error (should (string= (cadr err)
+ "No kbd macro has been defined"))))
+
+ ;; Check that it stopped defining and that no macro was recorded.
+ (should-not defining-kbd-macro)
+ (should-not last-kbd-macro)
+
+ ;; Now try it while not recording, but first record a non-nil macro.
+ (kmacro-tests-define-macro "macro")
+ (kmacro-tests-should-insert "macro"
+ (kmacro-tests-simulate-command '(kmacro-end-call-mouse nil)))))
+
+(kmacro-tests-deftest kmacro-tests-call-macro-hint-and-repeat ()
+ "`kmacro-call-macro' gives hint in Messages and sets up repeat keymap.
+This is a regression test for: Bug#3412, Bug#11817."
+ (kmacro-tests-define-macro [?m])
+ (let ((kmacro-call-repeat-key t)
+ (kmacro-call-repeat-with-arg t)
+ (overriding-terminal-local-map overriding-terminal-local-map)
+ (last-input-event ?e))
+ (message "") ; Clear the echo area. (Bug#3412)
+ (kmacro-tests-should-match-message "Type e to repeat macro"
+ (kmacro-tests-should-insert "mmmmmm"
+ (cl-letf (((symbol-function #'this-single-command-keys) (lambda ()
+ [?\C-x ?e])))
+ (kmacro-call-macro 3))
+ ;; Check that it set up for repeat, and run the repeat.
+ (funcall (lookup-key overriding-terminal-local-map "e"))))))
+
+(kmacro-tests-deftest
+ kmacro-tests-run-macro-command-recorded-in-macro ()
+ "No infinite loop if `kmacro-end-and-call-macro' is recorded in the macro.
+\(Bug#15126)"
+ (:expected-result :failed)
+ (ert-skip "Skipping due to Bug#24921 (an ERT bug)")
+ (kmacro-tests-define-macro (vconcat "foo" [return] "\M-x"
+ "kmacro-end-and-call-macro"))
+ (use-local-map kmacro-tests-keymap)
+ (kmacro-tests-simulate-command '(kmacro-end-and-call-macro nil)))
+
+
+(kmacro-tests-deftest kmacro-tests-test-ring-2nd-commands ()
+ "2nd macro in ring is displayed and executed normally and on repeat."
+ (use-local-map kmacro-tests-keymap)
+ ;; Record one macro, with count.
+ (push (vconcat "\C-cxi" "\C-u\C-cxi") kmacro-tests-macros)
+ (kmacro-tests-simulate-command '(kmacro-start-macro 1))
+ (kmacro-tests-simulate-command '(kmacro-end-macro nil))
+ ;; Check that execute and display do nothing with no 2nd macro.
+ (kmacro-tests-should-insert ""
+ (kmacro-tests-simulate-command '(kmacro-call-ring-2nd nil)))
+ (kmacro-tests-should-match-message "Only one keyboard macro defined"
+ (kmacro-tests-simulate-command '(kmacro-view-ring-2nd)))
+ ;; Record another one, with format.
+ (kmacro-set-format "=%d=")
+ (kmacro-tests-define-macro (vconcat "bar"))
+ ;; Execute the first one, mocked up to insert counter.
+ ;; Should get default format.
+ (kmacro-tests-should-insert "11"
+ (kmacro-tests-simulate-command '(kmacro-call-ring-2nd nil)))
+ ;; Now display the 2nd ring macro and check result.
+ (kmacro-tests-should-match-message "C-c x i C-u C-c x i"
+ (kmacro-view-ring-2nd)))
+
+(kmacro-tests-deftest kmacro-tests-fill-ring-and-rotate ()
+ "Macro ring can shift one way, shift the other way, swap and pop."
+ (cl-letf ((kmacro-ring-max 4))
+ ;; Record enough macros that the first one drops off the history.
+ (dotimes (n (1+ kmacro-ring-max))
+ (kmacro-tests-define-macro (make-vector (1+ n) (+ ?a n))))
+ ;; Cycle the ring and check that #2 comes up.
+ (kmacro-tests-should-match-message "2*b"
+ (kmacro-tests-simulate-command '(kmacro-cycle-ring-next nil)))
+ ;; Execute the current macro and check arguments.
+ (kmacro-tests-should-insert "bbbb"
+ (kmacro-call-macro 2 t))
+ ;; Cycle the ring the other way; #5 expected.
+ (kmacro-tests-should-match-message "5*e" (kmacro-cycle-ring-previous nil))
+ ;; Swapping the top two should give #4.
+ (kmacro-tests-should-match-message "4*d" (kmacro-swap-ring))
+ ;; Delete the top and expect #5.
+ (kmacro-tests-should-match-message "5*e" (kmacro-delete-ring-head))))
+
+
+(kmacro-tests-deftest kmacro-tests-test-ring-commands-when-no-macros ()
+ "Ring commands give appropriate message when no macros exist."
+ (dolist (cmd '((kmacro-cycle-ring-next nil)
+ (kmacro-cycle-ring-previous nil)
+ (kmacro-swap-ring)
+ (kmacro-delete-ring-head)
+ (kmacro-view-ring-2nd)
+ (kmacro-call-ring-2nd nil)
+ (kmacro-view-macro)))
+ (kmacro-tests-should-match-message "No keyboard macro defined"
+ (kmacro-tests-simulate-command cmd))))
+
+(kmacro-tests-deftest kmacro-tests-repeat-on-last-key ()
+ "Kmacro commands can be run in sequence without prefix keys."
+ (let* ((prefix (where-is-internal 'kmacro-keymap nil t))
+ ;; Make a sequence of events to run.
+ ;; Comments are expected output of mock macros
+ ;; on the first and second run of the sequence (see below).
+ (events (mapcar #'kmacro-tests-get-kmacro-key
+ '(kmacro-end-or-call-macro-repeat ;c / b
+ kmacro-end-or-call-macro-repeat ;c / b
+ kmacro-call-ring-2nd-repeat ;b / a
+ kmacro-cycle-ring-next
+ kmacro-end-or-call-macro-repeat ;a / a
+ kmacro-cycle-ring-previous
+ kmacro-end-or-call-macro-repeat ;c / b
+ kmacro-delete-ring-head
+ kmacro-end-or-call-macro-repeat ;b / a
+ )))
+ (kmacro-tests-macros (list [?a] [?b] [?c]))
+ ;; What we want kmacro to see as keyboard command sequence
+ (first-event (seq-concatenate
+ 'vector
+ prefix
+ (vector (kmacro-tests-get-kmacro-key
+ 'kmacro-end-or-call-macro-repeat)))))
+ (cl-letf
+ ;; standardize repeat options
+ ((kmacro-repeat-no-prefix t)
+ (kmacro-call-repeat-key t)
+ (kmacro-call-repeat-with-arg nil))
+ ;; "Record" two macros
+ (dotimes (_n 2)
+ (kmacro-tests-simulate-command '(kmacro-start-macro nil))
+ (kmacro-tests-simulate-command '(kmacro-end-macro nil)))
+ ;; Start recording #3
+ (kmacro-tests-simulate-command '(kmacro-start-macro nil))
+
+ ;; Set up pending keyboard events and a fresh buffer
+ ;; kmacro-set-counter is not one of the repeating kmacro
+ ;; commands so it should end the sequence.
+ (let* ((end-key (kmacro-tests-get-kmacro-key 'kmacro-set-counter))
+ (kmacro-tests-events (append events (list end-key))))
+ (cl-letf (((symbol-function #'this-single-command-keys)
+ (lambda () first-event)))
+ (use-local-map kmacro-tests-keymap)
+ (kmacro-tests-should-insert "ccbacb"
+ ;; End #3 and launch loop to read events.
+ (kmacro-end-or-call-macro-repeat nil))))
+
+ ;; `kmacro-edit-macro-repeat' should also stop the sequence,
+ ;; so run it again with that at the end.
+ (let* ((end-key (kmacro-tests-get-kmacro-key 'kmacro-edit-macro-repeat))
+ (kmacro-tests-events (append events (list end-key))))
+ (cl-letf (((symbol-function #'edit-kbd-macro) #'ignore)
+ ((symbol-function #'this-single-command-keys)
+ (lambda () first-event)))
+ (use-local-map kmacro-tests-keymap)
+ (kmacro-tests-should-insert "bbbbbaaba"
+ (kmacro-end-or-call-macro-repeat 3)))))))
+
+(kmacro-tests-deftest kmacro-tests-repeat-view-and-run ()
+ "Kmacro view cycles through ring and executes macro just viewed."
+ (let* ((prefix (where-is-internal 'kmacro-keymap nil t))
+ (kmacro-tests-events
+ (mapcar #'kmacro-tests-get-kmacro-key
+ (append (make-list 5 'kmacro-view-macro-repeat)
+ '(kmacro-end-or-call-macro-repeat
+ kmacro-set-counter))))
+ ;; Make kmacro see this as keyboard command sequence.
+ (first-event (seq-concatenate
+ 'vector
+ prefix
+ (vector (kmacro-tests-get-kmacro-key
+ 'kmacro-view-macro-repeat))))
+ ;; Construct a regexp to match the messages which should be
+ ;; produced by repeated view-repeats.
+ (macros-regexp (apply #'concat
+ (mapcar (lambda (c) (format ".+%s\n" c))
+ '("d" "c" "b" "a" "d" "c")))))
+ (cl-letf ((kmacro-repeat-no-prefix t)
+ (kmacro-call-repeat-key t)
+ (kmacro-call-repeat-with-arg nil)
+ ((symbol-function #'this-single-command-keys) (lambda ()
+ first-event)))
+ ;; "Record" some macros.
+ (dotimes (n 4)
+ (kmacro-tests-define-macro (make-vector 1 (+ ?a n))))
+
+ (use-local-map kmacro-tests-keymap)
+ ;; 6 views (the direct call plus the 5 in events) should
+ ;; cycle through the ring and get to the second-to-last
+ ;; macro defined.
+ (kmacro-tests-should-insert "c"
+ (kmacro-tests-should-match-message macros-regexp
+ (kmacro-tests-simulate-command '(kmacro-view-macro-repeat nil)))))))
+
+(kmacro-tests-deftest kmacro-tests-bind-to-key-when-recording ()
+ "Bind to key doesn't bind a key during macro recording."
+ (cl-letf ((global-map global-map)
+ (saved-binding (key-binding "\C-a"))
+ (kmacro-tests-sequences (list "\C-a")))
+ (kmacro-tests-simulate-command '(kmacro-start-macro 1))
+ (kmacro-bind-to-key nil)
+ (should (eq saved-binding (key-binding "\C-a")))))
+
+(kmacro-tests-deftest kmacro-tests-name-or-bind-to-key-when-no-macro ()
+ "Bind to key, symbol or register fails when when no macro exists."
+ (should-error (kmacro-bind-to-key nil))
+ (should-error (kmacro-name-last-macro 'kmacro-tests-symbol-for-test))
+ (should-error (kmacro-to-register)))
+
+(kmacro-tests-deftest kmacro-tests-bind-to-key-bad-key-sequence ()
+ "Bind to key fails to bind to ^G."
+ (let ((global-map global-map)
+ (saved-binding (key-binding "\C-g"))
+ (kmacro-tests-sequences (list "\C-g")))
+ (kmacro-tests-define-macro [1])
+ (kmacro-bind-to-key nil)
+ (should (eq saved-binding (key-binding "\C-g")))))
+
+(kmacro-tests-deftest kmacro-tests-bind-to-key-with-key-sequence-in-use ()
+ "Bind to key respects yes-or-no-p when given already bound key sequence."
+ (kmacro-tests-define-macro (vconcat "abaab"))
+ (let ((global-map global-map)
+ (map (make-sparse-keymap))
+ (kmacro-tests-sequences (make-list 2 "\C-hi")))
+ (define-key map "\C-hi" 'info)
+ (use-local-map map)
+ ;; Try the command with yes-or-no-p set up to say no.
+ (cl-letf (((symbol-function #'yes-or-no-p)
+ (lambda (prompt)
+ (should (string-match-p "info" prompt))
+ (should (string-match-p "C-h i" prompt))
+ nil)))
+ (kmacro-bind-to-key nil))
+
+ (should (equal (where-is-internal 'info nil t)
+ (vconcat "\C-hi")))
+ ;; Try it again with yes.
+ (cl-letf (((symbol-function #' yes-or-no-p)
+ (lambda (_prompt) t)))
+ (kmacro-bind-to-key nil))
+
+ (should-not (equal (where-is-internal 'info global-map t)
+ (vconcat "\C-hi")))
+ (use-local-map nil)
+ (kmacro-tests-should-insert "abaab"
+ (funcall (key-binding "\C-hi")))))
+
+(kmacro-tests-deftest kmacro-tests-kmacro-bind-to-single-key ()
+ "Bind to key uses C-x C-k A when asked to bind to A."
+ (let ((global-map global-map)
+ (kmacro-tests-macros (list (string-to-vector "\C-cxi"))))
+ (use-local-map kmacro-tests-keymap)
+
+ ;; Record a macro with counter and format set.
+ (kmacro-set-format "<%d>")
+ (kmacro-tests-simulate-command '(kmacro-start-macro-or-insert-counter 5))
+ (kmacro-tests-simulate-command '(kmacro-end-macro nil))
+
+ (let ((kmacro-tests-sequences (list "A")))
+ (kmacro-bind-to-key nil))
+
+ ;; Record a second macro with different counter and format.
+ (kmacro-set-format "%d")
+ (kmacro-tests-define-macro [2])
+
+ ;; Check the bound key and run it and verify correct counter
+ ;; and format.
+ (should (equal (string-to-vector "\C-cxi")
+ (car (kmacro-extract-lambda
+ (key-binding "\C-x\C-kA")))))
+ (kmacro-tests-should-insert "<5>"
+ (funcall (key-binding "\C-x\C-kA")))))
+
+(kmacro-tests-deftest kmacro-tests-name-last-macro-unable-to-bind ()
+ "Name last macro won't bind to symbol which is already bound."
+ (kmacro-tests-define-macro [1])
+ ;; Set up a test symbol which looks like a function.
+ (setplist 'kmacro-tests-symbol-for-test nil)
+ (fset 'kmacro-tests-symbol-for-test #'ignore)
+ (should-error (kmacro-name-last-macro 'kmacro-tests-symbol-for-test))
+ ;; The empty string symbol also can't be bound.
+ (should-error (kmacro-name-last-macro (make-symbol ""))))
+
+(kmacro-tests-deftest kmacro-tests-name-last-macro-bind-and-rebind ()
+ "Name last macro can rebind a symbol it binds."
+ ;; Make sure our symbol is unbound.
+ (when (fboundp 'kmacro-tests-symbol-for-test)
+ (fmakunbound 'kmacro-tests-symbol-for-test))
+ (setplist 'kmacro-tests-symbol-for-test nil)
+ ;; Make two macros and bind them to the same symbol.
+ (dotimes (i 2)
+ (kmacro-tests-define-macro (make-vector (1+ i) (+ ?a i)))
+ (kmacro-name-last-macro 'kmacro-tests-symbol-for-test)
+ (should (fboundp 'kmacro-tests-symbol-for-test)))
+
+ ;; Now run the function bound to the symbol. Result should be the
+ ;; second macro.
+ (kmacro-tests-should-insert "bb"
+ (kmacro-tests-simulate-command '(kmacro-tests-symbol-for-test))))
+
+(kmacro-tests-deftest kmacro-tests-store-in-register ()
+ "Macro can be stored in and retrieved from a register."
+ (use-local-map kmacro-tests-keymap)
+ ;; Save and restore register 200 so we can use it for the test.
+ (let ((saved-reg-contents (get-register 200)))
+ (unwind-protect
+ (progn
+ ;; Define a macro, and save it to a register.
+ (kmacro-tests-define-macro (vconcat "a\C-a\C-cxu"))
+ (kmacro-to-register 200)
+ ;; Then make a new different macro.
+ (kmacro-tests-define-macro (vconcat "bb\C-a\C-cxu"))
+ ;; When called from the register, result should be first macro.
+ (kmacro-tests-should-insert "AAA"
+ (kmacro-tests-simulate-command '(jump-to-register 200 3) 3))
+ (kmacro-tests-should-insert "a C-a C-c x u"
+ (kmacro-tests-simulate-command '(insert-register 200 t) '(4))))
+ (set-register 200 saved-reg-contents))))
+
+(kmacro-tests-deftest kmacro-tests-step-edit-act ()
+ "Step-edit steps-through a macro with act and act-repeat."
+ (kmacro-tests-run-step-edit "he\C-u2lo"
+ :events (make-list 6 'act)
+ :result "hello"
+ :macro-result "he\C-u2lo")
+
+ (kmacro-tests-run-step-edit "f\C-aoo\C-abar"
+ :events (make-list 5 'act-repeat)
+ :result "baroof"
+ :macro-result "f\C-aoo\C-abar"))
+
+(kmacro-tests-deftest kmacro-tests-step-edit-skip ()
+ "Step-editing can skip parts of macro."
+ (kmacro-tests-run-step-edit "ofoofff"
+ :events '(skip skip-keep skip-keep skip-keep
+ skip-rest)
+ :result ""
+ :macro-result "foo"))
+
+(kmacro-tests-deftest kmacro-tests-step-edit-quit ()
+ "Quit while step-editing leaves macro unchanged."
+ (kmacro-tests-run-step-edit "bar"
+ :events '(help insert skip help quit)
+ :sequences '("f" "o" "o" "\C-j")
+ :result "foo"
+ :macro-result "bar"))
+
+(kmacro-tests-deftest kmacro-tests-step-insert ()
+ "Step edit can insert in macro."
+ (kmacro-tests-run-step-edit "fbazbop"
+ :events '(insert act insert-1 act-repeat)
+ :sequences '("o" "o" "\C-a" "\C-j" "\C-e")
+ :result "foobazbop"
+ :macro-result "oo\C-af\C-ebazbop"))
+
+(kmacro-tests-deftest kmacro-tests-step-edit-replace-digit-argument ()
+ "Step-edit replace can replace a numeric argument in a macro.
+This is a regression for item 1 in Bug#24991."
+ (:expected-result :failed)
+ (kmacro-tests-run-step-edit "\C-u3b\C-a\C-cxu"
+ :events '(act replace automatic)
+ :sequences '("8" "x" "\C-j")
+ :result "XXXXXXXX"
+ :macro-result "\C-u8x\C-a\C-cxu"))
+
+(kmacro-tests-deftest kmacro-tests-step-edit-replace ()
+ "Step-edit replace and replace-1 can replace parts of a macro."
+ (kmacro-tests-run-step-edit "a\C-a\C-cxu"
+ :events '(act act replace)
+ :sequences '("b" "c" "\C-j")
+ :result "bca"
+ :macro-result "a\C-abc")
+ (kmacro-tests-run-step-edit "a\C-a\C-cxucd"
+ :events '(act replace-1 automatic)
+ :sequences '("b")
+ :result "abcd"
+ :macro-result "ab\C-cxucd")
+ (kmacro-tests-run-step-edit "by"
+ :events '(act replace)
+ :sequences '("a" "r" "\C-j")
+ :result "bar"
+ :macro-result "bar"))
+
+(kmacro-tests-deftest kmacro-tests-step-edit-append ()
+ "Step edit append inserts after point, and append-end inserts at end."
+ (kmacro-tests-run-step-edit "f-b"
+ :events '(append append-end)
+ :sequences '("o" "o" "\C-j" "a" "r" "\C-j")
+ :result "foo-bar"
+ :macro-result "foo-bar")
+ (kmacro-tests-run-step-edit "x"
+ :events '(append)
+ :sequences '("\C-a" "\C-cxu" "\C-e" "y" "\C-j")
+ :result "Xy"
+ :macro-result "x\C-a\C-cxu\C-ey"))
+
+(kmacro-tests-deftest kmacro-tests-append-end-at-end-appends ()
+ "Append-end when already at end of macro appends to end of macro.
+This is a regression for item 2 in Bug#24991."
+ (:expected-result :failed)
+ (kmacro-tests-run-step-edit "x"
+ :events '(append-end)
+ :sequences '("\C-a" "\C-cxu" "\C-e" "y" "\C-j")
+ :result "Xy"
+ :macro-result "x\C-a\C-cxu\C-ey"))
+
+
+(kmacro-tests-deftest kmacro-tests-step-edit-skip-entire ()
+ "Skipping a whole macro in step-edit leaves macro unchanged.
+This is a regression for item 3 in Bug#24991."
+ (:expected-result :failed)
+ (kmacro-tests-run-step-edit "xyzzy"
+ :events '(skip-rest)
+ :result ""
+ :macro-result "xyzzy"))
+
+(kmacro-tests-deftest kmacro-tests-step-edit-step-through-negative-argument ()
+ "Step edit works on macros using negative universal argument.
+This is a regression for item 4 in Bug#24991."
+ (:expected-result :failed)
+ (kmacro-tests-run-step-edit "boo\C-u-\C-cu"
+ :events '(act-repeat automatic)
+ :result "BOO"
+ :macro-result "boo\C-u-\C-cd"))
+
+(kmacro-tests-deftest kmacro-tests-step-edit-with-quoted-insert ()
+ "Stepping through a macro that uses quoted insert leaves macro unchanged.
+This is a regression for item 5 in Bug#24991."
+ (:expected-result :failed)
+ (let ((read-quoted-char-radix 8))
+ (kmacro-tests-run-step-edit "\C-cxq17051i there"
+ :events '(act automatic)
+ :result "ḩi there"
+ :macro-result "\C-cxq17051i there")
+ (kmacro-tests-run-step-edit "g\C-cxq17051i"
+ :events '(act insert-1 automatic)
+ :sequences '("-")
+ :result "g-ḩi"
+ :macro-result "g-\C-cxq17051i")))
+
+(kmacro-tests-deftest kmacro-tests-step-edit-can-replace-meta-keys ()
+ "Replacing C-w with M-w produces the expected result.
+This is a regression for item 7 in Bug#24991."
+ (:expected-result :failed)
+ (kmacro-tests-run-step-edit "abc\C-b\C-b\C-SPC\C-f\C-w\C-e\C-y"
+ :events '(act-repeat act-repeat
+ act-repeat act-repeat
+ replace automatic)
+ :sequences '("\M-w" "\C-j")
+ :result "abcb"
+ :macro-result "abc\C-b\C-b\C-SPC\C-f\M-w\C-e\C-y")
+ (kmacro-tests-should-insert "abcb" (kmacro-call-macro nil)))
+
+(kmacro-tests-deftest kmacro-tests-step-edit-ignores-qr-map-commands ()
+ "Unimplemented commands from `query-replace-map' are ignored."
+ (kmacro-tests-run-step-edit "yep"
+ :events '(edit-replacement
+ act-and-show act-and-exit
+ delete-and-edit
+ recenter backup
+ scroll-up scroll-down
+ scroll-other-window
+ scroll-other-window-down
+ exit-prefix
+ act act act)
+ :result "yep"
+ :macro-result "yep"))
+
+(kmacro-tests-deftest
+ kmacro-tests-step-edit-edits-macro-with-extended-command ()
+ "Step-editing a macro which uses the minibuffer can change the macro."
+ (let ((mac (vconcat [?\M-x] "eval-expression" '[return]
+ "(insert-char (+ ?a \C-e" [?1] "))" '[return]))
+ (mac-after (vconcat [?\M-x] "eval-expression" '[return]
+ "(insert-char (+ ?a \C-e" [?2] "))" '[return])))
+
+ (kmacro-tests-run-step-edit mac
+ :events '(act act-repeat
+ act act-repeat act
+ replace-1 act-repeat act)
+ :sequences '("2")
+ :result "c"
+ :macro-result mac-after)))
+
+(kmacro-tests-deftest kmacro-tests-step-edit-step-through-isearch ()
+ "Step-editing can edit a macro which uses `isearch-backward' (Bug#22488)."
+ (:expected-result :failed)
+ (let ((mac (vconcat "test Input" '[return]
+ [?\C-r] "inp" '[return] "\C-cxu"))
+ (mac-after (vconcat "test input" '[return]
+ [?\C-r] "inp" '[return] "\C-cd")))
+
+ (kmacro-tests-run-step-edit mac
+ :events '(act-repeat act act
+ act-repeat act
+ replace-1)
+ :sequences '("\C-cd")
+ :result "test input\n"
+ :macro-result mac-after)))
+
+(kmacro-tests-deftest kmacro-tests-step-edit-cleans-up-hook ()
+ "Step-editing properly cleans up `post-command-hook.' (Bug #18708)"
+ (:expected-result :failed)
+ (let (post-command-hook)
+ (setq-local post-command-hook '(t))
+ (kmacro-tests-run-step-edit "x"
+ :events '(act)
+ :result "x"
+ :macro-result "x")
+ (kmacro-tests-simulate-command '(beginning-of-line))))
+
+(cl-defun kmacro-tests-run-step-edit
+ (macro &key events sequences result macro-result)
+ "Set up and run a test of `kmacro-step-edit-macro'.
+
+Run `kmacro-step-edit-macro' with MACRO defined as a keyboard macro
+and `read-event' and `read-key-sequence' set up to return items from
+EVENTS and SEQUENCES respectively. SEQUENCES may be nil, but
+EVENTS should not be. EVENTS should be a list of symbols bound
+in `kmacro-step-edit-map' or `query-replace' map, and this function
+will do the keymap lookup for you. SEQUENCES should contain
+return values for `read-key-sequence'.
+
+Before running the macro, the current buffer will be erased.
+RESULT is the string that should be inserted during the
+step-editing process, and MACRO-RESULT is the expected value of
+`last-kbd-macro' after the editing is complete."
+
+ (let* ((kmacro-tests-events (mapcar #'kmacro-tests-get-kmacro-step-edit-key events))
+ (kmacro-tests-sequences sequences))
+
+ (kmacro-tests-define-macro (string-to-vector macro))
+ (use-local-map kmacro-tests-keymap)
+ (erase-buffer)
+ (kmacro-step-edit-macro)
+ (when result
+ (should (equal result (buffer-string))))
+ (when macro-result
+ (should (equal last-kbd-macro (string-to-vector macro-result))))))
+
+;;; Utilities:
+
+(defun kmacro-tests-simulate-command (command &optional arg)
+ "Call `ert-simulate-command' after setting `current-prefix-arg'.
+Sets `current-prefix-arg' to ARG if it is non-nil, otherwise to
+the second element of COMMAND, before executing COMMAND using
+`ert-simulate-command'."
+ (let ((current-prefix-arg (or arg (cadr command))))
+ (ert-simulate-command command)))
+
+(defun kmacro-tests-define-macro (mac)
+ "Define MAC as a keyboard macro using kmacro commands."
+ (push mac kmacro-tests-macros)
+ (kmacro-tests-simulate-command '(kmacro-start-macro nil))
+ (should defining-kbd-macro)
+ (kmacro-tests-simulate-command '(kmacro-end-macro nil))
+ (should (equal mac last-kbd-macro)))
+
+(defun kmacro-tests-get-kmacro-key (sym)
+ "Look up kmacro command SYM in kmacro's keymap.
+Return the integer key value found."
+ (aref (where-is-internal sym kmacro-keymap t) 0))
+
+(defun kmacro-tests-get-kmacro-step-edit-key (sym)
+ "Return the first key bound to SYM in `kmacro-step-edit-map'."
+ (let ((where (aref (where-is-internal sym kmacro-step-edit-map t) 0)))
+ (if (consp where)
+ (car where)
+ where)))
+
+(provide 'kmacro-tests)
+
+;;; kmacro-tests.el ends here
diff --git a/test/lisp/minibuffer-tests.el b/test/lisp/minibuffer-tests.el
index efed8f8bed4..7c5fcb4838f 100644
--- a/test/lisp/minibuffer-tests.el
+++ b/test/lisp/minibuffer-tests.el
@@ -28,7 +28,7 @@
(ert-deftest completion-test1 ()
(with-temp-buffer
- (cl-flet* ((test/completion-table (string pred action)
+ (cl-flet* ((test/completion-table (_string _pred action)
(if (eq action 'lambda)
nil
"test: "))
diff --git a/test/lisp/net/dbus-tests.el b/test/lisp/net/dbus-tests.el
index 525709b92e7..0a59e3b42d1 100644
--- a/test/lisp/net/dbus-tests.el
+++ b/test/lisp/net/dbus-tests.el
@@ -22,7 +22,8 @@
(require 'ert)
(require 'dbus)
-(setq dbus-debug nil)
+(defvar dbus-debug nil)
+(declare-function dbus-get-unique-name "dbusbind.c" (bus))
(defvar dbus--test-enabled-session-bus
(and (featurep 'dbusbind)
diff --git a/test/lisp/progmodes/js-tests.el b/test/lisp/progmodes/js-tests.el
index 84749efa45b..7cb737c30e2 100644
--- a/test/lisp/progmodes/js-tests.el
+++ b/test/lisp/progmodes/js-tests.el
@@ -85,6 +85,20 @@ if (!/[ (:,='\"]/.test(value)) {
(should (= (current-column) x))
(forward-line))))
+(ert-deftest js-mode-auto-fill ()
+ (with-temp-buffer
+ (js-mode)
+ (setq fill-column 70)
+ (insert "/* ")
+ (dotimes (_ 16)
+ (insert "test "))
+ (do-auto-fill)
+ ;; The bug is that, after auto-fill, the second line starts with
+ ;; "/*", whereas it should start with " * ".
+ (goto-char (point-min))
+ (forward-line)
+ (should (looking-at " \\* test"))))
+
(provide 'js-tests)
;;; js-tests.el ends here
diff --git a/test/lisp/progmodes/python-tests.el b/test/lisp/progmodes/python-tests.el
index 2df1bbf50d8..1e6b867d30b 100644
--- a/test/lisp/progmodes/python-tests.el
+++ b/test/lisp/progmodes/python-tests.el
@@ -1156,6 +1156,27 @@ if do:
(python-tests-look-at "that)")
(should (= (current-indentation) 6))))
+(ert-deftest python-indent-electric-colon-4 ()
+ "Test indentation case where there is one more-indented previous open block."
+ (python-tests-with-temp-buffer
+ "
+def f():
+ if True:
+ a = 5
+
+ if True:
+ a = 10
+
+ b = 3
+
+else
+"
+ (python-tests-look-at "else")
+ (goto-char (line-end-position))
+ (python-tests-self-insert ":")
+ (python-tests-look-at "else" -1)
+ (should (= (current-indentation) 4))))
+
(ert-deftest python-indent-region-1 ()
"Test indentation case from Bug#18843."
(let ((contents "
@@ -2457,7 +2478,7 @@ if x:
(python-tests-with-temp-buffer
" \"\n"
(goto-char (point-min))
- (font-lock-fontify-buffer)))
+ (call-interactively 'font-lock-fontify-buffer)))
;;; Shell integration
diff --git a/test/lisp/simple-tests.el b/test/lisp/simple-tests.el
index 6194cada1c6..f4849c4b21d 100644
--- a/test/lisp/simple-tests.el
+++ b/test/lisp/simple-tests.el
@@ -30,8 +30,9 @@
(insert "(a b")
(save-excursion (insert " c d)"))
,@body
- (cons (buffer-substring (point-min) (point))
- (buffer-substring (point) (point-max)))))
+ (with-no-warnings
+ (cons (buffer-substring (point-min) (point))
+ (buffer-substring (point) (point-max))))))
(defmacro simple-test--transpositions (&rest body)
@@ -266,7 +267,6 @@
(with-temp-buffer
(setq buffer-undo-list nil)
(insert "hello")
- (car buffer-undo-list)
(undo-auto--boundaries 'test))))
;;; Transposition with negative args (bug#20698, bug#21885)
diff --git a/test/lisp/textmodes/css-mode-tests.el b/test/lisp/textmodes/css-mode-tests.el
index 6eb32ea7fc4..5372c37a179 100644
--- a/test/lisp/textmodes/css-mode-tests.el
+++ b/test/lisp/textmodes/css-mode-tests.el
@@ -218,5 +218,20 @@
(should (member "body" completions))
(should-not (member "article" completions)))))
+(ert-deftest css-mdn-symbol-guessing ()
+ (dolist (item '(("@med" "ia" "@media")
+ ("@keyframes " "{" "@keyframes")
+ ("p::after" "" "::after")
+ ("p:before" "" ":before")
+ ("a:v" "isited" ":visited")
+ ("border-" "color: red" "border-color")
+ ("border-color: red" ";" "border-color")
+ ("border-color: red; color: green" ";" "color")))
+ (with-temp-buffer
+ (css-mode)
+ (insert (nth 0 item))
+ (save-excursion (insert (nth 1 item)))
+ (should (equal (nth 2 item) (css--mdn-find-symbol))))))
+
(provide 'css-mode-tests)
;;; css-mode-tests.el ends here
diff --git a/test/lisp/textmodes/tildify-tests.el b/test/lisp/textmodes/tildify-tests.el
index 0a82b2521fb..f958fbc547a 100644
--- a/test/lisp/textmodes/tildify-tests.el
+++ b/test/lisp/textmodes/tildify-tests.el
@@ -226,7 +226,7 @@ The function must terminate as soon as callback returns nil."
(defun tildify-space-undo-test--test
- (modes nbsp env-open &optional set-space-string)
+ (modes nbsp _env-open &optional set-space-string)
(with-temp-buffer
(setq-local buffer-file-coding-system 'utf-8)
(dolist (mode modes)
diff --git a/test/lisp/vc/diff-mode-tests.el b/test/lisp/vc/diff-mode-tests.el
new file mode 100644
index 00000000000..807a411fa5d
--- /dev/null
+++ b/test/lisp/vc/diff-mode-tests.el
@@ -0,0 +1,203 @@
+;; Copyright (C) 2017 Free Software Foundation, Inc
+
+;; Author: Dima Kogan <dima@secretsauce.net>
+;; Maintainer: emacs-devel@gnu.org
+
+;; 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/>.
+
+;;; Code:
+
+(require 'diff-mode)
+
+
+(ert-deftest diff-mode-test-ignore-trailing-dashes ()
+ "Check to make sure we successfully ignore trailing -- made by
+'git format-patch'. This is bug #9597"
+
+ ;; I made a test repo, put some files in it, made arbitrary changes
+ ;; and invoked 'git format-patch' to get a patch out of it. The
+ ;; patch and the before and after versions of the files appear here.
+ ;; The test simply tries to apply the patch. The patch contains
+ ;; trailing --, which confused diff-mode previously
+ (let ((patch "From 18ed35640be496647e0a02fc155b4ee4a0490eca Mon Sep 17 00:00:00 2001
+From: Dima Kogan <dima@secretsauce.net>
+Date: Mon, 30 Jan 2017 22:24:13 -0800
+Subject: [PATCH] test commit
+
+---
+ fil | 3 ---
+ fil2 | 4 ----
+ 2 files changed, 7 deletions(-)
+
+diff --git a/fil b/fil
+index 10344f1..2a56245 100644
+--- a/fil
++++ b/fil
+@@ -2,10 +2,8 @@ Afrocentrism
+ Americanisms
+ Americanization
+ Americanizations
+-Americanized
+ Americanizes
+ Americanizing
+-Andrianampoinimerina
+ Anglicanisms
+ Antananarivo
+ Apalachicola
+@@ -15,6 +13,5 @@ Aristophanes
+ Aristotelian
+ Ashurbanipal
+ Australopithecus
+-Austronesian
+ Bangladeshis
+ Barquisimeto
+diff --git a/fil2 b/fil2
+index 8858f0d..86e8ea5 100644
+--- a/fil2
++++ b/fil2
+@@ -1,20 +1,16 @@
+ whippoorwills
+ whitewashing
+ wholehearted
+-wholeheartedly
+ wholesomeness
+ wildernesses
+ windbreakers
+ wisecracking
+ withstanding
+-woodcarvings
+ woolgathering
+ workstations
+ worthlessness
+ wretchedness
+ wristwatches
+-wrongfulness
+ wrongheadedly
+ wrongheadedness
+-xylophonists
+ youthfulness
+--
+2.11.0
+
+")
+ (fil_before "Afrocentrism
+Americanisms
+Americanization
+Americanizations
+Americanized
+Americanizes
+Americanizing
+Andrianampoinimerina
+Anglicanisms
+Antananarivo
+Apalachicola
+Appalachians
+Argentinians
+Aristophanes
+Aristotelian
+Ashurbanipal
+Australopithecus
+Austronesian
+Bangladeshis
+Barquisimeto
+")
+ (fil_after "Afrocentrism
+Americanisms
+Americanization
+Americanizations
+Americanizes
+Americanizing
+Anglicanisms
+Antananarivo
+Apalachicola
+Appalachians
+Argentinians
+Aristophanes
+Aristotelian
+Ashurbanipal
+Australopithecus
+Bangladeshis
+Barquisimeto
+")
+ (fil2_before "whippoorwills
+whitewashing
+wholehearted
+wholeheartedly
+wholesomeness
+wildernesses
+windbreakers
+wisecracking
+withstanding
+woodcarvings
+woolgathering
+workstations
+worthlessness
+wretchedness
+wristwatches
+wrongfulness
+wrongheadedly
+wrongheadedness
+xylophonists
+youthfulness
+")
+ (fil2_after "whippoorwills
+whitewashing
+wholehearted
+wholesomeness
+wildernesses
+windbreakers
+wisecracking
+withstanding
+woolgathering
+workstations
+worthlessness
+wretchedness
+wristwatches
+wrongheadedly
+wrongheadedness
+youthfulness
+")
+ (temp-dir (make-temp-file "diff-mode-test" 'dir)))
+
+ (let ((buf (find-file-noselect (format "%s/%s" temp-dir "fil" )))
+ (buf2 (find-file-noselect (format "%s/%s" temp-dir "fil2"))))
+ (unwind-protect
+ (progn
+ (with-current-buffer buf (insert fil_before) (save-buffer))
+ (with-current-buffer buf2 (insert fil2_before) (save-buffer))
+
+ (with-temp-buffer
+ (cd temp-dir)
+ (insert patch)
+ (beginning-of-buffer)
+ (diff-apply-hunk)
+ (diff-apply-hunk)
+ (diff-apply-hunk))
+
+ (should (equal (with-current-buffer buf (buffer-string))
+ fil_after))
+ (should (equal (with-current-buffer buf2 (buffer-string))
+ fil2_after)))
+
+ (ignore-errors
+ (with-current-buffer buf (set-buffer-modified-p nil))
+ (kill-buffer buf)
+ (with-current-buffer buf2 (set-buffer-modified-p nil))
+ (kill-buffer buf2)
+ (delete-directory temp-dir 'recursive))))))
+
+
+(provide 'diff-mode-tests)
diff --git a/test/lisp/xml-tests.el b/test/lisp/xml-tests.el
index 0f2182a6a75..d0da2094db7 100644
--- a/test/lisp/xml-tests.el
+++ b/test/lisp/xml-tests.el
@@ -134,6 +134,21 @@ Parser is called with and without 'symbol-qnames argument.")
(append xml-default-ns
'(("F" . "FOOBAR:"))))))))))
+;; Test bug #23440 (proper expansion of default namespace)
+; Test data for default namespace
+(defvar xml-parse-test--default-namespace-qnames
+ (cons "<something xmlns=\"myns:\"><whatever></whatever></something>"
+ '((myns:something
+ ((("http://www.w3.org/2000/xmlns/" . "")
+ . "myns:"))
+ (myns:whatever nil)))))
+
+(ert-deftest xml-parse-test-default-namespace-qnames ()
+ (with-temp-buffer
+ (insert (car xml-parse-test--default-namespace-qnames))
+ (should (equal (cdr xml-parse-test--default-namespace-qnames)
+ (xml-parse-region nil nil nil nil 'symbol-qnames)))))
+
;; Local Variables:
;; no-byte-compile: t
;; End: