OverviewFilesCommitsIssuesDocsReleasesPeers

hyper-tps / scenes / test / gate_script_host_res.gd

122 lines · gd ref:
extends SceneTree
## Gate: ScriptHost can load a brain that ships inside the pck.
##
## THE BUG THIS EXISTS FOR. run_file() read every script with
## FileAccess.open(path) + get_as_text() and compiled the source. GDScript
## exports as .gdc BINARY TOKENS, so in an exported build every
## res://bots/*.gd returned error 7 and no shipped brain could run. 0.1141.0
## put bots/*.gd into the pck (export_filter="selected_scenes" had been
## dropping them) and nothing could read them once there. hyper-games #77
## item 3; script_host.gd had flagged it as unverified, and it is verified.
##
## ⛔ WHAT THIS GATE CANNOT DO. The real condition exists only in an EXPORTED
## build -- from the source tree res://bots/walker.gd is an ordinary .gd on
## disk, so the OLD code passes a functional load here too. A gate that only
## called run_file() would be green before and after the fix and would prove
## nothing. That is exactly the shape hyper-games #68 warns about: a gate that
## supplies the condition hiding the bug.
##
## So this checks two different things:
##   1. functionally, that a res:// brain loads and initialises at all
##      (a regression guard, honest about being weaker than the real case)
##   2. structurally, that run_file takes a res:// branch which uses load()
##      rather than FileAccess -- the part that actually differs in the pck
## with a self-check proving the structural test can fail.

var ok := true
var _ran := false

const FIXTURE := "res://scenes/test/fixture_brain.gd"


func _p(c: bool, m: String) -> void:
	print(("ok   " if c else "FAIL: ") + m)
	if not c:
		ok = false


func _process(_delta: float) -> bool:
	if _ran:
		return true
	_ran = true
	_run()
	print("GATE script_host_res: %s" % ("PASS" if ok else "FAIL"))
	if not ok:
		quit(1)
	return true


func _run() -> void:
	# ---- 1. functional: a res:// brain loads and runs _bot_init ----
	var host: Node = root.get_node_or_null("ScriptHost")
	_p(host != null, "ScriptHost autoload present")
	if host == null:
		return

	var inst: Variant = host.run_file(FIXTURE, "gate_fixture")
	_p(inst != null, "run_file loaded %s" % FIXTURE.get_file())
	if inst != null:
		_p(bool(inst.get("inited")), "_bot_init ran on the loaded brain")
		host.unload("gate_fixture")

	# Every brain we actually ship must load the same way.
	for brain: String in ["res://bots/beacon.gd", "res://bots/patrol.gd", "res://bots/walker.gd"]:
		# Loaded as a RESOURCE, not run: beacon writes durable state in _bot_init
		# and this gate must not put keys in a real room.
		var gd: Variant = load(brain)
		_p(gd != null and gd is GDScript, "%s loads as a GDScript" % brain.get_file())

	# ---- 2. structural: run_file must have a res:// branch that uses load() ----
	var src: String = FileAccess.get_file_as_string("res://autoloads/script_host.gd")
	_p(src != "", "script_host.gd readable")
	if src == "":
		return

	var idx: int = src.find("func run_file")
	_p(idx >= 0, "run_file exists")
	if idx < 0:
		return
	var body: String = src.substr(idx)
	var nxt: int = body.find("\nfunc ")
	if nxt > 0:
		body = body.substr(0, nxt)

	_p(body.contains("res://"),
		"run_file distinguishes res:// -- without it a packed brain is read as source")
	_p(body.contains("load("),
		"run_file uses load() for the packed case (.gdc has no readable source text)")

	# ---- 3. BotRegistry's own pre-check: the SAME bug in a second place ----
	# _start_script_brain refuses a brain before ScriptHost is ever called, and it
	# used FileAccess.file_exists(). In the pck res://bots/beacon.gd does not exist
	# under that name -- beacon.gdc does -- so every script brain was refused with
	# "no such script" while run_file was perfectly able to load it. Found by
	# registering the bot matrix against the DEPLOYED build; no gate saw it,
	# including the first version of this one, because from the source tree both
	# checks answer true.
	var br: Node = root.get_node_or_null("BotRegistry")
	_p(br != null, "BotRegistry autoload present")
	if br != null:
		var has_check: bool = br.has_method("_brain_script_exists")
		_p(has_check, "BotRegistry routes its existence check through _brain_script_exists()")
		if has_check:
			for brain: String in ["res://bots/beacon.gd", "res://bots/patrol.gd", "res://bots/walker.gd"]:
				_p(bool(br._brain_script_exists(brain)), "%s is found by the pre-check" % brain.get_file())
			_p(not bool(br._brain_script_exists("res://bots/definitely_not_here.gd")),
				"a missing res:// brain is still reported missing")
			_p(not bool(br._brain_script_exists("user://definitely_not_here.gd")),
				"a missing user:// brain is still reported missing")

		var reg_src: String = FileAccess.get_file_as_string("res://autoloads/bot_registry.gd")
		var si: int = reg_src.find("func _start_script_brain")
		_p(si >= 0, "_start_script_brain exists")
		var sbody: String = reg_src.substr(si, 900) if si >= 0 else ""
		_p(not sbody.contains("FileAccess.file_exists"),
			"_start_script_brain no longer asks FileAccess whether a packed brain exists")

	# SELF-CHECK: the pre-fix shape must be detected as lacking that branch, or
	# the two assertions above could pass on any file that merely mentions load().
	var old_shape: String = 'func run_file(path: String) -> Object:\n\tvar f := FileAccess.open(path, FileAccess.READ)\n\treturn run(f.get_as_text(), path)\n'
	_p(not old_shape.contains("res://"),
		"self-check: the pre-fix run_file is detected as having no res:// branch")