One directory with sixty scripts stopped being navigable, and the grouping is also the sandbox boundary:
Tools/unreal/ commandlets and the two map builders, with ue_common.py and ue_placement.py as the
shared imports (they need the engine)
Tools/blender/ builders, exporters, the FBX preset, the MPFB character pipeline, blender_screenshots
(they need the Blender Flatpak)
Tools/review/ the visual loop: render_screenshots.sh, judge_screenshot.py, hero_compare.py,
build_comparison_md.py, e2e_asset_check.py, hero_iterate.sh, visual_loop.py,
visual_targets.json
Tools/gen/ image and texture generation: the ComfyUI driver, deterministic texture writers,
reference fetching
Tools/input/ OS-level input injection
root _common.py, check-lfs.sh, addon_compat_probe.py, sibling_asset_audit.py, tests/
Every moved script now resolves the repository root as ../.. and puts the tools root plus its siblings on
sys.path, so 'import ue_common' still works from unreal/. The test suite bootstrap does the same for the five
folders. render_screenshots.sh takes the folder as a third argument to run_cmdlet, and the e2e driver,
hero_iterate.sh and the comparison generator were repointed.
51 README rows now link to their new location, the README gains a Layout section naming each folder's
sandbox, and the architecture notes and Blender README follow the moves.
Verified: 108 tests pass, prettier is clean, judge_screenshot.py and hero_compare.py run from their new
homes, and the e2e run was aborted mid-way (it had been rebuilding an empty DemoMap for 15 minutes) and is
being rerun against the new layout.
96 lines
3.9 KiB
Python
96 lines
3.9 KiB
Python
"""Tests for Tools/ue_placement.py — the single source of truth for placement.
|
|
|
|
A regression test for a real bug: `ue_hero_facade_import.py` carried its own hero
|
|
position (the pre-0.1.0 one) while `ue_build_demo_map.py` staged the verified one.
|
|
Running the importer moved the hero 13 m east and onto the north side of the
|
|
boulevard, and nothing complained because a displaced building still renders.
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
|
|
_TOOLS = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
sys.path.insert(0, _TOOLS)
|
|
for _sub in ("review", "gen", "unreal", "blender", "input"):
|
|
sys.path.insert(0, os.path.join(_TOOLS, _sub))
|
|
|
|
|
|
import os
|
|
import sys
|
|
import unittest
|
|
|
|
ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", ".."))
|
|
sys.path.insert(0, os.path.join(ROOT, "Tools"))
|
|
|
|
import ue_placement # noqa: E402
|
|
|
|
|
|
class HeroPlacementTest(unittest.TestCase):
|
|
def test_hero_actor_name(self):
|
|
self.assertEqual(ue_placement.HERO_ACTOR, "Hero_Facade")
|
|
|
|
def test_hero_location_is_the_verified_building_line(self):
|
|
x, y, z = ue_placement.HERO_LOCATION_CM
|
|
self.assertEqual((x, y, z), (-13336.0, 1307.0, 0.0))
|
|
# North is -Y in Unreal, so the hero on the south side must have Y > 0.
|
|
self.assertGreater(y, 0.0, "hero must be on the south side (Y > 0)")
|
|
|
|
def test_hero_span_is_verified(self):
|
|
# The mesh spans -1308..+1308 cm in X about its origin.
|
|
lo = ue_placement.HERO_LOCATION_CM[0] - 1308.0
|
|
hi = ue_placement.HERO_LOCATION_CM[0] + 1308.0
|
|
exp_min, exp_max, ok = ue_placement.span_error("hero", lo, hi)
|
|
self.assertTrue(ok)
|
|
# The mesh spans exactly the verified footprint, so the two agree.
|
|
self.assertAlmostEqual(exp_min, lo, delta=ue_placement.SPAN_TOLERANCE_CM)
|
|
self.assertAlmostEqual(exp_max, hi, delta=ue_placement.SPAN_TOLERANCE_CM)
|
|
|
|
|
|
class SpanValidatorTest(unittest.TestCase):
|
|
def test_misplaced_hero_is_reported(self):
|
|
# The exact failure observed: the stale import position put the hero here.
|
|
lo, hi = -13332.0, -10716.0
|
|
exp_min, exp_max, ok = ue_placement.span_error("hero", lo, hi)
|
|
self.assertFalse(ok)
|
|
message = ue_placement.describe_span_failure("hero", lo, hi)
|
|
self.assertIn("does not match the verified", message)
|
|
self.assertIn(f"{exp_min:.0f}", message)
|
|
|
|
def test_correct_span_reports_nothing(self):
|
|
self.assertEqual(ue_placement.describe_span_failure("hero", -14644, -12028), "")
|
|
|
|
def test_within_tolerance_passes(self):
|
|
# 99 cm off is inside the ±100 cm tolerance.
|
|
self.assertTrue(ue_placement.span_error("6922", -24276 + 99, -17700 - 99)[2])
|
|
|
|
def test_outside_tolerance_fails(self):
|
|
self.assertFalse(ue_placement.span_error("6922", -24276 + 101, -17700)[2])
|
|
|
|
def test_unknown_building_raises(self):
|
|
with self.assertRaises(KeyError):
|
|
ue_placement.span_error("no-such-building", 0, 0)
|
|
|
|
def test_every_frontage_span_is_ordered(self):
|
|
for name, (lo, hi) in ue_placement.FRONTAGE_SPANS_CM.items():
|
|
self.assertLess(lo, hi, f"{name} span is inverted")
|
|
|
|
|
|
class SingleSourceOfTruthTest(unittest.TestCase):
|
|
"""The import script and the demo builder must not carry their own literals."""
|
|
|
|
def test_import_script_uses_the_shared_constant(self):
|
|
src = open(os.path.join(ROOT, "Tools", "unreal", "ue_hero_facade_import.py"),
|
|
encoding="utf-8").read()
|
|
self.assertIn("ue_placement.HERO_LOCATION_CM", src)
|
|
self.assertNotIn("(-12024.5, -1450.0, 0.0)", src,
|
|
"the stale pre-0.1.0 hero position must not come back")
|
|
|
|
def test_demo_builder_uses_the_shared_constant(self):
|
|
src = open(os.path.join(ROOT, "Tools", "unreal", "ue_build_demo_map.py"),
|
|
encoding="utf-8").read()
|
|
self.assertIn("ue_placement.HERO_LOCATION_CM", src)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|