extends MainWindowTestSuite ## The quiz is saved after every change and goes on where it was when the game starts ## again: players, points, characters and songs, from the server or a music folder. A new ## game, a changed server playlist or a file that can't be read start over. const SECOND_SONG: Dictionary = {"SongNo": 1, "Soundtrack": "Final Fantasy VI", "Song": "Dancing Mad"} var _mp3: PackedByteArray = FileAccess.get_file_as_bytes("res://tests/data/short.mp3") func _saved() -> QuizSave: # Saving waits until the end of the frame await _runner.simulate_frames(1) return QuizSave.read() ## Closes the game and starts it again, like the user does: everything in memory is gone ## and only the files are left. The server answers with [param server_songs] as its ## playlist, the songs it handed out before. func _restart_game(server_songs: Array = SONG_INFO) -> void: await _runner.simulate_frames(1) _window.free() Settings.player_array.clear() Settings.music_folder = null Settings.character_folder = "" Settings.add_to_stats = true Playlist.song_object_array.clear() Playlist.currently_playing_song = -1 Playlist.latest_played_song = 0 Playlist.game_start_song = 0 Playlist.first_song_played = false Playlist.is_song_downloading = false Playlist._song_files_written = 0 _server.respond_json("/music/list", server_songs) _server.requested_paths.clear() _runner = scene_runner("res://main/MainWindow.tscn") _window = _runner.scene() _quiz = _window.get_node("Quiz") await _runner.simulate_frames(3) # Crono and Marle play the first song; Crono gets the point and the answer is shown. The # second song is downloaded ahead. func _play_a_little() -> Array[PlayerObject]: var crono: PlayerObject = _add_player("Crono") var marle: PlayerObject = _add_player("Marle") _next_song_is(SECOND_SONG) await _play_first_song() await _give_point_with_keybind(1) _press("SongControls/ShowAnswerButton") await _runner.simulate_frames(2) return [crono, marle] func _now_playing() -> Array[String]: var now_playing: Node = _quiz.get_node("NowPlaying") return [(now_playing.get_node("SoundtrackLabel") as Label).text, (now_playing.get_node("SongLabel") as Label).text] # Saving func test_players_points_and_songs_are_saved_as_the_quiz_goes_on() -> void: var players: Array[PlayerObject] = await _play_a_little() var saved: QuizSave = await _saved() assert_array(saved.players.map(func(p: PlayerObject) -> String: return p.player_name)) \ .contains_exactly(["Crono", "Marle"]) assert_int(saved.players[0].player_score).is_equal(1) assert_int(saved.songs.size()).is_equal(2) assert_array(saved.songs[0].players_given_point).contains_exactly([players[0].id]) assert_bool(saved.songs[0].is_answered).is_true() assert_bool(saved.songs[0].is_playing).is_true() assert_bool(saved.songs[1].has_played).is_false() assert_int(saved.currently_playing_song).is_equal(0) func test_taking_a_point_is_saved() -> void: await _play_a_little() await _take_point_with_keybind(1) var saved: QuizSave = await _saved() assert_int(saved.players[0].player_score).is_equal(0) assert_array(saved.songs[0].players_given_point).is_empty() func test_picking_a_character_is_saved() -> void: var crono: PlayerObject = _add_player("Crono") _player_row(crono).change_character() var texture: ImageTexture = ImageTexture.create_from_image( Image.create(4, 4, false, Image.FORMAT_RGB8)) _quiz.character_select_popup.character_selected.emit(texture, "Frog.png") assert_str((await _saved()).players[0].character_name).is_equal("Frog.png") func test_removing_a_player_is_saved() -> void: var players: Array[PlayerObject] = await _play_a_little() await _remove_player(players[0]) var saved: QuizSave = await _saved() assert_int(saved.players.size()).is_equal(1) assert_array(saved.songs[0].players_given_point).is_empty() func test_reset_points_saves_a_new_game() -> void: await _play_a_little() _press("QuizToolbar/ResetPointsButton") var saved: QuizSave = await _saved() assert_int(saved.players.size()).is_equal(2) assert_int(saved.players[0].player_score).is_equal(0) assert_array(saved.songs[0].players_given_point).is_empty() assert_int(saved.game_start_song).is_equal(1) func test_a_sync_saves_a_new_game() -> void: await _play_a_little() _quiz.on_sync_finished() await _runner.simulate_frames(3) var saved: QuizSave = await _saved() assert_int(saved.players[0].player_score).is_equal(0) assert_str(saved.players[0].player_name).is_equal("Crono") func test_winning_is_saved_with_statistics_off() -> void: Settings.winning_score = 1 await _play_a_little() assert_bool((await _saved()).stats_paused_by_win).is_true() # Continuing after a restart func test_quiz_goes_on_after_a_restart() -> void: var before: Array[PlayerObject] = await _play_a_little() await _restart_game([SONG_INFO[0], SECOND_SONG]) assert_int(Settings.player_array.size()).is_equal(2) var crono: PlayerObject = Settings.player_array[0] assert_str(crono.player_name).is_equal("Crono") assert_str(crono.id).is_equal(before[0].id) assert_int(crono.player_score).is_equal(1) assert_bool(crono.is_first_point).is_false() assert_int(Playlist.song_object_array.size()).is_equal(2) assert_array(Playlist.song_object_array[0].players_given_point).contains_exactly([crono.id]) assert_bool(Playlist.song_object_array[0].is_answered).is_true() assert_bool(Playlist.song_object_array[1].has_played).is_false() assert_int(Playlist.currently_playing_song).is_equal(0) assert_array(_now_playing()).contains_exactly(["Chrono Trigger", "Corridors of Time"]) # The server's playlist was kept, not reset assert_int(_server.count("/music/reset")).is_equal(0) func test_players_rows_show_the_saved_scores() -> void: await _play_a_little() await _restart_game([SONG_INFO[0], SECOND_SONG]) var row: PlayerRow = _player_row(Settings.player_array[0]) assert_str(row.player_name_field.text).ends_with("Crono: 1") func test_song_downloaded_ahead_is_kept_and_not_downloaded_again() -> void: await _play_a_little() var file: String = (await _saved()).songs[1].song_path await _restart_game([SONG_INFO[0], SECOND_SONG]) assert_bool(FileAccess.file_exists(file)).is_true() assert_int(_server.count("/music/rand")).is_equal(0) assert_bool((_quiz.get_node("SongControls/NextButton") as Button).disabled).is_false() func test_next_plays_the_song_downloaded_before_the_restart() -> void: await _play_a_little() await _restart_game([SONG_INFO[0], SECOND_SONG]) _press("SongControls/NextButton") await await_millis(700) assert_int(Playlist.currently_playing_song).is_equal(1) assert_bool(_quiz.music_player.audio_player.playing).is_true() func test_song_that_was_playing_is_ready_to_play_again() -> void: await _play_a_little() await _restart_game([SONG_INFO[0], SECOND_SONG]) var player: MusicPlayer = _quiz.music_player assert_object(player.audio_player.stream).is_not_null() assert_bool(player.audio_player.playing).is_false() assert_bool(player.shows_playing()).is_false() player.play_or_pause() assert_bool(player.audio_player.playing).is_true() func test_keybinds_give_points_after_a_restart() -> void: await _play_a_little() await _restart_game([SONG_INFO[0], SECOND_SONG]) await _give_point_with_keybind(2) assert_array(_scores()).contains_exactly([1, 1]) assert_int((await _saved()).players[1].player_score).is_equal(1) func test_song_files_that_are_gone_are_downloaded_again() -> void: await _play_a_little() var saved: QuizSave = await _saved() for song_object: SongObject in saved.songs: DirAccess.remove_absolute(song_object.song_path) _server.respond_bytes("/music?song=0", _mp3) _server.respond_bytes("/music?song=1", _mp3) await _restart_game([SONG_INFO[0], SECOND_SONG]) # The song that was playing and the one downloaded ahead assert_int(_server.count("/music?song=0")).is_equal(1) assert_int(_server.count("/music?song=1")).is_equal(1) assert_object(Playlist.get_song(1)).is_not_null() func test_unused_song_files_are_deleted_at_start() -> void: await _play_a_little() var stray: String = Playlist.songs_folder().path_join("left_over.mp3") FileAccess.open(stray, FileAccess.WRITE).store_buffer(_mp3) await _restart_game([SONG_INFO[0], SECOND_SONG]) assert_bool(FileAccess.file_exists(stray)).is_false() assert_int(DirAccess.get_files_at(Playlist.songs_folder()).size()).is_equal(2) func test_changed_server_playlist_starts_over_with_the_same_players() -> void: await _play_a_little() # The server's playlist was reset, e.g. by a sync from another computer await _restart_game([{"SongNo": 0, "Soundtrack": "Terranigma", "Song": "Underworld"}]) assert_array(Settings.player_array.map(func(p: PlayerObject) -> String: return p.player_name)).contains_exactly(["Crono", "Marle"]) assert_array(_scores()).contains_exactly([0, 0]) assert_str(Playlist.song_object_array[0].soundtrack_title).is_equal("Terranigma") assert_bool(Playlist.song_object_array[0].has_played).is_true() func test_songs_the_server_added_after_the_save_are_kept_as_played() -> void: await _play_a_little() var third: Dictionary = {"SongNo": 2, "Soundtrack": "Terranigma", "Song": "Underworld"} await _restart_game([SONG_INFO[0], SECOND_SONG, third]) assert_int(Playlist.song_object_array.size()).is_equal(3) assert_int(Settings.player_array[0].player_score).is_equal(1) assert_bool(Playlist.song_object_array[2].has_played).is_true() func test_another_server_starts_over_with_the_same_players() -> void: await _play_a_little() var server_before: String = Settings.default_path Settings.default_path = "https://other.example.com" await _restart_game([SONG_INFO[0], SECOND_SONG]) Settings.default_path = server_before assert_int(Settings.player_array.size()).is_equal(2) assert_array(_scores()).contains_exactly([0, 0]) func test_file_that_cannot_be_read_starts_a_new_quiz() -> void: await _play_a_little() await _runner.simulate_frames(1) FileAccess.open(QuizSave.path(), FileAccess.WRITE).store_string("{ half a fi") await _restart_game([SONG_INFO[0], SECOND_SONG]) assert_array(Settings.player_array).is_empty() # Like any start without a saved quiz: the server's songs count as played, and a new # one is downloaded ahead assert_bool(Playlist.song_object_array[0].has_played).is_true() assert_bool(Playlist.song_object_array[1].has_played).is_true() assert_int(Playlist.song_object_array.size()).is_equal(3) # The broken file is replaced by the new quiz assert_object(await _saved()).is_not_null() func test_statistics_stay_off_after_a_won_game() -> void: Settings.winning_score = 1 await _play_a_little() await _restart_game([SONG_INFO[0], SECOND_SONG]) assert_bool(Settings.add_to_stats).is_false() _press("QuizToolbar/ResetPointsButton") assert_bool(Settings.add_to_stats).is_true() func test_nothing_is_saved_before_the_saved_songs_are_back() -> void: await _play_a_little() _server.hold("/music/list") await _restart_game([SONG_INFO[0], SECOND_SONG]) var file_before: String = FileAccess.get_file_as_string(QuizSave.path()) # A player added while the server has not answered yet _add_player("Lucca") await _runner.simulate_frames(2) assert_str(FileAccess.get_file_as_string(QuizSave.path())).is_equal(file_before) _server.release("/music/list") await _runner.simulate_frames(2) assert_int((await _saved()).players.size()).is_equal(3) assert_int((await _saved()).songs.size()).is_equal(2) # Characters func test_character_picture_comes_back_after_a_restart() -> void: var picture: Image = Image.create(20, 10, false, Image.FORMAT_RGB8) _server.respond_bytes("/character?name=Frog.png", picture.save_png_to_buffer()) var crono: PlayerObject = _add_player("Crono") _player_row(crono).new_character_selected(ImageTexture.create_from_image(picture), "Frog.png") _quiz.save_soon() await _restart_game() crono = Settings.player_array[0] assert_str(crono.character_name).is_equal("Frog.png") assert_vector(crono.character.get_size()).is_equal(Vector2(20, 10)) assert_object(_player_row(crono).character.texture_normal).is_same(crono.character) func test_characters_from_a_folder_picked_for_the_session_are_reset_after_a_restart() -> void: var folder: String = create_temp_dir("session_characters_%d" % randi()) FileAccess.open(folder.path_join("Santa.png"), FileAccess.WRITE).store_buffer( Image.create(8, 4, false, Image.FORMAT_RGB8).save_png_to_buffer()) _window.settings_popup.use_character_folder(folder) var crono: PlayerObject = _add_player("Crono") _player_row(crono).new_character_selected(ImageTexture.create_from_image( Image.create(8, 4, false, Image.FORMAT_RGB8)), "Santa.png") _quiz.save_soon() # The folder is only used until the game is closed await _restart_game() assert_str(Settings.player_array[0].character_name).is_empty() assert_str(Settings.player_array[0].character.resource_path) \ .is_equal(PlayerObject.NO_CHARACTER_PATH) # A music folder func _prepared_quiz_folder() -> String: var root: String = create_temp_dir("continue_offline_%d" % randi()) for file_name: String in ["01. Opening.mp3", "02. Jingle Bells.mp3", "03. Finale.mp3"]: FileAccess.open(root.path_join(file_name), FileAccess.WRITE).store_buffer(_mp3) return root func test_quiz_from_a_music_folder_goes_on_offline() -> void: var folder: String = _prepared_quiz_folder() _window.settings_popup.use_music_folder(folder) await _runner.simulate_frames(2) _add_player("Crono") _press("SongControls/NextButton") await await_millis(700) await _give_point_with_keybind(1) await _restart_game() assert_bool(Settings.is_offline()).is_true() assert_str(Settings.music_folder.path).is_equal(folder) assert_bool(_window.top_bar.offline_label.visible).is_true() assert_int(Settings.player_array[0].player_score).is_equal(1) assert_str(Playlist.song_object_array[0].song_title).is_equal("01. Opening") assert_array(_server.requested_paths.filter(func(path: String) -> bool: return path.begins_with("/music"))).is_empty() _press("SongControls/NextButton") await await_millis(700) assert_str(Playlist.song_object_array[Playlist.currently_playing_song].song_title) \ .is_equal("02. Jingle Bells") func test_music_folder_that_is_gone_starts_over_with_the_server() -> void: var folder: String = _prepared_quiz_folder() _window.settings_popup.use_music_folder(folder) await _runner.simulate_frames(2) _add_player("Crono") _press("SongControls/NextButton") await await_millis(700) await _give_point_with_keybind(1) for file_name: String in DirAccess.get_files_at(folder): DirAccess.remove_absolute(folder.path_join(file_name)) await _restart_game() assert_bool(Settings.is_offline()).is_false() assert_int(Settings.player_array.size()).is_equal(1) assert_array(_scores()).contains_exactly([0]) assert_str(Playlist.song_object_array[0].soundtrack_title).is_equal("Chrono Trigger")