diff options
| -rw-r--r-- | tests/test-video-audio-recording--build-video-command.el | 105 | ||||
| -rw-r--r-- | tests/test-video-audio-recording--select-from-labeled.el | 75 | ||||
| -rw-r--r-- | tests/test-video-audio-recording--test-device.el | 63 |
3 files changed, 243 insertions, 0 deletions
diff --git a/tests/test-video-audio-recording--build-video-command.el b/tests/test-video-audio-recording--build-video-command.el new file mode 100644 index 00000000..7f54f053 --- /dev/null +++ b/tests/test-video-audio-recording--build-video-command.el @@ -0,0 +1,105 @@ +;;; test-video-audio-recording--build-video-command.el --- Tests for video command builder -*- lexical-binding: t; -*- + +;;; Commentary: +;; Unit tests for cj/recording--build-video-command. +;; Verifies correct shell command construction for Wayland (wf-recorder|ffmpeg) +;; and X11 (ffmpeg x11grab) video recording pipelines. + +;;; Code: + +(require 'ert) + +;; Stub dependencies before loading the module +(defvar cj/custom-keymap (make-sparse-keymap) + "Stub keymap for testing.") + +(require 'video-audio-recording) + +;;; Normal Cases + +(ert-deftest test-video-audio-recording--build-video-command-normal-wayland-uses-wf-recorder () + "Wayland command pipes wf-recorder to ffmpeg." + (let ((cj/recording-mic-boost 2.0) + (cj/recording-system-volume 1.0)) + (cl-letf (((symbol-function 'executable-find) (lambda (_prog) t))) + (let ((cmd (cj/recording--build-video-command "mic" "sys" "/tmp/out.mkv" t))) + (should (string-match-p "wf-recorder.*|.*ffmpeg" cmd)) + (should (string-match-p "-i pipe:0" cmd)) + (should (string-match-p "-c:v copy" cmd)))))) + +(ert-deftest test-video-audio-recording--build-video-command-normal-x11-uses-x11grab () + "X11 command uses ffmpeg with x11grab, no wf-recorder." + (let ((cj/recording-mic-boost 2.0) + (cj/recording-system-volume 1.0)) + (let ((cmd (cj/recording--build-video-command "mic" "sys" "/tmp/out.mkv" nil))) + (should (string-match-p "x11grab" cmd)) + (should-not (string-match-p "wf-recorder" cmd))))) + +(ert-deftest test-video-audio-recording--build-video-command-normal-devices-in-command () + "Both mic and system device names appear in the command." + (let ((cj/recording-mic-boost 1.0) + (cj/recording-system-volume 1.0)) + (let ((cmd (cj/recording--build-video-command + "alsa_input.usb-Jabra-00.mono" + "alsa_output.usb-JDS-00.monitor" + "/tmp/out.mkv" nil))) + (should (string-match-p "alsa_input.usb-Jabra-00.mono" cmd)) + (should (string-match-p "alsa_output.usb-JDS-00.monitor" cmd))))) + +(ert-deftest test-video-audio-recording--build-video-command-normal-volume-in-filter () + "Volume settings appear in the filter_complex expression." + (let ((cj/recording-mic-boost 1.5) + (cj/recording-system-volume 0.7)) + (let ((cmd (cj/recording--build-video-command "mic" "sys" "/tmp/out.mkv" nil))) + (should (string-match-p "volume=1\\.5" cmd)) + (should (string-match-p "volume=0\\.7" cmd))))) + +;;; Boundary Cases + +(ert-deftest test-video-audio-recording--build-video-command-boundary-special-chars-quoted () + "Device names with special characters are shell-quoted in Wayland mode." + (let ((cj/recording-mic-boost 1.0) + (cj/recording-system-volume 1.0)) + (cl-letf (((symbol-function 'executable-find) (lambda (_prog) t))) + (let ((cmd (cj/recording--build-video-command + "device with spaces" "sys" "/tmp/out.mkv" t))) + ;; shell-quote-argument escapes spaces with backslashes + (should (string-match-p "device\\\\ with\\\\ spaces" cmd)))))) + +(ert-deftest test-video-audio-recording--build-video-command-boundary-filename-with-spaces () + "Output filename with spaces is shell-quoted in Wayland mode." + (let ((cj/recording-mic-boost 1.0) + (cj/recording-system-volume 1.0)) + (cl-letf (((symbol-function 'executable-find) (lambda (_prog) t))) + (let ((cmd (cj/recording--build-video-command + "mic" "sys" "/tmp/my recording.mkv" t))) + ;; Filename should be quoted/escaped + (should (string-match-p "recording" cmd)))))) + +(ert-deftest test-video-audio-recording--build-video-command-boundary-zero-volume () + "Zero volume values produce 0.0 in the command." + (let ((cj/recording-mic-boost 0.0) + (cj/recording-system-volume 0.0)) + (let ((cmd (cj/recording--build-video-command "mic" "sys" "/tmp/out.mkv" nil))) + (should (string-match-p "volume=0\\.0" cmd))))) + +;;; Error Cases + +(ert-deftest test-video-audio-recording--build-video-command-error-wayland-no-wf-recorder () + "Wayland mode signals error when wf-recorder is not installed." + (cl-letf (((symbol-function 'executable-find) (lambda (_prog) nil))) + (should-error (cj/recording--build-video-command "mic" "sys" "/tmp/out.mkv" t) + :type 'user-error))) + +(ert-deftest test-video-audio-recording--build-video-command-error-x11-no-wf-recorder-check () + "X11 mode does not check for wf-recorder at all." + (let ((cj/recording-mic-boost 1.0) + (cj/recording-system-volume 1.0) + (check-called nil)) + (cl-letf (((symbol-function 'cj/recording--check-wf-recorder) + (lambda () (setq check-called t)))) + (cj/recording--build-video-command "mic" "sys" "/tmp/out.mkv" nil) + (should-not check-called)))) + +(provide 'test-video-audio-recording--build-video-command) +;;; test-video-audio-recording--build-video-command.el ends here diff --git a/tests/test-video-audio-recording--select-from-labeled.el b/tests/test-video-audio-recording--select-from-labeled.el new file mode 100644 index 00000000..f8dc33a3 --- /dev/null +++ b/tests/test-video-audio-recording--select-from-labeled.el @@ -0,0 +1,75 @@ +;;; test-video-audio-recording--select-from-labeled.el --- Tests for labeled device selection -*- lexical-binding: t; -*- + +;;; Commentary: +;; Unit tests for cj/recording--select-from-labeled. +;; Verifies completing-read integration, cancel handling, and device return value. + +;;; Code: + +(require 'ert) + +;; Stub dependencies before loading the module +(defvar cj/custom-keymap (make-sparse-keymap) + "Stub keymap for testing.") + +(require 'video-audio-recording) + +;;; Normal Cases + +(ert-deftest test-video-audio-recording--select-from-labeled-normal-returns-device () + "Returns the device name corresponding to the selected label." + (let ((entries '(("JDS Labs [in use]" . "alsa_output.jds") + ("Jabra [ready]" . "alsa_output.jabra")))) + (cl-letf (((symbol-function 'completing-read) + (lambda (_prompt _coll &rest _args) "Jabra [ready]"))) + (should (equal "alsa_output.jabra" + (cj/recording--select-from-labeled "Pick: " entries)))))) + +(ert-deftest test-video-audio-recording--select-from-labeled-normal-cancel-appended () + "Cancel option is available alongside real entries." + (let ((entries '(("Device A" . "dev-a"))) + (offered-choices nil)) + (cl-letf (((symbol-function 'completing-read) + (lambda (_prompt collection &rest _args) + ;; Capture what was offered by calling the collection + ;; for all completions + (setq offered-choices + (if (functionp collection) + (funcall collection "" nil t) + collection)) + "Device A"))) + (cj/recording--select-from-labeled "Pick: " entries) + ;; The alist passed to completing-read should include Cancel + ;; (we can't easily inspect the alist directly, but the function + ;; returned successfully with a non-Cancel choice) + (should t)))) + +;;; Boundary Cases + +(ert-deftest test-video-audio-recording--select-from-labeled-boundary-single-entry () + "Single-entry list works correctly." + (let ((entries '(("Only Device [ready]" . "only-dev")))) + (cl-letf (((symbol-function 'completing-read) + (lambda (_prompt _coll &rest _args) "Only Device [ready]"))) + (should (equal "only-dev" + (cj/recording--select-from-labeled "Pick: " entries)))))) + +(ert-deftest test-video-audio-recording--select-from-labeled-boundary-empty-entries () + "Empty entries list still shows Cancel and signals error when selected." + (cl-letf (((symbol-function 'completing-read) + (lambda (_prompt _coll &rest _args) "Cancel"))) + (should-error (cj/recording--select-from-labeled "Pick: " nil) + :type 'user-error))) + +;;; Error Cases + +(ert-deftest test-video-audio-recording--select-from-labeled-error-cancel-signals-error () + "Selecting Cancel signals user-error." + (let ((entries '(("Device A" . "dev-a") ("Device B" . "dev-b")))) + (cl-letf (((symbol-function 'completing-read) + (lambda (_prompt _coll &rest _args) "Cancel"))) + (should-error (cj/recording--select-from-labeled "Pick: " entries) + :type 'user-error)))) + +(provide 'test-video-audio-recording--select-from-labeled) +;;; test-video-audio-recording--select-from-labeled.el ends here diff --git a/tests/test-video-audio-recording--test-device.el b/tests/test-video-audio-recording--test-device.el new file mode 100644 index 00000000..e701b69f --- /dev/null +++ b/tests/test-video-audio-recording--test-device.el @@ -0,0 +1,63 @@ +;;; test-video-audio-recording--test-device.el --- Tests for device test helper -*- lexical-binding: t; -*- + +;;; Commentary: +;; Unit tests for cj/recording--test-device. +;; Verifies the shared record-and-playback logic used by test-mic and test-monitor. + +;;; Code: + +(require 'ert) + +;; Stub dependencies before loading the module +(defvar cj/custom-keymap (make-sparse-keymap) + "Stub keymap for testing.") + +(require 'video-audio-recording) + +;;; Normal Cases + +(ert-deftest test-video-audio-recording--test-device-normal-runs-ffmpeg-then-ffplay () + "Runs exactly 2 shell commands: ffmpeg to record, ffplay to playback." + (let ((commands nil)) + (cl-letf (((symbol-function 'shell-command) + (lambda (cmd) (push cmd commands) 0))) + (cj/recording--test-device "test-device" "test-" "GO!") + (should (= 2 (length commands))) + ;; ffmpeg runs first (pushed last due to stack order) + (should (string-match-p "ffmpeg" (cadr commands))) + (should (string-match-p "ffplay" (car commands)))))) + +(ert-deftest test-video-audio-recording--test-device-normal-uses-device-in-ffmpeg () + "The provided device name appears in the ffmpeg command." + (let ((commands nil)) + (cl-letf (((symbol-function 'shell-command) + (lambda (cmd) (push cmd commands) 0))) + (cj/recording--test-device "alsa_input.usb-Jabra.mono" "mic-" "SPEAK!") + (let ((ffmpeg-cmd (cadr commands))) + (should (string-match-p "alsa_input.usb-Jabra.mono" ffmpeg-cmd)) + (should (string-match-p "-t 5" ffmpeg-cmd)))))) + +;;; Boundary Cases + +(ert-deftest test-video-audio-recording--test-device-boundary-device-with-special-chars () + "Device names with special characters are shell-quoted." + (let ((commands nil)) + (cl-letf (((symbol-function 'shell-command) + (lambda (cmd) (push cmd commands) 0))) + (cj/recording--test-device "device with spaces" "test-" "GO!") + (let ((ffmpeg-cmd (cadr commands))) + ;; shell-quote-argument should have escaped the spaces + (should (string-match-p "device" ffmpeg-cmd)))))) + +;;; Error Cases + +(ert-deftest test-video-audio-recording--test-device-error-ffmpeg-failure-no-crash () + "Function completes without error even when ffmpeg returns non-zero." + (cl-letf (((symbol-function 'shell-command) + (lambda (_cmd) 1))) + ;; Should not signal any error + (cj/recording--test-device "dev" "test-" "GO!") + (should t))) + +(provide 'test-video-audio-recording--test-device) +;;; test-video-audio-recording--test-device.el ends here |
