spaceship/tests/test_research_bar_columns.gd

159 lines
6 KiB
GDScript3
Raw Normal View History

extends SceneTree
# Headless test: run with
# godot --headless -s res://tests/test_research_bar_columns.gd
# Forgejo #95, both halves of it.
#
# The research menu's resource table printed BANKED, FLEET and LOCAL, and
# its LOCAL column was always a copy of BANKED. That was not a coincidence:
# the research action lives on the mothership, the mothership also carries
# the "bank" capability, and GameState.building_inventory() redirects any
# bank-capable building to bank_inventory itself. The two columns were the
# same store read twice.
#
# The panel also wore a "Research" heading, which named a screen you can
# only arrive at by pressing the research button, and charged the tree
# canvas 128px for the privilege.
#
# Covers:
# A. the research bar shows BANKED and FLEET and hides LOCAL
# B. the two remaining columns tile the full width - dropping one must not
# leave a third of the table blank
# C. a building that is NOT the bank still gets its LOCAL column, so the
# fix removes a duplicate rather than the feature
# D. no heading above the table, and the tree canvas starts right under it
const GAP_TOLERANCE: float = 48.0
var game_state: Node
var scene: Node
var controller: Node
var skill_menu: Control
var craft_menu: Control
var stage: int = 0
var elapsed: float = 0.0
func _initialize() -> void:
# GameState is an autoload: a bare GameState.<instance member> does not
# compile in a SceneTree script, so the node is fetched by name instead.
game_state = root.get_node("GameState")
game_state.buildings.clear()
game_state.extractors.clear()
game_state.reset_progression()
game_state.reset_formation()
game_state.rocks = 500
scene = load("res://world/Main.tscn").instantiate()
root.add_child(scene)
controller = scene.get_node("BuildController")
skill_menu = scene.get_node("UI/SkillMenu")
craft_menu = scene.get_node("UI/CraftMenu")
func _building(building_type: String) -> Node2D:
for building in scene.get_node("BuildingField").spawned:
if is_instance_valid(building) and building.building_type == building_type:
return building
return null
func _shown_headers(bar: Control) -> Array:
var shown: Array = []
for label in bar._header_labels:
if label.visible and label.text != "":
shown.append(label.text)
return shown
func _process(delta: float) -> bool:
elapsed += delta
if stage == 0:
scene.get_node("AsteroidField").set_process(false)
scene.get_node("EnemySpawner").set_process(false)
var field: Node = scene.get_node("BuildingField")
field.place(GameState.BUILDING_MOTHERSHIP, Vector2(400.0, 0.0))
field.place(GameState.BUILDING_SHIP_CRAFTER, Vector2(-400.0, 0.0))
elapsed = 0.0
stage = 1
return false
if stage != 1 or elapsed < 0.2:
return false
stage = 2
var mothership: Node2D = _building(GameState.BUILDING_MOTHERSHIP)
if mothership == null:
return _fail("no mothership was placed")
controller.current_building = mothership
controller._on_action_pressed()
if not skill_menu.visible:
return _fail("the mothership did not open the research menu")
var bar: Control = scene.get_node("UI/SkillMenu/Bar")
# One frame of _process is what tells the bar its building changed.
bar._process(0.0)
# --- A. LOCAL is gone from the research table ----------------------
var headers: Array = _shown_headers(bar)
if headers != ["BANKED", "FLEET"]:
return _fail("the research table shows %s, not just BANKED and FLEET" % str(headers))
for resource_type in game_state.resource_types():
var row: Dictionary = bar._rows[resource_type]
if row.local.visible:
return _fail("the LOCAL value for %s is still drawn" % resource_type)
if not row.bank.visible or not row.fleet.visible:
return _fail("dropping LOCAL also hid a column that is not redundant")
print("the research table drops the LOCAL copy of BANKED")
# --- B. the survivors share the freed width -------------------------
var first_row: Dictionary = bar._rows[game_state.resource_types()[0]]
var covered: float = (first_row.fleet.position.x + first_row.fleet.size.x) \
- first_row.bank.position.x
var expected: float = bar.size.x - bar.size.x * 0.25
if not is_equal_approx(covered, expected):
return _fail("the two columns cover %.0fpx of %.0fpx - the dropped column left a hole" \
% [covered, expected])
print("BANKED and FLEET share the whole %.0fpx of value width" % covered)
# --- C. a non-bank building keeps its LOCAL column ------------------
skill_menu.visible = false
var crafter: Node2D = _building(GameState.BUILDING_SHIP_CRAFTER)
if crafter == null:
return _fail("no ship crafter was placed")
controller.current_building = crafter
controller._on_action_pressed()
if not craft_menu.visible:
return _fail("the crafter did not open the craft menu")
var craft_bar: Control = scene.get_node("UI/CraftMenu/Bar")
craft_bar._process(0.0)
var craft_headers: Array = _shown_headers(craft_bar)
if craft_headers != ["BANKED", "FLEET", "LOCAL"]:
return _fail("the crafter table shows %s - its LOCAL stock is not the bank" \
% str(craft_headers))
print("a building that is not the bank keeps LOCAL")
# --- D. no heading, and the tree starts under the table -------------
for child in skill_menu.get_children():
if child is Label and String(child.text).strip_edges() == "Research":
return _fail("the \"Research\" heading is still in the panel")
var tree_view: Control = scene.get_node("UI/SkillMenu/Tree")
var bar_bottom: float = bar.position.y + bar.size.y
var head_gap: float = tree_view.position.y - bar_bottom
if bar.position.y > GAP_TOLERANCE:
return _fail("%.0fpx of empty panel above the table - the heading's hole" % bar.position.y)
if head_gap < 0.0:
return _fail("the tree canvas overlaps the table above it")
if head_gap > GAP_TOLERANCE:
return _fail("%.0fpx of unused panel between the table and the tree" % head_gap)
print("no heading: the table sits %.0fpx down and the tree is %.0fpx tall" \
% [bar.position.y, tree_view.size.y])
print("TEST_RESEARCH_BAR_COLUMNS: PASS")
quit(0)
return true
func _fail(message: String) -> bool:
print("TEST_RESEARCH_BAR_COLUMNS: FAIL (%s)" % message)
quit(1)
return true