aboutsummaryrefslogtreecommitdiff
path: root/tests/test-coverage-core--parse-simplecov.el
blob: 09a3051e967477a6d8bb0552705b02543075f6bd (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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
;;; test-coverage-core--parse-simplecov.el --- Tests for cj/--coverage-parse-simplecov -*- lexical-binding: t; -*-

;;; Commentary:
;; Unit tests for `cj/--coverage-parse-simplecov', the pure helper
;; that reads a simplecov JSON report and returns a hash table of
;; file → set of covered line numbers.
;;
;; Simplecov JSON structure:
;;   { <test-name>: { "coverage": { <path>: [null | 0 | int, ...] } } }
;;
;; Array index i (0-based) corresponds to line (i+1).
;;   nil          — line is not executable (blank, comment)
;;   0            — line is executable but not hit
;;   positive int — hit count
;;
;; When the JSON has multiple top-level test-name keys, coverage is
;; unioned across them.

;;; Code:

(require 'ert)

(add-to-list 'load-path (expand-file-name "modules" user-emacs-directory))
(require 'coverage-core)

(defun test-coverage-parse-simplecov--write-temp-json (content)
  "Write CONTENT (a JSON string) to a temp file; return its path."
  (let ((file (make-temp-file "test-simplecov-" nil ".json")))
	(with-temp-file file
	  (insert content))
	file))

;;; Normal cases

(ert-deftest test-coverage-parse-simplecov-single-file-all-hit ()
  "Normal: one file with every executable line hit."
  (let* ((content "{\"run\":{\"timestamp\":1,\"coverage\":{\"/foo.el\":[null,1,2,3]}}}")
		 (file (test-coverage-parse-simplecov--write-temp-json content)))
	(unwind-protect
		(let* ((result (cj/--coverage-parse-simplecov file))
			   (lines (gethash "/foo.el" result)))
		  (should (hash-table-p result))
		  (should (= 3 (hash-table-count lines)))
		  (should (gethash 2 lines))
		  (should (gethash 3 lines))
		  (should (gethash 4 lines))
		  (should-not (gethash 1 lines)))
	  (delete-file file))))

(ert-deftest test-coverage-parse-simplecov-multiple-files ()
  "Normal: multiple files under one test-name are both parsed."
  (let* ((content "{\"run\":{\"timestamp\":1,\"coverage\":{\"/a.el\":[1,1],\"/b.el\":[null,5]}}}")
		 (file (test-coverage-parse-simplecov--write-temp-json content)))
	(unwind-protect
		(let ((result (cj/--coverage-parse-simplecov file)))
		  (should (= 2 (hash-table-count result)))
		  (should (gethash "/a.el" result))
		  (should (gethash "/b.el" result))
		  (should (= 2 (hash-table-count (gethash "/a.el" result))))
		  (should (= 1 (hash-table-count (gethash "/b.el" result)))))
	  (delete-file file))))

(ert-deftest test-coverage-parse-simplecov-mixed-hits ()
  "Normal: null (not executable) and 0 (not hit) are excluded."
  (let* ((content "{\"run\":{\"timestamp\":1,\"coverage\":{\"/foo.el\":[null,1,0,5,null,0,2]}}}")
		 (file (test-coverage-parse-simplecov--write-temp-json content)))
	(unwind-protect
		(let* ((result (cj/--coverage-parse-simplecov file))
			   (lines (gethash "/foo.el" result)))
		  (should (= 3 (hash-table-count lines)))
		  (should-not (gethash 1 lines))
		  (should (gethash 2 lines))
		  (should-not (gethash 3 lines))
		  (should (gethash 4 lines))
		  (should-not (gethash 5 lines))
		  (should-not (gethash 6 lines))
		  (should (gethash 7 lines)))
	  (delete-file file))))

;;; Boundary cases

(ert-deftest test-coverage-parse-simplecov-empty-json ()
  "Boundary: a JSON object with no test-name keys returns empty hash."
  (let* ((content "{}")
		 (file (test-coverage-parse-simplecov--write-temp-json content)))
	(unwind-protect
		(let ((result (cj/--coverage-parse-simplecov file)))
		  (should (hash-table-p result))
		  (should (= 0 (hash-table-count result))))
	  (delete-file file))))

(ert-deftest test-coverage-parse-simplecov-file-with-spaces-in-path ()
  "Boundary: filename with spaces is parsed as one key."
  (let* ((content "{\"run\":{\"timestamp\":1,\"coverage\":{\"/my path/spaces.el\":[1]}}}")
		 (file (test-coverage-parse-simplecov--write-temp-json content)))
	(unwind-protect
		(let ((result (cj/--coverage-parse-simplecov file)))
		  (should (gethash "/my path/spaces.el" result)))
	  (delete-file file))))

(ert-deftest test-coverage-parse-simplecov-all-zero-hits ()
  "Boundary: file with every executable line at 0 returns empty set."
  (let* ((content "{\"run\":{\"timestamp\":1,\"coverage\":{\"/foo.el\":[0,0,null,0]}}}")
		 (file (test-coverage-parse-simplecov--write-temp-json content)))
	(unwind-protect
		(let* ((result (cj/--coverage-parse-simplecov file))
			   (lines (gethash "/foo.el" result)))
		  (should (hash-table-p lines))
		  (should (= 0 (hash-table-count lines))))
	  (delete-file file))))

(ert-deftest test-coverage-parse-simplecov-all-null-entries ()
  "Boundary: all-null coverage array (no executable lines) returns empty set."
  (let* ((content "{\"run\":{\"timestamp\":1,\"coverage\":{\"/foo.el\":[null,null,null]}}}")
		 (file (test-coverage-parse-simplecov--write-temp-json content)))
	(unwind-protect
		(let* ((result (cj/--coverage-parse-simplecov file))
			   (lines (gethash "/foo.el" result)))
		  (should (hash-table-p lines))
		  (should (= 0 (hash-table-count lines))))
	  (delete-file file))))

(ert-deftest test-coverage-parse-simplecov-multiple-test-names-unioned ()
  "Boundary: multiple top-level test-name keys are unioned for the same file."
  (let* ((content "{\"run1\":{\"timestamp\":1,\"coverage\":{\"/foo.el\":[1,1,0,0]}},\"run2\":{\"timestamp\":2,\"coverage\":{\"/foo.el\":[0,0,1,1]}}}")
		 (file (test-coverage-parse-simplecov--write-temp-json content)))
	(unwind-protect
		(let* ((result (cj/--coverage-parse-simplecov file))
			   (lines (gethash "/foo.el" result)))
		  (should (= 4 (hash-table-count lines)))
		  (should (gethash 1 lines))
		  (should (gethash 2 lines))
		  (should (gethash 3 lines))
		  (should (gethash 4 lines)))
	  (delete-file file))))

;;; Error cases

(ert-deftest test-coverage-parse-simplecov-missing-file-errors ()
  "Error: nonexistent file signals user-error."
  (should-error (cj/--coverage-parse-simplecov "/nonexistent/path/xyz.json")
				:type 'user-error))

(ert-deftest test-coverage-parse-simplecov-malformed-json-errors ()
  "Error: malformed JSON input signals user-error naming the file."
  (let ((file (test-coverage-parse-simplecov--write-temp-json "{not valid json")))
	(unwind-protect
		(should-error (cj/--coverage-parse-simplecov file)
					  :type 'user-error)
	  (delete-file file))))

(provide 'test-coverage-core--parse-simplecov)
;;; test-coverage-core--parse-simplecov.el ends here