summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/fixtures/audio/microphone-input-test.wavbin0 -> 167258 bytes
-rw-r--r--tests/fixtures/audio/speaker-output-test.wavbin0 -> 572668 bytes
-rw-r--r--tests/test-integration-recording-monitor-capture-interactive.el186
3 files changed, 186 insertions, 0 deletions
diff --git a/tests/fixtures/audio/microphone-input-test.wav b/tests/fixtures/audio/microphone-input-test.wav
new file mode 100644
index 00000000..9a59c6a4
--- /dev/null
+++ b/tests/fixtures/audio/microphone-input-test.wav
Binary files differ
diff --git a/tests/fixtures/audio/speaker-output-test.wav b/tests/fixtures/audio/speaker-output-test.wav
new file mode 100644
index 00000000..008e9710
--- /dev/null
+++ b/tests/fixtures/audio/speaker-output-test.wav
Binary files differ
diff --git a/tests/test-integration-recording-monitor-capture-interactive.el b/tests/test-integration-recording-monitor-capture-interactive.el
new file mode 100644
index 00000000..ece8b79e
--- /dev/null
+++ b/tests/test-integration-recording-monitor-capture-interactive.el
@@ -0,0 +1,186 @@
+;;; test-integration-recording-monitor-capture-interactive.el --- Interactive recording test -*- lexical-binding: t; -*-
+
+;; Author: Craig Jennings <c@cjennings.net>
+;; Created: 2025-11-14
+
+;;; Commentary:
+;;
+;; **INTERACTIVE TEST - Run from within Emacs**
+;;
+;; This test must be run from an interactive Emacs session where recording
+;; devices are already configured (C-; r c).
+;;
+;; USAGE:
+;; 1. Ensure devices are configured: C-; r c
+;; 2. Load this file: M-x load-file RET tests/test-integration-recording-monitor-capture-interactive.el RET
+;; 3. Run test: M-x test-recording-monitor-now RET
+;;
+;; OR simply:
+;; M-x ert RET test-integration-recording-monitor-capture RET
+;;
+;; The test will:
+;; - Play test audio through your speakers (5 seconds)
+;; - Record it
+;; - Transcribe it
+;; - Verify the transcription contains the expected text
+;;
+;; This verifies that phone call audio (speaker output) is being captured correctly.
+
+;;; Code:
+
+(require 'video-audio-recording)
+(require 'transcription-config)
+
+(defvar test-recording--test-audio
+ (expand-file-name "tests/fixtures/audio/speaker-output-test.wav" user-emacs-directory)
+ "Test audio file for speaker output testing.")
+
+(defvar test-recording--expected-phrases
+ '("hear me" "testing" "one")
+ "Expected phrases in transcription (partial match OK).
+Based on actual recording: 'Can you hear me? Testing, one, two, three.'")
+
+(defun test-recording--cleanup-files (recording-file)
+ "Clean up RECORDING-FILE and associated files."
+ (when (and recording-file (file-exists-p recording-file))
+ (let* ((base (file-name-sans-extension recording-file))
+ (txt-file (concat base ".txt"))
+ (log-file (concat base ".log")))
+ (when (file-exists-p recording-file) (delete-file recording-file))
+ (when (file-exists-p txt-file) (delete-file txt-file))
+ (when (file-exists-p log-file) (delete-file log-file)))))
+
+(defun test-recording--wait-for-file (file timeout)
+ "Wait for FILE to exist and have content, up to TIMEOUT seconds.
+Returns FILE path if successful, nil if timeout."
+ (let ((deadline (time-add (current-time) (seconds-to-time timeout))))
+ (while (and (time-less-p (current-time) deadline)
+ (or (not (file-exists-p file))
+ (= 0 (file-attribute-size (file-attributes file)))))
+ (sleep-for 1)
+ (message "Waiting for %s... (%d sec remaining)"
+ (file-name-nondirectory file)
+ (ceiling (float-time (time-subtract deadline (current-time))))))
+ (when (and (file-exists-p file)
+ (> (file-attribute-size (file-attributes file)) 0))
+ file)))
+
+;;;###autoload
+(defun test-recording-monitor-now ()
+ "Test recording monitor capture interactively.
+This function can be called with M-x to test recording without ERT framework."
+ (interactive)
+
+ ;; Pre-flight checks
+ (unless (executable-find "paplay")
+ (user-error "paplay not found. Install pulseaudio-utils"))
+ (unless (executable-find "ffmpeg")
+ (user-error "ffmpeg not found. Install ffmpeg"))
+ (unless (file-exists-p test-recording--test-audio)
+ (user-error "Test audio file not found: %s" test-recording--test-audio))
+ (unless (and cj/recording-mic-device cj/recording-system-device)
+ (user-error "Recording devices not configured. Run C-; r c first"))
+
+ (let ((test-dir (make-temp-file "recording-test-" t))
+ (recording-file nil)
+ (playback-proc nil))
+ (unwind-protect
+ (progn
+ (message "\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")
+ (message "RECORDING MONITOR CAPTURE TEST")
+ (message "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n")
+ (message "Configuration:")
+ (message " Mic: %s" cj/recording-mic-device)
+ (message " Monitor: %s" cj/recording-system-device)
+ (message " Backend: %s\n" cj/transcribe-backend)
+
+ ;; Step 1: Start recording
+ (message "[1/6] Starting recording...")
+ (cj/ffmpeg-record-audio test-dir)
+ (sleep-for 1)
+ (unless (process-live-p cj/audio-recording-ffmpeg-process)
+ (error "Failed to start recording"))
+ (message "✓ Recording started\n")
+
+ ;; Step 2: Play test audio
+ (message "[2/6] Playing test audio through speakers...")
+ (setq playback-proc (start-process "test-playback" "*test-playback*"
+ "paplay" test-recording--test-audio))
+ (message "✓ Playback started\n")
+
+ ;; Step 3: Wait for playback
+ (message "[3/6] Waiting for playback to complete...")
+ (let ((waited 0))
+ (while (and (process-live-p playback-proc) (< waited 10))
+ (sleep-for 0.5)
+ (setq waited (+ waited 0.5)))
+ (when (process-live-p playback-proc)
+ (kill-process playback-proc)
+ (error "Playback timed out")))
+ (sleep-for 1)
+ (message "✓ Playback completed\n")
+
+ ;; Step 4: Stop recording
+ (message "[4/6] Stopping recording...")
+ (cj/audio-recording-stop)
+ (sleep-for 1)
+
+ ;; Find recording file
+ (let ((files (directory-files test-dir t "\\.m4a$")))
+ (unless (= 1 (length files))
+ (error "Expected 1 recording file, found %d" (length files)))
+ (setq recording-file (car files)))
+
+ (message "✓ Recording stopped")
+ (message " File: %s" recording-file)
+ (message " Size: %d bytes\n"
+ (file-attribute-size (file-attributes recording-file)))
+
+ ;; Step 5: Transcribe
+ (message "[5/6] Transcribing (this may take 30-60 seconds)...")
+ (cj/transcribe-audio recording-file)
+
+ (let ((txt-file (concat (file-name-sans-extension recording-file) ".txt")))
+ (unless (test-recording--wait-for-file txt-file 120)
+ (error "Transcription timed out or failed"))
+ (message "✓ Transcription completed\n")
+
+ ;; Step 6: Verify
+ (message "[6/6] Verifying transcription...")
+ (let ((transcript (with-temp-buffer
+ (insert-file-contents txt-file)
+ (downcase (buffer-string))))
+ (matches 0))
+ (message "Transcript (%d chars): %s..."
+ (length transcript)
+ (substring transcript 0 (min 80 (length transcript))))
+
+ (dolist (phrase test-recording--expected-phrases)
+ (when (string-match-p phrase transcript)
+ (setq matches (1+ matches))
+ (message " ✓ Found: '%s'" phrase)))
+
+ (message "\nMatched %d/%d expected phrases"
+ matches (length test-recording--expected-phrases))
+
+ (if (>= matches 2)
+ (progn
+ (message "\n✓✓✓ TEST PASSED ✓✓✓")
+ (message "Monitor is correctly capturing speaker audio!"))
+ (error "TEST FAILED: Only matched %d/%d phrases"
+ matches (length test-recording--expected-phrases)))))))
+
+ ;; Cleanup
+ (when (and playback-proc (process-live-p playback-proc))
+ (kill-process playback-proc))
+ (when (and cj/audio-recording-ffmpeg-process
+ (process-live-p cj/audio-recording-ffmpeg-process))
+ (cj/audio-recording-stop))
+ (when recording-file
+ (test-recording--cleanup-files recording-file))
+ (when (file-exists-p test-dir)
+ (delete-directory test-dir t))
+ (message "\nCleanup complete."))))
+
+(provide 'test-integration-recording-monitor-capture-interactive)
+;;; test-integration-recording-monitor-capture-interactive.el ends here