Files
aetherbound-guild/runtime/tools/m63_entry_visual_probe.gd
T

175 lines
6.5 KiB
GDScript

extends SceneTree
## M63 entry-page visual probe (W5/DEV-1R). It captures the migrated entry
## states at the shipped fixtures with the runtime content, the chrome-only
## static view and the content-only dynamic view on a neutral field, and it
## runs the shared ink checker on every build. Boot is strict from M63-A; the
## later entry pages join this probe as they migrate.
const EntryShell = preload("res://entry/entry_shell.gd")
const SettingsStore = preload("res://entry/settings_store.gd")
const InkCheck = preload("res://tools/ui_ink_check.gd")
const SIZES := [Vector2i(760, 360), Vector2i(844, 390), Vector2i(1280, 720)]
const LOCALES := ["zh_CN", "en"]
const STATES := ["running", "error"]
var output_root := ""
var ink_scanned := 0
var ink_violations: Array = []
func _initialize() -> void:
call_deferred("_run")
func _run() -> void:
output_root = OS.get_environment("ABG_EVIDENCE_DIR")
if output_root.is_empty():
push_error("M63 entry probe requires the owned test-attempt runner")
quit(1)
return
DirAccess.make_dir_recursive_absolute(output_root)
var self_test := InkCheck.self_test(root)
if self_test.size() < 3:
push_error("M63 ink self-test expected at least 3 violations, got %d" % self_test.size())
quit(1)
return
print("ABG_M63_INK_SELF_TEST_OK violations=%d" % self_test.size())
for size in SIZES:
for locale in LOCALES:
for state in STATES:
await _capture_boot(size, locale, state)
await _capture_setup(size, locale)
var report_path := output_root.path_join("ink-violations-m63-entry.json")
var file := FileAccess.open(report_path, FileAccess.WRITE)
if file != null:
file.store_string(JSON.stringify(ink_violations, " "))
file.close()
if ink_violations.is_empty():
print("ABG_M63_ENTRY_INK_OK scanned=%d violations=0" % ink_scanned)
else:
for violation in ink_violations:
push_error("M63 ink violation %s %s: %s" % [violation.node, violation.kind, violation.detail])
print("ABG_M63_ENTRY_INK_FAIL scanned=%d violations=%d" % [ink_scanned, ink_violations.size()])
quit(1)
return
print("ABG_M63_ENTRY_VISUAL_PROBE_OK sizes=3 locales=2 pages=boot,setup states=running,error views=static,dynamic,final")
quit(0)
func _capture_boot(size: Vector2i, locale: String, state: String) -> void:
var viewport := SubViewport.new()
viewport.size = size
viewport.render_target_update_mode = SubViewport.UPDATE_ALWAYS
root.add_child(viewport)
var shell := EntryShell.new()
shell.auto_start = false
viewport.add_child(shell)
await _frames(3)
shell.settings = SettingsStore.new().defaults()
shell.settings.locale = locale
shell.settings.text_scale = 1.0
shell.boot_snapshot = (
{"state": "error", "completed": 2, "total": 4, "step": "preferences", "code": "SETTINGS_FILE_INVALID"}
if state == "error"
else {"state": "running", "completed": 2, "total": 4, "step": "preferences", "code": ""}
)
shell._render_boot_state()
await _frames(4)
_scan_ink(shell, locale, "%dx%d-%s" % [size.x, size.y, state])
await _save(viewport, "boot-%dx%d-%s-%s" % [size.x, size.y, locale, state], "final")
# static: chrome only, runtime copy hidden.
for node_name in ["BootIdentity", "BootStatus", "BootDetail", "BootProgressValue", "BootDiagnostics"]:
var control := shell.find_child(node_name, true, false) as CanvasItem
if control != null:
control.visible = false
await _frames(2)
await _save(viewport, "boot-%dx%d-%s-%s" % [size.x, size.y, locale, state], "static")
for node_name in ["BootIdentity", "BootStatus", "BootDetail", "BootProgressValue", "BootDiagnostics"]:
var control := shell.find_child(node_name, true, false) as CanvasItem
if control != null:
control.visible = true
# dynamic: content only on a neutral field (world hidden).
var world := shell.find_child("BootWorld", true, false) as CanvasItem
var shade := shell.find_child("BootWorldShade", true, false) as CanvasItem
if world != null:
world.visible = false
if shade != null:
shade.visible = false
var neutral := ColorRect.new()
neutral.name = "M63NeutralField"
neutral.color = Color("#5b6165")
neutral.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
neutral.mouse_filter = Control.MOUSE_FILTER_IGNORE
shell.add_child(neutral)
shell.move_child(neutral, 0)
await _frames(2)
await _save(viewport, "boot-%dx%d-%s-%s" % [size.x, size.y, locale, state], "dynamic")
viewport.queue_free()
await process_frame
func _capture_setup(size: Vector2i, locale: String) -> void:
var viewport := SubViewport.new()
viewport.size = size
viewport.render_target_update_mode = SubViewport.UPDATE_ALWAYS
root.add_child(viewport)
var shell := EntryShell.new()
shell.auto_start = false
viewport.add_child(shell)
await _frames(3)
shell.settings = SettingsStore.new().defaults()
shell.settings.locale = locale
shell.settings.text_scale = 1.0
shell.size = Vector2(size)
shell.show_first_launch()
await _frames(5)
# The shell lays out on resize; drive it once for the offscreen capture.
shell._layout_first_launch()
shell._refresh_first_launch_labels()
await _frames(3)
_scan_ink(shell, locale, "setup-%dx%d" % [size.x, size.y])
await _save(viewport, "setup-%dx%d-%s" % [size.x, size.y, locale], "final")
for node_name in ["FirstLaunchHeading", "FirstLaunchSubheading", "FirstLaunchPreviewHeading", "SetupPreviewTitle", "SetupPreviewDetail"]:
var control := shell.find_child(node_name, true, false) as CanvasItem
if control != null:
control.visible = false
for row_name in ["SetupLanguage", "SetupSafeArea", "SetupTextScale", "SetupReducedMotion", "SetupReducedFlashes", "SetupCaptions"]:
var row := shell.find_child(row_name, true, false) as Control
if row != null:
var label := row.get_node_or_null("RowLabel") as CanvasItem
var value := row.get_node_or_null("ValuePlate") as CanvasItem
if label != null:
label.visible = false
if value != null:
value.visible = false
await _frames(2)
await _save(viewport, "setup-%dx%d-%s" % [size.x, size.y, locale], "static")
viewport.queue_free()
await process_frame
func _scan_ink(view: Control, locale: String, label: String) -> void:
var violations := InkCheck.scan(view)
ink_scanned += 1
for violation in violations:
violation["locale"] = locale
violation["fixture"] = label
ink_violations.append(violation)
func _save(viewport: SubViewport, stem: String, mode: String) -> void:
await RenderingServer.frame_post_draw
var texture := viewport.get_texture()
if texture == null:
return
var image := texture.get_image()
if image != null and not image.is_empty():
image.save_png(output_root.path_join("%s-%s.png" % [stem, mode]))
func _frames(count: int) -> void:
for _index in count:
await process_frame