diff --git a/tree/ntuple/test/ntuple_processor.cxx b/tree/ntuple/test/ntuple_processor.cxx index e96c6a6b3f9c6..42ffc6dc69d10 100644 --- a/tree/ntuple/test/ntuple_processor.cxx +++ b/tree/ntuple/test/ntuple_processor.cxx @@ -798,3 +798,157 @@ TEST_F(RNTupleProcessorTest, PrintStructureJoinedChainAsymmetric) " +-----------------------------+\n"; EXPECT_EQ(exp2, os2.str()); } + +// This test is a translation using RNTupleProcessor of the test +// introduced by https://github.com/root-project/root/pull/19322, +// to ensure that the TTree friendship mechanism works equivalently +// with the RNTuple join mechanism. +class GH16805ProcessorTest : public testing::Test { +protected: + const std::vector fStepZeroFiles{ + "gh16805_rntuple_stepzero_0.root", + "gh16805_rntuple_stepzero_1.root" + }; + + const std::vector fFriendFiles{ + "gh16805_rntuple_friend_0.root", + "gh16805_rntuple_friend_1.root", + "gh16805_rntuple_friend_2.root" + }; + + const std::string fStepOneFile = "gh16805_rntuple_stepone.root"; + + void WriteStepZero(const std::string &fileName, int begin, int end) + { + auto model = ROOT::RNTupleModel::Create(); + + auto br1 = model->MakeField("stepZeroBr1"); + auto br2 = model->MakeField("stepZeroBr2"); + + auto writer = ROOT::RNTupleWriter::Recreate(std::move(model), "stepzero", fileName); + + for (int i = begin; i < end; ++i) { + *br1 = i; + *br2 = 2 * i; + writer->Fill(); + } + } + + void WriteStepOne(const std::string &fileName, int begin, int end) + { + auto model = ROOT::RNTupleModel::Create(); + + auto br1 = model->MakeField("stepOneBr1"); + + auto writer = ROOT::RNTupleWriter::Recreate(std::move(model), "stepone", fileName); + + for (int i = begin; i < end; ++i) { + *br1 = i; + writer->Fill(); + } + } + + void WriteFriend(const std::string &fileName, int begin, int end) + { + auto model = ROOT::RNTupleModel::Create(); + + auto br1 = model->MakeField("friendBr1"); + auto br2 = model->MakeField("friendBr2"); + + auto writer = ROOT::RNTupleWriter::Recreate(std::move(model), "topLevelFriend", fileName); + + for (int i = begin; i < end; ++i) { + *br1 = i; + *br2 = 2 * i; + writer->Fill(); + } + } + + void SetUp() override + { + WriteStepZero(fStepZeroFiles[0], 0, 10); + WriteStepZero(fStepZeroFiles[1], 10, 20); + + WriteFriend(fFriendFiles[0], 200, 207); + WriteFriend(fFriendFiles[1], 207, 214); + WriteFriend(fFriendFiles[2], 214, 220); + + WriteStepOne(fStepOneFile, 100, 120); + } + + void TearDown() override + { + for (const auto &f : fStepZeroFiles) + std::remove(f.c_str()); + + for (const auto &f : fFriendFiles) + std::remove(f.c_str()); + + std::remove(fStepOneFile.c_str()); + } +}; + +TEST_F(GH16805ProcessorTest, JoinReading) +{ + std::vector stepOneSpecs{ + {"stepone", fStepOneFile} + }; + + std::vector stepZeroSpecs{ + {"stepzero", fStepZeroFiles[0]}, + {"stepzero", fStepZeroFiles[1]} + }; + + std::vector friendSpecs{ + {"topLevelFriend", fFriendFiles[0]}, + {"topLevelFriend", fFriendFiles[1]}, + {"topLevelFriend", fFriendFiles[2]} + }; + + auto stepOneProc = + RNTupleProcessor::CreateChain(stepOneSpecs, "stepone"); + + auto stepZeroProc = + RNTupleProcessor::CreateChain(stepZeroSpecs, "stepzero"); + + auto friendProc = + RNTupleProcessor::CreateChain(friendSpecs, "topLevelFriend"); + + auto joinedWithFriend = + RNTupleProcessor::CreateJoin( + std::move(stepOneProc), + std::move(friendProc), + {} + ); + + auto joinedAll = + RNTupleProcessor::CreateJoin( + std::move(joinedWithFriend), + std::move(stepZeroProc), + {} + ); + + auto stepOneBr1 = joinedAll->RequestField("stepOneBr1"); + auto friendBr1 = joinedAll->RequestField("topLevelFriend.friendBr1"); + auto friendBr2 = joinedAll->RequestField("topLevelFriend.friendBr2"); + auto stepZeroBr1 = joinedAll->RequestField("stepzero.stepZeroBr1"); + auto stepZeroBr2 = joinedAll->RequestField("stepzero.stepZeroBr2"); + + std::size_t i = 0; + + for (auto idx : *joinedAll) { + EXPECT_EQ(i, idx); + + EXPECT_EQ(static_cast(i), *stepZeroBr1); + EXPECT_EQ(static_cast(2 * i), *stepZeroBr2); + EXPECT_EQ(static_cast(100 + i), *stepOneBr1); + EXPECT_EQ(static_cast(200 + i), *friendBr1); + EXPECT_EQ(static_cast(2 * (200 + i)), *friendBr2); + + ++i; + } + + EXPECT_EQ(20u, i); + EXPECT_EQ(20u, joinedAll->GetNEntriesProcessed()); +} +