;;; test-video-audio-recording-validate-system-audio.el --- Tests for cj/recording--validate-system-audio -*- lexical-binding: t; -*- ;;; Commentary: ;; Unit tests for cj/recording--validate-system-audio function. ;; Tests the pre-recording validation that catches stale/drifted system ;; audio devices before they cause silent recordings. ;;; Code: (require 'ert) ;; Stub dependencies before loading the module (defvar cj/custom-keymap (make-sparse-keymap) "Stub keymap for testing.") (require 'video-audio-recording) ;;; Setup and Teardown (defun test-validate-setup () "Reset device variables before each test." (setq cj/recording-system-device nil)) (defun test-validate-teardown () "Clean up device variables after each test." (setq cj/recording-system-device nil)) ;;; Normal Cases (ert-deftest test-validate-system-audio-normal-device-matches-default-no-change () "Test that no change occurs when device matches current default and audio is active." (test-validate-setup) (unwind-protect (let ((cj/recording-system-device "alsa_output.usb-Jabra-00.analog-stereo.monitor")) (cl-letf (((symbol-function 'shell-command-to-string) (lambda (cmd) (cond ((string-match-p "sources short" cmd) "65\talsa_output.usb-Jabra-00.analog-stereo.monitor\tPipeWire\ts32le 2ch 48000Hz\tSUSPENDED\n") ((string-match-p "sinks short" cmd) "65\talsa_output.usb-Jabra-00.analog-stereo\tPipeWire\ts32le 2ch 48000Hz\tSUSPENDED\n") ((string-match-p "sink-inputs" cmd) "Sink Input #1\n\tSink: 65\n") ((string-match-p "get-default-sink" cmd) "alsa_output.usb-Jabra-00.analog-stereo") (t ""))))) (cj/recording--validate-system-audio) (should (equal "alsa_output.usb-Jabra-00.analog-stereo.monitor" cj/recording-system-device)))) (test-validate-teardown))) (ert-deftest test-validate-system-audio-normal-stale-device-auto-updates () "Test that a stale (non-existent) device is auto-updated to current default." (test-validate-setup) (unwind-protect (let ((cj/recording-system-device "old_disappeared_device.monitor") (messages nil)) (cl-letf (((symbol-function 'shell-command-to-string) (lambda (cmd) (cond ((string-match-p "sources short" cmd) ;; Old device NOT in list "65\talsa_output.usb-Jabra-00.analog-stereo.monitor\tPipeWire\ts32le 2ch 48000Hz\tSUSPENDED\n") ((string-match-p "sinks short" cmd) "65\talsa_output.usb-Jabra-00.analog-stereo\tPipeWire\ts32le 2ch 48000Hz\tSUSPENDED\n") ((string-match-p "sink-inputs" cmd) "Sink Input #1\n\tSink: 65\n") ((string-match-p "get-default-sink" cmd) "alsa_output.usb-Jabra-00.analog-stereo") (t "")))) ((symbol-function 'message) (lambda (fmt &rest args) (push (apply #'format fmt args) messages)))) (cj/recording--validate-system-audio) (should (equal "alsa_output.usb-Jabra-00.analog-stereo.monitor" cj/recording-system-device)) (should (cl-some (lambda (m) (string-match-p "no longer exists" m)) messages)))) (test-validate-teardown))) (ert-deftest test-validate-system-audio-normal-drifted-default-auto-updates () "Test that device is updated when default sink has drifted." (test-validate-setup) (unwind-protect (let ((cj/recording-system-device "alsa_output.pci-0000.hdmi-stereo.monitor") (messages nil)) (cl-letf (((symbol-function 'shell-command-to-string) (lambda (cmd) (cond ((string-match-p "sources short" cmd) ;; Old device still exists (concat "65\talsa_output.pci-0000.hdmi-stereo.monitor\tPipeWire\ts32le 2ch 48000Hz\tSUSPENDED\n" "69\talsa_output.usb-Jabra-00.analog-stereo.monitor\tPipeWire\ts32le 2ch 48000Hz\tSUSPENDED\n")) ((string-match-p "sinks short" cmd) "69\talsa_output.usb-Jabra-00.analog-stereo\tPipeWire\ts32le 2ch 48000Hz\tSUSPENDED\n") ((string-match-p "sink-inputs" cmd) "Sink Input #1\n\tSink: 69\n") ;; But default has changed to Jabra ((string-match-p "get-default-sink" cmd) "alsa_output.usb-Jabra-00.analog-stereo") (t "")))) ((symbol-function 'message) (lambda (fmt &rest args) (push (apply #'format fmt args) messages)))) (cj/recording--validate-system-audio) (should (equal "alsa_output.usb-Jabra-00.analog-stereo.monitor" cj/recording-system-device)) (should (cl-some (lambda (m) (string-match-p "default output changed" m)) messages)))) (test-validate-teardown))) (ert-deftest test-validate-system-audio-normal-no-audio-warns () "Test that no active audio triggers a y-or-n-p warning." (test-validate-setup) (unwind-protect (let ((cj/recording-system-device "alsa_output.usb-Jabra-00.analog-stereo.monitor") (prompted nil)) (cl-letf (((symbol-function 'shell-command-to-string) (lambda (cmd) (cond ((string-match-p "sources short" cmd) "65\talsa_output.usb-Jabra-00.analog-stereo.monitor\tPipeWire\ts32le 2ch 48000Hz\tSUSPENDED\n") ((string-match-p "sinks short" cmd) "65\talsa_output.usb-Jabra-00.analog-stereo\tPipeWire\ts32le 2ch 48000Hz\tSUSPENDED\n") ;; No sink inputs — nothing playing ((string-match-p "sink-inputs" cmd) "") ((string-match-p "get-default-sink" cmd) "alsa_output.usb-Jabra-00.analog-stereo") (t "")))) ((symbol-function 'y-or-n-p) (lambda (prompt) (setq prompted prompt) t))) ; User says yes, continue (cj/recording--validate-system-audio) (should prompted) (should (string-match-p "No audio is playing" prompted)))) (test-validate-teardown))) (ert-deftest test-validate-system-audio-normal-no-audio-user-cancels () "Test that user declining the warning cancels recording." (test-validate-setup) (unwind-protect (let ((cj/recording-system-device "alsa_output.usb-Jabra-00.analog-stereo.monitor")) (cl-letf (((symbol-function 'shell-command-to-string) (lambda (cmd) (cond ((string-match-p "sources short" cmd) "65\talsa_output.usb-Jabra-00.analog-stereo.monitor\tPipeWire\ts32le 2ch 48000Hz\tSUSPENDED\n") ((string-match-p "sinks short" cmd) "65\talsa_output.usb-Jabra-00.analog-stereo\tPipeWire\ts32le 2ch 48000Hz\tSUSPENDED\n") ((string-match-p "sink-inputs" cmd) "") ((string-match-p "get-default-sink" cmd) "alsa_output.usb-Jabra-00.analog-stereo") (t "")))) ((symbol-function 'y-or-n-p) (lambda (_prompt) nil))) ; User says no (should-error (cj/recording--validate-system-audio) :type 'user-error))) (test-validate-teardown))) ;;; Boundary Cases (ert-deftest test-validate-system-audio-boundary-nil-device-skips-validation () "Test that nil system device skips all validation." (let ((cj/recording-system-device nil) (shell-called nil)) (cl-letf (((symbol-function 'shell-command-to-string) (lambda (_cmd) (setq shell-called t) ""))) (cj/recording--validate-system-audio) (should-not shell-called)))) (provide 'test-video-audio-recording-validate-system-audio) ;;; test-video-audio-recording-validate-system-audio.el ends here