The e2e asset check now completes: PASS on all six steps - empty maps 9 s, demo build 10 s, review build 9 s, captures 80 s, judge 388 s, review file. Four breakages from the folder move had to be fixed first: - ue_placement and ue_common are siblings of the scripts that import them, but the rewrite put only the tools root on sys.path, so every Unreal commandlet died with ModuleNotFoundError. Both paths are inserted now. - render_screenshots.sh resolved the repository root one level up, so commandlet paths became Tools/Tools/... and the capture step failed in 0 s. - the driver treated a result file left by an earlier run as success, which is how a ten-second 'demo build' passed without building. Result files are unlinked before each step and child output streams to a per-step log file rather than a pipe. - two editors on one project hang instead of failing, so the driver refuses to start while one is running. Also corrects ue_recreate_maps.py: LevelEditorSubsystem.new_level() refuses a path whose package is still in memory and delete_asset() leaves the file on disk, measured create->delete->create returning False. The step now loads each map, destroys every actor, saves, and reports the count it left (0), which is the property the builders actually need. Open question recorded in TODO.md: the rebuilt map holds 409 actors and 613 KB against 600 and 886 KB before, and the ~190 missing ones look like the facade-kit and theatre-trim placements that run as separate steps.
94 lines
3.3 KiB
Python
94 lines
3.3 KiB
Python
"""Tests for the pure parts of Tools/character_match.py."""
|
|
|
|
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
|
|
|
|
from PIL import Image
|
|
|
|
_HERE = os.path.dirname(os.path.abspath(__file__))
|
|
sys.path.insert(0, os.path.dirname(_HERE)) # tools root, for _common
|
|
sys.path.insert(0, _HERE) # siblings in this folder (ue_common, ue_placement)
|
|
import character_match as cm # noqa: E402
|
|
|
|
|
|
class ColourTests(unittest.TestCase):
|
|
def test_white_is_light_and_neutral(self):
|
|
light, a, b = cm.rgb_to_lab((255, 255, 255))
|
|
self.assertAlmostEqual(light, 100.0, delta=0.2)
|
|
self.assertAlmostEqual(a, 0.0, delta=0.2)
|
|
self.assertAlmostEqual(b, 0.0, delta=0.2)
|
|
|
|
def test_black_is_dark(self):
|
|
self.assertAlmostEqual(cm.rgb_to_lab((0, 0, 0))[0], 0.0, delta=0.1)
|
|
|
|
def test_identical_colours_have_no_distance(self):
|
|
self.assertAlmostEqual(cm.delta_e76(cm.rgb_to_lab((120, 90, 70)),
|
|
cm.rgb_to_lab((120, 90, 70))), 0.0, delta=1e-9)
|
|
|
|
def test_red_green_are_far_apart(self):
|
|
distance = cm.delta_e76(cm.rgb_to_lab((200, 40, 40)), cm.rgb_to_lab((40, 160, 40)))
|
|
self.assertGreater(distance, 40.0)
|
|
|
|
|
|
class MaskTests(unittest.TestCase):
|
|
def _image(self, figure_colour, background_colour):
|
|
image = Image.new("RGB", (60, 100), background_colour)
|
|
pixels = image.load()
|
|
for y in range(20, 90):
|
|
for x in range(25, 35):
|
|
pixels[x, y] = figure_colour
|
|
return image
|
|
|
|
def test_saturated_figure_on_grey_is_found(self):
|
|
mask = cm._subject_mask(self._image((150, 90, 60), (190, 190, 190)))
|
|
box = cm.bbox(mask)
|
|
self.assertIsNotNone(box)
|
|
self.assertLess(box[0], 30)
|
|
self.assertGreater(box[2], 30)
|
|
|
|
def test_grey_frame_yields_empty_mask(self):
|
|
mask = cm._subject_mask(self._image((190, 190, 190), (190, 190, 190)))
|
|
self.assertIsNone(cm.bbox(mask))
|
|
|
|
def test_stats_report_figure_proportions(self):
|
|
mask = Image.new("L", (40, 80), 0)
|
|
pixels = mask.load()
|
|
for y in range(10, 70):
|
|
for x in range(15, 25):
|
|
pixels[x, y] = 255
|
|
stats = cm.mask_stats(mask)
|
|
self.assertEqual(stats["area"], 600)
|
|
self.assertAlmostEqual(stats["aspect"], 6.0, delta=0.01)
|
|
self.assertAlmostEqual(stats["fill"], 1.0, delta=0.01)
|
|
|
|
|
|
class PaletteTests(unittest.TestCase):
|
|
def test_dominant_colour_is_reported(self):
|
|
colours = [(200, 40, 40)] * 90 + [(20, 60, 200)] * 10
|
|
palette = cm.dominant(colours, count=2)
|
|
self.assertEqual(len(palette), 2)
|
|
red, _, _ = cm.rgb_to_lab(palette[0])
|
|
self.assertGreater(red, 30.0)
|
|
|
|
def test_palette_distance_is_zero_for_the_same_palette(self):
|
|
palette = cm.dominant([(200, 40, 40)] * 50)
|
|
self.assertAlmostEqual(cm.palette_delta_e(palette, palette), 0.0, delta=1e-6)
|
|
|
|
def test_empty_palette_is_not_a_number(self):
|
|
self.assertNotEqual(cm.palette_delta_e([], [(1, 2, 3)]),
|
|
cm.palette_delta_e([], [(1, 2, 3)]))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|