initial commit
This commit is contained in:
183
tests/test_integration.py
Normal file
183
tests/test_integration.py
Normal file
@@ -0,0 +1,183 @@
|
||||
"""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
|
||||
Reference in New Issue
Block a user