Spaces:
Running
Running
File size: 7,492 Bytes
bb59211 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 | """ๆธ
ๆดๆฅๅ็ป่ฃ
ๅๆต(app/processing/cleaning/report.py)ใ
่ฆ็:ๅๅฑ็ปๆ็ๆฏไธๅฑใๆถ้ด่ฝดๆๅนณไธๅป้ใ่ฎญ็ป็จ้ๅคๅฎใfail-closed ่ฏญไนใ
ๅ
จๆฏ็บฏๅฝๆฐ โโ ไธ้่ฆ็่ง้ขใไธ้่ฆ cv2ใไธ้่ฆไปปไฝ mockใ
pytest tests/test_cleaning_report.py
python3 tests/test_cleaning_report.py
"""
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
from app.processing.cleaning import contract as C # noqa: E402
from app.processing.cleaning import report as R # noqa: E402
def _range(start, end, code="video.freeze", severity=C.WARN, stream="s1"):
return C.Range(start_sec=start, end_sec=end, code=code,
severity=severity, stream=stream)
def _stream(name="s1", status=C.PASS, ranges=(), channels=("rgb", "time")):
return C.StreamResult(stream=name, status=status, ranges=tuple(ranges),
channels=tuple(channels))
# โโ ็ฌฌไบๅฑ:ๆถ้ด่ฝด โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
def test_timeline_sorted_by_time():
streams = [
_stream("s1", ranges=[_range(5, 6), _range(1, 2)]),
_stream("s2", ranges=[_range(3, 4)]),
]
axis = R.build_timeline(streams)
assert [row["start_sec"] for row in axis] == [1, 3, 5]
def test_timeline_dedupes_identical_rows():
dup = _range(1, 2)
axis = R.build_timeline([_stream("s1", ranges=[dup, dup])])
assert len(axis) == 1
def test_timeline_fills_stream_from_parent():
"""range ่ชๅทฑๆฒกๅ stream ๆถ,ไปๆๅฑ็ stream ่กฅไธใ"""
bare = C.Range(start_sec=1, end_sec=2, code="x", severity=C.WARN)
axis = R.build_timeline([_stream("cam_a", ranges=[bare])])
assert axis[0]["stream"] == "cam_a"
def test_timeline_accepts_dict_streams():
"""ๆฅๅไป็ฃ็่ฏปๅๆฅๆถ streams ๆฏ dict,ไธๆฏ StreamResultใ"""
axis = R.build_timeline([{
"stream": "s1",
"ranges": [{"start_sec": 1, "end_sec": 2, "code": "x", "severity": C.WARN}],
}])
assert axis[0]["code"] == "x"
def test_timeline_empty():
assert R.build_timeline([]) == []
# โโ ็ฌฌไธๅฑ:ๆด้็ป่ฎบ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
def test_episode_takes_worst_stream():
episode = R.rollup_episode([_stream("a", C.PASS), _stream("b", C.FAIL)])
assert episode["status"] == C.FAIL
assert episode["failed_streams"] == ["b"]
def test_episode_includes_findings_in_rollup():
"""่ทจๆจกๆๆฃๆฅๆฒกๆๅฏนๅบ็ stream,ๅฎ็็ป่ฎบไน่ฆๅไธๆด้ๅคๅฎใ"""
finding = C.Finding(rule="cross.sync", version="1.0", status=C.FAIL,
scope="cross_modal")
episode = R.rollup_episode([_stream("a", C.PASS)], [finding])
assert episode["status"] == C.FAIL
def test_episode_all_pass():
episode = R.rollup_episode([_stream("a", C.PASS), _stream("b", C.PASS)])
assert episode["status"] == C.PASS
assert episode["summary"] == "All checks passed"
def test_episode_summary_counts_each_bucket():
episode = R.rollup_episode([
_stream("a", C.FAIL), _stream("b", C.WARN), _stream("c", C.PENDING),
])
assert "1 stream(s) failed" in episode["summary"]
assert "1 stream(s) with warnings" in episode["summary"]
assert "1 stream(s) pending review" in episode["summary"]
def test_episode_empty_streams_is_pass():
"""ๆฒกๆๆตๅฏๆฃๆฅ โ ่ฟไธๅฑๅค PASS(ๆฏๅฆ fail-closed ็ฑ build_report/passed ๅณๅฎ)ใ"""
assert R.rollup_episode([])["status"] == C.PASS
# โโ ็ฌฌๅๅฑ:่ฎญ็ป็จ้ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
def test_training_use_fails_on_missing_channel():
rows = {row["use"]: row for row in R.rollup_training_use({"rgb", "time"})}
assert rows["pure_vision"]["status"] == C.PASS
assert rows["vision_tactile_joint"]["status"] == C.FAIL
assert "not collected" in rows["vision_tactile_joint"]["reason"]
assert rows["vision_tactile_joint"]["missing"] == ["tactile"]
def test_training_use_reports_all_missing_channels():
rows = {row["use"]: row for row in R.rollup_training_use(set())}
assert set(rows["pure_vision"]["missing"]) == {"rgb", "time"}
def test_training_use_inherits_episode_status():
"""ไฟก้้ฝๅ
จไฝๆด้ๆ้ฎ้ข โ ่ฏฅ็จ้่ท็ๅค warn/fail,่ไธๆฏ passใ"""
rows = {row["use"]: row for row in R.rollup_training_use({"rgb", "time"}, C.WARN)}
assert rows["pure_vision"]["status"] == C.WARN
assert rows["pure_vision"]["reason"] == "data quality problem"
def test_training_use_covers_every_declared_rule():
rows = R.rollup_training_use({"rgb", "time"})
assert len(rows) == len(R.TRAINING_USE_RULES)
# โโ ๅฎๆดๆฅๅ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
def test_build_report_shape():
rep = R.build_report(
[_stream("cam_a", C.WARN, [_range(1, 2)])],
[],
episode_id="ep1", project="proj",
modalities={"actual": ["mono_rgb"]},
channels={"rgb", "time"},
ruleset_revision="abc123",
template_source="workflow",
)
assert rep["schema_version"] == R.SCHEMA_VERSION
assert rep["episode_id"] == "ep1"
assert rep["ruleset_revision"] == "abc123"
assert rep["template_source"] == "workflow"
assert rep["episode"]["status"] == C.WARN
assert rep["passed"] is False # ๅชๆ PASS ็ฎ้่ฟ,WARN ไธ็ฎ
assert len(rep["ranges"]) == 1
assert len(rep["streams"]) == 1
assert rep["training_use"]
def test_build_report_passed_only_when_all_clean():
rep = R.build_report([_stream("a", C.PASS)], [], channels={"rgb", "time"})
assert rep["episode"]["status"] == C.PASS
assert rep["passed"] is True
def test_warn_is_not_passed():
"""WARN ไธ็ฎ้่ฟ โโ ่ฟๆฏ"ๅกๅจไบบๅทฅๅฎกๆ ธ"็ๅคๅฎไพๆฎ,ๅฟ
้กปๆพๅผ้ไฝใ"""
assert R.passed_for(C.WARN) is False
assert R.passed_for(C.PASS) is True
def test_build_report_pulls_channels_from_streams():
"""ๆฒกๆพๅผไผ channels ๆถ,ไปๅๆกๆต่ชๅทฑๅฃฐๆ็ไฟก้ๆฑๆปใ"""
rep = R.build_report([_stream("a", C.PASS, channels=("rgb", "time", "tactile"))])
rows = {row["use"]: row for row in rep["training_use"]}
assert rows["vision_tactile_joint"]["status"] == C.PASS
def test_empty_report_is_fail_closed():
"""ๆฒกๆๆฐๆฎ โ ๆฐๆฎๅๆ ผใ"""
rep = R.empty_report(episode_id="ep1", reason="no_data")
assert rep["passed"] is False
assert rep["episode"]["status"] == C.ERROR
assert rep["reason"] == "no_data"
assert rep["streams"] == []
if __name__ == "__main__":
import inspect
import tempfile
_failures = 0
for _name, _fn in sorted(globals().items()):
if not _name.startswith("test_") or not callable(_fn):
continue
try:
if "tmp_path" in inspect.signature(_fn).parameters:
with tempfile.TemporaryDirectory() as _tmp:
_fn(Path(_tmp))
else:
_fn()
print(f"PASS {_name}")
except Exception as _exc: # noqa: BLE001
_failures += 1
print(f"FAIL {_name}: {_exc}")
raise SystemExit(1 if _failures else 0)
|