systems rework: buffs, prestige graph, research, modular architecture
Decouples core systems from centralized globals in favor of catalogue-based architecture and data-driven content. Key changes: - Prestige: node-based buff tech tree, graph panel, progress bar - Research: multi-worker system, per-generator research, xp generation - Ascension: meta-currency layer via multi-currency prestige resets - Buffs: refactored as generator-independent via catalogues - UI: currency panel, edge-scrolling camera + zoom, current-goal panel - Alchemy Tower: crafting building with recipe/cost system - Testing: 7 test suites (ascension, prestige, research, goals) - Content: migrated from hardcoded idles/ to gym format (tiny_sword)
This commit was merged in pull request #1.
This commit is contained in:
318
tests/test_ascension.gd
Normal file
318
tests/test_ascension.gd
Normal file
@@ -0,0 +1,318 @@
|
||||
extends Node
|
||||
|
||||
var passed: int = 0
|
||||
var failed: int = 0
|
||||
var _game_root: Node = null
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
await run()
|
||||
|
||||
|
||||
func run() -> void:
|
||||
print("\n=== TEST: Ascension Buff System ===\n")
|
||||
TestUtils.set_test_name("ascension")
|
||||
|
||||
_test_node_resource()
|
||||
_test_node_validation()
|
||||
_test_node_parent_checking()
|
||||
_test_catalogue_queries()
|
||||
_test_purchase_flow()
|
||||
_test_prestige_does_not_reset_prestige_buffs()
|
||||
_test_multiplier_computation()
|
||||
_test_save_load_roundtrip()
|
||||
|
||||
_print_summary()
|
||||
|
||||
|
||||
#region Node Resource Tests
|
||||
func _test_node_resource() -> void:
|
||||
print("--- Node Resource ---")
|
||||
|
||||
var node: PrestigeBuffNode = PrestigeBuffNode.new()
|
||||
node.id = &"test_node"
|
||||
node.display_name = "Test Node"
|
||||
node.effect_type = PrestigeBuffNode.EffectType.CURRENCY_PRODUCTION_MULTIPLIER
|
||||
node.effect_value = 2.0
|
||||
node.cost_mantissa = 5.0
|
||||
node.cost_exponent = 2 # cost = 5e2 = 500
|
||||
|
||||
var cost: BigNumber = node.get_cost()
|
||||
TestUtils.assert_equals(5.0, cost.mantissa, "cost mantissa matches")
|
||||
TestUtils.assert_equals(2, cost.exponent, "cost exponent matches")
|
||||
TestUtils.assert_false(node.has_parents(), "node with no parents returns false")
|
||||
|
||||
|
||||
func _test_node_validation() -> void:
|
||||
print("--- Node Validation ---")
|
||||
|
||||
var valid: PrestigeBuffNode = PrestigeBuffNode.new()
|
||||
valid.id = &"valid_id"
|
||||
valid.cost_mantissa = 1.0
|
||||
TestUtils.assert_true(valid.is_valid(), "node with id and positive cost is valid")
|
||||
|
||||
var no_id: PrestigeBuffNode = PrestigeBuffNode.new()
|
||||
no_id.cost_mantissa = 1.0
|
||||
TestUtils.assert_false(no_id.is_valid(), "node with empty id is invalid")
|
||||
|
||||
var zero_cost: PrestigeBuffNode = PrestigeBuffNode.new()
|
||||
zero_cost.id = &"has_id"
|
||||
zero_cost.cost_mantissa = 0.0
|
||||
TestUtils.assert_false(zero_cost.is_valid(), "node with zero cost is invalid")
|
||||
|
||||
|
||||
func _test_node_parent_checking() -> void:
|
||||
print("--- Parent Checking ---")
|
||||
|
||||
var unlocked: Dictionary = {}
|
||||
unlocked[&"parent_a"] = true
|
||||
unlocked[&"parent_b"] = false
|
||||
|
||||
var node: PrestigeBuffNode = PrestigeBuffNode.new()
|
||||
node.id = &"child"
|
||||
node.parent_ids = [&"parent_a"]
|
||||
|
||||
TestUtils.assert_true(node.all_parents_unlocked(unlocked), "all parents unlocked when all are true")
|
||||
|
||||
node.parent_ids = [&"parent_a", &"parent_b"]
|
||||
TestUtils.assert_false(node.all_parents_unlocked(unlocked), "not all parents unlocked when one is false")
|
||||
|
||||
node.parent_ids = [&"nonexistent"]
|
||||
TestUtils.assert_false(node.all_parents_unlocked(unlocked), "missing parent treated as not unlocked")
|
||||
#endregion
|
||||
|
||||
|
||||
#region Catalogue Tests
|
||||
func _test_catalogue_queries() -> void:
|
||||
print("--- Catalogue Queries ---")
|
||||
|
||||
var root: PrestigeBuffNode = _make_node(&"root", [], 0, 0.0)
|
||||
var child_a: PrestigeBuffNode = _make_node(&"child_a", [&"root"], 1, 0.0)
|
||||
var child_b: PrestigeBuffNode = _make_node(&"child_b", [&"root"], 1, 1.0)
|
||||
var grandchild: PrestigeBuffNode = _make_node(&"grandchild", [&"child_a", &"child_b"], 2, 0.0)
|
||||
|
||||
var cat: PrestigeBuffCatalogue = PrestigeBuffCatalogue.new()
|
||||
cat.nodes = [root, child_a, child_b, grandchild]
|
||||
|
||||
TestUtils.assert_equals(root, cat.get_node_by_id(&"root"), "get_node_by_id finds root")
|
||||
TestUtils.assert_equals(null, cat.get_node_by_id(&"nonexistent"), "get_node_by_id returns null for unknown id")
|
||||
|
||||
var ids: Array[StringName] = cat.get_all_ids()
|
||||
TestUtils.assert_equals(4, ids.size(), "get_all_ids returns 4 ids")
|
||||
|
||||
var roots: Array[PrestigeBuffNode] = cat.get_root_nodes()
|
||||
TestUtils.assert_equals(1, roots.size(), "one root node")
|
||||
TestUtils.assert_equals(&"root", roots[0].id, "root node id is 'root'")
|
||||
|
||||
var children: Array[PrestigeBuffNode] = cat.get_children_of(&"root")
|
||||
TestUtils.assert_equals(2, children.size(), "root has 2 children")
|
||||
|
||||
var grandkids: Array[PrestigeBuffNode] = cat.get_children_of(&"child_a")
|
||||
TestUtils.assert_equals(1, grandkids.size(), "child_a has 1 child")
|
||||
#endregion
|
||||
|
||||
|
||||
#region Purchase Flow Tests
|
||||
func _test_purchase_flow() -> void:
|
||||
print("--- Purchase Flow ---")
|
||||
|
||||
var scene: PackedScene = load("res://docs/gyms/tiny_sword/tiny_sword.tscn")
|
||||
_game_root = scene.instantiate()
|
||||
add_child(_game_root)
|
||||
await _wait()
|
||||
|
||||
var gs: LevelGameState = _game_root.find_child("LevelGameState")
|
||||
if gs == null:
|
||||
print("[ERROR] LevelGameState not found")
|
||||
return
|
||||
|
||||
# Give ascension currency for purchasing
|
||||
gs.add_currency_by_id(&"ascension", BigNumber.from_float(100.0))
|
||||
await _wait()
|
||||
|
||||
# Verify catalogue has our two nodes
|
||||
var cat: PrestigeBuffCatalogue = gs.prestige_buff_catalogue
|
||||
TestUtils.assert_not_null(cat, "prestige_buff_catalogue is set")
|
||||
if cat == null:
|
||||
return
|
||||
|
||||
TestUtils.assert_true(cat.nodes.size() >= 2, "catalogue has at least 2 nodes")
|
||||
|
||||
# Verify nodes initialized in state
|
||||
TestUtils.assert_false(gs.is_prestige_buff_unlocked(&"gold_boost"), "gold_boost starts locked")
|
||||
TestUtils.assert_false(gs.is_prestige_buff_unlocked(&"farm_boost"), "farm_boost starts locked")
|
||||
|
||||
# Check can_purchase (both are root nodes with no parents, should be purchaseable)
|
||||
TestUtils.assert_true(gs.can_purchase_prestige_buff(&"gold_boost"), "gold_boost is purchasable")
|
||||
TestUtils.assert_true(gs.can_purchase_prestige_buff(&"farm_boost"), "farm_boost is purchasable")
|
||||
|
||||
# Check cost
|
||||
var cost: BigNumber = gs.get_prestige_buff_cost(&"gold_boost")
|
||||
TestUtils.assert_greater_than(cost.mantissa, 0.0, "gold_boost cost > 0")
|
||||
|
||||
# Purchase gold_boost
|
||||
var purchased: bool = gs.purchase_prestige_buff(&"gold_boost")
|
||||
TestUtils.assert_true(purchased, "purchase_prestige_buff returns true")
|
||||
TestUtils.assert_true(gs.is_prestige_buff_unlocked(&"gold_boost"), "gold_boost is now unlocked")
|
||||
TestUtils.assert_false(gs.can_purchase_prestige_buff(&"gold_boost"), "cannot repurchase already unlocked node")
|
||||
|
||||
# farm_boost should still be available (both are roots, independent)
|
||||
TestUtils.assert_true(gs.can_purchase_prestige_buff(&"farm_boost"), "farm_boost is still purchasable")
|
||||
|
||||
# Verify available buffs
|
||||
var available: Array[PrestigeBuffNode] = gs.get_available_prestige_buffs()
|
||||
var found_farm: bool = false
|
||||
var found_gold: bool = false
|
||||
for node in available:
|
||||
if node.id == &"farm_boost":
|
||||
found_farm = true
|
||||
if node.id == &"gold_boost":
|
||||
found_gold = true
|
||||
TestUtils.assert_true(found_farm, "farm_boost appears in available buffs")
|
||||
TestUtils.assert_false(found_gold, "gold_boost does NOT appear in available buffs (already unlocked)")
|
||||
|
||||
# Test cannot purchase with insufficient currency
|
||||
gs._prestige_buff_unlocked.clear()
|
||||
gs._initialize_prestige_buffs()
|
||||
# Drain ascension currency
|
||||
var bal: BigNumber = gs.get_currency_amount_by_id(&"ascension")
|
||||
gs.spend_currency_by_id(&"ascension", bal)
|
||||
TestUtils.assert_false(gs.can_purchase_prestige_buff(&"gold_boost"), "cannot purchase with no currency")
|
||||
|
||||
# Restore currency, re-unlock gold_boost for later tests
|
||||
gs.add_currency_by_id(&"ascension", BigNumber.from_float(100.0))
|
||||
gs.purchase_prestige_buff(&"gold_boost")
|
||||
await _wait()
|
||||
#endregion
|
||||
|
||||
|
||||
func _test_prestige_does_not_reset_prestige_buffs() -> void:
|
||||
print("--- Prestige Does Not Reset Prestige ---")
|
||||
|
||||
if _game_root == null:
|
||||
return
|
||||
|
||||
var gs: LevelGameState = _game_root.find_child("LevelGameState")
|
||||
if gs == null:
|
||||
return
|
||||
|
||||
# gold_boost should be unlocked from previous test
|
||||
TestUtils.assert_true(gs.is_prestige_buff_unlocked(&"gold_boost"), "gold_boost unlocked before prestige")
|
||||
|
||||
# Give enough currency to prestige
|
||||
gs.add_currency_by_id(&"gold", BigNumber.new(1.0, 10)) # 1e10 gold
|
||||
await _wait()
|
||||
|
||||
var pm: PrestigeManager = _game_root.find_child("PrestigeManager") as PrestigeManager
|
||||
if pm == null:
|
||||
print("[WARN] PrestigeManager not found; skipping prestige test")
|
||||
return
|
||||
|
||||
# Perform prestige if possible
|
||||
if pm.can_prestige():
|
||||
pm.perform_prestige()
|
||||
await _wait()
|
||||
|
||||
# After prestige, gold_boost should STILL be unlocked
|
||||
TestUtils.assert_true(gs.is_prestige_buff_unlocked(&"gold_boost"), "gold_boost still unlocked after prestige")
|
||||
else:
|
||||
# Manual simulation: call reset_for_prestige directly and verify ascension survives
|
||||
gs.reset_for_prestige(true, false, [&"ascension"])
|
||||
await _wait()
|
||||
TestUtils.assert_true(gs.is_prestige_buff_unlocked(&"gold_boost"), "gold_boost still unlocked after reset_for_prestige")
|
||||
#endregion
|
||||
|
||||
|
||||
func _test_multiplier_computation() -> void:
|
||||
print("--- Multiplier Computation ---")
|
||||
|
||||
if _game_root == null:
|
||||
return
|
||||
|
||||
var gs: LevelGameState = _game_root.find_child("LevelGameState")
|
||||
if gs == null:
|
||||
return
|
||||
|
||||
# gold_boost is already unlocked (from purchase test). Effect type 1 = GENERATOR_PRODUCTION_MULTIPLIER
|
||||
var mult: float = gs.get_prestige_buff_multiplier(PrestigeBuffNode.EffectType.CURRENCY_PRODUCTION_MULTIPLIER, &"goldmine")
|
||||
TestUtils.assert_greater_than(mult, 1.0, "multiplier for goldmine is > 1.0 with gold_boost unlocked")
|
||||
|
||||
# farm_boost is NOT unlocked, multiplier for farm should be 1.0
|
||||
var farm_mult: float = gs.get_prestige_buff_multiplier(PrestigeBuffNode.EffectType.CURRENCY_PRODUCTION_MULTIPLIER, &"farm")
|
||||
TestUtils.assert_equals(1.0, farm_mult, "multiplier for farm is 1.0 (farm_boost not unlocked)")
|
||||
|
||||
# Unlock farm_boost and check again
|
||||
gs.add_currency_by_id(&"ascension", BigNumber.from_float(100.0))
|
||||
gs.purchase_prestige_buff(&"farm_boost")
|
||||
await _wait()
|
||||
|
||||
var farm_mult2: float = gs.get_prestige_buff_multiplier(PrestigeBuffNode.EffectType.CURRENCY_PRODUCTION_MULTIPLIER, &"farm")
|
||||
TestUtils.assert_greater_than(farm_mult2, 1.0, "multiplier for farm is > 1.0 after unlocking farm_boost")
|
||||
|
||||
# Check that unrelated effect type returns 1.0
|
||||
var unrelated: float = gs.get_prestige_buff_multiplier(PrestigeBuffNode.EffectType.RESEARCH_XP_MULTIPLIER, &"")
|
||||
TestUtils.assert_equals(1.0, unrelated, "unrelated effect type returns 1.0")
|
||||
#endregion
|
||||
|
||||
|
||||
func _test_save_load_roundtrip() -> void:
|
||||
print("--- Save/Load Roundtrip ---")
|
||||
|
||||
if _game_root == null:
|
||||
return
|
||||
|
||||
var gs: LevelGameState = _game_root.find_child("LevelGameState")
|
||||
if gs == null:
|
||||
return
|
||||
|
||||
# Both gold_boost and farm_boost should be unlocked
|
||||
TestUtils.assert_true(gs.is_prestige_buff_unlocked(&"gold_boost"), "gold_boost unlocked before save")
|
||||
TestUtils.assert_true(gs.is_prestige_buff_unlocked(&"farm_boost"), "farm_boost unlocked before save")
|
||||
|
||||
# Save
|
||||
gs.save_game()
|
||||
await _wait()
|
||||
|
||||
# Clear state manually to simulate fresh load
|
||||
gs._prestige_buff_unlocked.clear()
|
||||
TestUtils.assert_false(gs.is_prestige_buff_unlocked(&"gold_boost"), "gold_boost false after clear")
|
||||
TestUtils.assert_false(gs.is_prestige_buff_unlocked(&"farm_boost"), "farm_boost false after clear")
|
||||
|
||||
# Load
|
||||
gs.load_game()
|
||||
await _wait()
|
||||
|
||||
# Both should be restored
|
||||
TestUtils.assert_true(gs.is_prestige_buff_unlocked(&"gold_boost"), "gold_boost restored after load")
|
||||
TestUtils.assert_true(gs.is_prestige_buff_unlocked(&"farm_boost"), "farm_boost restored after load")
|
||||
#endregion
|
||||
|
||||
|
||||
#region Helpers
|
||||
func _make_node(id: StringName, parent_ids: Array[StringName], tier: int, x_pos: float) -> PrestigeBuffNode:
|
||||
var n: PrestigeBuffNode = PrestigeBuffNode.new()
|
||||
n.id = id
|
||||
n.display_name = String(id)
|
||||
n.parent_ids = parent_ids
|
||||
n.tier = tier
|
||||
n.x_position = x_pos
|
||||
n.cost_mantissa = 1.0
|
||||
return n
|
||||
|
||||
|
||||
func _wait() -> void:
|
||||
await get_tree().create_timer(0.3).timeout
|
||||
|
||||
|
||||
func _print_summary() -> void:
|
||||
TestUtils.print_result()
|
||||
passed = TestUtils.get_passed()
|
||||
failed = TestUtils.get_failed()
|
||||
|
||||
|
||||
func get_passed() -> int:
|
||||
return passed
|
||||
|
||||
|
||||
func get_failed() -> int:
|
||||
return failed
|
||||
1
tests/test_ascension.gd.uid
Normal file
1
tests/test_ascension.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://cwmf4h7be356h
|
||||
146
tests/test_goals_prestige_reset.gd
Normal file
146
tests/test_goals_prestige_reset.gd
Normal file
@@ -0,0 +1,146 @@
|
||||
@tool
|
||||
extends Node
|
||||
|
||||
var _game_root: Node = null
|
||||
|
||||
func _ready() -> void:
|
||||
if not Engine.is_editor_hint():
|
||||
test_goals_prestige_reset()
|
||||
get_tree().quit()
|
||||
|
||||
func test_goals_prestige_reset() -> void:
|
||||
print("\n=== TEST: Goals Prestige Reset ===\n")
|
||||
TestUtils.set_test_name("goals_prestige_reset")
|
||||
|
||||
# Load the tiny_sword scene directly
|
||||
var scene = load("res://docs/gyms/tiny_sword/tiny_sword.tscn")
|
||||
_game_root = scene.instantiate()
|
||||
add_child(_game_root)
|
||||
|
||||
print("[TARGET] children_after_load: %d" % get_child_count())
|
||||
|
||||
var game_state: LevelGameState = _game_root.find_child("LevelGameState")
|
||||
if game_state == null:
|
||||
print("[ERROR] LevelGameState not found")
|
||||
_print_summary(0, 1)
|
||||
return
|
||||
|
||||
print("[TARGET] game_state_found: true")
|
||||
|
||||
var goals: Array[GoalData] = game_state.get_all_goals()
|
||||
if goals.is_empty():
|
||||
print("[ERROR] No goals found in game state")
|
||||
_print_summary(0, 1)
|
||||
return
|
||||
|
||||
var test_goal: GoalData = goals[0]
|
||||
print("[TARGET] test_goal_id: %s" % String(test_goal.id))
|
||||
|
||||
var gold: BigNumber = BigNumber.from_float(1000000.0)
|
||||
game_state.add_currency_by_id(&"gold", gold)
|
||||
|
||||
var is_met_before: bool = game_state.is_goal_met(test_goal)
|
||||
print("[TARGET] goal_met_before_prestige: %s" % str(is_met_before))
|
||||
|
||||
TestUtils.assert_true(
|
||||
is_met_before,
|
||||
"Goal should be met before prestige (with high currency)"
|
||||
)
|
||||
|
||||
var is_completed_before: bool = game_state.is_goal_completed(test_goal.id)
|
||||
print("[TARGET] goal_completed_before_prestige: %s" % str(is_completed_before))
|
||||
|
||||
game_state.reset_for_prestige(true, true, [])
|
||||
|
||||
var is_completed_after_reset: bool = game_state.is_goal_completed(test_goal.id)
|
||||
print("[TARGET] goal_completed_after_prestige: %s" % str(is_completed_after_reset))
|
||||
|
||||
TestUtils.assert_true(
|
||||
not is_completed_after_reset,
|
||||
"Goal should NOT be completed after prestige reset (BUG FIX VERIFICATION)"
|
||||
)
|
||||
|
||||
var gold_after: BigNumber = game_state.get_currency_amount_by_id(&"gold")
|
||||
print("[TARGET] gold_after_prestige: %s" % gold_after.to_string_suffix(2))
|
||||
|
||||
TestUtils.assert_equals(
|
||||
0.0,
|
||||
gold_after.mantissa,
|
||||
"Gold should be reset to 0 after prestige"
|
||||
)
|
||||
|
||||
var total_gold_after: BigNumber = game_state.get_total_currency_acquired_by_id(&"gold")
|
||||
print("[TARGET] total_gold_after_prestige: %s" % total_gold_after.to_string_suffix(2))
|
||||
|
||||
TestUtils.assert_equals(
|
||||
0.0,
|
||||
total_gold_after.mantissa,
|
||||
"Total gold should be reset to 0 after prestige"
|
||||
)
|
||||
|
||||
var is_met_after: bool = game_state.is_goal_met(test_goal)
|
||||
print("[TARGET] goal_met_after_prestige: %s" % str(is_met_after))
|
||||
|
||||
TestUtils.assert_true(
|
||||
not is_met_after,
|
||||
"Goal should NOT be met after prestige (currency reset to 0)"
|
||||
)
|
||||
|
||||
var gold_for_unlock: BigNumber = BigNumber.from_float(1000000.0)
|
||||
game_state.add_currency_by_id(&"gold", gold_for_unlock)
|
||||
|
||||
var is_met_again: bool = game_state.is_goal_met(test_goal)
|
||||
print("[TARGET] goal_met_after_reaching_currency: %s" % str(is_met_again))
|
||||
|
||||
TestUtils.assert_true(
|
||||
is_met_again,
|
||||
"Goal should be met again after adding sufficient currency"
|
||||
)
|
||||
|
||||
var is_completed_auto: bool = game_state.is_goal_completed(test_goal.id)
|
||||
print("[TARGET] goal_completed_auto_check: %s" % str(is_completed_auto))
|
||||
|
||||
# This goal has AUTOMATIC unlock behavior, so it should be auto-completed when met
|
||||
if test_goal.unlock_behavior == GoalData.UnlockBehavior.AUTOMATIC:
|
||||
TestUtils.assert_true(
|
||||
is_completed_auto,
|
||||
"Goal with AUTOMATIC unlock should be auto-completed when met"
|
||||
)
|
||||
|
||||
# Try manual completion (should be a no-op if already auto-completed)
|
||||
game_state._complete_goal_manually(test_goal.id)
|
||||
|
||||
var is_completed: bool = game_state.is_goal_completed(test_goal.id)
|
||||
print("[TARGET] goal_completed_after_attempt: %s" % str(is_completed))
|
||||
|
||||
TestUtils.assert_true(
|
||||
is_completed,
|
||||
"Goal should be completed (either auto or manual)"
|
||||
)
|
||||
|
||||
game_state.reset_for_prestige(true, true, [])
|
||||
|
||||
var is_completed_after_second_prestige: bool = game_state.is_goal_completed(test_goal.id)
|
||||
print("[TARGET] goal_completed_after_second_prestige: %s" % str(is_completed_after_second_prestige))
|
||||
|
||||
TestUtils.assert_true(
|
||||
not is_completed_after_second_prestige,
|
||||
"Goal should NOT be completed after second prestige reset"
|
||||
)
|
||||
|
||||
var is_met_final: bool = game_state.is_goal_met(test_goal)
|
||||
print("[TARGET] goal_met_final: %s" % str(is_met_final))
|
||||
|
||||
TestUtils.assert_true(
|
||||
not is_met_final,
|
||||
"Goal should NOT be met after second prestige reset"
|
||||
)
|
||||
|
||||
_print_summary(TestUtils.get_passed(), TestUtils.get_failed())
|
||||
|
||||
func _print_summary(passed_count: int, failed_count: int) -> void:
|
||||
TestUtils.print_result()
|
||||
if failed_count == 0:
|
||||
print("[RESULT] PASS")
|
||||
else:
|
||||
print("[RESULT] FAIL")
|
||||
1
tests/test_goals_prestige_reset.gd.uid
Normal file
1
tests/test_goals_prestige_reset.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://d23axeulmeeih
|
||||
6
tests/test_goals_prestige_reset.tscn
Normal file
6
tests/test_goals_prestige_reset.tscn
Normal file
@@ -0,0 +1,6 @@
|
||||
[gd_scene format=3 uid="uid://test_goals_prestige_reset"]
|
||||
|
||||
[ext_resource type="Script" path="res://tests/test_goals_prestige_reset.gd" id="1_test"]
|
||||
|
||||
[node name="TestRoot" type="Node"]
|
||||
script = ExtResource("1_test")
|
||||
75
tests/test_gold_mine_click.gd
Normal file
75
tests/test_gold_mine_click.gd
Normal file
@@ -0,0 +1,75 @@
|
||||
extends Node
|
||||
|
||||
var passed: int = 0
|
||||
var failed: int = 0
|
||||
var _game_root: Node = null
|
||||
|
||||
func _ready():
|
||||
await run()
|
||||
# Don't quit here - let test_runner handle it
|
||||
|
||||
func run():
|
||||
print("\n=== TEST: Gold Mine Click ===\n")
|
||||
TestUtils.set_test_name("gold_mine_click")
|
||||
|
||||
# Load tiny_sword scene
|
||||
var scene = load("res://docs/gyms/tiny_sword/tiny_sword.tscn")
|
||||
_game_root = scene.instantiate()
|
||||
add_child(_game_root)
|
||||
|
||||
await _wait()
|
||||
|
||||
var game_state: LevelGameState = _game_root.find_child("LevelGameState")
|
||||
var gold_mine = _game_root.find_child("GoldMine") as CurrencyGenerator
|
||||
|
||||
if game_state == null:
|
||||
print("[ERROR] LevelGameState not found")
|
||||
_print_summary()
|
||||
return
|
||||
|
||||
if gold_mine == null:
|
||||
print("[ERROR] GoldMine node not found")
|
||||
_print_summary()
|
||||
return
|
||||
|
||||
# Record initial gold
|
||||
var initial_gold: BigNumber = game_state.get_currency_amount_by_id(&"gold")
|
||||
print("[TARGET] initial_gold: %s" % initial_gold.to_string_suffix(2))
|
||||
|
||||
# Simulate clicking the generator
|
||||
gold_mine._on_pressed()
|
||||
|
||||
await _wait()
|
||||
|
||||
# Validate gold increased
|
||||
var final_gold: BigNumber = game_state.get_currency_amount_by_id(&"gold")
|
||||
print("[TARGET] final_gold: %s" % final_gold.to_string_suffix(2))
|
||||
|
||||
TestUtils.assert_greater_than(
|
||||
final_gold.mantissa,
|
||||
initial_gold.mantissa,
|
||||
"Gold increased after generator click"
|
||||
)
|
||||
|
||||
# Check specific value (adjust based on click_mantissa in data)
|
||||
var expected_min: float = initial_gold.mantissa + 1.0
|
||||
TestUtils.assert_true(
|
||||
final_gold.mantissa >= expected_min,
|
||||
"Gold increased by at least 1.0"
|
||||
)
|
||||
|
||||
_print_summary()
|
||||
|
||||
func _wait() -> void:
|
||||
await get_tree().create_timer(0.5).timeout
|
||||
|
||||
func _print_summary():
|
||||
TestUtils.print_result()
|
||||
passed = TestUtils.get_passed()
|
||||
failed = TestUtils.get_failed()
|
||||
|
||||
func get_passed() -> int:
|
||||
return passed
|
||||
|
||||
func get_failed() -> int:
|
||||
return failed
|
||||
1
tests/test_gold_mine_click.gd.uid
Normal file
1
tests/test_gold_mine_click.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://c2xborf2klkc4
|
||||
92
tests/test_prestige.gd
Normal file
92
tests/test_prestige.gd
Normal file
@@ -0,0 +1,92 @@
|
||||
extends Node
|
||||
|
||||
var passed: int = 0
|
||||
var failed: int = 0
|
||||
var _game_root: Node = null
|
||||
|
||||
func _ready():
|
||||
await run()
|
||||
# Let test_runner handle exit
|
||||
pass
|
||||
|
||||
func run():
|
||||
print("\n=== TEST: Prestige Mechanics ===\n")
|
||||
TestUtils.set_test_name("prestige")
|
||||
|
||||
var scene = load("res://docs/gyms/tiny_sword/tiny_sword.tscn")
|
||||
_game_root = scene.instantiate()
|
||||
add_child(_game_root)
|
||||
|
||||
await _wait()
|
||||
|
||||
var game_state: LevelGameState = _game_root.find_child("LevelGameState")
|
||||
var prestige_manager = _game_root.find_child("PrestigeManager") as PrestigeManager
|
||||
|
||||
if game_state == null:
|
||||
print("[ERROR] LevelGameState not found")
|
||||
_print_summary()
|
||||
return
|
||||
|
||||
if prestige_manager == null:
|
||||
print("[ERROR] PrestigeManager not found")
|
||||
_print_summary()
|
||||
return
|
||||
|
||||
# Setup: Accumulate currencies to meet prestige threshold
|
||||
game_state.add_currency_by_id(&"gold", BigNumber.from_float(100000.0))
|
||||
game_state.add_currency_by_id(&"gems", BigNumber.from_float(100.0))
|
||||
|
||||
await _wait()
|
||||
|
||||
# Check if can prestige
|
||||
var can_prestige: bool = prestige_manager.can_prestige()
|
||||
print("[TARGET] can_prestige: %s" % str(can_prestige))
|
||||
|
||||
TestUtils.assert_true(can_prestige, "Should be able to prestige after accumulating currencies")
|
||||
|
||||
if can_prestige:
|
||||
# Perform prestige
|
||||
prestige_manager.perform_prestige()
|
||||
|
||||
await _wait()
|
||||
|
||||
# Check ascension currency
|
||||
var ascension: BigNumber = game_state.get_currency_amount_by_id(&"ascension")
|
||||
print("[TARGET] ascension_currency: %s" % ascension.to_string_suffix(2))
|
||||
|
||||
TestUtils.assert_true(
|
||||
ascension.mantissa > 0,
|
||||
"Should receive ascension currency after prestige"
|
||||
)
|
||||
|
||||
# Check prestige multiplier increased
|
||||
var total_multiplier = prestige_manager.get_total_multiplier()
|
||||
print("[TARGET] total_multiplier: %s" % str(total_multiplier))
|
||||
|
||||
var mult_value: float
|
||||
if total_multiplier is float:
|
||||
mult_value = total_multiplier
|
||||
else:
|
||||
mult_value = float(total_multiplier)
|
||||
|
||||
TestUtils.assert_greater_than(
|
||||
mult_value,
|
||||
1.0,
|
||||
"Prestige multiplier should be > 1.0"
|
||||
)
|
||||
|
||||
_print_summary()
|
||||
|
||||
func _wait() -> void:
|
||||
await get_tree().create_timer(0.5).timeout
|
||||
|
||||
func _print_summary():
|
||||
TestUtils.print_result()
|
||||
passed = TestUtils.get_passed()
|
||||
failed = TestUtils.get_failed()
|
||||
|
||||
func get_passed() -> int:
|
||||
return passed
|
||||
|
||||
func get_failed() -> int:
|
||||
return failed
|
||||
1
tests/test_prestige.gd.uid
Normal file
1
tests/test_prestige.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://mm5vcjq44wf1
|
||||
159
tests/test_research_activation.gd
Normal file
159
tests/test_research_activation.gd
Normal file
@@ -0,0 +1,159 @@
|
||||
extends SceneTree
|
||||
|
||||
var passed: int = 0
|
||||
var failed: int = 0
|
||||
var _game_root: Node = null
|
||||
var _local_passed: int = 0
|
||||
var _local_failed: int = 0
|
||||
|
||||
func _init():
|
||||
await run()
|
||||
|
||||
func run():
|
||||
print("\n=== TEST: Research Activation ===\n")
|
||||
TestUtils.set_test_name("research_activation")
|
||||
|
||||
var scene = load("res://docs/gyms/tiny_sword/tiny_sword.tscn")
|
||||
_game_root = scene.instantiate()
|
||||
root.add_child(_game_root)
|
||||
|
||||
await _wait()
|
||||
|
||||
var game_state: LevelGameState = _game_root.find_child("LevelGameState")
|
||||
var research_panel: ResearchPanel = _game_root.find_child("ResearchPanel") as ResearchPanel
|
||||
|
||||
if game_state == null or research_panel == null:
|
||||
print("[ERROR] Missing LevelGameState or ResearchPanel node")
|
||||
_print_summary()
|
||||
quit(1)
|
||||
return
|
||||
|
||||
var gold_mine_node = game_state.find_child("GoldMine")
|
||||
var gold_mine: CurrencyGenerator = gold_mine_node.find_child("CurrencyGenerator") as CurrencyGenerator if gold_mine_node else null
|
||||
|
||||
if gold_mine == null:
|
||||
print("[ERROR] GoldMine node not found")
|
||||
_print_summary()
|
||||
quit(1)
|
||||
return
|
||||
|
||||
# Test 1: Verify initial state - no research should be active
|
||||
print("[TARGET] initial_active_research_id: %s" % research_panel.get_active_research_id())
|
||||
TestUtils.assert_true(
|
||||
research_panel.get_active_research_id() == &"",
|
||||
"No research active initially"
|
||||
)
|
||||
|
||||
# Test 2: Verify gold mine research exists and is linked to goldmine generator
|
||||
var gold_research = game_state.research_catalogue.get_research_by_id(&"gold_research")
|
||||
TestUtils.assert_not_null(
|
||||
gold_research,
|
||||
"Gold research exists in catalogue"
|
||||
)
|
||||
|
||||
if gold_research != null:
|
||||
TestUtils.assert_true(
|
||||
gold_research.generator_id == &"goldmine",
|
||||
"Gold research is linked to goldmine generator"
|
||||
)
|
||||
|
||||
# Test 3: Activate gold research
|
||||
research_panel.activate_research(&"gold_research")
|
||||
await _wait()
|
||||
|
||||
print("[TARGET] active_after_gold_activation: %s" % research_panel.get_active_research_id())
|
||||
TestUtils.assert_true(
|
||||
research_panel.get_active_research_id() == &"gold_research",
|
||||
"Gold research is active after activation"
|
||||
)
|
||||
|
||||
TestUtils.assert_true(
|
||||
research_panel.is_research_active(&"gold_research"),
|
||||
"is_research_active returns true for gold_research"
|
||||
)
|
||||
|
||||
# Test 4: Verify research XP is granted when research is active
|
||||
var initial_gold_research_xp = game_state.get_research_xp(&"gold_research")
|
||||
print("[TARGET] initial_gold_research_xp: %s" % initial_gold_research_xp.to_string_suffix(2))
|
||||
|
||||
# Simulate gold mine production (gold mine grants XP to gold_research)
|
||||
gold_mine._on_pressed()
|
||||
await _wait()
|
||||
|
||||
var final_gold_research_xp_active = game_state.get_research_xp(&"gold_research")
|
||||
print("[TARGET] final_gold_research_xp_active: %s" % final_gold_research_xp_active.to_string_suffix(2))
|
||||
|
||||
TestUtils.assert_true(
|
||||
final_gold_research_xp_active.mantissa > initial_gold_research_xp.mantissa,
|
||||
"Gold research XP increases when active (gold mine produces)"
|
||||
)
|
||||
|
||||
# Test 5: Deactivate gold research by activating another research
|
||||
var farm_research = game_state.research_catalogue.get_research_by_id(&"farm_research")
|
||||
TestUtils.assert_not_null(
|
||||
farm_research,
|
||||
"Farm research exists in catalogue"
|
||||
)
|
||||
|
||||
if farm_research != null:
|
||||
# Activate farm research (should deactivate gold research)
|
||||
research_panel.activate_research(&"farm_research")
|
||||
await _wait()
|
||||
|
||||
print("[TARGET] active_after_farm_activation: %s" % research_panel.get_active_research_id())
|
||||
TestUtils.assert_true(
|
||||
research_panel.get_active_research_id() == &"farm_research",
|
||||
"Farm research is active after activation"
|
||||
)
|
||||
|
||||
TestUtils.assert_false(
|
||||
research_panel.is_research_active(&"gold_research"),
|
||||
"Gold research is deactivated when farm is activated"
|
||||
)
|
||||
|
||||
# Test 6: Verify research XP is NOT granted when research is inactive
|
||||
var initial_gold_research_xp_inactive = game_state.get_research_xp(&"gold_research")
|
||||
print("[TARGET] initial_gold_research_xp_inactive: %s" % initial_gold_research_xp_inactive.to_string_suffix(2))
|
||||
|
||||
# Simulate gold mine production (gold mine should NOT grant XP to inactive gold_research)
|
||||
gold_mine._on_pressed()
|
||||
await _wait()
|
||||
|
||||
var final_gold_research_xp_inactive = game_state.get_research_xp(&"gold_research")
|
||||
print("[TARGET] final_gold_research_xp_inactive: %s" % final_gold_research_xp_inactive.to_string_suffix(2))
|
||||
|
||||
TestUtils.assert_equals(
|
||||
final_gold_research_xp_inactive.mantissa,
|
||||
initial_gold_research_xp_inactive.mantissa,
|
||||
"Gold research XP does not increase when inactive (gold mine produces)"
|
||||
)
|
||||
|
||||
# Test 7: Test mutual exclusivity with multiple activations
|
||||
research_panel.activate_research(&"gold_research")
|
||||
await _wait()
|
||||
research_panel.activate_research(&"farm_research")
|
||||
await _wait()
|
||||
research_panel.activate_research(&"gold_research")
|
||||
await _wait()
|
||||
|
||||
print("[TARGET] final_active_research_id: %s" % research_panel.get_active_research_id())
|
||||
TestUtils.assert_true(
|
||||
research_panel.get_active_research_id() == &"gold_research",
|
||||
"Only one research can be active at a time"
|
||||
)
|
||||
|
||||
_print_summary()
|
||||
if failed == 0:
|
||||
quit(0)
|
||||
else:
|
||||
quit(1)
|
||||
|
||||
func _wait() -> void:
|
||||
await create_timer(0.5).timeout
|
||||
|
||||
func _print_summary():
|
||||
TestUtils.print_result()
|
||||
_local_passed = TestUtils.get_passed()
|
||||
_local_failed = TestUtils.get_failed()
|
||||
passed = _local_passed
|
||||
failed = _local_failed
|
||||
1
tests/test_research_activation.gd.uid
Normal file
1
tests/test_research_activation.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://cf23bjqannrog
|
||||
128
tests/test_research_prestige_reset.gd
Normal file
128
tests/test_research_prestige_reset.gd
Normal file
@@ -0,0 +1,128 @@
|
||||
extends SceneTree
|
||||
|
||||
var passed: int = 0
|
||||
var failed: int = 0
|
||||
var _game_root: Node = null
|
||||
var _test_complete: bool = false
|
||||
|
||||
func _init():
|
||||
print("\n=== TEST: Research Level and XP Reset After Prestige ===\n")
|
||||
TestUtils.set_test_name("research_prestige_reset")
|
||||
|
||||
var scene = load("res://docs/gyms/tiny_sword/tiny_sword.tscn")
|
||||
change_scene_to_packed(scene)
|
||||
|
||||
await _wait()
|
||||
|
||||
_game_root = root.get_child(0)
|
||||
|
||||
var game_state: LevelGameState = _game_root.find_child("LevelGameState")
|
||||
var prestige_manager = _game_root.find_child("PrestigeManager") as PrestigeManager
|
||||
|
||||
if game_state == null:
|
||||
print("[ERROR] LevelGameState not found")
|
||||
_print_summary()
|
||||
_test_complete = true
|
||||
quit(1)
|
||||
return
|
||||
|
||||
if prestige_manager == null:
|
||||
print("[ERROR] PrestigeManager not found")
|
||||
_print_summary()
|
||||
_test_complete = true
|
||||
quit(1)
|
||||
return
|
||||
|
||||
var research_id: StringName = &"gold_research"
|
||||
|
||||
var initial_xp: BigNumber = game_state.get_research_xp(research_id)
|
||||
var initial_level: int = game_state.get_research_level(research_id)
|
||||
print("[TARGET] initial_research_xp: mantissa=%s, exponent=%s" % [initial_xp.mantissa, initial_xp.exponent])
|
||||
print("[TARGET] initial_research_level: %d" % initial_level)
|
||||
|
||||
TestUtils.assert_equals(
|
||||
0,
|
||||
initial_xp.mantissa,
|
||||
"Initial research XP should be 0"
|
||||
)
|
||||
|
||||
TestUtils.assert_equals(
|
||||
0,
|
||||
initial_level,
|
||||
"Initial research level should be 0"
|
||||
)
|
||||
|
||||
game_state.add_currency_by_id(&"gold", BigNumber.from_float(100000.0))
|
||||
game_state.add_currency_by_id(&"gems", BigNumber.from_float(100.0))
|
||||
|
||||
await _wait()
|
||||
|
||||
for i in range(10):
|
||||
game_state.add_research_xp(research_id, BigNumber.from_float(500.0))
|
||||
await _wait(0.05)
|
||||
|
||||
var pre_prestige_xp: BigNumber = game_state.get_research_xp(research_id)
|
||||
var pre_prestige_level: int = game_state.get_research_level(research_id)
|
||||
print("[TARGET] pre_prestige_research_xp: mantissa=%s, exponent=%s" % [pre_prestige_xp.mantissa, pre_prestige_xp.exponent])
|
||||
print("[TARGET] pre_prestige_research_level: %d" % pre_prestige_level)
|
||||
|
||||
TestUtils.assert_true(
|
||||
pre_prestige_xp.mantissa > 0,
|
||||
"Research XP should be > 0 before prestige"
|
||||
)
|
||||
|
||||
TestUtils.assert_true(
|
||||
pre_prestige_level > 0,
|
||||
"Research level should be > 0 before prestige"
|
||||
)
|
||||
|
||||
var can_prestige: bool = prestige_manager.can_prestige()
|
||||
print("[TARGET] can_prestige: %s" % str(can_prestige))
|
||||
|
||||
TestUtils.assert_true(
|
||||
can_prestige,
|
||||
"Should be able to prestige after accumulating currencies"
|
||||
)
|
||||
|
||||
if can_prestige:
|
||||
prestige_manager.perform_prestige()
|
||||
|
||||
await _wait()
|
||||
|
||||
var post_prestige_xp: BigNumber = game_state.get_research_xp(research_id)
|
||||
var post_prestige_level: int = game_state.get_research_level(research_id)
|
||||
print("[TARGET] post_prestige_research_xp: mantissa=%s, exponent=%s" % [post_prestige_xp.mantissa, post_prestige_xp.exponent])
|
||||
print("[TARGET] post_prestige_research_level: %d" % post_prestige_level)
|
||||
|
||||
TestUtils.assert_equals(
|
||||
0,
|
||||
post_prestige_xp.mantissa,
|
||||
"Research XP should be reset to 0 after prestige"
|
||||
)
|
||||
|
||||
TestUtils.assert_equals(
|
||||
0,
|
||||
post_prestige_level,
|
||||
"Research level should be reset to 0 after prestige"
|
||||
)
|
||||
|
||||
_print_summary()
|
||||
_test_complete = true
|
||||
if failed == 0:
|
||||
quit(0)
|
||||
else:
|
||||
quit(1)
|
||||
|
||||
func _wait(seconds: float = 0.1) -> void:
|
||||
await create_timer(seconds).timeout
|
||||
|
||||
func _print_summary():
|
||||
TestUtils.print_result()
|
||||
passed = TestUtils.get_passed()
|
||||
failed = TestUtils.get_failed()
|
||||
|
||||
func get_passed() -> int:
|
||||
return passed
|
||||
|
||||
func get_failed() -> int:
|
||||
return failed
|
||||
1
tests/test_research_prestige_reset.gd.uid
Normal file
1
tests/test_research_prestige_reset.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://j2a4c8w1qrgj
|
||||
146
tests/test_research_xp_acquisition.gd
Normal file
146
tests/test_research_xp_acquisition.gd
Normal file
@@ -0,0 +1,146 @@
|
||||
extends SceneTree
|
||||
|
||||
var passed: int = 0
|
||||
var failed: int = 0
|
||||
var _game_root: Node = null
|
||||
var _test_complete: bool = false
|
||||
|
||||
func _init():
|
||||
print("\n=== TEST: Research XP Acquisition ===\n")
|
||||
TestUtils.set_test_name("research_xp_acquisition")
|
||||
|
||||
# Load tiny_sword scene
|
||||
var scene = load("res://docs/gyms/tiny_sword/tiny_sword.tscn")
|
||||
change_scene_to_packed(scene)
|
||||
|
||||
await _wait()
|
||||
|
||||
_game_root = root.get_child(0)
|
||||
|
||||
var game_state: LevelGameState = _game_root.find_child("LevelGameState")
|
||||
|
||||
if game_state == null:
|
||||
print("[ERROR] LevelGameState not found")
|
||||
_print_summary()
|
||||
_test_complete = true
|
||||
quit(1)
|
||||
return
|
||||
|
||||
# Find GoldMine CurrencyGenerator
|
||||
var gold_mine: CurrencyGenerator = null
|
||||
var world = game_state.get_node_or_null("World")
|
||||
if world:
|
||||
for child in world.get_children():
|
||||
if child.get_name() == "GoldMine":
|
||||
for gc_child in child.get_children():
|
||||
if gc_child is CurrencyGenerator:
|
||||
gold_mine = gc_child
|
||||
break
|
||||
|
||||
if gold_mine == null:
|
||||
print("[ERROR] GoldMine CurrencyGenerator node not found")
|
||||
_print_summary()
|
||||
_test_complete = true
|
||||
quit(1)
|
||||
return
|
||||
|
||||
# Give the generator initial ownership so clicks work properly
|
||||
if gold_mine.owned <= 0:
|
||||
game_state.add_currency_by_id(&"worker", BigNumber.from_float(100.0))
|
||||
await _wait(0.05)
|
||||
gold_mine.buy(1)
|
||||
await _wait(0.05)
|
||||
|
||||
# Get initial gold currency
|
||||
var initial_gold: BigNumber = game_state.get_currency_amount_by_id(&"gold")
|
||||
print("[TARGET] initial_gold: mantissa=%s, exponent=%s" % [initial_gold.mantissa, initial_gold.exponent])
|
||||
|
||||
# Get initial research XP
|
||||
var initial_xp: BigNumber = game_state.get_research_xp(&"gold_research")
|
||||
print("[TARGET] initial_research_xp: mantissa=%s, exponent=%s" % [initial_xp.mantissa, initial_xp.exponent])
|
||||
|
||||
# Set press_buys_generator to false so clicks grant currency instead of buying
|
||||
gold_mine.press_buys_generator = false
|
||||
|
||||
# Simulate clicking the generator multiple times to produce currency
|
||||
var click_count: int = 5
|
||||
for i in range(click_count):
|
||||
gold_mine._on_pressed()
|
||||
await _wait(0.1)
|
||||
|
||||
# Check gold increased
|
||||
var final_gold: BigNumber = game_state.get_currency_amount_by_id(&"gold")
|
||||
print("[TARGET] final_gold: mantissa=%s, exponent=%s" % [final_gold.mantissa, final_gold.exponent])
|
||||
|
||||
# Check owned count
|
||||
var owned: int = gold_mine.owned
|
||||
print("[TARGET] gold_mine_owned: %d" % owned)
|
||||
|
||||
# Validate research XP increased
|
||||
var final_xp: BigNumber = game_state.get_research_xp(&"gold_research")
|
||||
print("[TARGET] final_research_xp: mantissa=%s, exponent=%s" % [final_xp.mantissa, final_xp.exponent])
|
||||
|
||||
TestUtils.assert_true(
|
||||
final_xp.is_greater_than(initial_xp),
|
||||
"Research XP increased after generator clicks"
|
||||
)
|
||||
|
||||
# Test level calculation
|
||||
var level: int = game_state.get_research_level(&"gold_research")
|
||||
print("[TARGET] research_level: %d" % level)
|
||||
|
||||
TestUtils.assert_true(
|
||||
level >= 0,
|
||||
"Research level is non-negative"
|
||||
)
|
||||
|
||||
# Test research multiplier
|
||||
var multiplier: float = game_state.get_research_multiplier(&"gold_research")
|
||||
print("[TARGET] research_multiplier: %f" % multiplier)
|
||||
|
||||
TestUtils.assert_greater_than(
|
||||
multiplier,
|
||||
0.0,
|
||||
"Research multiplier is positive"
|
||||
)
|
||||
|
||||
# Test BigNumber XP progress calculation
|
||||
var research_data: ResearchData = game_state.research_catalogue.get_research_by_id(&"gold_research")
|
||||
if research_data != null:
|
||||
var progress: float = research_data.get_xp_progress(final_xp)
|
||||
print("[TARGET] xp_progress: %f" % progress)
|
||||
|
||||
TestUtils.assert_true(
|
||||
progress >= 0.0 and progress <= 1.0,
|
||||
"XP progress is between 0.0 and 1.0"
|
||||
)
|
||||
|
||||
# Test level calculation with BigNumber
|
||||
var calculated_level: int = research_data.get_level_for_xp(final_xp)
|
||||
print("[TARGET] calculated_level: %d" % calculated_level)
|
||||
|
||||
TestUtils.assert_true(
|
||||
calculated_level >= 0,
|
||||
"Calculated level is non-negative"
|
||||
)
|
||||
|
||||
_print_summary()
|
||||
_test_complete = true
|
||||
if failed == 0:
|
||||
quit(0)
|
||||
else:
|
||||
quit(1)
|
||||
|
||||
func _wait(seconds: float = 0.1) -> void:
|
||||
await create_timer(seconds).timeout
|
||||
|
||||
func _print_summary():
|
||||
TestUtils.print_result()
|
||||
passed = TestUtils.get_passed()
|
||||
failed = TestUtils.get_failed()
|
||||
|
||||
func get_passed() -> int:
|
||||
return passed
|
||||
|
||||
func get_failed() -> int:
|
||||
return failed
|
||||
1
tests/test_research_xp_acquisition.gd.uid
Normal file
1
tests/test_research_xp_acquisition.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://cq65acistj7ar
|
||||
79
tests/test_runner.gd
Normal file
79
tests/test_runner.gd
Normal file
@@ -0,0 +1,79 @@
|
||||
extends SceneTree
|
||||
|
||||
var test_scripts: Array[String] = []
|
||||
var test_index: int = 0
|
||||
var total_passed: int = 0
|
||||
var total_failed: int = 0
|
||||
|
||||
func _init():
|
||||
print("\n=== GODOT TEST RUNNER ===\n")
|
||||
_discover_tests()
|
||||
|
||||
if test_scripts.is_empty():
|
||||
print("[ERROR] No test scripts found in res://tests/")
|
||||
print("[RESULT] FAIL")
|
||||
quit(1)
|
||||
return
|
||||
|
||||
print("Found %d test(s): %s\n" % [test_scripts.size(), str(test_scripts)])
|
||||
|
||||
await _run_next_test()
|
||||
|
||||
print("\n=== TEST SUMMARY ===")
|
||||
print("Total passed: %d" % total_passed)
|
||||
print("Total failed: %d" % total_failed)
|
||||
|
||||
if total_failed == 0:
|
||||
print("[OVERALL_RESULT] PASS")
|
||||
quit(0)
|
||||
else:
|
||||
print("[OVERALL_RESULT] FAIL")
|
||||
quit(1)
|
||||
|
||||
func _discover_tests() -> void:
|
||||
var dir = DirAccess.open("res://tests")
|
||||
if dir:
|
||||
dir.list_dir_begin()
|
||||
var file_name = dir.get_next()
|
||||
while file_name != "":
|
||||
if file_name.ends_with(".gd") and file_name.begins_with("test_") and \
|
||||
file_name != "test_runner.gd" and file_name != "test_utils.gd":
|
||||
test_scripts.append("res://tests/%s" % file_name)
|
||||
file_name = dir.get_next()
|
||||
dir.list_dir_end()
|
||||
test_scripts.sort()
|
||||
|
||||
func _run_next_test() -> void:
|
||||
if test_index >= test_scripts.size():
|
||||
return
|
||||
|
||||
var test_script = test_scripts[test_index]
|
||||
print("--- Running test %d/%d: %s ---" % [test_index + 1, test_scripts.size(), test_script])
|
||||
print("")
|
||||
|
||||
# Load and run test
|
||||
var test_instance = load(test_script).new()
|
||||
get_root().add_child(test_instance)
|
||||
|
||||
# Check if test has run() method (new pattern) or uses _ready() (old pattern)
|
||||
if test_instance.has_method("run"):
|
||||
await test_instance.run()
|
||||
# Clean up test instance
|
||||
test_instance.queue_free()
|
||||
else:
|
||||
# For old pattern, wait a bit for _ready() to complete
|
||||
await create_timer(0.2).timeout
|
||||
# Clean up test instance
|
||||
test_instance.queue_free()
|
||||
|
||||
# Get results if available
|
||||
if test_instance.has_method("get_passed"):
|
||||
total_passed += test_instance.get_passed()
|
||||
total_failed += test_instance.get_failed()
|
||||
|
||||
# Wait for cleanup
|
||||
await create_timer(0.1).timeout
|
||||
|
||||
print("")
|
||||
test_index += 1
|
||||
await _run_next_test()
|
||||
1
tests/test_runner.gd.uid
Normal file
1
tests/test_runner.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://chame3fmeexw6
|
||||
74
tests/test_utils.gd
Normal file
74
tests/test_utils.gd
Normal file
@@ -0,0 +1,74 @@
|
||||
class_name TestUtils
|
||||
extends RefCounted
|
||||
|
||||
static var _test_name: String = ""
|
||||
static var _passed: int = 0
|
||||
static var _failed: int = 0
|
||||
|
||||
static func set_test_name(name: String) -> void:
|
||||
_test_name = name
|
||||
_passed = 0
|
||||
_failed = 0
|
||||
|
||||
static func assert_equals(expected: Variant, actual: Variant, message: String) -> void:
|
||||
if expected == actual:
|
||||
print("[PASS] %s: %s" % [_test_name, message])
|
||||
_passed += 1
|
||||
else:
|
||||
print("[FAIL] %s: %s (expected %s, got %s)" % [
|
||||
_test_name, message, str(expected), str(actual)
|
||||
])
|
||||
_failed += 1
|
||||
|
||||
static func assert_true(condition: bool, message: String) -> void:
|
||||
if condition:
|
||||
print("[PASS] %s: %s" % [_test_name, message])
|
||||
_passed += 1
|
||||
else:
|
||||
print("[FAIL] %s: %s" % [_test_name, message])
|
||||
_failed += 1
|
||||
|
||||
static func assert_false(condition: bool, message: String) -> void:
|
||||
if not condition:
|
||||
print("[PASS] %s: %s" % [_test_name, message])
|
||||
_passed += 1
|
||||
else:
|
||||
print("[FAIL] %s: %s (expected false, got true)" % [_test_name, message])
|
||||
_failed += 1
|
||||
|
||||
static func assert_greater_than(a: float, b: float, message: String) -> void:
|
||||
if a > b:
|
||||
print("[PASS] %s: %s" % [_test_name, message])
|
||||
_passed += 1
|
||||
else:
|
||||
print("[FAIL] %s: %s (expected %s > %s)" % [_test_name, message, str(a), str(b)])
|
||||
_failed += 1
|
||||
|
||||
static func assert_greater_or_equal(a: float, b: float, message: String) -> void:
|
||||
if a >= b:
|
||||
print("[PASS] %s: %s" % [_test_name, message])
|
||||
_passed += 1
|
||||
else:
|
||||
print("[FAIL] %s: %s (expected %s >= %s)" % [_test_name, message, str(a), str(b)])
|
||||
_failed += 1
|
||||
|
||||
static func assert_not_null(value: Variant, message: String) -> void:
|
||||
if value != null:
|
||||
print("[PASS] %s: %s" % [_test_name, message])
|
||||
_passed += 1
|
||||
else:
|
||||
print("[FAIL] %s: %s" % [_test_name, message])
|
||||
_failed += 1
|
||||
|
||||
static func get_passed() -> int:
|
||||
return _passed
|
||||
|
||||
static func get_failed() -> int:
|
||||
return _failed
|
||||
|
||||
static func print_result() -> void:
|
||||
print("[SUMMARY] %s: %d passed, %d failed" % [_test_name, _passed, _failed])
|
||||
if _failed == 0:
|
||||
print("[RESULT] PASS")
|
||||
else:
|
||||
print("[RESULT] FAIL")
|
||||
1
tests/test_utils.gd.uid
Normal file
1
tests/test_utils.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://ous5m4c7jotn
|
||||
Reference in New Issue
Block a user