| """Tests for the CarDD multi-label classifier dataset utilities and model.""" |
|
|
| from __future__ import annotations |
|
|
| from pathlib import Path |
|
|
| import pytest |
|
|
| from ccdp.data import damage_dataset as dd |
| from ccdp.data.schema import DAMAGE_TYPES, Record |
|
|
|
|
| def _mk(types: list[str], path: str = "x.jpg", ds: str = "cardd") -> Record: |
| return Record(image_path=Path(path), dataset=ds, damage_types=types) |
|
|
|
|
| def test_encode_labels_canonical(): |
| v = dd.encode_labels(["dent", "scratch"]) |
| assert len(v) == len(DAMAGE_TYPES) |
| assert v[DAMAGE_TYPES.index("dent")] == 1.0 |
| assert v[DAMAGE_TYPES.index("scratch")] == 1.0 |
| assert sum(v) == 2.0 |
|
|
|
|
| def test_encode_labels_unknown_dropped(): |
| v = dd.encode_labels(["dent", "foobar"]) |
| assert sum(v) == 1.0 |
|
|
|
|
| def test_split_records_deterministic(): |
| recs = [_mk(["dent"], path=f"{i}.jpg") for i in range(100)] |
| a = dd.split_records(recs, seed=42) |
| b = dd.split_records(recs, seed=42) |
| assert [r.image_path for r in a[0]] == [r.image_path for r in b[0]] |
| n_train, n_val, n_test = len(a[0]), len(a[1]), len(a[2]) |
| assert n_train + n_val + n_test == 100 |
| assert abs(n_train - 80) <= 1 |
| assert abs(n_val - 10) <= 1 |
|
|
|
|
| def test_pos_weight_inverse_frequency(): |
| recs = [] |
| |
| for _ in range(90): |
| recs.append(_mk(["dent"])) |
| for _ in range(10): |
| recs.append(_mk(["tire_flat"])) |
| pw = dd.pos_weight(recs) |
| i_dent = DAMAGE_TYPES.index("dent") |
| i_tf = DAMAGE_TYPES.index("tire_flat") |
| |
| assert pw[i_tf] > pw[i_dent] |
| |
| assert abs(pw[i_tf] - 9.0) < 0.01 |
| assert pw[i_dent] >= 1.0 |
|
|
|
|
| def test_pos_weight_empty_class_safe(): |
| recs = [_mk(["dent"]) for _ in range(10)] |
| pw = dd.pos_weight(recs) |
| assert all(w >= 1.0 for w in pw) |
| assert len(pw) == len(DAMAGE_TYPES) |
|
|