Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-10-27 08:41:18

0001 # test_dex_schema.py - validates DEX producers and repo samples against the
0002 # published JSON Schema (dex-schema/firebird-dex-v1.schema.json).
0003 #
0004 # The hand-rolled structural validator (pyrobird.dex.validate_dex) checks rules
0005 # the schema cannot express (column lengths vs count, ref ranges); this file
0006 # guards the OTHER direction: the schema document itself stays in sync with
0007 # what the converters emit. Skips cleanly outside the monorepo checkout
0008 # (pip-installed pyrobird has no dex-schema/ directory).
0009 
0010 import json
0011 import os
0012 
0013 import pytest
0014 import uproot
0015 
0016 jsonschema = pytest.importorskip("jsonschema")
0017 
0018 # Repo layout: <repo>/pyrobird/tests/unit_tests/this_file -> <repo>/dex-schema/
0019 REPO_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", ".."))
0020 SCHEMA_FILE = os.path.join(REPO_ROOT, "dex-schema", "firebird-dex-v1.schema.json")
0021 
0022 TEST_EDM4EIC_FILE = os.path.join(os.path.dirname(__file__), 'data', 'reco_2024-09_craterlake_2evt.edm4eic.root')
0023 TEST_EDM4HEP_FILE = os.path.join(os.path.dirname(__file__), 'data', 'k_lambda_10x100_2evt.edm4hep.root')
0024 
0025 pytestmark = pytest.mark.skipif(
0026     not os.path.isfile(SCHEMA_FILE),
0027     reason="dex-schema/ not present (running outside the monorepo checkout)")
0028 
0029 
0030 @pytest.fixture(scope="module")
0031 def validator():
0032     with open(SCHEMA_FILE) as f:
0033         schema = json.load(f)
0034     # The schema declares draft 2020-12; check_schema catches a broken schema
0035     # file itself (the most likely drift after hand edits)
0036     jsonschema.Draft202012Validator.check_schema(schema)
0037     return jsonschema.Draft202012Validator(schema)
0038 
0039 
0040 def assert_valid(validator, document, what):
0041     errors = sorted(validator.iter_errors(document), key=lambda e: e.json_path)
0042     assert not errors, f"{what} violates the DEX schema:\n" + "\n".join(
0043         f"  {error.json_path}: {error.message}" for error in errors[:10])
0044 
0045 
0046 def test_schema_accepts_edm4eic_converter_output(validator):
0047     from pyrobird.edm4eic import edm4eic_to_dex_dict
0048     tree = uproot.open(TEST_EDM4EIC_FILE)['events']
0049     document = edm4eic_to_dex_dict(tree, [0, 1], origin_info={'file': 'test'})
0050     assert_valid(validator, document, "edm4eic converter output")
0051 
0052 
0053 def test_schema_accepts_edm4hep_converter_output(validator):
0054     from pyrobird.edm4hep import edm4hep_to_dex_dict
0055     tree = uproot.open(TEST_EDM4HEP_FILE)['events']
0056     document = edm4hep_to_dex_dict(tree, [0, 1], origin_info={'file': 'test'})
0057     assert_valid(validator, document, "edm4hep converter output")
0058 
0059 
0060 def test_schema_accepts_upgraded_004(validator):
0061     from pyrobird.dex import upgrade_dex
0062     old_document = {
0063         "type": "firebird-dex-json",
0064         "version": "0.04",
0065         "events": [{
0066             "id": "event_0",
0067             "groups": [
0068                 {
0069                     "name": "Hits", "type": "BoxHit",
0070                     "hits": [{"pos": [1, 2, 3], "dim": [1, 1, 1], "t": [4, 0], "ed": [0.001, 0]}],
0071                 },
0072                 {
0073                     "name": "Tracks", "type": "PointTrajectory",
0074                     "paramColumns": ["theta"], "pointColumns": ["x", "y", "z", "t"],
0075                     "trajectories": [{"points": [[0, 0, 0, 0], [1, 1, 1, 1]], "params": [0.5]}],
0076                 },
0077             ],
0078         }],
0079     }
0080     assert_valid(validator, upgrade_dex(old_document), "upgrade_dex output")
0081 
0082 
0083 @pytest.mark.parametrize("relative_path", [
0084     "firebird-ng/src/assets/data/example-cherenkov.firebird.json",
0085     "dd4hep-plugin/test.edm4eic.firebird.json",
0086 ])
0087 def test_schema_accepts_repo_samples(validator, relative_path):
0088     sample_file = os.path.join(REPO_ROOT, relative_path)
0089     if not os.path.isfile(sample_file):
0090         pytest.skip(f"{relative_path} not present")
0091     with open(sample_file) as f:
0092         assert_valid(validator, json.load(f), relative_path)
0093 
0094 
0095 def test_schema_rejects_malformed_documents(validator):
0096     # wrong version
0097     assert not validator.is_valid({"type": "firebird-dex-json", "version": "0.04", "events": []})
0098     # piece missing count/columns
0099     assert not validator.is_valid({
0100         "type": "firebird-dex-json", "version": "1.0",
0101         "events": [{"id": 0, "pieces": [{"name": "X", "type": "BoxHit", "version": "1.0"}]}],
0102     })
0103     # BoxHit without the required pos/dim columns
0104     assert not validator.is_valid({
0105         "type": "firebird-dex-json", "version": "1.0",
0106         "events": [{"id": 0, "pieces": [{
0107             "name": "X", "type": "BoxHit", "version": "1.0",
0108             "count": 0, "columns": {},
0109         }]}],
0110     })
0111     # PointTrajectory without points/pointColumns
0112     assert not validator.is_valid({
0113         "type": "firebird-dex-json", "version": "1.0",
0114         "events": [{"id": 0, "pieces": [{
0115             "name": "X", "type": "PointTrajectory", "version": "1.0",
0116             "count": 0, "columns": {},
0117         }]}],
0118     })