aboutsummaryrefslogtreecommitdiff
path: root/tests/test-meta-package-headers.el
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-06-29 04:08:19 -0400
committerCraig Jennings <c@cjennings.net>2026-06-29 04:08:19 -0400
commit3f46bda7047a3b6bb9fe778887174209b835d504 (patch)
tree2340127103b34adde07d87579401e3135b37c745 /tests/test-meta-package-headers.el
parent613b6d11e89e0395ec64f771f36ec0bb2ab517e0 (diff)
downloaddotemacs-3f46bda7047a3b6bb9fe778887174209b835d504.tar.gz
dotemacs-3f46bda7047a3b6bb9fe778887174209b835d504.zip
refactor: normalize module package headers and enforce them
The first-line header on 33 modules named the file without its .el extension (;;; font-config --- ... rather than ;;; font-config.el --- ...), the form checkdoc and package-lint expect and the other modules already use. I normalized all 33 to the canonical ;;; name.el --- summary shape. The change is line 1 only. A new test, test-meta-package-headers.el, locks the convention. It checks every module for the canonical first line, Commentary before Code, a provide footer, and no BOM, and unit-tests the checker against each malformed shape so the guard itself is proven.
Diffstat (limited to 'tests/test-meta-package-headers.el')
-rw-r--r--tests/test-meta-package-headers.el98
1 files changed, 98 insertions, 0 deletions
diff --git a/tests/test-meta-package-headers.el b/tests/test-meta-package-headers.el
new file mode 100644
index 000000000..f9b57cbfc
--- /dev/null
+++ b/tests/test-meta-package-headers.el
@@ -0,0 +1,98 @@
+;;; test-meta-package-headers.el --- Enforce Elisp package-header conventions -*- lexical-binding: t; -*-
+
+;;; Commentary:
+;; Checks that every owned active config module follows the standard Emacs
+;; Library Header conventions -- the part test-init-module-headers.el does not
+;; cover (it enforces the load-graph metadata block inside the Commentary):
+;;
+;; 1. First line is ;;; NAME.el --- SUMMARY -*- ... -*- (name carries the
+;; .el, summary present, file-local-variable cookie present).
+;; 2. ;;; Commentary: appears before ;;; Code:.
+;; 3. A (provide 'NAME) footer, so the file is require-able.
+;; 4. No UTF-8 BOM before the header.
+;;
+;; Scope is modules/*.el, the owned active module set. Vendored (custom/),
+;; generated (themes/, browser-choice.el), archived (archive/), and private
+;; (*.local.el) files are out of scope by design -- classifying those is the
+;; file-class policy task, not this test. The checker reads files on disk
+;; without loading them, so it adds no startup or package dependency.
+
+;;; Code:
+
+(require 'ert)
+
+(defconst test-pkg-header--exempt '()
+ "Basenames under modules/ exempt from the package-header checks.
+Empty today. Add a basename with a comment when a module is intentionally
+shaped differently, so the exemption is explicit rather than silent.")
+
+(defun test-pkg-header--check (name text)
+ "Return the list of violation symbols for module NAME given file TEXT.
+NAME is the basename (e.g. \"font-config.el\"). An empty list means the
+file is conformant. Possible symbols: `bom', `header', `markers',
+`order', `provide'."
+ (let ((violations '()))
+ (when (string-prefix-p "" text)
+ (push 'bom violations))
+ (let ((first-line (car (split-string text "\n"))))
+ (unless (string-match-p
+ (concat "\\`;;; " (regexp-quote name) " --- .+-\\*-.*-\\*-")
+ first-line)
+ (push 'header violations)))
+ (let ((commentary (string-match "^;;; Commentary:" text))
+ (code (string-match "^;;; Code:" text)))
+ (cond ((or (null commentary) (null code)) (push 'markers violations))
+ ((>= commentary code) (push 'order violations))))
+ (let ((stem (file-name-sans-extension name)))
+ (unless (string-match-p (concat "^(provide '" (regexp-quote stem) ")") text)
+ (push 'provide violations)))
+ (nreverse violations)))
+
+(ert-deftest test-pkg-header-checker-flags-malformed ()
+ "Error: the checker catches each malformed shape."
+ (should (memq 'bom
+ (test-pkg-header--check
+ "foo.el"
+ ";;; foo.el --- x -*- lexical-binding: t; -*-\n;;; Commentary:\n;;; Code:\n(provide 'foo)")))
+ (should (memq 'header
+ (test-pkg-header--check
+ "foo.el"
+ ";;; foo --- x -*- lexical-binding: t; -*-\n;;; Commentary:\n;;; Code:\n(provide 'foo)")))
+ (should (memq 'order
+ (test-pkg-header--check
+ "foo.el"
+ ";;; foo.el --- x -*- lexical-binding: t; -*-\n;;; Code:\n;;; Commentary:\n(provide 'foo)")))
+ (should (memq 'provide
+ (test-pkg-header--check
+ "foo.el"
+ ";;; foo.el --- x -*- lexical-binding: t; -*-\n;;; Commentary:\n;;; Code:\n"))))
+
+(ert-deftest test-pkg-header-checker-passes-conformant ()
+ "Normal: a well-formed module yields no violations."
+ (should-not (test-pkg-header--check
+ "foo.el"
+ ";;; foo.el --- A thing -*- lexical-binding: t; -*-\n;;; Commentary:\n;; doc\n;;; Code:\n(provide 'foo)\n")))
+
+(ert-deftest test-pkg-header-checker-boundary-empty ()
+ "Boundary: empty file text reports every applicable violation, no crash."
+ (let ((v (test-pkg-header--check "foo.el" "")))
+ (should (memq 'header v))
+ (should (memq 'markers v))
+ (should (memq 'provide v))))
+
+(ert-deftest test-pkg-header-all-modules-conform ()
+ "Normal: every modules/*.el passes the package-header checks."
+ (let ((dir (expand-file-name "modules" user-emacs-directory))
+ (bad '()))
+ (dolist (file (directory-files dir t "\\.el\\'"))
+ (let ((name (file-name-nondirectory file)))
+ (unless (member name test-pkg-header--exempt)
+ (let* ((text (with-temp-buffer
+ (insert-file-contents file)
+ (buffer-string)))
+ (violations (test-pkg-header--check name text)))
+ (when violations (push (cons name violations) bad))))))
+ (should-not bad)))
+
+(provide 'test-meta-package-headers)
+;;; test-meta-package-headers.el ends here