Files
MusicPlayer/tests/unit/test_upload_folder.gd
SansanandClaude Opus 5.5 c86dacb890
Build / test (pull_request) Successful in 2m3s
Build / build (pull_request) Skipped
Add a soundtrack by picking or dropping a folder (#50)
"Add soundtrack…" in the statistics toolbar, or a folder dropped anywhere on
the window, opens a popup that lists the folder's songs and uploads them to
the server, which adds the soundtrack at once (Sansan/MusicServer#33).

- The name comes from the folder and can be changed; characters the server
  refuses are replaced.
- Only MP3s are uploaded, as the server takes no other; other song files
  are listed greyed out as left out. Hidden files are skipped.
- Disc folders are flattened with the disc's number first ("1-01 Title.mp3"),
  as the server wants one folder. With one disc folder, names stay as
  they are.
- Songs are sent one at a time with a progress bar, reading one file at a
  time. A failed song is sent again twice, then the upload is cancelled
  and the server's reason shown. Closing the popup cancels it too.
- Not offered while the server syncs, offline, or without MP3s.
- Afterwards search, the inspiration list and the statistics reload.

The API types come from the server's spec (Upload, UploadFile,
UploadRequest).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 21:43:59 +02:00

123 lines
3.7 KiB
GDScript

extends GdUnitTestSuite
## Reading a folder to upload as a soundtrack: only MP3s are sent, disc folders are
## flattened with the disc's number first, and other song files are listed as left out.
var _dir: String
func before_test() -> void:
_dir = create_temp_dir("upload_folder_%d" % randi()).path_join("Chrono Trigger")
DirAccess.make_dir_recursive_absolute(_dir)
func _file(relative: String, size: int = 10) -> void:
var path: String = _dir.path_join(relative)
DirAccess.make_dir_recursive_absolute(path.get_base_dir())
var bytes: PackedByteArray = PackedByteArray()
bytes.resize(size)
var file: FileAccess = FileAccess.open(path, FileAccess.WRITE)
file.store_buffer(bytes)
file.close()
func _names(folder: UploadFolder) -> Array:
return folder.songs.map(func(song: Dictionary) -> String: return song.name)
func test_name_comes_from_the_folder() -> void:
_file("01 Title.mp3")
assert_str(UploadFolder.scan(_dir).suggested_name).is_equal("Chrono Trigger")
assert_str(UploadFolder.scan(_dir + "/").suggested_name).is_equal("Chrono Trigger")
func test_songs_in_natural_order_with_sizes() -> void:
_file("10 Ending.mp3", 300)
_file("2 Peaceful Days.MP3", 200)
_file("1 Title.mp3", 100)
var folder: UploadFolder = UploadFolder.scan(_dir)
assert_array(_names(folder)).contains_exactly(
["1 Title.mp3", "2 Peaceful Days.MP3", "10 Ending.mp3"])
assert_int(folder.songs[0].size).is_equal(100)
assert_int(folder.total_size).is_equal(600)
assert_str(folder.songs[0].path).is_equal(_dir.path_join("1 Title.mp3"))
func test_disc_folders_are_flattened_with_their_number() -> void:
_file("Disc 10/01 Last.mp3")
_file("Disc 2/01 Middle.mp3")
_file("Disc 1/01 First.mp3")
_file("Disc 1/Bonus/01 Extra.mp3")
var folder: UploadFolder = UploadFolder.scan(_dir)
assert_array(_names(folder)).contains_exactly(
["1-01 First.mp3", "1-01 Extra.mp3", "2-01 Middle.mp3", "3-01 Last.mp3"])
func test_one_disc_folder_keeps_the_names() -> void:
_file("CD1/01 Title.mp3")
_file("CD1/02 Theme.mp3")
_file("Scans/cover.jpg")
assert_array(_names(UploadFolder.scan(_dir))).contains_exactly(
["01 Title.mp3", "02 Theme.mp3"])
func test_folders_without_songs_are_not_discs() -> void:
_file("Artwork/front.jpg")
_file("Disc 1/01 First.mp3")
_file("Disc 2/01 Second.mp3")
assert_array(_names(UploadFolder.scan(_dir))).contains_exactly(
["1-01 First.mp3", "2-01 Second.mp3"])
func test_other_songs_are_listed_as_left_out_and_the_rest_ignored() -> void:
_file("01 Title.mp3")
_file("02 Theme.flac")
_file("Disc 2/01 Song.ogg")
_file("cover.jpg")
_file("notes.txt")
var folder: UploadFolder = UploadFolder.scan(_dir)
assert_array(_names(folder)).contains_exactly(["01 Title.mp3"])
assert_array(folder.skipped).contains_exactly(["02 Theme.flac", "Disc 2/01 Song.ogg"])
func test_hidden_files_and_folders_are_left_out() -> void:
_file("01 Title.mp3")
_file("._01 Title.mp3")
_file(".sync/01 Copy.mp3")
assert_array(_names(UploadFolder.scan(_dir))).contains_exactly(["01 Title.mp3"])
func test_same_name_twice_gets_a_number() -> void:
_file("Disc 1/01 Title.mp3")
_file("Disc 1/Extra/01 Title.mp3")
assert_array(_names(UploadFolder.scan(_dir))).contains_exactly(
["01 Title.mp3", "01 Title (2).mp3"])
func test_empty_folder_has_no_songs() -> void:
_file("cover.jpg")
assert_bool(UploadFolder.scan(_dir).has_songs()).is_false()
@warning_ignore("unused_parameter")
func test_clean_name(name: String, cleaned: String, test_parameters: Array = [
["Chrono Trigger", "Chrono Trigger"],
["Re:Zero", "Re-Zero"],
["What? <Live>", "What- -Live-"],
["AC/DC", "AC-DC"],
[" .hidden name. ", "hidden name"],
["クロノ・トリガー", "クロノ・トリガー"],
]) -> void:
assert_str(UploadFolder.clean_name(name)).is_equal(cleaned)