156 lines
6.4 KiB
GDScript
156 lines
6.4 KiB
GDScript
extends SceneTree
|
|
|
|
# Headless gameplay test: run with
|
|
# godot --headless -s res://tests/test_main_menu.gd
|
|
# Verifies:
|
|
# 1. the menu loads with Play/Map Editor/Quit; Map Editor is enabled and
|
|
# reaches PlanetSetupEditor.tscn (#48)
|
|
# 2. there is no Quit entry in any build - a web build has nothing to quit
|
|
# to, and Android's back gesture already does it
|
|
# 3. Play resets GameState to a fresh game AND reaches Main.tscn
|
|
# 4. #169 - a press puts a loading screen up FIRST and swaps the scene
|
|
# afterwards. A button that does nothing for several seconds reads as a
|
|
# button that was not pressed, which is how the web build reads; the
|
|
# order is the whole fix, so it is the thing asserted.
|
|
# 5. #169 - a second press while a load is running is ignored, or an
|
|
# impatient double tap builds the world twice (on the web a button fires
|
|
# on PRESS, so that tap costs nothing to make)
|
|
# 6. #174 - the opaque loading cover fills the viewport and puts its
|
|
# activity bar at the middle of the screen
|
|
|
|
var menu: Node
|
|
var game_state: Node
|
|
var elapsed: float = 0.0
|
|
var stage: int = 0
|
|
|
|
|
|
func _initialize() -> void:
|
|
game_state = root.get_node("GameState")
|
|
menu = load("res://ui/MainMenu.tscn").instantiate()
|
|
root.add_child(menu)
|
|
current_scene = menu
|
|
|
|
|
|
func _process(delta: float) -> bool:
|
|
elapsed += delta
|
|
match stage:
|
|
0:
|
|
# A scene added to the tree inside _initialize() has not had its
|
|
# _ready() flushed yet - see test_planet_setups.gd for the same
|
|
# hazard - so the menu's @onready buttons aren't valid until a
|
|
# frame has actually processed.
|
|
if elapsed < 0.1:
|
|
return false
|
|
if not menu.editor_button.enabled:
|
|
return _fail("map editor button is not enabled (#48 shipped)")
|
|
# Both development entries follow the same rule, and the rule is
|
|
# what is under test rather than the concrete boolean - this run
|
|
# is from Godot, so it exercises the "shown" branch.
|
|
var developing: bool = menu.is_developer_build()
|
|
if menu.editor_button.visible != developing or menu.editor_label.visible != developing:
|
|
return _fail("map editor entry visibility did not follow is_developer_build()")
|
|
if menu.dev_button.visible != developing or menu.dev_label.visible != developing:
|
|
return _fail("dev mode entry visibility did not follow is_developer_build()")
|
|
if menu.has_node("UI/QuitButton") or menu.has_node("UI/QuitLabel"):
|
|
return _fail("the menu still carries a Quit entry")
|
|
|
|
_press(menu.editor_button)
|
|
|
|
# --- 4. the cover goes up before anything blocks --------------
|
|
if menu.loading_path != "res://surface/PlanetSetupEditor.tscn":
|
|
return _fail("the press did not start a load (loading_path %s)" % menu.loading_path)
|
|
if menu.loading_screen == null or not menu.loading_screen.is_inside_tree():
|
|
return _fail("the press started a load with no loading screen up")
|
|
if current_scene != menu:
|
|
return _fail("the scene swapped inside the press, so nothing was ever shown")
|
|
var viewport_size: Vector2 = menu.get_viewport().get_visible_rect().size
|
|
if menu.loading_screen.size.distance_to(viewport_size) > 1.0:
|
|
return _fail("loading cover is %s, viewport is %s" \
|
|
% [menu.loading_screen.size, viewport_size])
|
|
if menu.loading_screen.BACKDROP.a < 0.999:
|
|
return _fail("loading cover is translucent (alpha %.2f)" \
|
|
% menu.loading_screen.BACKDROP.a)
|
|
var bar: Rect2 = menu.loading_screen._bar_rect()
|
|
if absf(bar.get_center().y - viewport_size.y * 0.5) > 1.0:
|
|
return _fail("loading bar centre %.1f is not screen centre %.1f" \
|
|
% [bar.get_center().y, viewport_size.y * 0.5])
|
|
print("pressing an entry raises the loading screen and leaves the menu up")
|
|
print("loading cover fills %s and centres its bar" % viewport_size)
|
|
|
|
# --- 5. and a second press changes nothing --------------------
|
|
var screen: Node = menu.loading_screen
|
|
_press(menu.editor_button)
|
|
_press(menu.play_button)
|
|
if menu.loading_screen != screen \
|
|
or menu.loading_path != "res://surface/PlanetSetupEditor.tscn":
|
|
return _fail("a second press while loading started another scene")
|
|
print("further presses while a load is running are ignored")
|
|
|
|
elapsed = 0.0
|
|
stage = 1
|
|
return false
|
|
1:
|
|
# Waited on rather than timed: the load is spread over frames now,
|
|
# and how many it takes is a property of the machine.
|
|
var on_editor: bool = current_scene != null and current_scene.name == "PlanetSetupEditor"
|
|
if not on_editor:
|
|
if elapsed < 30.0:
|
|
return false
|
|
return _fail("pressing Map Editor did not reach PlanetSetupEditor.tscn in 30s")
|
|
print("the load finished and the scene swapped")
|
|
|
|
# Return to the menu the same way the player would, then test Play.
|
|
menu = load("res://ui/MainMenu.tscn").instantiate()
|
|
root.add_child(menu)
|
|
current_scene = menu
|
|
elapsed = 0.0
|
|
stage = 2
|
|
return false
|
|
2:
|
|
if elapsed < 0.1:
|
|
return false
|
|
# Dirty the game state exactly like an in-progress run would,
|
|
# so a genuine reset is distinguishable from a coincidental match.
|
|
game_state.bank_inventory.set_amount(GameState.RESOURCE_ORE, 999)
|
|
game_state.add_building(GameState.BUILDING_MOTHERSHIP, Vector2.ZERO)
|
|
game_state.surviving_ship_count = 1
|
|
game_state.planet_capture["chunk:1:1"] = {"captured": [true], "conquered": true}
|
|
|
|
_press(menu.play_button)
|
|
elapsed = 0.0
|
|
stage = 3
|
|
return false
|
|
3:
|
|
var on_main: bool = current_scene != null and current_scene.name == "Main"
|
|
if not on_main and elapsed < 30.0:
|
|
return false
|
|
# A normal game now starts with NOTHING banked - the opening
|
|
# mining run is the tutorial, and 500 free ore skipped it.
|
|
var reset_ok: bool = game_state.rocks == 0 and game_state.buildings.is_empty() \
|
|
and game_state.surviving_ship_count == GameState.STARTING_SHIP_COUNT \
|
|
and game_state.planet_capture.is_empty()
|
|
print("on_main=%s reset_ok=%s rocks=%d buildings=%d ship_count=%d" % [
|
|
on_main, reset_ok, game_state.rocks, game_state.buildings.size(),
|
|
game_state.surviving_ship_count
|
|
])
|
|
if not on_main or not reset_ok:
|
|
return _fail("Play did not reset to a fresh game before reaching Main.tscn")
|
|
print("TEST_MAIN_MENU: PASS")
|
|
quit(0)
|
|
return true
|
|
return false
|
|
|
|
|
|
func _press(button: IconButton) -> void:
|
|
var down: InputEventScreenTouch = InputEventScreenTouch.new()
|
|
down.pressed = true
|
|
button._gui_input(down)
|
|
var up: InputEventScreenTouch = InputEventScreenTouch.new()
|
|
up.pressed = false
|
|
button._gui_input(up)
|
|
|
|
|
|
func _fail(message: String) -> bool:
|
|
print("TEST_MAIN_MENU: FAIL (%s)" % message)
|
|
quit(1)
|
|
return true
|