1 ;;; cus-test.el --- tests for custom types and load problems
3 ;; Copyright (C) 1998, 2000, 2002-2018 Free Software Foundation, Inc.
5 ;; Author: Markus Rost <markus.rost@mathematik.uni-regensburg.de>
6 ;; Maintainer: Markus Rost <rost@math.ohio-state.edu>
7 ;; Created: 13 Sep 1998
10 ;; This file is part of GNU Emacs.
12 ;; GNU Emacs is free software: you can redistribute it and/or modify
13 ;; it under the terms of the GNU General Public License as published by
14 ;; the Free Software Foundation, either version 3 of the License, or
15 ;; (at your option) any later version.
17 ;; GNU Emacs is distributed in the hope that it will be useful,
18 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 ;; GNU General Public License for more details.
22 ;; You should have received a copy of the GNU General Public License
23 ;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
27 ;; This file provides simple tests to detect custom options with
28 ;; incorrect customization types and load problems for custom and
29 ;; autoload dependencies.
31 ;; The basic tests can be run in batch mode. Invoke them with
33 ;; src/emacs -batch -l admin/cus-test.el -f cus-test-opts [all]
35 ;; src/emacs -batch -l admin/cus-test.el -f cus-test-deps
37 ;; src/emacs -batch -l admin/cus-test.el -f cus-test-libs [all]
39 ;; src/emacs -batch -l admin/cus-test.el -f cus-test-noloads
41 ;; in the emacs source directory.
43 ;; For interactive use: Load this file. Then
45 ;; M-x cus-test-apropos REGEXP RET
47 ;; checks the options matching REGEXP. In particular
49 ;; M-x cus-test-apropos RET
51 ;; checks all options. The detected options are stored in the
52 ;; variable `cus-test-errors'.
54 ;; Only those options are checked which have been already loaded.
55 ;; Therefore `cus-test-apropos' is more efficient after loading many
58 ;; M-x cus-test-load-custom-loads
60 ;; loads all (!) custom dependencies and
62 ;; M-x cus-test-load-libs
64 ;; loads all (!) libraries with autoloads.
66 ;; Options with a custom-get property, usually defined by a :get
67 ;; declaration, are stored in the variable
69 ;; `cus-test-vars-with-custom-get'
71 ;; Options with a state of 'changed ("changed outside the customize
72 ;; buffer") are stored in the variable
74 ;; `cus-test-vars-with-changed-state'
76 ;; These lists are prepared just in case one wants to investigate
77 ;; those options further.
79 ;; The command `cus-test-opts' tests many (all?) custom options.
81 ;; The command `cus-test-deps' is like `cus-test-load-custom-loads'
82 ;; but reports about load errors.
84 ;; The command `cus-test-libs' runs for all libraries with autoloads
85 ;; separate emacs processes of the form "emacs -batch -l LIB".
87 ;; The command `cus-test-noloads' returns a list of variables which
88 ;; are somewhere declared as custom options, but not loaded by
89 ;; `custom-load-symbol'.
94 ;;; Workarounds. For a smooth run and to avoid some side effects.
96 (defvar cus-test-after-load-libs-hook nil
97 "Used to switch off undesired side effects of loading libraries.")
99 (defvar cus-test-skip-list nil
100 "List of variables to disregard by `cus-test-apropos'.")
102 (defvar cus-test-libs-noloads
103 ;; Loading dunnet in batch mode leads to a Dead end.
104 ;; blessmail writes a file.
105 ;; characters cannot be loaded twice ("Category `a' is already defined").
106 '("play/dunnet.el" "emulation/edt-mapper.el"
107 "loadup.el" "mail/blessmail.el" "international/characters.el"
108 "cedet/ede/loaddefs.el" "cedet/semantic/loaddefs.el"
109 "net/tramp-loaddefs.el")
110 "List of files not to load by `cus-test-load-libs'.
111 Names should be as they appear in loaddefs.el.")
113 ;; This avoids a hang of `cus-test-apropos' in 21.2.
114 ;; (add-to-list 'cus-test-skip-list 'sh-alias-alist)
118 (setq viper-mode nil
))
120 ;; Don't create a file `save-place-file'.
121 (eval-after-load "saveplace"
122 '(remove-hook 'kill-emacs-hook
'save-place-kill-emacs-hook
))
124 ;; Don't create a file `abbrev-file-name'.
125 (setq save-abbrevs nil
)
127 ;; Avoid compile logs from adviced functions.
128 (eval-after-load "bytecomp"
129 '(setq ad-default-compilation-action
'never
))
134 ;; We want to log all messages.
135 (setq message-log-max t
)
140 (defvar cus-test-errors nil
141 "List of problematic variables found by `cus-test-apropos'.")
143 (defvar cus-test-tested-variables nil
144 "List of options tested by last call of `cus-test-apropos'.")
146 ;; I haven't understood this :get stuff. The symbols with a
147 ;; custom-get property are stored here.
148 (defvar cus-test-vars-with-custom-get nil
149 "Set by `cus-test-apropos' to a list of options with :get property.")
151 (defvar cus-test-vars-with-changed-state nil
152 "Set by `cus-test-apropos' to a list of options with state 'changed.")
154 (defvar cus-test-deps-errors nil
155 "List of require/load problems found by `cus-test-deps'.")
157 (defvar cus-test-deps-required nil
158 "List of dependencies required by `cus-test-deps'.
159 Only unloaded features will be require'd.")
161 (defvar cus-test-deps-loaded nil
162 "List of dependencies loaded by `cus-test-deps'.")
164 (defvar cus-test-libs-errors nil
165 "List of load problems found by `cus-test-load-libs' or `cus-test-libs'.")
167 (defvar cus-test-libs-loaded nil
168 "List of files loaded by `cus-test-load-libs' or `cus-test-libs'.")
170 (defvar cus-test-vars-not-cus-loaded nil
171 "A list of options not loaded by `custom-load-symbol'.
172 Set by `cus-test-noloads'.")
174 ;; (defvar cus-test-vars-cus-loaded nil
175 ;; "A list of options loaded by `custom-load-symbol'.")
177 (defun cus-test-apropos (regexp)
178 "Check the options matching REGEXP.
179 The detected problematic options are stored in `cus-test-errors'."
180 (interactive "sVariable regexp: ")
181 (setq cus-test-errors nil
)
182 (setq cus-test-tested-variables nil
)
185 (push symbol cus-test-tested-variables
)
186 ;; Be verbose in case we hang.
187 (message "Cus Test running...%s %s"
188 (length cus-test-tested-variables
) symbol
)
189 (condition-case alpha
190 ;; FIXME This defaults to 'sexp if no type was specified.
191 ;; Always report such instances as a type mismatch.
192 ;; Currently abusing cusver-scan to do that.
193 (let* ((type (custom-variable-type symbol
))
194 (conv (widget-convert type
))
195 (get (or (get symbol
'custom-get
) 'default-value
))
198 (when (default-boundp symbol
)
199 (push (funcall get symbol
) values
)
200 (push (eval (car (get symbol
'standard-value
))) values
))
202 (push (symbol-value symbol
) values
))
203 ;; That does not work.
204 ;; (push (widget-get conv :value) values)
207 (mapc (lambda (value)
208 ;; TODO for booleans, check for values that can be
209 ;; evaluated and are not t or nil. Usually a bug.
210 (unless (widget-apply conv
:match value
)
211 (setq mismatch
'mismatch
)))
214 ;; Store symbols with a custom-get property.
215 (when (get symbol
'custom-get
)
216 (add-to-list 'cus-test-vars-with-custom-get symbol
))
218 ;; Changed outside the customize buffer?
219 ;; This routine is not very much tested.
221 (or (get symbol
'customized-value
)
222 (get symbol
'saved-value
)
223 (get symbol
'standard-value
))))
226 (not (equal (eval (car c-value
)) (symbol-value symbol
)))
227 (add-to-list 'cus-test-vars-with-changed-state symbol
)))
230 (push symbol cus-test-errors
)))
233 (push symbol cus-test-errors
)
234 (message "Error for %s: %s" symbol alpha
))))
235 (cus-test-get-options regexp
))
236 (message "%s options tested"
237 (length cus-test-tested-variables
))
238 (cus-test-errors-display))
240 (defun cus-test-cus-load-groups (&optional cus-load
)
241 "Return a list of current custom groups.
242 If CUS-LOAD is non-nil, include groups from cus-load.el."
243 (append (mapcar 'cdr custom-current-group-alist
)
246 (insert-file-contents (locate-library "cus-load.el"))
247 (search-forward "(put '")
250 (while (and (looking-at "^(put '\\(\\S-+\\)")
251 (zerop (forward-line 1)))
252 (push (intern (match-string 1)) res
))
255 (defun cus-test-get-options (regexp &optional group
)
256 "Return a list of custom options matching REGEXP.
257 If GROUP is non-nil, return groups rather than options.
258 If GROUP is `cus-load', include groups listed in cus-loads as well as
259 currently defined groups."
260 (let ((groups (if group
(cus-test-cus-load-groups (eq group
'cus-load
))))
268 ;; (user-variable-p symbol)
269 (get symbol
'standard-value
)
270 ;; (get symbol 'saved-value)
271 (get symbol
'custom-type
)))
272 (string-match regexp
(symbol-name symbol
))
273 (not (member symbol cus-test-skip-list
))
274 (push symbol found
))))
277 (defun cus-test-errors-display ()
278 "Report about the errors found by cus-test."
279 (with-output-to-temp-buffer "*cus-test-errors*"
280 (set-buffer standard-output
)
281 (insert (format "Cus Test tested %s variables.\
282 See `cus-test-tested-variables'.\n\n"
283 (length cus-test-tested-variables
)))
284 (if (not cus-test-errors
)
285 (insert "No errors found by cus-test.")
286 (insert "The following variables seem to have problems:\n\n")
287 (dolist (e cus-test-errors
)
288 (insert (symbol-name e
) "\n")))))
290 (defun cus-test-load-custom-loads ()
291 "Call `custom-load-symbol' on all atoms."
293 (if noninteractive
(let (noninteractive) (require 'dunnet
)))
294 (mapatoms 'custom-load-symbol
)
295 (run-hooks 'cus-test-after-load-libs-hook
))
297 (defmacro cus-test-load-1
(&rest body
)
299 (setq cus-test-libs-errors nil
300 cus-test-libs-loaded nil
)
302 (message "%s libraries loaded successfully"
303 (length cus-test-libs-loaded
))
304 (if (not cus-test-libs-errors
)
305 (message "No load problems encountered")
306 (message "The following load problems appeared:")
307 (cus-test-message cus-test-libs-errors
))
308 (run-hooks 'cus-test-after-load-libs-hook
)))
310 ;; This is just cus-test-libs, but loading in the current Emacs process.
311 (defun cus-test-load-libs (&optional more
)
312 "Load the libraries with autoloads.
313 Don't load libraries in `cus-test-libs-noloads'.
314 If optional argument MORE is \"defcustom\", load all files with defcustoms.
315 If it is \"all\", load all Lisp files."
318 (let ((lispdir (file-name-directory (locate-library "loaddefs"))))
321 (condition-case alpha
322 (unless (member file cus-test-libs-noloads
)
323 (load (file-name-sans-extension (expand-file-name file lispdir
)))
324 (push file cus-test-libs-loaded
))
326 (push (cons file alpha
) cus-test-libs-errors
)
327 (message "Error for %s: %s" file alpha
))))
329 (cus-test-get-lisp-files (equal more
"all"))
330 (cus-test-get-autoload-deps))))))
332 (defun cus-test-get-autoload-deps ()
333 "Return the list of files with autoloads."
335 (insert-file-contents (locate-library "loaddefs"))
337 (while (search-forward "\n;;; Generated autoloads from " nil t
)
338 (push (buffer-substring (match-end 0) (line-end-position)) files
))
341 (defun cus-test-get-lisp-files (&optional all
)
342 "Return list of all Lisp files with defcustoms.
343 Optional argument ALL non-nil means list all (non-obsolete) Lisp files."
344 (let ((default-directory (expand-file-name "lisp/" source-directory
))
345 (msg "Finding files..."))
348 ;; Hack to remove leading "./".
349 (mapcar (lambda (e) (substring e
2))
350 (apply 'process-lines find-program
351 "-name" "obsolete" "-prune" "-o"
352 "-name" "[^.]*.el" ; ignore .dir-locals.el
355 (list "-exec" grep-program
356 "-l" "^[ \t]*(defcustom" "{}" "+"))))
357 (message "%sdone" msg
))))
359 (defun cus-test-message (list)
360 "Print the members of LIST line by line."
361 (dolist (m list
) (message "%s" m
)))
364 ;;; The routines for batch mode:
366 (defun cus-test-opts (&optional all
)
367 "Test custom options.
368 This function is suitable for batch mode. E.g., invoke
370 src/emacs -batch -l admin/cus-test.el -f cus-test-opts
372 in the Emacs source directory.
373 Normally only tests options belonging to files in loaddefs.el.
374 If optional argument ALL is non-nil, test all files with defcustoms."
377 command-line-args-left
378 (setq all
(pop command-line-args-left
)))
379 (message "Running %s" 'cus-test-load-libs
)
380 (cus-test-load-libs (if all
"defcustom"))
381 (message "Running %s" 'cus-test-load-custom-loads
)
382 (cus-test-load-custom-loads)
383 (message "Running %s" 'cus-test-apropos
)
384 (cus-test-apropos "")
385 (if (not cus-test-errors
)
386 (message "No problems found")
387 (message "The following options might have problems:")
388 (cus-test-message cus-test-errors
)))
390 (defun cus-test-deps ()
391 "Run a verbose version of `custom-load-symbol' on all atoms.
392 This function is suitable for batch mode. E.g., invoke
394 src/emacs -batch -l admin/cus-test.el -f cus-test-deps
396 in the Emacs source directory."
398 (setq cus-test-deps-errors nil
)
399 (setq cus-test-deps-required nil
)
400 (setq cus-test-deps-loaded nil
)
402 ;; This code is mainly from `custom-load-symbol'.
404 (let ((custom-load-recursion t
))
405 (dolist (load (get symbol
'custom-loads
))
408 ;; (condition-case nil (require load) (error nil))
409 (condition-case alpha
410 (unless (or (featurep load
)
411 (and noninteractive
(eq load
'dunnet
)))
413 (push (list symbol load
) cus-test-deps-required
))
415 (push (list symbol load alpha
) cus-test-deps-errors
)
416 (message "Require problem: %s %s %s" symbol load alpha
))))
417 ((equal load
"loaddefs")
419 (message "Symbol %s has loaddefs as custom dependency" symbol
)
420 cus-test-deps-errors
))
421 ;; This is subsumed by the test below, but it's much
423 ((assoc load load-history
))
425 ;; (assoc (locate-library load) load-history)
426 ;; but has been optimized not to load locate-library
428 ((let ((regexp (concat "\\(\\`\\|/\\)" (regexp-quote load
)
431 (dolist (loaded load-history
)
432 (and (stringp (car loaded
))
433 (string-match regexp
(car loaded
))
436 ;; Without this, we would load cus-edit recursively.
437 ;; We are still loading it when we call this,
438 ;; and it is not in load-history yet.
439 ((equal load
"cus-edit"))
440 ;; This would ignore load problems with files in
442 ;; ((locate-library (concat term-file-prefix load)))
444 ;; (condition-case nil (load load) (error nil))
445 (condition-case alpha
448 (push (list symbol load
) cus-test-deps-loaded
))
450 (push (list symbol load alpha
) cus-test-deps-errors
)
451 (message "Load Problem: %s %s %s" symbol load alpha
))))
453 (message "%s features required"
454 (length cus-test-deps-required
))
455 (message "%s files loaded"
456 (length cus-test-deps-loaded
))
457 (if (not cus-test-deps-errors
)
458 (message "No load problems encountered")
459 (message "The following load problems appeared:")
460 (cus-test-message cus-test-deps-errors
))
461 (run-hooks 'cus-test-after-load-libs-hook
))
463 (defun cus-test-libs (&optional more
)
464 "Load the libraries with autoloads in separate processes.
465 This function is useful to detect load problems of libraries.
466 It is suitable for batch mode. E.g., invoke
468 ./src/emacs -batch -l admin/cus-test.el -f cus-test-libs
470 in the Emacs source directory.
472 If optional argument MORE is \"defcustom\", load all files with defcustoms.
473 If it is \"all\", load all Lisp files."
476 command-line-args-left
477 (setq more
(pop command-line-args-left
)))
479 (let* ((default-directory source-directory
)
480 (emacs (expand-file-name "src/emacs"))
482 (or (file-executable-p emacs
)
483 (error "No such executable `%s'" emacs
))
486 (if (member file cus-test-libs-noloads
)
488 (condition-case alpha
489 (let* ((fn (expand-file-name file
"lisp/"))
490 (elc (concat fn
"c"))
492 (if (file-readable-p elc
) ; load compiled if present (faster)
494 (or (file-readable-p fn
)
495 (error "Library %s not found" file
)))
496 (if (equal 0 (setq status
(call-process emacs nil nil nil
500 (push file cus-test-libs-loaded
))
502 (push (cons file alpha
) cus-test-libs-errors
)
503 (message "Error for %s: %s" file alpha
)))))
505 (cus-test-get-lisp-files (equal more
"all"))
506 (cus-test-get-autoload-deps)))
507 (message "Default directory: %s" default-directory
)
509 (message "The following libraries were skipped:")
510 (cus-test-message skipped
)))))
512 (defun cus-test-noloads ()
513 "Find custom options not loaded by `custom-load-symbol'.
514 Calling this function after `cus-test-load-libs' is not meaningful.
515 It is suitable for batch mode. E.g., invoke
517 src/emacs -batch -l admin/cus-test.el -f cus-test-noloads
519 in the Emacs source directory."
521 (let ((groups-loaded (cus-test-get-options "" 'cus-load
))
522 cus-loaded groups-not-loaded
)
524 (message "Running %s" 'cus-test-load-custom-loads
)
525 (cus-test-load-custom-loads)
526 (setq cus-loaded
(cus-test-get-options ""))
528 (message "Running %s" 'cus-test-load-libs
)
529 (cus-test-load-libs "all")
530 (setq cus-test-vars-not-cus-loaded
(cus-test-get-options "")
531 groups-not-loaded
(cus-test-get-options "" t
))
533 (dolist (o cus-loaded
)
534 (setq cus-test-vars-not-cus-loaded
535 (delete o cus-test-vars-not-cus-loaded
)))
537 (if (not cus-test-vars-not-cus-loaded
)
538 (message "No options not loaded by custom-load-symbol found")
539 (message "The following options were not loaded by custom-load-symbol:")
541 (sort cus-test-vars-not-cus-loaded
'string
<)))
543 (dolist (o groups-loaded
)
544 (setq groups-not-loaded
(delete o groups-not-loaded
)))
546 (if (not groups-not-loaded
)
547 (message "No groups not in cus-load.el found")
548 (message "The following groups are not in cus-load.el:")
549 (cus-test-message (sort groups-not-loaded
'string
<)))))
553 ;;; cus-test.el ends here