184 lines
6.9 KiB
Python
184 lines
6.9 KiB
Python
"""Integration tests using the synthetic MME fixture.
|
|
|
|
Tests the full pipeline: read MME -> transform -> criteria -> protocol scoring.
|
|
"""
|
|
|
|
from pathlib import Path
|
|
|
|
import numpy as np
|
|
import pytest
|
|
|
|
from impakt.criteria import chest_deflection, clip_3ms, femur_load, hic15, nij, tibia_index
|
|
from impakt.io.mme import MMEReader
|
|
from impakt.protocol import euro_ncap, iihs, us_ncap
|
|
from impakt.transform.cfc import cfc_filter
|
|
|
|
FIXTURE_DIR = Path(__file__).parent / "fixtures" / "sample_mme"
|
|
|
|
|
|
@pytest.fixture(scope="module")
|
|
def test_data():
|
|
"""Load the synthetic MME fixture."""
|
|
reader = MMEReader()
|
|
assert reader.supports(FIXTURE_DIR), f"MME reader should support {FIXTURE_DIR}"
|
|
return reader.read(FIXTURE_DIR)
|
|
|
|
|
|
class TestMMEFixtureLoading:
|
|
def test_metadata(self, test_data):
|
|
assert test_data.test_id == "IMPAKT_SYNTH_001"
|
|
assert test_data.metadata.vehicle.make == "Synthetic"
|
|
assert test_data.metadata.vehicle.model == "TestCar"
|
|
assert test_data.metadata.dummy.dummy_type == "Hybrid III 50th Percentile Male"
|
|
assert test_data.metadata.impact.test_type == "Full Frontal Rigid Barrier"
|
|
assert test_data.metadata.impact.speed_kmh == 56.3
|
|
|
|
def test_channel_count(self, test_data):
|
|
assert len(test_data) == 26
|
|
|
|
def test_head_channels_found(self, test_data):
|
|
head_accel = test_data.find("11HEAD0000AC*")
|
|
assert len(head_accel) == 3 # X, Y, Z
|
|
|
|
def test_channel_properties(self, test_data):
|
|
ch = test_data.get("11HEAD0000ACXA")
|
|
assert ch.unit == "g"
|
|
assert ch.sample_rate == 20000.0
|
|
assert ch.n_samples == 4200
|
|
assert ch.cfc_class == 1000
|
|
|
|
def test_auto_grouping(self, test_data):
|
|
groups = test_data.groups()
|
|
# Should have groups for: head accel, head ang vel, chest accel, pelvis accel
|
|
group_keys = list(groups.keys())
|
|
head_groups = [k for k in group_keys if "HEAD" in k and "AC" in k]
|
|
assert len(head_groups) == 1
|
|
|
|
def test_channel_tree(self, test_data):
|
|
tree = test_data.channel_tree()
|
|
assert "Driver (1st Row Left)" in tree
|
|
assert "Vehicle Structure" in tree
|
|
|
|
|
|
class TestTransformPipeline:
|
|
def test_cfc_filter_on_fixture(self, test_data):
|
|
ch = test_data.get("11HEAD0000ACXA")
|
|
filtered = cfc_filter(ch, 600)
|
|
assert filtered.cfc_class == 600
|
|
assert filtered.peak <= ch.peak # Filter reduces peak
|
|
assert filtered.peak > 30.0 # But retains signal
|
|
|
|
def test_resultant_from_group(self, test_data):
|
|
group = test_data.group("HEAD0000AC")
|
|
resultant = group.resultant()
|
|
assert resultant.code.direction == "R"
|
|
# Resultant should be >= any individual component
|
|
assert resultant.peak >= test_data.get("11HEAD0000ACXA").peak
|
|
|
|
|
|
class TestInjuryCriteriaPipeline:
|
|
def test_hic15(self, test_data):
|
|
group = test_data.group("HEAD0000AC")
|
|
result = hic15(group)
|
|
assert result.criterion == "HIC15"
|
|
assert result.value > 0
|
|
assert result.body_region == "Head"
|
|
# With a ~45g half-sine pulse, HIC15 should be meaningful
|
|
assert result.value > 50
|
|
assert result.value < 5000
|
|
|
|
def test_3ms_clip(self, test_data):
|
|
group = test_data.group("CHST0000AC")
|
|
result = clip_3ms(group)
|
|
assert result.criterion == "3ms Clip"
|
|
assert result.value > 0
|
|
assert result.value < 100 # Reasonable for ~38g chest pulse
|
|
|
|
def test_nij(self, test_data):
|
|
fz = test_data.get("11NECKUP00FOZA")
|
|
my = test_data.get("11NECKUP00MOYA")
|
|
result = nij(fz_channel=fz, my_channel=my, dummy=test_data.metadata.dummy)
|
|
assert result.criterion == "Nij"
|
|
assert result.value > 0
|
|
assert result.value < 5.0 # Should be reasonable
|
|
|
|
def test_chest_deflection(self, test_data):
|
|
ch = test_data.get("11CHST0000DCXA")
|
|
result = chest_deflection(channel=ch)
|
|
assert result.criterion == "Chest Deflection"
|
|
assert result.unit == "mm"
|
|
assert 30.0 < result.value < 45.0 # ~36mm peak in fixture
|
|
|
|
def test_femur_load(self, test_data):
|
|
ch_left = test_data.get("11FEMRLE00FOZA")
|
|
result = femur_load(channel=ch_left, side="left")
|
|
assert result.criterion == "Femur Load Left"
|
|
assert result.unit == "kN"
|
|
assert 3.0 < result.value < 7.0 # ~4.8kN peak in fixture
|
|
|
|
def test_tibia_index(self, test_data):
|
|
fz = test_data.get("11TIBILEUOFOZA")
|
|
mx = test_data.get("11TIBILEUOMOXA")
|
|
my = test_data.get("11TIBILEUOMOYA")
|
|
result = tibia_index(fz_channel=fz, mx_channel=mx, my_channel=my)
|
|
assert result.value > 0
|
|
|
|
|
|
class TestProtocolScoringPipeline:
|
|
@pytest.fixture
|
|
def all_criteria(self, test_data):
|
|
"""Compute all criteria from the fixture data."""
|
|
head_group = test_data.group("HEAD0000AC")
|
|
chest_group = test_data.group("CHST0000AC")
|
|
|
|
results = {}
|
|
results["HIC15"] = hic15(head_group)
|
|
results["3ms Clip"] = clip_3ms(chest_group)
|
|
results["Nij"] = nij(
|
|
fz_channel=test_data.get("11NECKUP00FOZA"),
|
|
my_channel=test_data.get("11NECKUP00MOYA"),
|
|
dummy=test_data.metadata.dummy,
|
|
)
|
|
results["Chest Deflection"] = chest_deflection(channel=test_data.get("11CHST0000DCXA"))
|
|
results["Femur Load Left"] = femur_load(
|
|
channel=test_data.get("11FEMRLE00FOZA"), side="left"
|
|
)
|
|
results["Femur Load Right"] = femur_load(
|
|
channel=test_data.get("11FEMRRI00FOZA"), side="right"
|
|
)
|
|
results["Tibia Index"] = tibia_index(
|
|
fz_channel=test_data.get("11TIBILEUOFOZA"),
|
|
mx_channel=test_data.get("11TIBILEUOMOXA"),
|
|
my_channel=test_data.get("11TIBILEUOMOYA"),
|
|
)
|
|
return results
|
|
|
|
def test_euro_ncap_scoring(self, all_criteria):
|
|
result = euro_ncap.evaluate(all_criteria)
|
|
assert result.protocol == "Euro NCAP"
|
|
assert result.stars is not None
|
|
assert 0 <= result.stars <= 5
|
|
assert len(result.region_scores) > 0
|
|
# Check that all region scores have colors
|
|
for rs in result.region_scores:
|
|
assert rs.color is not None
|
|
|
|
def test_us_ncap_scoring(self, all_criteria):
|
|
result = us_ncap.evaluate(all_criteria)
|
|
assert result.protocol == "US NCAP"
|
|
assert result.stars is not None
|
|
assert 1 <= result.stars <= 5
|
|
|
|
def test_iihs_scoring(self, all_criteria):
|
|
result = iihs.evaluate(all_criteria)
|
|
assert result.protocol == "IIHS"
|
|
assert result.overall_rating in ("GOOD", "ACCEPTABLE", "MARGINAL", "POOR")
|
|
assert len(result.region_scores) > 0
|
|
|
|
def test_protocol_summary_printable(self, all_criteria):
|
|
for scorer in [euro_ncap, us_ncap, iihs]:
|
|
result = scorer.evaluate(all_criteria)
|
|
summary = result.summary()
|
|
assert len(summary) > 0
|
|
assert result.protocol in summary
|