summaryrefslogtreecommitdiff
path: root/tests/test-coverage-core--changed-lines.el
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-05-03 20:11:49 -0500
committerCraig Jennings <c@cjennings.net>2026-05-03 20:11:49 -0500
commit570b91b74ac981d392f1724d6011d0558b7150c2 (patch)
treecc0f1a97309e5a4ce798f2fee974dcf749640900 /tests/test-coverage-core--changed-lines.el
parentf3d50d1b2aea4f1eda436c54a15cc18596f2df0b (diff)
downloaddotemacs-570b91b74ac981d392f1724d6011d0558b7150c2.tar.gz
dotemacs-570b91b74ac981d392f1724d6011d0558b7150c2.zip
refactor: invoke git via argv in coverage diff helpers
`coverage-core.el` was running git through `shell-command-to-string`, which has two practical problems for central tooling: shell parsing surfaces (especially the `$(git merge-base ...)` substitution), and silent failure modes when git exits non-zero (the bad output just becomes empty parse results). I extracted three small helpers. `cj/--coverage-git-string` runs git via `process-file` against a temp buffer and signals `user-error` on non-zero exit, with the argv, status, and trimmed output included. `cj/--coverage-git-merge-base` does its own `git merge-base HEAD <base>` invocation. `cj/--coverage-git-diff` is the diff wrapper that always appends `--unified=0`. `cj/--coverage-changed-lines` now uses `pcase` over the scope symbol and composes the helpers. Branch-vs-main and branch-vs-parent compute the merge-base in a separate call before running `git diff <merge-base>..HEAD`, with no shell substitution involved. One behavior change is worth flagging. A git failure used to disappear into an empty hash table. It now signals a `user-error` with the failing command, exit status, and git's stderr output. Tests: I added two argv-boundary cases (working-tree and branch-vs-parent both assert the exact argv list seen) plus a non-zero-exit case that asserts the user-error path. The existing `test-coverage-core--command.el` smoke test gets its `shell-command-to-string` stub upgraded to a `process-file` stub.
Diffstat (limited to 'tests/test-coverage-core--changed-lines.el')
-rw-r--r--tests/test-coverage-core--changed-lines.el56
1 files changed, 47 insertions, 9 deletions
diff --git a/tests/test-coverage-core--changed-lines.el b/tests/test-coverage-core--changed-lines.el
index dcf37603..f271fde1 100644
--- a/tests/test-coverage-core--changed-lines.el
+++ b/tests/test-coverage-core--changed-lines.el
@@ -3,7 +3,7 @@
;;; Commentary:
;; Unit tests for:
;; `cj/--coverage-parse-diff-output' (pure parser over git-diff text)
-;; `cj/--coverage-changed-lines' (scope → hash table, shells to git)
+;; `cj/--coverage-changed-lines' (scope → hash table, invokes git by argv)
;;
;; The parser takes the output of `git diff --unified=0' and returns
;; a hash table of file → set of changed (added) line numbers in the
@@ -173,16 +173,54 @@ Binary files a/image.png and b/image.png differ
(should (gethash 10 lines))
(should (gethash 11 lines))))
-;;; Smoke test — changed-lines (stubbed git invocation)
+;;; Smoke tests — changed-lines (stubbed git invocation)
(ert-deftest test-coverage-changed-lines-working-tree-stubbed ()
- "Smoke: scope dispatches, shell is stubbed, parser is applied to the result."
- (cl-letf (((symbol-function 'shell-command-to-string)
- (lambda (_cmd) test-coverage-diff--simple-single-file)))
- (let* ((result (cj/--coverage-changed-lines 'working-tree))
- (lines (gethash "foo.el" result)))
- (should (= 1 (hash-table-count result)))
- (should (= 3 (hash-table-count lines))))))
+ "Smoke: working-tree scope invokes git diff via argv and parses the result."
+ (let (seen-calls)
+ (cl-letf (((symbol-function 'process-file)
+ (lambda (program _infile destination _display &rest args)
+ (push (cons program args) seen-calls)
+ (with-current-buffer destination
+ (insert test-coverage-diff--simple-single-file))
+ 0)))
+ (let* ((result (cj/--coverage-changed-lines 'working-tree))
+ (lines (gethash "foo.el" result)))
+ (should (equal (nreverse seen-calls)
+ '(("git" "diff" "HEAD" "--unified=0"))))
+ (should (= 1 (hash-table-count result)))
+ (should (= 3 (hash-table-count lines)))))))
+
+(ert-deftest test-coverage-changed-lines-branch-vs-parent-computes-merge-base ()
+ "Branch scopes should compute merge-base separately before diffing."
+ (let (seen-calls)
+ (cl-letf (((symbol-function 'process-file)
+ (lambda (program _infile destination _display &rest args)
+ (push (cons program args) seen-calls)
+ (with-current-buffer destination
+ (insert
+ (pcase args
+ (`("merge-base" "HEAD" "feature/base") "abc123\n")
+ (`("diff" "abc123..HEAD" "--unified=0")
+ test-coverage-diff--simple-single-file)
+ (_ ""))))
+ 0)))
+ (let* ((result (cj/--coverage-changed-lines 'branch-vs-parent "feature/base"))
+ (lines (gethash "foo.el" result)))
+ (should (equal (nreverse seen-calls)
+ '(("git" "merge-base" "HEAD" "feature/base")
+ ("git" "diff" "abc123..HEAD" "--unified=0"))))
+ (should (= 3 (hash-table-count lines)))))))
+
+(ert-deftest test-coverage-changed-lines-git-failure-errors-clearly ()
+ "Git failures should surface as user-error messages."
+ (cl-letf (((symbol-function 'process-file)
+ (lambda (_program _infile destination _display &rest _args)
+ (with-current-buffer destination
+ (insert "fatal: not a git repository\n"))
+ 128)))
+ (should-error (cj/--coverage-changed-lines 'working-tree)
+ :type 'user-error)))
(ert-deftest test-coverage-changed-lines-unknown-scope-errors ()
"Error: an unknown scope symbol signals user-error."