From 500709e9578b64fadae56a9a9c47146e3c2454f8 Mon Sep 17 00:00:00 2001 From: Andrew Olivier Date: Tue, 3 Mar 2026 16:57:42 -0600 Subject: [PATCH 1/3] First working version of schema evolution test for FORM. Tests that FORM's internal interface can write a data product, add a simple field to that data product, read it back, and print the same fields from both. Really tests ROOT's schema evolution facilities for now. This will become more relevant for RNTuple and other formats, and it may even cover future advanced schema evolution features in FORM. --- test/form/CMakeLists.txt | 46 +++++++++++++++++ test/form/data_products/CMakeLists.txt | 2 + .../data_products/extra_member/CMakeLists.txt | 11 +++++ .../extra_member/classes_def.xml | 4 ++ test/form/data_products/extra_member/dict.h | 2 + .../extra_member/track_start.cpp | 49 +++++++++++++++++++ .../extra_member/track_start.hpp | 39 +++++++++++++++ test/form/form_schema_read_test.cpp | 24 +++++++++ test/form/form_schema_write_test.cpp | 29 +++++++++++ 9 files changed, 206 insertions(+) create mode 100644 test/form/data_products/extra_member/CMakeLists.txt create mode 100644 test/form/data_products/extra_member/classes_def.xml create mode 100644 test/form/data_products/extra_member/dict.h create mode 100644 test/form/data_products/extra_member/track_start.cpp create mode 100644 test/form/data_products/extra_member/track_start.hpp create mode 100644 test/form/form_schema_read_test.cpp create mode 100644 test/form/form_schema_write_test.cpp diff --git a/test/form/CMakeLists.txt b/test/form/CMakeLists.txt index d126e9ed5..330731bf1 100644 --- a/test/form/CMakeLists.txt +++ b/test/form/CMakeLists.txt @@ -31,6 +31,52 @@ if(FORM_USE_ROOT_STORAGE) WriteVector ) target_include_directories(ReadVector PRIVATE ${PROJECT_SOURCE_DIR}/form) + + cet_test( + form_schema_write_test + SOURCE + form_schema_write_test.cpp + toy_tracker.cpp + LIBRARIES + form + form_test_data_products + TEST_WORKDIR + form_schema_test + ) + target_include_directories(form_schema_write_test PRIVATE ${PROJECT_SOURCE_DIR}/form) + + cet_test( + form_schema_read_test + SOURCE + form_schema_read_test.cpp + LIBRARIES + form + form_test_data_products_schema_evolution + DIRTY_WORKDIR + TEST_WORKDIR + form_schema_test + REQUIRED_FIXTURES + form_schema_write_test + ) + target_include_directories(form_schema_read_test PRIVATE ${PROJECT_SOURCE_DIR}/form) + + cet_test( + check_schema_evolution + HANDBUILT + TEST_EXEC + ${CMAKE_COMMAND} + TEST_ARGS + -E + compare_files + form_schema_write_log.txt + form_schema_read_log.txt + DIRTY_WORKDIR + TEST_WORKDIR + form_schema_test + REQUIRED_FIXTURES + form_schema_write_test + form_schema_read_test + ) endif() cet_test( diff --git a/test/form/data_products/CMakeLists.txt b/test/form/data_products/CMakeLists.txt index 405951815..3bf154308 100644 --- a/test/form/data_products/CMakeLists.txt +++ b/test/form/data_products/CMakeLists.txt @@ -8,4 +8,6 @@ if(FORM_USE_ROOT_STORAGE) ${FORM_DATA_PROD_LIB_NAME} dict.h SELECTION classes_def.xml ) target_link_libraries(${FORM_DATA_PROD_LIB_NAME} PRIVATE ROOT::RIO) + + add_subdirectory(extra_member) endif() diff --git a/test/form/data_products/extra_member/CMakeLists.txt b/test/form/data_products/extra_member/CMakeLists.txt new file mode 100644 index 000000000..b422a196f --- /dev/null +++ b/test/form/data_products/extra_member/CMakeLists.txt @@ -0,0 +1,11 @@ +set(FORM_EXTRA_DATA_PROD_LIB_NAME "form_test_data_products_schema_evolution") +add_library(${FORM_EXTRA_DATA_PROD_LIB_NAME} SHARED track_start.cpp) + +if(FORM_USE_ROOT_STORAGE) + find_package(ROOT REQUIRED COMPONENTS RIO) + include_directories(${ROOT_INCLUDE_DIRS}) + reflex_generate_dictionary( + ${FORM_EXTRA_DATA_PROD_LIB_NAME} dict.h SELECTION classes_def.xml + ) + target_link_libraries(${FORM_EXTRA_DATA_PROD_LIB_NAME} PRIVATE ROOT::RIO) +endif() diff --git a/test/form/data_products/extra_member/classes_def.xml b/test/form/data_products/extra_member/classes_def.xml new file mode 100644 index 000000000..249f2d09b --- /dev/null +++ b/test/form/data_products/extra_member/classes_def.xml @@ -0,0 +1,4 @@ + + + + diff --git a/test/form/data_products/extra_member/dict.h b/test/form/data_products/extra_member/dict.h new file mode 100644 index 000000000..42162db0c --- /dev/null +++ b/test/form/data_products/extra_member/dict.h @@ -0,0 +1,2 @@ +#include "track_start.hpp" +#include diff --git a/test/form/data_products/extra_member/track_start.cpp b/test/form/data_products/extra_member/track_start.cpp new file mode 100644 index 000000000..4ebe3511a --- /dev/null +++ b/test/form/data_products/extra_member/track_start.cpp @@ -0,0 +1,49 @@ +#include "track_start.hpp" + +TrackStart::TrackStart() : m_x(0), m_y(0), m_z(0), m_index(0) {} + +TrackStart::TrackStart(float x, float y, float z, int index) : + m_x(x), m_y(y), m_z(z), m_index(index) +{ +} + +float TrackStart::getX() const { return m_x; } + +float TrackStart::getY() const { return m_y; } + +float TrackStart::getZ() const { return m_z; } + +int TrackStart::getIndex() const { return m_index; } + +void TrackStart::setX(float x) { m_x = x; } + +void TrackStart::setY(float y) { m_y = y; } + +void TrackStart::setZ(float z) { m_z = z; } + +void TrackStart::setIndex(int index) { m_index = index; } + +TrackStart TrackStart::operator+(TrackStart const& other) const +{ + return TrackStart(m_x + other.m_x, m_y + other.m_y, m_z + other.m_z, m_index + other.m_index); +} + +TrackStart& TrackStart::operator+=(TrackStart const& other) +{ + m_x += other.m_x; + m_y += other.m_y; + m_z += other.m_z; + m_index += other.m_index; + return *this; +} + +TrackStart TrackStart::operator-(TrackStart const& other) const +{ + return TrackStart(m_x - other.m_x, m_y - other.m_y, m_z - other.m_z, m_index - other.m_index); +} + +std::ostream& operator<<(std::ostream& os, TrackStart const& track) +{ + os << "TrackStart{" << track.getX() << ", " << track.getY() << ", " << track.getZ() << "}"; + return os; +} diff --git a/test/form/data_products/extra_member/track_start.hpp b/test/form/data_products/extra_member/track_start.hpp new file mode 100644 index 000000000..b93e744a6 --- /dev/null +++ b/test/form/data_products/extra_member/track_start.hpp @@ -0,0 +1,39 @@ +//A TrackStart is a 3-vector of position components. +//This is a simple test data product for demonstrating the features of FORM. + +#include + +#ifndef TRACKSTART_HPP +#define TRACKSTART_HPP + +class TrackStart { +public: + TrackStart(); + TrackStart(float x, float y, float z, int index); + ~TrackStart() = default; + + float getX() const; + float getY() const; + float getZ() const; + int getIndex() const; + + void setX(float x); + void setY(float y); + void setZ(float z); + void setIndex(int index); + + TrackStart operator+(TrackStart const& other) const; + TrackStart& operator+=(TrackStart const& other); + TrackStart operator-(TrackStart const& other) const; + +private: + float m_x; + float m_y; + float m_z; + + int m_index; +}; + +std::ostream& operator<<(std::ostream& os, TrackStart const& track); + +#endif //TRACKSTART_HPP diff --git a/test/form/form_schema_read_test.cpp b/test/form/form_schema_read_test.cpp new file mode 100644 index 000000000..1e95a9dc5 --- /dev/null +++ b/test/form/form_schema_read_test.cpp @@ -0,0 +1,24 @@ +//Part two of schema evolution unit test: can we build a program against a new dictionary for the same product that reads the old product and gets back the correct values? + +#include "test/form/data_products/extra_member/track_start.hpp" +#include "test/form/test_utils.hpp" + +#include +#include +#include + +using namespace form::test; + +int main(int const argc, char const** argv) +{ + int const technology = getTechnology(argc, argv); + if (technology < 0) + return 1; + + auto const& [prods] = read>(technology); + std::ofstream outFile("form_schema_read_log.txt"); + for (auto const& prod : *prods) + outFile << prod << std::endl; + + return 0; +} diff --git a/test/form/form_schema_write_test.cpp b/test/form/form_schema_write_test.cpp new file mode 100644 index 000000000..ef2782378 --- /dev/null +++ b/test/form/form_schema_write_test.cpp @@ -0,0 +1,29 @@ +//Schema evolution test component: write old version of a data product to disk + +#include "test/form/data_products/track_start.hpp" +#include "test/form/toy_tracker.hpp" +#include "test/form/test_utils.hpp" + +#include +#include +#include + +using namespace form::test; + +int main(int const argc, char const** argv) +{ + int const technology = getTechnology(argc, argv); + if (technology < 0) + return 1; + + ToyTracker tracker(4 * 1024); + std::vector const prods = tracker(); + + std::ofstream outFile("form_schema_write_log.txt"); + for (auto const& prod : prods) + outFile << prod << std::endl; + + write(technology, prods); + + return 0; +} From 9c88d056acfe1d903d47eeaf9416d0cffe9cf6c9 Mon Sep 17 00:00:00 2001 From: Andrew Olivier Date: Wed, 4 Mar 2026 15:23:56 -0600 Subject: [PATCH 2/3] Renamed FORM schema evolution test to reflect that it's only testing how FORM uses ROOT's schema evolution. --- test/form/CMakeLists.txt | 30 +++++++++---------- ...est.cpp => form_root_schema_read_test.cpp} | 2 +- ...st.cpp => form_root_schema_write_test.cpp} | 2 +- 3 files changed, 17 insertions(+), 17 deletions(-) rename test/form/{form_schema_read_test.cpp => form_root_schema_read_test.cpp} (91%) rename test/form/{form_schema_write_test.cpp => form_root_schema_write_test.cpp} (91%) diff --git a/test/form/CMakeLists.txt b/test/form/CMakeLists.txt index 330731bf1..8c605504b 100644 --- a/test/form/CMakeLists.txt +++ b/test/form/CMakeLists.txt @@ -33,49 +33,49 @@ if(FORM_USE_ROOT_STORAGE) target_include_directories(ReadVector PRIVATE ${PROJECT_SOURCE_DIR}/form) cet_test( - form_schema_write_test + form_root_schema_write_test SOURCE - form_schema_write_test.cpp + form_root_schema_write_test.cpp toy_tracker.cpp LIBRARIES form form_test_data_products TEST_WORKDIR - form_schema_test + form_root_schema_test ) - target_include_directories(form_schema_write_test PRIVATE ${PROJECT_SOURCE_DIR}/form) + target_include_directories(form_root_schema_write_test PRIVATE ${PROJECT_SOURCE_DIR}/form) cet_test( - form_schema_read_test + form_root_schema_read_test SOURCE - form_schema_read_test.cpp + form_root_schema_read_test.cpp LIBRARIES form form_test_data_products_schema_evolution DIRTY_WORKDIR TEST_WORKDIR - form_schema_test + form_root_schema_test REQUIRED_FIXTURES - form_schema_write_test + form_root_schema_write_test ) - target_include_directories(form_schema_read_test PRIVATE ${PROJECT_SOURCE_DIR}/form) + target_include_directories(form_root_schema_read_test PRIVATE ${PROJECT_SOURCE_DIR}/form) cet_test( - check_schema_evolution + check_root_schema_evolution HANDBUILT TEST_EXEC ${CMAKE_COMMAND} TEST_ARGS -E compare_files - form_schema_write_log.txt - form_schema_read_log.txt + form_root_schema_write_log.txt + form_root_schema_read_log.txt DIRTY_WORKDIR TEST_WORKDIR - form_schema_test + form_root_schema_test REQUIRED_FIXTURES - form_schema_write_test - form_schema_read_test + form_root_schema_write_test + form_root_schema_read_test ) endif() diff --git a/test/form/form_schema_read_test.cpp b/test/form/form_root_schema_read_test.cpp similarity index 91% rename from test/form/form_schema_read_test.cpp rename to test/form/form_root_schema_read_test.cpp index 1e95a9dc5..4115e5f4c 100644 --- a/test/form/form_schema_read_test.cpp +++ b/test/form/form_root_schema_read_test.cpp @@ -16,7 +16,7 @@ int main(int const argc, char const** argv) return 1; auto const& [prods] = read>(technology); - std::ofstream outFile("form_schema_read_log.txt"); + std::ofstream outFile("form_root_schema_read_log.txt"); for (auto const& prod : *prods) outFile << prod << std::endl; diff --git a/test/form/form_schema_write_test.cpp b/test/form/form_root_schema_write_test.cpp similarity index 91% rename from test/form/form_schema_write_test.cpp rename to test/form/form_root_schema_write_test.cpp index ef2782378..24127c484 100644 --- a/test/form/form_schema_write_test.cpp +++ b/test/form/form_root_schema_write_test.cpp @@ -19,7 +19,7 @@ int main(int const argc, char const** argv) ToyTracker tracker(4 * 1024); std::vector const prods = tracker(); - std::ofstream outFile("form_schema_write_log.txt"); + std::ofstream outFile("form_root_schema_write_log.txt"); for (auto const& prod : prods) outFile << prod << std::endl; From df6d2da2a68a92a0bdfa164d0bccca6108f038c1 Mon Sep 17 00:00:00 2001 From: Andrew Olivier Date: Wed, 4 Mar 2026 15:41:26 -0600 Subject: [PATCH 3/3] Fixed clang-format requests. --- test/form/form_root_schema_read_test.cpp | 2 +- test/form/form_root_schema_write_test.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/form/form_root_schema_read_test.cpp b/test/form/form_root_schema_read_test.cpp index 4115e5f4c..1840076df 100644 --- a/test/form/form_root_schema_read_test.cpp +++ b/test/form/form_root_schema_read_test.cpp @@ -3,9 +3,9 @@ #include "test/form/data_products/extra_member/track_start.hpp" #include "test/form/test_utils.hpp" +#include #include #include -#include using namespace form::test; diff --git a/test/form/form_root_schema_write_test.cpp b/test/form/form_root_schema_write_test.cpp index 24127c484..d5574502a 100644 --- a/test/form/form_root_schema_write_test.cpp +++ b/test/form/form_root_schema_write_test.cpp @@ -1,12 +1,12 @@ //Schema evolution test component: write old version of a data product to disk #include "test/form/data_products/track_start.hpp" -#include "test/form/toy_tracker.hpp" #include "test/form/test_utils.hpp" +#include "test/form/toy_tracker.hpp" +#include #include #include -#include using namespace form::test;