aboutsummaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/test-coverage-core--whole-project.el137
1 files changed, 137 insertions, 0 deletions
diff --git a/tests/test-coverage-core--whole-project.el b/tests/test-coverage-core--whole-project.el
new file mode 100644
index 00000000..946ff304
--- /dev/null
+++ b/tests/test-coverage-core--whole-project.el
@@ -0,0 +1,137 @@
+;;; test-coverage-core--whole-project.el --- Tests for whole-project coverage scope -*- lexical-binding: t; -*-
+
+;;; Commentary:
+;; Unit tests for the helpers that power the "Whole project" scope in
+;; `cj/coverage-report':
+;;
+;; `cj/--coverage-simplecov-executable-lines' — returns all executable
+;; lines per file from a simplecov JSON report, including lines with
+;; zero hits (symmetric with `cj/--coverage-parse-simplecov', which
+;; returns only hit lines).
+;;
+;; `cj/--coverage-format-summary' — renders intersect records as a
+;; per-file percentage summary, sorted by coverage ascending (worst
+;; first, most useful when scanning for what to test next).
+
+;;; Code:
+
+(require 'ert)
+
+(add-to-list 'load-path (expand-file-name "modules" user-emacs-directory))
+(require 'coverage-core)
+
+(defun test-coverage-whole--write-json (content)
+ "Write CONTENT to a temp file; return its path."
+ (let ((file (make-temp-file "test-whole-" nil ".json")))
+ (with-temp-file file (insert content))
+ file))
+
+(defun test-coverage-whole--record (path changed covered uncovered tracked)
+ "Build an intersect record."
+ (list :path path
+ :changed-lines changed
+ :covered-lines covered
+ :uncovered-lines uncovered
+ :tracked tracked))
+
+;;; cj/--coverage-simplecov-executable-lines
+
+(ert-deftest test-coverage-simplecov-executable-lines-basic ()
+ "Normal: executable lines include both hit (>0) and 0-hit entries."
+ (let* ((content "{\"run\":{\"timestamp\":1,\"coverage\":{\"/foo.el\":[null,1,0,2,null,0]}}}")
+ (file (test-coverage-whole--write-json content)))
+ (unwind-protect
+ (let* ((result (cj/--coverage-simplecov-executable-lines file))
+ (lines (gethash "/foo.el" result)))
+ (should (= 4 (hash-table-count lines)))
+ (should-not (gethash 1 lines)) ; null
+ (should (gethash 2 lines)) ; 1 hit
+ (should (gethash 3 lines)) ; 0 hits (still executable)
+ (should (gethash 4 lines)) ; 2 hits
+ (should-not (gethash 5 lines)) ; null
+ (should (gethash 6 lines))) ; 0 hits
+ (delete-file file))))
+
+(ert-deftest test-coverage-simplecov-executable-lines-all-null ()
+ "Boundary: all-null array returns empty set (not nil)."
+ (let* ((content "{\"run\":{\"coverage\":{\"/foo.el\":[null,null,null]}}}")
+ (file (test-coverage-whole--write-json content)))
+ (unwind-protect
+ (let* ((result (cj/--coverage-simplecov-executable-lines file))
+ (lines (gethash "/foo.el" result)))
+ (should (hash-table-p lines))
+ (should (= 0 (hash-table-count lines))))
+ (delete-file file))))
+
+(ert-deftest test-coverage-simplecov-executable-lines-multiple-runs-unioned ()
+ "Boundary: multiple test-name keys are unioned (matches parse-simplecov semantics)."
+ (let* ((content (concat "{\"run1\":{\"coverage\":{\"/foo.el\":[1,null,null]}},"
+ "\"run2\":{\"coverage\":{\"/foo.el\":[null,null,0]}}}"))
+ (file (test-coverage-whole--write-json content)))
+ (unwind-protect
+ (let* ((result (cj/--coverage-simplecov-executable-lines file))
+ (lines (gethash "/foo.el" result)))
+ (should (= 2 (hash-table-count lines)))
+ (should (gethash 1 lines))
+ (should (gethash 3 lines)))
+ (delete-file file))))
+
+(ert-deftest test-coverage-simplecov-executable-lines-missing-file ()
+ "Error: nonexistent file signals user-error."
+ (should-error (cj/--coverage-simplecov-executable-lines
+ "/nonexistent/path/xyz.json")
+ :type 'user-error))
+
+;;; cj/--coverage-format-summary
+
+(ert-deftest test-coverage-format-summary-multiple-files-sorted ()
+ "Normal: per-file summary is sorted by coverage percentage ascending."
+ (let* ((records (list
+ (test-coverage-whole--record
+ "high.el" '(1 2 3 4) '(1 2 3 4) nil t) ; 100%
+ (test-coverage-whole--record
+ "low.el" '(1 2 3 4 5 6 7 8 9 10)
+ '(1) '(2 3 4 5 6 7 8 9 10) t) ; 10%
+ (test-coverage-whole--record
+ "mid.el" '(1 2) '(1) '(2) t))) ; 50%
+ (output (cj/--coverage-format-summary records "Whole project"))
+ ;; Position of each filename in the output string
+ (pos-low (string-match "low\\.el" output))
+ (pos-mid (string-match "mid\\.el" output))
+ (pos-high (string-match "high\\.el" output)))
+ (should pos-low)
+ (should pos-mid)
+ (should pos-high)
+ ;; Lower coverage files appear first
+ (should (< pos-low pos-mid))
+ (should (< pos-mid pos-high))
+ ;; Summary totals appear
+ (should (string-match-p "6 of 16" output))
+ (should (string-match-p "Whole project" output))))
+
+(ert-deftest test-coverage-format-summary-shows-percentages ()
+ "Normal: each file shows its own hit/total and percentage."
+ (let* ((records (list (test-coverage-whole--record
+ "foo.el" '(1 2 3 4) '(1 2 3) '(4) t))) ; 75%
+ (output (cj/--coverage-format-summary records "Whole project")))
+ (should (string-match-p "3/4" output))
+ (should (string-match-p "75" output))))
+
+(ert-deftest test-coverage-format-summary-empty ()
+ "Boundary: empty records produces a clear \"nothing to report\" message."
+ (let ((output (cj/--coverage-format-summary nil "Whole project")))
+ (should (string-match-p "No coverage data" output))))
+
+(ert-deftest test-coverage-format-summary-not-tracked-files-excluded ()
+ "Boundary: files with :tracked nil don't appear in the summary."
+ (let* ((records (list
+ (test-coverage-whole--record
+ "modules/foo.el" '(1) '(1) nil t)
+ (test-coverage-whole--record
+ "README.md" '(5 6) nil nil nil)))
+ (output (cj/--coverage-format-summary records "Whole project")))
+ (should (string-match-p "modules/foo\\.el" output))
+ (should-not (string-match-p "README\\.md" output))))
+
+(provide 'test-coverage-core--whole-project)
+;;; test-coverage-core--whole-project.el ends here