aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGemini Lasswell2017-02-04 13:55:47 +0200
committerEli Zaretskii2017-02-04 13:55:47 +0200
commitff4dd0d39c3f5dfb8f4988f840c2c05621db32db (patch)
treec12961f075fd882c0fbbaa1f609aadca0f4672c8
parentc8f91b168b0955e1e21acbf291171af1f70725ec (diff)
downloademacs-ff4dd0d39c3f5dfb8f4988f840c2c05621db32db.tar.gz
emacs-ff4dd0d39c3f5dfb8f4988f840c2c05621db32db.zip
Add tests for lisp/kmacro.el
* test/lisp/kmacro-tests.el: New file. (Bug#24939)
-rw-r--r--test/lisp/kmacro-tests.el890
1 files changed, 890 insertions, 0 deletions
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 @@
1;;; kmacro-tests.el --- Tests for kmacro.el -*- lexical-binding: t; -*-
2
3;; Copyright (C) 2017 Free Software Foundation, Inc.
4
5;; Author: Gemini Lasswell <gazally@runbox.com>
6
7;; This file is part of GNU Emacs.
8
9;; GNU Emacs is free software: you can redistribute it and/or modify
10;; it under the terms of the GNU General Public License as published by
11;; the Free Software Foundation, either version 3 of the License, or
12;; (at your option) any later version.
13
14;; GNU Emacs is distributed in the hope that it will be useful,
15;; but WITHOUT ANY WARRANTY; without even the implied warranty of
16;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17;; GNU General Public License for more details.
18
19;; You should have received a copy of the GNU General Public License
20;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
21
22;;; Commentary:
23
24;;; Code:
25
26(require 'kmacro)
27(require 'ert)
28(require 'ert-x)
29
30;;; Test fixtures:
31
32(defmacro kmacro-tests-with-kmacro-clean-slate (&rest body)
33 "Create a clean environment for a kmacro test BODY to run in."
34 (declare (debug (body)))
35 `(cl-letf* ((kmacro-execute-before-append t)
36 (kmacro-ring-max 8)
37 (kmacro-repeat-no-prefix t)
38 (kmacro-call-repeat-key nil)
39 (kmacro-call-repeat-with-arg nil)
40
41 (kbd-macro-termination-hook nil)
42 (defining-kbd-macro nil)
43 (executing-kbd-macro nil)
44 (executing-kbd-macro-index 0)
45 (last-kbd-macro nil)
46
47 (kmacro-ring nil)
48
49 (kmacro-counter 0)
50 (kmacro-default-counter-format "%d")
51 (kmacro-counter-format "%d")
52 (kmacro-counter-format-start "%d")
53 (kmacro-counter-value-start 0)
54 (kmacro-last-counter 0)
55 (kmacro-initial-counter-value nil)
56
57 (kmacro-tests-macros nil)
58 (kmacro-tests-events nil)
59 (kmacro-tests-sequences nil))
60 (advice-add 'end-kbd-macro :after #'kmacro-tests-end-macro-advice)
61 (advice-add 'read-event :around #'kmacro-tests-read-event-advice )
62 (advice-add 'read-key-sequence :around #'kmacro-tests-read-key-sequence-advice)
63 (unwind-protect
64 (ert-with-test-buffer (:name "")
65 (switch-to-buffer (current-buffer))
66 ,@body)
67 (advice-remove 'read-key-sequence #'kmacro-tests-read-key-sequence-advice)
68 (advice-remove 'read-event #'kmacro-tests-read-event-advice)
69 (advice-remove 'end-kbd-macro #'kmacro-tests-end-macro-advice))))
70
71(defmacro kmacro-tests-deftest (name _args docstring &rest keys-and-body)
72 "Define a kmacro unit test.
73NAME is the name of the test, _ARGS should be nil, and DOCSTRING
74is required. To avoid having to duplicate ert's keyword parsing
75here, its keywords and values (if any) must be inside a list
76after the docstring, preceding the body, here combined with the
77body in KEYS-AND-BODY."
78 (declare (debug (&define name sexp stringp
79 [&optional (&rest &or [keywordp sexp])]
80 def-body))
81 (doc-string 3)
82 (indent 2))
83
84 (let* ((keys (when (and (listp (car keys-and-body))
85 (keywordp (caar keys-and-body)))
86 (car keys-and-body)))
87 (body (if keys (cdr keys-and-body)
88 keys-and-body)))
89 `(ert-deftest ,name ()
90 ,docstring ,@keys
91 (kmacro-tests-with-kmacro-clean-slate ,@body))))
92
93(defvar kmacro-tests-keymap
94 (let ((map (make-sparse-keymap)))
95 (dotimes (i 26)
96 (define-key map (string (+ ?a i)) 'self-insert-command))
97 (dotimes (i 10)
98 (define-key map (string (+ ?0 i)) 'self-insert-command))
99 ;; Define a few key sequences of different lengths.
100 (dolist (item '(("\C-a" . beginning-of-line)
101 ("\C-b" . backward-char)
102 ("\C-e" . end-of-line)
103 ("\C-f" . forward-char)
104 ("\C-r" . isearch-backward)
105 ("\C-u" . universal-argument)
106 ("\C-w" . kill-region)
107 ("\C-SPC" . set-mark-command)
108 ("\M-w" . kill-ring-save)
109 ("\M-x" . execute-extended-command)
110 ("\C-cd" . downcase-word)
111 ("\C-cxu" . upcase-word)
112 ("\C-cxq" . quoted-insert)
113 ("\C-cxi" . kmacro-insert-counter)
114 ("\C-x\C-k" . kmacro-keymap)))
115 (define-key map (car item) (cdr item)))
116 map)
117 "Keymap to use for testing keyboard macros.
118This is used to obtain consistent results even if tests are run
119in an environment with rebound keys.")
120
121(defvar kmacro-tests-events nil
122 "Input events used by the kmacro test in progress.")
123
124(defun kmacro-tests-read-event-advice (orig-func &rest args)
125 "Pop and return an event from `kmacro-tests-events'.
126Return the result of calling ORIG-FUNC with ARGS if
127`kmacro-tests-events' is empty, or if a keyboard macro is
128running."
129 (if (or executing-kbd-macro (null kmacro-tests-events))
130 (apply orig-func args)
131 (pop kmacro-tests-events)))
132
133(defvar kmacro-tests-sequences nil
134 "Input sequences used by the kmacro test in progress.")
135
136(defun kmacro-tests-read-key-sequence-advice (orig-func &rest args)
137 "Pop and return a string from `kmacro-tests-sequences'.
138Return the result of calling ORIG-FUNC with ARGS if
139`kmacro-tests-sequences' is empty, or if a keyboard macro is
140running."
141 (if (or executing-kbd-macro (null kmacro-tests-sequences))
142 (apply orig-func args)
143 (pop kmacro-tests-sequences)))
144
145(defvar kmacro-tests-macros nil
146 "Keyboard macros (in vector form) used by the kmacro test in progress.")
147
148(defun kmacro-tests-end-macro-advice (&rest _args)
149 "Pop a macro from `kmacro-tests-macros' and assign it to `last-kbd-macro'.
150If `kmacro-tests-macros' is empty, do nothing."
151 (when kmacro-tests-macros
152 (setq last-kbd-macro (pop kmacro-tests-macros))))
153
154;;; Some more powerful expectations:
155
156(defmacro kmacro-tests-should-insert (value &rest body)
157 "Verify that VALUE is inserted by the execution of BODY.
158Execute BODY, then check that the string VALUE was inserted
159into the current buffer at point."
160 (declare (debug (stringp body))
161 (indent 1))
162 (let ((g-p (cl-gensym))
163 (g-bsize (cl-gensym)))
164 `(let ((,g-p (point))
165 (,g-bsize (buffer-size)))
166 ,@body
167 (should (equal (buffer-substring ,g-p (point)) ,value))
168 (should (equal (- (buffer-size) ,g-bsize) (length ,value))))))
169
170(defmacro kmacro-tests-should-match-message (value &rest body)
171 "Verify that a message matching VALUE is issued while executing BODY.
172Execute BODY, and then if there is not a regexp match between
173VALUE and any text written to *Messages* during the execution,
174cause the current test to fail."
175 (declare (debug (form body))
176 (indent 1))
177 (let ((g-captured-messages (cl-gensym)))
178 `(ert-with-message-capture ,g-captured-messages
179 ,@body
180 (should (string-match-p ,value ,g-captured-messages)))))
181
182;;; Tests:
183
184(kmacro-tests-deftest kmacro-tests-test-insert-counter-01-nil ()
185 "`kmacro-insert-counter' adds one to macro counter with nil arg."
186 (kmacro-tests-should-insert "0"
187 (kmacro-tests-simulate-command '(kmacro-insert-counter nil)))
188 (kmacro-tests-should-insert "1"
189 (kmacro-tests-simulate-command '(kmacro-insert-counter nil))))
190
191(kmacro-tests-deftest kmacro-tests-test-insert-counter-02-int ()
192 "`kmacro-insert-counter' increments by value of list argument."
193 (kmacro-tests-should-insert "0"
194 (kmacro-tests-simulate-command '(kmacro-insert-counter 2)))
195 (kmacro-tests-should-insert "2"
196 (kmacro-tests-simulate-command '(kmacro-insert-counter 3)))
197 (kmacro-tests-should-insert "5"
198 (kmacro-tests-simulate-command '(kmacro-insert-counter nil))))
199
200(kmacro-tests-deftest kmacro-tests-test-insert-counter-03-list ()
201 "`kmacro-insert-counter' doesn't increment when given universal argument."
202 (kmacro-tests-should-insert "0"
203 (kmacro-tests-simulate-command '(kmacro-insert-counter (16))))
204 (kmacro-tests-should-insert "0"
205 (kmacro-tests-simulate-command '(kmacro-insert-counter (4)))))
206
207(kmacro-tests-deftest kmacro-tests-test-insert-counter-04-neg ()
208 "`kmacro-insert-counter' decrements with '- prefix argument"
209 (kmacro-tests-should-insert "0"
210 (kmacro-tests-simulate-command '(kmacro-insert-counter -)))
211 (kmacro-tests-should-insert "-1"
212 (kmacro-tests-simulate-command '(kmacro-insert-counter nil))))
213
214(kmacro-tests-deftest kmacro-tests-test-start-format-counter ()
215 "`kmacro-insert-counter' uses start value and format."
216 (kmacro-tests-simulate-command '(kmacro-set-counter 10))
217 (kmacro-tests-should-insert "10"
218 (kmacro-tests-simulate-command '(kmacro-insert-counter nil)))
219 (kmacro-tests-should-insert "11"
220 (kmacro-tests-simulate-command '(kmacro-insert-counter nil)))
221 (kmacro-set-format "c=%s")
222 (kmacro-tests-simulate-command '(kmacro-set-counter 50))
223 (kmacro-tests-should-insert "c=50"
224 (kmacro-tests-simulate-command '(kmacro-insert-counter nil))))
225
226(kmacro-tests-deftest kmacro-tests-test-start-macro-when-defining-macro ()
227 "Starting a macro while defining a macro does not start a second macro."
228 (kmacro-tests-simulate-command '(kmacro-start-macro nil))
229 ;; We should now be in the macro-recording state.
230 (should defining-kbd-macro)
231 (should-not last-kbd-macro)
232 ;; Calling it again should leave us in the same state.
233 (kmacro-tests-simulate-command '(kmacro-start-macro nil))
234 (should defining-kbd-macro)
235 (should-not last-kbd-macro))
236
237
238(kmacro-tests-deftest kmacro-tests-set-macro-counter-while-defining ()
239 "Use of the prefix arg with kmacro-start sets kmacro-counter."
240 ;; Give kmacro-start-macro an argument.
241 (kmacro-tests-simulate-command '(kmacro-start-macro 5))
242 (should defining-kbd-macro)
243 ;; Verify that the counter is set to that value.
244 (kmacro-tests-should-insert "5"
245 (kmacro-tests-simulate-command '(kmacro-insert-counter nil)))
246 ;; Change it while defining a macro.
247 (kmacro-tests-simulate-command '(kmacro-set-counter 1))
248 (kmacro-tests-should-insert "1"
249 (kmacro-tests-simulate-command '(kmacro-insert-counter nil)))
250 ;; Using universal arg to to set counter should reset to starting value.
251 (kmacro-tests-simulate-command '(kmacro-set-counter (4)) '(4))
252 (kmacro-tests-should-insert "5"
253 (kmacro-tests-simulate-command '(kmacro-insert-counter nil))))
254
255
256(kmacro-tests-deftest kmacro-tests-start-insert-counter-appends-to-macro ()
257 "Use of the universal arg appends to the previous macro."
258 (let ((kmacro-tests-macros (list (string-to-vector "hello"))))
259 ;; Start recording a macro.
260 (kmacro-tests-simulate-command '(kmacro-start-macro-or-insert-counter nil))
261 ;; Make sure we are recording.
262 (should defining-kbd-macro)
263 ;; Call it again and it should insert the counter.
264 (kmacro-tests-should-insert "0"
265 (kmacro-tests-simulate-command '(kmacro-start-macro-or-insert-counter nil)))
266 ;; We should still be in the recording state.
267 (should defining-kbd-macro)
268 ;; End recording with repeat count.
269 (kmacro-tests-simulate-command '(kmacro-end-or-call-macro 3))
270 ;; Recording should be finished.
271 (should-not defining-kbd-macro)
272 ;; Now use prefix arg to append to the previous macro.
273 ;; This should run the previous macro first.
274 (kmacro-tests-should-insert "hello"
275 (kmacro-tests-simulate-command
276 '(kmacro-start-macro-or-insert-counter (4))))
277 ;; Verify that the recording state has changed.
278 (should (equal defining-kbd-macro 'append))))
279
280(kmacro-tests-deftest kmacro-tests-end-call-macro-prefix-args ()
281 "kmacro-end-call-macro changes behavior based on prefix arg."
282 ;; "Record" two macros.
283 (dotimes (i 2)
284 (kmacro-tests-define-macro (vconcat (format "macro #%d" (1+ i)))))
285 ;; With no prefix arg, it should call the second macro.
286 (kmacro-tests-should-insert "macro #2"
287 (kmacro-tests-simulate-command '(kmacro-end-or-call-macro nil)))
288 ;; With universal arg, it should call the first one.
289 (kmacro-tests-should-insert "macro #1"
290 (kmacro-tests-simulate-command '(kmacro-end-or-call-macro (4)))))
291
292(kmacro-tests-deftest kmacro-tests-end-and-call-macro ()
293 "Keyboard command to end and call macro works under various conditions."
294 ;; First, try it with no macro to record.
295 (setq kmacro-tests-macros '(""))
296 (kmacro-tests-simulate-command '(kmacro-start-macro nil))
297 (condition-case err
298 (kmacro-tests-simulate-command '(kmacro-end-and-call-macro 2) 2)
299 (error (should (string= (cadr err)
300 "No kbd macro has been defined"))))
301
302 ;; Check that it stopped defining and that no macro was recorded.
303 (should-not defining-kbd-macro)
304 (should-not last-kbd-macro)
305
306 ;; Now try it while not recording, but first record a non-nil macro.
307 (kmacro-tests-define-macro "macro")
308 (kmacro-tests-should-insert "macro"
309 (kmacro-tests-simulate-command '(kmacro-end-and-call-macro nil))))
310
311(kmacro-tests-deftest kmacro-tests-end-and-call-macro-mouse ()
312 "Commands to end and call macro work under various conditions.
313This is a regression test for Bug#24992."
314 (:expected-result :failed)
315 (cl-letf (((symbol-function #'mouse-set-point) #'ignore))
316 ;; First, try it with no macro to record.
317 (setq kmacro-tests-macros '(""))
318 (kmacro-tests-simulate-command '(kmacro-start-macro nil))
319 (condition-case err
320 (kmacro-tests-simulate-command '(kmacro-end-call-mouse 2) 2)
321 (error (should (string= (cadr err)
322 "No kbd macro has been defined"))))
323
324 ;; Check that it stopped defining and that no macro was recorded.
325 (should-not defining-kbd-macro)
326 (should-not last-kbd-macro)
327
328 ;; Now try it while not recording, but first record a non-nil macro.
329 (kmacro-tests-define-macro "macro")
330 (kmacro-tests-should-insert "macro"
331 (kmacro-tests-simulate-command '(kmacro-end-call-mouse nil)))))
332
333(kmacro-tests-deftest kmacro-tests-call-macro-hint-and-repeat ()
334 "`kmacro-call-macro' gives hint in Messages and sets up repeat keymap.
335This is a regression test for: Bug#3412, Bug#11817."
336 (kmacro-tests-define-macro [?m])
337 (let ((kmacro-call-repeat-key t)
338 (kmacro-call-repeat-with-arg t)
339 (overriding-terminal-local-map overriding-terminal-local-map)
340 (last-input-event ?e))
341 (message "") ; Clear the echo area. (Bug#3412)
342 (kmacro-tests-should-match-message "Type e to repeat macro"
343 (kmacro-tests-should-insert "mmmmmm"
344 (cl-letf (((symbol-function #'this-single-command-keys) (lambda ()
345 [?\C-x ?e])))
346 (kmacro-call-macro 3))
347 ;; Check that it set up for repeat, and run the repeat.
348 (funcall (lookup-key overriding-terminal-local-map "e"))))))
349
350(kmacro-tests-deftest
351 kmacro-tests-run-macro-command-recorded-in-macro ()
352 "No infinite loop if `kmacro-end-and-call-macro' is recorded in the macro.
353\(Bug#15126)"
354 (:expected-result :failed)
355 (ert-skip "Skipping due to Bug#24921 (an ERT bug)")
356 (kmacro-tests-define-macro (vconcat "foo" [return] "\M-x"
357 "kmacro-end-and-call-macro"))
358 (use-local-map kmacro-tests-keymap)
359 (kmacro-tests-simulate-command '(kmacro-end-and-call-macro nil)))
360
361
362(kmacro-tests-deftest kmacro-tests-test-ring-2nd-commands ()
363 "2nd macro in ring is displayed and executed normally and on repeat."
364 (use-local-map kmacro-tests-keymap)
365 ;; Record one macro, with count.
366 (push (vconcat "\C-cxi" "\C-u\C-cxi") kmacro-tests-macros)
367 (kmacro-tests-simulate-command '(kmacro-start-macro 1))
368 (kmacro-tests-simulate-command '(kmacro-end-macro nil))
369 ;; Check that execute and display do nothing with no 2nd macro.
370 (kmacro-tests-should-insert ""
371 (kmacro-tests-simulate-command '(kmacro-call-ring-2nd nil)))
372 (kmacro-tests-should-match-message "Only one keyboard macro defined"
373 (kmacro-tests-simulate-command '(kmacro-view-ring-2nd)))
374 ;; Record another one, with format.
375 (kmacro-set-format "=%d=")
376 (kmacro-tests-define-macro (vconcat "bar"))
377 ;; Execute the first one, mocked up to insert counter.
378 ;; Should get default format.
379 (kmacro-tests-should-insert "11"
380 (kmacro-tests-simulate-command '(kmacro-call-ring-2nd nil)))
381 ;; Now display the 2nd ring macro and check result.
382 (kmacro-tests-should-match-message "C-c x i C-u C-c x i"
383 (kmacro-view-ring-2nd)))
384
385(kmacro-tests-deftest kmacro-tests-fill-ring-and-rotate ()
386 "Macro ring can shift one way, shift the other way, swap and pop."
387 (cl-letf ((kmacro-ring-max 4))
388 ;; Record enough macros that the first one drops off the history.
389 (dotimes (n (1+ kmacro-ring-max))
390 (kmacro-tests-define-macro (make-vector (1+ n) (+ ?a n))))
391 ;; Cycle the ring and check that #2 comes up.
392 (kmacro-tests-should-match-message "2*b"
393 (kmacro-tests-simulate-command '(kmacro-cycle-ring-next nil)))
394 ;; Execute the current macro and check arguments.
395 (kmacro-tests-should-insert "bbbb"
396 (kmacro-call-macro 2 t))
397 ;; Cycle the ring the other way; #5 expected.
398 (kmacro-tests-should-match-message "5*e" (kmacro-cycle-ring-previous nil))
399 ;; Swapping the top two should give #4.
400 (kmacro-tests-should-match-message "4*d" (kmacro-swap-ring))
401 ;; Delete the top and expect #5.
402 (kmacro-tests-should-match-message "5*e" (kmacro-delete-ring-head))))
403
404
405(kmacro-tests-deftest kmacro-tests-test-ring-commands-when-no-macros ()
406 "Ring commands give appropriate message when no macros exist."
407 (dolist (cmd '((kmacro-cycle-ring-next nil)
408 (kmacro-cycle-ring-previous nil)
409 (kmacro-swap-ring)
410 (kmacro-delete-ring-head)
411 (kmacro-view-ring-2nd)
412 (kmacro-call-ring-2nd nil)
413 (kmacro-view-macro)))
414 (kmacro-tests-should-match-message "No keyboard macro defined"
415 (kmacro-tests-simulate-command cmd))))
416
417(kmacro-tests-deftest kmacro-tests-repeat-on-last-key ()
418 "Kmacro commands can be run in sequence without prefix keys."
419 (let* ((prefix (where-is-internal 'kmacro-keymap nil t))
420 ;; Make a sequence of events to run.
421 ;; Comments are expected output of mock macros
422 ;; on the first and second run of the sequence (see below).
423 (events (mapcar #'kmacro-tests-get-kmacro-key
424 '(kmacro-end-or-call-macro-repeat ;c / b
425 kmacro-end-or-call-macro-repeat ;c / b
426 kmacro-call-ring-2nd-repeat ;b / a
427 kmacro-cycle-ring-next
428 kmacro-end-or-call-macro-repeat ;a / a
429 kmacro-cycle-ring-previous
430 kmacro-end-or-call-macro-repeat ;c / b
431 kmacro-delete-ring-head
432 kmacro-end-or-call-macro-repeat ;b / a
433 )))
434 (kmacro-tests-macros (list [?a] [?b] [?c]))
435 ;; What we want kmacro to see as keyboard command sequence
436 (first-event (seq-concatenate
437 'vector
438 prefix
439 (vector (kmacro-tests-get-kmacro-key
440 'kmacro-end-or-call-macro-repeat)))))
441 (cl-letf
442 ;; standardize repeat options
443 ((kmacro-repeat-no-prefix t)
444 (kmacro-call-repeat-key t)
445 (kmacro-call-repeat-with-arg nil))
446 ;; "Record" two macros
447 (dotimes (_n 2)
448 (kmacro-tests-simulate-command '(kmacro-start-macro nil))
449 (kmacro-tests-simulate-command '(kmacro-end-macro nil)))
450 ;; Start recording #3
451 (kmacro-tests-simulate-command '(kmacro-start-macro nil))
452
453 ;; Set up pending keyboard events and a fresh buffer
454 ;; kmacro-set-counter is not one of the repeating kmacro
455 ;; commands so it should end the sequence.
456 (let* ((end-key (kmacro-tests-get-kmacro-key 'kmacro-set-counter))
457 (kmacro-tests-events (append events (list end-key))))
458 (cl-letf (((symbol-function #'this-single-command-keys)
459 (lambda () first-event)))
460 (use-local-map kmacro-tests-keymap)
461 (kmacro-tests-should-insert "ccbacb"
462 ;; End #3 and launch loop to read events.
463 (kmacro-end-or-call-macro-repeat nil))))
464
465 ;; `kmacro-edit-macro-repeat' should also stop the sequence,
466 ;; so run it again with that at the end.
467 (let* ((end-key (kmacro-tests-get-kmacro-key 'kmacro-edit-macro-repeat))
468 (kmacro-tests-events (append events (list end-key))))
469 (cl-letf (((symbol-function #'edit-kbd-macro) #'ignore)
470 ((symbol-function #'this-single-command-keys)
471 (lambda () first-event)))
472 (use-local-map kmacro-tests-keymap)
473 (kmacro-tests-should-insert "bbbbbaaba"
474 (kmacro-end-or-call-macro-repeat 3)))))))
475
476(kmacro-tests-deftest kmacro-tests-repeat-view-and-run ()
477 "Kmacro view cycles through ring and executes macro just viewed."
478 (let* ((prefix (where-is-internal 'kmacro-keymap nil t))
479 (kmacro-tests-events
480 (mapcar #'kmacro-tests-get-kmacro-key
481 (append (make-list 5 'kmacro-view-macro-repeat)
482 '(kmacro-end-or-call-macro-repeat
483 kmacro-set-counter))))
484 ;; Make kmacro see this as keyboard command sequence.
485 (first-event (seq-concatenate
486 'vector
487 prefix
488 (vector (kmacro-tests-get-kmacro-key
489 'kmacro-view-macro-repeat))))
490 ;; Construct a regexp to match the messages which should be
491 ;; produced by repeated view-repeats.
492 (macros-regexp (apply #'concat
493 (mapcar (lambda (c) (format ".+%s\n" c))
494 '("d" "c" "b" "a" "d" "c")))))
495 (cl-letf ((kmacro-repeat-no-prefix t)
496 (kmacro-call-repeat-key t)
497 (kmacro-call-repeat-with-arg nil)
498 ((symbol-function #'this-single-command-keys) (lambda ()
499 first-event)))
500 ;; "Record" some macros.
501 (dotimes (n 4)
502 (kmacro-tests-define-macro (make-vector 1 (+ ?a n))))
503
504 (use-local-map kmacro-tests-keymap)
505 ;; 6 views (the direct call plus the 5 in events) should
506 ;; cycle through the ring and get to the second-to-last
507 ;; macro defined.
508 (kmacro-tests-should-insert "c"
509 (kmacro-tests-should-match-message macros-regexp
510 (kmacro-tests-simulate-command '(kmacro-view-macro-repeat nil)))))))
511
512(kmacro-tests-deftest kmacro-tests-bind-to-key-when-recording ()
513 "Bind to key doesn't bind a key during macro recording."
514 (cl-letf ((global-map global-map)
515 (saved-binding (key-binding "\C-a"))
516 (kmacro-tests-sequences (list "\C-a")))
517 (kmacro-tests-simulate-command '(kmacro-start-macro 1))
518 (kmacro-bind-to-key nil)
519 (should (eq saved-binding (key-binding "\C-a")))))
520
521(kmacro-tests-deftest kmacro-tests-name-or-bind-to-key-when-no-macro ()
522 "Bind to key, symbol or register fails when when no macro exists."
523 (should-error (kmacro-bind-to-key nil))
524 (should-error (kmacro-name-last-macro 'kmacro-tests-symbol-for-test))
525 (should-error (kmacro-to-register)))
526
527(kmacro-tests-deftest kmacro-tests-bind-to-key-bad-key-sequence ()
528 "Bind to key fails to bind to ^G."
529 (let ((global-map global-map)
530 (saved-binding (key-binding "\C-g"))
531 (kmacro-tests-sequences (list "\C-g")))
532 (kmacro-tests-define-macro [1])
533 (kmacro-bind-to-key nil)
534 (should (eq saved-binding (key-binding "\C-g")))))
535
536(kmacro-tests-deftest kmacro-tests-bind-to-key-with-key-sequence-in-use ()
537 "Bind to key respects yes-or-no-p when given already bound key sequence."
538 (kmacro-tests-define-macro (vconcat "abaab"))
539 (let ((global-map global-map)
540 (map (make-sparse-keymap))
541 (kmacro-tests-sequences (make-list 2 "\C-hi")))
542 (define-key map "\C-hi" 'info)
543 (use-local-map map)
544 ;; Try the command with yes-or-no-p set up to say no.
545 (cl-letf (((symbol-function #'yes-or-no-p)
546 (lambda (prompt)
547 (should (string-match-p "info" prompt))
548 (should (string-match-p "C-h i" prompt))
549 nil)))
550 (kmacro-bind-to-key nil))
551
552 (should (equal (where-is-internal 'info nil t)
553 (vconcat "\C-hi")))
554 ;; Try it again with yes.
555 (cl-letf (((symbol-function #' yes-or-no-p)
556 (lambda (_prompt) t)))
557 (kmacro-bind-to-key nil))
558
559 (should-not (equal (where-is-internal 'info global-map t)
560 (vconcat "\C-hi")))
561 (use-local-map nil)
562 (kmacro-tests-should-insert "abaab"
563 (funcall (key-binding "\C-hi")))))
564
565(kmacro-tests-deftest kmacro-tests-kmacro-bind-to-single-key ()
566 "Bind to key uses C-x C-k A when asked to bind to A."
567 (let ((global-map global-map)
568 (kmacro-tests-macros (list (string-to-vector "\C-cxi"))))
569 (use-local-map kmacro-tests-keymap)
570
571 ;; Record a macro with counter and format set.
572 (kmacro-set-format "<%d>")
573 (kmacro-tests-simulate-command '(kmacro-start-macro-or-insert-counter 5))
574 (kmacro-tests-simulate-command '(kmacro-end-macro nil))
575
576 (let ((kmacro-tests-sequences (list "A")))
577 (kmacro-bind-to-key nil))
578
579 ;; Record a second macro with different counter and format.
580 (kmacro-set-format "%d")
581 (kmacro-tests-define-macro [2])
582
583 ;; Check the bound key and run it and verify correct counter
584 ;; and format.
585 (should (equal (string-to-vector "\C-cxi")
586 (car (kmacro-extract-lambda
587 (key-binding "\C-x\C-kA")))))
588 (kmacro-tests-should-insert "<5>"
589 (funcall (key-binding "\C-x\C-kA")))))
590
591(kmacro-tests-deftest kmacro-tests-name-last-macro-unable-to-bind ()
592 "Name last macro won't bind to symbol which is already bound."
593 (kmacro-tests-define-macro [1])
594 ;; Set up a test symbol which looks like a function.
595 (setplist 'kmacro-tests-symbol-for-test nil)
596 (fset 'kmacro-tests-symbol-for-test #'ignore)
597 (should-error (kmacro-name-last-macro 'kmacro-tests-symbol-for-test))
598 ;; The empty string symbol also can't be bound.
599 (should-error (kmacro-name-last-macro (make-symbol ""))))
600
601(kmacro-tests-deftest kmacro-tests-name-last-macro-bind-and-rebind ()
602 "Name last macro can rebind a symbol it binds."
603 ;; Make sure our symbol is unbound.
604 (when (fboundp 'kmacro-tests-symbol-for-test)
605 (fmakunbound 'kmacro-tests-symbol-for-test))
606 (setplist 'kmacro-tests-symbol-for-test nil)
607 ;; Make two macros and bind them to the same symbol.
608 (dotimes (i 2)
609 (kmacro-tests-define-macro (make-vector (1+ i) (+ ?a i)))
610 (kmacro-name-last-macro 'kmacro-tests-symbol-for-test)
611 (should (fboundp 'kmacro-tests-symbol-for-test)))
612
613 ;; Now run the function bound to the symbol. Result should be the
614 ;; second macro.
615 (kmacro-tests-should-insert "bb"
616 (kmacro-tests-simulate-command '(kmacro-tests-symbol-for-test))))
617
618(kmacro-tests-deftest kmacro-tests-store-in-register ()
619 "Macro can be stored in and retrieved from a register."
620 (use-local-map kmacro-tests-keymap)
621 ;; Save and restore register 200 so we can use it for the test.
622 (let ((saved-reg-contents (get-register 200)))
623 (unwind-protect
624 (progn
625 ;; Define a macro, and save it to a register.
626 (kmacro-tests-define-macro (vconcat "a\C-a\C-cxu"))
627 (kmacro-to-register 200)
628 ;; Then make a new different macro.
629 (kmacro-tests-define-macro (vconcat "bb\C-a\C-cxu"))
630 ;; When called from the register, result should be first macro.
631 (kmacro-tests-should-insert "AAA"
632 (kmacro-tests-simulate-command '(jump-to-register 200 3) 3))
633 (kmacro-tests-should-insert "a C-a C-c x u"
634 (kmacro-tests-simulate-command '(insert-register 200 t) '(4))))
635 (set-register 200 saved-reg-contents))))
636
637(kmacro-tests-deftest kmacro-tests-step-edit-act ()
638 "Step-edit steps-through a macro with act and act-repeat."
639 (kmacro-tests-run-step-edit "he\C-u2lo"
640 :events (make-list 6 'act)
641 :result "hello"
642 :macro-result "he\C-u2lo")
643
644 (kmacro-tests-run-step-edit "f\C-aoo\C-abar"
645 :events (make-list 5 'act-repeat)
646 :result "baroof"
647 :macro-result "f\C-aoo\C-abar"))
648
649(kmacro-tests-deftest kmacro-tests-step-edit-skip ()
650 "Step-editing can skip parts of macro."
651 (kmacro-tests-run-step-edit "ofoofff"
652 :events '(skip skip-keep skip-keep skip-keep
653 skip-rest)
654 :result ""
655 :macro-result "foo"))
656
657(kmacro-tests-deftest kmacro-tests-step-edit-quit ()
658 "Quit while step-editing leaves macro unchanged."
659 (kmacro-tests-run-step-edit "bar"
660 :events '(help insert skip help quit)
661 :sequences '("f" "o" "o" "\C-j")
662 :result "foo"
663 :macro-result "bar"))
664
665(kmacro-tests-deftest kmacro-tests-step-insert ()
666 "Step edit can insert in macro."
667 (kmacro-tests-run-step-edit "fbazbop"
668 :events '(insert act insert-1 act-repeat)
669 :sequences '("o" "o" "\C-a" "\C-j" "\C-e")
670 :result "foobazbop"
671 :macro-result "oo\C-af\C-ebazbop"))
672
673(kmacro-tests-deftest kmacro-tests-step-edit-replace-digit-argument ()
674 "Step-edit replace can replace a numeric argument in a macro.
675This is a regression for item 1 in Bug#24991."
676 (:expected-result :failed)
677 (kmacro-tests-run-step-edit "\C-u3b\C-a\C-cxu"
678 :events '(act replace automatic)
679 :sequences '("8" "x" "\C-j")
680 :result "XXXXXXXX"
681 :macro-result "\C-u8x\C-a\C-cxu"))
682
683(kmacro-tests-deftest kmacro-tests-step-edit-replace ()
684 "Step-edit replace and replace-1 can replace parts of a macro."
685 (kmacro-tests-run-step-edit "a\C-a\C-cxu"
686 :events '(act act replace)
687 :sequences '("b" "c" "\C-j")
688 :result "bca"
689 :macro-result "a\C-abc")
690 (kmacro-tests-run-step-edit "a\C-a\C-cxucd"
691 :events '(act replace-1 automatic)
692 :sequences '("b")
693 :result "abcd"
694 :macro-result "ab\C-cxucd")
695 (kmacro-tests-run-step-edit "by"
696 :events '(act replace)
697 :sequences '("a" "r" "\C-j")
698 :result "bar"
699 :macro-result "bar"))
700
701(kmacro-tests-deftest kmacro-tests-step-edit-append ()
702 "Step edit append inserts after point, and append-end inserts at end."
703 (kmacro-tests-run-step-edit "f-b"
704 :events '(append append-end)
705 :sequences '("o" "o" "\C-j" "a" "r" "\C-j")
706 :result "foo-bar"
707 :macro-result "foo-bar")
708 (kmacro-tests-run-step-edit "x"
709 :events '(append)
710 :sequences '("\C-a" "\C-cxu" "\C-e" "y" "\C-j")
711 :result "Xy"
712 :macro-result "x\C-a\C-cxu\C-ey"))
713
714(kmacro-tests-deftest kmacro-tests-append-end-at-end-appends ()
715 "Append-end when already at end of macro appends to end of macro.
716This is a regression for item 2 in Bug#24991."
717 (:expected-result :failed)
718 (kmacro-tests-run-step-edit "x"
719 :events '(append-end)
720 :sequences '("\C-a" "\C-cxu" "\C-e" "y" "\C-j")
721 :result "Xy"
722 :macro-result "x\C-a\C-cxu\C-ey"))
723
724
725(kmacro-tests-deftest kmacro-tests-step-edit-skip-entire ()
726 "Skipping a whole macro in step-edit leaves macro unchanged.
727This is a regression for item 3 in Bug#24991."
728 (:expected-result :failed)
729 (kmacro-tests-run-step-edit "xyzzy"
730 :events '(skip-rest)
731 :result ""
732 :macro-result "xyzzy"))
733
734(kmacro-tests-deftest kmacro-tests-step-edit-step-through-negative-argument ()
735 "Step edit works on macros using negative universal argument.
736This is a regression for item 4 in Bug#24991."
737 (:expected-result :failed)
738 (kmacro-tests-run-step-edit "boo\C-u-\C-cu"
739 :events '(act-repeat automatic)
740 :result "BOO"
741 :macro-result "boo\C-u-\C-cd"))
742
743(kmacro-tests-deftest kmacro-tests-step-edit-with-quoted-insert ()
744 "Stepping through a macro that uses quoted insert leaves macro unchanged.
745This is a regression for item 5 in Bug#24991."
746 (:expected-result :failed)
747 (let ((read-quoted-char-radix 8))
748 (kmacro-tests-run-step-edit "\C-cxq17051i there"
749 :events '(act automatic)
750 :result "ḩi there"
751 :macro-result "\C-cxq17051i there")
752 (kmacro-tests-run-step-edit "g\C-cxq17051i"
753 :events '(act insert-1 automatic)
754 :sequences '("-")
755 :result "g-ḩi"
756 :macro-result "g-\C-cxq17051i")))
757
758(kmacro-tests-deftest kmacro-tests-step-edit-can-replace-meta-keys ()
759 "Replacing C-w with M-w produces the expected result.
760This is a regression for item 7 in Bug#24991."
761 (:expected-result :failed)
762 (kmacro-tests-run-step-edit "abc\C-b\C-b\C-SPC\C-f\C-w\C-e\C-y"
763 :events '(act-repeat act-repeat
764 act-repeat act-repeat
765 replace automatic)
766 :sequences '("\M-w" "\C-j")
767 :result "abcb"
768 :macro-result "abc\C-b\C-b\C-SPC\C-f\M-w\C-e\C-y")
769 (kmacro-tests-should-insert "abcb" (kmacro-call-macro nil)))
770
771(kmacro-tests-deftest kmacro-tests-step-edit-ignores-qr-map-commands ()
772 "Unimplemented commands from `query-replace-map' are ignored."
773 (kmacro-tests-run-step-edit "yep"
774 :events '(edit-replacement
775 act-and-show act-and-exit
776 delete-and-edit
777 recenter backup
778 scroll-up scroll-down
779 scroll-other-window
780 scroll-other-window-down
781 exit-prefix
782 act act act)
783 :result "yep"
784 :macro-result "yep"))
785
786(kmacro-tests-deftest
787 kmacro-tests-step-edit-edits-macro-with-extended-command ()
788 "Step-editing a macro which uses the minibuffer can change the macro."
789 (let ((mac (vconcat [?\M-x] "eval-expression" '[return]
790 "(insert-char (+ ?a \C-e" [?1] "))" '[return]))
791 (mac-after (vconcat [?\M-x] "eval-expression" '[return]
792 "(insert-char (+ ?a \C-e" [?2] "))" '[return])))
793
794 (kmacro-tests-run-step-edit mac
795 :events '(act act-repeat
796 act act-repeat act
797 replace-1 act-repeat act)
798 :sequences '("2")
799 :result "c"
800 :macro-result mac-after)))
801
802(kmacro-tests-deftest kmacro-tests-step-edit-step-through-isearch ()
803 "Step-editing can edit a macro which uses `isearch-backward' (Bug#22488)."
804 (:expected-result :failed)
805 (let ((mac (vconcat "test Input" '[return]
806 [?\C-r] "inp" '[return] "\C-cxu"))
807 (mac-after (vconcat "test input" '[return]
808 [?\C-r] "inp" '[return] "\C-cd")))
809
810 (kmacro-tests-run-step-edit mac
811 :events '(act-repeat act act
812 act-repeat act
813 replace-1)
814 :sequences '("\C-cd")
815 :result "test input\n"
816 :macro-result mac-after)))
817
818(kmacro-tests-deftest kmacro-tests-step-edit-cleans-up-hook ()
819 "Step-editing properly cleans up `post-command-hook.' (Bug #18708)"
820 (:expected-result :failed)
821 (let (post-command-hook)
822 (setq-local post-command-hook '(t))
823 (kmacro-tests-run-step-edit "x"
824 :events '(act)
825 :result "x"
826 :macro-result "x")
827 (kmacro-tests-simulate-command '(beginning-of-line))))
828
829(cl-defun kmacro-tests-run-step-edit
830 (macro &key events sequences result macro-result)
831 "Set up and run a test of `kmacro-step-edit-macro'.
832
833Run `kmacro-step-edit-macro' with MACRO defined as a keyboard macro
834and `read-event' and `read-key-sequence' set up to return items from
835EVENTS and SEQUENCES respectively. SEQUENCES may be nil, but
836EVENTS should not be. EVENTS should be a list of symbols bound
837in `kmacro-step-edit-map' or `query-replace' map, and this function
838will do the keymap lookup for you. SEQUENCES should contain
839return values for `read-key-sequence'.
840
841Before running the macro, the current buffer will be erased.
842RESULT is the string that should be inserted during the
843step-editing process, and MACRO-RESULT is the expected value of
844`last-kbd-macro' after the editing is complete."
845
846 (let* ((kmacro-tests-events (mapcar #'kmacro-tests-get-kmacro-step-edit-key events))
847 (kmacro-tests-sequences sequences))
848
849 (kmacro-tests-define-macro (string-to-vector macro))
850 (use-local-map kmacro-tests-keymap)
851 (erase-buffer)
852 (kmacro-step-edit-macro)
853 (when result
854 (should (equal result (buffer-string))))
855 (when macro-result
856 (should (equal last-kbd-macro (string-to-vector macro-result))))))
857
858;;; Utilities:
859
860(defun kmacro-tests-simulate-command (command &optional arg)
861 "Call `ert-simulate-command' after setting `current-prefix-arg'.
862Sets `current-prefix-arg' to ARG if it is non-nil, otherwise to
863the second element of COMMAND, before executing COMMAND using
864`ert-simulate-command'."
865 (let ((current-prefix-arg (or arg (cadr command))))
866 (ert-simulate-command command)))
867
868(defun kmacro-tests-define-macro (mac)
869 "Define MAC as a keyboard macro using kmacro commands."
870 (push mac kmacro-tests-macros)
871 (kmacro-tests-simulate-command '(kmacro-start-macro nil))
872 (should defining-kbd-macro)
873 (kmacro-tests-simulate-command '(kmacro-end-macro nil))
874 (should (equal mac last-kbd-macro)))
875
876(defun kmacro-tests-get-kmacro-key (sym)
877 "Look up kmacro command SYM in kmacro's keymap.
878Return the integer key value found."
879 (aref (where-is-internal sym kmacro-keymap t) 0))
880
881(defun kmacro-tests-get-kmacro-step-edit-key (sym)
882 "Return the first key bound to SYM in `kmacro-step-edit-map'."
883 (let ((where (aref (where-is-internal sym kmacro-step-edit-map t) 0)))
884 (if (consp where)
885 (car where)
886 where)))
887
888(provide 'kmacro-tests)
889
890;;; kmacro-tests.el ends here