aboutsummaryrefslogtreecommitdiff
path: root/modules/coverage-core.el
blob: 4a3112ce8973042cff8accef0eca84a563bace64 (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
;;; coverage-core.el --- Coverage reporting engine and backend registry -*- lexical-binding: t; coding: utf-8; -*-
;; author: Craig Jennings <c@cjennings.net>

;;; Commentary:
;; Language-agnostic core for diff-aware coverage reporting.
;;
;; Reads an LCOV file, shells to git diff at a selectable scope,
;; intersects the results, and displays a report buffer.  Languages
;; plug in via the backend registry (see `cj/coverage-backends').
;;
;; See docs/design/coverage.org for the design rationale.

;;; Code:

(defun cj/--coverage-parse-lcov (file)
  "Parse FILE as LCOV and return a hash table of covered lines.
Keys are source-file paths (strings).  Values are hash tables whose
keys are line numbers (integers) that had a hit count greater than
zero.  Only the SF, DA, and end_of_record fields are read; other
LCOV fields are ignored.  Malformed DA lines are skipped silently.
Signals `user-error' if FILE does not exist."
  (unless (file-exists-p file)
	(user-error "LCOV file not found: %s" file))
  (let ((result (make-hash-table :test 'equal))
		(current-file nil)
		(current-lines nil))
	(with-temp-buffer
	  (insert-file-contents file)
	  (goto-char (point-min))
	  (while (not (eobp))
		(let ((line (buffer-substring-no-properties
					 (line-beginning-position) (line-end-position))))
		  (cond
		   ((string-prefix-p "SF:" line)
			(setq current-file (substring line 3))
			(setq current-lines (make-hash-table :test 'eql)))
		   ((string-prefix-p "DA:" line)
			(when current-lines
			  (let* ((rest (substring line 3))
					 (parts (split-string rest ","))
					 (line-str (car parts))
					 (hits-str (cadr parts))
					 (line-num (and line-str (string-match-p "\\`[0-9]+\\'" line-str)
									(string-to-number line-str)))
					 (hits (and hits-str (string-match-p "\\`[0-9]+\\'" hits-str)
								(string-to-number hits-str))))
				(when (and line-num hits (> hits 0))
				  (puthash line-num t current-lines)))))
		   ((string= line "end_of_record")
			(when current-file
			  (puthash current-file current-lines result))
			(setq current-file nil
				  current-lines nil))))
		(forward-line 1)))
	result))

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