aboutsummaryrefslogtreecommitdiff
path: root/tests/test-system-lib-process-output-or-error.el
blob: 26b70abb01408fdcd869c295ae4caa360f110097 (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
;;; test-system-lib-process-output-or-error.el --- Tests for cj/process-output-or-error and cj/git-output-or-error -*- lexical-binding: t; -*-

;;; Commentary:
;; `cj/process-output-or-error' is the generic argv-based runner: take
;; a program name and its argument list, run it via `process-file', and
;; return stdout on success or signal a clear `user-error' on non-zero
;; exit (with program/status/output in the message).
;;
;; `cj/git-output-or-error' is the thin wrapper that supplies "git" as
;; the program.  Both helpers stay deterministic in tests by stubbing
;; `process-file'.

;;; Code:

(require 'ert)
(require 'cl-lib)

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

(defmacro test-system-lib-runner--with-stub (status output &rest body)
  "Run BODY with `process-file' stubbed to return STATUS and write OUTPUT."
  (declare (indent 2))
  `(cl-letf (((symbol-function 'process-file)
              (lambda (_program _infile destination _display &rest _args)
                (when destination
                  (let ((buf (cond
                              ((eq destination t) (current-buffer))
                              ((bufferp destination) destination)
                              ((consp destination) (car destination)))))
                    (when (bufferp buf)
                      (with-current-buffer buf
                        (insert ,output)))))
                ,status)))
     ,@body))

(ert-deftest test-cj-process-output-or-error-success-returns-stdout ()
  "Normal: zero exit returns stdout."
  (test-system-lib-runner--with-stub 0 "hello world\n"
    (should (equal (cj/process-output-or-error "echo" "hello" "world")
                   "hello world\n"))))

(ert-deftest test-cj-process-output-or-error-no-args-runs ()
  "Boundary: no args is a valid invocation."
  (test-system-lib-runner--with-stub 0 "ok\n"
    (should (equal (cj/process-output-or-error "true") "ok\n"))))

(ert-deftest test-cj-process-output-or-error-non-zero-signals-user-error ()
  "Error: non-zero exit signals user-error."
  (test-system-lib-runner--with-stub 1 "boom\n"
    (should-error (cj/process-output-or-error "false")
                  :type 'user-error)))

(ert-deftest test-cj-process-output-or-error-message-names-program-and-status ()
  "Error: the user-error message names the program, the exit status,
and the (trimmed) output so the user can see what went wrong."
  (test-system-lib-runner--with-stub 128 "fatal: not a git repo\n"
    (condition-case err
        (cj/process-output-or-error "git" "status")
      (user-error
       (let ((message (error-message-string err)))
         (should (string-match-p "git" message))
         (should (string-match-p "128" message))
         (should (string-match-p "fatal: not a git repo" message)))))))

(ert-deftest test-cj-git-output-or-error-uses-git-program ()
  "Normal: git wrapper passes its args through with `git' as the program."
  (let (captured-program captured-args)
    (cl-letf (((symbol-function 'process-file)
               (lambda (program _infile destination _display &rest args)
                 (setq captured-program program
                       captured-args args)
                 (when destination
                   (let ((buf (cond ((eq destination t) (current-buffer))
                                    ((bufferp destination) destination)
                                    ((consp destination) (car destination)))))
                     (when (bufferp buf)
                       (with-current-buffer buf (insert "abcdef\n")))))
                 0)))
      (should (equal (cj/git-output-or-error "rev-parse" "HEAD") "abcdef\n"))
      (should (equal captured-program "git"))
      (should (equal captured-args '("rev-parse" "HEAD"))))))

(ert-deftest test-cj-git-output-or-error-non-zero-signals-user-error ()
  "Error: git wrapper raises the same user-error shape on failure."
  (test-system-lib-runner--with-stub 1 "fatal: bad ref\n"
    (should-error (cj/git-output-or-error "rev-parse" "bogus")
                  :type 'user-error)))

(provide 'test-system-lib-process-output-or-error)
;;; test-system-lib-process-output-or-error.el ends here