Add working tests, fix bugs, rework popups and automate releases #36

Merged
Sansan merged 34 commits from feature/adding_tests into develop 2026-10-06 22:14:44 +02:00
32 changed files with 822 additions and 300 deletions
Showing only changes of commit e94c75e84d - Show all commits

No files matched your search

+15 -6
View File
@@ -1,14 +1,16 @@
class_name MusicPlayer
extends PanelContainer
extends Control
## The bar that plays the songs, with play, restart, progress and volume, and the
## auto repeat switch under it.
# LOC 20/1-24: 136
# LOC 12/6-24: 170
@onready
var play_button: TextureButton = $MusicPlayerContainer/PlayTextureButton
var play_button: TextureButton = $PlayerBar/MusicPlayerContainer/PlayTextureButton
@onready
var restart_button: TextureButton = $MusicPlayerContainer/RestartTextureButton
var restart_button: TextureButton = $PlayerBar/MusicPlayerContainer/RestartTextureButton
@onready
var audio_player: AudioStreamPlayer = $AudioStreamPlayer
@@ -20,13 +22,16 @@ var sound_player: AudioStreamPlayer = $AudioSoundPlayer
var sound_effect_player: AudioStreamPlayer = $AudioSoundEffectPlayer
@onready
var progress_slider: HSlider = $MusicPlayerContainer/MusicPlayerSlider
var progress_slider: HSlider = $PlayerBar/MusicPlayerContainer/MusicPlayerSlider
@onready
var volume_slider: HSlider = $MusicPlayerContainer/VolumeSlider
var volume_slider: HSlider = $PlayerBar/MusicPlayerContainer/VolumeSlider
@onready
var music_time_label: Label = $MusicPlayerContainer/MusicTimeLabel
var music_time_label: Label = $PlayerBar/MusicPlayerContainer/MusicTimeLabel
@onready
var auto_repeat_button: CheckButton = $RepeatSongCheckButton
var play_icon: Texture = preload("res://icons/play_icon_light.svg")
var pause_icon: Texture = preload("res://icons/pause_icon_light.svg")
@@ -49,6 +54,10 @@ func _ready() -> void:
progress_slider.drag_ended.connect(_on_drag_ended)
audio_player.finished.connect(_on_finished)
auto_repeat_button.pressed.connect(_on_auto_repeat_pressed)
func _on_auto_repeat_pressed() -> void:
Settings.auto_repeat_song = !Settings.auto_repeat_song
func _process(_delta: float) -> void:
if audio_player.has_stream_playback() && !is_changing && !audio_player.stream_paused:
+27 -19
View File
@@ -26,19 +26,11 @@ unicode = 90
[sub_resource type="Shortcut" id="Shortcut_i7swq"]
events = [SubResource("InputEventKey_2nvce")]
[node name="MusicPlayer" type="PanelContainer"]
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -121.0
offset_top = -15.5
offset_right = 121.0
offset_bottom = 16.5
grow_horizontal = 2
grow_vertical = 2
size_flags_vertical = 4
[node name="MusicPlayer" type="Control"]
layout_mode = 3
offset_right = 1792.0
offset_bottom = 71.0
mouse_filter = 2
script = ExtResource("1_t24ra")
[node name="AudioStreamPlayer" type="AudioStreamPlayer" parent="."]
@@ -53,11 +45,17 @@ bus = &"music"
stream = ExtResource("2_xti80")
bus = &"music"
[node name="MusicPlayerContainer" type="HBoxContainer" parent="."]
[node name="PlayerBar" type="PanelContainer" parent="."]
layout_mode = 0
offset_left = 1.0
offset_right = 1792.0
offset_bottom = 32.0
[node name="MusicPlayerContainer" type="HBoxContainer" parent="PlayerBar"]
layout_mode = 2
size_flags_vertical = 4
[node name="PlayTextureButton" type="TextureButton" parent="MusicPlayerContainer"]
[node name="PlayTextureButton" type="TextureButton" parent="PlayerBar/MusicPlayerContainer"]
custom_minimum_size = Vector2(32, 32)
layout_mode = 2
focus_mode = 0
@@ -67,7 +65,7 @@ texture_normal = ExtResource("3_6g308")
ignore_texture_size = true
stretch_mode = 0
[node name="RestartTextureButton" type="TextureButton" parent="MusicPlayerContainer"]
[node name="RestartTextureButton" type="TextureButton" parent="PlayerBar/MusicPlayerContainer"]
custom_minimum_size = Vector2(32, 32)
layout_mode = 2
focus_mode = 0
@@ -78,17 +76,27 @@ ignore_texture_size = true
stretch_mode = 0
flip_h = true
[node name="MusicPlayerSlider" type="HSlider" parent="MusicPlayerContainer"]
[node name="MusicPlayerSlider" type="HSlider" parent="PlayerBar/MusicPlayerContainer"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 4
scrollable = false
[node name="MusicTimeLabel" type="Label" parent="MusicPlayerContainer"]
[node name="MusicTimeLabel" type="Label" parent="PlayerBar/MusicPlayerContainer"]
layout_mode = 2
text = "0:00 / 0:00"
[node name="VolumeSlider" parent="MusicPlayerContainer" instance=ExtResource("5_iifuj")]
[node name="VolumeSlider" parent="PlayerBar/MusicPlayerContainer" instance=ExtResource("5_iifuj")]
custom_minimum_size = Vector2(100, 0)
layout_mode = 2
size_flags_vertical = 4
[node name="RepeatSongCheckButton" type="CheckButton" parent="."]
layout_mode = 0
offset_top = 40.0
offset_right = 144.0
offset_bottom = 71.0
focus_mode = 0
button_pressed = true
action_mode = 0
text = "Auto repeat"
+29
View File
@@ -0,0 +1,29 @@
[gd_scene format=3]
[ext_resource type="Script" path="res://now_playing.gd" id="part_script"]
[sub_resource type="LabelSettings" id="LabelSettings_qspbu"]
font_size = 50
[sub_resource type="LabelSettings" id="LabelSettings_3m52w"]
font_size = 35
[node name="NowPlaying" type="VBoxContainer" unique_id=42468498]
layout_mode = 3
offset_right = 1088.0
offset_bottom = 320.0
script = ExtResource("part_script")
[node name="SoundtrackLabel" type="Label" parent="." unique_id=173147793]
custom_minimum_size = Vector2(1, 1)
layout_mode = 2
label_settings = SubResource("LabelSettings_qspbu")
horizontal_alignment = 1
autowrap_mode = 2
[node name="SongLabel" type="Label" parent="." unique_id=1687490027]
custom_minimum_size = Vector2(1, 1)
layout_mode = 2
label_settings = SubResource("LabelSettings_3m52w")
horizontal_alignment = 1
autowrap_mode = 2
+12 -110
View File
@@ -9,30 +9,10 @@
[ext_resource type="PackedScene" path="res://MusicListPanel.tscn" id="scene_MusicListPanel"]
[ext_resource type="PackedScene" path="res://PlayersPanel.tscn" id="scene_PlayersPanel"]
[ext_resource type="PackedScene" path="res://InspirationList.tscn" id="scene_InspirationList"]
[sub_resource type="LabelSettings" id="LabelSettings_qspbu"]
font_size = 50
[sub_resource type="LabelSettings" id="LabelSettings_3m52w"]
font_size = 35
[sub_resource type="InputEventKey" id="InputEventKey_06rl4"]
device = -1
alt_pressed = true
keycode = 86
unicode = 8249
[sub_resource type="Shortcut" id="Shortcut_a7fvb"]
events = [SubResource("InputEventKey_06rl4")]
[sub_resource type="InputEventKey" id="InputEventKey_7dnqw"]
device = -1
alt_pressed = true
keycode = 67
unicode = 231
[sub_resource type="Shortcut" id="Shortcut_d6fml"]
events = [SubResource("InputEventKey_7dnqw")]
[ext_resource type="PackedScene" path="res://QuizToolbar.tscn" id="scene_QuizToolbar"]
[ext_resource type="PackedScene" path="res://SongControls.tscn" id="scene_SongControls"]
[ext_resource type="PackedScene" path="res://NowPlaying.tscn" id="scene_NowPlaying"]
[ext_resource type="Script" path="res://song_loader.gd" id="song_loader_script"]
[node name="Quiz" type="Control" unique_id=1146705218]
layout_mode = 3
@@ -76,40 +56,19 @@ offset_top = 128.0
offset_right = 1857.0
offset_bottom = 512.0
[node name="VBoxContainer" type="VBoxContainer" parent="." unique_id=42468498]
[node name="NowPlaying" parent="." unique_id=42468498 instance=ExtResource("scene_NowPlaying")]
layout_mode = 0
offset_left = 64.0
offset_top = 64.0
offset_right = 1152.0
offset_bottom = 384.0
[node name="SoundtrackLabel" type="Label" parent="VBoxContainer" unique_id=173147793]
custom_minimum_size = Vector2(1, 1)
layout_mode = 2
label_settings = SubResource("LabelSettings_qspbu")
horizontal_alignment = 1
autowrap_mode = 2
[node name="SongLabel" type="Label" parent="VBoxContainer" unique_id=1687490027]
custom_minimum_size = Vector2(1, 1)
layout_mode = 2
label_settings = SubResource("LabelSettings_3m52w")
horizontal_alignment = 1
autowrap_mode = 2
[node name="MusicPlayer" parent="." unique_id=1990459832 instance=ExtResource("5_emn36")]
layout_mode = 0
anchors_preset = 0
anchor_left = 0.0
anchor_top = 0.0
anchor_right = 0.0
anchor_bottom = 0.0
offset_left = 65.0
offset_left = 64.0
offset_top = 992.0
offset_right = 1856.0
offset_bottom = 1024.0
grow_horizontal = 1
grow_vertical = 1
offset_bottom = 1063.0
[node name="PlayersPanel" parent="." unique_id=1708375945 instance=ExtResource("scene_PlayersPanel")]
layout_mode = 0
@@ -118,68 +77,19 @@ offset_top = 576.0
offset_right = 1857.0
offset_bottom = 977.0
[node name="ResetPlaylistButton" type="Button" parent="." unique_id=1754155620]
[node name="QuizToolbar" parent="." unique_id=1873301941 instance=ExtResource("scene_QuizToolbar")]
layout_mode = 0
offset_left = 1360.0
offset_top = 64.0
offset_right = 1471.0
offset_bottom = 95.0
focus_mode = 0
action_mode = 0
text = "Reset playlist"
[node name="ResetPointsButton" type="Button" parent="." unique_id=942880776]
layout_mode = 0
offset_left = 1488.0
offset_top = 64.0
offset_right = 1599.0
offset_bottom = 95.0
focus_mode = 0
action_mode = 0
text = "Reset points"
[node name="SoundTestButton" type="Button" parent="." unique_id=1332971879]
layout_mode = 0
offset_left = 1744.0
offset_top = 64.0
offset_right = 1855.0
offset_bottom = 95.0
focus_mode = 0
action_mode = 0
text = "Sound test"
[node name="ShowAnswerButton" type="Button" parent="." unique_id=415388039]
[node name="SongControls" parent="." unique_id=1640212773 instance=ExtResource("scene_SongControls")]
layout_mode = 0
offset_left = 65.0
offset_top = 944.0
offset_right = 176.0
offset_bottom = 975.0
focus_mode = 0
action_mode = 0
shortcut = SubResource("Shortcut_a7fvb")
text = "Show answer"
[node name="NextButton" type="Button" parent="." unique_id=1744703488]
layout_mode = 0
offset_left = 193.0
offset_top = 944.0
offset_right = 368.0
offset_bottom = 975.0
focus_mode = 0
disabled = true
action_mode = 0
shortcut = SubResource("Shortcut_d6fml")
text = "Randomize new track"
[node name="SettingsButton" type="Button" parent="." unique_id=1193400323]
layout_mode = 0
offset_left = 1616.0
offset_top = 64.0
offset_right = 1727.0
offset_bottom = 95.0
focus_mode = 0
action_mode = 0
text = "Settings"
[node name="SettingsWindow" parent="." unique_id=1323246978 instance=ExtResource("11_k62u5")]
visible = false
@@ -207,17 +117,6 @@ offset_top = 153.0
offset_right = 720.0
offset_bottom = 153.0
[node name="RepeatSongCheckButton" type="CheckButton" parent="." unique_id=776835053]
layout_mode = 0
offset_left = 64.0
offset_top = 1032.0
offset_right = 208.0
offset_bottom = 1063.0
focus_mode = 0
button_pressed = true
action_mode = 0
text = "Auto repeat"
[node name="DebugLabel" type="Label" parent="." unique_id=1959221257]
visible = false
layout_mode = 0
@@ -240,4 +139,7 @@ offset_top = 132.0
offset_right = 1262.0
offset_bottom = 832.0
[node name="SongLoader" type="Node" parent="." unique_id=1520867349]
script = ExtResource("song_loader_script")
[connection signal="dir_selected" from="FileDialog" to="." method="_on_file_dialog_dir_selected"]
+45
View File
@@ -0,0 +1,45 @@
[gd_scene format=3]
[ext_resource type="Script" path="res://quiz_toolbar.gd" id="part_script"]
[node name="QuizToolbar" type="Control" unique_id=1873301941]
layout_mode = 3
offset_right = 495.0
offset_bottom = 31.0
mouse_filter = 2
script = ExtResource("part_script")
[node name="ResetPlaylistButton" type="Button" parent="." unique_id=1754155620]
layout_mode = 0
offset_right = 111.0
offset_bottom = 31.0
focus_mode = 0
action_mode = 0
text = "Reset playlist"
[node name="ResetPointsButton" type="Button" parent="." unique_id=942880776]
layout_mode = 0
offset_left = 128.0
offset_right = 239.0
offset_bottom = 31.0
focus_mode = 0
action_mode = 0
text = "Reset points"
[node name="SettingsButton" type="Button" parent="." unique_id=1193400323]
layout_mode = 0
offset_left = 256.0
offset_right = 367.0
offset_bottom = 31.0
focus_mode = 0
action_mode = 0
text = "Settings"
[node name="SoundTestButton" type="Button" parent="." unique_id=1332971879]
layout_mode = 0
offset_left = 384.0
offset_right = 495.0
offset_bottom = 31.0
focus_mode = 0
action_mode = 0
text = "Sound test"
+48
View File
@@ -0,0 +1,48 @@
[gd_scene format=3]
[ext_resource type="Script" path="res://song_controls.gd" id="part_script"]
[sub_resource type="InputEventKey" id="InputEventKey_06rl4"]
device = -1
alt_pressed = true
keycode = 86
unicode = 8249
[sub_resource type="Shortcut" id="Shortcut_a7fvb"]
events = [SubResource("InputEventKey_06rl4")]
[sub_resource type="InputEventKey" id="InputEventKey_7dnqw"]
device = -1
alt_pressed = true
keycode = 67
unicode = 231
[sub_resource type="Shortcut" id="Shortcut_d6fml"]
events = [SubResource("InputEventKey_7dnqw")]
[node name="SongControls" type="Control" unique_id=1640212773]
layout_mode = 3
offset_right = 303.0
offset_bottom = 31.0
mouse_filter = 2
script = ExtResource("part_script")
[node name="ShowAnswerButton" type="Button" parent="." unique_id=415388039]
layout_mode = 0
offset_right = 111.0
offset_bottom = 31.0
focus_mode = 0
action_mode = 0
shortcut = SubResource("Shortcut_a7fvb")
text = "Show answer"
[node name="NextButton" type="Button" parent="." unique_id=1744703488]
layout_mode = 0
offset_left = 128.0
offset_right = 303.0
offset_bottom = 31.0
focus_mode = 0
disabled = true
action_mode = 0
shortcut = SubResource("Shortcut_d6fml")
text = "Randomize new track"
+10
View File
@@ -0,0 +1,10 @@
class_name NowPlaying
extends VBoxContainer
## The soundtrack and song that are playing, in large text at the top of the quiz.
## Shows [param soundtrack_title] and [param song_title]. The playlist hides them as
## question marks until the answer is shown.
func show_song(soundtrack_title: String, song_title: String) -> void:
$SoundtrackLabel.text = soundtrack_title
$SongLabel.text = song_title
+1
View File
@@ -0,0 +1 @@
uid://beoi75d4u568h
+29 -121
View File
@@ -21,25 +21,13 @@ var fileDialog: FileDialog = $FileDialog
var inspiration_list: InspirationList = $InspirationList
@onready
var soundtrack_label: Label = $VBoxContainer/SoundtrackLabel
@onready
var song_label: Label = $VBoxContainer/SongLabel
var now_playing: NowPlaying = $NowPlaying
@onready
var players_panel: PlayersPanel = $PlayersPanel
@onready
var reset_playlist_button: Button = $ResetPlaylistButton
@onready
var reset_points_button: Button = $ResetPointsButton
@onready
var sound_test_button: Button = $SoundTestButton
@onready
var settings_button: Button = $SettingsButton
var toolbar: QuizToolbar = $QuizToolbar
@onready
var settings_window: Control = $SettingsWindow
@@ -48,10 +36,10 @@ var settings_window: Control = $SettingsWindow
var winner_window: Control = %WinnerWindow
@onready
var show_answer_button: Button = $ShowAnswerButton
var song_controls: SongControls = $SongControls
@onready
var next_button: Button = $NextButton
var song_loader: SongLoader = $SongLoader
@onready
var music_list_panel: MusicListPanel = $MusicListPanel
@@ -60,10 +48,7 @@ var music_list_panel: MusicListPanel = $MusicListPanel
var new_character_select: Control = %NewCharacterSelect
@onready
var auto_repeat_song_button: CheckButton = $RepeatSongCheckButton
@onready
var music_player_container: PanelContainer = $MusicPlayer
var music_player_container: MusicPlayer = $MusicPlayer
@onready
var log_window: Control = %Log
@@ -80,9 +65,6 @@ var stats_paused_by_win: bool = false
## Dims the window behind an open popup and keeps clicks from reaching the buttons under it.
var popup_backdrop: PopupBackdrop
## Seconds to wait before retrying a song download step that failed.
var song_fetch_retry_seconds: float = 5.0
func _ready() -> void:
popup_backdrop = PopupBackdrop.add_to(self, _popups())
@@ -92,12 +74,14 @@ func _ready() -> void:
Settings.is_debug = true
settings_window.set_winning_score(5)
settings_window.set_selected_server(4)
next_button.pressed.connect(play_next_song)
show_answer_button.pressed.connect(show_answer_pressed)
sound_test_button.pressed.connect(music_player_container.get_sound_test_song)
settings_button.pressed.connect(show_settings)
reset_playlist_button.pressed.connect(reset_playlist)
reset_points_button.pressed.connect(reset_points)
song_controls.next_pressed.connect(play_next_song)
song_controls.show_answer_pressed.connect(show_answer_pressed)
toolbar.sound_test_pressed.connect(music_player_container.get_sound_test_song)
toolbar.settings_pressed.connect(show_settings)
toolbar.reset_playlist_pressed.connect(reset_playlist)
toolbar.reset_points_pressed.connect(reset_points)
song_loader.song_list_loaded.connect(update_song_list)
song_loader.song_added.connect(_on_song_added)
new_character_select.connect("new_new_character_selected", _on_new_character_selected)
music_list_panel.song_clicked.connect(song_object_clicked)
@@ -110,10 +94,9 @@ func _ready() -> void:
settings_window.visibility_changed.connect(_on_settings_window_visibility_changed)
open_button.pressed.connect(open)
auto_repeat_song_button.pressed.connect(repeat_song)
inspiration_list.load_from_server()
fetch_song_list_at_start()
song_loader.load_song_list()
new_character_select.load_characters()
@@ -140,18 +123,15 @@ func _input(event: InputEvent) -> void:
## Disables the buttons that use the song list while the server syncs it.
func lock_for_sync() -> void:
sound_test_button.disabled = true
reset_playlist_button.disabled = true
show_answer_button.disabled = true
next_button.disabled = true
toolbar.set_locked(true)
song_controls.set_locked(true)
## Reloads from the server after a sync and starts a new game.
func on_sync_finished() -> void:
refresh_from_server()
sound_test_button.disabled = false
reset_playlist_button.disabled = false
show_answer_button.disabled = false
toolbar.set_locked(false)
song_controls.set_locked(false)
reset_points()
@@ -170,41 +150,12 @@ func _on_server_changed() -> void:
server_changed.emit()
func repeat_song() -> void:
Settings.auto_repeat_song = !Settings.auto_repeat_song
func show_answer_pressed() -> void:
print("show_answer_pressed")
Playlist.song_is_answered()
update_song_list()
# Loads the songs already in the server playlist, then starts preloading new ones.
# Preloading waits for the list, because loading the list clears the playlist.
func fetch_song_list_at_start() -> void:
print("fetch_song_list_at_start")
var fetch_song_list_at_start_done: Callable = func fetch_songs_at_start_done(data) -> void:
Playlist.clear_song_list()
if typeof(data) == TYPE_ARRAY:
for d: Dictionary in data:
var new_song_object: SongObject = SongObject.new()
new_song_object.song_number = d.SongNo
new_song_object.soundtrack_title = d.Soundtrack
new_song_object.song_title = d.Song
new_song_object.is_answered = true
new_song_object.has_played = true
Playlist.add_to_playlist(new_song_object)
elif data != null:
print("Unexpected data")
Playlist.update_last_played_song()
update_song_list()
if !Playlist.is_song_downloading:
fetch_song()
MusicServerClient.fetch_song_list(fetch_song_list_at_start_done,
_retry_later.bind(fetch_song_list_at_start))
func play_next_song() -> void:
print("play_next_song ", Playlist.latest_played_song)
if Playlist.first_song_played:
@@ -212,9 +163,8 @@ func play_next_song() -> void:
Playlist.first_song_played = true
music_player_container.play_song_object(Playlist.latest_played_song)
if !(Playlist.get_preloaded_songs() > 0):
next_button.disabled = true
if !Playlist.is_song_downloading:
fetch_song()
song_controls.set_next_enabled(false)
song_loader.fetch_song_if_idle()
func _popups() -> Array[PopupWindow]:
@@ -224,63 +174,21 @@ func _popups() -> Array[PopupWindow]:
# The cache size can be changed in the settings. Waiting until the settings are closed
# avoids starting downloads while the user is still clicking the number up and down.
func _on_settings_window_visibility_changed() -> void:
if !settings_window.visible && !Playlist.is_song_downloading && Playlist.should_preload_more_songs():
fetch_song()
if !settings_window.visible:
song_loader.fill_cache()
func fetch_song() -> void:
print("fetch_song")
Playlist.is_song_downloading = true
_download_random_song(SongObject.new())
# Fetching a song takes three requests. If one fails, only that step is retried, so
# the server and the game keep the same songs in their playlists.
func _download_random_song(new_song_object: SongObject) -> void:
var song_downloaded: Callable = func song_downloaded(body: PackedByteArray) -> void:
print("song_downloaded")
var sound: AudioStream = AudioStreamMP3.new()
sound.data = body
new_song_object.song = sound
_add_song_to_queue(new_song_object)
MusicServerClient.fetch_random_song(Settings.use_low_played_mode, song_downloaded,
_retry_later.bind(_download_random_song.bind(new_song_object)))
func _add_song_to_queue(new_song_object: SongObject) -> void:
MusicServerClient.add_to_queue(_fetch_song_info.bind(new_song_object),
_retry_later.bind(_add_song_to_queue.bind(new_song_object)))
func _fetch_song_info(new_song_object: SongObject) -> void:
var song_info_fetched: Callable = func song_info_fetched(data_received: Dictionary) -> void:
print("song_info_fetched")
new_song_object.song_number = data_received.SongNo
new_song_object.soundtrack_title = data_received.Soundtrack
new_song_object.song_title = data_received.Song
print("new_song_object: ", new_song_object.to_string())
Playlist.add_to_playlist(new_song_object)
next_button.disabled = false
if Playlist.should_preload_more_songs():
fetch_song()
else:
Playlist.is_song_downloading = false
if Settings.is_debug:
update_song_list()
MusicServerClient.fetch_song_info(song_info_fetched,
_retry_later.bind(_fetch_song_info.bind(new_song_object)))
func _retry_later(step: Callable) -> void:
print("Song download failed, retrying in %s seconds" % song_fetch_retry_seconds)
get_tree().create_timer(song_fetch_retry_seconds).timeout.connect(step)
func _on_song_added() -> void:
song_controls.set_next_enabled(true)
if Settings.is_debug:
update_song_list()
func update_song_list() -> void:
print("update_song_list")
if Playlist.currently_playing_song >= 0:
soundtrack_label.text = Playlist.get_current_soundtrack_title()
song_label.text = Playlist.get_current_song_title()
now_playing.show_song(Playlist.get_current_soundtrack_title(),
Playlist.get_current_song_title())
music_list_panel.refresh()
@@ -304,7 +212,7 @@ func reset_playlist() -> void:
print("reset_playlist")
var playlist_reset_done: Callable = func playlist_reset_done() -> void:
Playlist.reset_playlist()
fetch_song_list_at_start()
song_loader.load_song_list()
MusicServerClient.reset_playlist(playlist_reset_done)
+25
View File
@@ -0,0 +1,25 @@
class_name QuizToolbar
extends Control
## The row of quiz buttons under the top bar: reset playlist, reset points, settings
## and sound test.
signal reset_playlist_pressed
signal reset_points_pressed
signal settings_pressed
signal sound_test_pressed
@onready var reset_playlist_button: Button = $ResetPlaylistButton
@onready var sound_test_button: Button = $SoundTestButton
func _ready() -> void:
reset_playlist_button.pressed.connect(reset_playlist_pressed.emit)
$ResetPointsButton.pressed.connect(reset_points_pressed.emit)
$SettingsButton.pressed.connect(settings_pressed.emit)
sound_test_button.pressed.connect(sound_test_pressed.emit)
## Disables the buttons that use the song list, e.g. while the server syncs it.
func set_locked(locked: bool) -> void:
reset_playlist_button.disabled = locked
sound_test_button.disabled = locked
+1
View File
@@ -0,0 +1 @@
uid://dmsy36s1ht7x5
+27
View File
@@ -0,0 +1,27 @@
class_name SongControls
extends Control
## The buttons for the song that is playing: show the answer, and go to the next song.
signal show_answer_pressed
signal next_pressed
@onready var show_answer_button: Button = $ShowAnswerButton
@onready var next_button: Button = $NextButton
func _ready() -> void:
show_answer_button.pressed.connect(show_answer_pressed.emit)
next_button.pressed.connect(next_pressed.emit)
## Enables Next while there is a downloaded song to play next.
func set_next_enabled(enabled: bool) -> void:
next_button.disabled = !enabled
## Disables both buttons, e.g. while the server syncs the song list. Unlocking enables
## Show answer again; Next comes back when the next song has been downloaded.
func set_locked(locked: bool) -> void:
show_answer_button.disabled = locked
if locked:
next_button.disabled = true
+1
View File
@@ -0,0 +1 @@
uid://d6nevh66yaw1
+100
View File
@@ -0,0 +1,100 @@
class_name SongLoader
extends Node
## Downloads songs from the server into the Playlist ahead of time, so the next song
## can start at once.
##
## How many songs it keeps ready is the cache size in the settings
## ([code]Playlist.number_of_tracks_to_preload[/code]).
## Emitted when the songs already in the server playlist have been loaded into the
## Playlist.
signal song_list_loaded
## Emitted when a new song has been downloaded and added to the Playlist.
signal song_added
## Seconds to wait before retrying a download step that failed.
var retry_seconds: float = 5.0
## Loads the songs already in the server playlist, then starts preloading new ones.
## Preloading waits for the list, because loading the list clears the playlist.
func load_song_list() -> void:
print("load_song_list")
var song_list_fetched: Callable = func song_list_fetched(data) -> void:
Playlist.clear_song_list()
if typeof(data) == TYPE_ARRAY:
for d: Dictionary in data:
var new_song_object: SongObject = SongObject.new()
new_song_object.song_number = d.SongNo
new_song_object.soundtrack_title = d.Soundtrack
new_song_object.song_title = d.Song
new_song_object.is_answered = true
new_song_object.has_played = true
Playlist.add_to_playlist(new_song_object)
elif data != null:
print("Unexpected data")
Playlist.update_last_played_song()
song_list_loaded.emit()
fetch_song_if_idle()
MusicServerClient.fetch_song_list(song_list_fetched, _retry_later.bind(load_song_list))
## Starts downloading one more song, which keeps downloading until the cache is full.
func fetch_song() -> void:
print("fetch_song")
Playlist.is_song_downloading = true
_download_random_song(SongObject.new())
## Starts downloading a song unless one is already downloading.
func fetch_song_if_idle() -> void:
if !Playlist.is_song_downloading:
fetch_song()
## Starts downloading if nothing is downloading and the cache is not full, e.g. after
## the cache size was raised.
func fill_cache() -> void:
if !Playlist.is_song_downloading && Playlist.should_preload_more_songs():
fetch_song()
# Fetching a song takes three requests. If one fails, only that step is retried, so
# the server and the game keep the same songs in their playlists.
func _download_random_song(new_song_object: SongObject) -> void:
var song_downloaded: Callable = func song_downloaded(body: PackedByteArray) -> void:
print("song_downloaded")
var sound: AudioStream = AudioStreamMP3.new()
sound.data = body
new_song_object.song = sound
_add_song_to_queue(new_song_object)
MusicServerClient.fetch_random_song(Settings.use_low_played_mode, song_downloaded,
_retry_later.bind(_download_random_song.bind(new_song_object)))
func _add_song_to_queue(new_song_object: SongObject) -> void:
MusicServerClient.add_to_queue(_fetch_song_info.bind(new_song_object),
_retry_later.bind(_add_song_to_queue.bind(new_song_object)))
func _fetch_song_info(new_song_object: SongObject) -> void:
var song_info_fetched: Callable = func song_info_fetched(data_received: Dictionary) -> void:
print("song_info_fetched")
new_song_object.song_number = data_received.SongNo
new_song_object.soundtrack_title = data_received.Soundtrack
new_song_object.song_title = data_received.Song
print("new_song_object: ", new_song_object.to_string())
Playlist.add_to_playlist(new_song_object)
if Playlist.should_preload_more_songs():
fetch_song()
else:
Playlist.is_song_downloading = false
song_added.emit()
MusicServerClient.fetch_song_info(song_info_fetched,
_retry_later.bind(_fetch_song_info.bind(new_song_object)))
func _retry_later(step: Callable) -> void:
print("Song download failed, retrying in %s seconds" % retry_seconds)
get_tree().create_timer(retry_seconds).timeout.connect(step)
+1
View File
@@ -0,0 +1 @@
uid://dwqirg7x7mmj6
+1 -1
View File
@@ -70,7 +70,7 @@ func _press(button_path: String) -> void:
## Presses Next and waits until the first song is playing.
func _play_first_song() -> void:
_press("NextButton")
_press("SongControls/NextButton")
await _runner.simulate_frames(2)
+13 -5
View File
@@ -12,16 +12,24 @@ const BASELINE_PATH: String = "res://tests/data/layout_baseline.json"
## Nodes that moved from the main window into the Quiz scene, by their old name.
const QUIZ_NODES: Array[String] = [
"Open", "FileDialog", "MusicListPanel", "VBoxContainer",
"MusicPlayer", "ResetPlaylistButton", "ResetPointsButton", "SettingsButton",
"SoundTestButton", "ShowAnswerButton", "NextButton", "RepeatSongCheckButton",
"DebugLabel", "SettingsWindow", "NewCharacterSelect", "WinnerWindow", "Log",
"Open", "FileDialog", "MusicListPanel", "DebugLabel", "SettingsWindow",
"NewCharacterSelect", "WinnerWindow", "Log",
]
## Nodes that moved into the Quiz scene and were renamed there: old name to new name.
## Nodes that moved into the Quiz scene under another path: old name to new path in
## the quiz.
const RENAMED_QUIZ_NODES: Dictionary = {
"Players": "PlayersPanel",
"ScrollContainer": "InspirationList",
"VBoxContainer": "NowPlaying",
"MusicPlayer": "MusicPlayer/PlayerBar",
"RepeatSongCheckButton": "MusicPlayer/RepeatSongCheckButton",
"ResetPlaylistButton": "QuizToolbar/ResetPlaylistButton",
"ResetPointsButton": "QuizToolbar/ResetPointsButton",
"SettingsButton": "QuizToolbar/SettingsButton",
"SoundTestButton": "QuizToolbar/SoundTestButton",
"ShowAnswerButton": "SongControls/ShowAnswerButton",
"NextButton": "SongControls/NextButton",
}
## Nodes that moved from the main window into the TopBar scene, by their old name.
+20 -20
View File
@@ -7,7 +7,7 @@ func test_startup_loads_soundtracks_and_preloads_a_song() -> void:
assert_int(_server.count("/music/all")).is_equal(1)
assert_array(_server.requested_paths).contains(["/music/rand", "/music/addQue", "/music/info"])
assert_int(Playlist.song_object_array.size()).is_equal(1)
assert_bool((_quiz.get_node("NextButton") as Button).disabled).is_false()
assert_bool((_quiz.get_node("SongControls/NextButton") as Button).disabled).is_false()
func test_low_played_mode_fetches_from_low_played_endpoint() -> void:
@@ -15,7 +15,7 @@ func test_low_played_mode_fetches_from_low_played_endpoint() -> void:
_server.respond_bytes(
"/music/rand/low", FileAccess.get_file_as_bytes("res://tests/data/short.mp3"))
_quiz.fetch_song()
_quiz.song_loader.fetch_song()
assert_int(_server.count("/music/rand/low")).is_equal(1)
@@ -23,7 +23,7 @@ func test_low_played_mode_fetches_from_low_played_endpoint() -> void:
func test_next_plays_preloaded_song_and_fetches_another() -> void:
_next_song_is(SONG_INFO[1])
_press("NextButton")
_press("SongControls/NextButton")
await _runner.simulate_frames(2)
assert_int(Playlist.currently_playing_song).is_equal(0)
@@ -33,23 +33,23 @@ func test_next_plays_preloaded_song_and_fetches_another() -> void:
func test_title_is_hidden_until_answer_is_shown() -> void:
var soundtrack_label: Label = _quiz.get_node("VBoxContainer/SoundtrackLabel")
_press("NextButton")
var soundtrack_label: Label = _quiz.get_node("NowPlaying/SoundtrackLabel")
_press("SongControls/NextButton")
await _runner.simulate_frames(2)
assert_str(soundtrack_label.text).is_equal("????????")
_press("ShowAnswerButton")
_press("SongControls/ShowAnswerButton")
assert_str(soundtrack_label.text).is_equal("Chrono Trigger")
assert_int(_server.count("/music/played?song=0")).is_equal(1)
func test_reset_playlist_clears_server_and_preloads_again() -> void:
_press("NextButton")
_press("SongControls/NextButton")
await _runner.simulate_frames(2)
_press("ResetPlaylistButton")
_press("QuizToolbar/ResetPlaylistButton")
assert_int(_server.count("/music/reset")).is_equal(1)
assert_int(Playlist.currently_playing_song).is_equal(-1)
@@ -61,7 +61,7 @@ func test_reset_points_sets_all_scores_to_zero() -> void:
_add_player("Crono").add_point()
_add_player("Marle").add_point()
_press("ResetPointsButton")
_press("QuizToolbar/ResetPointsButton")
assert_int(Settings.player_array[0].player_score).is_equal(0)
assert_int(Settings.player_array[1].player_score).is_equal(0)
@@ -81,13 +81,13 @@ func test_cannot_add_player_without_name() -> void:
func test_failed_song_download_is_retried_until_the_server_answers() -> void:
_quiz.song_fetch_retry_seconds = 0.05
_quiz.song_loader.retry_seconds = 0.05
var songs_before: int = Playlist.song_object_array.size()
var mp3: PackedByteArray = FileAccess.get_file_as_bytes("res://tests/data/short.mp3")
_server.respond_bytes("/music/rand", PackedByteArray(), 503)
var rand_requests_before: int = _server.count("/music/rand")
_quiz.fetch_song()
_quiz.song_loader.fetch_song()
await await_millis(200)
assert_int(_server.count("/music/rand") - rand_requests_before).is_greater(1)
@@ -102,13 +102,13 @@ func test_failed_song_download_is_retried_until_the_server_answers() -> void:
func test_failed_song_info_retries_only_that_step() -> void:
_quiz.song_fetch_retry_seconds = 0.05
_quiz.song_loader.retry_seconds = 0.05
var songs_before: int = Playlist.song_object_array.size()
_server.respond_bytes("/music/info", PackedByteArray(), 503)
var rand_before: int = _server.count("/music/rand")
var queue_before: int = _server.count("/music/addQue")
_quiz.fetch_song()
_quiz.song_loader.fetch_song()
await await_millis(200)
_next_song_is(SONG_INFO[1])
await await_millis(200)
@@ -124,7 +124,7 @@ func test_failed_song_info_retries_only_that_step() -> void:
func test_preloads_as_many_songs_as_the_cache_setting() -> void:
Playlist.number_of_tracks_to_preload = 3
_quiz.fetch_song()
_quiz.song_loader.fetch_song()
assert_int(Playlist.get_preloaded_songs()).is_equal(3)
assert_bool(Playlist.is_song_downloading).is_false()
@@ -132,7 +132,7 @@ func test_preloads_as_many_songs_as_the_cache_setting() -> void:
func test_playing_a_song_tops_the_cache_up_again() -> void:
Playlist.number_of_tracks_to_preload = 3
_quiz.fetch_song()
_quiz.song_loader.fetch_song()
await _play_first_song()
@@ -141,13 +141,13 @@ func test_playing_a_song_tops_the_cache_up_again() -> void:
func test_cache_keeps_filling_after_a_failed_download() -> void:
_quiz.song_fetch_retry_seconds = 0.05
_quiz.song_loader.retry_seconds = 0.05
Playlist.number_of_tracks_to_preload = 3
var mp3: PackedByteArray = FileAccess.get_file_as_bytes("res://tests/data/short.mp3")
# One more download works, then the server stops answering
_server.fail_after("/music/rand", 1)
_quiz.fetch_song()
_quiz.song_loader.fetch_song()
await await_millis(150)
assert_int(Playlist.get_preloaded_songs()).is_equal(2)
@@ -161,7 +161,7 @@ func test_cache_keeps_filling_after_a_failed_download() -> void:
func _open_settings() -> void:
_press("SettingsButton")
_press("QuizToolbar/SettingsButton")
func _close_settings() -> void:
@@ -217,14 +217,14 @@ func test_closing_settings_with_full_cache_downloads_nothing() -> void:
func test_show_answer_before_first_song_does_not_reveal_next_song() -> void:
_press("ShowAnswerButton")
_press("SongControls/ShowAnswerButton")
assert_bool(Playlist.song_object_array[0].is_answered).is_false()
assert_int(_server.count("/music/played?song=-1")).is_equal(0)
await _play_first_song()
assert_str((_quiz.get_node("VBoxContainer/SoundtrackLabel") as Label).text) \
assert_str((_quiz.get_node("NowPlaying/SoundtrackLabel") as Label).text) \
.is_equal("????????")
@@ -28,7 +28,7 @@ func test_next_plays_the_preloaded_song_after_a_slow_song_list() -> void:
func test_failed_song_list_is_retried_before_preloading() -> void:
_quiz.song_fetch_retry_seconds = 0.05
_quiz.song_loader.retry_seconds = 0.05
_server.respond_json("/music/list", [], 503)
_server.release("/music/list")
@@ -0,0 +1,50 @@
extends GdUnitTestSuite
## The music player scene: the player bar and the auto repeat switch under it.
var _snapshot: AutoloadSnapshot
var _runner: GdUnitSceneRunner
var _player: MusicPlayer
func before_test() -> void:
_snapshot = AutoloadSnapshot.take([Settings])
Settings.auto_repeat_song = true
_runner = scene_runner("res://MusicPlayer.tscn")
_player = _runner.scene()
func after_test() -> void:
_snapshot.restore()
func _click(control: Control) -> void:
_runner.set_mouse_position(_player.get_viewport().get_final_transform()
* control.get_global_rect().get_center())
await _runner.simulate_mouse_button_pressed(MOUSE_BUTTON_LEFT)
await _runner.await_input_processed()
func test_auto_repeat_starts_on() -> void:
assert_bool(_player.auto_repeat_button.button_pressed).is_true()
func test_clicking_auto_repeat_turns_it_off_and_on() -> void:
await _click(_player.auto_repeat_button)
assert_bool(Settings.auto_repeat_song).is_false()
assert_bool(_player.auto_repeat_button.button_pressed).is_false()
await _click(_player.auto_repeat_button)
assert_bool(Settings.auto_repeat_song).is_true()
func test_auto_repeat_is_under_the_player_bar() -> void:
await _runner.simulate_frames(1)
var bar: Rect2 = (_player.get_node("PlayerBar") as Control).get_global_rect()
var repeat: Rect2 = _player.auto_repeat_button.get_global_rect()
assert_float(repeat.position.y).is_greater_equal(bar.end.y)
assert_bool(_player.get_global_rect().encloses(repeat)).is_true()
func test_empty_space_lets_clicks_through() -> void:
assert_int(_player.mouse_filter).is_equal(Control.MOUSE_FILTER_IGNORE)
@@ -0,0 +1 @@
uid://ck3ti57dgce3w
+34
View File
@@ -0,0 +1,34 @@
extends GdUnitTestSuite
## The soundtrack and song shown at the top of the quiz.
var _runner: GdUnitSceneRunner
var _now_playing: NowPlaying
func before_test() -> void:
_runner = scene_runner("res://NowPlaying.tscn")
_now_playing = _runner.scene()
func _label(label_name: String) -> Label:
return _now_playing.get_node(label_name)
func test_starts_empty() -> void:
assert_str(_label("SoundtrackLabel").text).is_empty()
assert_str(_label("SongLabel").text).is_empty()
func test_show_song_shows_soundtrack_and_song() -> void:
_now_playing.show_song("Chrono Trigger", "Corridors of Time")
assert_str(_label("SoundtrackLabel").text).is_equal("Chrono Trigger")
assert_str(_label("SongLabel").text).is_equal("Corridors of Time")
func test_soundtrack_is_above_song() -> void:
_now_playing.show_song("Chrono Trigger", "Corridors of Time")
await _runner.simulate_frames(1)
assert_float(_label("SoundtrackLabel").get_global_rect().end.y) \
.is_less_equal(_label("SongLabel").get_global_rect().position.y)
@@ -0,0 +1 @@
uid://cidytlxgrmlk0
+2 -2
View File
@@ -80,7 +80,7 @@ func test_click_inside_popup_keeps_it_open() -> void:
func test_closing_click_does_not_press_button_behind_popup() -> void:
var settings: PopupWindow = _popup("SettingsWindow")
var reset_button: Button = _quiz.get_node("ResetPlaylistButton")
var reset_button: Button = _quiz.get_node("QuizToolbar/ResetPlaylistButton")
settings.open()
await _runner.simulate_frames(1)
assert_bool(settings.panel.get_global_rect().intersects(reset_button.get_global_rect())) \
@@ -93,7 +93,7 @@ func test_closing_click_does_not_press_button_behind_popup() -> void:
func test_button_works_again_after_popup_closed() -> void:
var reset_button: Button = _quiz.get_node("ResetPlaylistButton")
var reset_button: Button = _quiz.get_node("QuizToolbar/ResetPlaylistButton")
await _click_at(reset_button.get_global_rect().get_center())
+69
View File
@@ -0,0 +1,69 @@
extends GdUnitTestSuite
## The row of quiz buttons: each button reports its press, and locking disables the
## buttons that use the song list.
const BUTTON_SIGNALS: Dictionary = {
"ResetPlaylistButton": "reset_playlist_pressed",
"ResetPointsButton": "reset_points_pressed",
"SettingsButton": "settings_pressed",
"SoundTestButton": "sound_test_pressed",
}
var _runner: GdUnitSceneRunner
var _toolbar: QuizToolbar
func before_test() -> void:
_runner = scene_runner("res://QuizToolbar.tscn")
_toolbar = _runner.scene()
func _button(button_name: String) -> Button:
return _toolbar.get_node(button_name)
func _click(button_name: String) -> void:
_runner.set_mouse_position(_toolbar.get_viewport().get_final_transform()
* _button(button_name).get_global_rect().get_center())
await _runner.simulate_mouse_button_pressed(MOUSE_BUTTON_LEFT)
await _runner.await_input_processed()
func test_clicking_each_button_reports_it() -> void:
for button_name: String in BUTTON_SIGNALS:
var signals: GdUnitSignalAssert = assert_signal(monitor_signals(_toolbar))
await _click(button_name)
await signals.override_failure_message(button_name + " not reported") \
.is_emitted(BUTTON_SIGNALS[button_name])
func test_buttons_are_in_order_left_to_right() -> void:
var lefts: Array = ["ResetPlaylistButton", "ResetPointsButton", "SettingsButton",
"SoundTestButton"].map(func(n: String) -> float: return _button(n).position.x)
for i: int in lefts.size() - 1:
assert_float(lefts[i]).is_less(lefts[i + 1])
func test_locking_disables_only_buttons_that_use_the_song_list() -> void:
_toolbar.set_locked(true)
assert_bool(_button("ResetPlaylistButton").disabled).is_true()
assert_bool(_button("SoundTestButton").disabled).is_true()
assert_bool(_button("ResetPointsButton").disabled).is_false()
assert_bool(_button("SettingsButton").disabled).is_false()
func test_unlocking_enables_them_again() -> void:
_toolbar.set_locked(true)
_toolbar.set_locked(false)
assert_bool(_button("ResetPlaylistButton").disabled).is_false()
assert_bool(_button("SoundTestButton").disabled).is_false()
func test_clicks_between_buttons_go_through() -> void:
assert_int(_toolbar.mouse_filter).is_equal(Control.MOUSE_FILTER_IGNORE)
@@ -0,0 +1 @@
uid://ecsdudhpw8oi
+44 -10
View File
@@ -51,11 +51,21 @@ func test_top_bar_is_drawn_below_the_quiz() -> void:
func test_quiz_parts_are_their_own_scenes() -> void:
for node_name: String in ["MusicListPanel", "PlayersPanel", "InspirationList"]:
for node_name: String in ["MusicListPanel", "PlayersPanel", "InspirationList",
"NowPlaying", "QuizToolbar", "SongControls", "MusicPlayer"]:
assert_str(_quiz.get_node(node_name).scene_file_path) \
.is_equal("res://%s.tscn" % node_name)
func test_quiz_downloads_songs_with_a_song_loader() -> void:
assert_object(_quiz.song_loader).is_instanceof(SongLoader)
func test_auto_repeat_is_part_of_the_music_player() -> void:
assert_object(_quiz.get_node("MusicPlayer/RepeatSongCheckButton")).is_instanceof(CheckButton)
assert_object(_quiz.get_node_or_null("RepeatSongCheckButton")).is_null()
func test_top_bar_holds_only_title_and_buttons() -> void:
assert_array(_children(_window.get_node("TopBar"))) \
.contains_exactly_in_any_order(TOP_BAR_CHILDREN)
@@ -83,22 +93,22 @@ func test_clicking_each_top_bar_button_opens_its_popup() -> void:
func test_clicking_quiz_buttons_plays_and_shows_answer() -> void:
await _click_at(_center(_quiz.get_node("NextButton")))
await _click_at(_center(_quiz.get_node("SongControls/NextButton")))
await _runner.simulate_frames(2)
assert_int(Playlist.currently_playing_song).is_equal(0)
assert_str((_quiz.get_node("VBoxContainer/SoundtrackLabel") as Label).text) \
assert_str((_quiz.get_node("NowPlaying/SoundtrackLabel") as Label).text) \
.is_not_equal("Chrono Trigger")
await _click_at(_center(_quiz.get_node("ShowAnswerButton")))
await _click_at(_center(_quiz.get_node("SongControls/ShowAnswerButton")))
assert_str((_quiz.get_node("VBoxContainer/SoundtrackLabel") as Label).text) \
assert_str((_quiz.get_node("NowPlaying/SoundtrackLabel") as Label).text) \
.is_equal("Chrono Trigger")
func test_clicking_a_song_in_the_music_list_plays_it() -> void:
_next_song_is(SONG_INFO[1])
await _play_first_song()
_press("NextButton")
_press("SongControls/NextButton")
# Changing song waits a moment for the old one to stop
await get_tree().create_timer(0.7).timeout
assert_int(Playlist.currently_playing_song).is_equal(1)
@@ -137,6 +147,30 @@ func test_character_button_in_the_player_list_picks_a_new_character() -> void:
assert_object(crono.character).is_same(texture)
func test_sound_test_button_fetches_the_sound_test_song() -> void:
_server.respond_bytes("/music/soundTest", FileAccess.get_file_as_bytes("res://tests/data/short.mp3"))
await _click_at(_center(_quiz.get_node("QuizToolbar/SoundTestButton")))
assert_int(_server.count("/music/soundTest")).is_equal(1)
func test_loaded_song_list_shows_in_the_music_list() -> void:
# No new song finishes downloading, so only loading the list can show the songs
_server.hold("/music/rand")
_server.respond_json("/music/list", [
{"SongNo": 0, "Soundtrack": "Chrono Trigger", "Song": "Corridors of Time"},
{"SongNo": 1, "Soundtrack": "Final Fantasy VI", "Song": "Dancing Mad"},
])
Playlist.is_song_downloading = false
_quiz.song_loader.load_song_list()
var rows: Array[Node] = _quiz.get_node("MusicListPanel/ScrollContainer/MusicList").get_children()
assert_array(rows.map(func(row: SongListObject) -> int: return row.song_object.song_number)) \
.contains_exactly([0, 1])
func test_quiz_popup_backdrop_covers_the_top_bar() -> void:
var settings: PopupWindow = _quiz.get_node("SettingsWindow")
settings.open()
@@ -169,8 +203,8 @@ func test_sync_disables_quiz_buttons_until_finished() -> void:
_top_bar_button("SyncButton").pressed.emit()
sync_window.sync_timer.stop()
for button_name: String in ["SoundTestButton", "ResetPlaylistButton", "ShowAnswerButton",
"NextButton"]:
for button_name: String in ["QuizToolbar/SoundTestButton", "QuizToolbar/ResetPlaylistButton", "SongControls/ShowAnswerButton",
"SongControls/NextButton"]:
assert_bool((_quiz.get_node(button_name) as Button).disabled) \
.override_failure_message(button_name + " not disabled").is_true()
assert_bool(_top_bar_button("SyncButton").disabled).is_false()
@@ -180,8 +214,8 @@ func test_sync_disables_quiz_buttons_until_finished() -> void:
sync_window._on_sync_timeout()
await _runner.simulate_frames(2)
for button_name: String in ["SoundTestButton", "ResetPlaylistButton", "ShowAnswerButton",
"NextButton"]:
for button_name: String in ["QuizToolbar/SoundTestButton", "QuizToolbar/ResetPlaylistButton", "SongControls/ShowAnswerButton",
"SongControls/NextButton"]:
assert_bool((_quiz.get_node(button_name) as Button).disabled) \
.override_failure_message(button_name + " still disabled").is_false()
# The search list in the main window and the quiz playlist are both reloaded
+85
View File
@@ -0,0 +1,85 @@
extends GdUnitTestSuite
## Show answer and Next: each reports its press, also from its keyboard shortcut, and
## Next is only enabled while a song is ready.
var _runner: GdUnitSceneRunner
var _controls: SongControls
func before_test() -> void:
_runner = scene_runner("res://SongControls.tscn")
_controls = _runner.scene()
func _click(button: Button) -> void:
_runner.set_mouse_position(_controls.get_viewport().get_final_transform()
* button.get_global_rect().get_center())
await _runner.simulate_mouse_button_pressed(MOUSE_BUTTON_LEFT)
await _runner.await_input_processed()
func _press_keys(keys: Array[Key]) -> void:
for key: Key in keys:
_runner.simulate_key_press(key)
await _runner.await_input_processed()
keys.reverse()
for key: Key in keys:
_runner.simulate_key_release(key)
await _runner.await_input_processed()
func test_next_starts_disabled() -> void:
assert_bool(_controls.next_button.disabled).is_true()
func test_clicking_show_answer_reports_it() -> void:
var signals: GdUnitSignalAssert = assert_signal(monitor_signals(_controls))
await _click(_controls.show_answer_button)
await signals.is_emitted("show_answer_pressed")
func test_clicking_next_reports_it_when_enabled() -> void:
_controls.set_next_enabled(true)
var signals: GdUnitSignalAssert = assert_signal(monitor_signals(_controls))
await _click(_controls.next_button)
await signals.is_emitted("next_pressed")
func test_clicking_disabled_next_does_nothing() -> void:
var signals: GdUnitSignalAssert = assert_signal(monitor_signals(_controls))
await _click(_controls.next_button)
await signals.wait_until(200).is_not_emitted("next_pressed")
func test_alt_v_shows_answer_and_alt_c_plays_next() -> void:
_controls.set_next_enabled(true)
var signals: GdUnitSignalAssert = assert_signal(monitor_signals(_controls))
await _press_keys([KEY_ALT, KEY_V])
await signals.is_emitted("show_answer_pressed")
await _press_keys([KEY_ALT, KEY_C])
await signals.is_emitted("next_pressed")
func test_locking_disables_both() -> void:
_controls.set_next_enabled(true)
_controls.set_locked(true)
assert_bool(_controls.show_answer_button.disabled).is_true()
assert_bool(_controls.next_button.disabled).is_true()
func test_unlocking_enables_show_answer_but_waits_for_a_song_for_next() -> void:
_controls.set_locked(true)
_controls.set_locked(false)
assert_bool(_controls.show_answer_button.disabled).is_false()
assert_bool(_controls.next_button.disabled).is_true()
@@ -0,0 +1 @@
uid://c43ii6v73reks
+122
View File
@@ -0,0 +1,122 @@
extends GdUnitTestSuite
## Downloading songs into the Playlist ahead of time, against a fake server.
const SONG_INFO: Dictionary = {"SongNo": 0, "Soundtrack": "Chrono Trigger", "Song": "Corridors of Time"}
var _snapshot: AutoloadSnapshot
var _server: FakeHttpTransport
var _loader: SongLoader
func before_test() -> void:
_snapshot = AutoloadSnapshot.take([Settings, Playlist])
Playlist.reset_playlist()
Playlist.is_song_downloading = false
Playlist.number_of_tracks_to_preload = 1
Settings.use_low_played_mode = false
_server = FakeHttpTransport.new().install()
_server.respond_json("/music/list", [])
_server.respond_bytes("/music/rand", FileAccess.get_file_as_bytes("res://tests/data/short.mp3"))
_server.respond_bytes("/music/addQue", PackedByteArray())
_server.respond_json("/music/info", SONG_INFO)
_loader = auto_free(SongLoader.new())
add_child(_loader)
func after_test() -> void:
_server.uninstall()
_snapshot.restore()
func test_load_song_list_adds_songs_already_played_on_the_server() -> void:
_server.respond_json("/music/list", [
{"SongNo": 0, "Soundtrack": "Chrono Trigger", "Song": "Corridors of Time"},
])
_server.respond_json("/music/info", {"SongNo": 1, "Soundtrack": "Final Fantasy VI", "Song": "Dancing Mad"})
var signals: GdUnitSignalAssert = assert_signal(monitor_signals(_loader))
_loader.load_song_list()
await signals.is_emitted("song_list_loaded")
var first: SongObject = Playlist.song_object_array[0]
assert_str(first.soundtrack_title).is_equal("Chrono Trigger")
assert_bool(first.has_played).is_true()
assert_bool(first.is_answered).is_true()
func test_load_song_list_then_preloads_a_new_song() -> void:
_loader.load_song_list()
assert_int(_server.count("/music/rand")).is_equal(1)
assert_int(Playlist.song_object_array.size()).is_equal(1)
func test_fetch_song_downloads_queues_and_adds_the_song() -> void:
var signals: GdUnitSignalAssert = assert_signal(monitor_signals(_loader))
_loader.fetch_song()
await signals.is_emitted("song_added")
assert_array(_server.requested_paths).contains_exactly(["/music/rand", "/music/addQue", "/music/info"])
var song: SongObject = Playlist.song_object_array[0]
assert_str(song.song_title).is_equal("Corridors of Time")
assert_object(song.song).is_not_null()
assert_bool(Playlist.is_song_downloading).is_false()
func test_fetch_song_keeps_going_until_the_cache_is_full() -> void:
Playlist.number_of_tracks_to_preload = 3
_loader.fetch_song()
assert_int(Playlist.song_object_array.size()).is_equal(3)
assert_int(_server.count("/music/rand")).is_equal(3)
func test_low_played_mode_uses_its_endpoint() -> void:
Settings.use_low_played_mode = true
_server.respond_bytes("/music/rand/low", FileAccess.get_file_as_bytes("res://tests/data/short.mp3"))
_loader.fetch_song()
assert_int(_server.count("/music/rand/low")).is_equal(1)
assert_int(_server.count("/music/rand")).is_equal(0)
func test_fetch_song_if_idle_does_nothing_while_downloading() -> void:
Playlist.is_song_downloading = true
_loader.fetch_song_if_idle()
assert_int(_server.count("/music/rand")).is_equal(0)
func test_fill_cache_does_nothing_when_the_cache_is_full() -> void:
_loader.fetch_song()
_loader.fill_cache()
assert_int(_server.count("/music/rand")).is_equal(1)
func test_fill_cache_downloads_after_the_cache_size_grows() -> void:
_loader.fetch_song()
Playlist.number_of_tracks_to_preload = 2
_loader.fill_cache()
assert_int(_server.count("/music/rand")).is_equal(2)
func test_failed_step_is_retried_alone() -> void:
_loader.retry_seconds = 0.05
_server.fail_after("/music/info", 0)
_loader.fetch_song()
assert_int(Playlist.song_object_array.size()).is_equal(0)
_server.respond_json("/music/info", SONG_INFO)
await get_tree().create_timer(0.2).timeout
assert_int(_server.count("/music/rand")).is_equal(1)
assert_int(_server.count("/music/addQue")).is_equal(1)
assert_int(Playlist.song_object_array.size()).is_equal(1)
@@ -0,0 +1 @@
uid://caew6u3yi8m4n
+5 -5
View File
@@ -24,7 +24,7 @@ func test_winning_turns_statistics_off() -> void:
func test_resetting_points_turns_statistics_back_on() -> void:
await _give_point_with_keybind(1)
_press("ResetPointsButton")
_press("QuizToolbar/ResetPointsButton")
assert_bool(Settings.add_to_stats).is_true()
@@ -33,7 +33,7 @@ func test_statistics_turned_off_by_hand_stay_off_after_reset() -> void:
Settings.add_to_stats = false
await _give_point_with_keybind(1)
_press("ResetPointsButton")
_press("QuizToolbar/ResetPointsButton")
assert_bool(Settings.add_to_stats).is_false()
@@ -41,7 +41,7 @@ func test_statistics_turned_off_by_hand_stay_off_after_reset() -> void:
func test_settings_checkbox_shows_statistics_off_after_win() -> void:
await _give_point_with_keybind(1)
_press("SettingsButton")
_press("QuizToolbar/SettingsButton")
assert_bool(_stats_checkbox().button_pressed).is_false()
@@ -50,13 +50,13 @@ func test_songs_after_a_win_are_not_counted() -> void:
await _give_point_with_keybind(1)
var played_before: int = _server.count("/music/played?song=0")
_press("ShowAnswerButton")
_press("SongControls/ShowAnswerButton")
assert_int(_server.count("/music/played?song=0")).is_equal(played_before)
func test_winning_score_cannot_go_below_one() -> void:
_press("SettingsButton")
_press("QuizToolbar/SettingsButton")
var lower_button: Button = _quiz.get_node("SettingsWindow").get_node("%LowerButton")
for i: int in 3: