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