Replace the broken test suite with working GdUnit4 tests
The previous tests never compiled: they used GUT's assert functions, Godot 3 APIs and reassigned autoload functions, so no test ran. - 90 tests: unit tests for the playlist, songs, player scoring, time formatting, search and the server client, plus integration tests that run MainWindow and SyncWindow against a fake server - Keybind tests check that Alt+<number> and Ctrl+Alt+<number> keep following the player rows while players are added and removed - run_tests.sh finds Godot and runs the suite headless (~10 seconds) - CI runs the tests before the build job in gitea-build.yaml; the separate test.yml used a GitHub action that does not exist - GdUnit4 is committed instead of ignored, since the project enables it, and .gitea/ is no longer ignored - Exported builds leave out tests/ and addons/gdUnit4/
This commit is contained in:
1 parent
56e9fe66d3
commit
c985a6ce5b
559 files changed
+32474
-2100
No files matched your search
Whitespace-only changes.
Binary file not shown.
@@ -0,0 +1,27 @@
|
||||
class_name AutoloadSnapshot
|
||||
extends RefCounted
|
||||
## Saves and restores the script variables of autoloads such as Settings and Playlist,
|
||||
## so a test that changes global state does not leak into the next test.
|
||||
|
||||
var _values: Dictionary = {}
|
||||
|
||||
|
||||
## Records the current values of every script variable on [param autoloads].
|
||||
static func take(autoloads: Array[Node]) -> AutoloadSnapshot:
|
||||
var snapshot: AutoloadSnapshot = AutoloadSnapshot.new()
|
||||
for autoload: Node in autoloads:
|
||||
var values: Dictionary = {}
|
||||
for property: Dictionary in autoload.get_property_list():
|
||||
if property.usage & PROPERTY_USAGE_SCRIPT_VARIABLE:
|
||||
var value: Variant = autoload.get(property.name)
|
||||
values[property.name] = value.duplicate() if value is Array else value
|
||||
snapshot._values[autoload] = values
|
||||
return snapshot
|
||||
|
||||
|
||||
## Puts back the values recorded by [method take].
|
||||
func restore() -> void:
|
||||
for autoload: Node in _values:
|
||||
var values: Dictionary = _values[autoload]
|
||||
for property_name: String in values:
|
||||
autoload.set(property_name, values[property_name])
|
||||
@@ -0,0 +1 @@
|
||||
uid://d1l1oehlhm5ss
|
||||
@@ -0,0 +1,56 @@
|
||||
class_name FakeHttpTransport
|
||||
extends HttpTransport
|
||||
## Stands in for the MusicServer in tests.
|
||||
##
|
||||
## Register canned responses per path with [method respond_json] or
|
||||
## [method respond_bytes], then install it with [method install]. Requests to paths
|
||||
## without a response get a 404. Responses are delivered immediately.
|
||||
|
||||
## Paths requested so far, relative to [code]Settings.default_path[/code].
|
||||
var requested_paths: Array[String] = []
|
||||
## HTTP method of each request, in the same order as [member requested_paths].
|
||||
var requested_methods: Array[int] = []
|
||||
|
||||
var _responses: Dictionary = {}
|
||||
var _previous_transport: HttpTransport
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
super(null)
|
||||
|
||||
|
||||
## Replaces the MusicServerClient transport with this fake.
|
||||
func install() -> FakeHttpTransport:
|
||||
_previous_transport = MusicServerClient.transport
|
||||
MusicServerClient.transport = self
|
||||
return self
|
||||
|
||||
|
||||
## Restores the transport that was active before [method install].
|
||||
func uninstall() -> void:
|
||||
MusicServerClient.transport = _previous_transport
|
||||
|
||||
|
||||
## Answers requests to [param path] with [param data] encoded as JSON.
|
||||
func respond_json(path: String, data: Variant, response_code: int = 200) -> void:
|
||||
respond_bytes(path, JSON.stringify(data).to_utf8_buffer(), response_code)
|
||||
|
||||
|
||||
## Answers requests to [param path] with [param body] as-is.
|
||||
func respond_bytes(path: String, body: PackedByteArray, response_code: int = 200) -> void:
|
||||
_responses[path] = {"code": response_code, "body": body}
|
||||
|
||||
|
||||
## Returns how many times [param path] has been requested.
|
||||
func count(path: String) -> int:
|
||||
return requested_paths.count(path)
|
||||
|
||||
|
||||
func request(url: String, method: int, on_complete: Callable) -> void:
|
||||
var path: String = url.trim_prefix(Settings.default_path)
|
||||
requested_paths.append(path)
|
||||
requested_methods.append(method)
|
||||
if _responses.has(path):
|
||||
on_complete.call(_responses[path].code, _responses[path].body)
|
||||
else:
|
||||
on_complete.call(404, PackedByteArray())
|
||||
@@ -0,0 +1 @@
|
||||
uid://blmtk4sw1iylj
|
||||
@@ -0,0 +1,115 @@
|
||||
class_name MainWindowTestSuite
|
||||
extends GdUnitTestSuite
|
||||
## Base for tests that run the real main window against a fake MusicServer.
|
||||
|
||||
const SONG_INFO: Array[Dictionary] = [
|
||||
{"SongNo": 0, "Soundtrack": "Chrono Trigger", "Song": "Corridors of Time"},
|
||||
{"SongNo": 1, "Soundtrack": "Final Fantasy VI", "Song": "Dancing Mad"},
|
||||
]
|
||||
|
||||
var _server: FakeHttpTransport
|
||||
var _snapshot: AutoloadSnapshot
|
||||
var _runner: GdUnitSceneRunner
|
||||
var _window: Control
|
||||
|
||||
|
||||
func before_test() -> void:
|
||||
_snapshot = AutoloadSnapshot.take([Settings, Playlist])
|
||||
Playlist.reset_playlist()
|
||||
Playlist.first_song_played = false
|
||||
Playlist.is_song_downloading = false
|
||||
Settings.player_array.clear()
|
||||
Settings.add_to_stats = true
|
||||
Settings.hide_next_track = true
|
||||
Settings.use_low_played_mode = false
|
||||
Settings.winning_score = 20
|
||||
|
||||
_server = FakeHttpTransport.new().install()
|
||||
var mp3: PackedByteArray = FileAccess.get_file_as_bytes("res://tests/data/short.mp3")
|
||||
_server.respond_json("/music/all", ["Chrono Trigger", "Final Fantasy VI"])
|
||||
_server.respond_json("/music/all/order", ["Chrono Trigger", "Final Fantasy VI"])
|
||||
_server.respond_json("/music/list", [])
|
||||
_server.respond_json("/characters", [])
|
||||
_server.respond_bytes("/music/rand", mp3)
|
||||
_server.respond_bytes("/music/addQue", PackedByteArray())
|
||||
_server.respond_json("/music/info", SONG_INFO[0])
|
||||
_server.respond_bytes("/music/reset", PackedByteArray())
|
||||
_server.respond_bytes("/music/played?song=0", PackedByteArray())
|
||||
|
||||
_runner = scene_runner("res://MainWindow.tscn")
|
||||
_window = _runner.scene()
|
||||
# The debug build lowers the winning score on startup; keep it out of reach
|
||||
Settings.winning_score = 20
|
||||
|
||||
|
||||
func after_test() -> void:
|
||||
# Let nodes queued for deletion by the window be freed before orphans are counted
|
||||
await await_idle_frame()
|
||||
_server.uninstall()
|
||||
_snapshot.restore()
|
||||
|
||||
|
||||
## Makes the next song the server hands out the one described by [param info].
|
||||
func _next_song_is(info: Dictionary) -> void:
|
||||
_server.respond_json("/music/info", info)
|
||||
|
||||
|
||||
func _press(button_path: String) -> void:
|
||||
(_window.get_node(button_path) as Button).pressed.emit()
|
||||
|
||||
|
||||
## Presses Next and waits until the first song is playing.
|
||||
func _play_first_song() -> void:
|
||||
_press("NextButton")
|
||||
await _runner.simulate_frames(2)
|
||||
|
||||
|
||||
## Adds a player through the name field, the same way the user does.
|
||||
func _add_player(player_name: String) -> PlayerObject:
|
||||
var name_field: TextEdit = _window.get_node(
|
||||
"Players/VBoxContainer/AddPlayerContainer/PlayerNameField")
|
||||
name_field.text = player_name
|
||||
_runner.invoke("add_player")
|
||||
return Settings.player_array.back() if !Settings.player_array.is_empty() else null
|
||||
|
||||
|
||||
## Returns the row in the player list that shows [param player_object].
|
||||
func _player_row(player_object: PlayerObject) -> Player:
|
||||
var rows: Node = _window.get_node(
|
||||
"Players/VBoxContainer/ScrollContainer/PlayersListVBoxContainer")
|
||||
for row: Player in rows.get_children():
|
||||
if row.player_object == player_object:
|
||||
return row
|
||||
return null
|
||||
|
||||
|
||||
## Removes a player with the remove button in its row.
|
||||
func _remove_player(player_object: PlayerObject) -> void:
|
||||
(_player_row(player_object).get_node("HBoxContainer/RemovePlayer") as BaseButton) \
|
||||
.pressed.emit()
|
||||
await _runner.simulate_frames(1)
|
||||
|
||||
|
||||
## Presses [param keys] together, e.g. [code][KEY_ALT, KEY_1][/code], then releases them.
|
||||
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()
|
||||
|
||||
|
||||
## Gives a point with Alt+<number>.
|
||||
func _give_point_with_keybind(number: int) -> void:
|
||||
await _press_keys([KEY_ALT, (KEY_0 + number) as Key])
|
||||
|
||||
|
||||
## Takes a point with Ctrl+Alt+<number>.
|
||||
func _take_point_with_keybind(number: int) -> void:
|
||||
await _press_keys([KEY_CTRL, KEY_ALT, (KEY_0 + number) as Key])
|
||||
|
||||
|
||||
func _scores() -> Array:
|
||||
return Settings.player_array.map(func(p: PlayerObject) -> int: return p.player_score)
|
||||
@@ -0,0 +1 @@
|
||||
uid://cjplwhulmw00
|
||||
@@ -1,261 +0,0 @@
|
||||
class_name FullFlowTest
|
||||
extends GdUnitTestSuite
|
||||
|
||||
|
||||
func test_fetch_song_list_at_start_populates_playlist():
|
||||
# Mock Settings.make_request2
|
||||
var original_make_request = Settings.make_request2
|
||||
|
||||
Settings.make_request2 = func(address: String, func_name: Callable, expect_data: bool) -> void:
|
||||
if address == "/music/list":
|
||||
func_name.call([
|
||||
{"SongNo": 1, "Soundtrack": "Test Soundtrack 1", "Song": "Test Song 1"},
|
||||
{"SongNo": 2, "Soundtrack": "Test Soundtrack 2", "Song": "Test Song 2"}
|
||||
])
|
||||
|
||||
Playlist.clear_song_list()
|
||||
|
||||
var main_window = MainWindow.new()
|
||||
main_window.fetch_song_list_at_start()
|
||||
|
||||
# Wait for async
|
||||
await main_window.get_tree().create_timer(0.1).timeout
|
||||
|
||||
assert_eq(Playlist.song_object_array.size(), 2)
|
||||
|
||||
Settings.make_request2 = original_make_request
|
||||
|
||||
|
||||
func test_fetch_song_adds_to_playlist():
|
||||
# Mock Settings.make_request2
|
||||
var original_make_request = Settings.make_request2
|
||||
var rand_called = false
|
||||
var info_called = false
|
||||
var add_que_called = false
|
||||
|
||||
Settings.make_request2 = func(address: String, func_name: Callable, expect_data: bool) -> void:
|
||||
if address == "/music/rand":
|
||||
rand_called = true
|
||||
var test_stream = TestHelpers.create_mock_audio_stream()
|
||||
func_name.call(test_stream.get_data())
|
||||
elif address == "/music/info":
|
||||
info_called = true
|
||||
func_name.call({"SongNo": 3, "Soundtrack": "New Soundtrack", "Song": "New Song"})
|
||||
elif address == "/music/addQue":
|
||||
add_que_called = true
|
||||
func_name.call()
|
||||
|
||||
Playlist.clear_song_list()
|
||||
Playlist.is_song_downloading = false
|
||||
|
||||
var main_window = MainWindow.new()
|
||||
main_window.fetch_song()
|
||||
|
||||
# Wait for async
|
||||
await main_window.get_tree().create_timer(0.2).timeout
|
||||
|
||||
assert_true(rand_called)
|
||||
assert_true(info_called)
|
||||
assert_true(add_que_called)
|
||||
assert_eq(Playlist.song_object_array.size(), 1)
|
||||
|
||||
Settings.make_request2 = original_make_request
|
||||
|
||||
|
||||
func test_search_function_filters_soundtracks():
|
||||
var main_window = MainWindow.new()
|
||||
|
||||
# Setup test data
|
||||
main_window.soundtracks = ["Soundtrack 1", "Soundtrack 2", "Test Soundtrack"]
|
||||
|
||||
# This is a simplified test - in reality, search would be more complex
|
||||
# We're testing that the search_view gets the list
|
||||
var mock_search_view = mock(Control)
|
||||
mock_search_view.get_list_of_soundtracks = func():
|
||||
pass # Mock implementation
|
||||
|
||||
main_window.search_view = mock_search_view
|
||||
|
||||
# Call the method that populates the list
|
||||
main_window.get_suggestion_list()
|
||||
|
||||
# Wait for async
|
||||
await main_window.get_tree().create_timer(0.1).timeout
|
||||
|
||||
# Verify search_view was called
|
||||
assert_true(mock_search_view.get_list_of_soundtracks.was_called())
|
||||
|
||||
|
||||
func test_player_list_updates_on_add():
|
||||
var main_window = MainWindow.new()
|
||||
|
||||
Settings.player_array.clear()
|
||||
Settings.edit_players = false
|
||||
|
||||
# Setup mock container
|
||||
var mock_container = mock(VBoxContainer)
|
||||
main_window.players_list_container = mock_container
|
||||
|
||||
# Add a player
|
||||
var player_obj = PlayerObject.new("Test Player", 0)
|
||||
Settings.player_array.append(player_obj)
|
||||
|
||||
main_window.load_players()
|
||||
|
||||
# Verify player was added to container
|
||||
assert_eq(mock_container.get_child_count(), 1)
|
||||
|
||||
|
||||
func test_player_list_updates_on_remove():
|
||||
var main_window = MainWindow.new()
|
||||
|
||||
Settings.player_array.clear()
|
||||
|
||||
# Setup mock container
|
||||
var mock_container = mock(VBoxContainer)
|
||||
main_window.players_list_container = mock_container
|
||||
|
||||
# Add players
|
||||
for i in range(3):
|
||||
var player_obj = PlayerObject.new("Player %d" % i, i)
|
||||
Settings.player_array.append(player_obj)
|
||||
|
||||
main_window.load_players()
|
||||
assert_eq(mock_container.get_child_count(), 3)
|
||||
|
||||
# Remove a player
|
||||
main_window._on_player_removed(1)
|
||||
|
||||
assert_eq(Settings.player_array.size(), 2)
|
||||
assert_eq(mock_container.get_child_count(), 2)
|
||||
|
||||
|
||||
func test_point_given_updates_log_and_playlist():
|
||||
var main_window = MainWindow.new()
|
||||
|
||||
# Setup
|
||||
Settings.player_array.clear()
|
||||
var player = PlayerObject.new("Test Player", 0)
|
||||
Settings.player_array.append(player)
|
||||
|
||||
Playlist.clear_song_list()
|
||||
var song = TestHelpers.create_test_song_object(1)
|
||||
Playlist.add_to_playlist(song)
|
||||
Playlist.currently_playing_song = 0
|
||||
|
||||
# Mock log window
|
||||
var mock_log = mock(Control)
|
||||
mock_log.add_log_row = func(text: String):
|
||||
pass
|
||||
|
||||
main_window.log_window = mock_log
|
||||
|
||||
# Emit point given
|
||||
player.player_point_given.emit()
|
||||
|
||||
# Wait for async
|
||||
await main_window.get_tree().create_timer(0.1).timeout
|
||||
|
||||
# Verify log was updated
|
||||
assert_true(mock_log.add_log_row.was_called())
|
||||
# Verify playlist was updated
|
||||
assert_true(song.players_given_point.has(0))
|
||||
|
||||
|
||||
func test_point_taken_updates_log_and_playlist():
|
||||
var main_window = MainWindow.new()
|
||||
|
||||
# Setup
|
||||
Settings.player_array.clear()
|
||||
var player = PlayerObject.new("Test Player", 0)
|
||||
player.player_score = 5
|
||||
Settings.player_array.append(player)
|
||||
|
||||
Playlist.clear_song_list()
|
||||
var song = TestHelpers.create_test_song_object(1)
|
||||
song.players_given_point.append(0)
|
||||
Playlist.add_to_playlist(song)
|
||||
Playlist.currently_playing_song = 0
|
||||
|
||||
# Mock log window
|
||||
var mock_log = mock(Control)
|
||||
mock_log.add_log_row = func(text: String):
|
||||
pass
|
||||
|
||||
main_window.log_window = mock_log
|
||||
|
||||
# Emit point taken
|
||||
player.player_point_taken.emit()
|
||||
|
||||
# Wait for async
|
||||
await main_window.get_tree().create_timer(0.1).timeout
|
||||
|
||||
# Verify log was updated
|
||||
assert_true(mock_log.add_log_row.was_called())
|
||||
# Verify playlist was updated
|
||||
assert_false(song.players_given_point.has(0))
|
||||
|
||||
|
||||
func test_player_won_shows_winner_window():
|
||||
var main_window = MainWindow.new()
|
||||
|
||||
# Setup
|
||||
Settings.player_array.clear()
|
||||
var player = PlayerObject.new("Test Player", 0)
|
||||
Settings.player_array.append(player)
|
||||
|
||||
# Mock winner window
|
||||
var mock_winner_window = mock(Control)
|
||||
mock_winner_window.show_winner = func(winning_player_id: int):
|
||||
pass
|
||||
|
||||
main_window.winner_window = mock_winner_window
|
||||
|
||||
# Emit winner
|
||||
player.winner_triggerd.emit()
|
||||
|
||||
# Wait for async
|
||||
await main_window.get_tree().create_timer(0.1).timeout
|
||||
|
||||
# Verify winner window was shown
|
||||
assert_true(mock_winner_window.show_winner.was_called_with([0]))
|
||||
|
||||
|
||||
func test_show_answer_pressed_marks_song_as_answered():
|
||||
var main_window = MainWindow.new()
|
||||
|
||||
Playlist.clear_song_list()
|
||||
var song = TestHelpers.create_test_song_object(1)
|
||||
song.is_answered = false
|
||||
Playlist.add_to_playlist(song)
|
||||
Playlist.currently_playing_song = 0
|
||||
|
||||
main_window.show_answer_pressed()
|
||||
|
||||
assert_true(song.is_answered)
|
||||
|
||||
|
||||
func test_update_song_list_refreshes_ui():
|
||||
var main_window = MainWindow.new()
|
||||
|
||||
# Setup mock UI elements
|
||||
var mock_soundtrack_label = mock(Label)
|
||||
var mock_song_label = mock(Label)
|
||||
var mock_music_list = mock(VBoxContainer)
|
||||
|
||||
main_window.soundtrack_label = mock_soundtrack_label
|
||||
main_window.song_label = mock_song_label
|
||||
main_window.music_list = mock_music_list
|
||||
|
||||
Playlist.clear_song_list()
|
||||
var song = TestHelpers.create_test_song_object(1, "Test Soundtrack", "Test Song")
|
||||
song.is_answered = true
|
||||
song.has_played = true
|
||||
Playlist.add_to_playlist(song)
|
||||
Playlist.currently_playing_song = 0
|
||||
|
||||
main_window.update_song_list()
|
||||
|
||||
# Verify labels were updated
|
||||
assert_eq(mock_soundtrack_label.text, "Test Soundtrack")
|
||||
assert_eq(mock_song_label.text, "Test Song")
|
||||
@@ -0,0 +1,80 @@
|
||||
extends MainWindowTestSuite
|
||||
## Main window flows: startup, playing songs and resetting.
|
||||
|
||||
|
||||
func test_startup_loads_soundtracks_and_preloads_a_song() -> void:
|
||||
assert_int(_server.count("/music/list")).is_equal(1)
|
||||
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((_window.get_node("NextButton") as Button).disabled).is_false()
|
||||
|
||||
|
||||
func test_low_played_mode_fetches_from_low_played_endpoint() -> void:
|
||||
Settings.use_low_played_mode = true
|
||||
_server.respond_bytes(
|
||||
"/music/rand/low", FileAccess.get_file_as_bytes("res://tests/data/short.mp3"))
|
||||
|
||||
_runner.invoke("fetch_song")
|
||||
|
||||
assert_int(_server.count("/music/rand/low")).is_equal(1)
|
||||
|
||||
|
||||
func test_next_plays_preloaded_song_and_fetches_another() -> void:
|
||||
_next_song_is(SONG_INFO[1])
|
||||
|
||||
_press("NextButton")
|
||||
await _runner.simulate_frames(2)
|
||||
|
||||
assert_int(Playlist.currently_playing_song).is_equal(0)
|
||||
assert_bool(Playlist.song_object_array[0].has_played).is_true()
|
||||
assert_int(Playlist.song_object_array.size()).is_equal(2)
|
||||
assert_int(_server.count("/music/rand")).is_equal(2)
|
||||
|
||||
|
||||
func test_title_is_hidden_until_answer_is_shown() -> void:
|
||||
var soundtrack_label: Label = _window.get_node("VBoxContainer/SoundtrackLabel")
|
||||
_press("NextButton")
|
||||
await _runner.simulate_frames(2)
|
||||
|
||||
assert_str(soundtrack_label.text).is_equal("????????")
|
||||
|
||||
_press("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")
|
||||
await _runner.simulate_frames(2)
|
||||
|
||||
_press("ResetPlaylistButton")
|
||||
|
||||
assert_int(_server.count("/music/reset")).is_equal(1)
|
||||
assert_int(Playlist.currently_playing_song).is_equal(-1)
|
||||
assert_int(Playlist.song_object_array.size()).is_equal(1)
|
||||
assert_bool(Playlist.song_object_array[0].has_played).is_false()
|
||||
|
||||
|
||||
func test_reset_points_sets_all_scores_to_zero() -> void:
|
||||
_add_player("Crono").add_point()
|
||||
_add_player("Marle").add_point()
|
||||
|
||||
_press("ResetPointsButton")
|
||||
|
||||
assert_int(Settings.player_array[0].player_score).is_equal(0)
|
||||
assert_int(Settings.player_array[1].player_score).is_equal(0)
|
||||
|
||||
|
||||
func test_cannot_add_more_than_max_players() -> void:
|
||||
for i: int in Settings.MAX_NUMBER_OF_PLAYERS + 1:
|
||||
_add_player("Player %d" % i)
|
||||
|
||||
assert_int(Settings.player_array.size()).is_equal(Settings.MAX_NUMBER_OF_PLAYERS)
|
||||
|
||||
|
||||
func test_cannot_add_player_without_name() -> void:
|
||||
_add_player("")
|
||||
|
||||
assert_array(Settings.player_array).is_empty()
|
||||
@@ -0,0 +1 @@
|
||||
uid://c1a77blpbbckh
|
||||
@@ -0,0 +1,152 @@
|
||||
extends MainWindowTestSuite
|
||||
## Alt+<number> gives a point to the player in that row of the player list and
|
||||
## Ctrl+Alt+<number> takes one away. These tests check that the keybinds keep
|
||||
## following the rows while players are added and removed, and that points always
|
||||
## end up with the right player.
|
||||
|
||||
var _crono: PlayerObject
|
||||
var _marle: PlayerObject
|
||||
var _lucca: PlayerObject
|
||||
|
||||
|
||||
func before_test() -> void:
|
||||
super()
|
||||
_crono = _add_player("Crono")
|
||||
_marle = _add_player("Marle")
|
||||
_lucca = _add_player("Lucca")
|
||||
await _play_first_song()
|
||||
|
||||
|
||||
func _song_points() -> Array[String]:
|
||||
return Playlist.song_object_array[Playlist.currently_playing_song].players_given_point
|
||||
|
||||
|
||||
func test_alt_number_gives_point_to_player_in_that_row() -> void:
|
||||
await _give_point_with_keybind(2)
|
||||
|
||||
assert_array(_scores()).contains_exactly([0, 1, 0])
|
||||
assert_array(_song_points()).contains_exactly([_marle.id])
|
||||
|
||||
|
||||
func test_ctrl_alt_number_takes_point_from_player_in_that_row() -> void:
|
||||
await _give_point_with_keybind(3)
|
||||
await _give_point_with_keybind(3)
|
||||
|
||||
await _take_point_with_keybind(3)
|
||||
|
||||
assert_array(_scores()).contains_exactly([0, 0, 1])
|
||||
|
||||
|
||||
func test_taking_last_point_removes_player_from_song() -> void:
|
||||
await _give_point_with_keybind(1)
|
||||
|
||||
await _take_point_with_keybind(1)
|
||||
|
||||
assert_int(_crono.player_score).is_equal(0)
|
||||
assert_array(_song_points()).is_empty()
|
||||
|
||||
|
||||
func test_each_keybind_only_affects_its_own_player() -> void:
|
||||
await _give_point_with_keybind(1)
|
||||
await _give_point_with_keybind(3)
|
||||
await _give_point_with_keybind(3)
|
||||
|
||||
assert_int(_crono.player_score).is_equal(1)
|
||||
assert_int(_marle.player_score).is_equal(0)
|
||||
assert_int(_lucca.player_score).is_equal(2)
|
||||
assert_array(_song_points()).contains_exactly([_crono.id, _lucca.id])
|
||||
|
||||
|
||||
func test_number_without_player_does_nothing() -> void:
|
||||
await _give_point_with_keybind(4)
|
||||
|
||||
assert_array(_scores()).contains_exactly([0, 0, 0])
|
||||
assert_array(_song_points()).is_empty()
|
||||
|
||||
|
||||
func test_keybinds_move_up_when_first_player_is_removed() -> void:
|
||||
await _remove_player(_crono)
|
||||
|
||||
await _give_point_with_keybind(1)
|
||||
await _give_point_with_keybind(2)
|
||||
await _give_point_with_keybind(2)
|
||||
|
||||
assert_int(_marle.player_score).is_equal(1)
|
||||
assert_int(_lucca.player_score).is_equal(2)
|
||||
assert_array(_song_points()).contains_exactly([_marle.id, _lucca.id])
|
||||
|
||||
|
||||
func test_keybind_after_last_row_does_nothing_after_removal() -> void:
|
||||
await _remove_player(_marle)
|
||||
|
||||
await _give_point_with_keybind(3)
|
||||
|
||||
assert_int(_crono.player_score).is_equal(0)
|
||||
assert_int(_lucca.player_score).is_equal(0)
|
||||
|
||||
|
||||
func test_removing_player_removes_only_their_points() -> void:
|
||||
await _give_point_with_keybind(1)
|
||||
await _give_point_with_keybind(2)
|
||||
await _give_point_with_keybind(3)
|
||||
|
||||
await _remove_player(_marle)
|
||||
|
||||
assert_array(_song_points()).contains_exactly([_crono.id, _lucca.id])
|
||||
assert_int(_crono.player_score).is_equal(1)
|
||||
assert_int(_lucca.player_score).is_equal(1)
|
||||
|
||||
|
||||
func test_new_player_gets_next_free_keybind_after_removal() -> void:
|
||||
await _remove_player(_marle)
|
||||
var robo: PlayerObject = _add_player("Robo")
|
||||
|
||||
await _give_point_with_keybind(2)
|
||||
await _give_point_with_keybind(3)
|
||||
|
||||
assert_int(_lucca.player_score).is_equal(1)
|
||||
assert_int(robo.player_score).is_equal(1)
|
||||
assert_int(_crono.player_score).is_equal(0)
|
||||
assert_array(_song_points()).contains_exactly([_lucca.id, robo.id])
|
||||
|
||||
|
||||
func test_new_player_does_not_inherit_removed_players_points() -> void:
|
||||
await _give_point_with_keybind(3)
|
||||
await _remove_player(_lucca)
|
||||
|
||||
var robo: PlayerObject = _add_player("Robo")
|
||||
|
||||
assert_int(robo.player_score).is_equal(0)
|
||||
assert_array(_song_points()).is_empty()
|
||||
|
||||
|
||||
func test_points_follow_players_through_several_changes() -> void:
|
||||
await _give_point_with_keybind(1)
|
||||
await _remove_player(_crono)
|
||||
var robo: PlayerObject = _add_player("Robo")
|
||||
await _give_point_with_keybind(1)
|
||||
await _remove_player(_marle)
|
||||
var ayla: PlayerObject = _add_player("Ayla")
|
||||
await _give_point_with_keybind(3)
|
||||
await _give_point_with_keybind(2)
|
||||
await _take_point_with_keybind(2)
|
||||
|
||||
# Rows are now: Lucca, Robo, Ayla
|
||||
assert_array(Settings.player_array).contains_exactly([_lucca, robo, ayla])
|
||||
assert_int(_lucca.player_score).is_equal(0)
|
||||
assert_int(robo.player_score).is_equal(0)
|
||||
assert_int(ayla.player_score).is_equal(1)
|
||||
assert_array(_song_points()).contains_exactly([ayla.id])
|
||||
|
||||
|
||||
func test_player_list_shows_rows_in_player_order() -> void:
|
||||
await _remove_player(_crono)
|
||||
var robo: PlayerObject = _add_player("Robo")
|
||||
|
||||
var rows: Array[Node] = _window.get_node(
|
||||
"Players/VBoxContainer/ScrollContainer/PlayersListVBoxContainer").get_children()
|
||||
|
||||
assert_array(rows.map(func(row: Player) -> PlayerObject: return row.player_object)) \
|
||||
.contains_exactly([_marle, _lucca, robo])
|
||||
assert_array(rows.map(func(row: Player) -> int: return row.list_index)) \
|
||||
.contains_exactly([0, 1, 2])
|
||||
@@ -0,0 +1 @@
|
||||
uid://k3q6jrbpg3wa
|
||||
@@ -0,0 +1,145 @@
|
||||
extends GdUnitTestSuite
|
||||
|
||||
var _server: FakeHttpTransport
|
||||
var _snapshot: AutoloadSnapshot
|
||||
var _runner: GdUnitSceneRunner
|
||||
var _window: Control
|
||||
|
||||
|
||||
func before_test() -> void:
|
||||
_snapshot = AutoloadSnapshot.take([Settings])
|
||||
Settings.currently_syncing = false
|
||||
_server = FakeHttpTransport.new().install()
|
||||
_server.respond_bytes("/sync", PackedByteArray())
|
||||
_runner = scene_runner("res://SyncWindow.tscn")
|
||||
_window = _runner.scene()
|
||||
|
||||
|
||||
func after_test() -> void:
|
||||
_server.uninstall()
|
||||
_snapshot.restore()
|
||||
|
||||
|
||||
func _node(unique_name: String) -> Control:
|
||||
return _window.get_node("%" + unique_name)
|
||||
|
||||
|
||||
func _finish_sync(result: Dictionary) -> void:
|
||||
_server.respond_json("/sync/progress", result)
|
||||
_runner.invoke("_on_sync_timeout")
|
||||
|
||||
|
||||
func _empty_result() -> Dictionary:
|
||||
return {
|
||||
"soundtracks_added": [],
|
||||
"soundtracks_re_added": [],
|
||||
"soundtracks_changed_title": {},
|
||||
"soundtracks_changed_content": [],
|
||||
"soundtracks_removed": [],
|
||||
"catched_errors": [],
|
||||
"total_time": "00:00:03",
|
||||
}
|
||||
|
||||
|
||||
func test_start_sync_asks_server_and_shows_progress() -> void:
|
||||
monitor_signals(_window, false)
|
||||
|
||||
_runner.invoke("start_sync")
|
||||
|
||||
assert_array(_server.requested_paths).contains_exactly(["/sync"])
|
||||
assert_bool(Settings.currently_syncing).is_true()
|
||||
assert_bool(_node("ProgressHBoxContainer").visible).is_true()
|
||||
assert_str(_node("SyncTitleLabel").text).is_equal("Sync in Progress")
|
||||
await assert_signal(_window).is_emitted("sync_started")
|
||||
|
||||
|
||||
func test_start_sync_does_nothing_while_already_syncing() -> void:
|
||||
Settings.currently_syncing = true
|
||||
|
||||
_runner.invoke("start_sync")
|
||||
|
||||
assert_array(_server.requested_paths).is_empty()
|
||||
|
||||
|
||||
func test_progress_updates_progress_bar() -> void:
|
||||
_runner.invoke("start_sync")
|
||||
_server.respond_json("/sync/progress", {"progress": "42", "time_spent": "00:00:12"})
|
||||
|
||||
_runner.invoke("_on_sync_timeout")
|
||||
|
||||
assert_float(_node("SyncProgressBar").value).is_equal(42.0)
|
||||
assert_str(_node("SyncTime").text).is_equal("00:00:12")
|
||||
|
||||
|
||||
func test_finished_sync_shows_only_sections_with_changes() -> void:
|
||||
_runner.invoke("start_sync")
|
||||
monitor_signals(_window, false)
|
||||
var result: Dictionary = _empty_result()
|
||||
result.soundtracks_added = ["Chrono Cross", "Xenogears"]
|
||||
result.soundtracks_removed = ["Bad Rip"]
|
||||
|
||||
_finish_sync(result)
|
||||
|
||||
assert_bool(_node("SoundtracksAddedVBox").visible).is_true()
|
||||
assert_bool(_node("SoundtracksRemovedVBox").visible).is_true()
|
||||
assert_bool(_node("SoundtracksReAddedVBox").visible).is_false()
|
||||
assert_bool(_node("SoundtracksChangedTitleVBox").visible).is_false()
|
||||
assert_bool(_node("NoChangesLabel").visible).is_false()
|
||||
# Header plus one label per soundtrack
|
||||
assert_int(_node("SoundtracksAddedVBox").get_child_count()).is_equal(3)
|
||||
assert_str(_node("SyncTime").text).is_equal("Total time: 00:00:03")
|
||||
assert_bool(Settings.currently_syncing).is_false()
|
||||
await assert_signal(_window).is_emitted("sync_finished")
|
||||
|
||||
|
||||
func test_separator_shown_only_between_visible_sections() -> void:
|
||||
_runner.invoke("start_sync")
|
||||
var result: Dictionary = _empty_result()
|
||||
result.soundtracks_added = ["Chrono Cross"]
|
||||
result.soundtracks_removed = ["Bad Rip"]
|
||||
|
||||
_finish_sync(result)
|
||||
|
||||
assert_bool(_node("SoundtracksAddedSeparator").visible).is_true()
|
||||
assert_bool(_node("SoundtracksRemovedSeparator").visible).is_false()
|
||||
|
||||
|
||||
func test_changed_titles_list_old_and_new_title() -> void:
|
||||
_runner.invoke("start_sync")
|
||||
var result: Dictionary = _empty_result()
|
||||
result.soundtracks_changed_title = {"FF7": "Final Fantasy VII"}
|
||||
|
||||
_finish_sync(result)
|
||||
|
||||
var vbox: Control = _node("SoundtracksChangedTitleVBox")
|
||||
assert_bool(vbox.visible).is_true()
|
||||
assert_str((vbox.get_child(1) as Label).text).is_equal(
|
||||
"FF7\nchanged title to\nFinal Fantasy VII")
|
||||
|
||||
|
||||
func test_no_changes_shows_no_changes_label() -> void:
|
||||
_runner.invoke("start_sync")
|
||||
|
||||
_finish_sync(_empty_result())
|
||||
|
||||
assert_bool(_node("NoChangesLabel").visible).is_true()
|
||||
|
||||
|
||||
func test_errors_are_listed() -> void:
|
||||
_runner.invoke("start_sync")
|
||||
var result: Dictionary = _empty_result()
|
||||
result.catched_errors = ["Could not read tags in broken.mp3"]
|
||||
|
||||
_finish_sync(result)
|
||||
|
||||
assert_bool(_node("CatchedErrorsVBox").visible).is_true()
|
||||
|
||||
|
||||
func test_reopening_after_sync_shows_result_without_syncing_again() -> void:
|
||||
_runner.invoke("start_sync")
|
||||
_finish_sync(_empty_result())
|
||||
|
||||
_runner.invoke("start_sync")
|
||||
|
||||
assert_int(_server.count("/sync")).is_equal(1)
|
||||
assert_str(_node("SyncTitleLabel").text).is_equal("Sync Result")
|
||||
@@ -0,0 +1 @@
|
||||
uid://bvi8cyucyd8j4
|
||||
@@ -1,37 +0,0 @@
|
||||
class_name MockHTTPRequest
|
||||
extends HTTPRequest
|
||||
|
||||
signal mock_request_completed(result: int, response_code: int, headers: PackedStringArray, body: PackedByteArray)
|
||||
|
||||
var mock_responses: Dictionary = {}
|
||||
var request_count: int = 0
|
||||
|
||||
func _ready() -> void:
|
||||
request_completed.connect(_on_request_completed)
|
||||
|
||||
func _on_request_completed(result: int, response_code: int, headers: PackedStringArray, body: PackedByteArray) -> void:
|
||||
pass
|
||||
|
||||
func add_mock_response(url: String, response_code: int = 200, body: PackedByteArray = PackedByteArray(), headers: PackedStringArray = PackedStringArray()) -> void:
|
||||
mock_responses[url] = {
|
||||
"code": response_code,
|
||||
"body": body,
|
||||
"headers": headers
|
||||
}
|
||||
|
||||
func request(url: String, headers: PackedStringArray = [], method: int = HTTPClient.METHOD_GET, body: PackedByteArray = PackedByteArray()) -> int:
|
||||
request_count += 1
|
||||
if mock_responses.has(url):
|
||||
var response = mock_responses[url]
|
||||
emit_signal("request_completed", OK, response.code, response.headers, response.body)
|
||||
return OK
|
||||
else:
|
||||
emit_signal("request_completed", FAILED, 404, [], PackedByteArray())
|
||||
return FAILED
|
||||
|
||||
func get_request_count() -> int:
|
||||
return request_count
|
||||
|
||||
func reset() -> void:
|
||||
request_count = 0
|
||||
mock_responses.clear()
|
||||
@@ -1,23 +0,0 @@
|
||||
class_name TestHelpers
|
||||
extends RefCounted
|
||||
|
||||
static func create_mock_audio_stream() -> AudioStreamMP3:
|
||||
var stream = AudioStreamMP3.new()
|
||||
stream.data = PackedByteArray()
|
||||
return stream
|
||||
|
||||
static func create_test_song_object(song_no: int = 1, soundtrack: String = "Test Soundtrack", song_title: String = "Test Song") -> SongObject:
|
||||
var song_obj = SongObject.new()
|
||||
song_obj.song_number = song_no
|
||||
song_obj.soundtrack_title = soundtrack
|
||||
song_obj.song_title = song_title
|
||||
song_obj.song = create_mock_audio_stream()
|
||||
return song_obj
|
||||
|
||||
static func setup_test_environment() -> void:
|
||||
Settings.is_debug = true
|
||||
Settings.stop_after_current = false
|
||||
Settings.auto_repeat_song = false
|
||||
|
||||
static func cleanup_test_environment() -> void:
|
||||
Settings.is_debug = false
|
||||
@@ -1,26 +0,0 @@
|
||||
class_name BasicTest
|
||||
extends GdUnitTestSuite
|
||||
|
||||
|
||||
func test_true_is_true():
|
||||
assert_true(true)
|
||||
|
||||
|
||||
func test_false_is_false():
|
||||
assert_false(false)
|
||||
|
||||
|
||||
func test_one_equals_one():
|
||||
assert_eq(1, 1)
|
||||
|
||||
|
||||
func test_arrays_work():
|
||||
var arr = [1, 2, 3]
|
||||
assert_eq(arr.size(), 3)
|
||||
assert_eq(arr[0], 1)
|
||||
|
||||
|
||||
func test_dictionaries_work():
|
||||
var dict = {"key": "value", "number": 42}
|
||||
assert_eq(dict["key"], "value")
|
||||
assert_eq(dict["number"], 42)
|
||||
@@ -1,357 +0,0 @@
|
||||
class_name MainWindowTest
|
||||
extends GdUnitTestSuite
|
||||
|
||||
|
||||
func test_next_button_pressed_connects_to_play_next_song():
|
||||
var main_window = MainWindow.new()
|
||||
var mock_next_button = mock(Button)
|
||||
|
||||
main_window.next_button = mock_next_button
|
||||
|
||||
# Connect the signal
|
||||
main_window._ready()
|
||||
|
||||
# Simulate button press
|
||||
mock_next_button.emit_signal("pressed")
|
||||
|
||||
# Verify the connection was made (play_next_song should be callable)
|
||||
assert_true(main_window.is_connected("next_button", "pressed", main_window, "play_next_song"))
|
||||
|
||||
|
||||
func test_show_answer_button_pressed_connects_to_handler():
|
||||
var main_window = MainWindow.new()
|
||||
var mock_show_answer_button = mock(Button)
|
||||
|
||||
main_window.show_answer_button = mock_show_answer_button
|
||||
main_window._ready()
|
||||
|
||||
assert_true(main_window.is_connected("show_answer_button", "pressed", main_window, "show_answer_pressed"))
|
||||
|
||||
|
||||
func test_search_button_pressed_connects_to_show_search():
|
||||
var main_window = MainWindow.new()
|
||||
var mock_search_button = mock(Button)
|
||||
|
||||
main_window.search_button = mock_search_button
|
||||
main_window._ready()
|
||||
|
||||
assert_true(main_window.is_connected("search_button", "pressed", main_window, "show_search"))
|
||||
|
||||
|
||||
func test_sync_button_pressed_connects_to_sync_soundtracks():
|
||||
var main_window = MainWindow.new()
|
||||
var mock_sync_button = mock(Button)
|
||||
|
||||
main_window.sync_button = mock_sync_button
|
||||
main_window._ready()
|
||||
|
||||
assert_true(main_window.is_connected("sync_button", "pressed", main_window, "sync_soundtracks"))
|
||||
|
||||
|
||||
func test_sound_test_button_pressed_connects_to_get_sound_test_song():
|
||||
var main_window = MainWindow.new()
|
||||
var mock_sound_test_button = mock(Button)
|
||||
var mock_music_player = mock(MusicPlayer)
|
||||
|
||||
main_window.sound_test_button = mock_sound_test_button
|
||||
main_window.music_player_container = mock_music_player
|
||||
main_window._ready()
|
||||
|
||||
assert_true(main_window.is_connected("sound_test_button", "pressed", mock_music_player, "get_sound_test_song"))
|
||||
|
||||
|
||||
func test_settings_button_pressed_connects_to_show_settings():
|
||||
var main_window = MainWindow.new()
|
||||
var mock_settings_button = mock(Button)
|
||||
|
||||
main_window.settings_button = mock_settings_button
|
||||
main_window._ready()
|
||||
|
||||
assert_true(main_window.is_connected("settings_button", "pressed", main_window, "show_settings"))
|
||||
|
||||
|
||||
func test_reset_playlist_button_pressed_connects_to_reset_playlist():
|
||||
var main_window = MainWindow.new()
|
||||
var mock_reset_button = mock(Button)
|
||||
|
||||
main_window.reset_playlist_button = mock_reset_button
|
||||
main_window._ready()
|
||||
|
||||
assert_true(main_window.is_connected("reset_playlist_button", "pressed", main_window, "reset_playlist"))
|
||||
|
||||
|
||||
func test_reset_points_button_pressed_connects_to_reset_points():
|
||||
var main_window = MainWindow.new()
|
||||
var mock_reset_points_button = mock(Button)
|
||||
|
||||
main_window.reset_points_button = mock_reset_points_button
|
||||
main_window._ready()
|
||||
|
||||
assert_true(main_window.is_connected("reset_points_button", "pressed", main_window, "reset_points"))
|
||||
|
||||
|
||||
func test_add_players_button_toggles_visibility():
|
||||
var main_window = MainWindow.new()
|
||||
var mock_add_players_button = mock(TextureButton)
|
||||
var mock_container = mock(HBoxContainer)
|
||||
|
||||
main_window.add_players_button = mock_add_players_button
|
||||
main_window.add_player_container = mock_container
|
||||
|
||||
Settings.edit_players = false
|
||||
mock_container.visible = false
|
||||
|
||||
main_window.add_players()
|
||||
|
||||
assert_true(Settings.edit_players)
|
||||
assert_true(mock_container.visible)
|
||||
|
||||
main_window.add_players()
|
||||
|
||||
assert_false(Settings.edit_players)
|
||||
assert_false(mock_container.visible)
|
||||
|
||||
|
||||
func test_add_player_adds_to_array():
|
||||
var main_window = MainWindow.new()
|
||||
|
||||
Settings.player_array.clear()
|
||||
Settings.edit_players = true
|
||||
|
||||
var initial_count = Settings.player_array.size()
|
||||
|
||||
main_window.new_player_name_field = mock(TextEdit)
|
||||
main_window.new_player_name_field.text = "Test Player"
|
||||
|
||||
main_window.add_player()
|
||||
|
||||
assert_eq(Settings.player_array.size(), initial_count + 1)
|
||||
assert_eq(Settings.player_array[-1].player_name, "Test Player")
|
||||
|
||||
|
||||
func test_add_player_does_not_add_empty_name():
|
||||
var main_window = MainWindow.new()
|
||||
|
||||
Settings.player_array.clear()
|
||||
Settings.edit_players = true
|
||||
|
||||
main_window.new_player_name_field = mock(TextEdit)
|
||||
main_window.new_player_name_field.text = ""
|
||||
|
||||
var initial_count = Settings.player_array.size()
|
||||
|
||||
main_window.add_player()
|
||||
|
||||
assert_eq(Settings.player_array.size(), initial_count)
|
||||
|
||||
|
||||
func test_add_player_does_not_exceed_max():
|
||||
var main_window = MainWindow.new()
|
||||
|
||||
Settings.player_array.clear()
|
||||
Settings.edit_players = true
|
||||
|
||||
# Fill up to max
|
||||
for i in range(Settings.MAX_NUMBER_OF_PLAYERS):
|
||||
var player_obj = PlayerObject.new("Player %d" % i, i)
|
||||
Settings.player_array.append(player_obj)
|
||||
|
||||
main_window.new_player_name_field = mock(TextEdit)
|
||||
main_window.new_player_name_field.text = "Extra Player"
|
||||
|
||||
var initial_count = Settings.player_array.size()
|
||||
|
||||
main_window.add_player()
|
||||
|
||||
assert_eq(Settings.player_array.size(), initial_count)
|
||||
|
||||
|
||||
func test_show_search_toggles_visibility():
|
||||
var main_window = MainWindow.new()
|
||||
var mock_search_view = mock(Control)
|
||||
|
||||
main_window.search_view = mock_search_view
|
||||
mock_search_view.visible = false
|
||||
|
||||
main_window.show_search()
|
||||
|
||||
assert_true(mock_search_view.visible)
|
||||
|
||||
main_window.show_search()
|
||||
|
||||
assert_false(mock_search_view.visible)
|
||||
|
||||
|
||||
func test_show_settings_makes_window_visible():
|
||||
var main_window = MainWindow.new()
|
||||
var mock_settings_window = mock(Control)
|
||||
|
||||
main_window.settings_window = mock_settings_window
|
||||
mock_settings_window.visible = false
|
||||
|
||||
main_window.show_settings()
|
||||
|
||||
assert_true(mock_settings_window.visible)
|
||||
|
||||
|
||||
func test_show_qr_calls_show_window():
|
||||
var main_window = MainWindow.new()
|
||||
var mock_qr_window = mock(Control)
|
||||
|
||||
main_window.qr_window = mock_qr_window
|
||||
|
||||
main_window.show_qr()
|
||||
|
||||
# Verify show_window was called (mock should track this)
|
||||
assert_true(mock_qr_window.show_window.was_called())
|
||||
|
||||
|
||||
func test_show_about_makes_window_visible():
|
||||
var main_window = MainWindow.new()
|
||||
var mock_about_window = mock(Control)
|
||||
|
||||
main_window.about_window = mock_about_window
|
||||
mock_about_window.visible = false
|
||||
|
||||
main_window.show_about()
|
||||
|
||||
assert_true(mock_about_window.visible)
|
||||
|
||||
|
||||
func test_get_statistics_shows_popup():
|
||||
var main_window = MainWindow.new()
|
||||
var mock_popup = mock(PopupPanel)
|
||||
var mock_label = mock(Label)
|
||||
|
||||
main_window.statistic_popup = mock_popup
|
||||
main_window.statistic_label = mock_label
|
||||
|
||||
main_window.get_statistics()
|
||||
|
||||
assert_true(mock_popup.visible)
|
||||
|
||||
|
||||
func test_sync_soundtracks_starts_sync():
|
||||
var main_window = MainWindow.new()
|
||||
var mock_sync_window = mock(Control)
|
||||
|
||||
main_window.sync_window = mock_sync_window
|
||||
|
||||
main_window.sync_soundtracks()
|
||||
|
||||
assert_true(mock_sync_window.start_sync.was_called())
|
||||
|
||||
|
||||
func test_reset_playlist_clears_and_refetches():
|
||||
var main_window = MainWindow.new()
|
||||
|
||||
# Mock Settings.make_request2
|
||||
var original_make_request = Settings.make_request2
|
||||
var reset_called = false
|
||||
|
||||
Settings.make_request2 = func(address: String, func_name: Callable, expect_data: bool) -> void:
|
||||
if address == "/music/reset":
|
||||
reset_called = true
|
||||
func_name.call()
|
||||
elif address == "/music/list":
|
||||
func_name.call([])
|
||||
elif address == "/music/rand":
|
||||
func_name.call(PackedByteArray())
|
||||
|
||||
Playlist.clear_song_list()
|
||||
Playlist.add_to_playlist(TestHelpers.create_test_song_object(1))
|
||||
|
||||
main_window.reset_playlist()
|
||||
|
||||
assert_true(reset_called)
|
||||
|
||||
Settings.make_request2 = original_make_request
|
||||
|
||||
|
||||
func test_reset_points_resets_all_players():
|
||||
var main_window = MainWindow.new()
|
||||
|
||||
Settings.player_array.clear()
|
||||
|
||||
var player1 = PlayerObject.new("Player 1", 0)
|
||||
var player2 = PlayerObject.new("Player 2", 1)
|
||||
|
||||
player1.player_score = 5
|
||||
player2.player_score = 10
|
||||
|
||||
Settings.player_array.append(player1)
|
||||
Settings.player_array.append(player2)
|
||||
|
||||
main_window.reset_points()
|
||||
|
||||
assert_eq(player1.player_score, 0)
|
||||
assert_eq(player2.player_score, 0)
|
||||
|
||||
|
||||
func test_play_next_song_increments_and_plays():
|
||||
var main_window = MainWindow.new()
|
||||
var mock_music_player = mock(MusicPlayer)
|
||||
|
||||
main_window.music_player_container = mock_music_player
|
||||
|
||||
Playlist.clear_song_list()
|
||||
Playlist.add_to_playlist(TestHelpers.create_test_song_object(1))
|
||||
Playlist.add_to_playlist(TestHelpers.create_test_song_object(2))
|
||||
Playlist.latest_played_song = 0
|
||||
Playlist.first_song_played = true
|
||||
|
||||
main_window.play_next_song()
|
||||
|
||||
assert_eq(Playlist.latest_played_song, 1)
|
||||
assert_true(mock_music_player.play_song_object.was_called_with([1]))
|
||||
|
||||
|
||||
func test_song_object_clicked_handles_left_click():
|
||||
var main_window = MainWindow.new()
|
||||
var mock_music_player = mock(MusicPlayer)
|
||||
|
||||
main_window.music_player_container = mock_music_player
|
||||
|
||||
Playlist.clear_song_list()
|
||||
var song = TestHelpers.create_test_song_object(1)
|
||||
Playlist.add_to_playlist(song)
|
||||
|
||||
var mock_event = mock(InputEventMouseButton)
|
||||
mock_event.pressed = true
|
||||
mock_event.button_index = MOUSE_BUTTON_LEFT
|
||||
|
||||
main_window.song_object_clicked(mock_event, 0)
|
||||
|
||||
assert_true(mock_music_player.play_song_object.was_called_with([0]))
|
||||
|
||||
|
||||
func test_song_object_clicked_downloads_if_not_available():
|
||||
var main_window = MainWindow.new()
|
||||
var mock_music_player = mock(MusicPlayer)
|
||||
|
||||
main_window.music_player_container = mock_music_player
|
||||
|
||||
Playlist.clear_song_list()
|
||||
var song = TestHelpers.create_test_song_object(1)
|
||||
song.song = null # No song loaded
|
||||
Playlist.add_to_playlist(song)
|
||||
|
||||
var mock_event = mock(InputEventMouseButton)
|
||||
mock_event.pressed = true
|
||||
mock_event.button_index = MOUSE_BUTTON_LEFT
|
||||
|
||||
# Mock Settings.make_request2 for download
|
||||
var original_make_request = Settings.make_request2
|
||||
var download_called = false
|
||||
|
||||
Settings.make_request2 = func(address: String, func_name: Callable, expect_data: bool) -> void:
|
||||
if address.begins_with("/music?song="):
|
||||
download_called = true
|
||||
var test_stream = TestHelpers.create_mock_audio_stream()
|
||||
func_name.call(test_stream.get_data())
|
||||
|
||||
main_window.song_object_clicked(mock_event, 0)
|
||||
|
||||
assert_true(download_called)
|
||||
|
||||
Settings.make_request2 = original_make_request
|
||||
@@ -1,158 +0,0 @@
|
||||
class_name MusicPlayerTest
|
||||
extends GdUnitTestSuite
|
||||
|
||||
|
||||
func test_play_or_pause_toggles_play_state():
|
||||
var music_player = MusicPlayer.new()
|
||||
var mock_audio = mock(AudioStreamPlayer)
|
||||
var mock_play_button = mock(TextureButton)
|
||||
|
||||
music_player.audio_player = mock_audio
|
||||
music_player.play_button = mock_play_button
|
||||
music_player.play_icon = load("res://icons/play_icon_light.svg")
|
||||
music_player.pause_icon = load("res://icons/pause_icon_light.svg")
|
||||
|
||||
# Initial state - paused
|
||||
mock_audio.stream_paused = true
|
||||
music_player.song_finished = false
|
||||
|
||||
# First call should play
|
||||
music_player.play_or_pause()
|
||||
assert_false(mock_audio.stream_paused)
|
||||
assert_eq(mock_play_button.texture_normal, music_player.pause_icon)
|
||||
|
||||
# Second call should pause
|
||||
music_player.play_or_pause()
|
||||
assert_true(mock_audio.stream_paused)
|
||||
assert_eq(mock_play_button.texture_normal, music_player.play_icon)
|
||||
|
||||
|
||||
func test_restart_resets_playback():
|
||||
var music_player = MusicPlayer.new()
|
||||
var mock_audio = mock(AudioStreamPlayer)
|
||||
var mock_slider = mock(HSlider)
|
||||
|
||||
music_player.audio_player = mock_audio
|
||||
music_player.progress_slider = mock_slider
|
||||
|
||||
mock_audio.stream_paused = false
|
||||
mock_slider.value = 50.0
|
||||
music_player.song_finished = true
|
||||
|
||||
music_player.restart()
|
||||
|
||||
assert_true(mock_audio.stop.was_called())
|
||||
assert_false(mock_audio.stream_paused)
|
||||
assert_false(music_player.song_finished)
|
||||
assert_eq(mock_slider.value, 0)
|
||||
assert_true(mock_audio.play.was_called())
|
||||
|
||||
|
||||
func test_pause_sets_correct_state():
|
||||
var music_player = MusicPlayer.new()
|
||||
var mock_audio = mock(AudioStreamPlayer)
|
||||
var mock_play_button = mock(TextureButton)
|
||||
|
||||
music_player.audio_player = mock_audio
|
||||
music_player.play_button = mock_play_button
|
||||
music_player.play_icon = load("res://icons/play_icon_light.svg")
|
||||
|
||||
mock_audio.stream_paused = false
|
||||
|
||||
music_player.pause()
|
||||
|
||||
assert_true(mock_audio.stream_paused)
|
||||
assert_eq(mock_play_button.texture_normal, music_player.play_icon)
|
||||
|
||||
|
||||
func test_format_time_correctly_formats_seconds():
|
||||
var music_player = MusicPlayer.new()
|
||||
|
||||
assert_eq(music_player.format_time(65.0), "01:05")
|
||||
assert_eq(music_player.format_time(0.0), "00:00")
|
||||
assert_eq(music_player.format_time(3661.0), "61:01")
|
||||
|
||||
|
||||
func test_format_text_hides_info_when_configured():
|
||||
var music_player = MusicPlayer.new()
|
||||
|
||||
# Test with hide_beginning and hide_length
|
||||
Settings.hide_beginning = true
|
||||
Settings.hide_length = true
|
||||
Settings.hide_for_seconds = 5.0
|
||||
|
||||
assert_eq(music_player.format_text(3.0, 100.0), "??:?? / ??:??")
|
||||
|
||||
Settings.hide_beginning = false
|
||||
Settings.hide_length = true
|
||||
assert_eq(music_player.format_text(10.0, 100.0), "00:10 / ??:??")
|
||||
|
||||
Settings.hide_beginning = true
|
||||
Settings.hide_length = false
|
||||
assert_eq(music_player.format_text(3.0, 100.0), "??:?? / 01:40")
|
||||
|
||||
Settings.hide_beginning = false
|
||||
Settings.hide_length = false
|
||||
assert_eq(music_player.format_text(10.0, 100.0), "00:10 / 01:40")
|
||||
|
||||
# Reset
|
||||
Settings.hide_beginning = false
|
||||
Settings.hide_length = false
|
||||
|
||||
|
||||
func test_on_drag_started_sets_is_changing():
|
||||
var music_player = MusicPlayer.new()
|
||||
|
||||
assert_false(music_player.is_changing)
|
||||
|
||||
music_player._on_drag_started()
|
||||
|
||||
assert_true(music_player.is_changing)
|
||||
|
||||
|
||||
func test_on_drag_ended_updates_position():
|
||||
var music_player = MusicPlayer.new()
|
||||
var mock_audio = mock(AudioStreamPlayer)
|
||||
var mock_slider = mock(HSlider)
|
||||
|
||||
music_player.audio_player = mock_audio
|
||||
music_player.progress_slider = mock_slider
|
||||
|
||||
mock_slider.value = 30.0
|
||||
music_player.is_changing = true
|
||||
|
||||
music_player._on_drag_ended(true)
|
||||
|
||||
assert_false(music_player.is_changing)
|
||||
assert_true(mock_audio.seek.was_called_with([30.0]))
|
||||
assert_eq(music_player.playback_position, 30.0)
|
||||
|
||||
|
||||
func test_seek_updates_slider_and_position():
|
||||
var music_player = MusicPlayer.new()
|
||||
var mock_audio = mock(AudioStreamPlayer)
|
||||
var mock_slider = mock(HSlider)
|
||||
|
||||
music_player.audio_player = mock_audio
|
||||
music_player.progress_slider = mock_slider
|
||||
|
||||
mock_slider.value = 10.0
|
||||
|
||||
music_player.seek(5.0)
|
||||
|
||||
assert_eq(mock_slider.value, 15.0)
|
||||
assert_true(mock_audio.seek.was_called_with([15.0]))
|
||||
assert_eq(music_player.playback_position, 15.0)
|
||||
|
||||
|
||||
func test_change_volume_updates_slider():
|
||||
var music_player = MusicPlayer.new()
|
||||
var mock_slider = mock(HSlider)
|
||||
|
||||
music_player.volume_slider = mock_slider
|
||||
mock_slider.value = 0.5
|
||||
|
||||
music_player.change_volume(0.1)
|
||||
|
||||
assert_eq(mock_slider.value, 0.6)
|
||||
assert_true(mock_slider.change_volume.was_called_with([0.6]))
|
||||
@@ -0,0 +1,68 @@
|
||||
extends GdUnitTestSuite
|
||||
|
||||
var _music_player: MusicPlayer
|
||||
var _hide_beginning: bool
|
||||
var _hide_length: bool
|
||||
var _hide_for_seconds: float
|
||||
|
||||
|
||||
func before_test() -> void:
|
||||
_music_player = auto_free(MusicPlayer.new())
|
||||
_hide_beginning = Settings.hide_beginning
|
||||
_hide_length = Settings.hide_length
|
||||
_hide_for_seconds = Settings.hide_for_seconds
|
||||
Settings.hide_beginning = false
|
||||
Settings.hide_length = false
|
||||
Settings.hide_for_seconds = 5.0
|
||||
|
||||
|
||||
func after_test() -> void:
|
||||
Settings.hide_beginning = _hide_beginning
|
||||
Settings.hide_length = _hide_length
|
||||
Settings.hide_for_seconds = _hide_for_seconds
|
||||
|
||||
|
||||
func test_format_time() -> void:
|
||||
assert_str(_music_player.format_time(0.0)).is_equal("00:00")
|
||||
assert_str(_music_player.format_time(65.0)).is_equal("01:05")
|
||||
assert_str(_music_player.format_time(3661.0)).is_equal("61:01")
|
||||
|
||||
|
||||
func test_format_time_rounds_up_into_next_minute() -> void:
|
||||
assert_str(_music_player.format_time(59.6)).is_equal("01:00")
|
||||
|
||||
|
||||
func test_format_text_shows_position_and_length() -> void:
|
||||
assert_str(_music_player.format_text(10.0, 100.0)).is_equal("00:10 / 01:40")
|
||||
|
||||
|
||||
func test_format_text_hides_length() -> void:
|
||||
Settings.hide_length = true
|
||||
|
||||
assert_str(_music_player.format_text(10.0, 100.0)).is_equal("00:10 / ??:??")
|
||||
|
||||
|
||||
func test_format_text_hides_position_at_start() -> void:
|
||||
Settings.hide_beginning = true
|
||||
|
||||
assert_str(_music_player.format_text(3.0, 100.0)).is_equal("??:?? / 01:40")
|
||||
|
||||
|
||||
func test_format_text_shows_position_after_hidden_start() -> void:
|
||||
Settings.hide_beginning = true
|
||||
|
||||
assert_str(_music_player.format_text(5.0, 100.0)).is_equal("00:05 / 01:40")
|
||||
|
||||
|
||||
func test_format_text_hides_everything_at_start() -> void:
|
||||
Settings.hide_beginning = true
|
||||
Settings.hide_length = true
|
||||
|
||||
assert_str(_music_player.format_text(3.0, 100.0)).is_equal("??:?? / ??:??")
|
||||
|
||||
|
||||
func test_format_text_keeps_length_hidden_after_hidden_start() -> void:
|
||||
Settings.hide_beginning = true
|
||||
Settings.hide_length = true
|
||||
|
||||
assert_str(_music_player.format_text(5.0, 100.0)).is_equal("00:05 / ??:??")
|
||||
@@ -0,0 +1 @@
|
||||
uid://xoskre03iorg
|
||||
@@ -0,0 +1,113 @@
|
||||
extends GdUnitTestSuite
|
||||
|
||||
var _server: FakeHttpTransport
|
||||
|
||||
|
||||
func before_test() -> void:
|
||||
_server = FakeHttpTransport.new().install()
|
||||
|
||||
|
||||
func after_test() -> void:
|
||||
_server.uninstall()
|
||||
|
||||
|
||||
func test_fetch_song_list_passes_parsed_json() -> void:
|
||||
var songs: Array = [{"SongNo": 0, "Soundtrack": "Chrono Trigger", "Song": "Corridors of Time"}]
|
||||
_server.respond_json("/music/list", songs)
|
||||
var received: Array = []
|
||||
|
||||
MusicServerClient.fetch_song_list(func(data: Array) -> void: received.append_array(data))
|
||||
|
||||
assert_int(received.size()).is_equal(1)
|
||||
assert_int(int(received[0].SongNo)).is_equal(0)
|
||||
assert_str(received[0].Soundtrack).is_equal("Chrono Trigger")
|
||||
|
||||
|
||||
func test_fetch_random_song_uses_normal_endpoint() -> void:
|
||||
MusicServerClient.fetch_random_song(false, func(_body: PackedByteArray) -> void: pass)
|
||||
|
||||
assert_array(_server.requested_paths).contains_exactly(["/music/rand"])
|
||||
|
||||
|
||||
func test_fetch_random_song_uses_low_played_endpoint() -> void:
|
||||
MusicServerClient.fetch_random_song(true, func(_body: PackedByteArray) -> void: pass)
|
||||
|
||||
assert_array(_server.requested_paths).contains_exactly(["/music/rand/low"])
|
||||
|
||||
|
||||
func test_fetch_song_passes_raw_bytes() -> void:
|
||||
var mp3: PackedByteArray = PackedByteArray([0x49, 0x44, 0x33])
|
||||
_server.respond_bytes("/music?song=4", mp3)
|
||||
var received: Array[PackedByteArray] = []
|
||||
|
||||
MusicServerClient.fetch_song(4, func(body: PackedByteArray) -> void: received.append(body))
|
||||
|
||||
assert_array(received).contains_exactly([mp3])
|
||||
|
||||
|
||||
func test_failed_request_does_not_call_back() -> void:
|
||||
_server.respond_json("/music/list", [], 500)
|
||||
var called: Array[bool] = [false]
|
||||
|
||||
MusicServerClient.fetch_song_list(func(_data: Variant) -> void: called[0] = true)
|
||||
|
||||
assert_bool(called[0]).is_false()
|
||||
|
||||
|
||||
func test_invalid_json_does_not_call_back() -> void:
|
||||
_server.respond_bytes("/music/info", "not json".to_utf8_buffer())
|
||||
var called: Array[bool] = [false]
|
||||
|
||||
MusicServerClient.fetch_song_info(func(_data: Variant) -> void: called[0] = true)
|
||||
|
||||
assert_bool(called[0]).is_false()
|
||||
|
||||
|
||||
func test_send_calls_back_without_arguments() -> void:
|
||||
_server.respond_bytes("/music/reset", PackedByteArray())
|
||||
var called: Array[bool] = [false]
|
||||
|
||||
MusicServerClient.reset_playlist(func() -> void: called[0] = true)
|
||||
|
||||
assert_bool(called[0]).is_true()
|
||||
|
||||
|
||||
func test_mark_song_played_sends_put() -> void:
|
||||
_server.respond_bytes("/music/played?song=3", PackedByteArray())
|
||||
|
||||
MusicServerClient.mark_song_played(3)
|
||||
|
||||
assert_array(_server.requested_paths).contains_exactly(["/music/played?song=3"])
|
||||
assert_array(_server.requested_methods).contains_exactly([HTTPClient.METHOD_PUT])
|
||||
|
||||
|
||||
func test_character_name_is_url_encoded() -> void:
|
||||
MusicServerClient.fetch_character_image("Paper Mario.png", func(_body: PackedByteArray) -> void: pass)
|
||||
|
||||
assert_array(_server.requested_paths).contains_exactly(["/character?name=Paper%20Mario.png"])
|
||||
|
||||
|
||||
func test_requests_go_to_selected_server() -> void:
|
||||
var previous_path: String = Settings.default_path
|
||||
Settings.default_path = "http://localhost:8080"
|
||||
var requested_urls: Array[String] = []
|
||||
var recording_transport: HttpTransport = _RecordingTransport.new(requested_urls)
|
||||
MusicServerClient.transport = recording_transport
|
||||
|
||||
MusicServerClient.fetch_song_info(func(_data: Variant) -> void: pass)
|
||||
|
||||
Settings.default_path = previous_path
|
||||
assert_array(requested_urls).contains_exactly(["http://localhost:8080/music/info"])
|
||||
|
||||
|
||||
class _RecordingTransport:
|
||||
extends HttpTransport
|
||||
|
||||
var _urls: Array[String]
|
||||
|
||||
func _init(urls: Array[String]) -> void:
|
||||
super(null)
|
||||
_urls = urls
|
||||
|
||||
func request(url: String, _method: int, _on_complete: Callable) -> void:
|
||||
_urls.append(url)
|
||||
@@ -0,0 +1 @@
|
||||
uid://3grferwg0j7g
|
||||
@@ -0,0 +1,119 @@
|
||||
extends GdUnitTestSuite
|
||||
|
||||
var _winning_score: int
|
||||
var _player: PlayerObject
|
||||
|
||||
|
||||
func before_test() -> void:
|
||||
_winning_score = Settings.winning_score
|
||||
Settings.winning_score = 3
|
||||
_player = PlayerObject.new("Crono")
|
||||
|
||||
|
||||
func after_test() -> void:
|
||||
Settings.winning_score = _winning_score
|
||||
|
||||
|
||||
func test_first_point_triggers_first_point_signal() -> void:
|
||||
monitor_signals(_player, false)
|
||||
|
||||
_player.add_point()
|
||||
|
||||
assert_int(_player.player_score).is_equal(1)
|
||||
await assert_signal(_player).is_emitted("first_point_triggerd")
|
||||
await assert_signal(_player).is_emitted("player_point_given")
|
||||
|
||||
|
||||
func test_one_point_from_winning_triggers_match_point() -> void:
|
||||
_player.add_point()
|
||||
monitor_signals(_player, false)
|
||||
|
||||
_player.add_point()
|
||||
|
||||
await assert_signal(_player).is_emitted("match_point_triggerd")
|
||||
|
||||
|
||||
func test_reaching_winning_score_triggers_winner() -> void:
|
||||
_player.add_point()
|
||||
_player.add_point()
|
||||
monitor_signals(_player, false)
|
||||
|
||||
_player.add_point()
|
||||
|
||||
assert_int(_player.player_score).is_equal(3)
|
||||
await assert_signal(_player).is_emitted("winner_triggerd")
|
||||
|
||||
|
||||
func test_score_does_not_go_past_winning_score() -> void:
|
||||
for i: int in 3:
|
||||
_player.add_point()
|
||||
monitor_signals(_player, false)
|
||||
|
||||
_player.add_point()
|
||||
|
||||
assert_int(_player.player_score).is_equal(3)
|
||||
await assert_signal(_player).wait_until(50).is_not_emitted("player_point_given")
|
||||
|
||||
|
||||
func test_ordinary_point_plays_point_sound() -> void:
|
||||
Settings.winning_score = 10
|
||||
_player.add_point()
|
||||
monitor_signals(_player, false)
|
||||
|
||||
_player.add_point()
|
||||
|
||||
await assert_signal(_player).is_emitted("point_given_sound")
|
||||
|
||||
|
||||
func test_minus_point_lowers_score() -> void:
|
||||
_player.add_point()
|
||||
monitor_signals(_player, false)
|
||||
|
||||
_player.minus_point()
|
||||
|
||||
assert_int(_player.player_score).is_equal(0)
|
||||
await assert_signal(_player).is_emitted("player_point_taken")
|
||||
|
||||
|
||||
func test_score_does_not_go_below_zero() -> void:
|
||||
monitor_signals(_player, false)
|
||||
|
||||
_player.minus_point()
|
||||
|
||||
assert_int(_player.player_score).is_equal(0)
|
||||
await assert_signal(_player).wait_until(50).is_not_emitted("player_point_taken")
|
||||
|
||||
|
||||
func test_dropping_back_to_zero_allows_first_point_again() -> void:
|
||||
_player.add_point()
|
||||
_player.minus_point()
|
||||
monitor_signals(_player, false)
|
||||
|
||||
_player.add_point()
|
||||
|
||||
await assert_signal(_player).is_emitted("first_point_triggerd")
|
||||
|
||||
|
||||
func test_reset_points() -> void:
|
||||
_player.add_point()
|
||||
_player.add_point()
|
||||
|
||||
_player.reset_points()
|
||||
|
||||
assert_int(_player.player_score).is_equal(0)
|
||||
assert_bool(_player.is_first_point).is_true()
|
||||
|
||||
|
||||
func test_id_is_a_uuid() -> void:
|
||||
var uuid_pattern: RegEx = RegEx.create_from_string(
|
||||
"^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$")
|
||||
|
||||
assert_object(uuid_pattern.search(_player.id)).is_not_null()
|
||||
|
||||
|
||||
func test_every_player_gets_a_different_id() -> void:
|
||||
var ids: Dictionary = {}
|
||||
for i: int in 100:
|
||||
ids[PlayerObject.new("Player %d" % i).id] = true
|
||||
|
||||
assert_int(ids.size()).is_equal(100)
|
||||
@@ -0,0 +1 @@
|
||||
uid://cq4vprpa1epya
|
||||
+160
-149
@@ -1,173 +1,184 @@
|
||||
class_name PlaylistTest
|
||||
extends GdUnitTestSuite
|
||||
|
||||
|
||||
func test_add_to_playlist_increases_size():
|
||||
Playlist.clear_song_list()
|
||||
var initial_size = Playlist.song_object_array.size()
|
||||
|
||||
var song_obj = TestHelpers.create_test_song_object()
|
||||
Playlist.add_to_playlist(song_obj)
|
||||
|
||||
assert_eq(Playlist.song_object_array.size(), initial_size + 1)
|
||||
var _server: FakeHttpTransport
|
||||
var _add_to_stats: bool
|
||||
var _is_debug: bool
|
||||
|
||||
|
||||
func test_get_next_song_returns_correct_song():
|
||||
Playlist.clear_song_list()
|
||||
|
||||
var song1 = TestHelpers.create_test_song_object(1, "Soundtrack 1", "Song 1")
|
||||
var song2 = TestHelpers.create_test_song_object(2, "Soundtrack 2", "Song 2")
|
||||
|
||||
Playlist.add_to_playlist(song1)
|
||||
Playlist.add_to_playlist(song2)
|
||||
Playlist.latest_played_song = 0
|
||||
|
||||
var next_song = Playlist.get_next_song()
|
||||
|
||||
assert_eq(next_song, song1)
|
||||
func before_test() -> void:
|
||||
_server = FakeHttpTransport.new().install()
|
||||
_add_to_stats = Settings.add_to_stats
|
||||
_is_debug = Settings.is_debug
|
||||
Settings.add_to_stats = false
|
||||
Settings.is_debug = false
|
||||
Playlist.reset_playlist()
|
||||
Playlist.number_of_tracks_to_preload = 1
|
||||
|
||||
|
||||
func test_set_latest_played_song_increments():
|
||||
Playlist.clear_song_list()
|
||||
|
||||
var song1 = TestHelpers.create_test_song_object(1)
|
||||
var song2 = TestHelpers.create_test_song_object(2)
|
||||
|
||||
Playlist.add_to_playlist(song1)
|
||||
Playlist.add_to_playlist(song2)
|
||||
Playlist.latest_played_song = 0
|
||||
|
||||
Playlist.set_latest_played_song()
|
||||
|
||||
assert_eq(Playlist.latest_played_song, 1)
|
||||
func after_test() -> void:
|
||||
Playlist.reset_playlist()
|
||||
Settings.add_to_stats = _add_to_stats
|
||||
Settings.is_debug = _is_debug
|
||||
_server.uninstall()
|
||||
|
||||
|
||||
func test_set_latest_played_song_does_not_exceed_bounds():
|
||||
Playlist.clear_song_list()
|
||||
|
||||
var song1 = TestHelpers.create_test_song_object(1)
|
||||
Playlist.add_to_playlist(song1)
|
||||
Playlist.latest_played_song = 0
|
||||
|
||||
Playlist.set_latest_played_song()
|
||||
Playlist.set_latest_played_song()
|
||||
|
||||
assert_eq(Playlist.latest_played_song, 0)
|
||||
func _add_songs(count: int) -> Array[SongObject]:
|
||||
var songs: Array[SongObject] = []
|
||||
for i: int in count:
|
||||
var song: SongObject = SongObject.new()
|
||||
song.song_number = i
|
||||
song.soundtrack_title = "Soundtrack %d" % i
|
||||
song.song_title = "Song %d" % i
|
||||
Playlist.add_to_playlist(song)
|
||||
songs.append(song)
|
||||
return songs
|
||||
|
||||
|
||||
func test_get_preloaded_songs_returns_correct_count():
|
||||
Playlist.clear_song_list()
|
||||
|
||||
var song1 = TestHelpers.create_test_song_object(1)
|
||||
var song2 = TestHelpers.create_test_song_object(2)
|
||||
var song3 = TestHelpers.create_test_song_object(3)
|
||||
|
||||
song1.has_played = true
|
||||
song2.has_played = false
|
||||
song3.has_played = false
|
||||
|
||||
Playlist.add_to_playlist(song1)
|
||||
Playlist.add_to_playlist(song2)
|
||||
Playlist.add_to_playlist(song3)
|
||||
|
||||
assert_eq(Playlist.get_preloaded_songs(), 2)
|
||||
func test_set_latest_played_song_advances() -> void:
|
||||
_add_songs(3)
|
||||
|
||||
Playlist.set_latest_played_song()
|
||||
|
||||
assert_int(Playlist.latest_played_song).is_equal(1)
|
||||
|
||||
|
||||
func test_should_preload_more_songs():
|
||||
Playlist.clear_song_list()
|
||||
Playlist.number_of_tracks_to_preload = 2
|
||||
|
||||
var song1 = TestHelpers.create_test_song_object(1)
|
||||
var song2 = TestHelpers.create_test_song_object(2)
|
||||
|
||||
song1.has_played = true
|
||||
song2.has_played = false
|
||||
|
||||
Playlist.add_to_playlist(song1)
|
||||
Playlist.add_to_playlist(song2)
|
||||
|
||||
assert_true(Playlist.should_preload_more_songs())
|
||||
|
||||
var song3 = TestHelpers.create_test_song_object(3)
|
||||
Playlist.add_to_playlist(song3)
|
||||
|
||||
assert_false(Playlist.should_preload_more_songs())
|
||||
func test_set_latest_played_song_stops_at_last_song() -> void:
|
||||
_add_songs(2)
|
||||
|
||||
Playlist.set_latest_played_song()
|
||||
Playlist.set_latest_played_song()
|
||||
Playlist.set_latest_played_song()
|
||||
|
||||
assert_int(Playlist.latest_played_song).is_equal(1)
|
||||
|
||||
|
||||
func test_song_is_answered_sets_flag():
|
||||
Playlist.clear_song_list()
|
||||
|
||||
var song = TestHelpers.create_test_song_object(1)
|
||||
Playlist.add_to_playlist(song)
|
||||
Playlist.currently_playing_song = 0
|
||||
|
||||
assert_false(song.is_answered)
|
||||
|
||||
Playlist.song_is_answered()
|
||||
|
||||
assert_true(song.is_answered)
|
||||
func test_get_next_song_returns_latest_played() -> void:
|
||||
var songs: Array[SongObject] = _add_songs(3)
|
||||
Playlist.latest_played_song = 2
|
||||
|
||||
assert_object(Playlist.get_next_song()).is_same(songs[2])
|
||||
|
||||
|
||||
func test_add_point_to_song():
|
||||
Playlist.clear_song_list()
|
||||
|
||||
var song = TestHelpers.create_test_song_object(1)
|
||||
Playlist.add_to_playlist(song)
|
||||
Playlist.currently_playing_song = 0
|
||||
|
||||
Playlist.add_point(0)
|
||||
|
||||
assert_true(song.players_given_point.has(0))
|
||||
func test_update_last_played_song_points_past_existing_songs() -> void:
|
||||
_add_songs(4)
|
||||
|
||||
Playlist.update_last_played_song()
|
||||
|
||||
assert_int(Playlist.latest_played_song).is_equal(4)
|
||||
|
||||
|
||||
func test_remove_point_from_song():
|
||||
Playlist.clear_song_list()
|
||||
|
||||
var song = TestHelpers.create_test_song_object(1)
|
||||
Playlist.add_to_playlist(song)
|
||||
Playlist.currently_playing_song = 0
|
||||
|
||||
Playlist.add_point(0)
|
||||
Playlist.remove_point(0)
|
||||
|
||||
assert_false(song.players_given_point.has(0))
|
||||
func test_preloaded_songs_counts_unplayed_songs() -> void:
|
||||
var songs: Array[SongObject] = _add_songs(3)
|
||||
songs[0].has_played = true
|
||||
|
||||
assert_int(Playlist.get_preloaded_songs()).is_equal(2)
|
||||
|
||||
|
||||
func test_get_song_returns_correct_stream():
|
||||
Playlist.clear_song_list()
|
||||
|
||||
var expected_stream = TestHelpers.create_mock_audio_stream()
|
||||
var song = TestHelpers.create_test_song_object(1)
|
||||
song.song = expected_stream
|
||||
|
||||
Playlist.add_to_playlist(song)
|
||||
|
||||
var retrieved_stream = Playlist.get_song(0)
|
||||
|
||||
assert_eq(retrieved_stream, expected_stream)
|
||||
func test_should_preload_more_songs_when_below_target() -> void:
|
||||
var songs: Array[SongObject] = _add_songs(1)
|
||||
songs[0].has_played = true
|
||||
|
||||
assert_bool(Playlist.should_preload_more_songs()).is_true()
|
||||
|
||||
|
||||
func test_reset_playlist_clears_all():
|
||||
Playlist.clear_song_list()
|
||||
|
||||
var song = TestHelpers.create_test_song_object(1)
|
||||
Playlist.add_to_playlist(song)
|
||||
Playlist.latest_played_song = 1
|
||||
Playlist.currently_playing_song = 0
|
||||
|
||||
Playlist.reset_playlist()
|
||||
|
||||
assert_eq(Playlist.song_object_array.size(), 0)
|
||||
assert_eq(Playlist.latest_played_song, 0)
|
||||
assert_eq(Playlist.currently_playing_song, -1)
|
||||
func test_should_not_preload_more_songs_when_target_reached() -> void:
|
||||
_add_songs(1)
|
||||
|
||||
assert_bool(Playlist.should_preload_more_songs()).is_false()
|
||||
|
||||
|
||||
func test_clear_song_list_removes_all():
|
||||
Playlist.clear_song_list()
|
||||
|
||||
Playlist.add_to_playlist(TestHelpers.create_test_song_object(1))
|
||||
Playlist.add_to_playlist(TestHelpers.create_test_song_object(2))
|
||||
|
||||
Playlist.clear_song_list()
|
||||
|
||||
assert_eq(Playlist.song_object_array.size(), 0)
|
||||
func test_get_playlist_hides_unplayed_songs() -> void:
|
||||
var songs: Array[SongObject] = _add_songs(3)
|
||||
songs[0].has_played = true
|
||||
|
||||
assert_array(Playlist.get_playlist()).contains_exactly([songs[0]])
|
||||
|
||||
|
||||
func test_get_playlist_shows_everything_in_debug() -> void:
|
||||
Settings.is_debug = true
|
||||
var songs: Array[SongObject] = _add_songs(2)
|
||||
|
||||
assert_array(Playlist.get_playlist()).contains_exactly(songs)
|
||||
|
||||
|
||||
func test_song_is_answered_marks_song() -> void:
|
||||
var songs: Array[SongObject] = _add_songs(2)
|
||||
|
||||
Playlist.song_is_answered(1)
|
||||
|
||||
assert_bool(songs[1].is_answered).is_true()
|
||||
assert_bool(songs[0].is_answered).is_false()
|
||||
|
||||
|
||||
func test_song_is_answered_reports_to_stats_when_enabled() -> void:
|
||||
Settings.add_to_stats = true
|
||||
_add_songs(2)
|
||||
|
||||
Playlist.song_is_answered(1)
|
||||
|
||||
assert_array(_server.requested_paths).contains_exactly(["/music/played?song=1"])
|
||||
|
||||
|
||||
func test_song_is_answered_does_not_report_when_stats_disabled() -> void:
|
||||
_add_songs(2)
|
||||
|
||||
Playlist.song_is_answered(1)
|
||||
|
||||
assert_array(_server.requested_paths).is_empty()
|
||||
|
||||
|
||||
func test_points_are_recorded_on_current_song() -> void:
|
||||
var songs: Array[SongObject] = _add_songs(2)
|
||||
Playlist.set_currently_playing_song(1)
|
||||
|
||||
Playlist.add_point("lucca-id")
|
||||
Playlist.add_point("crono-id")
|
||||
|
||||
assert_array(songs[1].players_given_point).contains_exactly(["lucca-id", "crono-id"])
|
||||
assert_array(songs[0].players_given_point).is_empty()
|
||||
|
||||
|
||||
func test_remove_point_from_current_song() -> void:
|
||||
var songs: Array[SongObject] = _add_songs(1)
|
||||
Playlist.set_currently_playing_song(0)
|
||||
Playlist.add_point("crono-id")
|
||||
Playlist.add_point("marle-id")
|
||||
|
||||
Playlist.remove_point("crono-id")
|
||||
|
||||
assert_array(songs[0].players_given_point).contains_exactly(["marle-id"])
|
||||
|
||||
|
||||
func test_remove_player_removes_only_that_players_points() -> void:
|
||||
var songs: Array[SongObject] = _add_songs(2)
|
||||
songs[0].players_given_point.assign(["crono-id", "marle-id"])
|
||||
songs[1].players_given_point.assign(["crono-id"])
|
||||
|
||||
Playlist.remove_player_from_list("marle-id")
|
||||
|
||||
assert_array(songs[0].players_given_point).contains_exactly(["crono-id"])
|
||||
assert_array(songs[1].players_given_point).contains_exactly(["crono-id"])
|
||||
|
||||
|
||||
func test_reset_playlist_clears_songs_and_position() -> void:
|
||||
_add_songs(3)
|
||||
Playlist.latest_played_song = 2
|
||||
Playlist.set_currently_playing_song(1)
|
||||
|
||||
Playlist.reset_playlist()
|
||||
|
||||
assert_array(Playlist.song_object_array).is_empty()
|
||||
assert_int(Playlist.latest_played_song).is_equal(0)
|
||||
assert_int(Playlist.currently_playing_song).is_equal(-1)
|
||||
|
||||
|
||||
func test_current_titles_are_hidden_until_answered() -> void:
|
||||
var songs: Array[SongObject] = _add_songs(1)
|
||||
Playlist.set_currently_playing_song(0)
|
||||
|
||||
assert_str(Playlist.get_current_soundtrack_title()).is_equal("????????")
|
||||
|
||||
songs[0].is_answered = true
|
||||
|
||||
assert_str(Playlist.get_current_soundtrack_title()).is_equal("Soundtrack 0")
|
||||
assert_str(Playlist.get_current_song_title()).is_equal("Song 0")
|
||||
@@ -0,0 +1 @@
|
||||
uid://c2ibkc7bd60cx
|
||||
@@ -0,0 +1,62 @@
|
||||
extends GdUnitTestSuite
|
||||
|
||||
const SearchWindowScript: GDScript = preload("res://SearchWindow.gd")
|
||||
|
||||
var _search: Control
|
||||
|
||||
|
||||
func before_test() -> void:
|
||||
_search = auto_free(SearchWindowScript.new())
|
||||
_search.set_soundtracks([
|
||||
"Chrono Trigger",
|
||||
"Final Fantasy VI",
|
||||
"Mario & Luigi: Superstar Saga",
|
||||
"Pokémon Red",
|
||||
"Q*bert",
|
||||
"Super Mario 64",
|
||||
"The Legend of Zelda: A Link to the Past",
|
||||
])
|
||||
|
||||
|
||||
func test_plain_text_matches_case_insensitively() -> void:
|
||||
assert_array(_search.find_matches("chrono")).contains_exactly(["Chrono Trigger"])
|
||||
|
||||
|
||||
func test_contains_matches_come_before_letter_order_matches() -> void:
|
||||
var matches: Array[String] = _search.find_matches("mario")
|
||||
|
||||
assert_array(matches).contains_exactly([
|
||||
"Mario & Luigi: Superstar Saga",
|
||||
"Super Mario 64",
|
||||
])
|
||||
|
||||
|
||||
func test_ignores_spaces() -> void:
|
||||
assert_array(_search.find_matches("finalfantasy")).contains_exactly(["Final Fantasy VI"])
|
||||
|
||||
|
||||
func test_and_matches_ampersand() -> void:
|
||||
assert_array(_search.find_matches("mario and luigi")).contains(["Mario & Luigi: Superstar Saga"])
|
||||
|
||||
|
||||
func test_accented_letters_match_plain_letters() -> void:
|
||||
assert_array(_search.find_matches("pokemon")).contains_exactly(["Pokémon Red"])
|
||||
|
||||
|
||||
func test_letters_in_order_match_abbreviations() -> void:
|
||||
assert_array(_search.find_matches("lozlttp")).contains_exactly([
|
||||
"The Legend of Zelda: A Link to the Past",
|
||||
])
|
||||
|
||||
|
||||
func test_regex_characters_are_matched_literally() -> void:
|
||||
assert_array(_search.find_matches("q*b")).contains_exactly(["Q*bert"])
|
||||
|
||||
|
||||
func test_no_matches() -> void:
|
||||
assert_array(_search.find_matches("metroid")).is_empty()
|
||||
|
||||
|
||||
func test_clean_term() -> void:
|
||||
assert_str(_search.clean_term("Rock 'n Roll Racing")).is_equal("rockandrollracing")
|
||||
assert_str(_search.clean_term("Ys I & II")).is_equal("ysiandii")
|
||||
@@ -0,0 +1 @@
|
||||
uid://p8ygq52abebj
|
||||
@@ -1,94 +0,0 @@
|
||||
class_name SettingsTest
|
||||
extends GdUnitTestSuite
|
||||
|
||||
|
||||
func test_make_request2_calls_callback_on_success():
|
||||
var callback_called = false
|
||||
var received_data = null
|
||||
|
||||
var callback = func(data):
|
||||
callback_called = true
|
||||
received_data = data
|
||||
|
||||
# Mock the HTTP request behavior
|
||||
var original_make_request = Settings.make_request2
|
||||
Settings.make_request2 = func(address: String, func_name: Callable, expect_data: bool) -> void:
|
||||
if expect_data:
|
||||
func_name.call([{"SongNo": 1, "Soundtrack": "Test", "Song": "Test Song"}])
|
||||
else:
|
||||
func_name.call()
|
||||
|
||||
Settings.make_request2("/music/list", callback, true)
|
||||
|
||||
assert_true(callback_called)
|
||||
assert_eq(received_data[0].SongNo, 1)
|
||||
|
||||
# Restore original
|
||||
Settings.make_request2 = original_make_request
|
||||
|
||||
|
||||
func test_make_request2_handles_error_response():
|
||||
var error_called = false
|
||||
|
||||
var callback = func(data):
|
||||
# This shouldn't be called for error responses
|
||||
pass
|
||||
|
||||
# Mock error response
|
||||
var original_make_request = Settings.make_request2
|
||||
Settings.make_request2 = func(address: String, func_name: Callable, expect_data: bool) -> void:
|
||||
# Simulate error by not calling the callback
|
||||
pass
|
||||
|
||||
Settings.make_request2("/music/error", callback, true)
|
||||
|
||||
# Restore and verify callback wasn't called with data
|
||||
Settings.make_request2 = original_make_request
|
||||
assert_false(error_called)
|
||||
|
||||
|
||||
func test_make_request3_does_not_expect_data():
|
||||
var callback_called = false
|
||||
|
||||
var callback = func():
|
||||
callback_called = true
|
||||
|
||||
var original_make_request = Settings.make_request3
|
||||
Settings.make_request3 = func(address: String) -> void:
|
||||
callback.call()
|
||||
|
||||
Settings.make_request3("/music/reset")
|
||||
|
||||
assert_true(callback_called)
|
||||
|
||||
Settings.make_request3 = original_make_request
|
||||
|
||||
|
||||
func test_delete_children_removes_all():
|
||||
var node = Node.new()
|
||||
var child1 = Node.new()
|
||||
var child2 = Node.new()
|
||||
|
||||
node.add_child(child1)
|
||||
node.add_child(child2)
|
||||
|
||||
assert_eq(node.get_child_count(), 2)
|
||||
|
||||
Settings.delete_children(node)
|
||||
|
||||
assert_eq(node.get_child_count(), 0)
|
||||
|
||||
|
||||
func test_delete_player_children_removes_all():
|
||||
var node = Node.new()
|
||||
var player1 = Player.new()
|
||||
var player2 = Player.new()
|
||||
|
||||
node.add_child(player1)
|
||||
node.add_child(player2)
|
||||
|
||||
assert_eq(node.get_child_count(), 2)
|
||||
|
||||
Settings.delete_player_children(node)
|
||||
|
||||
assert_eq(node.get_child_count(), 0)
|
||||
@@ -0,0 +1,60 @@
|
||||
extends GdUnitTestSuite
|
||||
|
||||
var _is_debug: bool
|
||||
var _song: SongObject
|
||||
|
||||
|
||||
func before_test() -> void:
|
||||
_is_debug = Settings.is_debug
|
||||
Settings.is_debug = false
|
||||
_song = SongObject.new()
|
||||
_song.song_number = 4
|
||||
_song.soundtrack_title = "Final Fantasy VI"
|
||||
_song.song_title = "Dancing Mad"
|
||||
|
||||
|
||||
func after_test() -> void:
|
||||
Settings.is_debug = _is_debug
|
||||
|
||||
|
||||
func test_titles_are_hidden_until_answered() -> void:
|
||||
assert_str(_song.get_soundtrack_title()).is_equal("????????")
|
||||
assert_str(_song.get_song_title()).is_equal("??????")
|
||||
assert_str(_song.get_song_info()).is_equal("??? - ???")
|
||||
|
||||
|
||||
func test_titles_are_shown_once_answered() -> void:
|
||||
_song.is_answered = true
|
||||
|
||||
assert_str(_song.get_soundtrack_title()).is_equal("Final Fantasy VI")
|
||||
assert_str(_song.get_song_title()).is_equal("Dancing Mad")
|
||||
assert_str(_song.get_song_info()).is_equal("5. Final Fantasy VI - Dancing Mad")
|
||||
|
||||
|
||||
func test_debug_info_shows_unanswered_songs() -> void:
|
||||
Settings.is_debug = true
|
||||
|
||||
assert_str(_song.get_song_info()).is_equal(
|
||||
"5. Final Fantasy VI - Dancing Mad | played: false | answered: false")
|
||||
|
||||
|
||||
func test_points_are_kept_in_the_order_given() -> void:
|
||||
_song.add_point("marle-id")
|
||||
_song.add_point("crono-id")
|
||||
|
||||
assert_array(_song.players_given_point).contains_exactly(["marle-id", "crono-id"])
|
||||
|
||||
|
||||
func test_player_gets_at_most_one_point_per_song() -> void:
|
||||
_song.add_point("crono-id")
|
||||
_song.add_point("crono-id")
|
||||
|
||||
assert_array(_song.players_given_point).contains_exactly(["crono-id"])
|
||||
|
||||
|
||||
func test_removing_point_of_player_without_point_changes_nothing() -> void:
|
||||
_song.add_point("crono-id")
|
||||
|
||||
_song.remove_point("marle-id")
|
||||
|
||||
assert_array(_song.players_given_point).contains_exactly(["crono-id"])
|
||||
@@ -0,0 +1 @@
|
||||
uid://cfc78daetbf6i
|
||||
Reference in new issue
Block a user