@@ -1720,6 +1720,76 @@ TEST(RNTupleMerger, MergeProjectedFieldsOnlySecond)
17201720 }
17211721}
17221722
1723+ TEST (RNTupleMerger, MergeProjectedFieldsDifferent)
1724+ {
1725+ // Merge two files where both the first and the second have a projection with the same name, but different source.
1726+ // Should refuse to merge.
1727+ FileRaii fileGuard1 (" test_ntuple_merge_proj_diff_in_1.root" );
1728+ {
1729+ auto model = RNTupleModel::Create ();
1730+ auto fieldFoo = model->MakeField <std::vector<CustomStruct>>(" foo" );
1731+ auto fieldBar = model->MakeField <std::vector<CustomStruct>>(" bar" );
1732+ auto projBaz = RFieldBase::Create (" baz" , " std::vector<CustomStruct>" ).Unwrap ();
1733+ const auto mapping = [](const std::string &name) {
1734+ std::string replaced = name;
1735+ replaced.replace (0 , 3 , " bar" );
1736+ return replaced;
1737+ };
1738+ model->AddProjectedField (std::move (projBaz), mapping);
1739+ auto ntuple = RNTupleWriter::Recreate (std::move (model), " ntuple" , fileGuard1.GetPath ());
1740+ for (int i = 0 ; i < 10 ; ++i) {
1741+ CustomStruct s;
1742+ s.v1 .push_back (i);
1743+ s.s = std::to_string (i);
1744+ *fieldFoo = {s};
1745+ ntuple->Fill ();
1746+ }
1747+ }
1748+ FileRaii fileGuard2 (" test_ntuple_merge_proj_diff_in_2.root" );
1749+ {
1750+ auto model = RNTupleModel::Create ();
1751+ auto fieldFoo = model->MakeField <std::vector<CustomStruct>>(" foo" );
1752+ auto fieldBar = model->MakeField <std::vector<CustomStruct>>(" bar" );
1753+ auto projBaz = RFieldBase::Create (" baz" , " std::vector<CustomStruct>" ).Unwrap ();
1754+ const auto mapping = [](const std::string &name) {
1755+ std::string replaced = name;
1756+ replaced.replace (0 , 3 , " foo" );
1757+ return replaced;
1758+ };
1759+ model->AddProjectedField (std::move (projBaz), mapping);
1760+ auto ntuple = RNTupleWriter::Recreate (std::move (model), " ntuple" , fileGuard2.GetPath ());
1761+ for (int i = 0 ; i < 10 ; ++i) {
1762+ CustomStruct s;
1763+ s.v2 .push_back ({(float )i});
1764+ s.b = static_cast <std::byte>(i);
1765+ ntuple->Fill ();
1766+ }
1767+ }
1768+
1769+ {
1770+ // Gather the input sources
1771+ std::vector<std::unique_ptr<RPageSource>> sources;
1772+ sources.push_back (RPageSource::Create (" ntuple" , fileGuard1.GetPath (), RNTupleReadOptions ()));
1773+ sources.push_back (RPageSource::Create (" ntuple" , fileGuard2.GetPath (), RNTupleReadOptions ()));
1774+ std::vector<RPageSource *> sourcePtrs;
1775+ for (const auto &s : sources) {
1776+ sourcePtrs.push_back (s.get ());
1777+ }
1778+
1779+ // Now merge the inputs
1780+ for (const auto mmode : {ENTupleMergingMode::kFilter , ENTupleMergingMode::kStrict , ENTupleMergingMode::kUnion }) {
1781+ FileRaii fileGuardOut (" test_ntuple_merge_proj_diff_out.root" );
1782+ auto destination = std::make_unique<RPageSinkFile>(" ntuple" , fileGuardOut.GetPath (), RNTupleWriteOptions ());
1783+ RNTupleMerger merger{std::move (destination)};
1784+ RNTupleMergeOptions opts;
1785+ opts.fMergingMode = mmode;
1786+ auto res = merger.Merge (sourcePtrs, opts);
1787+ ASSERT_FALSE (bool (res));
1788+ EXPECT_THAT (res.GetError ()->GetReport (), testing::HasSubstr (" projected to a different field" ));
1789+ }
1790+ }
1791+ }
1792+
17231793struct RNTupleMergerCheckEncoding : public ::testing::TestWithParam<std::tuple<int , int , int , bool >> {};
17241794
17251795TEST_P (RNTupleMergerCheckEncoding, CorrectEncoding)
0 commit comments