aboutsummaryrefslogtreecommitdiff
path: root/tests/test-meta-package-headers.el
blob: f9b57cbfc856ea156f168170b8e06e0a8415c8d3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
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