extends GdUnitTestSuite ## The saved quiz: what is saved from the game, writing and reading the file, and files ## that can't be used. var _snapshot: AutoloadSnapshot var _config_dir_before: String func before_test() -> void: _snapshot = AutoloadSnapshot.take([Settings, Playlist]) _config_dir_before = OS.get_environment(Settings.CONFIG_DIR_ENV) OS.set_environment(Settings.CONFIG_DIR_ENV, create_temp_dir("quiz_save_%d" % randi())) Settings.is_debug = false Settings.music_folder = null Settings.character_folder = "" Settings.default_path = "https://music.example.com" Settings.player_array.clear() Playlist.song_object_array.clear() func after_test() -> void: OS.set_environment(Settings.CONFIG_DIR_ENV, _config_dir_before) _snapshot.restore() func _player(player_name: String, score: int, character: String = "") -> PlayerObject: var player: PlayerObject = PlayerObject.new(player_name) player.player_score = score player.is_first_point = score == 0 player.character_name = character Settings.player_array.append(player) return player func _song(number: int, played: bool, points: Array[String] = []) -> SongObject: var song_object: SongObject = SongObject.new() song_object.song_number = number song_object.soundtrack_title = "Soundtrack %d" % number song_object.song_title = "Song %d" % number song_object.has_played = played song_object.is_answered = played song_object.song_path = "/songs/song_%d.mp3" % number for player_id: String in points: song_object.add_point(player_id) Playlist.song_object_array.append(song_object) return song_object # A quiz with two players, two played songs and one downloaded ahead func _quiz() -> void: var crono: PlayerObject = _player("Crono", 2, "Crono.png") var marle: PlayerObject = _player("Marle", 1) _song(0, true, [crono.id]) _song(1, true, [crono.id, marle.id]) _song(2, false) Playlist.song_object_array[1].is_playing = true Playlist.currently_playing_song = 1 Playlist.latest_played_song = 1 Playlist.game_start_song = 0 Playlist.first_song_played = true func _read_back() -> QuizSave: assert_bool(QuizSave.capture(false).write()).is_true() return QuizSave.read() # What is saved func test_players_are_saved_with_points_and_characters() -> void: _quiz() var players: Array[PlayerObject] = _read_back().players assert_int(players.size()).is_equal(2) assert_str(players[0].player_name).is_equal("Crono") assert_str(players[0].id).is_equal(Settings.player_array[0].id) assert_int(players[0].player_score).is_equal(2) assert_bool(players[0].is_first_point).is_false() assert_str(players[0].character_name).is_equal("Crono.png") assert_str(players[1].character_name).is_empty() func test_songs_are_saved_with_who_got_points_and_how_far_they_came() -> void: _quiz() var songs: Array[SongObject] = _read_back().songs assert_int(songs.size()).is_equal(3) var crono: String = Settings.player_array[0].id var marle: String = Settings.player_array[1].id assert_array(songs[0].players_given_point).contains_exactly([crono]) assert_array(songs[1].players_given_point).contains_exactly([crono, marle]) assert_bool(songs[1].is_playing).is_true() assert_bool(songs[1].has_played).is_true() assert_bool(songs[2].has_played).is_false() assert_bool(songs[2].is_answered).is_false() assert_str(songs[2].song_title).is_equal("Song 2") assert_str(songs[2].soundtrack_title).is_equal("Soundtrack 2") assert_str(songs[2].song_path).is_equal("/songs/song_2.mp3") func test_playlist_positions_are_saved() -> void: _quiz() var save: QuizSave = _read_back() assert_int(save.currently_playing_song).is_equal(1) assert_int(save.latest_played_song).is_equal(1) assert_int(save.game_start_song).is_equal(0) assert_bool(save.first_song_played).is_true() func test_where_the_songs_and_characters_came_from_is_saved() -> void: _quiz() Settings.character_folder = "/home/me/Christmas characters" assert_bool(QuizSave.capture(true).write()).is_true() var save: QuizSave = QuizSave.read() assert_str(save.server).is_equal("https://music.example.com") assert_str(save.music_folder_path).is_empty() assert_str(save.characters_dir).is_equal("/home/me/Christmas characters") assert_bool(save.stats_paused_by_win).is_true() func test_a_music_folder_is_saved_with_how_far_its_quiz_has_come() -> void: var root: String = create_temp_dir("quiz_save_folder_%d" % randi()) for file_name: String in ["01. One.mp3", "02. Two.mp3"]: FileAccess.open(root.path_join(file_name), FileAccess.WRITE).store_buffer( FileAccess.get_file_as_bytes("res://tests/data/short.mp3")) Settings.music_folder = MusicFolder.scan(root) Settings.music_folder.next_song() var save: QuizSave = _read_back() assert_str(save.server).is_empty() assert_str(save.music_folder_path).is_equal(root) assert_int(int(save.music_folder_progress.next)).is_equal(1) func test_song_files_are_the_saved_songs_files() -> void: _quiz() Playlist.song_object_array[0].song_path = "" assert_array(_read_back().song_files()).contains_exactly( ["/songs/song_1.mp3", "/songs/song_2.mp3"]) # The file func test_quiz_is_saved_next_to_the_settings_and_apart_in_debug_mode() -> void: assert_str(QuizSave.path()).is_equal(Settings.config_dir().path_join("current_quiz.json")) Settings.is_debug = true assert_str(QuizSave.path()).is_equal( Settings.config_dir().path_join("current_quiz_debug.json")) func test_no_file_means_no_saved_quiz() -> void: assert_object(QuizSave.read()).is_null() func test_saving_leaves_no_temporary_file() -> void: _quiz() QuizSave.capture(false).write() assert_array(DirAccess.get_files_at(Settings.config_dir())).contains_exactly( ["current_quiz.json"]) func test_saving_again_replaces_the_quiz() -> void: _quiz() QuizSave.capture(false).write() Settings.player_array[0].player_score = 5 assert_int(_read_back().players[0].player_score).is_equal(5) func test_delete_removes_the_saved_quiz() -> void: _quiz() QuizSave.capture(false).write() QuizSave.delete() assert_object(QuizSave.read()).is_null() func test_file_that_is_not_a_quiz_is_ignored() -> void: for text: String in ["", "not json {", "[1, 2]", '{"version": 1}', '{"players": [], "songs": []}', '{"version": 99, "players": [], "songs": []}']: FileAccess.open(QuizSave.path(), FileAccess.WRITE).store_string(text) assert_object(QuizSave.read()).override_failure_message(text).is_null() func test_players_that_cannot_be_read_are_left_out() -> void: _quiz() var data: Dictionary = QuizSave.capture(false).to_dict() data.players.append({"name": 3}) data.players.append("Lucca") var save: QuizSave = QuizSave.from_dict(data) assert_int(save.players.size()).is_equal(2) func test_songs_are_dropped_together_if_one_cannot_be_read() -> void: _quiz() var data: Dictionary = QuizSave.capture(false).to_dict() data.songs[1] = {"song": "no number"} var save: QuizSave = QuizSave.from_dict(data) # A song's place in the list is its number, so the others can't be kept without it assert_array(save.songs).is_empty() assert_int(save.currently_playing_song).is_equal(-1) assert_int(save.players.size()).is_equal(2) func test_positions_outside_the_songs_are_kept_inside() -> void: _quiz() var data: Dictionary = QuizSave.capture(false).to_dict() data.currently_playing_song = 40 data.latest_played_song = -3 data.game_start_song = 99 var save: QuizSave = QuizSave.from_dict(data) assert_int(save.currently_playing_song).is_equal(2) assert_int(save.latest_played_song).is_equal(0) assert_int(save.game_start_song).is_equal(3) func test_no_more_players_than_the_game_allows() -> void: var data: Dictionary = QuizSave.new().to_dict() for i: int in Settings.MAX_NUMBER_OF_PLAYERS + 3: data.players.append(PlayerObject.new("Player %d" % i).to_dict()) assert_int(QuizSave.from_dict(data).players.size()).is_equal(Settings.MAX_NUMBER_OF_PLAYERS) func test_drop_songs_keeps_the_players_without_points() -> void: _quiz() var save: QuizSave = _read_back() save.drop_songs() assert_array(save.songs).is_empty() assert_int(save.currently_playing_song).is_equal(-1) assert_int(save.players.size()).is_equal(2) assert_int(save.players[0].player_score).is_equal(0) assert_bool(save.players[0].is_first_point).is_true() assert_str(save.players[0].character_name).is_equal("Crono.png") # Players and songs on their own func test_player_round_trip() -> void: var player: PlayerObject = _player("Lucca", 3, "Lucca.png") var again: PlayerObject = PlayerObject.from_dict( JSON.parse_string(JSON.stringify(player.to_dict()))) assert_str(again.id).is_equal(player.id) assert_str(again.player_name).is_equal("Lucca") assert_int(again.player_score).is_equal(3) assert_str(again.character_name).is_equal("Lucca.png") # The picture is found again by name later assert_str(again.character.resource_path).is_equal(PlayerObject.NO_CHARACTER_PATH) func test_song_round_trip() -> void: var song_object: SongObject = _song(7, true, ["a", "b"]) song_object.is_playing = true var again: SongObject = SongObject.from_dict( JSON.parse_string(JSON.stringify(song_object.to_dict()))) assert_int(again.song_number).is_equal(7) assert_str(again.soundtrack_title).is_equal("Soundtrack 7") assert_str(again.song_title).is_equal("Song 7") assert_array(again.players_given_point).contains_exactly(["a", "b"]) assert_bool(again.has_played).is_true() assert_bool(again.is_answered).is_true() assert_bool(again.is_playing).is_true() assert_str(again.song_path).is_equal("/songs/song_7.mp3") func test_clear_character_goes_back_to_no_character() -> void: var player: PlayerObject = _player("Crono", 0) var texture: ImageTexture = ImageTexture.create_from_image( Image.create(4, 4, false, Image.FORMAT_RGB8)) player.set_character(texture, "Crono.png") assert_object(player.character).is_same(texture) player.clear_character() assert_str(player.character_name).is_empty() assert_str(player.character.resource_path).is_equal(PlayerObject.NO_CHARACTER_PATH)