Files

106 lines
5.2 KiB
Python

"""CPU-only schema-export and Player validation contracts for optional Lua."""
import argparse
import json
from pathlib import Path
import subprocess
import tempfile
parser = argparse.ArgumentParser()
parser.add_argument("--exporter", required=True)
parser.add_argument("--player")
parser.add_argument("--disabled", action="store_true")
args = parser.parse_args()
def run(executable, *arguments, success=True):
result = subprocess.run([executable, *map(str, arguments)], capture_output=True,
text=True, encoding="utf-8", timeout=20)
assert (result.returncode == 0) == success, (result.stdout, result.stderr)
return result
def behavior(type_id="test.lua.cli", callback='error("LUA_CALLBACK_MUST_NOT_RUN")'):
return f'''local behavior = faset.behavior {{
id = "{type_id}", version = 1, name = "CLI behavior",
fields = {{ speed = {{ type = "number", default = 3, min = 0 }} }}
}}
function behavior:on_start() {callback} end
return behavior
'''
with tempfile.TemporaryDirectory(prefix="faset-lua-cli-") as temporary:
root = Path(temporary) / "Lua Café 世界"
scripts = root / "Scripts"
scripts.mkdir(parents=True)
manifest = root / "project.faset.json"
manifest.write_text(json.dumps({"format": "faset.project", "version": 1}), encoding="utf-8")
native = json.loads(run(args.exporter).stdout)
assert json.loads(run(args.exporter, "--project", root).stdout) == native
manifest.write_text(json.dumps({"format": "faset.project", "version": 1,
"scripting": {"lua": {"scripts": ["Scripts/cli.lua"]}}}),
encoding="utf-8")
source = scripts / "cli.lua"
source.write_text(behavior(), encoding="utf-8")
if args.disabled:
result = run(args.exporter, "--project", root, success=False)
assert "without Lua support" in result.stderr, result.stderr
if args.player:
run(args.player, "--help")
print("Lua-disabled schema exporter accepts C++ projects and rejects Lua clearly")
else:
exported = root / "schema.json"
run(args.exporter, "--project", root, "--output", exported)
merged = json.loads(exported.read_text(encoding="utf-8"))
assert merged["format"] == "faset.schema" and merged["version"] == 1
by_id = {item["id"]: item for item in merged["types"]}
assert len(by_id) == len(native["types"]) + 1
assert by_id["test.lua.cli"]["fields"]["speed"]["default"] == 3
for item in native["types"]:
assert by_id[item["id"]] == item
# Lua lifecycle errors do not execute in the schema exporter or --validate.
scene = root / "scene.scene.json"
scene.write_text(json.dumps({
"format": "faset.scene", "version": 1, "dimension": 2,
"id": "lua-cli-scene", "name": "Lua CLI", "instances": [],
"entities": [{"id": "lua-object", "name": "Lua", "parent": None,
"components": [{"id": "lua-behavior", "type": "test.lua.cli",
"version": 1, "fields": {"speed": 3}}]}]
}), encoding="utf-8")
if args.player:
for project_arguments in [("--project", root), ()]:
result = run(args.player, "--scene", scene, "--validate", *project_arguments)
assert json.loads(result.stdout)["validated"]
assert "LUA_CALLBACK_MUST_NOT_RUN" not in result.stderr
valid_scene = scene.read_text(encoding="utf-8")
invalid_scene = json.loads(valid_scene)
for invalid_speed in ["fast", -1]:
invalid_scene["entities"][0]["components"][0]["fields"]["speed"] = invalid_speed
scene.write_text(json.dumps(invalid_scene), encoding="utf-8")
result = run(args.player, "--scene", scene, "--validate", "--project", root,
success=False)
assert "speed" in result.stderr, result.stderr
assert "LUA_CALLBACK_MUST_NOT_RUN" not in result.stderr, result.stderr
scene.write_text(valid_scene, encoding="utf-8")
run(args.player, "--scene", scene, "--validate", "--watch-lua", success=False)
# A failed schema candidate never overwrites a previously published file.
previous = exported.read_bytes()
source.write_text("this is invalid Lua!", encoding="utf-8")
result = run(args.exporter, "--project", root, "--output", exported, success=False)
assert "cli.lua" in result.stderr, result.stderr
assert exported.read_bytes() == previous
source.write_text(behavior("faset.transform"), encoding="utf-8")
run(args.exporter, "--project", root, success=False)
if native["types"]:
source.write_text(behavior(native["types"][0]["id"]), encoding="utf-8")
run(args.exporter, "--project", root, success=False)
source.write_text("while true do end", encoding="utf-8")
run(args.exporter, "--project", root, success=False)
source.write_text(behavior(), encoding="utf-8")
assert json.loads(run(args.exporter, "--project", root).stdout) == merged
print("Lua schema merge, lifecycle-free validation, failure isolation and budgets passed")