16from shutil
import copyfile
18from ROOT.Belle2
import FileMetaData
21from b2test_utils
import clean_working_directory, skip_test_if_light
24def create_testfile(name, exp=0, run=0, events=100, branchNames=None, **argk):
25 """Create a test file from a steering string"""
26 if branchNames
is None:
28 global testfile_steering
29 env = dict(os.environ)
32 steering_file = f
"steering-{name}.py"
33 with open(steering_file,
"w")
as f:
34 f.write(testfile_steering)
54def create_testfile_direct(
57 release="test_release",
61 global_tag="test_globaltag",
62 steering="test_steering",
64 """similar to create_testfile but does it manually without running basf2 for
65 full control over the FileMetaData"""
67 metadata = FileMetaData()
72 metadata.setRandomSeed(seed)
74 metadata.setCreationData(
75 "the most auspicious of days for testing", site, user, release
77 metadata.setDatabaseGlobalTag(global_tag)
78 metadata.setSteering(steering)
79 f = ROOT.TFile(name,
"RECREATE")
80 t = ROOT.TTree(
"persistent",
"persistent")
81 t.Branch(
"FileMetaData", metadata)
84 t = ROOT.TTree(
"tree",
"tree")
85 event_meta = ROOT.Belle2.EventMetaData()
86 t.Branch(
"EventMetaData", event_meta)
92def create_testfile_ntuple(input, output, treeNames=["tree", "anotherTree"], **argk):
93 """Create a test ntuple file from a steering string"""
94 global testfile_ntuple_steering
95 env = dict(os.environ)
98 steering_file =
"steering-ntuple.py"
99 with open(steering_file,
"w")
as f:
100 f.write(testfile_ntuple_steering)
103 [
"basf2",
"-i", input,
"-o", output, steering_file] + treeNames, env=env
107 metadata = basf2.get_file_metadata(output)
108 metadata.setCreationData(
109 metadata.getDate(), metadata.getSite(), metadata.getUser(),
"test-release"
111 f = ROOT.TFile(output,
"UPDATE")
112 t = ROOT.TTree(
"persistent",
"persistent")
113 t.Branch(
"FileMetaData", metadata)
119def merge_files(*args, output="output.root", filter_modified=False):
120 """run the merging tool on all passed files
123 output: name of the output file
124 filter_modified: if True omit warnings that the release
is modified
and
125 consistency cannot be checked
127 process = subprocess.run(
128 ["b2file-merge",
"-q", output] + list(args), stdout=subprocess.PIPE
133 process.stdout = re.sub(
134 rb
"^\[WARNING\] File \"(.*?)\" created with modified software ([a-zA-Z0-9\-+]*?): "
135 rb
"cannot verify that files are compatible\n",
142 sys.stdout.buffer.write(process.stdout)
143 sys.stdout.buffer.flush()
145 return process.returncode
149testfile_steering =
"""
153basf2.set_log_level(basf2.LogLevel.ERROR)
154if "BELLE2_GLOBALTAG" in os.environ:
155 basf2.conditions.override_globaltags([os.environ["BELLE2_GLOBALTAG"]])
156if "BELLE2_SEED" in os.environ:
157 basf2.set_random_seed(os.environ[
"BELLE2_SEED"])
158main = basf2.create_path()
159main.add_module(
"EventInfoSetter")
160main.add_module(
"ParticleGun")
161main.add_module(
"RootOutput", branchNames=sys.argv[1:])
166## Minimal steering file to create output ntuples we can merge
167testfile_ntuple_steering = """
170basf2.set_log_level(basf2.LogLevel.ERROR)
171main = basf2.create_path()
172main.add_module(
'RootInput')
173main.add_module(
'VariablesToNtuple',
176main.add_module(
'VariablesToNtuple',
183def check_01_existing():
184 """Check that merging a non existing file fails"""
185 create_testfile_direct(
"test2.root")
189def check_02_nonroot():
190 """Check that merging fails on non-root input files"""
191 with open(
"test1.root",
"w")
as f:
192 f.write(
"This is not a ROOT file")
196def check_03_overwrite():
197 """Check that overwriting fails if -f is missing"""
198 create_testfile_direct(
"test1.root")
199 with open(
"output.root",
"w")
as f:
204def check_04_access():
205 """Check that it fails if we cannot create output file"""
206 create_testfile_direct(
"test1.root")
207 return merge_files(
"test1.root", output=
"/nosuchdir/foo") != 0
210def check_05_release():
211 """Check that it fails if the releases are different"""
212 create_testfile_direct(
"test1.root")
213 create_testfile_direct(
"test2.root", release=
"other_release")
214 return merge_files(
"test1.root",
"test2.root") != 0
217def check_06_empty_release():
218 """Check that merging fails with empty release values"""
219 create_testfile_direct(
"test1.root")
220 create_testfile_direct(
"test2.root", release=
"")
221 return merge_files(
"test1.root",
"test2.root") != 0
224def check_07_modified_release():
225 """Check that merging modified release gives warning about that but merging should work"""
226 create_testfile_direct(
"test1.root", release=
"test_release")
227 create_testfile_direct(
"test2.root", release=
"test_release-modified")
228 return merge_files(
"test1.root",
"test2.root") == 0
231def check_08_duplicate_seed():
232 """Check that we get a warning for identical seeds but merging should work"""
233 create_testfile_direct(
"test1.root", seed=
"seed1")
234 create_testfile_direct(
"test2.root", seed=
"seed1")
235 return merge_files(
"test1.root",
"test2.root") == 0
238def check_09_different_steering():
239 """Check that merging fails if the steering file is different"""
240 create_testfile_direct(
"test1.root")
241 create_testfile_direct(
"test2.root", steering=
"my other steering")
242 return merge_files(
"test1.root",
"test2.root") != 0
245def check_10_different_globaltag():
246 """Check that merging fails if the global tag is different"""
247 create_testfile_direct(
"test1.root")
248 create_testfile_direct(
"test2.root", global_tag=
"other_globaltag")
249 return merge_files(
"test1.root",
"test2.root") != 0
252def check_11_branches():
253 """Check that merging fails if the branches in the event tree are different"""
254 create_testfile(
"test1.root")
255 create_testfile(
"test2.root", branchNames=[
"EventMetaData"])
256 return merge_files(
"test1.root",
"test2.root", filter_modified=
True) != 0
259def check_12_hadded():
260 """Check that merging fails if the file has more then one entry in the persistent tree"""
261 create_testfile_direct(
"test1.root")
262 subprocess.call([
"hadd",
"test11.root",
"test1.root",
"test1.root"])
266def check_13_nopersistent():
267 """Check that merging fails without persistent tree"""
268 f = ROOT.TFile(
"test1.root",
"RECREATE")
269 t = ROOT.TTree(
"tree",
"tree")
275def check_14_noeventtree():
276 """Check that merging fails without event tree"""
277 f = ROOT.TFile(
"test1.root",
"RECREATE")
278 t = ROOT.TTree(
"persistent",
"persistent")
279 meta = FileMetaData()
280 t.Branch(
"FileMetaData", meta)
287def check_15_noeventbranches():
288 """Check that merging fails without event tree"""
289 f = ROOT.TFile(
"test1.root",
"RECREATE")
290 t = ROOT.TTree(
"persistent",
"persistent")
291 meta = FileMetaData()
292 meta.setCreationData(
"date",
"site",
"user",
"release")
293 t.Branch(
"FileMetaData", meta)
296 t = ROOT.TTree(
"tree",
"tree")
302def check_16_nonmergeable():
303 """Check that merging fails if there are multiple mergeable persistent trees"""
304 f = ROOT.TFile(
"test1.root",
"RECREATE")
305 t = ROOT.TTree(
"persistent",
"persistent")
306 meta = FileMetaData()
307 meta.setCreationData(
"date",
"site",
"user",
"release")
308 t.Branch(
"FileMetaData", meta)
309 t.Branch(
"AnotherMetaData", meta)
312 t = ROOT.TTree(
"tree",
"tree")
313 t.Branch(
"EventMetaData", meta)
320def check_17_checkparentLFN():
321 """Check that parent LFN get merged correctly"""
322 parents = [(
"a",
"b",
"c"), (
"a",
"c",
"d")]
325 lfn1 = ROOT.std.vector(
"std::string")()
326 lfn2 = ROOT.std.vector(
"std::string")()
333 m1.setRandomSeed(
"1")
334 m2.setRandomSeed(
"2")
335 create_testfile_direct(
"test1.root", m1)
336 create_testfile_direct(
"test2.root", m2)
338 meta = basf2.get_file_metadata(
"output.root")
339 should_be = [e
for e
in sorted(set(parents[0] + parents[1]))]
340 is_actual = [meta.getParent(i)
for i
in range(meta.getNParents())]
341 return should_be == is_actual
344def check_18_checkEventNr():
345 """Check that event and mc numbers are summed correctly"""
346 evtNr = [10, 1243, 232, 1272, 25]
347 evtNrFullEvents = [i - 1
for i
in evtNr]
348 mcNr = [120, 821, 23, 923, 1]
350 for i, (e, f, m)
in enumerate(zip(evtNr, evtNrFullEvents, mcNr)):
351 meta = FileMetaData()
353 meta.setNFullEvents(f)
355 meta.setRandomSeed(str(i))
356 files.append(f
"test{i}.root")
357 create_testfile_direct(files[-1], meta)
359 meta = basf2.get_file_metadata(
"output.root")
361 sum(evtNr) == meta.getNEvents()
362 and sum(evtNrFullEvents) == meta.getNFullEvents()
363 and sum(mcNr) == meta.getMcEvents()
367def check_19_lowhigh():
368 """Check that the low/high event numbers are merged correctly"""
378 for i, e
in enumerate(lowhigh):
379 meta = FileMetaData()
380 meta.setNEvents(0
if e == (-1, -1, 0)
else 1)
381 meta.setNFullEvents(0
if e == (-1, -1, 0)
else 1)
382 meta.setRandomSeed(str(i))
383 meta.setLow(e[0], e[1], e[2])
384 meta.setHigh(e[0], e[1], e[2])
385 files.append(f
"test{i}.root")
386 create_testfile_direct(files[-1], meta)
390 indices = range(len(files))
391 tests = list(itertools.permutations(indices, 2)) + [indices]
392 for indices
in tests:
393 low = min(lowhigh[i]
for i
in indices
if lowhigh[i] != (-1, -1, 0))
394 high = max(lowhigh[i]
for i
in indices
if lowhigh[i] != (-1, -1, 0))
395 if merge_files(
"-f",
"--no-catalog", *(files[i]
for i
in indices)) != 0:
397 meta = basf2.get_file_metadata(
"output.root")
399 meta.getExperimentLow() != low[0]
400 or meta.getRunLow() != low[1]
401 or meta.getEventLow() != low[2]
403 print(
"low event should be", low)
407 meta.getExperimentHigh() != high[0]
408 or meta.getRunHigh() != high[1]
409 or meta.getEventHigh() != high[2]
411 print(
"high event should be", high)
417def check_20_test_file():
418 """Check that a merged file passes the b2file-check program"""
419 create_testfile(
"test1.root", events=1111)
420 create_testfile(
"test2.root", events=123)
421 merge_files(
"test1.root",
"test2.root", filter_modified=
True)
439def check_21_eventmetadata():
440 """Check that merged files has all the correct even infos"""
442 "test1.root", run=0, events=100, BELLE2_SEED=
"test1", BELLE2_USER=
"user1"
445 "test2.root", run=1, events=100, BELLE2_SEED=
"test2", BELLE2_USER=
"user2"
447 merge_files(
"test1.root",
"test2.root",
"test1.root", filter_modified=
True)
448 out = ROOT.TFile(
"output.root")
449 events = out.Get(
"tree")
450 entries = events.GetEntriesFast()
455 eventcount = {(0, 0, i + 1): 2
for i
in range(100)}
456 eventcount.update({(0, 1, i + 1): 1
for i
in range(100)})
457 for i
in range(entries):
459 e = events.EventMetaData
460 eventcount[(e.getExperiment(), e.getRun(), e.getEvent())] -= 1
461 return max(eventcount.values()) == 0
and min(eventcount.values()) == 0
464def check_22_real_mc():
465 """Check that merging fails if real and MC data are mixed"""
466 create_testfile_direct(
"test1.root")
467 copyfile(basf2.find_file(
"framework/tests/fake_real.root"),
"test2.root")
468 return merge_files(
"test1.root",
"test2.root") != 0
471def check_23_legacy_ip():
472 """Check that we can merge if the Legacy_IP_Information is inconsistent"""
473 create_testfile_direct(
"test1.root", global_tag=
"test_globaltag")
474 create_testfile_direct(
475 "test2.root", global_tag=
"test_globaltag,Legacy_IP_Information"
479 meta = basf2.get_file_metadata(
"output.root")
480 return meta.getDatabaseGlobalTag() ==
"test_globaltag"
483def check_24_legacy_ip_middle():
484 """Check that we can merge if the Legacy_IP_Information is inconsistent"""
485 create_testfile_direct(
"test1.root", global_tag=
"test_globaltag,other")
486 create_testfile_direct(
487 "test2.root", global_tag=
"test_globaltag,Legacy_IP_Information,other"
491 meta = basf2.get_file_metadata(
"output.root")
492 return meta.getDatabaseGlobalTag() ==
"test_globaltag,other"
495def check_25_legacy_ip_only():
496 """Check that we can merge if the Legacy_IP_Information is inconsistent"""
497 create_testfile_direct(
"test1.root", global_tag=
"")
498 create_testfile_direct(
"test2.root", global_tag=
"Legacy_IP_Information")
501 meta = basf2.get_file_metadata(
"output.root")
502 return meta.getDatabaseGlobalTag() ==
""
505def check_26_ntuple_merge():
506 """Check that we can merge two ntuple output files"""
507 create_testfile(
"test1.root", exp=1, run=2, events=111)
508 create_testfile(
"test2.root", exp=1, run=2, events=123)
509 create_testfile_ntuple(input=
"test1.root", output=
"ntuple1.root")
510 create_testfile_ntuple(input=
"test2.root", output=
"ntuple2.root")
511 return merge_files(
"ntuple1.root",
"ntuple2.root") == 0
514def check_27_ntuple_trees():
515 """Check that ntuple merge fails if the tree names are different"""
516 create_testfile(
"test1.root")
517 create_testfile(
"test2.root")
518 create_testfile_ntuple(input=
"test1.root", output=
"ntuple1.root")
519 create_testfile_ntuple(
520 input=
"test2.root", output=
"ntuple2.root", treeNames=[
"differentTree",
"tree"]
522 return merge_files(
"ntuple1.root",
"ntuple2.root") != 0
525def check_28_streaming():
526 """Check if we can merge streamed input files"""
528 input_file =
'https://github.com/belle2/basf2/raw/refs/heads/main/mdst/tests/mdst-v09-00-00.root'
532def check_29_parent_release():
533 """Check that merging files does not modify the release version in the metadata."""
534 create_testfile_direct(
"test1.root", release=
"abcd")
535 create_testfile_direct(
"test2.root", release=
"abcd")
536 merge_files(
"test1.root",
"test2.root", output=
"output.root")
537 meta = basf2.get_file_metadata(
"output.root")
538 return meta.getRelease() ==
"abcd"
541def check_XX_filemetaversion():
542 """Check that the Version of the FileMetaData hasn't changed.
543 If this check fails please check that the changes to FileMetaData don't affect b2file-merge and adapt the correct version number here.
"""
544 return FileMetaData.Class().GetClassVersion() == 11
547if __name__ ==
"__main__":
550 existing = [e
for e
in sorted(globals().items())
if e[0].startswith(
"check_")]
551 for name, fcn
in existing:
552 print(f
"running {name}: {fcn.__doc__}")
553 with clean_working_directory():
555 print(f
"{name} failed")
558 print(f
"{name} passed")