Commit | Line | Data |
---|---|---|
3b8d5131 JT |
1 | ;;; electric-tests.el --- tests for electric.el |
2 | ||
3 | ;; Copyright (C) 2013 João Távora | |
4 | ||
5 | ;; Author: João Távora <joaotavora@gmail.com> | |
6 | ;; Keywords: | |
7 | ||
8 | ;; This program is free software; you can redistribute it and/or modify | |
9 | ;; it under the terms of the GNU General Public License as published by | |
10 | ;; the Free Software Foundation, either version 3 of the License, or | |
11 | ;; (at your option) any later version. | |
12 | ||
13 | ;; This program is distributed in the hope that it will be useful, | |
14 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | ;; GNU General Public License for more details. | |
17 | ||
18 | ;; You should have received a copy of the GNU General Public License | |
19 | ;; along with this program. If not, see <http://www.gnu.org/licenses/>. | |
20 | ||
21 | ;;; Commentary: Tests for Electric Pair mode. | |
22 | ;;; TODO: Add tests for other Electric-* functionality | |
23 | ||
24 | ;; | |
25 | ||
26 | ;;; Code: | |
27 | (require 'ert) | |
28 | (require 'ert-x) | |
29 | (require 'electric) | |
30 | (require 'cl-lib) | |
31 | ||
32 | (defun call-with-saved-electric-modes (fn) | |
33 | (let ((saved-electric (if electric-pair-mode 1 -1)) | |
34 | (saved-layout (if electric-layout-mode 1 -1)) | |
35 | (saved-indent (if electric-indent-mode 1 -1))) | |
36 | (electric-pair-mode -1) | |
37 | (electric-layout-mode -1) | |
38 | (electric-indent-mode -1) | |
39 | (unwind-protect | |
40 | (funcall fn) | |
41 | (electric-pair-mode saved-electric) | |
42 | (electric-indent-mode saved-indent) | |
43 | (electric-layout-mode saved-layout)))) | |
44 | ||
45 | (defmacro save-electric-modes (&rest body) | |
46 | (declare (indent defun) (debug t)) | |
47 | `(call-with-saved-electric-modes #'(lambda () ,@body))) | |
48 | ||
49 | (defun electric-pair-test-for (fixture where char expected-string | |
50 | expected-point mode bindings fixture-fn) | |
51 | (with-temp-buffer | |
52 | (funcall mode) | |
53 | (insert fixture) | |
54 | (save-electric-modes | |
55 | (let ((last-command-event char)) | |
56 | (goto-char where) | |
57 | (funcall fixture-fn) | |
58 | (cl-progv | |
59 | (mapcar #'car bindings) | |
60 | (mapcar #'cdr bindings) | |
61 | (self-insert-command 1)))) | |
62 | (should (equal (buffer-substring-no-properties (point-min) (point-max)) | |
63 | expected-string)) | |
64 | (should (equal (point) | |
65 | expected-point)))) | |
66 | ||
67 | (eval-when-compile | |
68 | (defun electric-pair-define-test-form (name fixture | |
69 | char | |
70 | pos | |
71 | expected-string | |
72 | expected-point | |
73 | skip-pair-string | |
74 | prefix | |
75 | suffix | |
76 | extra-desc | |
77 | mode | |
78 | bindings | |
79 | fixture-fn) | |
80 | (let* ((expected-string-and-point | |
81 | (if skip-pair-string | |
82 | (with-temp-buffer | |
83 | (cl-progv | |
84 | ;; FIXME: avoid `eval' | |
85 | (mapcar #'car (eval bindings)) | |
86 | (mapcar #'cdr (eval bindings)) | |
87 | (funcall mode) | |
88 | (insert fixture) | |
89 | (goto-char (1+ pos)) | |
90 | (insert char) | |
91 | (cond ((eq (aref skip-pair-string pos) | |
92 | ?p) | |
93 | (insert (cadr (electric-pair-syntax-info char))) | |
94 | (backward-char 1)) | |
95 | ((eq (aref skip-pair-string pos) | |
96 | ?s) | |
97 | (delete-char -1) | |
98 | (forward-char 1))) | |
99 | (list | |
100 | (buffer-substring-no-properties (point-min) (point-max)) | |
101 | (point)))) | |
102 | (list expected-string expected-point))) | |
103 | (expected-string (car expected-string-and-point)) | |
104 | (expected-point (cadr expected-string-and-point)) | |
105 | (fixture (format "%s%s%s" prefix fixture suffix)) | |
106 | (expected-string (format "%s%s%s" prefix expected-string suffix)) | |
107 | (expected-point (+ (length prefix) expected-point)) | |
108 | (pos (+ (length prefix) pos))) | |
109 | `(ert-deftest ,(intern (format "electric-pair-%s-at-point-%s-in-%s%s" | |
110 | name | |
111 | (1+ pos) | |
112 | mode | |
113 | extra-desc)) | |
114 | () | |
115 | ,(format "With \"%s\", try input %c at point %d. \ | |
116 | Should %s \"%s\" and point at %d" | |
117 | fixture | |
118 | char | |
119 | (1+ pos) | |
120 | (if (string= fixture expected-string) | |
121 | "stay" | |
122 | "become") | |
123 | (replace-regexp-in-string "\n" "\\\\n" expected-string) | |
124 | expected-point) | |
125 | (electric-pair-test-for ,fixture | |
126 | ,(1+ pos) | |
127 | ,char | |
128 | ,expected-string | |
129 | ,expected-point | |
130 | ',mode | |
131 | ,bindings | |
132 | ,fixture-fn))))) | |
133 | ||
134 | (cl-defmacro define-electric-pair-test | |
135 | (name fixture | |
136 | input | |
137 | &key | |
138 | skip-pair-string | |
139 | expected-string | |
140 | expected-point | |
141 | bindings | |
142 | (modes '(quote (emacs-lisp-mode ruby-mode c++-mode))) | |
143 | (test-in-comments t) | |
144 | (test-in-strings t) | |
145 | (test-in-code t) | |
146 | (fixture-fn #'(lambda () | |
147 | (electric-pair-mode 1)))) | |
148 | `(progn | |
149 | ,@(cl-loop | |
150 | for mode in (eval modes) ;FIXME: avoid `eval' | |
151 | append | |
152 | (cl-loop | |
153 | for (prefix suffix extra-desc) in | |
154 | (append (if test-in-comments | |
155 | `((,(with-temp-buffer | |
156 | (funcall mode) | |
157 | (insert "z") | |
158 | (comment-region (point-min) (point-max)) | |
159 | (buffer-substring-no-properties (point-min) | |
160 | (1- (point-max)))) | |
161 | "" | |
162 | "-in-comments"))) | |
163 | (if test-in-strings | |
164 | `(("\"" "\"" "-in-strings"))) | |
165 | (if test-in-code | |
166 | `(("" "" "")))) | |
167 | append | |
168 | (cl-loop | |
169 | for char across input | |
170 | for pos from 0 | |
171 | unless (eq char ?-) | |
172 | collect (electric-pair-define-test-form | |
173 | name | |
174 | fixture | |
175 | (aref input pos) | |
176 | pos | |
177 | expected-string | |
178 | expected-point | |
179 | skip-pair-string | |
180 | prefix | |
181 | suffix | |
182 | extra-desc | |
183 | mode | |
184 | bindings | |
185 | fixture-fn)))))) | |
186 | \f | |
187 | ;;; Basic pairings and skippings | |
188 | ;;; | |
189 | (define-electric-pair-test balanced-situation | |
190 | " (()) " "(((((((" :skip-pair-string "ppppppp" | |
191 | :modes '(ruby-mode)) | |
192 | ||
193 | (define-electric-pair-test too-many-openings | |
194 | " ((()) " "(((((((" :skip-pair-string "ppppppp") | |
195 | ||
196 | (define-electric-pair-test too-many-closings | |
197 | " (())) " "(((((((" :skip-pair-string "------p") | |
198 | ||
199 | (define-electric-pair-test too-many-closings-2 | |
200 | "() ) " "---(---" :skip-pair-string "-------") | |
201 | ||
202 | (define-electric-pair-test too-many-closings-3 | |
203 | ")() " "(------" :skip-pair-string "-------") | |
204 | ||
205 | (define-electric-pair-test balanced-autoskipping | |
206 | " (()) " "---))--" :skip-pair-string "---ss--") | |
207 | ||
208 | (define-electric-pair-test too-many-openings-autoskipping | |
209 | " ((()) " "----))-" :skip-pair-string "-------") | |
210 | ||
211 | (define-electric-pair-test too-many-closings-autoskipping | |
212 | " (())) " "---)))-" :skip-pair-string "---sss-") | |
213 | ||
214 | \f | |
215 | ;;; Mixed parens | |
216 | ;;; | |
217 | (define-electric-pair-test mixed-paren-1 | |
218 | " ()] " "-(-(---" :skip-pair-string "-p-p---") | |
219 | ||
220 | (define-electric-pair-test mixed-paren-2 | |
221 | " [() " "-(-()--" :skip-pair-string "-p-ps--") | |
222 | ||
223 | (define-electric-pair-test mixed-paren-3 | |
224 | " (]) " "-(-()--" :skip-pair-string "---ps--") | |
225 | ||
226 | (define-electric-pair-test mixed-paren-4 | |
227 | " ()] " "---)]--" :skip-pair-string "---ss--") | |
228 | ||
229 | (define-electric-pair-test mixed-paren-5 | |
230 | " [() " "----(--" :skip-pair-string "----p--") | |
231 | ||
232 | (define-electric-pair-test find-matching-different-paren-type | |
233 | " ()] " "-[-----" :skip-pair-string "-------") | |
234 | ||
235 | (define-electric-pair-test find-matching-different-paren-type-inside-list | |
236 | "( ()]) " "-[-----" :skip-pair-string "-------") | |
237 | ||
238 | (define-electric-pair-test ignore-different-unmatching-paren-type | |
239 | "( ()]) " "-(-----" :skip-pair-string "-p-----") | |
240 | ||
241 | (define-electric-pair-test autopair-keep-least-amount-of-mixed-unbalance | |
242 | "( ()] " "-(-----" :skip-pair-string "-p-----") | |
243 | ||
244 | (define-electric-pair-test dont-autopair-to-resolve-mixed-unbalance | |
245 | "( ()] " "-[-----" :skip-pair-string "-------") | |
246 | ||
247 | (define-electric-pair-test autopair-so-as-not-to-worsen-unbalance-situation | |
248 | "( (]) " "-[-----" :skip-pair-string "-p-----") | |
249 | ||
250 | (define-electric-pair-test skip-over-partially-balanced | |
251 | " [([]) " "-----)---" :skip-pair-string "-----s---") | |
252 | ||
253 | (define-electric-pair-test only-skip-over-at-least-partially-balanced-stuff | |
254 | " [([()) " "-----))--" :skip-pair-string "-----s---") | |
255 | ||
256 | ||
257 | ||
258 | \f | |
259 | ;;; Quotes | |
260 | ;;; | |
261 | (define-electric-pair-test pair-some-quotes-skip-others | |
262 | " \"\" " "-\"\"-----" :skip-pair-string "-ps------" | |
263 | :test-in-strings nil | |
264 | :bindings `((electric-pair-text-syntax-table | |
265 | . ,prog-mode-syntax-table))) | |
266 | ||
267 | (define-electric-pair-test skip-single-quotes-in-ruby-mode | |
268 | " '' " "--'-" :skip-pair-string "--s-" | |
269 | :modes '(ruby-mode) | |
270 | :test-in-comments nil | |
271 | :test-in-strings nil | |
272 | :bindings `((electric-pair-text-syntax-table | |
273 | . ,prog-mode-syntax-table))) | |
274 | ||
275 | (define-electric-pair-test leave-unbalanced-quotes-alone | |
276 | " \"' " "-\"'-" :skip-pair-string "----" | |
277 | :modes '(ruby-mode) | |
278 | :test-in-strings nil | |
279 | :bindings `((electric-pair-text-syntax-table | |
280 | . ,prog-mode-syntax-table))) | |
281 | ||
282 | (define-electric-pair-test leave-unbalanced-quotes-alone-2 | |
283 | " \"\\\"' " "-\"--'-" :skip-pair-string "------" | |
284 | :modes '(ruby-mode) | |
285 | :test-in-strings nil | |
286 | :bindings `((electric-pair-text-syntax-table | |
287 | . ,prog-mode-syntax-table))) | |
288 | ||
289 | (define-electric-pair-test leave-unbalanced-quotes-alone-3 | |
290 | " foo\\''" "'------" :skip-pair-string "-------" | |
291 | :modes '(ruby-mode) | |
292 | :test-in-strings nil | |
293 | :bindings `((electric-pair-text-syntax-table | |
294 | . ,prog-mode-syntax-table))) | |
295 | ||
296 | (define-electric-pair-test inhibit-only-if-next-is-mismatched | |
297 | "\"foo\"\"bar" "\"" | |
298 | :expected-string "\"\"\"foo\"\"bar" | |
299 | :expected-point 2 | |
300 | :test-in-strings nil | |
301 | :bindings `((electric-pair-text-syntax-table | |
302 | . ,prog-mode-syntax-table))) | |
303 | ||
304 | \f | |
305 | ;;; More quotes, but now don't bind `electric-pair-text-syntax-table' | |
306 | ;;; to `prog-mode-syntax-table'. Use the defaults for | |
307 | ;;; `electric-pair-pairs' and `electric-pair-text-pairs'. | |
308 | ;;; | |
309 | (define-electric-pair-test pairing-skipping-quotes-in-code | |
310 | " \"\" " "-\"\"-----" :skip-pair-string "-ps------" | |
311 | :test-in-strings nil | |
312 | :test-in-comments nil) | |
313 | ||
314 | (define-electric-pair-test skipping-quotes-in-comments | |
315 | " \"\" " "--\"-----" :skip-pair-string "--s------" | |
316 | :test-in-strings nil) | |
317 | ||
318 | \f | |
319 | ;;; Skipping over whitespace | |
320 | ;;; | |
321 | (define-electric-pair-test whitespace-jumping | |
322 | " ( ) " "--))))---" :expected-string " ( ) " :expected-point 8 | |
323 | :bindings '((electric-pair-skip-whitespace . t))) | |
324 | ||
325 | (define-electric-pair-test whitespace-chomping | |
326 | " ( ) " "--)------" :expected-string " () " :expected-point 4 | |
327 | :bindings '((electric-pair-skip-whitespace . chomp))) | |
328 | ||
329 | (define-electric-pair-test whitespace-chomping-2 | |
330 | " ( \n\t\t\n ) " "--)------" :expected-string " () " :expected-point 4 | |
331 | :bindings '((electric-pair-skip-whitespace . chomp)) | |
332 | :test-in-comments nil) | |
333 | ||
334 | (define-electric-pair-test whitespace-chomping-dont-cross-comments | |
335 | " ( \n\t\t\n ) " "--)------" :expected-string " () \n\t\t\n ) " | |
336 | :expected-point 4 | |
337 | :bindings '((electric-pair-skip-whitespace . chomp)) | |
338 | :test-in-strings nil | |
339 | :test-in-code nil | |
340 | :test-in-comments t) | |
341 | ||
342 | \f | |
343 | ;;; Pairing arbitrary characters | |
344 | ;;; | |
345 | (define-electric-pair-test angle-brackets-everywhere | |
346 | "<>" "<>" :skip-pair-string "ps" | |
347 | :bindings '((electric-pair-pairs . ((?\< . ?\>))))) | |
348 | ||
349 | (define-electric-pair-test angle-brackets-everywhere-2 | |
350 | "(<>" "-<>" :skip-pair-string "-ps" | |
351 | :bindings '((electric-pair-pairs . ((?\< . ?\>))))) | |
352 | ||
353 | (defvar electric-pair-test-angle-brackets-table | |
354 | (let ((table (make-syntax-table prog-mode-syntax-table))) | |
355 | (modify-syntax-entry ?\< "(>" table) | |
356 | (modify-syntax-entry ?\> ")<`" table) | |
357 | table)) | |
358 | ||
359 | (define-electric-pair-test angle-brackets-pair | |
360 | "<>" "<" :expected-string "<><>" :expected-point 2 | |
361 | :test-in-code nil | |
362 | :bindings `((electric-pair-text-syntax-table | |
363 | . ,electric-pair-test-angle-brackets-table))) | |
364 | ||
365 | (define-electric-pair-test angle-brackets-skip | |
366 | "<>" "->" :expected-string "<>" :expected-point 3 | |
367 | :test-in-code nil | |
368 | :bindings `((electric-pair-text-syntax-table | |
369 | . ,electric-pair-test-angle-brackets-table))) | |
370 | ||
371 | (define-electric-pair-test pair-backtick-and-quote-in-comments | |
372 | ";; " "---`" :expected-string ";; `'" :expected-point 5 | |
373 | :test-in-comments nil | |
374 | :test-in-strings nil | |
375 | :modes '(emacs-lisp-mode) | |
376 | :bindings '((electric-pair-text-pairs . ((?\` . ?\'))))) | |
377 | ||
378 | (define-electric-pair-test skip-backtick-and-quote-in-comments | |
379 | ";; `foo'" "-------'" :expected-string ";; `foo'" :expected-point 9 | |
380 | :test-in-comments nil | |
381 | :test-in-strings nil | |
382 | :modes '(emacs-lisp-mode) | |
383 | :bindings '((electric-pair-text-pairs . ((?\` . ?\'))))) | |
384 | ||
385 | (define-electric-pair-test pair-backtick-and-quote-in-strings | |
386 | "\"\"" "-`" :expected-string "\"`'\"" :expected-point 3 | |
387 | :test-in-comments nil | |
388 | :test-in-strings nil | |
389 | :modes '(emacs-lisp-mode) | |
390 | :bindings '((electric-pair-text-pairs . ((?\` . ?\'))))) | |
391 | ||
392 | (define-electric-pair-test skip-backtick-and-quote-in-strings | |
393 | "\"`'\"" "--'" :expected-string "\"`'\"" :expected-point 4 | |
394 | :test-in-comments nil | |
395 | :test-in-strings nil | |
396 | :modes '(emacs-lisp-mode) | |
397 | :bindings '((electric-pair-text-pairs . ((?\` . ?\'))))) | |
398 | ||
399 | (define-electric-pair-test skip-backtick-and-quote-in-strings-2 | |
400 | " \"`'\"" "----'" :expected-string " \"`'\"" :expected-point 6 | |
401 | :test-in-comments nil | |
402 | :test-in-strings nil | |
403 | :modes '(emacs-lisp-mode) | |
404 | :bindings '((electric-pair-text-pairs . ((?\` . ?\'))))) | |
405 | ||
406 | \f | |
407 | ;;; `js-mode' has `electric-layout-rules' for '{ and '} | |
408 | ;;; | |
409 | (define-electric-pair-test js-mode-braces | |
410 | "" "{" :expected-string "{}" :expected-point 2 | |
411 | :modes '(js-mode) | |
412 | :fixture-fn #'(lambda () | |
413 | (electric-pair-mode 1))) | |
414 | ||
415 | (define-electric-pair-test js-mode-braces-with-layout | |
416 | "" "{" :expected-string "{\n\n}" :expected-point 3 | |
417 | :modes '(js-mode) | |
418 | :test-in-comments nil | |
419 | :test-in-strings nil | |
420 | :fixture-fn #'(lambda () | |
421 | (electric-layout-mode 1) | |
422 | (electric-pair-mode 1))) | |
423 | ||
424 | (define-electric-pair-test js-mode-braces-with-layout-and-indent | |
425 | "" "{" :expected-string "{\n \n}" :expected-point 7 | |
426 | :modes '(js-mode) | |
427 | :test-in-comments nil | |
428 | :test-in-strings nil | |
429 | :fixture-fn #'(lambda () | |
430 | (electric-pair-mode 1) | |
431 | (electric-indent-mode 1) | |
432 | (electric-layout-mode 1))) | |
433 | ||
434 | \f | |
435 | ;;; Backspacing | |
436 | ;;; TODO: better tests | |
437 | ;;; | |
438 | (ert-deftest electric-pair-backspace-1 () | |
439 | (save-electric-modes | |
440 | (with-temp-buffer | |
441 | (insert "()") | |
442 | (goto-char 2) | |
443 | (electric-pair-backward-delete-char 1) | |
444 | (should (equal "" (buffer-string)))))) | |
445 | ||
446 | \f | |
447 | ;;; Electric newlines between pairs | |
448 | ;;; TODO: better tests | |
449 | (ert-deftest electric-pair-open-extra-newline () | |
450 | (save-electric-modes | |
451 | (with-temp-buffer | |
452 | (c-mode) | |
453 | (electric-pair-mode 1) | |
454 | (electric-indent-mode 1) | |
455 | (insert "int main {}") | |
456 | (backward-char 1) | |
457 | (let ((c-basic-offset 4)) | |
458 | (newline 1 t) | |
459 | (should (equal "int main {\n \n}" | |
460 | (buffer-string))) | |
461 | (should (equal (point) (- (point-max) 2))))))) | |
462 | ||
463 | ||
464 | \f | |
465 | ;;; Autowrapping | |
466 | ;;; | |
467 | (define-electric-pair-test autowrapping-1 | |
468 | "foo" "(" :expected-string "(foo)" :expected-point 2 | |
469 | :fixture-fn #'(lambda () | |
470 | (electric-pair-mode 1) | |
471 | (mark-sexp 1))) | |
472 | ||
473 | (define-electric-pair-test autowrapping-2 | |
474 | "foo" ")" :expected-string "(foo)" :expected-point 6 | |
475 | :fixture-fn #'(lambda () | |
476 | (electric-pair-mode 1) | |
477 | (mark-sexp 1))) | |
478 | ||
479 | (define-electric-pair-test autowrapping-3 | |
480 | "foo" ")" :expected-string "(foo)" :expected-point 6 | |
481 | :fixture-fn #'(lambda () | |
482 | (electric-pair-mode 1) | |
483 | (goto-char (point-max)) | |
484 | (skip-chars-backward "\"") | |
485 | (mark-sexp -1))) | |
486 | ||
487 | (define-electric-pair-test autowrapping-4 | |
488 | "foo" "(" :expected-string "(foo)" :expected-point 2 | |
489 | :fixture-fn #'(lambda () | |
490 | (electric-pair-mode 1) | |
491 | (goto-char (point-max)) | |
492 | (skip-chars-backward "\"") | |
493 | (mark-sexp -1))) | |
494 | ||
495 | (define-electric-pair-test autowrapping-5 | |
496 | "foo" "\"" :expected-string "\"foo\"" :expected-point 2 | |
497 | :fixture-fn #'(lambda () | |
498 | (electric-pair-mode 1) | |
499 | (mark-sexp 1))) | |
500 | ||
501 | (define-electric-pair-test autowrapping-6 | |
502 | "foo" "\"" :expected-string "\"foo\"" :expected-point 6 | |
503 | :fixture-fn #'(lambda () | |
504 | (electric-pair-mode 1) | |
505 | (goto-char (point-max)) | |
506 | (skip-chars-backward "\"") | |
507 | (mark-sexp -1))) | |
508 | ||
509 | (provide 'electric-tests) | |
510 | ;;; electric-tests.el ends here |