1 ;;; yasnippet-tests.el --- some yasnippet tests -*- lexical-binding: t -*-
3 ;; Copyright (C) 2012, 2013, 2014, 2015 Free Software Foundation, Inc.
5 ;; Author: João Távora <joaot@siscog.pt>
6 ;; Keywords: emulations, convenience
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.
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.
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/>.
23 ;; Test basic snippet mechanics and the loading system
35 (defun yas--buffer-contents ()
36 (buffer-substring-no-properties (point-min) (point-max)))
38 (ert-deftest field-navigation ()
41 (yas-expand-snippet "${1:brother} from another ${2:mother}")
42 (should (string= (yas--buffer-contents)
43 "brother from another mother"))
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"))))
51 (ert-deftest simple-mirror ()
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"))))
61 (ert-deftest mirror-with-transformation ()
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"))))
71 (ert-deftest mirror-with-transformation-and-autofill ()
72 "Test interaction of autofill with mirror transforms"
73 (let ((words "one two three four five")
76 (c-mode) ; In `c-mode' filling comments works by narrowing.
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)
86 (yas-mock-insert words)
87 (should (string= (yas--buffer-contents)
88 (concat filled-words "\n"))))))
91 (ert-deftest primary-field-transformation ()
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")))))
100 (ert-deftest nested-placeholders-kill-superfield ()
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!"))))
110 (ert-deftest nested-placeholders-use-subfield ()
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!"))))
119 (ert-deftest mirrors-adjacent-to-fields-with-nested-mirrors ()
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...' %>"))))
129 (ert-deftest deep-nested-mirroring-issue-351 ()
132 (yas-expand-snippet "${1:FOOOOOOO}${2:$1}${3:$2}${4:$3}")
133 (yas-mock-insert "abc")
134 (should (string= (yas--buffer-contents) "abcabcabcabc"))))
136 (ert-deftest delete-numberless-inner-snippet-issue-562 ()
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)))))
147 (ert-deftest ignore-trailing-whitespace ()
150 (insert "# key: foo\n# --\nfoo")
151 (yas--parse-template))
153 (insert "# key: foo \n# --\nfoo")
154 (yas--parse-template)))))
156 ;; (ert-deftest in-snippet-undo ()
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!"))))
166 (ert-deftest dont-clear-on-partial-deletion-issue-515 ()
167 "Ensure fields are not cleared when user doesn't really mean to."
170 (yas-expand-snippet "my ${1:kid brother} from another ${2:mother}")
172 (ert-simulate-command '(kill-word 1))
173 (ert-simulate-command '(delete-char 1))
175 (should (string= (yas--buffer-contents)
176 "my brother from another mother"))
177 (should (looking-at "brother"))
179 (ert-simulate-command '(yas-next-field))
180 (should (looking-at "mother"))
181 (ert-simulate-command '(yas-prev-field))
182 (should (looking-at "brother"))))
184 (ert-deftest do-clear-on-yank-issue-515 ()
185 "A yank should clear an unmodified field."
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"))))
196 (ert-deftest basic-indentation ()
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)
211 end" (buffer-string)))
212 (should (= 4 (current-column)))))
214 (ert-deftest navigate-a-snippet-with-multiline-mirrors-issue-665 ()
215 "In issue 665, a multi-line mirror is attempted.
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"
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 \", \")
228 (yas-mock-insert "bla, ble, bli")
229 (ert-simulate-command '(yas-next-field))
230 (let ((expected (mapconcat #'identity
232 "[[:blank:]]*@ble = ble"
233 "[[:blank:]]*@bli = bli")
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))))))
242 ;;; Snippet expansion and character escaping
243 ;;; Thanks to @zw963 (Billy) for the testing
245 (ert-deftest escape-dollar ()
248 (yas-expand-snippet "bla\\${1:bla}ble")
249 (should (string= (yas--buffer-contents) "bla${1:bla}ble"))))
251 (ert-deftest escape-closing-brace ()
254 (yas-expand-snippet "bla${1:bla\\}}ble")
255 (should (string= (yas--buffer-contents) "blabla}ble"))
256 (should (string= (yas-field-value 1) "bla}"))))
258 (ert-deftest escape-backslashes ()
261 (yas-expand-snippet "bla\\ble")
262 (should (string= (yas--buffer-contents) "bla\\ble"))))
264 (ert-deftest escape-backquotes ()
267 (yas-expand-snippet "bla`(upcase \"foo\\`bar\")`ble")
268 (should (string= (yas--buffer-contents) "blaFOO`BARble"))))
270 (ert-deftest escape-some-elisp-with-strings ()
271 "elisp with strings and unbalance parens inside it"
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
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"))))
280 (ert-deftest escape-some-elisp-with-backslashes ()
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"))))
288 (ert-deftest be-careful-when-escaping-in-yas-selected-text ()
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)
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)
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)
307 (ert-deftest be-careful-when-escaping-in-yas-selected-text-2 ()
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?")))))
314 (ert-deftest example-for-issue-271 ()
317 (let ((yas-selected-text "aaa")
318 (snippet "if ${1:condition}\n`yas-selected-text`\nelse\n$3\nend"))
319 (yas-expand-snippet snippet)
321 (yas-mock-insert "bbb")
322 (should (string= (yas--buffer-contents) "if condition\naaa\nelse\nbbb\nend")))))
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))
337 (progn (require 'font-lock)
338 ;; turn on font locking before major mode change
341 (and (buffer-name ,temp-buffer)
342 (kill-buffer ,temp-buffer))))))))
344 (defmacro yas-saving-variables (&rest body)
345 `(yas-call-with-saving-variables #'(lambda () ,@body)))
347 (defmacro yas-with-snippet-dirs (dirs &rest body)
348 (declare (indent defun))
349 `(yas-call-with-snippet-dirs ,dirs
353 (ert-deftest example-for-issue-474 ()
354 (yas--with-font-locked-temp-buffer
357 (insert "#include <foo>\n")
358 (let ((yas-good-grace nil)) (yas-expand-snippet "`\"TODO: \"`"))
359 (should (string= (yas--buffer-contents) "#include <foo>\nTODO: "))))
361 (ert-deftest example-for-issue-404 ()
362 (yas--with-font-locked-temp-buffer
365 (insert "#include <foo>\n")
366 (let ((yas-good-grace nil)) (yas-expand-snippet "main"))
367 (should (string= (yas--buffer-contents) "#include <foo>\nmain"))))
369 (ert-deftest example-for-issue-404-c-mode ()
370 (yas--with-font-locked-temp-buffer
373 (insert "#include <foo>\n")
374 (let ((yas-good-grace nil)) (yas-expand-snippet "main"))
375 (should (string= (yas--buffer-contents) "#include <foo>\nmain"))))
377 (ert-deftest middle-of-buffer-snippet-insertion ()
381 (save-excursion (insert "end"))
382 (yas-expand-snippet "-middle-")
383 (should (string= (yas--buffer-contents) "beginning-middle-end"))))
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.
389 :expected-result (if noninteractive
394 (let ((snippet "\\${${1:1}:`yas-selected-text`}"))
398 (yas-expand-snippet snippet)
399 (should (string= (yas--buffer-contents) "aaa${1:bbb}ccc")))))
401 (ert-deftest string-match-with-subregexp-in-embedded-elisp ()
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\")
409 (yas-expand-snippet snippet))
410 (should (string= (yas--buffer-contents) "ok"))))
412 (ert-deftest string-match-with-subregexp-in-mirror-transformations ()
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)
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")))))
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!
434 TODO: correct this bug!"
435 :expected-result :failed
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!
444 (defvar yas--foobarbaz)
446 ;; See issue #497. To understand this test, follow the example of the
447 ;; `yas-key-syntaxes' docstring.
449 (ert-deftest complicated-yas-key-syntaxes ()
451 (yas-saving-variables
452 (yas-with-snippet-dirs
453 '((".emacs.d/snippets"
455 ("foo-barbaz" . "# condition: yas--foobarbaz\n# --\nOKfoo-barbazOK")
456 ("barbaz" . "# condition: yas--barbaz\n# --\nOKbarbazOK")
458 ("'quote" . "OKquoteOK"))))
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 "-")
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 "))))))))
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.
500 (cl-labels ((set-fdefinitions (names functions)
501 (loop for name in names
504 (set-fdefinitions definition-names overriding-functions)
505 (unwind-protect (funcall function)
506 (set-fdefinitions definition-names saved-functions)))))
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) ...)
512 ;; ;; code that eventually calls foo, bar of (setf foo)
514 ;; FIXME: This is hideous! Better use defadvice (or at least letf).
515 `(yas--call-with-temporary-redefinitions
517 ,@(mapcar #'(lambda (thingy)
518 `(list ',(first thingy)
519 (lambda ,@(rest thingy))))
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
527 (remove-if #'(lambda (buf)
528 (with-current-buffer buf
529 (eq major-mode 'lisp-interaction-mode)))
530 (funcall ,saved-sym))))
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"
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")))
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"))))
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))))
562 (ert-deftest basic-jit-loading ()
563 "Test basic loading and expansion of snippets"
564 (yas-with-some-interesting-snippet-dirs
566 (yas--basic-jit-loading-1)))
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
573 (yas--with-temporary-redefinitions ((yas--load-directory-2
575 (ert-fail "yas--load-directory-2 shouldn't be called when snippets have been compiled")))
577 (yas--basic-jit-loading-1))))
579 (ert-deftest visiting-compiled-snippets ()
580 "Test snippet visiting for compiled snippets."
581 (yas-with-some-interesting-snippet-dirs
583 (yas-reload-all 'no-jit) ; must be loaded for `yas-lookup-snippet' to work.
584 (yas--with-temporary-redefinitions ((find-file-noselect
586 (throw 'yas-snippet-file filename)))
587 (should (string-suffix-p
589 (catch 'yas-snippet-file
590 (yas--visit-snippet-file-1
591 (yas--lookup-snippet-1 "def" 'cc-mode))))))))
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"
598 (".yas-parents" . "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"))))
605 (let* ((major-mode 'c-mode)
611 ;; prog-mode doesn't exist in emacs 24.3
612 ,@(if (fboundp 'prog-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<))))))))
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"
625 (".yas-parents" . "cc-mode"))
626 ("yet-another-c-mode"
627 (".yas-parents" . "c-mode and-also-this-one lisp-interaction-mode"))))
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
637 (expected-rest `(cc-mode
638 ;; prog-mode doesn't exist in emacs 24.3
639 ,@(if (fboundp 'prog-mode)
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<))))))))
650 (defalias 'yas--phony-c-mode 'c-mode)
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"
658 (let* ((major-mode 'yas--test-mode)
660 ,@(if (fboundp 'prog-mode)
664 (observed (yas--modes-to-activate)))
665 (should (null (cl-set-exclusive-or expected observed)))
666 (should (= (length expected)
667 (length observed)))))))
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")
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"))
678 (let ((b (with-current-buffer (generate-new-buffer "*yas-test*")
679 (yas--another-test-mode)
684 (should (= 0 (hash-table-count yas--scheduled-jit-loads))))
687 (defun yas--basic-jit-loading-1 ()
689 (should (= 4 (hash-table-count yas--scheduled-jit-loads)))
690 (should (= 0 (hash-table-count yas--tables)))
691 (lisp-interaction-mode)
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 () )")))
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"))))
713 (defmacro yas-with-even-more-interesting-snippet-dirs (&rest body)
714 `(yas-saving-variables
715 (yas-with-snippet-dirs
716 `((".emacs.d/snippets"
718 (".yas-make-groups" . "")
719 ("printf" . "printf($1);")
721 ("fnprintf" . "fprintf($1);")
722 ("snprintf" . "snprintf($1);"))
724 ("strcmp" . "strecmp($1);")
725 ("strcasecmp" . "strcasecmp($1);")))
726 ("lisp-interaction-mode"
727 ("ert-deftest" . "# group: barbar\n# --\n(ert-deftest ${1:name} () $0)"))
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")
739 ((yas-item "12345")))
740 (yas-submenu "ladies"
741 ((yas-item "54321"))))
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"))
757 (find submenu menu :key #'third :test #'string=)))))
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
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"))
774 (find submenu menu :key #'third :test #'string=))))))
775 ;; now group directives
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=))
782 (find "barbar" menu :key #'third :test #'string=))))))))
784 (ert-deftest test-group-menus-twisted ()
785 "Same as similarly named test, but be mean.
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
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"))
804 (find submenu menu :key #'third :test #'string=))))))
805 ;; delete the .yas-make-groups file and watch behaviour
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
814 (let ((menu (cdr (gethash 'lisp-interaction-mode yas--menu-table))))
815 (should (find "barbar" menu :key #'third :test #'string=)))
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=))
827 (find "foofoo" menu :key #'third :test #'string=))))))))
830 ;;; The infamous and problematic tab keybinding
832 (ert-deftest test-yas-tab-binding ()
835 (should (not (eq (key-binding (yas--read-keybinding "<tab>")) 'yas-expand)))
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))))
844 (ert-deftest test-rebindings ()
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)
852 (should (not (eq (key-binding (yas--read-keybinding "TAB")) 'yas-expand)))
853 (should (eq (key-binding (yas--read-keybinding "SPC")) 'yas-expand))
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.
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)))
863 (ert-deftest test-yas-in-org ()
867 (should (eq (key-binding [(tab)]) 'yas-expand))
868 (should (eq (key-binding (kbd "TAB")) 'yas-expand))))
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."
874 (yas-saving-variables
875 (yas-with-snippet-dirs
876 '((".emacs.d/snippets"
880 ("car" . "(car )"))))
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 )")))))))
896 (defun yas-should-expand (keys-and-expansions)
897 (dolist (key-and-expansion keys-and-expansions)
898 (yas-exit-all-snippets)
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))
910 (defun yas-should-not-expand (keys)
912 (yas-exit-all-snippets)
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\""
920 (yas--buffer-contents))))))
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)))))
927 (defun yas-mock-yank (string)
928 (let ((interprogram-paste-function (lambda () string)))
929 (ert-simulate-command '(yank nil))))
931 (defun yas-make-file-or-dirs (ass)
932 (let ((file-or-dir-name (car 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)))
941 (write-region nil nil file-or-dir-name nil 'nomessage)))
943 (message "[yas] oops don't know this content")))))
946 (defun yas-variables ()
948 (mapatoms #'(lambda (sym)
949 (if (and (string-match "^yas-[^/]" (symbol-name sym))
954 (defun yas-call-with-saving-variables (fn)
955 (let* ((vars (yas-variables))
956 (saved-values (mapcar #'symbol-value vars)))
959 (loop for var in vars
960 for saved in saved-values
961 do (set var saved)))))
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 ""
969 (mapc #'yas-make-file-or-dirs dirs)
971 (when (>= emacs-major-version 24)
972 (delete-directory default-directory 'recursive))))))
976 (unless (fboundp 'special-mode)
977 ;; FIXME: Why provide this default definition here?!?
978 (defalias 'special-mode 'fundamental))
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))))))
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
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)
1003 (put 'yas--with-temporary-redefinitions 'lisp-indent-function 1)
1004 (put 'yas--with-temporary-redefinitions 'edebug-form-spec '((&rest (defun*)) cl-declarations body))
1009 (provide 'yasnippet-tests)
1011 ;; indent-tabs-mode: nil
1012 ;; byte-compile-warnings: (not cl-functions)
1014 ;;; yasnippet-tests.el ends here