aboutsummaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-05-15 23:00:59 -0500
committerCraig Jennings <c@cjennings.net>2026-05-15 23:00:59 -0500
commit2dd56761df8e6b6d7b5f99c5c7c6d20f5b2d6447 (patch)
tree503a9863e8177d514b41ef9a534bb0217cea63e2 /tests
parent769cacb7d2719374df88626792914f240f74dbed (diff)
downloaddotemacs-2dd56761df8e6b6d7b5f99c5c7c6d20f5b2d6447.tar.gz
dotemacs-2dd56761df8e6b6d7b5f99c5c7c6d20f5b2d6447.zip
fix(flycheck): correct abbrev-mode no-arg toggle in cj/prose-helpers-on
The shape (if (not (abbrev-mode)) (abbrev-mode)) calls abbrev-mode with no argument -- that's the toggle signature, not a query. When the mode was already on the function flipped it off then on instead of being a no-op. Replaced with (unless (bound-and-true-p VAR) (MODE 1)) for both abbrev-mode and flycheck-mode. 4 ERT tests cover both-off, both-on, and the two mixed states. Also ran the module hardening pass across 24 newly-added modules, renamed the six completed Review sub-tasks to Harden, filed 11 new findings under their Harden parents, and broke three design specs (EMMS-free music, dev F-keys, dev-setup-project) into 20 dependency-ordered sub-tasks via parallel subagents. Verified the sqlite finalizer bug from 2026-04-26 is gone and closed its tracking entry.
Diffstat (limited to 'tests')
-rw-r--r--tests/test-flycheck-config-prose-helpers-on.el88
1 files changed, 88 insertions, 0 deletions
diff --git a/tests/test-flycheck-config-prose-helpers-on.el b/tests/test-flycheck-config-prose-helpers-on.el
new file mode 100644
index 00000000..5c6553b3
--- /dev/null
+++ b/tests/test-flycheck-config-prose-helpers-on.el
@@ -0,0 +1,88 @@
+;;; test-flycheck-config-prose-helpers-on.el --- Tests for cj/prose-helpers-on -*- lexical-binding: t; -*-
+
+;;; Commentary:
+
+;; Unit tests for `cj/prose-helpers-on'. The function must enable
+;; `abbrev-mode' and `flycheck-mode' with an explicit positive
+;; argument, and must be a no-op when both are already on.
+;;
+;; Regression: a prior shape
+;; (if (not (abbrev-mode)) (abbrev-mode))
+;; called `abbrev-mode' with no argument -- the toggle signature, not
+;; a query. When the mode was already on the function flipped it off
+;; then on (two transitions per invocation, firing the disable/enable
+;; hooks each time) instead of being a no-op.
+
+;;; Code:
+
+(require 'ert)
+(require 'cl-lib)
+(require 'flycheck-config)
+
+;; `abbrev-mode' is preloaded (defvar in core); `flycheck-mode' is autoloaded
+;; via use-package with =:defer t= so its defvar hasn't run by the time the
+;; tests dynamically let-bind these variables. Declare both as special here
+;; so `let' creates dynamic bindings that `bound-and-true-p' inside the
+;; production code can read -- otherwise lexical-binding=t makes the let
+;; lexical-only and the inner reads can't see the test's state.
+(defvar abbrev-mode)
+(defvar flycheck-mode)
+
+(defmacro test-flycheck-config--with-prose-spies (abbrev-state flycheck-state &rest body)
+ "Run BODY with `abbrev-mode' / `flycheck-mode' stubbed and dynamic.
+ABBREV-STATE and FLYCHECK-STATE seed the dynamic values of the mode
+variables so `bound-and-true-p' inside the production code reads
+them. Inside BODY, `abbrev-calls' / `flycheck-calls' carry the args
+each stub received in call order. The stubs return their arg so the
+buggy no-arg toggle shape terminates without infinite recursion."
+ (declare (indent 2))
+ `(let ((abbrev-calls '())
+ (flycheck-calls '())
+ (abbrev-mode ,abbrev-state)
+ (flycheck-mode ,flycheck-state))
+ (cl-letf (((symbol-function 'abbrev-mode)
+ (lambda (&optional arg)
+ (setq abbrev-calls (append abbrev-calls (list arg)))
+ arg))
+ ((symbol-function 'flycheck-mode)
+ (lambda (&optional arg)
+ (setq flycheck-calls (append flycheck-calls (list arg)))
+ arg)))
+ ,@body)))
+
+(ert-deftest test-flycheck-config-prose-helpers-on-normal-both-off-enables-both ()
+ "Normal: both modes off -> each enabled with explicit positive arg."
+ (test-flycheck-config--with-prose-spies nil nil
+ (cj/prose-helpers-on)
+ (should (= 1 (length abbrev-calls)))
+ (should (> (prefix-numeric-value (car abbrev-calls)) 0))
+ (should (= 1 (length flycheck-calls)))
+ (should (> (prefix-numeric-value (car flycheck-calls)) 0))))
+
+(ert-deftest test-flycheck-config-prose-helpers-on-boundary-both-on-is-noop ()
+ "Boundary: both modes already on -> neither mode function called.
+Catches the no-arg toggle shape; the bug records at least one call
+with a nil arg."
+ (test-flycheck-config--with-prose-spies t t
+ (cj/prose-helpers-on)
+ (should (null abbrev-calls))
+ (should (null flycheck-calls))))
+
+(ert-deftest test-flycheck-config-prose-helpers-on-boundary-abbrev-on-flycheck-off ()
+ "Boundary: abbrev on, flycheck off -> only flycheck enabled."
+ (test-flycheck-config--with-prose-spies t nil
+ (cj/prose-helpers-on)
+ (should (null abbrev-calls))
+ (should (= 1 (length flycheck-calls)))
+ (should (> (prefix-numeric-value (car flycheck-calls)) 0))))
+
+(ert-deftest test-flycheck-config-prose-helpers-on-boundary-flycheck-on-abbrev-off ()
+ "Boundary: flycheck on, abbrev off -> only abbrev enabled."
+ (test-flycheck-config--with-prose-spies nil t
+ (cj/prose-helpers-on)
+ (should (= 1 (length abbrev-calls)))
+ (should (> (prefix-numeric-value (car abbrev-calls)) 0))
+ (should (null flycheck-calls))))
+
+(provide 'test-flycheck-config-prose-helpers-on)
+;;; test-flycheck-config-prose-helpers-on.el ends here