aboutsummaryrefslogtreecommitdiffstats
path: root/test
diff options
context:
space:
mode:
authorF. Jason Park2023-10-06 17:34:04 -0700
committerF. Jason Park2023-10-13 07:47:00 -0700
commitd46c016fbd09cbce9ef23fe2b49d4fb5fc3b2b16 (patch)
tree7f1acf7155396ca7324853051071b13447a8b6e6 /test
parent9120d7a32ea4906d7c9460add31d37c3ca38931e (diff)
downloademacs-d46c016fbd09cbce9ef23fe2b49d4fb5fc3b2b16.tar.gz
emacs-d46c016fbd09cbce9ef23fe2b49d4fb5fc3b2b16.zip
Sort and dedupe when loading modules in erc-open
* doc/misc/erc.texi: Add new subheading "Module Loading" under the "Modules" chapter. * lisp/erc/erc.el (erc--sort-modules): New utility function to sort and dedupe modules. (erc-modules): In `custom-set' function, factor out collation into separate utility `erc--sort-modules'. (erc-update-modules): Call `erc--update-modules' with an argument, the current value of `erc-modules'. (erc--aberrant-modules): New variable, a list of symbols whose modules ERC suspects of being incorrectly defined. (erc--warn-about-aberrant-modules): New function to print an error message and emit a warning prior to connecting when `erc--aberrant-modules' is non-nil. (erc--find-mode): Make heuristic more robust by always checking for a mode activation command rather than just a state variable. This fixes a compatibility bug, new in 5.6, affecting third-party modules that autoload module definitions instead of their corresponding mode-activation commands. (erc--update-modules): Add new positional argument `modules'. (erc--setup-buffer-hook): Add new default member, `erc--warn-about-aberrant-modules'. (erc-open): Pass sorted `erc-modules' to `erc--update-modules'. * test/lisp/erc/erc-tests.el (erc--sort-modules): New test. (erc-tests--update-modules): New fixture. (erc--update-modules): Remove and rework as three separate tests dedicated to specific contexts. The existing one had poor coverage and was difficult, if not impossible, to follow. (erc--update-modules/unknown, erc--update-modules/local, erc--update-modules/realistic): New tests. (Bug#57955)
Diffstat (limited to 'test')
-rw-r--r--test/lisp/erc/erc-tests.el163
1 files changed, 114 insertions, 49 deletions
diff --git a/test/lisp/erc/erc-tests.el b/test/lisp/erc/erc-tests.el
index 64b503832f3..0b88ad9cfa9 100644
--- a/test/lisp/erc/erc-tests.el
+++ b/test/lisp/erc/erc-tests.el
@@ -2293,65 +2293,130 @@
2293 (should (eq (erc--find-group 'smiley nil) 'erc)) 2293 (should (eq (erc--find-group 'smiley nil) 'erc))
2294 (should (eq (erc--find-group 'unmorse nil) 'erc))) 2294 (should (eq (erc--find-group 'unmorse nil) 'erc)))
2295 2295
2296(ert-deftest erc--update-modules () 2296(ert-deftest erc--sort-modules ()
2297 (let (calls 2297 (should (equal (erc--sort-modules '(networks foo fill bar fill stamp bar))
2298 erc-modules 2298 ;; Third-party mods appear in original order.
2299 erc-kill-channel-hook erc-kill-server-hook erc-kill-buffer-hook) 2299 '(fill networks stamp foo bar))))
2300
2301(defun erc-tests--update-modules (fn)
2302 (let* ((calls nil)
2303 (custom-modes nil)
2304 (on-load nil)
2305
2306 (get-calls (lambda () (prog1 (nreverse calls) (setq calls nil))))
2307
2308 (add-onload (lambda (m k v)
2309 (put (intern m) 'erc--feature k)
2310 (push (cons k (lambda () (funcall v m))) on-load)))
2300 2311
2301 ;; This `lbaz' module is unknown, so ERC looks for it via the 2312 (mk-cmd (lambda (module)
2302 ;; symbol proerty `erc--feature' and, failing that, by 2313 (let ((mode (intern (format "erc-%s-mode" module))))
2303 ;; `require'ing its "erc-" prefixed symbol. 2314 (fset mode (lambda (n) (push (cons mode n) calls))))))
2304 (should-not (intern-soft "erc-lbaz-mode")) 2315
2316 (mk-builtin (lambda (module-string)
2317 (let ((s (intern module-string)))
2318 (put s 'erc--module s))))
2319
2320 (mk-global (lambda (module)
2321 (push (intern (format "erc-%s-mode" module))
2322 custom-modes))))
2305 2323
2306 (cl-letf (((symbol-function 'require) 2324 (cl-letf (((symbol-function 'require)
2307 (lambda (s &rest _) 2325 (lambda (s &rest _)
2308 (when (eq s 'erc--lbaz-feature) 2326 ;; Simulate library being loaded, things defined.
2309 (fset (intern "erc-lbaz-mode") ; local module 2327 (when-let ((h (alist-get s on-load))) (funcall h))
2310 (lambda (n) (push (cons 'lbaz n) calls)))) 2328 (push (cons 'req s) calls)))
2311 (push s calls))) 2329
2312 2330 ;; Spoof global module detection.
2313 ;; Local modules 2331 ((symbol-function 'custom-variable-p)
2314 ((symbol-function 'erc-lbar-mode) 2332 (lambda (v) (memq v custom-modes))))
2315 (lambda (n) (push (cons 'lbar n) calls))) 2333
2316 ((get 'lbaz 'erc--feature) 'erc--lbaz-feature) 2334 (funcall fn get-calls add-onload mk-cmd mk-builtin mk-global))
2317 2335 (should-not erc--aberrant-modules)))
2318 ;; Global modules 2336
2319 ((symbol-function 'erc-gfoo-mode) 2337(ert-deftest erc--update-modules/unknown ()
2320 (lambda (n) (push (cons 'gfoo n) calls))) 2338 (erc-tests--update-modules
2321 ((get 'erc-gfoo-mode 'standard-value) 'ignore) 2339
2340 (lambda (get-calls _ mk-cmd _ mk-global)
2341
2342 (ert-info ("Baseline")
2343 (let* ((erc-modules '(foo))
2344 (obarray (obarray-make))
2345 (err (should-error (erc--update-modules erc-modules))))
2346 (should (equal (cadr err) "`foo' is not a known ERC module"))
2347 (should (equal (funcall get-calls)
2348 `((req . ,(intern-soft "erc-foo")))))))
2349
2350 ;; Module's mode command exists but lacks an associated file.
2351 (ert-info ("Bad autoload flagged as suspect")
2352 (should-not erc--aberrant-modules)
2353 (let* ((erc--aberrant-modules nil)
2354 (obarray (obarray-make))
2355 (erc-modules (list (intern "foo"))))
2356
2357 ;; Create a mode activation command.
2358 (funcall mk-cmd "foo")
2359
2360 ;; Make the mode var global.
2361 (funcall mk-global "foo")
2362
2363 ;; No local modules to return.
2364 (should-not (erc--update-modules erc-modules))
2365 (should (equal (mapcar #'prin1-to-string erc--aberrant-modules)
2366 '("foo")))
2367 ;; ERC requires the library via prefixed module name.
2368 (should (equal (mapcar #'prin1-to-string (funcall get-calls))
2369 `("(req . erc-foo)" "(erc-foo-mode . 1)"))))))))
2370
2371;; A local module (here, `lo2') lacks a mode toggle, so ERC tries to
2372;; load its defining library, first via the symbol property
2373;; `erc--feature', and then via an "erc-" prefixed symbol.
2374(ert-deftest erc--update-modules/local ()
2375 (erc-tests--update-modules
2376
2377 (lambda (get-calls add-onload mk-cmd mk-builtin mk-global)
2378
2379 (let* ((obarray (obarray-make 20))
2380 (erc-modules (mapcar #'intern '("glo" "lo1" "lo2"))))
2381
2382 ;; Create a global and a local module.
2383 (mapc mk-cmd '("glo" "lo1"))
2384 (mapc mk-builtin '("glo" "lo1"))
2385 (funcall mk-global "glo")
2386 (funcall add-onload "lo2" 'explicit-feature-lib mk-cmd)
2387
2388 ;; Returns local modules.
2389 (should (equal (mapcar #'symbol-name (erc--update-modules erc-modules))
2390 '("erc-lo2-mode" "erc-lo1-mode")))
2391
2392 ;; Requiring `erc-lo2' defines `erc-lo2-mode'.
2393 (should (equal (mapcar #'prin1-to-string (funcall get-calls))
2394 `("(erc-glo-mode . 1)"
2395 "(req . explicit-feature-lib)")))))))
2396
2397(ert-deftest erc--update-modules/realistic ()
2398 (let ((calls nil)
2399 ;; Module `pcomplete' "resolves" to `completion'.
2400 (erc-modules '(pcomplete autojoin networks)))
2401 (cl-letf (((symbol-function 'require)
2402 (lambda (s &rest _) (push (cons 'req s) calls)))
2403
2404 ;; Spoof global module detection.
2405 ((symbol-function 'custom-variable-p)
2406 (lambda (v)
2407 (memq v '(erc-autojoin-mode erc-networks-mode
2408 erc-completion-mode))))
2409 ;; Mock and spy real builtins.
2322 ((symbol-function 'erc-autojoin-mode) 2410 ((symbol-function 'erc-autojoin-mode)
2323 (lambda (n) (push (cons 'autojoin n) calls))) 2411 (lambda (n) (push (cons 'autojoin n) calls)))
2324 ((get 'erc-autojoin-mode 'standard-value) 'ignore)
2325 ((symbol-function 'erc-networks-mode) 2412 ((symbol-function 'erc-networks-mode)
2326 (lambda (n) (push (cons 'networks n) calls))) 2413 (lambda (n) (push (cons 'networks n) calls)))
2327 ((get 'erc-networks-mode 'standard-value) 'ignore)
2328 ((symbol-function 'erc-completion-mode) 2414 ((symbol-function 'erc-completion-mode)
2329 (lambda (n) (push (cons 'completion n) calls))) 2415 (lambda (n) (push (cons 'completion n) calls))))
2330 ((get 'erc-completion-mode 'standard-value) 'ignore))
2331
2332 (ert-info ("Unknown module")
2333 (setq erc-modules '(lfoo))
2334 (should-error (erc--update-modules))
2335 (should (equal (pop calls) 'erc-lfoo))
2336 (should-not calls))
2337 2416
2338 (ert-info ("Local modules") 2417 (should-not (erc--update-modules erc-modules)) ; no locals
2339 (setq erc-modules '(gfoo lbar lbaz)) 2418 (should (equal (nreverse calls)
2340 ;; Don't expose the mode here 2419 '((completion . 1) (autojoin . 1) (networks . 1)))))))
2341 (should (equal (mapcar #'symbol-name (erc--update-modules))
2342 '("erc-lbaz-mode" "erc-lbar-mode")))
2343 ;; Lbaz required because unknown.
2344 (should (equal (nreverse calls) '((gfoo . 1) erc--lbaz-feature)))
2345 (fmakunbound (intern "erc-lbaz-mode"))
2346 (unintern (intern "erc-lbaz-mode") obarray)
2347 (setq calls nil))
2348
2349 (ert-info ("Global modules") ; `pcomplete' resolved to `completion'
2350 (setq erc-modules '(pcomplete autojoin networks))
2351 (should-not (erc--update-modules)) ; no locals
2352 (should (equal (nreverse calls)
2353 '((completion . 1) (autojoin . 1) (networks . 1))))
2354 (setq calls nil)))))
2355 2420
2356(ert-deftest erc--merge-local-modes () 2421(ert-deftest erc--merge-local-modes ()
2357 (cl-letf (((get 'erc-b-mode 'erc-module) 'b) 2422 (cl-letf (((get 'erc-b-mode 'erc-module) 'b)