]> code.delx.au - gnu-emacs-elpa/blob - yasnippet-tests.el
Don't indent empty lines in snippet expansion
[gnu-emacs-elpa] / yasnippet-tests.el
1 ;;; yasnippet-tests.el --- some yasnippet tests -*- lexical-binding: t -*-
2
3 ;; Copyright (C) 2012, 2013, 2014, 2015 Free Software Foundation, Inc.
4
5 ;; Author: João Távora <joaot@siscog.pt>
6 ;; Keywords: emulations, convenience
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:
22
23 ;; Test basic snippet mechanics and the loading system
24
25 ;;; Code:
26
27 (require 'yasnippet)
28 (require 'ert)
29 (require 'ert-x)
30 (require 'cl)
31
32 \f
33 ;;; Snippet mechanics
34
35 (defun yas--buffer-contents ()
36 (buffer-substring-no-properties (point-min) (point-max)))
37
38 (ert-deftest field-navigation ()
39 (with-temp-buffer
40 (yas-minor-mode 1)
41 (yas-expand-snippet "${1:brother} from another ${2:mother}")
42 (should (string= (yas--buffer-contents)
43 "brother from another mother"))
44
45 (should (looking-at "brother"))
46 (ert-simulate-command '(yas-next-field-or-maybe-expand))
47 (should (looking-at "mother"))
48 (ert-simulate-command '(yas-prev-field))
49 (should (looking-at "brother"))))
50
51 (ert-deftest simple-mirror ()
52 (with-temp-buffer
53 (yas-minor-mode 1)
54 (yas-expand-snippet "${1:brother} from another $1")
55 (should (string= (yas--buffer-contents)
56 "brother from another brother"))
57 (yas-mock-insert "bla")
58 (should (string= (yas--buffer-contents)
59 "bla from another bla"))))
60
61 (ert-deftest mirror-with-transformation ()
62 (with-temp-buffer
63 (yas-minor-mode 1)
64 (yas-expand-snippet "${1:brother} from another ${1:$(upcase yas-text)}")
65 (should (string= (yas--buffer-contents)
66 "brother from another BROTHER"))
67 (yas-mock-insert "bla")
68 (should (string= (yas--buffer-contents)
69 "bla from another BLA"))))
70
71 (ert-deftest mirror-with-transformation-and-autofill ()
72 "Test interaction of autofill with mirror transforms"
73 (let ((words "one two three four five")
74 filled-words)
75 (with-temp-buffer
76 (c-mode) ; In `c-mode' filling comments works by narrowing.
77 (yas-minor-mode +1)
78 (setq fill-column 10)
79 (auto-fill-mode +1)
80 (yas-expand-snippet "/* $0\n */")
81 (yas-mock-insert words)
82 (setq filled-words (delete-and-extract-region (point-min) (point-max)))
83 (yas-expand-snippet "/* $1\n */\n$2$2")
84 (should (string= (yas--buffer-contents)
85 "/* \n */\n"))
86 (yas-mock-insert words)
87 (should (string= (yas--buffer-contents)
88 (concat filled-words "\n"))))))
89
90
91 (ert-deftest primary-field-transformation ()
92 (with-temp-buffer
93 (yas-minor-mode 1)
94 (let ((snippet "${1:$$(upcase yas-text)}${1:$(concat \"bar\" yas-text)}"))
95 (yas-expand-snippet snippet)
96 (should (string= (yas--buffer-contents) "bar"))
97 (yas-mock-insert "foo")
98 (should (string= (yas--buffer-contents) "FOObarFOO")))))
99
100 (ert-deftest nested-placeholders-kill-superfield ()
101 (with-temp-buffer
102 (yas-minor-mode 1)
103 (yas-expand-snippet "brother from ${2:another ${3:mother}}!")
104 (should (string= (yas--buffer-contents)
105 "brother from another mother!"))
106 (yas-mock-insert "bla")
107 (should (string= (yas--buffer-contents)
108 "brother from bla!"))))
109
110 (ert-deftest nested-placeholders-use-subfield ()
111 (with-temp-buffer
112 (yas-minor-mode 1)
113 (yas-expand-snippet "brother from ${2:another ${3:mother}}!")
114 (ert-simulate-command '(yas-next-field-or-maybe-expand))
115 (yas-mock-insert "bla")
116 (should (string= (yas--buffer-contents)
117 "brother from another bla!"))))
118
119 (ert-deftest mirrors-adjacent-to-fields-with-nested-mirrors ()
120 (with-temp-buffer
121 (yas-minor-mode 1)
122 (yas-expand-snippet "<%= f.submit \"${1:Submit}\"${2:$(and (yas-text) \", :disable_with => '\")}${2:$1ing...}${2:$(and (yas-text) \"'\")} %>")
123 (should (string= (yas--buffer-contents)
124 "<%= f.submit \"Submit\", :disable_with => 'Submiting...' %>"))
125 (yas-mock-insert "Send")
126 (should (string= (yas--buffer-contents)
127 "<%= f.submit \"Send\", :disable_with => 'Sending...' %>"))))
128
129 (ert-deftest deep-nested-mirroring-issue-351 ()
130 (with-temp-buffer
131 (yas-minor-mode 1)
132 (yas-expand-snippet "${1:FOOOOOOO}${2:$1}${3:$2}${4:$3}")
133 (yas-mock-insert "abc")
134 (should (string= (yas--buffer-contents) "abcabcabcabc"))))
135
136 (ert-deftest delete-numberless-inner-snippet-issue-562 ()
137 (with-temp-buffer
138 (yas-minor-mode 1)
139 (yas-expand-snippet "${3:${test}bla}$0${2:ble}")
140 (ert-simulate-command '(yas-next-field-or-maybe-expand))
141 (should (looking-at "testblable"))
142 (ert-simulate-command '(yas-next-field-or-maybe-expand))
143 (ert-simulate-command '(yas-skip-and-clear-or-delete-char))
144 (should (looking-at "ble"))
145 (should (null (yas--snippets-at-point)))))
146
147 (ert-deftest ignore-trailing-whitespace ()
148 (should (equal
149 (with-temp-buffer
150 (insert "# key: foo\n# --\nfoo")
151 (yas--parse-template))
152 (with-temp-buffer
153 (insert "# key: foo \n# --\nfoo")
154 (yas--parse-template)))))
155
156 ;; (ert-deftest in-snippet-undo ()
157 ;; (with-temp-buffer
158 ;; (yas-minor-mode 1)
159 ;; (yas-expand-snippet "brother from ${2:another ${3:mother}}!")
160 ;; (ert-simulate-command '(yas-next-field-or-maybe-expand))
161 ;; (yas-mock-insert "bla")
162 ;; (ert-simulate-command '(undo))
163 ;; (should (string= (yas--buffer-contents)
164 ;; "brother from another mother!"))))
165
166 (ert-deftest dont-clear-on-partial-deletion-issue-515 ()
167 "Ensure fields are not cleared when user doesn't really mean to."
168 (with-temp-buffer
169 (yas-minor-mode 1)
170 (yas-expand-snippet "my ${1:kid brother} from another ${2:mother}")
171
172 (ert-simulate-command '(kill-word 1))
173 (ert-simulate-command '(delete-char 1))
174
175 (should (string= (yas--buffer-contents)
176 "my brother from another mother"))
177 (should (looking-at "brother"))
178
179 (ert-simulate-command '(yas-next-field))
180 (should (looking-at "mother"))
181 (ert-simulate-command '(yas-prev-field))
182 (should (looking-at "brother"))))
183
184 (ert-deftest do-clear-on-yank-issue-515 ()
185 "A yank should clear an unmodified field."
186 (with-temp-buffer
187 (yas-minor-mode 1)
188 (yas-expand-snippet "my ${1:kid brother} from another ${2:mother}")
189 (yas-mock-yank "little sibling")
190 (should (string= (yas--buffer-contents)
191 "my little sibling from another mother"))
192 (ert-simulate-command '(yas-next-field))
193 (ert-simulate-command '(yas-prev-field))
194 (should (looking-at "little sibling"))))
195
196 (ert-deftest basic-indentation ()
197 (with-temp-buffer
198 (ruby-mode)
199 (yas-minor-mode 1)
200 (set (make-local-variable 'yas-indent-line) 'auto)
201 (set (make-local-variable 'yas-also-auto-indent-first-line) t)
202 (yas-expand-snippet "def ${1:method}${2:(${3:args})}\n$0\nend")
203 ;; Note that empty line is not indented.
204 (should (string= "def method(args)
205
206 end" (buffer-string)))
207 (cl-loop repeat 3 do (ert-simulate-command '(yas-next-field)))
208 (yas-mock-insert (make-string (random 5) ?\ )) ; purposedly mess up indentation
209 (yas-expand-snippet "class << ${self}\n $0\nend")
210 (ert-simulate-command '(yas-next-field))
211 (should (string= "def method(args)
212 class << self
213
214 end
215 end" (buffer-string)))
216 (should (= 4 (current-column)))))
217
218 (ert-deftest navigate-a-snippet-with-multiline-mirrors-issue-665 ()
219 "In issue 665, a multi-line mirror is attempted.
220
221 Indentation doesn't (yet) happen on these mirrors, but let this
222 test guard against any misnavigations that might be introduced by
223 an incorrect implementation of mirror auto-indentation"
224 (with-temp-buffer
225 (ruby-mode)
226 (yas-minor-mode 1)
227 (yas-expand-snippet "def initialize(${1:params})\n$2${1:$(
228 mapconcat #'(lambda (arg)
229 (format \"@%s = %s\" arg arg))
230 (split-string yas-text \", \")
231 \"\n\")}\nend")
232 (yas-mock-insert "bla, ble, bli")
233 (ert-simulate-command '(yas-next-field))
234 (let ((expected (mapconcat #'identity
235 '("@bla = bla"
236 "[[:blank:]]*@ble = ble"
237 "[[:blank:]]*@bli = bli")
238 "\n")))
239 (should (looking-at expected))
240 (yas-mock-insert "blo")
241 (ert-simulate-command '(yas-prev-field))
242 (ert-simulate-command '(yas-next-field))
243 (should (looking-at (concat "blo" expected))))))
244
245 \f
246 ;;; Snippet expansion and character escaping
247 ;;; Thanks to @zw963 (Billy) for the testing
248 ;;;
249 (ert-deftest escape-dollar ()
250 (with-temp-buffer
251 (yas-minor-mode 1)
252 (yas-expand-snippet "bla\\${1:bla}ble")
253 (should (string= (yas--buffer-contents) "bla${1:bla}ble"))))
254
255 (ert-deftest escape-closing-brace ()
256 (with-temp-buffer
257 (yas-minor-mode 1)
258 (yas-expand-snippet "bla${1:bla\\}}ble")
259 (should (string= (yas--buffer-contents) "blabla}ble"))
260 (should (string= (yas-field-value 1) "bla}"))))
261
262 (ert-deftest escape-backslashes ()
263 (with-temp-buffer
264 (yas-minor-mode 1)
265 (yas-expand-snippet "bla\\ble")
266 (should (string= (yas--buffer-contents) "bla\\ble"))))
267
268 (ert-deftest escape-backquotes ()
269 (with-temp-buffer
270 (yas-minor-mode 1)
271 (yas-expand-snippet "bla`(upcase \"foo\\`bar\")`ble")
272 (should (string= (yas--buffer-contents) "blaFOO`BARble"))))
273
274 (ert-deftest escape-some-elisp-with-strings ()
275 "elisp with strings and unbalance parens inside it"
276 (with-temp-buffer
277 (yas-minor-mode 1)
278 ;; The rules here is: to output a literal `"' you need to escape
279 ;; it with one backslash. You don't need to escape them in
280 ;; embedded elisp.
281 (yas-expand-snippet "soon \\\"`(concat (upcase \"(my arms\")\"\\\" were all around her\")`")
282 (should (string= (yas--buffer-contents) "soon \"(MY ARMS\" were all around her"))))
283
284 (ert-deftest escape-some-elisp-with-backslashes ()
285 (with-temp-buffer
286 (yas-minor-mode 1)
287 ;; And the rule here is: to output a literal `\' inside a string
288 ;; inside embedded elisp you need a total of six `\'
289 (yas-expand-snippet "bla`(upcase \"hey\\\\\\yo\")`ble")
290 (should (string= (yas--buffer-contents) "blaHEY\\YOble"))))
291
292 (ert-deftest be-careful-when-escaping-in-yas-selected-text ()
293 (with-temp-buffer
294 (yas-minor-mode 1)
295 (let ((yas-selected-text "He\\\\o world!"))
296 (yas-expand-snippet "Look ma! `(yas-selected-text)`")
297 (should (string= (yas--buffer-contents) "Look ma! He\\\\o world!")))
298 (yas-exit-all-snippets)
299 (erase-buffer)
300 (let ((yas-selected-text "He\"o world!"))
301 (yas-expand-snippet "Look ma! `(yas-selected-text)`")
302 (should (string= (yas--buffer-contents) "Look ma! He\"o world!")))
303 (yas-exit-all-snippets)
304 (erase-buffer)
305 (let ((yas-selected-text "He\"\)\\o world!"))
306 (yas-expand-snippet "Look ma! `(yas-selected-text)`")
307 (should (string= (yas--buffer-contents) "Look ma! He\"\)\\o world!")))
308 (yas-exit-all-snippets)
309 (erase-buffer)))
310
311 (ert-deftest be-careful-when-escaping-in-yas-selected-text-2 ()
312 (with-temp-buffer
313 (yas-minor-mode 1)
314 (let ((yas-selected-text "He)}o world!"))
315 (yas-expand-snippet "Look ma! ${1:`(yas-selected-text)`} OK?")
316 (should (string= (yas--buffer-contents) "Look ma! He)}o world! OK?")))))
317
318 (ert-deftest example-for-issue-271 ()
319 (with-temp-buffer
320 (yas-minor-mode 1)
321 (let ((yas-selected-text "aaa")
322 (snippet "if ${1:condition}\n`yas-selected-text`\nelse\n$3\nend"))
323 (yas-expand-snippet snippet)
324 (yas-next-field)
325 (yas-mock-insert "bbb")
326 (should (string= (yas--buffer-contents) "if condition\naaa\nelse\nbbb\nend")))))
327
328 (defmacro yas--with-font-locked-temp-buffer (&rest body)
329 "Like `with-temp-buffer', but ensure `font-lock-mode'."
330 (declare (indent 0) (debug t))
331 (let ((temp-buffer (make-symbol "temp-buffer")))
332 ;; NOTE: buffer name must not start with a space, otherwise
333 ;; `font-lock-mode' doesn't turn on.
334 `(let ((,temp-buffer (generate-new-buffer "*yas-temp*")))
335 (with-current-buffer ,temp-buffer
336 ;; pretend we're interactive so `font-lock-mode' turns on
337 (let ((noninteractive nil)
338 ;; turn on font locking after major mode change
339 (change-major-mode-after-body-hook #'font-lock-mode))
340 (unwind-protect
341 (progn (require 'font-lock)
342 ;; turn on font locking before major mode change
343 (font-lock-mode +1)
344 ,@body)
345 (and (buffer-name ,temp-buffer)
346 (kill-buffer ,temp-buffer))))))))
347
348 (defmacro yas-saving-variables (&rest body)
349 `(yas-call-with-saving-variables #'(lambda () ,@body)))
350
351 (defmacro yas-with-snippet-dirs (dirs &rest body)
352 (declare (indent defun))
353 `(yas-call-with-snippet-dirs ,dirs
354 #'(lambda ()
355 ,@body)))
356
357 (ert-deftest example-for-issue-474 ()
358 (yas--with-font-locked-temp-buffer
359 (c-mode)
360 (yas-minor-mode 1)
361 (insert "#include <foo>\n")
362 (let ((yas-good-grace nil)) (yas-expand-snippet "`\"TODO: \"`"))
363 (should (string= (yas--buffer-contents) "#include <foo>\nTODO: "))))
364
365 (ert-deftest example-for-issue-404 ()
366 (yas--with-font-locked-temp-buffer
367 (c++-mode)
368 (yas-minor-mode 1)
369 (insert "#include <foo>\n")
370 (let ((yas-good-grace nil)) (yas-expand-snippet "main"))
371 (should (string= (yas--buffer-contents) "#include <foo>\nmain"))))
372
373 (ert-deftest example-for-issue-404-c-mode ()
374 (yas--with-font-locked-temp-buffer
375 (c-mode)
376 (yas-minor-mode 1)
377 (insert "#include <foo>\n")
378 (let ((yas-good-grace nil)) (yas-expand-snippet "main"))
379 (should (string= (yas--buffer-contents) "#include <foo>\nmain"))))
380
381 (ert-deftest middle-of-buffer-snippet-insertion ()
382 (with-temp-buffer
383 (yas-minor-mode 1)
384 (insert "beginning")
385 (save-excursion (insert "end"))
386 (yas-expand-snippet "-middle-")
387 (should (string= (yas--buffer-contents) "beginning-middle-end"))))
388
389 (ert-deftest another-example-for-issue-271 ()
390 ;; expect this to fail in batch mode since `region-active-p' doesn't
391 ;; used by `yas-expand-snippet' doesn't make sense in that context.
392 ;;
393 :expected-result (if noninteractive
394 :failed
395 :passed)
396 (with-temp-buffer
397 (yas-minor-mode 1)
398 (let ((snippet "\\${${1:1}:`yas-selected-text`}"))
399 (insert "aaabbbccc")
400 (set-mark 4)
401 (goto-char 7)
402 (yas-expand-snippet snippet)
403 (should (string= (yas--buffer-contents) "aaa${1:bbb}ccc")))))
404
405 (ert-deftest string-match-with-subregexp-in-embedded-elisp ()
406 (with-temp-buffer
407 (yas-minor-mode 1)
408 ;; the rule here is: To use regexps in embedded `(elisp)` expressions, write
409 ;; it like you would normal elisp, i.e. no need to escape the backslashes.
410 (let ((snippet "`(if (string-match \"foo\\\\(ba+r\\\\)foo\" \"foobaaaaaaaaaarfoo\")
411 \"ok\"
412 \"fail\")`"))
413 (yas-expand-snippet snippet))
414 (should (string= (yas--buffer-contents) "ok"))))
415
416 (ert-deftest string-match-with-subregexp-in-mirror-transformations ()
417 (with-temp-buffer
418 (yas-minor-mode 1)
419 ;; the rule here is: To use regexps in embedded `(elisp)` expressions,
420 ;; escape backslashes once, i.e. to use \\( \\) constructs, write \\\\( \\\\).
421 (let ((snippet "$1${1:$(if (string-match \"foo\\\\\\\\(ba+r\\\\\\\\)baz\" yas-text)
422 \"ok\"
423 \"fail\")}"))
424 (yas-expand-snippet snippet)
425 (should (string= (yas--buffer-contents) "fail"))
426 (yas-mock-insert "foobaaar")
427 (should (string= (yas--buffer-contents) "foobaaarfail"))
428 (yas-mock-insert "baz")
429 (should (string= (yas--buffer-contents) "foobaaarbazok")))))
430
431 \f
432 ;;; Misc tests
433 ;;;
434 (ert-deftest protection-overlay-no-cheating ()
435 "Protection overlays at the very end of the buffer are dealt
436 with by cheatingly inserting a newline!
437
438 TODO: correct this bug!"
439 :expected-result :failed
440 (with-temp-buffer
441 (yas-minor-mode 1)
442 (yas-expand-snippet "${2:brother} from another ${1:mother}")
443 (should (string= (yas--buffer-contents)
444 "brother from another mother") ;; no newline should be here!
445 )))
446
447 (defvar yas--barbaz)
448 (defvar yas--foobarbaz)
449
450 ;; See issue #497. To understand this test, follow the example of the
451 ;; `yas-key-syntaxes' docstring.
452 ;;
453 (ert-deftest complicated-yas-key-syntaxes ()
454 (with-temp-buffer
455 (yas-saving-variables
456 (yas-with-snippet-dirs
457 '((".emacs.d/snippets"
458 ("emacs-lisp-mode"
459 ("foo-barbaz" . "# condition: yas--foobarbaz\n# --\nOKfoo-barbazOK")
460 ("barbaz" . "# condition: yas--barbaz\n# --\nOKbarbazOK")
461 ("baz" . "OKbazOK")
462 ("'quote" . "OKquoteOK"))))
463 (yas-reload-all)
464 (emacs-lisp-mode)
465 (yas-minor-mode-on)
466 (let ((yas-key-syntaxes '("w" "w_")))
467 (let ((yas--barbaz t))
468 (yas-should-expand '(("foo-barbaz" . "foo-OKbarbazOK")
469 ("barbaz" . "OKbarbazOK"))))
470 (let ((yas--foobarbaz t))
471 (yas-should-expand '(("foo-barbaz" . "OKfoo-barbazOK"))))
472 (let ((yas-key-syntaxes
473 (cons #'(lambda (_start-point)
474 (unless (looking-back "-")
475 (backward-char)
476 'again))
477 yas-key-syntaxes))
478 (yas--foobarbaz t))
479 (yas-should-expand '(("foo-barbaz" . "foo-barOKbazOK")))))
480 (let ((yas-key-syntaxes '(yas-try-key-from-whitespace)))
481 (yas-should-expand '(("xxx\n'quote" . "xxx\nOKquoteOK")
482 ("xxx 'quote" . "xxx OKquoteOK"))))
483 (let ((yas-key-syntaxes '(yas-shortest-key-until-whitespace))
484 (yas--foobarbaz t) (yas--barbaz t))
485 (yas-should-expand '(("foo-barbaz" . "foo-barOKbazOK")))
486 (setq yas-key-syntaxes '(yas-longest-key-from-whitespace))
487 (yas-should-expand '(("foo-barbaz" . "OKfoo-barbazOK")
488 ("foo " . "foo "))))))))
489
490 \f
491 ;;; Loading
492 ;;;
493 (defun yas--call-with-temporary-redefinitions (function
494 &rest function-names-and-overriding-functions)
495 (let* ((overrides (remove-if-not #'(lambda (fdef)
496 (fboundp (first fdef)))
497 function-names-and-overriding-functions))
498 (definition-names (mapcar #'first overrides))
499 (overriding-functions (mapcar #'second overrides))
500 (saved-functions (mapcar #'symbol-function definition-names)))
501 ;; saving all definitions before overriding anything ensures FDEFINITION
502 ;; errors don't cause accidental permanent redefinitions.
503 ;;
504 (cl-labels ((set-fdefinitions (names functions)
505 (loop for name in names
506 for fn in functions
507 do (fset name fn))))
508 (set-fdefinitions definition-names overriding-functions)
509 (unwind-protect (funcall function)
510 (set-fdefinitions definition-names saved-functions)))))
511
512 (defmacro yas--with-temporary-redefinitions (fdefinitions &rest body)
513 ;; "Temporarily (but globally) redefine each function in FDEFINITIONS.
514 ;; E.g.: (yas--with-temporary-redefinitions ((foo (x) ...)
515 ;; (bar (x) ...))
516 ;; ;; code that eventually calls foo, bar of (setf foo)
517 ;; ...)"
518 ;; FIXME: This is hideous! Better use defadvice (or at least letf).
519 `(yas--call-with-temporary-redefinitions
520 (lambda () ,@body)
521 ,@(mapcar #'(lambda (thingy)
522 `(list ',(first thingy)
523 (lambda ,@(rest thingy))))
524 fdefinitions)))
525
526 (defmacro yas-with-overriden-buffer-list (&rest body)
527 (let ((saved-sym (make-symbol "yas--buffer-list")))
528 `(let ((,saved-sym (symbol-function 'buffer-list)))
529 (yas--with-temporary-redefinitions
530 ((buffer-list ()
531 (remove-if #'(lambda (buf)
532 (with-current-buffer buf
533 (eq major-mode 'lisp-interaction-mode)))
534 (funcall ,saved-sym))))
535 ,@body))))
536
537
538 (defmacro yas-with-some-interesting-snippet-dirs (&rest body)
539 `(yas-saving-variables
540 (yas-with-overriden-buffer-list
541 (yas-with-snippet-dirs
542 '((".emacs.d/snippets"
543 ("c-mode"
544 (".yas-parents" . "cc-mode")
545 ("printf" . "printf($1);")) ;; notice the overriding for issue #281
546 ("emacs-lisp-mode" ("ert-deftest" . "(ert-deftest ${1:name} () $0)"))
547 ("lisp-interaction-mode" (".yas-parents" . "emacs-lisp-mode")))
548 ("library/snippets"
549 ("c-mode"
550 (".yas-parents" . "c++-mode")
551 ("printf" . "printf"))
552 ("cc-mode" ("def" . "# define"))
553 ("emacs-lisp-mode" ("dolist" . "(dolist)"))
554 ("lisp-interaction-mode" ("sc" . "brother from another mother"))))
555 ,@body))))
556
557 (ert-deftest snippet-lookup ()
558 "Test `yas-lookup-snippet'."
559 (yas-with-some-interesting-snippet-dirs
560 (yas-reload-all 'no-jit)
561 (should (equal (yas-lookup-snippet "printf" 'c-mode) "printf($1);"))
562 (should (equal (yas-lookup-snippet "def" 'c-mode) "# define"))
563 (should-not (yas-lookup-snippet "no such snippet" nil 'noerror))
564 (should-not (yas-lookup-snippet "printf" 'emacs-lisp-mode 'noerror))))
565
566 (ert-deftest basic-jit-loading ()
567 "Test basic loading and expansion of snippets"
568 (yas-with-some-interesting-snippet-dirs
569 (yas-reload-all)
570 (yas--basic-jit-loading-1)))
571
572 (ert-deftest basic-jit-loading-with-compiled-snippets ()
573 "Test basic loading and expansion of compiled snippets"
574 (yas-with-some-interesting-snippet-dirs
575 (yas-reload-all)
576 (yas-recompile-all)
577 (yas--with-temporary-redefinitions ((yas--load-directory-2
578 (&rest _dummies)
579 (ert-fail "yas--load-directory-2 shouldn't be called when snippets have been compiled")))
580 (yas-reload-all)
581 (yas--basic-jit-loading-1))))
582
583 (ert-deftest visiting-compiled-snippets ()
584 "Test snippet visiting for compiled snippets."
585 (yas-with-some-interesting-snippet-dirs
586 (yas-recompile-all)
587 (yas-reload-all 'no-jit) ; must be loaded for `yas-lookup-snippet' to work.
588 (yas--with-temporary-redefinitions ((find-file-noselect
589 (filename &rest _)
590 (throw 'yas-snippet-file filename)))
591 (should (string-suffix-p
592 "cc-mode/def"
593 (catch 'yas-snippet-file
594 (yas--visit-snippet-file-1
595 (yas--lookup-snippet-1 "def" 'cc-mode))))))))
596
597 (ert-deftest loading-with-cyclic-parenthood ()
598 "Test loading when cyclic parenthood is setup."
599 (yas-saving-variables
600 (yas-with-snippet-dirs '((".emacs.d/snippets"
601 ("c-mode"
602 (".yas-parents" . "cc-mode"))
603 ("cc-mode"
604 (".yas-parents" . "yet-another-c-mode and-that-one"))
605 ("yet-another-c-mode"
606 (".yas-parents" . "c-mode and-also-this-one lisp-interaction-mode"))))
607 (yas-reload-all)
608 (with-temp-buffer
609 (let* ((major-mode 'c-mode)
610 (expected `(c-mode
611 cc-mode
612 yet-another-c-mode
613 and-also-this-one
614 and-that-one
615 ;; prog-mode doesn't exist in emacs 24.3
616 ,@(if (fboundp 'prog-mode)
617 '(prog-mode))
618 emacs-lisp-mode
619 lisp-interaction-mode))
620 (observed (yas--modes-to-activate)))
621 (should (equal major-mode (car observed)))
622 (should (equal (sort expected #'string<) (sort observed #'string<))))))))
623
624 (ert-deftest extra-modes-parenthood ()
625 "Test activation of parents of `yas--extra-modes'."
626 (yas-saving-variables
627 (yas-with-snippet-dirs '((".emacs.d/snippets"
628 ("c-mode"
629 (".yas-parents" . "cc-mode"))
630 ("yet-another-c-mode"
631 (".yas-parents" . "c-mode and-also-this-one lisp-interaction-mode"))))
632 (yas-reload-all)
633 (with-temp-buffer
634 (yas-activate-extra-mode 'c-mode)
635 (yas-activate-extra-mode 'yet-another-c-mode)
636 (yas-activate-extra-mode 'and-that-one)
637 (let* ((expected-first `(and-that-one
638 yet-another-c-mode
639 c-mode
640 ,major-mode))
641 (expected-rest `(cc-mode
642 ;; prog-mode doesn't exist in emacs 24.3
643 ,@(if (fboundp 'prog-mode)
644 '(prog-mode))
645 emacs-lisp-mode
646 and-also-this-one
647 lisp-interaction-mode))
648 (observed (yas--modes-to-activate)))
649 (should (equal expected-first
650 (cl-subseq observed 0 (length expected-first))))
651 (should (equal (sort expected-rest #'string<)
652 (sort (cl-subseq observed (length expected-first)) #'string<))))))))
653
654 (defalias 'yas--phony-c-mode 'c-mode)
655
656 (ert-deftest issue-492-and-494 ()
657 (define-derived-mode yas--test-mode yas--phony-c-mode "Just a test mode")
658 (yas-with-snippet-dirs '((".emacs.d/snippets"
659 ("yas--test-mode")))
660 (yas-reload-all)
661 (with-temp-buffer
662 (let* ((major-mode 'yas--test-mode)
663 (expected `(c-mode
664 ,@(if (fboundp 'prog-mode)
665 '(prog-mode))
666 yas--phony-c-mode
667 yas--test-mode))
668 (observed (yas--modes-to-activate)))
669 (should (null (cl-set-exclusive-or expected observed)))
670 (should (= (length expected)
671 (length observed)))))))
672
673 (define-derived-mode yas--test-mode c-mode "Just a test mode")
674 (define-derived-mode yas--another-test-mode c-mode "Another test mode")
675
676 (ert-deftest issue-504-tricky-jit ()
677 (yas-with-snippet-dirs
678 '((".emacs.d/snippets"
679 ("yas--another-test-mode"
680 (".yas-parents" . "yas--test-mode"))
681 ("yas--test-mode")))
682 (let ((b (with-current-buffer (generate-new-buffer "*yas-test*")
683 (yas--another-test-mode)
684 (current-buffer))))
685 (unwind-protect
686 (progn
687 (yas-reload-all)
688 (should (= 0 (hash-table-count yas--scheduled-jit-loads))))
689 (kill-buffer b)))))
690
691 (defun yas--basic-jit-loading-1 ()
692 (with-temp-buffer
693 (should (= 4 (hash-table-count yas--scheduled-jit-loads)))
694 (should (= 0 (hash-table-count yas--tables)))
695 (lisp-interaction-mode)
696 (yas-minor-mode 1)
697 (should (= 2 (hash-table-count yas--scheduled-jit-loads)))
698 (should (= 2 (hash-table-count yas--tables)))
699 (should (= 1 (hash-table-count (yas--table-uuidhash (gethash 'lisp-interaction-mode yas--tables)))))
700 (should (= 2 (hash-table-count (yas--table-uuidhash (gethash 'emacs-lisp-mode yas--tables)))))
701 (yas-should-expand '(("sc" . "brother from another mother")
702 ("dolist" . "(dolist)")
703 ("ert-deftest" . "(ert-deftest name () )")))
704 (c-mode)
705 (yas-minor-mode 1)
706 (should (= 0 (hash-table-count yas--scheduled-jit-loads)))
707 (should (= 4 (hash-table-count yas--tables)))
708 (should (= 1 (hash-table-count (yas--table-uuidhash (gethash 'c-mode yas--tables)))))
709 (should (= 1 (hash-table-count (yas--table-uuidhash (gethash 'cc-mode yas--tables)))))
710 (yas-should-expand '(("printf" . "printf();")
711 ("def" . "# define")))
712 (yas-should-not-expand '("sc" "dolist" "ert-deftest"))))
713
714 \f
715 ;;; Menu
716 ;;;
717 (defmacro yas-with-even-more-interesting-snippet-dirs (&rest body)
718 `(yas-saving-variables
719 (yas-with-snippet-dirs
720 `((".emacs.d/snippets"
721 ("c-mode"
722 (".yas-make-groups" . "")
723 ("printf" . "printf($1);")
724 ("foo-group-a"
725 ("fnprintf" . "fprintf($1);")
726 ("snprintf" . "snprintf($1);"))
727 ("foo-group-b"
728 ("strcmp" . "strecmp($1);")
729 ("strcasecmp" . "strcasecmp($1);")))
730 ("lisp-interaction-mode"
731 ("ert-deftest" . "# group: barbar\n# --\n(ert-deftest ${1:name} () $0)"))
732 ("fancy-mode"
733 ("a-guy" . "# uuid: 999\n# --\nyo!")
734 ("a-sir" . "# uuid: 12345\n# --\nindeed!")
735 ("a-lady" . "# uuid: 54321\n# --\noh-la-la!")
736 ("a-beggar" . "# uuid: 0101\n# --\narrrgh!")
737 ("an-outcast" . "# uuid: 666\n# --\narrrgh!")
738 (".yas-setup.el" . , (pp-to-string
739 '(yas-define-menu 'fancy-mode
740 '((yas-ignore-item "0101")
741 (yas-item "999")
742 (yas-submenu "sirs"
743 ((yas-item "12345")))
744 (yas-submenu "ladies"
745 ((yas-item "54321"))))
746 '("666")))))))
747 ,@body)))
748
749 (ert-deftest test-yas-define-menu ()
750 (let ((yas-use-menu t))
751 (yas-with-even-more-interesting-snippet-dirs
752 (yas-reload-all 'no-jit)
753 (let ((menu (cdr (gethash 'fancy-mode yas--menu-table))))
754 (should (eql 4 (length menu)))
755 (dolist (item '("a-guy" "a-beggar"))
756 (should (find item menu :key #'third :test #'string=)))
757 (should-not (find "an-outcast" menu :key #'third :test #'string=))
758 (dolist (submenu '("sirs" "ladies"))
759 (should (keymapp
760 (fourth
761 (find submenu menu :key #'third :test #'string=)))))
762 ))))
763
764 (ert-deftest test-group-menus ()
765 "Test group-based menus using .yas-make-groups and the group directive"
766 (let ((yas-use-menu t))
767 (yas-with-even-more-interesting-snippet-dirs
768 (yas-reload-all 'no-jit)
769 ;; first the subdir-based groups
770 ;;
771 (let ((menu (cdr (gethash 'c-mode yas--menu-table))))
772 (should (eql 3 (length menu)))
773 (dolist (item '("printf" "foo-group-a" "foo-group-b"))
774 (should (find item menu :key #'third :test #'string=)))
775 (dolist (submenu '("foo-group-a" "foo-group-b"))
776 (should (keymapp
777 (fourth
778 (find submenu menu :key #'third :test #'string=))))))
779 ;; now group directives
780 ;;
781 (let ((menu (cdr (gethash 'lisp-interaction-mode yas--menu-table))))
782 (should (eql 1 (length menu)))
783 (should (find "barbar" menu :key #'third :test #'string=))
784 (should (keymapp
785 (fourth
786 (find "barbar" menu :key #'third :test #'string=))))))))
787
788 (ert-deftest test-group-menus-twisted ()
789 "Same as similarly named test, but be mean.
790
791 TODO: be meaner"
792 (let ((yas-use-menu t))
793 (yas-with-even-more-interesting-snippet-dirs
794 ;; add a group directive conflicting with the subdir and watch
795 ;; behaviour
796 (with-temp-buffer
797 (insert "# group: foo-group-c\n# --\nstrecmp($1)")
798 (write-region nil nil (concat (first (yas-snippet-dirs))
799 "/c-mode/foo-group-b/strcmp")))
800 (yas-reload-all 'no-jit)
801 (let ((menu (cdr (gethash 'c-mode yas--menu-table))))
802 (should (eql 4 (length menu)))
803 (dolist (item '("printf" "foo-group-a" "foo-group-b" "foo-group-c"))
804 (should (find item menu :key #'third :test #'string=)))
805 (dolist (submenu '("foo-group-a" "foo-group-b" "foo-group-c"))
806 (should (keymapp
807 (fourth
808 (find submenu menu :key #'third :test #'string=))))))
809 ;; delete the .yas-make-groups file and watch behaviour
810 ;;
811 (delete-file (concat (first (yas-snippet-dirs))
812 "/c-mode/.yas-make-groups"))
813 (yas-reload-all 'no-jit)
814 (let ((menu (cdr (gethash 'c-mode yas--menu-table))))
815 (should (eql 5 (length menu))))
816 ;; Change a group directive and reload
817 ;;
818 (let ((menu (cdr (gethash 'lisp-interaction-mode yas--menu-table))))
819 (should (find "barbar" menu :key #'third :test #'string=)))
820
821 (with-temp-buffer
822 (insert "# group: foofoo\n# --\n(ert-deftest ${1:name} () $0)")
823 (write-region nil nil (concat (first (yas-snippet-dirs))
824 "/lisp-interaction-mode/ert-deftest")))
825 (yas-reload-all 'no-jit)
826 (let ((menu (cdr (gethash 'lisp-interaction-mode yas--menu-table))))
827 (should (eql 1 (length menu)))
828 (should (find "foofoo" menu :key #'third :test #'string=))
829 (should (keymapp
830 (fourth
831 (find "foofoo" menu :key #'third :test #'string=))))))))
832
833 \f
834 ;;; The infamous and problematic tab keybinding
835 ;;;
836 (ert-deftest test-yas-tab-binding ()
837 (with-temp-buffer
838 (yas-minor-mode -1)
839 (should (not (eq (key-binding (yas--read-keybinding "<tab>")) 'yas-expand)))
840 (yas-minor-mode 1)
841 (should (eq (key-binding (yas--read-keybinding "<tab>")) 'yas-expand))
842 (yas-expand-snippet "$1 $2 $3")
843 (should (eq (key-binding [(tab)]) 'yas-next-field-or-maybe-expand))
844 (should (eq (key-binding (kbd "TAB")) 'yas-next-field-or-maybe-expand))
845 (should (eq (key-binding [(shift tab)]) 'yas-prev-field))
846 (should (eq (key-binding [backtab]) 'yas-prev-field))))
847
848 (ert-deftest test-rebindings ()
849 (unwind-protect
850 (progn
851 (define-key yas-minor-mode-map [tab] nil)
852 (define-key yas-minor-mode-map (kbd "TAB") nil)
853 (define-key yas-minor-mode-map (kbd "SPC") 'yas-expand)
854 (with-temp-buffer
855 (yas-minor-mode 1)
856 (should (not (eq (key-binding (yas--read-keybinding "TAB")) 'yas-expand)))
857 (should (eq (key-binding (yas--read-keybinding "SPC")) 'yas-expand))
858 (yas-reload-all)
859 (should (not (eq (key-binding (yas--read-keybinding "TAB")) 'yas-expand)))
860 (should (eq (key-binding (yas--read-keybinding "SPC")) 'yas-expand))))
861 ;; FIXME: actually should restore to whatever saved values where there.
862 ;;
863 (define-key yas-minor-mode-map [tab] 'yas-expand)
864 (define-key yas-minor-mode-map (kbd "TAB") 'yas-expand)
865 (define-key yas-minor-mode-map (kbd "SPC") nil)))
866
867 (ert-deftest test-yas-in-org ()
868 (with-temp-buffer
869 (org-mode)
870 (yas-minor-mode 1)
871 (should (eq (key-binding [(tab)]) 'yas-expand))
872 (should (eq (key-binding (kbd "TAB")) 'yas-expand))))
873
874 (ert-deftest test-yas-activate-extra-modes ()
875 "Given a symbol, `yas-activate-extra-mode' should be able to
876 add the snippets associated with the given mode."
877 (with-temp-buffer
878 (yas-saving-variables
879 (yas-with-snippet-dirs
880 '((".emacs.d/snippets"
881 ("markdown-mode"
882 ("_" . "_Text_ "))
883 ("emacs-lisp-mode"
884 ("car" . "(car )"))))
885 (yas-reload-all)
886 (emacs-lisp-mode)
887 (yas-minor-mode-on)
888 (yas-activate-extra-mode 'markdown-mode)
889 (should (eq 'markdown-mode (car yas--extra-modes)))
890 (yas-should-expand '(("_" . "_Text_ ")))
891 (yas-should-expand '(("car" . "(car )")))
892 (yas-deactivate-extra-mode 'markdown-mode)
893 (should-not (eq 'markdown-mode (car yas--extra-modes)))
894 (yas-should-not-expand '("_"))
895 (yas-should-expand '(("car" . "(car )")))))))
896
897 \f
898 ;;; Helpers
899 ;;;
900 (defun yas-should-expand (keys-and-expansions)
901 (dolist (key-and-expansion keys-and-expansions)
902 (yas-exit-all-snippets)
903 (erase-buffer)
904 (insert (car key-and-expansion))
905 (let ((yas-fallback-behavior nil))
906 (ert-simulate-command '(yas-expand)))
907 (unless (string= (yas--buffer-contents) (cdr key-and-expansion))
908 (ert-fail (format "\"%s\" should have expanded to \"%s\" but got \"%s\""
909 (car key-and-expansion)
910 (cdr key-and-expansion)
911 (yas--buffer-contents)))))
912 (yas-exit-all-snippets))
913
914 (defun yas-should-not-expand (keys)
915 (dolist (key keys)
916 (yas-exit-all-snippets)
917 (erase-buffer)
918 (insert key)
919 (let ((yas-fallback-behavior nil))
920 (ert-simulate-command '(yas-expand)))
921 (unless (string= (yas--buffer-contents) key)
922 (ert-fail (format "\"%s\" should have stayed put, but instead expanded to \"%s\""
923 key
924 (yas--buffer-contents))))))
925
926 (defun yas-mock-insert (string)
927 (dotimes (i (length string))
928 (let ((last-command-event (aref string i)))
929 (ert-simulate-command '(self-insert-command 1)))))
930
931 (defun yas-mock-yank (string)
932 (let ((interprogram-paste-function (lambda () string)))
933 (ert-simulate-command '(yank nil))))
934
935 (defun yas-make-file-or-dirs (ass)
936 (let ((file-or-dir-name (car ass))
937 (content (cdr ass)))
938 (cond ((listp content)
939 (make-directory file-or-dir-name 'parents)
940 (let ((default-directory (concat default-directory "/" file-or-dir-name)))
941 (mapc #'yas-make-file-or-dirs content)))
942 ((stringp content)
943 (with-temp-buffer
944 (insert content)
945 (write-region nil nil file-or-dir-name nil 'nomessage)))
946 (t
947 (message "[yas] oops don't know this content")))))
948
949
950 (defun yas-variables ()
951 (let ((syms))
952 (mapatoms #'(lambda (sym)
953 (if (and (string-match "^yas-[^/]" (symbol-name sym))
954 (boundp sym))
955 (push sym syms))))
956 syms))
957
958 (defun yas-call-with-saving-variables (fn)
959 (let* ((vars (yas-variables))
960 (saved-values (mapcar #'symbol-value vars)))
961 (unwind-protect
962 (funcall fn)
963 (loop for var in vars
964 for saved in saved-values
965 do (set var saved)))))
966
967 (defun yas-call-with-snippet-dirs (dirs fn)
968 (let* ((default-directory (make-temp-file "yasnippet-fixture" t))
969 (yas-snippet-dirs (mapcar #'car dirs)))
970 (with-temp-message ""
971 (unwind-protect
972 (progn
973 (mapc #'yas-make-file-or-dirs dirs)
974 (funcall fn))
975 (when (>= emacs-major-version 24)
976 (delete-directory default-directory 'recursive))))))
977
978 ;;; Older emacsen
979 ;;;
980 (unless (fboundp 'special-mode)
981 ;; FIXME: Why provide this default definition here?!?
982 (defalias 'special-mode 'fundamental))
983
984 (unless (fboundp 'string-suffix-p)
985 ;; introduced in Emacs 24.4
986 (defun string-suffix-p (suffix string &optional ignore-case)
987 "Return non-nil if SUFFIX is a suffix of STRING.
988 If IGNORE-CASE is non-nil, the comparison is done without paying
989 attention to case differences."
990 (let ((start-pos (- (length string) (length suffix))))
991 (and (>= start-pos 0)
992 (eq t (compare-strings suffix nil nil
993 string start-pos nil ignore-case))))))
994
995 ;;; btw to test this in emacs22 mac osx:
996 ;;; curl -L -O https://github.com/mirrors/emacs/raw/master/lisp/emacs-lisp/ert.el
997 ;;; curl -L -O https://github.com/mirrors/emacs/raw/master/lisp/emacs-lisp/ert-x.el
998 ;;; /usr/bin/emacs -nw -Q -L . -l yasnippet-tests.el --batch -e ert
999
1000
1001 (put 'yas-saving-variables 'edebug-form-spec t)
1002 (put 'yas-with-snippet-dirs 'edebug-form-spec t)
1003 (put 'yas-with-overriden-buffer-list 'edebug-form-spec t)
1004 (put 'yas-with-some-interesting-snippet-dirs 'edebug-form-spec t)
1005
1006
1007 (put 'yas--with-temporary-redefinitions 'lisp-indent-function 1)
1008 (put 'yas--with-temporary-redefinitions 'edebug-form-spec '((&rest (defun*)) cl-declarations body))
1009
1010
1011
1012
1013 (provide 'yasnippet-tests)
1014 ;; Local Variables:
1015 ;; indent-tabs-mode: nil
1016 ;; byte-compile-warnings: (not cl-functions)
1017 ;; End:
1018 ;;; yasnippet-tests.el ends here