From fdc63ea6675709d6ca42362b1be1918f8bf24478 Mon Sep 17 00:00:00 2001 From: Craig Jennings Date: Wed, 1 Apr 2026 08:13:33 -0500 Subject: feat(music): add test coverage for 7 functions, refactor with extracted helpers Add 47 new unit tests across 7 test files covering playlist-modified-p, assert-valid-playlist-file, playlist-tracks, create-radio-station, ensure-playlist-buffer, after-playlist-clear, and header-text. Extract three helpers to reduce duplication: assert-m3u-files-exist (dedupes 2 identical guards), sync-playlist-file (dedupes 3 state-sync patterns), and select-m3u-file (reusable M3U selection with cancel). Simplify append-track-to-playlist nesting from 6 to 4 levels. Delete unused cj/music-keymap-prefix variable. --- tests/test-music-config--playlist-modified-p.el | 145 ++++++++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100644 tests/test-music-config--playlist-modified-p.el (limited to 'tests/test-music-config--playlist-modified-p.el') diff --git a/tests/test-music-config--playlist-modified-p.el b/tests/test-music-config--playlist-modified-p.el new file mode 100644 index 00000000..cbfc71e3 --- /dev/null +++ b/tests/test-music-config--playlist-modified-p.el @@ -0,0 +1,145 @@ +;;; test-music-config--playlist-modified-p.el --- Tests for playlist modification detection -*- coding: utf-8; lexical-binding: t; -*- +;; +;; Author: Craig Jennings +;; +;;; Commentary: +;; Unit tests for cj/music--playlist-modified-p function. +;; Tests the logic that compares the current EMMS playlist buffer +;; against its associated M3U file to detect unsaved changes. +;; +;; Test organization: +;; - Normal Cases: Matching/differing track lists +;; - Boundary Cases: Empty playlists, nil file, ordering +;; - Error Cases: Missing M3U file, missing buffer +;; +;;; 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))) + +(require 'emms) +(require 'emms-playlist-mode) +(require 'music-config) + +;;; Test helpers + +(defun test-modified--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." + (let ((buf (get-buffer-create cj/music-playlist-buffer-name))) + (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))))) + (setq emms-playlist-buffer buf) + buf)) + +(defun test-modified--teardown () + "Clean up test playlist buffer." + (when-let ((buf (get-buffer cj/music-playlist-buffer-name))) + (kill-buffer buf))) + +;;; Normal Cases + +(ert-deftest test-music-config--playlist-modified-p-normal-identical-tracks-returns-nil () + "Identical track lists in file and playlist returns nil (not modified)." + (unwind-protect + (let* ((tracks '("/music/a.mp3" "/music/b.mp3" "/music/c.mp3")) + (cj/music-playlist-file "/fake/playlist.m3u")) + (test-modified--setup-playlist-buffer tracks) + (cl-letf (((symbol-function 'cj/music--m3u-file-tracks) + (lambda (_file) tracks))) + (should-not (cj/music--playlist-modified-p)))) + (test-modified--teardown))) + +(ert-deftest test-music-config--playlist-modified-p-normal-different-tracks-returns-non-nil () + "Different track lists returns non-nil (modified)." + (unwind-protect + (let* ((file-tracks '("/music/a.mp3" "/music/b.mp3")) + (buf-tracks '("/music/a.mp3" "/music/c.mp3")) + (cj/music-playlist-file "/fake/playlist.m3u")) + (test-modified--setup-playlist-buffer buf-tracks) + (cl-letf (((symbol-function 'cj/music--m3u-file-tracks) + (lambda (_file) file-tracks))) + (should (cj/music--playlist-modified-p)))) + (test-modified--teardown))) + +(ert-deftest test-music-config--playlist-modified-p-normal-extra-track-returns-non-nil () + "Playlist with extra track compared to file returns non-nil." + (unwind-protect + (let* ((file-tracks '("/music/a.mp3" "/music/b.mp3")) + (buf-tracks '("/music/a.mp3" "/music/b.mp3" "/music/c.mp3")) + (cj/music-playlist-file "/fake/playlist.m3u")) + (test-modified--setup-playlist-buffer buf-tracks) + (cl-letf (((symbol-function 'cj/music--m3u-file-tracks) + (lambda (_file) file-tracks))) + (should (cj/music--playlist-modified-p)))) + (test-modified--teardown))) + +;;; Boundary Cases + +(ert-deftest test-music-config--playlist-modified-p-boundary-no-playlist-file-returns-nil () + "Nil cj/music-playlist-file returns nil without comparing tracks." + (unwind-protect + (let ((cj/music-playlist-file nil)) + (test-modified--setup-playlist-buffer '("/music/a.mp3")) + (should-not (cj/music--playlist-modified-p))) + (test-modified--teardown))) + +(ert-deftest test-music-config--playlist-modified-p-boundary-empty-file-and-playlist-returns-nil () + "Both empty file tracks and empty playlist returns nil." + (unwind-protect + (let ((cj/music-playlist-file "/fake/playlist.m3u")) + (test-modified--setup-playlist-buffer '()) + (cl-letf (((symbol-function 'cj/music--m3u-file-tracks) + (lambda (_file) nil))) + (should-not (cj/music--playlist-modified-p)))) + (test-modified--teardown))) + +(ert-deftest test-music-config--playlist-modified-p-boundary-order-difference-returns-non-nil () + "Same tracks in different order returns non-nil." + (unwind-protect + (let* ((file-tracks '("/music/a.mp3" "/music/b.mp3")) + (buf-tracks '("/music/b.mp3" "/music/a.mp3")) + (cj/music-playlist-file "/fake/playlist.m3u")) + (test-modified--setup-playlist-buffer buf-tracks) + (cl-letf (((symbol-function 'cj/music--m3u-file-tracks) + (lambda (_file) file-tracks))) + (should (cj/music--playlist-modified-p)))) + (test-modified--teardown))) + +;;; Error Cases + +(ert-deftest test-music-config--playlist-modified-p-error-missing-m3u-file-returns-nil () + "When M3U file doesn't exist, m3u-file-tracks returns nil; empty playlist matches." + (unwind-protect + (let ((cj/music-playlist-file "/nonexistent/playlist.m3u")) + (test-modified--setup-playlist-buffer '()) + ;; cj/music--m3u-file-tracks returns nil for nonexistent files + ;; empty playlist also returns nil, so equal = not modified + (should-not (cj/music--playlist-modified-p))) + (test-modified--teardown))) + +(ert-deftest test-music-config--playlist-modified-p-error-missing-m3u-with-tracks-returns-non-nil () + "When M3U file doesn't exist but playlist has tracks, returns non-nil." + (unwind-protect + (let ((cj/music-playlist-file "/nonexistent/playlist.m3u")) + (test-modified--setup-playlist-buffer '("/music/a.mp3")) + ;; m3u-file-tracks returns nil, but playlist has tracks = modified + (should (cj/music--playlist-modified-p))) + (test-modified--teardown))) + +(provide 'test-music-config--playlist-modified-p) +;;; test-music-config--playlist-modified-p.el ends here -- cgit v1.2.3