mirror of
https://github.com/azaion/ai-training.git
synced 2026-04-22 22:16:35 +00:00
18b88ba9bf
- Updated `.gitignore` to remove committed test fixture data exclusions. - Increased batch size in `config.test.yaml` from 4 to 128 for training. - Simplified directory structure in `config.yaml` by removing unnecessary data paths. - Adjusted paths in `augmentation.py`, `dataset-visualiser.py`, and `exports.py` to align with the new configuration structure. - Enhanced `annotation_queue_handler.py` to utilize the updated configuration for directory management. - Added CSV logging of test results in `conftest.py` for better test reporting. These changes streamline the configuration management and enhance the testing framework, ensuring better organization and clarity in the project.
160 lines
4.7 KiB
Python
160 lines
4.7 KiB
Python
import csv
|
|
import shutil
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
_PROJECT_ROOT = Path(__file__).resolve().parent.parent
|
|
_TESTS_DIR = Path(__file__).resolve().parent
|
|
_TEST_ROOT = _TESTS_DIR / "root"
|
|
_DATASET_IMAGES = _TEST_ROOT / "data" / "images"
|
|
_DATASET_LABELS = _TEST_ROOT / "data" / "labels"
|
|
_ONNX_MODEL = _PROJECT_ROOT / "_docs/00_problem/input_data/azaion.onnx"
|
|
_CLASSES_JSON = _PROJECT_ROOT / "src" / "classes.json"
|
|
_CONFIG_TEST = _PROJECT_ROOT / "config.test.yaml"
|
|
|
|
collect_ignore = ["security_test.py", "imagelabel_visualize_test.py"]
|
|
|
|
_test_results = []
|
|
|
|
|
|
@pytest.hookimpl(tryfirst=True, hookwrapper=True)
|
|
def pytest_runtest_makereport(item, call):
|
|
outcome = yield
|
|
report = outcome.get_result()
|
|
if report.when == "call" or (report.when == "setup" and report.skipped):
|
|
_test_results.append({
|
|
"module": item.nodeid.rsplit("::", 1)[0],
|
|
"name": item.name,
|
|
"result": report.outcome.upper(),
|
|
"duration": round(report.duration, 3),
|
|
})
|
|
|
|
|
|
def pytest_sessionfinish(session, exitstatus):
|
|
if not _test_results:
|
|
return
|
|
results_dir = Path(__file__).resolve().parent / "test-results"
|
|
results_dir.mkdir(exist_ok=True)
|
|
|
|
with open(results_dir / "test-results.csv", "w", newline="", encoding="utf-8") as f:
|
|
writer = csv.writer(f)
|
|
writer.writerow(["module", "test", "result", "duration_s"])
|
|
for r in _test_results:
|
|
writer.writerow([r["module"], r["name"], r["result"], f"{r['duration']:.3f}"])
|
|
|
|
|
|
def apply_constants_patch(monkeypatch, base: Path):
|
|
import constants as c
|
|
monkeypatch.setattr(c, "config", c.Config.from_yaml(str(_CONFIG_TEST), root=str(base / "azaion")))
|
|
|
|
|
|
@pytest.fixture(scope="session")
|
|
def fixture_images_dir():
|
|
p = _DATASET_IMAGES
|
|
if not p.is_dir():
|
|
pytest.skip(f"missing dataset images: {p}")
|
|
return p
|
|
|
|
|
|
@pytest.fixture(scope="session")
|
|
def fixture_labels_dir():
|
|
p = _DATASET_LABELS
|
|
if not p.is_dir():
|
|
pytest.skip(f"missing dataset labels: {p}")
|
|
return p
|
|
|
|
|
|
@pytest.fixture(scope="session")
|
|
def fixture_onnx_model():
|
|
p = _ONNX_MODEL
|
|
if not p.is_file():
|
|
pytest.skip(f"missing onnx model: {p}")
|
|
return p.read_bytes()
|
|
|
|
|
|
@pytest.fixture(scope="session")
|
|
def fixture_classes_json():
|
|
p = _CLASSES_JSON
|
|
if not p.is_file():
|
|
pytest.skip(f"missing classes.json: {p}")
|
|
return p
|
|
|
|
|
|
@pytest.fixture
|
|
def constants_patch(monkeypatch):
|
|
def _apply(base: Path):
|
|
apply_constants_patch(monkeypatch, base)
|
|
|
|
return _apply
|
|
|
|
|
|
@pytest.fixture
|
|
def work_dir(tmp_path):
|
|
w = tmp_path / "work"
|
|
(w / "images").mkdir(parents=True)
|
|
(w / "labels").mkdir(parents=True)
|
|
return w
|
|
|
|
|
|
@pytest.fixture
|
|
def sample_image_label(fixture_images_dir, fixture_labels_dir, tmp_path):
|
|
imgs = sorted(fixture_images_dir.glob("*.jpg"))
|
|
if not imgs:
|
|
raise RuntimeError("no images in fixture_images_dir")
|
|
stem = imgs[0].stem
|
|
src_img = fixture_images_dir / f"{stem}.jpg"
|
|
src_lbl = fixture_labels_dir / f"{stem}.txt"
|
|
dst_img = tmp_path / "images" / f"{stem}.jpg"
|
|
dst_lbl = tmp_path / "labels" / f"{stem}.txt"
|
|
dst_img.parent.mkdir(parents=True, exist_ok=True)
|
|
dst_lbl.parent.mkdir(parents=True, exist_ok=True)
|
|
shutil.copy2(src_img, dst_img)
|
|
shutil.copy2(src_lbl, dst_lbl)
|
|
return dst_img, dst_lbl
|
|
|
|
|
|
@pytest.fixture
|
|
def sample_images_labels(fixture_images_dir, fixture_labels_dir, tmp_path):
|
|
def _factory(count: int):
|
|
if count < 1:
|
|
raise ValueError("count must be >= 1")
|
|
imgs = sorted(fixture_images_dir.glob("*.jpg"))
|
|
if count > len(imgs):
|
|
raise ValueError("count exceeds available images")
|
|
out_img = tmp_path / "images"
|
|
out_lbl = tmp_path / "labels"
|
|
out_img.mkdir(parents=True, exist_ok=True)
|
|
out_lbl.mkdir(parents=True, exist_ok=True)
|
|
for p in imgs[:count]:
|
|
stem = p.stem
|
|
shutil.copy2(fixture_images_dir / f"{stem}.jpg", out_img / f"{stem}.jpg")
|
|
shutil.copy2(fixture_labels_dir / f"{stem}.txt", out_lbl / f"{stem}.txt")
|
|
return out_img, out_lbl
|
|
|
|
return _factory
|
|
|
|
|
|
@pytest.fixture
|
|
def corrupted_label(tmp_path):
|
|
p = tmp_path / "labels" / "corrupted.txt"
|
|
p.parent.mkdir(parents=True, exist_ok=True)
|
|
p.write_text("0 1.5 0.5 0.1 0.1\n", encoding="utf-8")
|
|
return p
|
|
|
|
|
|
@pytest.fixture
|
|
def edge_bbox_label(tmp_path):
|
|
p = tmp_path / "labels" / "edge.txt"
|
|
p.parent.mkdir(parents=True, exist_ok=True)
|
|
p.write_text("0 0.01 0.5 0.02 0.3\n", encoding="utf-8")
|
|
return p
|
|
|
|
|
|
@pytest.fixture
|
|
def empty_label(tmp_path):
|
|
p = tmp_path / "labels" / "empty.txt"
|
|
p.parent.mkdir(parents=True, exist_ok=True)
|
|
p.write_text("", encoding="utf-8")
|
|
return p
|