Skip to content

Commit 13b2de7

Browse files
committed
add a unit tests for outputing GRun, HIon, Special in FULL output mode
1 parent 7af1486 commit 13b2de7

File tree

2 files changed

+198
-0
lines changed

2 files changed

+198
-0
lines changed

HLTrigger/Configuration/test/BuildFile.xml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,18 @@
2323

2424
<!-- test script to check the consistency of event content of HLTScoutingExtra and HLTScoutingAll -->
2525
<test name="testHLTScoutingEventContent" command="python3 ${CMSSW_BASE}/src/HLTrigger/Configuration/test/testHLTScoutingEventContent.py"/>
26+
27+
<!-- test script check output "full" of GRun menu -->
28+
<test name="test_GRunFullOutput" command="python3
29+
${CMSSW_BASE}/src/HLTrigger/Configuration/test/testHLTOutput.py
30+
full GRun"/>
31+
32+
<!-- test script check output full of HIon menu -->
33+
<test name="test_HIonFullOutput" command="python3
34+
${CMSSW_BASE}/src/HLTrigger/Configuration/test/testHLTOutput.py
35+
full HIon"/>
36+
37+
<!-- test script check output full of Special menu -->
38+
<test name="test_SpecialFullOutput" command="python3
39+
${CMSSW_BASE}/src/HLTrigger/Configuration/test/testHLTOutput.py
40+
full Special"/>
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
#!/usr/bin/env python3
2+
import importlib.util
3+
import os
4+
import tempfile, shutil
5+
import subprocess
6+
import sys
7+
8+
from HLTrigger.Configuration.Tools.confdb import HLTProcess
9+
import FWCore.ParameterSet.Config as cms
10+
11+
class _DummyConfig:
12+
"""Minimal config to drive HLTProcess.overrideOutput()."""
13+
def __init__(self, output):
14+
self.hilton = False
15+
self.fragment = False
16+
self.output = output # "minimal", "all", or "full"
17+
18+
def _load_menu(menu_type):
19+
"""Generate a Run3 HLT menu with cmsDriver and load the process object."""
20+
21+
with tempfile.TemporaryDirectory() as tmpdir:
22+
menu_path = os.path.join(tmpdir, "myMenu.py")
23+
24+
# Run cmsDriver
25+
cmsdriver_cmd = [
26+
"cmsDriver.py",
27+
"TEST",
28+
"-s", f"L1REPACK:uGT,HLT:{menu_type}",
29+
"--data",
30+
"--scenario=pp",
31+
"-n", "1",
32+
"--conditions", f"auto:run3_hlt_{menu_type}",
33+
"--datatier", "RAW",
34+
"--eventcontent", "RAW",
35+
"--era", "Run3",
36+
"--process", "reHLT",
37+
"--no_exec",
38+
"--python_filename", menu_path,
39+
]
40+
41+
subprocess.run(cmsdriver_cmd, check=True)
42+
43+
# Import the generated configuration
44+
spec = importlib.util.spec_from_file_location("myMenu", menu_path)
45+
mod = importlib.util.module_from_spec(spec)
46+
spec.loader.exec_module(mod)
47+
48+
return mod.process
49+
50+
def _prune_all_outputs(process):
51+
"""Remove ALL existing output modules and any EndPaths that carry them (and unschedule them)."""
52+
# Collect current output modules
53+
outputs = list(process.outputModules_())
54+
if not outputs:
55+
return
56+
57+
# Identify EndPaths that contain those outputs
58+
endpaths_to_remove = []
59+
try:
60+
ep_items = process.endpaths_().items() # available in CMSSW python API
61+
except Exception:
62+
ep_items = []
63+
for ep_name, ep in ep_items:
64+
try:
65+
if any(lbl in ep.moduleNames() for lbl in outputs):
66+
endpaths_to_remove.append(ep_name)
67+
except Exception:
68+
pass
69+
70+
# Unschedule those EndPaths first (if schedule exists)
71+
if hasattr(process, "schedule") and process.schedule is not None:
72+
for ep_name in endpaths_to_remove:
73+
ep = getattr(process, ep_name, None)
74+
if ep is not None:
75+
try:
76+
process.schedule.remove(ep)
77+
except Exception:
78+
pass
79+
80+
# Drop EndPaths
81+
for ep_name in endpaths_to_remove:
82+
if hasattr(process, ep_name):
83+
delattr(process, ep_name)
84+
85+
# Drop the output modules
86+
for lbl in outputs:
87+
if hasattr(process, lbl):
88+
delattr(process, lbl)
89+
90+
def _build_hltprocess_from(process, output_mode: str, input_file: str) -> HLTProcess:
91+
"""Seed an HLTProcess with the (possibly pruned) process text."""
92+
cfg = _DummyConfig(output=output_mode)
93+
hlt = HLTProcess.__new__(HLTProcess) # bypass __init__ (no ConfDB query)
94+
hlt.config = cfg
95+
hlt.config.parent = None
96+
hlt.config.input = input_file
97+
hlt.config.emulator = "uGT"
98+
hlt.data = process.dumpPython()
99+
hlt.parent = []
100+
hlt.options = {k: [] for k in
101+
['essources','esmodules','modules','sequences','services','paths','psets','blocks']}
102+
hlt.labels = {'process': 'process', 'dict': 'process.__dict__'}
103+
return hlt
104+
105+
def _get_default_input():
106+
# Path to the filelist
107+
cmssw_base = os.environ["CMSSW_BASE"]
108+
filelist = os.path.join(cmssw_base,
109+
"src/HLTrigger/Configuration/test/testAccessToEDMInputsOfHLTTests_filelist.txt")
110+
111+
# Use pure Python (no external grep/tail needed)
112+
infile = None
113+
with open(filelist) as f:
114+
lines = [l.strip() for l in f if "/Run20" in l]
115+
if lines:
116+
infile = lines[-1] # tail -1 equivalent
117+
118+
if infile is None:
119+
raise RuntimeError(f"No matching input file found in {filelist}")
120+
return infile
121+
122+
def main():
123+
if len(sys.argv) not in (3, 4):
124+
print("Usage: python3 testHLTOutput.py [minimal|all|full] [GRun|HIon|PIon|...] [input_file.root (optional)]")
125+
sys.exit(1)
126+
127+
mode = sys.argv[1]
128+
menu_type = sys.argv[2]
129+
infile = sys.argv[3] if len(sys.argv) == 4 else _get_default_input()
130+
131+
if mode not in ("minimal", "all", "full"):
132+
print(f"Do not understand mode '{mode}'")
133+
sys.exit(1)
134+
135+
# --- Nice printouts ---
136+
print("=" * 80)
137+
print("Test HLT configuration outputs")
138+
print("-" * 80)
139+
print(f" Mode : {mode}")
140+
print(f" Menu type : {menu_type}")
141+
print(f" Input file : {infile}")
142+
print("=" * 80)
143+
144+
# 1) Load the real menu
145+
process = _load_menu(menu_type)
146+
147+
# 2) For minimal/full, prune existing outputs so we end up with ONLY the requested one
148+
if mode in ("minimal", "full"):
149+
_prune_all_outputs(process)
150+
# sanity: should be empty now
151+
assert len(process.outputModules_()) == 0, "Output pruning failed: outputs still present"
152+
153+
# 3) Wrap in HLTProcess and apply overrideOutput()
154+
hlt = _build_hltprocess_from(process, mode, infile)
155+
hlt.overrideOutput()
156+
hlt.build_source()
157+
158+
# 4) Make the job runnable without input files
159+
hlt.data += """
160+
# --- test harness tweaks ---
161+
%(process)s.options.wantSummary = cms.untracked.bool(False)
162+
"""
163+
164+
# 5) Finalize substitutions and write cfg
165+
cfg_text = hlt.dump()
166+
cfg_path = f"override_{mode}_cfg.py"
167+
with open(cfg_path, "w") as f:
168+
f.write(cfg_text)
169+
print(f"[ok] wrote {cfg_path}")
170+
171+
# Optional: quick check of outputs in the final text (informational)
172+
if mode in ("minimal", "full"):
173+
expect = "hltOutputMinimal" if mode == "minimal" else "hltOutputFull"
174+
if expect not in cfg_text:
175+
print(f"[warn] expected {expect} not found in generated cfg")
176+
177+
# 6) Run cmsRun
178+
print(f"[run] cmsRun -j job_{mode}.xml {cfg_path}")
179+
ret = subprocess.run(["cmsRun", "-j", f"job_{mode}.xml", cfg_path])
180+
sys.exit(ret.returncode)
181+
182+
if __name__ == "__main__":
183+
main()

0 commit comments

Comments
 (0)