From cebb70fd5799f0bb9eaa7c14747408095619fc08 Mon Sep 17 00:00:00 2001 From: Craig Jennings Date: Tue, 3 Feb 2026 08:57:10 -0600 Subject: test(video-recording): add process cleanup tests Unit tests verify pkill wf-recorder is called: - On Wayland video stop (to kill orphan wf-recorder) - On Wayland video start (to clean previous orphans) - NOT called on X11 (only relevant for Wayland pipeline) Integration tests (tagged :integration :wayland) verify actual cleanup: - No orphan processes after stop - Start cleans up existing orphans - Multiple start/stop cycles don't accumulate orphans --- .../test-video-audio-recording-process-cleanup.el | 229 +++++++++++++++++++++ 1 file changed, 229 insertions(+) create mode 100644 tests/test-video-audio-recording-process-cleanup.el (limited to 'tests/test-video-audio-recording-process-cleanup.el') diff --git a/tests/test-video-audio-recording-process-cleanup.el b/tests/test-video-audio-recording-process-cleanup.el new file mode 100644 index 00000000..10b17f2c --- /dev/null +++ b/tests/test-video-audio-recording-process-cleanup.el @@ -0,0 +1,229 @@ +;;; test-video-audio-recording-process-cleanup.el --- Tests for recording process cleanup -*- lexical-binding: t; -*- + +;;; Commentary: +;; Tests that verify proper cleanup of recording processes, especially +;; wf-recorder on Wayland which can become orphaned if not explicitly killed. +;; +;; Unit tests verify pkill is called at the right times. +;; Integration tests verify no orphan processes remain after stop. + +;;; Code: + +(require 'ert) + +;; Stub dependencies before loading the module +(defvar cj/custom-keymap (make-sparse-keymap) + "Stub keymap for testing.") + +(defvar video-recordings-dir "/tmp/video-recordings/") + +;; Now load the actual production module +(require 'video-audio-recording) + +;;; Setup and Teardown + +(defun test-cleanup-setup () + "Reset all variables before each test." + (setq cj/video-recording-ffmpeg-process nil) + (setq cj/recording-mic-device "test-mic-device") + (setq cj/recording-system-device "test-monitor-device") + (setq cj/recording-mic-boost 2.0) + (setq cj/recording-system-volume 1.0)) + +(defun test-cleanup-teardown () + "Clean up after each test." + (when cj/video-recording-ffmpeg-process + (ignore-errors (delete-process cj/video-recording-ffmpeg-process))) + (setq cj/video-recording-ffmpeg-process nil) + (setq cj/recording-mic-device nil) + (setq cj/recording-system-device nil)) + +;;; Unit Tests - Verify pkill is called + +(ert-deftest test-video-recording-stop-wayland-calls-pkill-wf-recorder () + "Test that stopping video on Wayland calls pkill to kill wf-recorder." + (test-cleanup-setup) + (unwind-protect + (let ((fake-process (make-process :name "test-video" :command '("sleep" "1000"))) + (pkill-called nil) + (pkill-args nil)) + (setq cj/video-recording-ffmpeg-process fake-process) + (cl-letf (((symbol-function 'cj/recording--wayland-p) (lambda () t)) + ((symbol-function 'call-process) + (lambda (program &rest args) + (when (equal program "pkill") + (setq pkill-called t) + (setq pkill-args args)) + 0)) + ((symbol-function 'interrupt-process) (lambda (_proc) nil))) + (cj/video-recording-stop) + (should pkill-called) + ;; Should call: pkill -INT wf-recorder + (should (member "-INT" pkill-args)) + (should (member "wf-recorder" pkill-args))) + (ignore-errors (delete-process fake-process))) + (test-cleanup-teardown))) + +(ert-deftest test-video-recording-stop-x11-does-not-call-pkill () + "Test that stopping video on X11 does NOT call pkill wf-recorder." + (test-cleanup-setup) + (unwind-protect + (let ((fake-process (make-process :name "test-video" :command '("sleep" "1000"))) + (pkill-called nil)) + (setq cj/video-recording-ffmpeg-process fake-process) + (cl-letf (((symbol-function 'cj/recording--wayland-p) (lambda () nil)) + ((symbol-function 'call-process) + (lambda (program &rest _args) + (when (equal program "pkill") + (setq pkill-called t)) + 0)) + ((symbol-function 'interrupt-process) (lambda (_proc) nil))) + (cj/video-recording-stop) + ;; Should NOT call pkill on X11 + (should-not pkill-called)) + (ignore-errors (delete-process fake-process))) + (test-cleanup-teardown))) + +(ert-deftest test-video-recording-start-wayland-kills-orphans () + "Test that starting video on Wayland kills orphan wf-recorder processes." + (test-cleanup-setup) + (unwind-protect + (let ((pkill-called nil) + (pkill-args nil)) + (cl-letf (((symbol-function 'cj/recording--wayland-p) (lambda () t)) + ((symbol-function 'cj/recording--check-wf-recorder) (lambda () t)) + ((symbol-function 'call-process) + (lambda (program &rest args) + (when (equal program "pkill") + (setq pkill-called t) + (setq pkill-args args)) + 0)) + ((symbol-function 'start-process-shell-command) + (lambda (_name _buffer _command) + (make-process :name "fake-video" :command '("sleep" "1000"))))) + (cj/ffmpeg-record-video video-recordings-dir) + ;; Should call pkill to clean up orphans before starting + (should pkill-called) + (should (member "-INT" pkill-args)) + (should (member "wf-recorder" pkill-args)))) + (test-cleanup-teardown))) + +(ert-deftest test-video-recording-start-x11-does-not-kill-orphans () + "Test that starting video on X11 does NOT call pkill." + (test-cleanup-setup) + (unwind-protect + (let ((pkill-called nil)) + (cl-letf (((symbol-function 'cj/recording--wayland-p) (lambda () nil)) + ((symbol-function 'call-process) + (lambda (program &rest _args) + (when (equal program "pkill") + (setq pkill-called t)) + 0)) + ((symbol-function 'start-process-shell-command) + (lambda (_name _buffer _command) + (make-process :name "fake-video" :command '("sleep" "1000"))))) + (cj/ffmpeg-record-video video-recordings-dir) + ;; Should NOT call pkill on X11 + (should-not pkill-called))) + (test-cleanup-teardown))) + +;;; Integration Tests - Verify actual process cleanup +;;; These tests require wf-recorder to be installed and a Wayland session + +(defun test-cleanup--count-wf-recorder-processes () + "Count running wf-recorder processes." + (let ((output (shell-command-to-string "pgrep -c wf-recorder 2>/dev/null || echo 0"))) + (string-to-number (string-trim output)))) + +(defun test-cleanup--get-wf-recorder-pids () + "Get list of wf-recorder PIDs." + (let ((output (shell-command-to-string "pgrep wf-recorder 2>/dev/null"))) + (when (and output (not (string-empty-p (string-trim output)))) + (mapcar #'string-to-number (split-string (string-trim output) "\n"))))) + +(ert-deftest test-integration-video-recording-no-orphan-wf-recorder-after-stop () + "Test that no wf-recorder processes remain after stopping recording. +This is an integration test that requires wf-recorder and Wayland." + :tags '(:integration :wayland) + (skip-unless (executable-find "wf-recorder")) + (skip-unless (cj/recording--wayland-p)) + (test-cleanup-setup) + (unwind-protect + (let ((initial-count (test-cleanup--count-wf-recorder-processes))) + ;; Start recording + (cj/ffmpeg-record-video video-recordings-dir) + (sit-for 1.0) ; Let it start + + ;; Should have at least one wf-recorder running + (should (> (test-cleanup--count-wf-recorder-processes) initial-count)) + + ;; Stop recording + (cj/video-recording-stop) + (sit-for 1.0) ; Let cleanup complete + + ;; Should be back to initial count (no orphans) + (should (= (test-cleanup--count-wf-recorder-processes) initial-count))) + (test-cleanup-teardown) + ;; Extra cleanup in case test failed mid-way + (ignore-errors (call-process "pkill" nil nil nil "-INT" "wf-recorder")))) + +(ert-deftest test-integration-video-recording-start-cleans-orphans () + "Test that starting a recording cleans up any orphan wf-recorder processes. +This is an integration test that requires wf-recorder and Wayland." + :tags '(:integration :wayland) + (skip-unless (executable-find "wf-recorder")) + (skip-unless (cj/recording--wayland-p)) + (test-cleanup-setup) + (unwind-protect + (progn + ;; Create an orphan wf-recorder (simulating a crash) + (start-process "orphan-wf" nil "wf-recorder" "-c" "libx264" "-m" "matroska" "-f" "/dev/null") + (sit-for 0.5) + + (let ((orphan-pids (test-cleanup--get-wf-recorder-pids))) + (should orphan-pids) ; Verify orphan was created + + ;; Start recording - should clean up orphan + (cj/ffmpeg-record-video video-recordings-dir) + (sit-for 1.0) + + ;; The orphan PIDs should no longer exist + (let ((current-pids (test-cleanup--get-wf-recorder-pids))) + ;; Old orphan should be gone (new recording process may exist) + (dolist (orphan-pid orphan-pids) + ;; Check if old orphan is still in current pids + ;; It's OK if there's a NEW wf-recorder, just not the orphan + (let ((still-running (member orphan-pid current-pids))) + (should-not still-running)))) + + ;; Clean up the recording we started + (cj/video-recording-stop) + (sit-for 0.5))) + (test-cleanup-teardown) + ;; Extra cleanup + (ignore-errors (call-process "pkill" nil nil nil "-INT" "wf-recorder")))) + +(ert-deftest test-integration-video-recording-multiple-start-stop-cycles () + "Test that multiple recording cycles don't accumulate orphan processes. +This is an integration test that requires wf-recorder and Wayland." + :tags '(:integration :wayland) + (skip-unless (executable-find "wf-recorder")) + (skip-unless (cj/recording--wayland-p)) + (test-cleanup-setup) + (unwind-protect + (let ((initial-count (test-cleanup--count-wf-recorder-processes))) + ;; Do 3 start/stop cycles + (dotimes (_ 3) + (cj/ffmpeg-record-video video-recordings-dir) + (sit-for 0.5) + (cj/video-recording-stop) + (sit-for 0.5)) + + ;; Should be back to initial count - no accumulated orphans + (should (= (test-cleanup--count-wf-recorder-processes) initial-count))) + (test-cleanup-teardown) + ;; Extra cleanup + (ignore-errors (call-process "pkill" nil nil nil "-INT" "wf-recorder")))) + +(provide 'test-video-audio-recording-process-cleanup) +;;; test-video-audio-recording-process-cleanup.el ends here -- cgit v1.2.3