0f5c4136f3
build / mod (push) Successful in 3m49s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
90 lines
3.7 KiB
Python
90 lines
3.7 KiB
Python
"""Tests of tools/hd_bench.py's comparison (#82): `python3 -m unittest tools/test_hd_bench.py`."""
|
|
import argparse
|
|
import sys
|
|
import unittest
|
|
from pathlib import Path
|
|
|
|
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
|
import hd_bench as hb # noqa: E402
|
|
|
|
|
|
def frame(w, h, v):
|
|
return bytes([v]) * (3 * w * h)
|
|
|
|
|
|
def args(**kw):
|
|
a = argparse.Namespace(budget_ms=18.0, max_flicker=1.0, max_animated=60.0, max_mean=3.0,
|
|
max_pct=5.0, max_new_animated=2.0, min_static=10.0, motion_level=hb.MOTION_LEVEL)
|
|
a.__dict__.update(kw)
|
|
return a
|
|
|
|
|
|
def judged(rgbs, base_rgbs, w=2, h=2):
|
|
rng = hb.motion_range(rgbs)
|
|
brng = hb.motion_range(base_rgbs)
|
|
entry = {"gpu_avg_ms": 1.0}
|
|
entry["flicker"] = hb.flicker_stats(rng, w, h, len(rgbs), hb.MOTION_LEVEL, hb.FLICKER_LEVEL)
|
|
entry["vs_baseline"] = hb.compare_runs(rgbs, rng, base_rgbs, brng, w, h, hb.MOTION_LEVEL)
|
|
return entry, hb.judge(entry, args())
|
|
|
|
|
|
class FlickerIsNotAnimation(unittest.TestCase):
|
|
def test_the_issues_black_white_strobe_fails(self):
|
|
# The issue: a 2x2 frame, the baseline steadily black, the run alternating black and white.
|
|
entry, reasons = judged([frame(2, 2, 0), frame(2, 2, 255)] * 2, [frame(2, 2, 0)] * 3)
|
|
self.assertEqual(entry["flicker"]["pct"], 0.0) # the old blind spot is still in these two numbers ...
|
|
self.assertEqual(entry["flicker"]["animated_pct"], 100.0)
|
|
self.assertEqual(entry["flicker"]["unsteady_pct"], 100.0)
|
|
self.assertEqual(entry["vs_baseline"]["new_animated_pct"], 100.0)
|
|
self.assertTrue(reasons, "... but the verdict is a failure")
|
|
|
|
def test_stronger_flicker_never_starts_passing(self):
|
|
w = h = 4
|
|
base = [frame(w, h, 100)] * 3
|
|
failed = []
|
|
unsteady = []
|
|
for amp in (8, 16, 32, 255):
|
|
lo, hi = 100 - min(amp // 2, 100), min(255, 100 + (amp + 1) // 2)
|
|
run = [frame(w, h, lo), frame(w, h, hi), frame(w, h, lo)]
|
|
entry, reasons = judged(run, base, w, h)
|
|
failed.append(bool(reasons))
|
|
unsteady.append(entry["flicker"]["unsteady_pct"])
|
|
self.assertEqual(failed, [True] * 4, failed)
|
|
self.assertEqual(unsteady, sorted(unsteady))
|
|
|
|
def test_a_fully_dynamic_frame_is_not_a_zero_error(self):
|
|
w = h = 2
|
|
moving = [frame(w, h, 0), frame(w, h, 255)]
|
|
entry, reasons = judged(moving, moving)
|
|
self.assertEqual(entry["vs_baseline"]["static_pct"], 0.0)
|
|
self.assertTrue(any("проверяемых" in r for r in reasons), reasons)
|
|
|
|
def test_water_and_foliage_that_also_move_in_the_baseline_pass(self):
|
|
w, h = 10, 10
|
|
n = w * h
|
|
def mixed(a, b): # the left 20% animates (water), the rest is still
|
|
f = bytearray(frame(w, h, 90))
|
|
for px in range(n // 5):
|
|
f[3 * px:3 * px + 3] = bytes([a]) * 3
|
|
return bytes(f)
|
|
run = [mixed(40, 40), mixed(120, 40), mixed(60, 40)]
|
|
base = [mixed(30, 40), mixed(130, 40), mixed(70, 40)]
|
|
entry, reasons = judged(run, base, w, h)
|
|
self.assertEqual(entry["vs_baseline"]["new_animated_pct"], 0.0)
|
|
self.assertEqual(reasons, [])
|
|
|
|
def test_the_mask_comes_from_the_baseline_not_from_the_run(self):
|
|
# Growth of the animated area against the baseline is counted and bounded.
|
|
w = h = 10
|
|
base = [frame(w, h, 90)] * 3
|
|
f = bytearray(frame(w, h, 90))
|
|
f[0:30] = bytes([250]) * 30 # 10% of the pixels
|
|
run = [bytes(f), frame(w, h, 90), bytes(f)]
|
|
entry, reasons = judged(run, base, w, h)
|
|
self.assertAlmostEqual(entry["vs_baseline"]["new_animated_pct"], 10.0)
|
|
self.assertTrue(reasons)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|