From 830e1b3a0b38b4693b9dcf551cf5fa8df9f970be Mon Sep 17 00:00:00 2001 From: Jakob Blomer Date: Tue, 20 Jan 2026 16:48:20 +0100 Subject: [PATCH] Add test for feature flags --- structure/feature_flag/README.md | 6 ++++ structure/feature_flag/read.C | 6 ++++ structure/feature_flag/write.C | 49 ++++++++++++++++++++++++++++++++ structure/read_structure.hxx | 17 +++++++++-- 4 files changed, 76 insertions(+), 2 deletions(-) create mode 100644 structure/feature_flag/README.md create mode 100644 structure/feature_flag/read.C create mode 100644 structure/feature_flag/write.C diff --git a/structure/feature_flag/README.md b/structure/feature_flag/README.md new file mode 100644 index 0000000..9729564 --- /dev/null +++ b/structure/feature_flag/README.md @@ -0,0 +1,6 @@ +# Unknown feature flag set + +An empty RNTuple with feature flag 137 (>62) set in the header. Reading should fail accordingly. +The error message should indicate the feature flag. + +A later version of this test should set the flag in the footer. diff --git a/structure/feature_flag/read.C b/structure/feature_flag/read.C new file mode 100644 index 0000000..0541a0c --- /dev/null +++ b/structure/feature_flag/read.C @@ -0,0 +1,6 @@ +#include "../read_structure.hxx" + +void read(std::string_view input = "structure.feature_flag.root", + std::string_view output = "structure.feature_flag.json") { + read_structure(input, output); +} diff --git a/structure/feature_flag/write.C b/structure/feature_flag/write.C new file mode 100644 index 0000000..907451a --- /dev/null +++ b/structure/feature_flag/write.C @@ -0,0 +1,49 @@ +#include +#include +#include +#include +#include + +#include +#include +#include + +using ROOT::Experimental::RFieldZero; +using ROOT::Experimental::RNTupleDescriptor; +using ROOT::Experimental::RNTupleWriteOptions; +using ROOT::Experimental::Internal::RFieldDescriptorBuilder; +using ROOT::Experimental::Internal::RNTupleDescriptorBuilder; +using ROOT::Experimental::Internal::RNTupleFileWriter; +using ROOT::Experimental::Internal::RNTupleSerializer; + +void write(std::string_view filename = "structure.feature_flag.root") { + // Note that we are writing a file with a so-far unused feature flag. This cannot use the regular + // production API but we have to use the internal, low-level classes to create the file. + + RNTupleDescriptorBuilder descBuilder; + // The following line will be required as of ROOT v6.36 + // descBuilder.SetVersionForWriting(); + descBuilder.SetNTuple("ntpl", ""); + descBuilder.SetFeature(RNTupleDescriptor::kFeatureFlagTest); + descBuilder.AddField(RFieldDescriptorBuilder::FromField(RFieldZero()).FieldId(0).MakeDescriptor().Unwrap()); + + RNTupleWriteOptions options; + auto writer = + RNTupleFileWriter::Recreate("ntpl", filename, RNTupleFileWriter::EContainerFormat::kTFile, RNTupleWriteOptions()); + + RNTupleSerializer serializer; + + auto ctx = serializer.SerializeHeader(nullptr, descBuilder.GetDescriptor()); + auto buffer = std::make_unique(ctx.GetHeaderSize()); + ctx = serializer.SerializeHeader(buffer.get(), descBuilder.GetDescriptor()); + writer->WriteNTupleHeader(buffer.get(), ctx.GetHeaderSize(), ctx.GetHeaderSize()); + + auto szFooter = serializer.SerializeFooter(nullptr, descBuilder.GetDescriptor(), ctx); + buffer = std::make_unique(szFooter); + serializer.SerializeFooter(buffer.get(), descBuilder.GetDescriptor(), ctx); + writer->WriteNTupleFooter(buffer.get(), szFooter, szFooter); + + writer->Commit(); + // Call destructor to flush data to disk + writer.reset(); +} diff --git a/structure/read_structure.hxx b/structure/read_structure.hxx index 5bf7091..dff1d2c 100644 --- a/structure/read_structure.hxx +++ b/structure/read_structure.hxx @@ -1,6 +1,8 @@ #include +#include #include +using ROOT::Experimental::RException; using ROOT::Experimental::RNTupleReader; #include @@ -11,9 +13,20 @@ using ROOT::Experimental::RNTupleReader; void read_structure(std::string_view input, std::string_view output) { std::ofstream os(std::string{output}); - os << "[\n"; - auto reader = RNTupleReader::Open("ntpl", input); + std::unique_ptr reader; + try { + reader = RNTupleReader::Open("ntpl", input); + } catch (const RException &e) { + std::string msgWithoutStacktrace; + std::getline(std::istringstream(e.what()), msgWithoutStacktrace); + os << "{\n"; + os << " \"error\": \"" << msgWithoutStacktrace << "\"\n"; + os << "}\n"; + return; + } + + os << "[\n"; auto Int32 = reader->GetModel().GetDefaultEntry().GetPtr("Int32"); bool first = true;