Preserve production code, regression fixtures, audio assets and provenance, test infrastructure, and the original-completion plan and progress ledger. Final game and Windows acceptance remain pending; excluded local artifacts and native experiments are not part of this snapshot.
174 lines
9.2 KiB
GDScript
174 lines
9.2 KiB
GDScript
extends SceneTree
|
|
const FixtureCleanup := preload("res://tests/helpers/ui_audio_fixture_cleanup.gd")
|
|
|
|
## Normal build/product controls exercise drafts, a real paid tick, queued Save,
|
|
## normal load, duplicate prevention and removal-free disabled restart state.
|
|
var checks := 0
|
|
var failures := 0
|
|
var main: Control
|
|
var panel: SupplyManagementPanel
|
|
var runtime: Variant
|
|
var clock: Variant
|
|
var state: Variant
|
|
var warehouse: StringName = &""
|
|
var slot := ""
|
|
|
|
func _initialize() -> void:
|
|
call_deferred("run")
|
|
|
|
func expect(ok: bool, message: String) -> void:
|
|
checks += 1
|
|
if not ok:
|
|
failures += 1
|
|
push_error("AUTO_PURCHASE_UI: " + message)
|
|
|
|
func setup_scene(save_slot: String) -> void:
|
|
runtime = root.get_node("SimulationRuntime")
|
|
clock = root.get_node("GameClock")
|
|
state = root.get_node("GameState")
|
|
clock.set_process(false)
|
|
clock.reset(1, 6, 0)
|
|
state.reset()
|
|
expect(runtime.initialize(clock, state, 420_502)[&"valid"], "current canonical Runtime initializes")
|
|
clock.set_paused(true)
|
|
root.size = Vector2i(1280, 720)
|
|
main = load("res://scenes/main/main.tscn").instantiate() as Control
|
|
main.manual_save_path = save_slot
|
|
root.add_child(main)
|
|
runtime.configure_save_policy({&"enabled": false, &"daily_checkpoint_enabled": false})
|
|
await process_frame
|
|
await process_frame
|
|
panel = main.find_child("SupplyManagementPanel", true, false) as SupplyManagementPanel
|
|
expect(panel != null, "ordinary scene owns product controls")
|
|
if panel != null: main.call("_activate_management_mode", &"product")
|
|
|
|
func build_warehouse() -> void:
|
|
var count: int = runtime.construction_service.get_jobs().size()
|
|
main.call("_select_build_definition", &"warehouse")
|
|
main.call("_on_map_cell_clicked", Vector3i(-8, 0, 1))
|
|
expect(runtime.construction_service.get_jobs().size() == count + 1, "normal build palette/map submits paid construction")
|
|
clock.advance_minutes(180)
|
|
for facility: FacilityInstance in runtime.facility_service.get_instances():
|
|
if facility.definition_id == &"warehouse": warehouse = facility.instance_id
|
|
expect(not warehouse.is_empty(), "completed ordinary warehouse becomes real purchase endpoint")
|
|
main.call("_activate_management_mode", &"product")
|
|
panel.refresh()
|
|
expect(panel.selected_warehouse_id == warehouse and panel.selected_item_id == SupplyCatalog.RAW_FOOD, "ordinary product selects actual warehouse/catalog item")
|
|
expect(not panel.auto_purchase_enabled.button_pressed and int(panel.auto_purchase_target.value) == 0, "new warehouse is explicitly disabled with no funded default")
|
|
|
|
func stock() -> int:
|
|
var facility: FacilityInstance = runtime.facility_service.get_instance(warehouse)
|
|
return runtime.inventory_service.total_quantity(facility.local_inventory_id, SupplyCatalog.RAW_FOOD) if facility != null else 0
|
|
|
|
func save_button() -> void:
|
|
for button: Node in main.find_children("*", "Button", true, false):
|
|
if button.text == "保存":
|
|
expect(not button.disabled, "normal Save button enabled")
|
|
button.pressed.emit()
|
|
expect(bool(runtime.latest_save_operation_result.get(&"ok", false)), "ordinary Save reports success")
|
|
return
|
|
expect(false, "normal Save button exists")
|
|
|
|
func load_menu() -> void:
|
|
var menu := main.find_child("SaveLoadMenu", true, false) as MenuButton
|
|
expect(menu != null and not menu.disabled, "normal load menu enabled")
|
|
if menu != null and not menu.disabled: menu.get_popup().id_pressed.emit(0)
|
|
|
|
func verify_order(target: int = 6) -> void:
|
|
warehouse = panel.selected_warehouse_id
|
|
var row: Dictionary = runtime.auto_purchase_plans.get_plan(warehouse, SupplyCatalog.RAW_FOOD)
|
|
expect(row[&"enabled"] and row[&"target_stock"] == target and row[&"last_result"] == &"PURCHASED", "normal tick retains enabled player goal and paid receipt")
|
|
expect(stock() == target and runtime.command_queue.pending_count() == 0, "actual warehouse has desired paid stock and no synthetic queued order")
|
|
expect(runtime.economy_service.get_transaction_id_for_idempotency_key(SupplyAutoPurchasePlans.order_key(warehouse, SupplyCatalog.RAW_FOOD, row[&"last_attempt_minute"])) == StringName(row[&"last_transaction_id"]), "normal restored receipt resolves real ledger key")
|
|
expect(state.get_resource(&"gold") == runtime.economy_service.get_balance(&"PLAYER_CASH"), "ordinary UI money mirror is ledger-owned")
|
|
expect(panel.auto_purchase_status.text.contains("ON") and not panel.auto_purchase_button.disabled, "ordinary UI reconstructs enabled completed controls")
|
|
|
|
func queue_intent(target: int = 6) -> void:
|
|
var before: Dictionary = runtime.create_snapshot()
|
|
panel.auto_purchase_enabled.button_pressed = true
|
|
panel.auto_purchase_target.value = target
|
|
expect(var_to_bytes(runtime.create_snapshot()) == var_to_bytes(before), "editing toggle/target changes draft only")
|
|
expect(panel.auto_purchase_status.text.contains("未保存"), "UI distinguishes draft from active intent")
|
|
clock.set_paused(false)
|
|
panel.auto_purchase_button.pressed.emit()
|
|
clock.set_paused(true)
|
|
expect(runtime.command_queue.pending_count() == 1 and stock() == 0 and panel.auto_purchase_button.disabled, "normal Apply queues one intent without stock/payment")
|
|
panel.auto_purchase_button.pressed.emit()
|
|
expect(runtime.command_queue.pending_count() == 1, "disabled/repeated normal Apply cannot double queue")
|
|
|
|
func run() -> void:
|
|
var explicit_slot := OS.get_environment("THE_TREE_AUTO_PURCHASE_UI_SLOT")
|
|
slot = explicit_slot if not explicit_slot.is_empty() else "user://tests/supply_auto_ui_%d_%d/manual" % [OS.get_process_id(), Time.get_ticks_usec()]
|
|
if not valid_slot(slot) or DirAccess.dir_exists_absolute(ProjectSettings.globalize_path(slot)):
|
|
push_error("AUTO_PURCHASE_UI needs a fresh user://tests slot; player fallback forbidden")
|
|
quit(90); return
|
|
await setup_scene(slot)
|
|
if panel == null:
|
|
var _audio_cleanup_early_0: Dictionary = await FixtureCleanup.free_owner_and_wait(self, main)
|
|
if not bool(_audio_cleanup_early_0.get(&"passed", false)):
|
|
push_error("Ordinary fixture audio cleanup failed: %s" % str(_audio_cleanup_early_0))
|
|
quit(96)
|
|
return
|
|
quit(failures); return
|
|
expect(panel.auto_purchase_button.disabled, "no warehouse is a safe empty setting state")
|
|
build_warehouse()
|
|
if warehouse.is_empty():
|
|
var _audio_cleanup_early_1: Dictionary = await FixtureCleanup.free_owner_and_wait(self, main)
|
|
if not bool(_audio_cleanup_early_1.get(&"passed", false)):
|
|
push_error("Ordinary fixture audio cleanup failed: %s" % str(_audio_cleanup_early_1))
|
|
quit(96)
|
|
return
|
|
quit(failures); return
|
|
queue_intent()
|
|
save_button()
|
|
expect(SaveService.load_snapshot(slot)[&"ok"], "pending actual command is written to isolated disk")
|
|
load_menu()
|
|
panel.refresh()
|
|
expect(runtime.command_queue.pending_count() == 1 and not runtime.auto_purchase_plans.get_plan(warehouse, SupplyCatalog.RAW_FOOD)[&"enabled"] and panel.auto_purchase_button.disabled, "normal reload reconstructs one pending setting with no premature enabling")
|
|
var cash: int = state.get_resource(&"gold")
|
|
var next_id: int = runtime.create_snapshot()[&"systems"][&"runtime"][&"next_command_id"]
|
|
clock.advance_minutes(1)
|
|
panel.refresh()
|
|
verify_order()
|
|
expect(state.get_resource(&"gold") == cash - 18 and runtime.create_snapshot()[&"systems"][&"runtime"][&"next_command_id"] == next_id, "loaded intent charges18G once without allocating auto commandID")
|
|
var paid: Dictionary = runtime.create_snapshot()
|
|
panel.refresh()
|
|
panel.select_tab(&"delivery")
|
|
panel.select_tab(&"purchase")
|
|
expect(var_to_bytes(runtime.create_snapshot()) == var_to_bytes(paid), "product navigation/refresh mutate no gameplay")
|
|
panel.auto_purchase_enabled.button_pressed = false
|
|
panel.auto_purchase_button.pressed.emit()
|
|
expect(not runtime.auto_purchase_plans.get_plan(warehouse, SupplyCatalog.RAW_FOOD)[&"enabled"], "normal controls disable auto ordering")
|
|
save_button()
|
|
load_menu()
|
|
panel.refresh()
|
|
expect(not panel.auto_purchase_enabled.button_pressed and stock() == 6, "normal load preserves disabled intent and paid stock")
|
|
await capture("normal-auto-purchase")
|
|
SaveService.delete_save(slot)
|
|
var _audio_cleanup_final: Dictionary = await FixtureCleanup.free_owner_and_wait(self, main)
|
|
if not bool(_audio_cleanup_final.get(&"passed", false)):
|
|
push_error("Ordinary fixture audio cleanup failed: %s" % str(_audio_cleanup_final))
|
|
quit(96)
|
|
return
|
|
await process_frame
|
|
print("SUPPLY_AUTO_PURCHASE_UI checks=%d failures=%d" % [checks, failures])
|
|
quit(failures)
|
|
|
|
func capture(label: String) -> void:
|
|
var folder := OS.get_environment("THE_TREE_AUTO_PURCHASE_UI_CAPTURE_DIR")
|
|
if folder.is_empty(): return
|
|
expect(folder.is_absolute_path() and DirAccess.dir_exists_absolute(folder) and DisplayServer.get_name() != "headless", "explicit UI capture uses an existing QA output and real renderer")
|
|
if not folder.is_absolute_path() or DisplayServer.get_name() == "headless": return
|
|
var path := folder.path_join(label + ".png")
|
|
expect(not FileAccess.file_exists(path), "UI capture is fresh")
|
|
if FileAccess.file_exists(path): return
|
|
await process_frame
|
|
await RenderingServer.frame_post_draw
|
|
expect(root.get_texture().get_image().save_png(path) == OK, "actual product image saved for ROOT visual review")
|
|
|
|
func valid_slot(value: String) -> bool:
|
|
if not value.begins_with("user://tests/") or value.contains("\\"): return false
|
|
for segment: String in value.trim_prefix("user://tests/").split("/", true):
|
|
if segment in ["", ".", ".."]: return false
|
|
return value.ends_with("/manual")
|