Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions io/io/src/TFileMerger.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -778,7 +778,7 @@ Bool_t TFileMerger::MergeOne(TDirectory *target, TList *sourcelist, Int_t type,
// and we are in incremental mode (because it will be reused
// and has not been written to disk (for performance reason).
// coverity[var_deref_model] the IsA()->InheritsFrom guarantees that the dynamic_cast will succeed.
if (ownobj && (!(type & kIncremental) || dirobj->GetFile() != target)) {
if (ownobj && (!(type & kIncremental) || dirobj->GetFile() != target->GetFile())) {
dirobj->ResetBit(kMustCleanup);
delete dirobj;
}
Expand Down Expand Up @@ -1116,4 +1116,3 @@ void TFileMerger::SetMsgPrefix(const char *prefix)
{
fMsgPrefix = prefix;
}

103 changes: 101 additions & 2 deletions io/io/test/TFileMergerTests.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "TMemFile.h"
#include "TTree.h"
#include "TH1.h"
#include "TSystem.h"

static void CreateATuple(TMemFile &file, const char *name, double value)
{
Expand Down Expand Up @@ -84,12 +85,12 @@ TEST(TFileMerger, MergeSingleOnlyListed)
hist3->Fill(1); hist3->Fill(1); hist3->Fill(1);
hist4->Fill(1); hist4->Fill(1); hist4->Fill(1); hist4->Fill(1);
a.Write();

TFileMerger merger;
auto output = std::unique_ptr<TFile>(new TFile("SingleOnlyListed.root", "RECREATE"));
bool success = merger.OutputFile(std::move(output));
ASSERT_TRUE(success);

merger.AddObjectNames("hist1");
merger.AddObjectNames("hist2");
merger.AddFile(&a, false);
Expand All @@ -101,3 +102,101 @@ TEST(TFileMerger, MergeSingleOnlyListed)
ASSERT_TRUE(output.get() && output->GetListOfKeys());
EXPECT_EQ(output->GetListOfKeys()->GetSize(), 2);
}

// Regression test for https://github.com/root-project/root/issues/23666
TEST(TFileMerger, NestedDirMaxOpenedFiles)
{
struct CleanupRAII {
std::vector<std::string> fFiles;
~CleanupRAII()
{
for (const auto &file : fFiles) {
gSystem->Unlink(file.c_str());
}
}
} cleanup;

const std::string file1 = "test_nested_merge_in1.root";
const std::string file2 = "test_nested_merge_in2.root";
const std::string file3 = "test_nested_merge_in3.root";
const std::string fileOut = "test_nested_merge_out.root";

cleanup.fFiles.push_back(file1);
cleanup.fFiles.push_back(file2);
cleanup.fFiles.push_back(file3);
cleanup.fFiles.push_back(fileOut);

const std::vector<std::pair<std::string, int>> inputs = {{file1, 5}, {file2, 7}, {file3, 11}};

// Create 3 input files with objects inside a nested directory (depth >= 2)
for (const auto &input : inputs) {
auto f = std::make_unique<TFile>(input.first.c_str(), "RECREATE");
ASSERT_FALSE(f->IsZombie());
auto topDir = f->mkdir("nested");
ASSERT_NE(topDir, nullptr);
auto subDir = topDir->mkdir("dir");
ASSERT_NE(subDir, nullptr);
subDir->cd();

auto tree = std::make_unique<TTree>("tree", "Test Tree");
tree->SetDirectory(subDir);

int val = 0;
tree->Branch("val", &val, "val/I");
for (int i = 0; i < input.second; ++i) {
val = i;
tree->Fill();
}

auto hist = std::make_unique<TH1F>("hist", "Test Hist", 10, 0, 10);
hist->SetDirectory(subDir);

for (int i = 0; i < input.second; ++i) {
hist->Fill(i % 10);
}

f->Write();
}

// Merge the input files into the output file. The original issue was triggered by the use of the `-n` parameter in
// `hadd`, which opens at most `N` files at once, set to the same number of input files (3). The merger used to only
// merge data from the first two files leaving out data from the third.
{
TFileMerger merger{false};
EXPECT_TRUE(merger.OutputFile(fileOut.c_str()));
merger.SetMaxOpenedFiles(3);
merger.AddFile(file1.c_str(), /*cpProgress*/ false);
merger.AddFile(file2.c_str(), /*cpProgress*/ false);
merger.AddFile(file3.c_str(), /*cpProgress*/ false);
EXPECT_TRUE(merger.Merge());
}

// Verify correct values for merged objects in the output file
{
auto fOut = std::make_unique<TFile>(fileOut.c_str(), "READ");
ASSERT_FALSE(fOut->IsZombie());

auto topDir = fOut->Get<TDirectory>("nested");
ASSERT_NE(topDir, nullptr);
auto subDir = topDir->Get<TDirectory>("dir");
ASSERT_NE(subDir, nullptr);

auto tree = subDir->Get<TTree>("tree");
ASSERT_NE(tree, nullptr);
EXPECT_EQ(tree->GetEntries(), 5 + 7 + 11);
int val = 0;
tree->SetBranchAddress("val", &val);
int entryCursor{};
for (const auto &input : inputs) {
for (int i = 0; i < input.second; ++i) {
tree->GetEntry(entryCursor);
EXPECT_EQ(val, i);
entryCursor++;
}
}

auto hist = subDir->Get<TH1F>("hist");
ASSERT_NE(hist, nullptr);
EXPECT_EQ(hist->GetEntries(), 5 + 7 + 11);
}
}
Loading