"""Unit test phần thuần Python của `scripts/cv/eval_anyball.py` (Bước 4, BRIEF 05/08 bàn giao 17). Script nằm ở `scripts/` (không phải package) nên nạp qua importlib từ đường dẫn file — top-level không import torch/ultralytics, chạy được trên môi trường app. Khâu match/AP KHÔNG test ở đây vì cố ý dùng nguyên bản DetectionValidator của ultralytics; nó được khoá bằng self-check identity vs metrics.json ngay trong script mỗi lần chạy thật (lệch > tolerance là script từ chối ghi anyball.json). """ from __future__ import annotations import importlib.util from pathlib import Path import pytest ROOT = Path(__file__).resolve().parents[1] _spec = importlib.util.spec_from_file_location( "eval_anyball", ROOT / "scripts" / "cv" / "eval_anyball.py") ea = importlib.util.module_from_spec(_spec) _spec.loader.exec_module(ea) NAMES = {0: "Black", 1: "Cue", 2: "Dot", 3: "Solid", 4: "Striped"} def test_remap_dong_nhat_khong_doi_gi(): assert ea.build_remap(NAMES, merged=False) == {i: i for i in range(5)} def test_remap_merged_gop_bi_loai_dot(): remap = ea.build_remap(NAMES, merged=True) assert remap[2] is None # Dot bị LOẠI, không phải bi assert [remap[i] for i in (0, 1, 3, 4)] == [0, 0, 0, 0] # 4 class bi -> "ball" def test_remap_thieu_class_bao_loi(): thieu_cue = {0: "Black", 1: "Dot", 2: "Solid", 3: "Striped"} with pytest.raises(ValueError, match="Cue"): ea.build_remap(thieu_cue, merged=True) def test_remap_khong_phu_thuoc_thu_tu_id(): # dataset khác có thể đánh id khác thứ tự — remap phải theo TÊN xao_tron = {0: "Dot", 1: "Striped", 2: "Black", 3: "Cue", 4: "Solid"} remap = ea.build_remap(xao_tron, merged=True) assert remap[0] is None assert [remap[i] for i in (1, 2, 3, 4)] == [0, 0, 0, 0] def test_count_gt_labels(tmp_path: Path): (tmp_path / "a.txt").write_text("2 0.5 0.5 0.1 0.1\n0 0.2 0.2 0.1 0.1\n", encoding="utf-8") (tmp_path / "b.txt").write_text("2 0.7 0.7 0.1 0.1\n\n", encoding="utf-8") counts = ea.count_gt_labels(tmp_path) assert counts[2] == 2 and counts[0] == 1 assert sum(counts.values()) == 3 def test_count_gt_labels_thu_muc_rong(tmp_path: Path): assert sum(ea.count_gt_labels(tmp_path).values()) == 0