blob: 8712fa9713bbae4173180ddca265c0ced1fbf1e3 (
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
|
;;; test-video-audio-recording--get-sink-apps.el --- Tests for sink app discovery -*- lexical-binding: t; -*-
;;; Commentary:
;; Unit tests for cj/recording--get-sink-apps.
;; Verifies parsing of `pactl list sink-inputs' output to map
;; sink indices to application names.
;;; Code:
(require 'ert)
;; Stub dependencies before loading the module
(defvar cj/custom-keymap (make-sparse-keymap)
"Stub keymap for testing.")
(require 'video-audio-recording)
;;; Helpers
(defvar test-sink-apps--dir
(file-name-directory (or load-file-name (locate-library "test-video-audio-recording--get-sink-apps")))
"Directory containing this test file.")
(defun test-sink-apps--fixture (filename)
"Read fixture FILENAME from the fixtures directory."
(with-temp-buffer
(insert-file-contents (expand-file-name (concat "fixtures/" filename) test-sink-apps--dir))
(buffer-string)))
;;; Normal Cases
(ert-deftest test-video-audio-recording--get-sink-apps-normal-single-app ()
"Test parsing a single app on a single sink."
(cl-letf (((symbol-function 'shell-command-to-string)
(lambda (_cmd) (test-sink-apps--fixture "pactl-sink-inputs-active.txt"))))
(let ((result (cj/recording--get-sink-apps)))
(should (= 1 (length result)))
(should (equal '("Firefox") (cdr (assoc "65" result)))))))
(ert-deftest test-video-audio-recording--get-sink-apps-normal-different-sink ()
"Test that sink index is correctly parsed from different fixture."
(cl-letf (((symbol-function 'shell-command-to-string)
(lambda (_cmd) (test-sink-apps--fixture "pactl-sink-inputs-different-sink.txt"))))
(let ((result (cj/recording--get-sink-apps)))
(should (= 1 (length result)))
(should (assoc "73" result))
(should-not (assoc "65" result)))))
(ert-deftest test-video-audio-recording--get-sink-apps-normal-multiple-sinks ()
"Test parsing apps across multiple sinks."
(cl-letf (((symbol-function 'shell-command-to-string)
(lambda (_cmd)
(concat (test-sink-apps--fixture "pactl-sink-inputs-active.txt")
"\n"
(test-sink-apps--fixture "pactl-sink-inputs-different-sink.txt")))))
(let ((result (cj/recording--get-sink-apps)))
(should (= 2 (length result)))
(should (assoc "65" result))
(should (assoc "73" result)))))
(ert-deftest test-video-audio-recording--get-sink-apps-normal-multiple-apps-same-sink ()
"Test that multiple apps on the same sink are collected together."
(let ((output "Sink Input #1\n\tSink: 65\n\tProperties:\n\t\tapplication.name = \"Firefox\"\nSink Input #2\n\tSink: 65\n\tProperties:\n\t\tapplication.name = \"Spotify\"\n"))
(cl-letf (((symbol-function 'shell-command-to-string)
(lambda (_cmd) output)))
(let ((result (cj/recording--get-sink-apps)))
(should (= 1 (length result)))
(should (equal '("Firefox" "Spotify") (cdr (assoc "65" result))))))))
(ert-deftest test-video-audio-recording--get-sink-apps-normal-deduplicates ()
"Test that duplicate app names on the same sink are deduplicated."
(let ((output "Sink Input #1\n\tSink: 65\n\tProperties:\n\t\tapplication.name = \"Firefox\"\nSink Input #2\n\tSink: 65\n\tProperties:\n\t\tapplication.name = \"Firefox\"\n"))
(cl-letf (((symbol-function 'shell-command-to-string)
(lambda (_cmd) output)))
(let ((result (cj/recording--get-sink-apps)))
(should (equal '("Firefox") (cdr (assoc "65" result))))))))
;;; Boundary Cases
(ert-deftest test-video-audio-recording--get-sink-apps-boundary-empty ()
"Test that empty pactl output returns empty alist."
(cl-letf (((symbol-function 'shell-command-to-string)
(lambda (_cmd) "")))
(should (null (cj/recording--get-sink-apps)))))
(ert-deftest test-video-audio-recording--get-sink-apps-boundary-no-properties ()
"Test sink input with no properties section returns empty."
(let ((output "Sink Input #1\n\tSink: 65\n\tDriver: PipeWire\n"))
(cl-letf (((symbol-function 'shell-command-to-string)
(lambda (_cmd) output)))
(should (null (cj/recording--get-sink-apps))))))
(ert-deftest test-video-audio-recording--get-sink-apps-boundary-no-sink-line ()
"Test sink input with no Sink: line does not crash."
(let ((output "Sink Input #1\n\tDriver: PipeWire\n\tProperties:\n\t\tapplication.name = \"Firefox\"\n"))
(cl-letf (((symbol-function 'shell-command-to-string)
(lambda (_cmd) output)))
;; current-sink is nil so app name won't be stored
(should (null (cj/recording--get-sink-apps))))))
;;; Error Cases
(ert-deftest test-video-audio-recording--get-sink-apps-error-garbled-output ()
"Test that garbled output does not crash, returns empty."
(cl-letf (((symbol-function 'shell-command-to-string)
(lambda (_cmd) "random garbage\nwith\nnewlines\n")))
(should (null (cj/recording--get-sink-apps)))))
(ert-deftest test-video-audio-recording--get-sink-apps-error-missing-app-name ()
"Test sink input with application.name missing value."
(let ((output "Sink Input #1\n\tSink: 65\n\tProperties:\n\t\tapplication.name = \"\"\n"))
(cl-letf (((symbol-function 'shell-command-to-string)
(lambda (_cmd) output)))
;; Empty string between quotes won't match [^\"]+ so no app stored
(should (null (cj/recording--get-sink-apps))))))
(provide 'test-video-audio-recording--get-sink-apps)
;;; test-video-audio-recording--get-sink-apps.el ends here
|