summaryrefslogtreecommitdiff
path: root/tests/test-music-config-toggle-consume.el
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-02-15 01:02:23 -0600
committerCraig Jennings <c@cjennings.net>2026-02-15 01:02:23 -0600
commitf010700dba1d240e6d485c440d7758de7473a7d2 (patch)
tree5b77bfbc5ec6569cb71f3a548a7c0315f05a4a65 /tests/test-music-config-toggle-consume.el
parent24a82f9e9a69fd567aa5afc2e3a4e6e7399fbf64 (diff)
feat(music): align EMMS keybindings with ncmpcpp, add consume mode
Reconcile playlist-mode bindings with ncmpcpp defaults: add ncmpcpp- compatible aliases (>/<, +, S-up/S-down, c), new toggles (r repeat, t repeat-track, z random, x consume), info keys (i, o), and relocate radio (r→R), reload (R→g), shuffle (x→Z). Add consume mode that removes tracks from playlist after playback via emms-player-finished- hook. Fix buffer-local bug where emms-playlist-selected-marker was checked outside with-current-buffer. 13 ERT tests.
Diffstat (limited to 'tests/test-music-config-toggle-consume.el')
-rw-r--r--tests/test-music-config-toggle-consume.el262
1 files changed, 262 insertions, 0 deletions
diff --git a/tests/test-music-config-toggle-consume.el b/tests/test-music-config-toggle-consume.el
new file mode 100644
index 00000000..c2c38d34
--- /dev/null
+++ b/tests/test-music-config-toggle-consume.el
@@ -0,0 +1,262 @@
+;;; test-music-config-toggle-consume.el --- Tests for consume mode -*- coding: utf-8; lexical-binding: t; -*-
+;;
+;; Author: Craig Jennings <c@cjennings.net>
+;;
+;;; Commentary:
+;; Unit tests for cj/music-toggle-consume and cj/music--consume-track.
+;;
+;; cj/music-toggle-consume toggles consume mode on/off, managing the
+;; emms-player-finished-hook to remove tracks after playback.
+;;
+;; cj/music--consume-track is the hook function that removes the track
+;; at the selected marker position from the playlist buffer.
+;;
+;; Test organization:
+;; - Normal Cases: Toggle on/off, track removal during consume
+;; - Boundary Cases: Double toggle, single-track playlist, consume off
+;; - Error Cases: No selected marker, empty playlist
+;;
+;;; Code:
+
+(require 'ert)
+
+;; Stub missing dependencies before loading music-config
+(defvar-keymap cj/custom-keymap
+ :doc "Stub keymap for testing")
+
+;; Add EMMS elpa directory to load path for batch testing
+(let ((emms-dir (car (file-expand-wildcards
+ (expand-file-name "elpa/emms-*" user-emacs-directory)))))
+ (when emms-dir
+ (add-to-list 'load-path emms-dir)))
+
+;; Load EMMS for playlist buffer setup
+(require 'emms)
+(require 'emms-playlist-mode)
+
+;; Load production code
+(require 'music-config)
+
+;;; Test helpers
+
+(defun test-consume--setup-playlist-buffer (track-names)
+ "Create an EMMS playlist buffer with TRACK-NAMES and return it.
+Each entry in TRACK-NAMES becomes a file track in the playlist.
+Uses EMMS's own insertion function for proper text properties.
+Selects the first track by default."
+ (let ((buf (get-buffer-create "*EMMS-Test-Playlist*")))
+ (with-current-buffer buf
+ (emms-playlist-mode)
+ (setq emms-playlist-buffer-p t)
+ (let ((inhibit-read-only t))
+ (erase-buffer)
+ (dolist (name track-names)
+ (emms-playlist-insert-track (emms-track 'file name)))
+ ;; Set selected marker to first track
+ (when track-names
+ (goto-char (point-min))
+ (unless emms-playlist-selected-marker
+ (setq emms-playlist-selected-marker (make-marker)))
+ (set-marker emms-playlist-selected-marker (point)))))
+ buf))
+
+(defun test-consume--teardown ()
+ "Clean up consume mode state and test buffers."
+ (setq cj/music-consume-mode nil)
+ (remove-hook 'emms-player-finished-hook #'cj/music--consume-track)
+ (when-let ((buf (get-buffer "*EMMS-Test-Playlist*")))
+ (kill-buffer buf)))
+
+(defun test-consume--track-count (buf)
+ "Return number of tracks in playlist buffer BUF."
+ (with-current-buffer buf
+ (if (= (point-min) (point-max))
+ 0
+ (count-lines (point-min) (point-max)))))
+
+(defun test-consume--capture-message (fn &rest args)
+ "Call FN with ARGS and return the message it produces.
+Works in both interactive and batch mode."
+ (let ((captured nil))
+ (cl-letf (((symbol-function 'message)
+ (lambda (fmt &rest msg-args)
+ (setq captured (apply #'format fmt msg-args)))))
+ (apply fn args))
+ captured))
+
+;;; Normal Cases - cj/music-toggle-consume
+
+(ert-deftest test-music-config-toggle-consume-normal-enable-sets-variable ()
+ "Validate toggling consume on sets cj/music-consume-mode to t."
+ (unwind-protect
+ (progn
+ (setq cj/music-consume-mode nil)
+ (cj/music-toggle-consume)
+ (should (eq cj/music-consume-mode t)))
+ (test-consume--teardown)))
+
+(ert-deftest test-music-config-toggle-consume-normal-enable-adds-hook ()
+ "Validate toggling consume on adds consume-track to finished hook."
+ (unwind-protect
+ (progn
+ (setq cj/music-consume-mode nil)
+ (cj/music-toggle-consume)
+ (should (memq #'cj/music--consume-track emms-player-finished-hook)))
+ (test-consume--teardown)))
+
+(ert-deftest test-music-config-toggle-consume-normal-disable-clears-variable ()
+ "Validate toggling consume off sets cj/music-consume-mode to nil."
+ (unwind-protect
+ (progn
+ (setq cj/music-consume-mode t)
+ (add-hook 'emms-player-finished-hook #'cj/music--consume-track)
+ (cj/music-toggle-consume)
+ (should (eq cj/music-consume-mode nil)))
+ (test-consume--teardown)))
+
+(ert-deftest test-music-config-toggle-consume-normal-disable-removes-hook ()
+ "Validate toggling consume off removes consume-track from finished hook."
+ (unwind-protect
+ (progn
+ (setq cj/music-consume-mode t)
+ (add-hook 'emms-player-finished-hook #'cj/music--consume-track)
+ (cj/music-toggle-consume)
+ (should-not (memq #'cj/music--consume-track emms-player-finished-hook)))
+ (test-consume--teardown)))
+
+(ert-deftest test-music-config-toggle-consume-normal-enable-message ()
+ "Validate toggling on produces enabled message."
+ (unwind-protect
+ (progn
+ (setq cj/music-consume-mode nil)
+ (let ((msg (test-consume--capture-message #'cj/music-toggle-consume)))
+ (should (string-match-p "enabled" msg))))
+ (test-consume--teardown)))
+
+(ert-deftest test-music-config-toggle-consume-normal-disable-message ()
+ "Validate toggling off produces disabled message."
+ (unwind-protect
+ (progn
+ (setq cj/music-consume-mode t)
+ (add-hook 'emms-player-finished-hook #'cj/music--consume-track)
+ (let ((msg (test-consume--capture-message #'cj/music-toggle-consume)))
+ (should (string-match-p "disabled" msg))))
+ (test-consume--teardown)))
+
+;;; Normal Cases - cj/music--consume-track
+
+(ert-deftest test-music-config--consume-track-normal-calls-kill-at-selected ()
+ "Validate consume-track calls kill-track at the selected marker position."
+ (unwind-protect
+ (let* ((cj/music-playlist-buffer-name "*EMMS-Test-Playlist*")
+ (buf (test-consume--setup-playlist-buffer
+ '("/music/track1.mp3" "/music/track2.mp3" "/music/track3.mp3")))
+ (cj/music-consume-mode t)
+ (kill-called nil)
+ (kill-position nil)
+ (expected-pos (with-current-buffer buf
+ (marker-position emms-playlist-selected-marker))))
+ (cl-letf (((symbol-function 'emms-playlist-mode-kill-track)
+ (lambda ()
+ (setq kill-called t
+ kill-position (point)))))
+ (cj/music--consume-track)
+ (should kill-called)
+ (should (= kill-position expected-pos))))
+ (test-consume--teardown)))
+
+(ert-deftest test-music-config--consume-track-normal-calls-kill-at-middle-track ()
+ "Validate consume-track navigates to middle track before killing."
+ (unwind-protect
+ (let* ((cj/music-playlist-buffer-name "*EMMS-Test-Playlist*")
+ (buf (test-consume--setup-playlist-buffer
+ '("/music/track1.mp3" "/music/track2.mp3" "/music/track3.mp3")))
+ (cj/music-consume-mode t)
+ (kill-position nil)
+ (expected-pos nil))
+ ;; Move selected marker to second track
+ (with-current-buffer buf
+ (goto-char (point-min))
+ (forward-line 1)
+ (set-marker emms-playlist-selected-marker (point))
+ (setq expected-pos (marker-position emms-playlist-selected-marker)))
+ (cl-letf (((symbol-function 'emms-playlist-mode-kill-track)
+ (lambda () (setq kill-position (point)))))
+ (cj/music--consume-track)
+ (should (= kill-position expected-pos))))
+ (test-consume--teardown)))
+
+;;; Boundary Cases
+
+(ert-deftest test-music-config-toggle-consume-boundary-double-toggle-restores-state ()
+ "Validate toggling twice returns to original off state."
+ (unwind-protect
+ (progn
+ (setq cj/music-consume-mode nil)
+ (cj/music-toggle-consume)
+ (cj/music-toggle-consume)
+ (should (eq cj/music-consume-mode nil))
+ (should-not (memq #'cj/music--consume-track emms-player-finished-hook)))
+ (test-consume--teardown)))
+
+(ert-deftest test-music-config--consume-track-boundary-noop-when-disabled ()
+ "Validate consume-track does nothing when consume mode is off."
+ (unwind-protect
+ (let* ((cj/music-playlist-buffer-name "*EMMS-Test-Playlist*")
+ (buf (test-consume--setup-playlist-buffer
+ '("/music/track1.mp3" "/music/track2.mp3")))
+ (cj/music-consume-mode nil))
+ (cj/music--consume-track)
+ (should (= 2 (test-consume--track-count buf))))
+ (test-consume--teardown)))
+
+(ert-deftest test-music-config--consume-track-boundary-single-track-calls-kill ()
+ "Validate consuming the only track in playlist still calls kill-track."
+ (unwind-protect
+ (let* ((cj/music-playlist-buffer-name "*EMMS-Test-Playlist*")
+ (buf (test-consume--setup-playlist-buffer
+ '("/music/only-track.mp3")))
+ (cj/music-consume-mode t)
+ (kill-called nil))
+ (cl-letf (((symbol-function 'emms-playlist-mode-kill-track)
+ (lambda () (setq kill-called t))))
+ (cj/music--consume-track)
+ (should kill-called)))
+ (test-consume--teardown)))
+
+;;; Error Cases
+
+(ert-deftest test-music-config--consume-track-error-no-marker-no-crash ()
+ "Validate consume-track handles nil selected marker without error."
+ (unwind-protect
+ (let* ((cj/music-playlist-buffer-name "*EMMS-Test-Playlist*")
+ (buf (test-consume--setup-playlist-buffer
+ '("/music/track1.mp3")))
+ (cj/music-consume-mode t))
+ ;; Clear the selected marker
+ (with-current-buffer buf
+ (setq emms-playlist-selected-marker nil))
+ ;; Should not error
+ (should-not (condition-case err
+ (progn (cj/music--consume-track) nil)
+ (error err))))
+ (test-consume--teardown)))
+
+(ert-deftest test-music-config--consume-track-error-empty-playlist-no-crash ()
+ "Validate consume-track handles empty playlist buffer without error."
+ (unwind-protect
+ (let* ((cj/music-playlist-buffer-name "*EMMS-Test-Playlist*")
+ (buf (get-buffer-create "*EMMS-Test-Playlist*"))
+ (cj/music-consume-mode t))
+ (with-current-buffer buf
+ (emms-playlist-mode)
+ (setq emms-playlist-buffer-p t)
+ (setq emms-playlist-selected-marker nil))
+ ;; Should not error
+ (should-not (condition-case err
+ (progn (cj/music--consume-track) nil)
+ (error err))))
+ (test-consume--teardown)))
+
+(provide 'test-music-config-toggle-consume)
+;;; test-music-config-toggle-consume.el ends here