1 ;;; elisp-mode-tests.el --- Tests for emacs-lisp-mode -*- lexical-binding: t; -*-
3 ;; Copyright (C) 2015 Free Software Foundation, Inc.
5 ;; Author: Dmitry Gutov <dgutov@yandex.ru>
6 ;; Author: Stephen Leake <stephen_leake@member.fsf.org>
8 ;; This file is part of GNU Emacs.
10 ;; GNU Emacs is free software: you can redistribute it and/or modify
11 ;; it under the terms of the GNU General Public License as published by
12 ;; the Free Software Foundation, either version 3 of the License, or
13 ;; (at your option) any later version.
15 ;; GNU Emacs is distributed in the hope that it will be useful,
16 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 ;; GNU General Public License for more details.
20 ;; You should have received a copy of the GNU General Public License
21 ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
30 (defun elisp--test-completions ()
31 (let ((data (elisp-completion-at-point)))
32 (all-completions (buffer-substring (nth 0 data
) (nth 1 data
))
34 (plist-get (nthcdr 3 data
) :predicate
))))
36 (ert-deftest elisp-completes-functions
()
40 (let ((comps (elisp--test-completions)))
41 (should (member "backup-buffer" comps
))
42 (should-not (member "backup-inhibited" comps
)))))
44 (ert-deftest elisp-completes-variables
()
48 (let ((comps (elisp--test-completions)))
49 (should (member "backup-inhibited" comps
))
50 (should-not (member "backup-buffer" comps
)))))
52 (ert-deftest elisp-completes-anything-quoted
()
53 (dolist (text '("`(foo ba" "(foo 'ba"
54 "`(,foo ba" "`,(foo `ba"
59 (let ((comps (elisp--test-completions)))
60 (should (member "backup-inhibited" comps
))
61 (should (member "backup-buffer" comps
))
62 (should (member "backup" comps
))))))
64 (ert-deftest elisp-completes-variables-unquoted
()
65 (dolist (text '("`(foo ,ba" "`(,(foo ba" "`(,ba"))
69 (let ((comps (elisp--test-completions)))
70 (should (member "backup-inhibited" comps
))
71 (should-not (member "backup-buffer" comps
))))))
73 (ert-deftest elisp-completes-functions-in-special-macros
()
74 (dolist (text '("(declare-function ba" "(cl-callf2 ba"))
78 (let ((comps (elisp--test-completions)))
79 (should (member "backup-buffer" comps
))
80 (should-not (member "backup-inhibited" comps
))))))
82 (ert-deftest elisp-completes-functions-after-hash-quote
()
83 (ert-deftest elisp-completes-functions-after-let-bindings
()
87 (let ((comps (elisp--test-completions)))
88 (should (member "backup-buffer" comps
))
89 (should-not (member "backup-inhibited" comps
))))))
91 (ert-deftest elisp-completes-local-variables
()
94 (insert "(let ((bar 1) baz) (foo ba")
95 (let ((comps (elisp--test-completions)))
96 (should (member "backup-inhibited" comps
))
97 (should (member "bar" comps
))
98 (should (member "baz" comps
)))))
100 (ert-deftest elisp-completest-variables-in-let-bindings
()
101 (dolist (text '("(let (ba" "(let* ((ba"))
105 (let ((comps (elisp--test-completions)))
106 (should (member "backup-inhibited" comps
))
107 (should-not (member "backup-buffer" comps
))))))
109 (ert-deftest elisp-completes-functions-after-let-bindings
()
112 (insert "(let ((bar 1) (baz 2)) (ba")
113 (let ((comps (elisp--test-completions)))
114 (should (member "backup-buffer" comps
))
115 (should-not (member "backup-inhibited" comps
)))))
119 (defun xref-elisp-test-descr-to-target (xref)
120 "Return an appropriate `looking-at' match string for XREF."
121 (let* ((loc (xref-item-location xref
))
122 (type (or (xref-elisp-location-type loc
)
127 ;; summary: "(defalias xref)"
128 ;; target : "(defalias 'xref)"
129 (concat "(defalias '" (substring (xref-item-summary xref
) 10 -
1)))
132 (let ((summary (xref-item-summary xref
))
133 (file (xref-elisp-location-file loc
)))
135 ((string= "c" (file-name-extension file
))
136 ;; summary: "(defun buffer-live-p)"
137 ;; target : "DEFUN (buffer-live-p"
139 (upcase (substring summary
1 6))
141 (substring summary
7 -
1)
145 (substring summary
0 -
1))
149 (let ((summary (xref-item-summary xref
))
150 (file (xref-elisp-location-file loc
)))
152 ((string= "c" (file-name-extension file
))
153 ;; summary: "(defvar system-name)"
154 ;; target : "DEFVAR_LISP ("system-name", "
155 ;; summary: "(defvar abbrev-mode)"
156 ;; target : DEFVAR_PER_BUFFER ("abbrev-mode"
158 (upcase (substring summary
1 7))
159 (if (bufferp (variable-binding-locus (xref-elisp-location-symbol loc
)))
162 (substring summary
8 -
1)
166 (substring summary
0 -
1))
170 ;; summary: "(feature xref)"
171 ;; target : "(provide 'xref)"
172 (concat "(provide '" (substring (xref-item-summary xref
) 9 -
1)))
175 (substring (xref-item-summary xref
) 0 -
1))
179 (defun xref-elisp-test-run (xrefs expected-xrefs
)
180 (should (= (length xrefs
) (length expected-xrefs
)))
182 (let ((xref (pop xrefs
))
183 (expected (pop expected-xrefs
)))
186 (or (when (consp expected
) (car expected
)) expected
)))
188 (xref--goto-location (xref-item-location xref
))
189 (should (looking-at (or (when (consp expected
) (cdr expected
))
190 (xref-elisp-test-descr-to-target expected
)))))
193 (defmacro xref-elisp-deftest
(name computed-xrefs expected-xrefs
)
194 "Define an ert test for an xref-elisp feature.
195 COMPUTED-XREFS and EXPECTED-XREFS are lists of xrefs, except if
196 an element of EXPECTED-XREFS is a cons (XREF . TARGET), TARGET is
197 matched to the found location; otherwise, match
198 to (xref-elisp-test-descr-to-target xref)."
199 (declare (indent defun
)
200 (debug (symbolp "name")))
201 `(ert-deftest ,(intern (concat "xref-elisp-test-" (symbol-name name
))) ()
202 (xref-elisp-test-run ,computed-xrefs
,expected-xrefs
)
205 ;; When tests are run from the Makefile, 'default-directory' is $HOME,
206 ;; so we must provide this dir to expand-file-name in the expected
207 ;; results. This also allows running these tests from other
209 (defconst emacs-test-dir
(file-name-directory (or load-file-name
(buffer-file-name))))
211 ;; alphabetical by test name
215 ;; FIXME: defalias-defun-c cmpl-prefix-entry-head
216 ;; FIXME: defalias-defvar-el allout-mode-map
218 (xref-elisp-deftest find-defs-constructor
219 (elisp--xref-find-definitions 'xref-make-elisp-location
)
220 ;; 'xref-make-elisp-location' is just a name for the default
221 ;; constructor created by the cl-defstruct, so the location is the
222 ;; cl-defstruct location.
225 (xref-make "(cl-defstruct (xref-elisp-location (:constructor xref-make-elisp-location)))"
226 (xref-make-elisp-location
227 'xref-elisp-location
'define-type
228 (expand-file-name "../../lisp/progmodes/elisp-mode.el" emacs-test-dir
)))
229 ;; It's not worth adding another special case to `xref-elisp-test-descr-to-target' for this
230 "(cl-defstruct (xref-elisp-location")
233 (xref-elisp-deftest find-defs-defalias-defun-el
234 (elisp--xref-find-definitions 'Buffer-menu-sort
)
236 (xref-make "(defalias Buffer-menu-sort)"
237 (xref-make-elisp-location
238 'Buffer-menu-sort
'defalias
239 (expand-file-name "../../lisp/buff-menu.elc" emacs-test-dir
)))
240 (xref-make "(defun tabulated-list-sort)"
241 (xref-make-elisp-location
242 'tabulated-list-sort nil
243 (expand-file-name "../../lisp/emacs-lisp/tabulated-list.el" emacs-test-dir
)))
248 ;; FIXME: eieio defclass
250 ;; Possible ways of defining the default method implementation for a
251 ;; generic function. We declare these here, so we know we cover all
252 ;; cases, and we don't rely on other code not changing.
254 ;; When the generic and default method are declared in the same place,
255 ;; elisp--xref-find-definitions only returns one.
257 (cl-defstruct (xref-elisp-root-type)
260 (cl-defgeneric xref-elisp-generic-no-methods
()
261 "doc string no-methods"
262 ;; No default implementation, no methods, but fboundp is true for
263 ;; this symbol; it calls cl-no-applicable-method
266 (cl-defmethod xref-elisp-generic-no-default ((this xref-elisp-root-type
))
267 "doc string no-default xref-elisp-root-type"
268 "non-default for no-default")
270 ;; defgeneric after defmethod in file to ensure the fallback search
271 ;; method of just looking for the function name will fail.
272 (cl-defgeneric xref-elisp-generic-no-default
()
273 "doc string no-default generic"
274 ;; No default implementation; this function calls the cl-generic
278 (cl-defgeneric xref-elisp-generic-co-located-default
()
279 "doc string co-located-default generic"
280 "co-located default")
282 (cl-defmethod xref-elisp-generic-co-located-default ((this xref-elisp-root-type
))
283 "doc string co-located-default xref-elisp-root-type"
284 "non-default for co-located-default")
286 (cl-defgeneric xref-elisp-generic-separate-default
()
287 "doc string separate-default generic"
288 ;; default implementation provided separately
291 (cl-defmethod xref-elisp-generic-separate-default ()
292 "doc string separate-default default"
295 (cl-defmethod xref-elisp-generic-separate-default ((this xref-elisp-root-type
))
296 "doc string separate-default xref-elisp-root-type"
297 "non-default for separate-default")
299 (cl-defmethod xref-elisp-generic-implicit-generic ()
300 "doc string implicit-generic default"
301 "default for implicit generic")
303 (cl-defmethod xref-elisp-generic-implicit-generic ((this xref-elisp-root-type
))
304 "doc string implicit-generic xref-elisp-root-type"
305 "non-default for implicit generic")
308 (xref-elisp-deftest find-defs-defgeneric-no-methods
309 (elisp--xref-find-definitions 'xref-elisp-generic-no-methods
)
311 (xref-make "(cl-defgeneric xref-elisp-generic-no-methods)"
312 (xref-make-elisp-location
313 'xref-elisp-generic-no-methods
'cl-defgeneric
314 (expand-file-name "elisp-mode-tests.el" emacs-test-dir
)))
317 (xref-elisp-deftest find-defs-defgeneric-no-default
318 (elisp--xref-find-definitions 'xref-elisp-generic-no-default
)
320 (xref-make "(cl-defgeneric xref-elisp-generic-no-default)"
321 (xref-make-elisp-location
322 'xref-elisp-generic-no-default
'cl-defgeneric
323 (expand-file-name "elisp-mode-tests.el" emacs-test-dir
)))
324 (xref-make "(cl-defmethod xref-elisp-generic-no-default ((this xref-elisp-root-type)))"
325 (xref-make-elisp-location
326 '(xref-elisp-generic-no-default xref-elisp-root-type
) 'cl-defmethod
327 (expand-file-name "elisp-mode-tests.el" emacs-test-dir
)))
330 (xref-elisp-deftest find-defs-defgeneric-co-located-default
331 (elisp--xref-find-definitions 'xref-elisp-generic-co-located-default
)
333 (xref-make "(cl-defgeneric xref-elisp-generic-co-located-default)"
334 (xref-make-elisp-location
335 'xref-elisp-generic-co-located-default
'cl-defgeneric
336 (expand-file-name "elisp-mode-tests.el" emacs-test-dir
)))
337 (xref-make "(cl-defmethod xref-elisp-generic-co-located-default ((this xref-elisp-root-type)))"
338 (xref-make-elisp-location
339 '(xref-elisp-generic-co-located-default xref-elisp-root-type
) 'cl-defmethod
340 (expand-file-name "elisp-mode-tests.el" emacs-test-dir
)))
343 (xref-elisp-deftest find-defs-defgeneric-separate-default
344 (elisp--xref-find-definitions 'xref-elisp-generic-separate-default
)
346 (xref-make "(cl-defgeneric xref-elisp-generic-separate-default)"
347 (xref-make-elisp-location
348 'xref-elisp-generic-separate-default
'cl-defgeneric
349 (expand-file-name "elisp-mode-tests.el" emacs-test-dir
)))
350 (xref-make "(cl-defmethod xref-elisp-generic-separate-default ())"
351 (xref-make-elisp-location
352 '(xref-elisp-generic-separate-default) 'cl-defmethod
353 (expand-file-name "elisp-mode-tests.el" emacs-test-dir
)))
355 (xref-make "(cl-defmethod xref-elisp-generic-separate-default ((this xref-elisp-root-type)))"
356 (xref-make-elisp-location
357 '(xref-elisp-generic-separate-default xref-elisp-root-type
) 'cl-defmethod
358 (expand-file-name "elisp-mode-tests.el" emacs-test-dir
)))
361 (xref-elisp-deftest find-defs-defgeneric-implicit-generic
362 (elisp--xref-find-definitions 'xref-elisp-generic-implicit-generic
)
364 (xref-make "(cl-defmethod xref-elisp-generic-implicit-generic ())"
365 (xref-make-elisp-location
366 '(xref-elisp-generic-implicit-generic) 'cl-defmethod
367 (expand-file-name "elisp-mode-tests.el" emacs-test-dir
)))
368 (xref-make "(cl-defmethod xref-elisp-generic-implicit-generic ((this xref-elisp-root-type)))"
369 (xref-make-elisp-location
370 '(xref-elisp-generic-implicit-generic xref-elisp-root-type
) 'cl-defmethod
371 (expand-file-name "elisp-mode-tests.el" emacs-test-dir
)))
374 ;; Test that we handle more than one method
376 ;; When run from the Makefile, etags is not loaded at compile time,
377 ;; but it is by the time this test is run. interactively; don't fail
380 (xref-elisp-deftest find-defs-defgeneric-el
381 (elisp--xref-find-definitions 'xref-location-marker
)
383 (xref-make "(cl-defgeneric xref-location-marker)"
384 (xref-make-elisp-location
385 'xref-location-marker
'cl-defgeneric
386 (expand-file-name "../../lisp/progmodes/xref.el" emacs-test-dir
)))
387 (xref-make "(cl-defmethod xref-location-marker ((l xref-elisp-location)))"
388 (xref-make-elisp-location
389 '(xref-location-marker xref-elisp-location
) 'cl-defmethod
390 (expand-file-name "../../lisp/progmodes/elisp-mode.el" emacs-test-dir
)))
391 (xref-make "(cl-defmethod xref-location-marker ((l xref-file-location)))"
392 (xref-make-elisp-location
393 '(xref-location-marker xref-file-location
) 'cl-defmethod
394 (expand-file-name "../../lisp/progmodes/xref.el" emacs-test-dir
)))
395 (xref-make "(cl-defmethod xref-location-marker ((l xref-buffer-location)))"
396 (xref-make-elisp-location
397 '(xref-location-marker xref-buffer-location
) 'cl-defmethod
398 (expand-file-name "../../lisp/progmodes/xref.el" emacs-test-dir
)))
399 (xref-make "(cl-defmethod xref-location-marker ((l xref-bogus-location)))"
400 (xref-make-elisp-location
401 '(xref-location-marker xref-bogus-location
) 'cl-defmethod
402 (expand-file-name "../../lisp/progmodes/xref.el" emacs-test-dir
)))
403 (xref-make "(cl-defmethod xref-location-marker ((l xref-etags-location)))"
404 (xref-make-elisp-location
405 '(xref-location-marker xref-etags-location
) 'cl-defmethod
406 (expand-file-name "../../lisp/progmodes/etags.el" emacs-test-dir
)))
409 (xref-elisp-deftest find-defs-defgeneric-eval
410 (elisp--xref-find-definitions (eval '(cl-defgeneric stephe-leake-cl-defgeneric
())))
413 (xref-elisp-deftest find-defs-defun-el
414 (elisp--xref-find-definitions 'xref-find-definitions
)
416 (xref-make "(defun xref-find-definitions)"
417 (xref-make-elisp-location
418 'xref-find-definitions nil
419 (expand-file-name "../../lisp/progmodes/xref.el" emacs-test-dir
)))))
421 (xref-elisp-deftest find-defs-defun-eval
422 (elisp--xref-find-definitions (eval '(defun stephe-leake-defun ())))
425 (xref-elisp-deftest find-defs-defun-c
426 (elisp--xref-find-definitions 'buffer-live-p
)
428 (xref-make "(defun buffer-live-p)"
429 (xref-make-elisp-location 'buffer-live-p nil
"src/buffer.c"))))
433 (xref-elisp-deftest find-defs-defun-c-defvar-c
434 (elisp-xref-find 'definitions
"system-name")
436 (xref-make "(defvar system-name)"
437 (xref-make-elisp-location 'system-name
'defvar
"src/editfns.c"))
438 (xref-make "(defun system-name)"
439 (xref-make-elisp-location 'system-name nil
"src/editfns.c")))
442 (xref-elisp-deftest find-defs-defun-el-defvar-c
443 (elisp-xref-find 'definitions
"abbrev-mode")
444 ;; It's a minor mode, but the variable is defined in buffer.c
446 (xref-make "(defvar abbrev-mode)"
447 (xref-make-elisp-location 'abbrev-mode
'defvar
"src/buffer.c"))
449 (xref-make "(defun abbrev-mode)"
450 (xref-make-elisp-location
452 (expand-file-name "../../lisp/abbrev.el" emacs-test-dir
)))
453 "(define-minor-mode abbrev-mode"))
456 ;; Source for both variable and defun is "(define-minor-mode
457 ;; compilation-minor-mode". There is no way to tell that directly from
458 ;; the symbol, but we can use (memq sym minor-mode-list) to detect
459 ;; that the symbol is a minor mode. See `elisp--xref-find-definitions'
460 ;; for more comments.
462 ;; IMPROVEME: return defvar instead of defun if source near starting
463 ;; point indicates the user is searching for a variable, not a
465 (require 'compile
) ;; not loaded by default at test time
466 (xref-elisp-deftest find-defs-defun-defvar-el
467 (elisp--xref-find-definitions 'compilation-minor-mode
)
470 (xref-make "(defun compilation-minor-mode)"
471 (xref-make-elisp-location
472 'compilation-minor-mode nil
473 (expand-file-name "../../lisp/progmodes/compile.el" emacs-test-dir
)))
474 "(define-minor-mode compilation-minor-mode")
477 (xref-elisp-deftest find-defs-defvar-el
478 (elisp--xref-find-definitions 'xref--marker-ring
)
480 (xref-make "(defvar xref--marker-ring)"
481 (xref-make-elisp-location
482 'xref--marker-ring
'defvar
483 (expand-file-name "../../lisp/progmodes/xref.el" emacs-test-dir
)))
486 (xref-elisp-deftest find-defs-defvar-c
487 (elisp--xref-find-definitions 'default-directory
)
490 (xref-make "(defvar default-directory)"
491 (xref-make-elisp-location 'default-directory
'defvar
"src/buffer.c"))
492 ;; IMPROVEME: we might be able to compute this target
493 "DEFVAR_PER_BUFFER (\"default-directory\"")))
495 (xref-elisp-deftest find-defs-defvar-eval
496 (elisp--xref-find-definitions (eval '(defvar stephe-leake-defvar nil
)))
499 (xref-elisp-deftest find-defs-face-el
500 (elisp--xref-find-definitions 'font-lock-keyword-face
)
501 ;; 'font-lock-keyword-face is both a face and a var
503 (xref-make "(defvar font-lock-keyword-face)"
504 (xref-make-elisp-location
505 'font-lock-keyword-face
'defvar
506 (expand-file-name "../../lisp/font-lock.el" emacs-test-dir
)))
507 (xref-make "(defface font-lock-keyword-face)"
508 (xref-make-elisp-location
509 'font-lock-keyword-face
'defface
510 (expand-file-name "../../lisp/font-lock.el" emacs-test-dir
)))
513 (xref-elisp-deftest find-defs-face-eval
514 (elisp--xref-find-definitions (eval '(defface stephe-leake-defface nil
"")))
517 (xref-elisp-deftest find-defs-feature-el
518 (elisp--xref-find-definitions 'xref
)
521 (xref-make "(feature xref)"
522 (xref-make-elisp-location
524 (expand-file-name "../../lisp/progmodes/xref.el" emacs-test-dir
)))
528 (xref-elisp-deftest find-defs-feature-eval
529 (elisp--xref-find-definitions (eval '(provide 'stephe-leake-feature
)))
532 (provide 'elisp-mode-tests
)
533 ;;; elisp-mode-tests.el ends here