summaryrefslogtreecommitdiff
path: root/tests/test-music-config--append-track-to-m3u-file.el
blob: 2bf3e87dec094095dc700257b1e0e6d09799d61d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
;;; test-music-config--append-track-to-m3u-file.el --- Tests for appending tracks to M3U files -*- coding: utf-8; lexical-binding: t; -*-
;;
;; Author: Craig Jennings <c@cjennings.net>
;;
;;; Commentary:
;; Unit tests for cj/music--append-track-to-m3u-file function.
;; Tests the pure, deterministic helper that appends track paths to M3U files.
;;
;; Test organization:
;; - Normal Cases: Standard append operations
;; - Boundary Cases: Edge conditions (unicode, long paths, special chars)
;; - Error Cases: File errors (missing, read-only, directory instead of file)
;;
;;; Code:

(require 'ert)
(require 'testutil-general)

;; Stub missing dependencies before loading music-config
(defvar-keymap cj/custom-keymap
  :doc "Stub keymap for testing")

;; Load production code
(require 'music-config)

;;; Setup & Teardown

(defun test-music-config--append-track-to-m3u-file-setup ()
  "Setup test environment."
  (cj/create-test-base-dir))

(defun test-music-config--append-track-to-m3u-file-teardown ()
  "Clean up test environment."
  (cj/delete-test-base-dir))

;;; Normal Cases

(ert-deftest test-music-config--append-track-to-m3u-file-normal-empty-file-appends-track ()
  "Append to brand new empty M3U file."
  (test-music-config--append-track-to-m3u-file-setup)
  (unwind-protect
      (let* ((m3u-file (cj/create-temp-test-file "test-playlist-"))
             (track-path "/home/user/music/artist/song.mp3"))
        (cj/music--append-track-to-m3u-file track-path m3u-file)
        (with-temp-buffer
          (insert-file-contents m3u-file)
          (should (string= (buffer-string) (concat track-path "\n")))))
    (test-music-config--append-track-to-m3u-file-teardown)))

(ert-deftest test-music-config--append-track-to-m3u-file-normal-existing-with-newline-appends-track ()
  "Append to file with existing content ending with newline."
  (test-music-config--append-track-to-m3u-file-setup)
  (unwind-protect
      (let* ((existing-content "/home/user/music/first.mp3\n")
             (m3u-file (cj/create-temp-test-file-with-content existing-content "test-playlist-"))
             (track-path "/home/user/music/second.mp3"))
        (cj/music--append-track-to-m3u-file track-path m3u-file)
        (with-temp-buffer
          (insert-file-contents m3u-file)
          (should (string= (buffer-string)
                          (concat existing-content track-path "\n")))))
    (test-music-config--append-track-to-m3u-file-teardown)))

(ert-deftest test-music-config--append-track-to-m3u-file-normal-existing-without-newline-appends-track ()
  "Append to file without trailing newline adds leading newline."
  (test-music-config--append-track-to-m3u-file-setup)
  (unwind-protect
      (let* ((existing-content "/home/user/music/first.mp3")
             (m3u-file (cj/create-temp-test-file-with-content existing-content "test-playlist-"))
             (track-path "/home/user/music/second.mp3"))
        (cj/music--append-track-to-m3u-file track-path m3u-file)
        (with-temp-buffer
          (insert-file-contents m3u-file)
          (should (string= (buffer-string)
                          (concat existing-content "\n" track-path "\n")))))
    (test-music-config--append-track-to-m3u-file-teardown)))

(ert-deftest test-music-config--append-track-to-m3u-file-normal-multiple-appends-all-succeed ()
  "Multiple appends to same file all succeed (allows duplicates)."
  (test-music-config--append-track-to-m3u-file-setup)
  (unwind-protect
      (let* ((m3u-file (cj/create-temp-test-file "test-playlist-"))
             (track1 "/home/user/music/track1.mp3")
             (track2 "/home/user/music/track2.mp3")
             (track1-duplicate "/home/user/music/track1.mp3"))
        (cj/music--append-track-to-m3u-file track1 m3u-file)
        (cj/music--append-track-to-m3u-file track2 m3u-file)
        (cj/music--append-track-to-m3u-file track1-duplicate m3u-file)
        (with-temp-buffer
          (insert-file-contents m3u-file)
          (let ((content (buffer-string)))
            (should (string= content
                            (concat track1 "\n" track2 "\n" track1-duplicate "\n"))))))
    (test-music-config--append-track-to-m3u-file-teardown)))

;;; Boundary Cases

(ert-deftest test-music-config--append-track-to-m3u-file-boundary-very-long-path-appends-successfully ()
  "Append very long track path without truncation."
  (test-music-config--append-track-to-m3u-file-setup)
  (unwind-protect
      (let* ((m3u-file (cj/create-temp-test-file "test-playlist-"))
             ;; Create a path that's ~500 chars long
             (track-path (concat "/home/user/music/"
                                (make-string 450 ?a)
                                "/song.mp3")))
        (cj/music--append-track-to-m3u-file track-path m3u-file)
        (with-temp-buffer
          (insert-file-contents m3u-file)
          (should (string= (buffer-string) (concat track-path "\n")))
          (should (= (length (buffer-string)) (1+ (length track-path))))))
    (test-music-config--append-track-to-m3u-file-teardown)))

(ert-deftest test-music-config--append-track-to-m3u-file-boundary-path-with-unicode-appends-successfully ()
  "Append path with unicode characters preserves UTF-8 encoding."
  (test-music-config--append-track-to-m3u-file-setup)
  (unwind-protect
      (let* ((m3u-file (cj/create-temp-test-file "test-playlist-"))
             (track-path "/home/user/music/中文/artist-名前/song🎵.mp3"))
        (cj/music--append-track-to-m3u-file track-path m3u-file)
        (with-temp-buffer
          (insert-file-contents m3u-file)
          (should (string= (buffer-string) (concat track-path "\n")))))
    (test-music-config--append-track-to-m3u-file-teardown)))

(ert-deftest test-music-config--append-track-to-m3u-file-boundary-path-with-spaces-appends-successfully ()
  "Append path with spaces and special characters."
  (test-music-config--append-track-to-m3u-file-setup)
  (unwind-protect
      (let* ((m3u-file (cj/create-temp-test-file "test-playlist-"))
             (track-path "/home/user/music/Artist Name/Album (2024)/01 - Song's Title [Remix].mp3"))
        (cj/music--append-track-to-m3u-file track-path m3u-file)
        (with-temp-buffer
          (insert-file-contents m3u-file)
          (should (string= (buffer-string) (concat track-path "\n")))))
    (test-music-config--append-track-to-m3u-file-teardown)))

(ert-deftest test-music-config--append-track-to-m3u-file-boundary-m3u-with-comments-appends-after ()
  "Append to M3U file containing comments and metadata."
  (test-music-config--append-track-to-m3u-file-setup)
  (unwind-protect
      (let* ((existing-content "#EXTM3U\n#EXTINF:-1,Radio Station\nhttp://stream.url/radio\n")
             (m3u-file (cj/create-temp-test-file-with-content existing-content "test-playlist-"))
             (track-path "/home/user/music/local-track.mp3"))
        (cj/music--append-track-to-m3u-file track-path m3u-file)
        (with-temp-buffer
          (insert-file-contents m3u-file)
          (should (string= (buffer-string)
                          (concat existing-content track-path "\n")))))
    (test-music-config--append-track-to-m3u-file-teardown)))

;;; Error Cases

(ert-deftest test-music-config--append-track-to-m3u-file-error-nonexistent-file-signals-error ()
  "Signal error when M3U file doesn't exist."
  (test-music-config--append-track-to-m3u-file-setup)
  (unwind-protect
      (let* ((m3u-file "/nonexistent/path/to/playlist.m3u")
             (track-path "/home/user/music/song.mp3"))
        (should-error (cj/music--append-track-to-m3u-file track-path m3u-file)
                     :type 'error))
    (test-music-config--append-track-to-m3u-file-teardown)))

(ert-deftest test-music-config--append-track-to-m3u-file-error-readonly-file-signals-error ()
  "Signal error when M3U file is read-only."
  (test-music-config--append-track-to-m3u-file-setup)
  (unwind-protect
      (let* ((m3u-file (cj/create-temp-test-file "test-playlist-"))
             (track-path "/home/user/music/song.mp3"))
        ;; Make file read-only
        (set-file-modes m3u-file #o444)
        (should-error (cj/music--append-track-to-m3u-file track-path m3u-file)
                     :type 'error))
    (test-music-config--append-track-to-m3u-file-teardown)))

(ert-deftest test-music-config--append-track-to-m3u-file-error-directory-not-file-signals-error ()
  "Signal error when path points to directory instead of file."
  (test-music-config--append-track-to-m3u-file-setup)
  (unwind-protect
      (let* ((m3u-dir (cj/create-test-subdirectory "test-playlist-dir"))
             (track-path "/home/user/music/song.mp3"))
        (should-error (cj/music--append-track-to-m3u-file track-path m3u-dir)
                     :type 'error))
    (test-music-config--append-track-to-m3u-file-teardown)))

(provide 'test-music-config--append-track-to-m3u-file)
;;; test-music-config--append-track-to-m3u-file.el ends here