From 4d8f979948d5349404a36fe335eb77955d068a8d Mon Sep 17 00:00:00 2001 From: Craig Jennings Date: Fri, 15 May 2026 02:13:10 -0500 Subject: feat(coverage): report modules missing from SimpleCov + project-module score =make coverage= used to print a line-weighted percentage that only saw files SimpleCov instrumented. 104 modules existed on disk but only 49 appeared in =.coverage/simplecov.json=, so the headline number was flattering: untouched modules counted for nothing. The summary script now adds two things on top of the existing report: - A =Not in SimpleCov report= section listing modules present under =modules/*.el= but absent from the SimpleCov output. Missing-module detection is exactly direct =modules/*.el=; subdirectories and =.elc= files are ignored. - A =Project module coverage= line that is module-weighted across every direct =modules/*.el= file. Tracked modules contribute their per-file coverage percentage; missing modules contribute 0%. The original line-weighted SimpleCov percentage stays as the =instrumented coverage= number. The new module-weighted score is the honest project-level reading: missing modules count as 0% without inventing a fake executable-line denominator for them. Tests assert the missing-module section, the new percentage, and the ignore rules for .elc / nested files. --- tests/test-coverage-summary.el | 91 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 79 insertions(+), 12 deletions(-) (limited to 'tests') diff --git a/tests/test-coverage-summary.el b/tests/test-coverage-summary.el index 01c9efa0..2dc9d517 100644 --- a/tests/test-coverage-summary.el +++ b/tests/test-coverage-summary.el @@ -18,6 +18,12 @@ (insert content)) file)) +(defun test-coverage-summary--touch (file) + "Create FILE and its parent directory." + (make-directory (file-name-directory file) t) + (with-temp-file file + (insert ";;; fixture\n"))) + (ert-deftest test-coverage-summary-modules-only () "Normal: summary includes modules files and ignores non-module files." (let* ((root (file-name-as-directory (make-temp-file "coverage-root-" t))) @@ -29,14 +35,20 @@ module-a module-b other)) (file (test-coverage-summary--write-json content))) (unwind-protect - (let ((output (cj/coverage-summary-text - file - (expand-file-name "modules" root) - root))) + (progn + (test-coverage-summary--touch module-a) + (test-coverage-summary--touch module-b) + (test-coverage-summary--touch other) + (let ((output (cj/coverage-summary-text + file + (expand-file-name "modules" root) + root))) (should (string-match-p "modules/a\\.el" output)) (should (string-match-p "modules/b\\.el" output)) (should-not (string-match-p "tests/test-a\\.el" output)) - (should (string-match-p "2 of 5" output))) + (should (string-match-p "2 of 5" output)) + (should (string-match-p "Project module coverage: 33\\.3%" output)) + (should (string-match-p "Not in SimpleCov report: 0 modules" output)))) (delete-file file) (delete-directory root t)))) @@ -50,17 +62,72 @@ low high)) (file (test-coverage-summary--write-json content))) (unwind-protect - (let* ((output (cj/coverage-summary-text - file - (expand-file-name "modules" root) - root)) - (pos-low (string-match "modules/low\\.el" output)) - (pos-high (string-match "modules/high\\.el" output))) + (progn + (test-coverage-summary--touch low) + (test-coverage-summary--touch high) + (let* ((output (cj/coverage-summary-text + file + (expand-file-name "modules" root) + root)) + (pos-low (string-match "modules/low\\.el" output)) + (pos-high (string-match "modules/high\\.el" output))) (should pos-low) (should pos-high) - (should (< pos-low pos-high))) + (should (< pos-low pos-high)))) (delete-file file) (delete-directory root t)))) +(ert-deftest test-coverage-summary-lists-modules-missing-from-simplecov () + "Normal: modules on disk but absent from SimpleCov are listed separately." + (let* ((root (file-name-as-directory (make-temp-file "coverage-root-" t))) + (tracked (expand-file-name "modules/tracked.el" root)) + (missing-a (expand-file-name "modules/missing-a.el" root)) + (missing-b (expand-file-name "modules/missing-b.el" root)) + (content (format + "{\"run\":{\"coverage\":{\"%s\":[1,0,null,1]}}}" + tracked)) + (file (test-coverage-summary--write-json content))) + (unwind-protect + (progn + (test-coverage-summary--touch tracked) + (test-coverage-summary--touch missing-a) + (test-coverage-summary--touch missing-b) + (let ((output (cj/coverage-summary-text + file + (expand-file-name "modules" root) + root))) + (should (string-match-p "Total: 2 of 3 lines covered" output)) + (should (string-match-p + "Project module coverage: 22\\.2% (1 tracked, 2 missing, 3 total; missing modules count as 0%)" + output)) + (should (string-match-p "Not in SimpleCov report: 2 modules" output)) + (should (string-match-p "modules/missing-a\\.el" output)) + (should (string-match-p "modules/missing-b\\.el" output)) + (should (string-match-p "count as 0% in project module coverage" output)))) + (delete-file file) + (delete-directory root t)))) + +(ert-deftest test-coverage-summary-missing-module-helper-ignores-elc-and-subdirs () + "Boundary: untracked module detection is exactly direct modules/*.el files." + (let* ((root (file-name-as-directory (make-temp-file "coverage-root-" t))) + (module-dir (expand-file-name "modules" root)) + (tracked-file (expand-file-name "modules/tracked.el" root)) + (missing-file (expand-file-name "modules/missing.el" root)) + (compiled-file (expand-file-name "modules/compiled.elc" root)) + (nested-file (expand-file-name "modules/nested/not-a-module.el" root)) + (tracked-table (make-hash-table :test 'equal))) + (unwind-protect + (progn + (test-coverage-summary--touch tracked-file) + (test-coverage-summary--touch missing-file) + (make-directory (file-name-directory compiled-file) t) + (with-temp-file compiled-file (insert "compiled")) + (test-coverage-summary--touch nested-file) + (puthash "modules/tracked.el" t tracked-table) + (should (equal (cj/coverage-summary--missing-module-files + tracked-table module-dir root) + '("modules/missing.el")))) + (delete-directory root t)))) + (provide 'test-coverage-summary) ;;; test-coverage-summary.el ends here -- cgit v1.2.3