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.
99 lines
8.3 KiB
GDScript
99 lines
8.3 KiB
GDScript
extends SceneTree
|
|
const FixtureCleanup := preload("res://tests/helpers/ui_audio_fixture_cleanup.gd")
|
|
const Demo := preload("res://scripts/ui/pixel_tower_demo.gd")
|
|
var checks := 0
|
|
var failures := 0
|
|
func expect(ok: bool, label: String) -> void:
|
|
checks += 1
|
|
if not ok: failures += 1; push_error(label)
|
|
func _initialize() -> void: call_deferred("run")
|
|
func run() -> void:
|
|
root.gui_embed_subwindows = true
|
|
root.size = Vector2i(1280,720)
|
|
var runtime: Variant = root.get_node("SimulationRuntime")
|
|
var clock: Variant = root.get_node("GameClock")
|
|
var state: Variant = root.get_node("GameState")
|
|
# Disposable Demo fixture funds exact Doran signing_cost 300; DEFAULT5000 and paid hire stay unchanged.
|
|
clock.reset(1,6,0);state.reset();state.resources[&"gold"] += 300;runtime.initialize(clock,state,424242);clock.set_paused(true)
|
|
runtime.configure_save_policy({&"enabled":false,&"daily_checkpoint_enabled":false})
|
|
var demo := Demo.new()
|
|
expect(await demo.prepare(runtime,clock,self),"prepare normal construction and autonomous visitors")
|
|
var guest: GuestRuntime
|
|
var record: Dictionary
|
|
for minute in 120:
|
|
for candidate: GuestRuntime in runtime.guest_service.get_guests():
|
|
var found: Dictionary = runtime.get_guest_decision_record(candidate.guest_id)
|
|
if found.get(&"action", &"") == &"MOVE_TO_FACILITY" and candidate.target_facility_id == StringName(found.get(&"facility_id", &"")):
|
|
guest=candidate;record=found;break
|
|
if guest != null: break
|
|
clock.advance_one_minute()
|
|
expect(guest != null,"autonomous decision produces matching recorded destination")
|
|
if guest == null: quit(failures);return
|
|
expect(record.has(&"needs") and record.has(&"travel_minutes") and record.has(&"estimated_total_gold"),"decision evidence includes actual scoring inputs")
|
|
var id := guest.guest_id
|
|
var original := record.duplicate(true)
|
|
record[&"needs"].clear()
|
|
expect(runtime.get_guest_decision_record(id)==original,"returned log does not expose saved event data")
|
|
var saved: Dictionary = runtime.create_snapshot()
|
|
expect(runtime.restore_snapshot(saved),"Runtime restores decision event with moving visitors")
|
|
expect(runtime.get_guest_decision_record(id)==original,"saved decision inputs survive restoration exactly")
|
|
var main: Control = load("res://scenes/main/main.tscn").instantiate();root.add_child(main)
|
|
await process_frame
|
|
runtime.configure_save_policy({&"enabled":false,&"daily_checkpoint_enabled":false})
|
|
main.guest_count_label.pressed.emit();await process_frame
|
|
var panel: GuestObservationPanel = main.guest_observer.find_child("GuestObservationPanel", true, false)
|
|
panel.selected_guest_id=id;panel.refresh()
|
|
expect(panel.decision_label.text.contains("判断時の最大欲求") and panel.decision_label.text.contains("見込料金"),"normal HUD observer exposes actual decision history")
|
|
expect(main.guest_observer.size.y <= root.size.y and main.guest_observer.get_ok_button().is_visible_in_tree(), "observer fits viewport with visible close action")
|
|
var displayed := panel.decision_label.text
|
|
var restored_guest: GuestRuntime = runtime.guest_service.get_guest(id)
|
|
restored_guest.needs[&"hunger"]=0;restored_guest.needs[&"sleep"]=100
|
|
panel.refresh()
|
|
expect(panel.decision_label.text==displayed,"current needs cannot rewrite past reasoning")
|
|
expect(panel._decision_text({}).contains("記録なし"),"legacy or expired history is explicitly unavailable")
|
|
var before: Dictionary=runtime.create_snapshot()
|
|
panel.refresh();panel.refresh()
|
|
expect(runtime.create_snapshot()==before,"opening and refreshing history is read-only")
|
|
if OS.get_environment("THE_TREE_DECISION_CAPTURE")!="":
|
|
await process_frame;await RenderingServer.frame_post_draw
|
|
expect(root.get_texture().get_image().save_png(OS.get_environment("THE_TREE_DECISION_CAPTURE"))==OK,"capture rendered decision UI")
|
|
expect(not runtime.submit_command(&"decide_guest", {&"guest_id": &"missing"}).is_success(), "failed decision is rejected")
|
|
expect(runtime.get_guest_decision_record(&"missing").is_empty(), "failure cannot fabricate a decision record")
|
|
expect(runtime.submit_command(&"spawn_guest", {&"guest_id": &"decision_leaver", &"guest_type_id": &"traveler", &"gold": 100, &"options": {&"state": GuestRuntime.State.DECIDING, &"planned_exit_at": clock.absolute_minute + 100}}).is_success(), "spawn controlled boundary visitor")
|
|
expect(runtime.submit_command(&"decide_guest", {&"guest_id": &"decision_leaver", &"context": {&"exit_threshold": 2147483647}}).is_success(), "no candidate exceeds departure threshold")
|
|
var leaving: Dictionary = runtime.get_guest_decision_record(&"decision_leaver")
|
|
expect(leaving.get(&"action", &"") == &"LEAVE" and leaving.get(&"facility_id", &"") == &"", "departure is recorded without a fictitious destination")
|
|
expect(panel._decision_text(leaving).contains("退館を選びました"), "departure reason is visible")
|
|
expect(runtime.submit_command(&"spawn_guest", {&"guest_id": &"decision_price", &"guest_type_id": &"traveler", &"gold": 100, &"options": {&"state": GuestRuntime.State.DECIDING, &"planned_exit_at": clock.absolute_minute + 100}}).is_success(), "spawn price alias fixture")
|
|
expect(runtime.submit_command(&"decide_guest", {&"guest_id": &"decision_price", &"candidates": [{&"facility_id": original[&"facility_id"], &"price_gold": 37, &"expected_quality": 100, &"respect_survival_reserve": false, &"need_relief": {&"sleep": 80}, &"travel_minutes": 5}], &"context": {&"exit_threshold": -10000}}).is_success(), "decision accepts supported price alias")
|
|
expect(runtime.get_guest_decision_record(&"decision_price").get(&"estimated_total_gold", -1) == 37, "history preserves effective quoted price rather than default zero")
|
|
expect(runtime.submit_command(&"spawn_guest", {&"guest_id": &"decision_rejected", &"guest_type_id": &"traveler", &"gold": 100, &"options": {&"state": GuestRuntime.State.DECIDING, &"planned_exit_at": clock.absolute_minute + 100}}).is_success(), "spawn rejection fixture")
|
|
var rejected_candidates: Array = []
|
|
for index in 9: rejected_candidates.append({&"facility_id": StringName("closed_%02d" % index), &"is_open": false})
|
|
rejected_candidates.append({&"facility_id": &"no_route", &"reachable": false})
|
|
rejected_candidates.append({&"facility_id": &"too_expensive", &"price_gold": 10000})
|
|
rejected_candidates.append({&"facility_id": &"missing_food", &"inputs_available": false})
|
|
expect(runtime.submit_command(&"decide_guest", {&"guest_id": &"decision_rejected", &"candidates": rejected_candidates}).is_success(), "actual filtering rejects twelve candidates")
|
|
var rejected: Dictionary = runtime.get_guest_decision_record(&"decision_rejected")
|
|
expect(rejected.get(&"rejection_counts", {}) == {"FACILITY_CLOSED": 9, "UNREACHABLE": 1, "CANNOT_PAY": 1, "MISSING_INPUTS": 1}, "reason counts match actual closed unreachable and unaffordable candidates")
|
|
expect(rejected.get(&"rejection_count", 0) == 12 and rejected.get(&"rejection_examples", []).size() == 8, "bounded examples retain full total")
|
|
panel.selected_guest_id = &"decision_rejected";panel.refresh()
|
|
expect(panel.decision_label.text.contains("閉店・稼働停止 9施設") and panel.decision_label.text.contains("経路なし 1施設") and panel.decision_label.text.contains("ほか4施設"), "normal observer renders translated actual causes and explicit truncation")
|
|
var event_saved: Dictionary = runtime.domain_events.create_snapshot()
|
|
expect(runtime.domain_events.restore_snapshot(event_saved), "saved event stream with rejection evidence restores")
|
|
expect(runtime.get_guest_decision_record(&"decision_rejected") == rejected, "all recorded reasons survive event restoration")
|
|
expect(panel._rejection_text({}).contains("この記録にはありません"), "old decision records do not fabricate zero rejections")
|
|
if OS.get_environment("THE_TREE_REJECTION_CAPTURE") != "":
|
|
var scroll: ScrollContainer = main.guest_observer.get_node("GuestObservationScroll")
|
|
await process_frame;await process_frame
|
|
scroll.scroll_vertical = 250
|
|
await process_frame;await RenderingServer.frame_post_draw
|
|
expect(root.get_texture().get_image().save_png(OS.get_environment("THE_TREE_REJECTION_CAPTURE")) == OK, "capture scrolled rejection details")
|
|
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("Guest decision log: %d checks, %d failures" % [checks,failures]);quit(failures)
|