From 0a69c5854378afcafc567d965f206cf6a0a984be Mon Sep 17 00:00:00 2001 From: Craig Jennings Date: Mon, 3 Nov 2025 15:26:11 -0600 Subject: test: Add comprehensive test suite for video-audio-recording module MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added 83 test cases across 9 test files with 100% pass rate, covering device detection, parsing, grouping, and complete workflow integration. ## What Was Done ### Refactoring for Testability - Extracted `cj/recording--parse-pactl-output` from `cj/recording-parse-sources` - Separated parsing logic from shell command execution - Enables testing with fixture data instead of live system calls ### Test Fixtures Created - `pactl-output-normal.txt` - All device types (built-in, USB, Bluetooth) - `pactl-output-empty.txt` - Empty output - `pactl-output-single.txt` - Single device - `pactl-output-monitors-only.txt` - Only monitor devices - `pactl-output-inputs-only.txt` - Only input devices - `pactl-output-malformed.txt` - Invalid/malformed output ### Unit Tests (8 files, 78 test cases) 1. **test-video-audio-recording-friendly-state.el** (10 tests) - State name conversion: SUSPENDED→Ready, RUNNING→Active 2. **test-video-audio-recording-parse-pactl-output.el** (14 tests) - Parse raw pactl output into structured data - Handle empty, malformed, and mixed valid/invalid input 3. **test-video-audio-recording-parse-sources.el** (6 tests) - Shell command wrapper testing with mocked output 4. **test-video-audio-recording-detect-mic-device.el** (13 tests) - Documents bugs: Returns ID numbers instead of device names - Doesn't filter monitors (legacy function, not actively used) 5. **test-video-audio-recording-detect-system-device.el** (13 tests) - Works correctly: Returns full device names - Tests monitor detection with various device types 6. **test-video-audio-recording-group-devices-by-hardware.el** (12 tests) - CRITICAL: Bluetooth MAC address normalization (colons vs underscores) - Device pairing logic (mic + monitor from same hardware) - Friendly name assignment - Filters incomplete devices 7. **test-video-audio-recording-check-ffmpeg.el** (3 tests) - ffmpeg availability detection 8. **test-video-audio-recording-get-devices.el** (7 tests) - Auto-detection fallback logic - Error handling for incomplete detection ### Integration Tests (1 file, 5 test cases) 9. **test-integration-recording-device-workflow.el** (5 tests) - Complete workflow: parse → group → friendly names - Bluetooth MAC normalization end-to-end - Incomplete device filtering across components - Malformed data graceful handling ## Key Testing Insights ### Bugs Documented - `cj/recording-detect-mic-device` has bugs (returns IDs, doesn't filter monitors) - These functions appear to be legacy code not used by main workflow - Tests document current behavior to catch regressions if fixed ### Critical Features Validated - **Bluetooth MAC normalization**: Input uses colons (00:1B:66:C0:91:6D), output uses underscores (00_1B_66_C0_91_6D), grouping normalizes correctly - **Device pairing**: Only devices with BOTH mic and monitor are included - **Friendly names**: USB/PCI/Bluetooth patterns correctly identified ### Test Coverage - Normal cases: Valid inputs, typical workflows - Boundary cases: Empty, single device, incomplete pairs - Error cases: Malformed input, missing devices, partial detection ## Test Execution All tests pass: 9/9 files, 83/83 test cases (100% pass rate) ```bash make test-file FILE=test-video-audio-recording-*.el # All pass individually # Integration test also passes make test-file FILE=test-integration-recording-device-workflow.el ``` 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- tests/test-video-audio-recording-parse-sources.el | 98 +++++++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 tests/test-video-audio-recording-parse-sources.el (limited to 'tests/test-video-audio-recording-parse-sources.el') diff --git a/tests/test-video-audio-recording-parse-sources.el b/tests/test-video-audio-recording-parse-sources.el new file mode 100644 index 00000000..d6d445b5 --- /dev/null +++ b/tests/test-video-audio-recording-parse-sources.el @@ -0,0 +1,98 @@ +;;; test-video-audio-recording-parse-sources.el --- Tests for cj/recording-parse-sources -*- lexical-binding: t; -*- + +;;; Commentary: +;; Unit tests for cj/recording-parse-sources function. +;; Tests the wrapper that calls pactl and delegates to internal parser. +;; Mocks shell-command-to-string to avoid system dependencies. + +;;; Code: + +(require 'ert) + +;; Stub dependencies before loading the module +(defvar cj/custom-keymap (make-sparse-keymap) + "Stub keymap for testing.") + +;; Now load the actual production module +(require 'video-audio-recording) + +;;; Test Fixtures Helper + +(defun test-load-fixture (filename) + "Load fixture file FILENAME from tests/fixtures directory." + (let ((fixture-path (expand-file-name + (concat "tests/fixtures/" filename) + user-emacs-directory))) + (with-temp-buffer + (insert-file-contents fixture-path) + (buffer-string)))) + +;;; Normal Cases + +(ert-deftest test-video-audio-recording-parse-sources-normal-calls-pactl-and-parses () + "Test that parse-sources calls shell command and returns parsed list." + (let ((fixture-output (test-load-fixture "pactl-output-normal.txt"))) + (cl-letf (((symbol-function 'shell-command-to-string) + (lambda (_cmd) fixture-output))) + (let ((result (cj/recording-parse-sources))) + (should (listp result)) + (should (= 6 (length result))) + ;; Verify it returns structured data + (should (equal "alsa_output.pci-0000_00_1f.3.analog-stereo.monitor" + (car (nth 0 result)))) + (should (equal "PipeWire" (nth 1 (nth 0 result)))) + (should (equal "SUSPENDED" (nth 2 (nth 0 result)))))))) + +(ert-deftest test-video-audio-recording-parse-sources-normal-single-device-returns-list () + "Test parse-sources with single device." + (let ((fixture-output (test-load-fixture "pactl-output-single.txt"))) + (cl-letf (((symbol-function 'shell-command-to-string) + (lambda (_cmd) fixture-output))) + (let ((result (cj/recording-parse-sources))) + (should (listp result)) + (should (= 1 (length result))))))) + +;;; Boundary Cases + +(ert-deftest test-video-audio-recording-parse-sources-boundary-empty-output-returns-empty-list () + "Test that empty pactl output returns empty list." + (cl-letf (((symbol-function 'shell-command-to-string) + (lambda (_cmd) ""))) + (let ((result (cj/recording-parse-sources))) + (should (listp result)) + (should (null result))))) + +(ert-deftest test-video-audio-recording-parse-sources-boundary-whitespace-output-returns-empty-list () + "Test that whitespace-only output returns empty list." + (cl-letf (((symbol-function 'shell-command-to-string) + (lambda (_cmd) " \n\t\n "))) + (let ((result (cj/recording-parse-sources))) + (should (listp result)) + (should (null result))))) + +;;; Error Cases + +(ert-deftest test-video-audio-recording-parse-sources-error-malformed-output-returns-empty-list () + "Test that malformed output is handled gracefully." + (let ((fixture-output (test-load-fixture "pactl-output-malformed.txt"))) + (cl-letf (((symbol-function 'shell-command-to-string) + (lambda (_cmd) fixture-output))) + (let ((result (cj/recording-parse-sources))) + (should (listp result)) + (should (null result)))))) + +(ert-deftest test-video-audio-recording-parse-sources-error-mixed-valid-invalid-returns-valid-only () + "Test that mix of valid and invalid lines returns only valid entries." + (let ((mixed-output (concat + "50\talsa_input.pci-0000_00_1f.3.analog-stereo\tPipeWire\ts32le 2ch 48000Hz\tSUSPENDED\n" + "invalid line\n" + "79\tbluez_input.00:1B:66:C0:91:6D\tPipeWire\tfloat32le 1ch 48000Hz\tRUNNING\n"))) + (cl-letf (((symbol-function 'shell-command-to-string) + (lambda (_cmd) mixed-output))) + (let ((result (cj/recording-parse-sources))) + (should (= 2 (length result))) + (should (equal "alsa_input.pci-0000_00_1f.3.analog-stereo" (car (nth 0 result)))) + (should (equal "bluez_input.00:1B:66:C0:91:6D" (car (nth 1 result)))))))) + +(provide 'test-video-audio-recording-parse-sources) +;;; test-video-audio-recording-parse-sources.el ends here -- cgit v1.2.3