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:]) 
  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 
  544    affect b2file-merge and adapt the correct version number here.""" 
  545    return FileMetaData.Class().GetClassVersion() == 11
 
  548if __name__ == 
"__main__":
 
  551    existing = [e 
for e 
in sorted(globals().items()) 
if e[0].startswith(
"check_")]
 
  552    for name, fcn 
in existing:
 
  553        print(f
"running {name}: {fcn.__doc__}")
 
  554        with clean_working_directory():
 
  556                print(f
"{name} failed")
 
  559                print(f
"{name} passed")