From ecca6c5809aa2945d593baae10308c0dcfe6ec17 Mon Sep 17 00:00:00 2001 From: Craig Jennings Date: Wed, 22 Apr 2026 17:17:05 -0500 Subject: feat(coverage): add elisp backend First of the pluggable coverage backends. Registers itself with coverage-core on load. - :name is elisp - :detect returns non-nil when the project root has a Makefile, Eask, or Cask alongside .el files at root or under modules/. The heuristic is deliberately loose. For anything unusual, .dir-locals.el can pin the backend with cj/coverage-backend. - :run invokes make coverage in a compilation buffer. On success the callback fires with the LCOV path. On failure the buffer stays visible so the user can read the error. - :lcov-path resolves to /.coverage/lcov.info. undercover is declared via use-package with :defer t so it's installed but not loaded at Emacs startup. The make coverage target will require it explicitly. Tests cover Normal (Makefile + modules/, Eask + root .el, Cask + modules/), Boundary (no build file, Makefile without .el, empty directory), and Error (nonexistent root returns nil). The registration-on-load case is also verified. The Makefile coverage target and the cj/coverage-report user command arrive in follow-up commits. --- tests/test-coverage-elisp--detect.el | 99 ++++++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 tests/test-coverage-elisp--detect.el (limited to 'tests/test-coverage-elisp--detect.el') diff --git a/tests/test-coverage-elisp--detect.el b/tests/test-coverage-elisp--detect.el new file mode 100644 index 00000000..1301495a --- /dev/null +++ b/tests/test-coverage-elisp--detect.el @@ -0,0 +1,99 @@ +;;; test-coverage-elisp--detect.el --- Tests for cj/--coverage-elisp-detect -*- lexical-binding: t; -*- + +;;; Commentary: +;; Unit tests for `cj/--coverage-elisp-detect', the heuristic that +;; decides whether a given project root is an Elisp project. +;; +;; Heuristic: requires BOTH a Makefile/Eask/Cask AND some .el files +;; (at root or under modules/). + +;;; Code: + +(require 'ert) + +(add-to-list 'load-path (expand-file-name "modules" user-emacs-directory)) +(require 'coverage-elisp) + +(defun test-coverage-elisp-detect--make-project (spec) + "Create a temp directory matching SPEC. +SPEC is a list of relative paths to create. Paths ending in `/' are +directories; others are files. Returns the temp directory path." + (let ((root (make-temp-file "test-elisp-project-" t))) + (dolist (path spec) + (let ((full (expand-file-name path root))) + (if (string-suffix-p "/" path) + (make-directory full t) + (make-directory (file-name-directory full) t) + (write-region "" nil full)))) + root)) + +;;; Normal cases + +(ert-deftest test-coverage-elisp-detect-with-makefile-and-modules () + "Normal: Makefile plus modules/foo.el is detected as elisp." + (let ((root (test-coverage-elisp-detect--make-project + '("Makefile" "modules/foo.el")))) + (unwind-protect + (should (cj/--coverage-elisp-detect root)) + (delete-directory root t)))) + +(ert-deftest test-coverage-elisp-detect-with-eask-and-root-el () + "Normal: Eask plus root-level .el file is detected." + (let ((root (test-coverage-elisp-detect--make-project + '("Eask" "main.el")))) + (unwind-protect + (should (cj/--coverage-elisp-detect root)) + (delete-directory root t)))) + +(ert-deftest test-coverage-elisp-detect-with-cask-and-modules () + "Normal: Cask plus modules/ directory is detected." + (let ((root (test-coverage-elisp-detect--make-project + '("Cask" "modules/bar.el")))) + (unwind-protect + (should (cj/--coverage-elisp-detect root)) + (delete-directory root t)))) + +;;; Boundary cases + +(ert-deftest test-coverage-elisp-detect-no-build-file () + "Boundary: .el files without a Makefile/Eask/Cask is NOT detected." + (let ((root (test-coverage-elisp-detect--make-project + '("main.el" "other.el")))) + (unwind-protect + (should-not (cj/--coverage-elisp-detect root)) + (delete-directory root t)))) + +(ert-deftest test-coverage-elisp-detect-makefile-without-el () + "Boundary: Makefile with no .el files is NOT detected." + (let ((root (test-coverage-elisp-detect--make-project + '("Makefile" "README.md")))) + (unwind-protect + (should-not (cj/--coverage-elisp-detect root)) + (delete-directory root t)))) + +(ert-deftest test-coverage-elisp-detect-empty-directory () + "Boundary: an empty directory is not an elisp project." + (let ((root (make-temp-file "test-empty-" t))) + (unwind-protect + (should-not (cj/--coverage-elisp-detect root)) + (delete-directory root t)))) + +;;; Error cases + +(ert-deftest test-coverage-elisp-detect-nonexistent-root () + "Error: a nonexistent ROOT returns nil, not an error." + (should-not (cj/--coverage-elisp-detect "/nonexistent/path/for-test-12345"))) + +;;; Registry integration + +(ert-deftest test-coverage-elisp-registered-on-load () + "Normal: loading coverage-elisp registers the `elisp' backend." + (let ((backend (cj/--coverage-backend-by-name 'elisp))) + (should backend) + (should (eq 'elisp (plist-get backend :name))) + (should (functionp (plist-get backend :detect))) + (should (functionp (plist-get backend :run))) + (should (functionp (plist-get backend :lcov-path))))) + +(provide 'test-coverage-elisp--detect) +;;; test-coverage-elisp--detect.el ends here -- cgit v1.2.3