summaryrefslogtreecommitdiff
path: root/tests/test-video-audio-recording--get-sink-apps.el
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-02-26 22:41:46 -0600
committerCraig Jennings <c@cjennings.net>2026-02-26 22:41:46 -0600
commita46f8af939b112b603a2c95b2e83a1932b208e20 (patch)
treec976afe867bfe7e57a3f425b911e2f654d913168 /tests/test-video-audio-recording--get-sink-apps.el
parent8de47fd766fe57c7f89960ee110b209a946024cb (diff)
test(recording): add tests for label-devices, label-sinks, get-sink-apps
Fill test gaps from quality-engineer review: 3 new test files (28 tests) and error cases for get-available-mics and get-available-sinks (+4 tests).
Diffstat (limited to 'tests/test-video-audio-recording--get-sink-apps.el')
-rw-r--r--tests/test-video-audio-recording--get-sink-apps.el118
1 files changed, 118 insertions, 0 deletions
diff --git a/tests/test-video-audio-recording--get-sink-apps.el b/tests/test-video-audio-recording--get-sink-apps.el
new file mode 100644
index 00000000..8712fa97
--- /dev/null
+++ b/tests/test-video-audio-recording--get-sink-apps.el
@@ -0,0 +1,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