From 2da91c4ed52e8679f15f959a0519a5c9d3a8f030 Mon Sep 17 00:00:00 2001 From: Axel Huebl Date: Wed, 17 Dec 2025 18:39:42 +0100 Subject: [PATCH 1/2] [Vibe] Migrate to Catch2 v3 Vibe-code start using Cursor.AI Prompts (after initial setup & test instructions): > I want us to update our Catch2 tests to use the latest version of Catch, version 3. > > I want to follow this path: > Build Catch2 as a proper (static) library, and move to piecewise headers > > Please read the upgrade guide and update the project as needed: > https://github.com/catchorg/Catch2/blob/devel/docs/migrate-v2-to-v3.md > > Start with our CMake logic and downloader, then update the C++ test files in test/ Then: > Great, please self-review your changes. > > The latest Catch2 version is 3.11.0 btw Then: > Self-review your changes once more, read the change logs on > https://github.com/catchorg/Catch2/releases for deprecated > and new APIs. Modernize as needed. --- CMakeLists.txt | 29 +++++++------------ cmake/dependencies/catch.cmake | 8 ++--- test/AuxiliaryTest.cpp | 2 +- test/CatchMain.cpp | 22 -------------- test/CatchRunner.cpp | 21 +++++--------- test/CoreTest.cpp | 26 +++++++++-------- .../automatic_variable_encoding.cpp | 2 +- .../iterate_nonstreaming_series.cpp | 2 +- .../read_variablebased_randomaccess.cpp | 2 +- test/Files_SerialIO/close_and_reopen_test.cpp | 2 +- .../components_without_extent.cpp | 2 +- test/JSONTest.cpp | 3 +- test/ParallelIOTest.cpp | 3 +- test/SerialIOTest.cpp | 7 +++-- 14 files changed, 49 insertions(+), 82 deletions(-) delete mode 100644 test/CatchMain.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index dba77d38be..8fc51512f1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -525,6 +525,9 @@ if(openPMD_BUILD_TESTING) add_library(openPMD::thirdparty::Catch2 INTERFACE IMPORTED) target_link_libraries(openPMD::thirdparty::Catch2 INTERFACE Catch2::Catch2) + add_library(openPMD::thirdparty::Catch2WithMain INTERFACE IMPORTED) + target_link_libraries(openPMD::thirdparty::Catch2WithMain + INTERFACE Catch2::Catch2WithMain) endif() if(openPMD_HAVE_MPI) @@ -770,14 +773,11 @@ if(openPMD_HAVE_PYTHON) endif() if(openPMD_BUILD_TESTING) - # compile Catch2 implementation part separately - add_library(CatchRunner ${_openpmd_lib_type} - test/CatchRunner.cpp) # Always MPI_Init with Serial Fallback - add_library(CatchMain ${_openpmd_lib_type} - test/CatchMain.cpp) # Serial only + # CatchRunner: custom main with MPI support for parallel tests + # In Catch2 v3, we link to Catch2 (not Catch2WithMain) since we provide our own main + add_library(CatchRunner ${_openpmd_lib_type} test/CatchRunner.cpp) openpmd_cxx_required(CatchRunner) - openpmd_cxx_required(CatchMain) - set_target_properties(CatchRunner CatchMain PROPERTIES + set_target_properties(CatchRunner PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${openPMD_ARCHIVE_OUTPUT_DIRECTORY} LIBRARY_OUTPUT_DIRECTORY ${openPMD_LIBRARY_OUTPUT_DIRECTORY} RUNTIME_OUTPUT_DIRECTORY ${openPMD_RUNTIME_OUTPUT_DIRECTORY} @@ -788,7 +788,6 @@ if(openPMD_BUILD_TESTING) WINDOWS_EXPORT_ALL_SYMBOLS ON ) set_target_properties(CatchRunner PROPERTIES COMPILE_PDB_NAME CatchRunner) - set_target_properties(CatchMain PROPERTIES COMPILE_PDB_NAME CatchMain) # note: same as above, but for Multi-Config generators if(isMultiConfig) foreach(CFG IN LISTS CMAKE_CONFIGURATION_TYPES) @@ -801,20 +800,10 @@ if(openPMD_BUILD_TESTING) PDB_OUTPUT_DIRECTORY_${CFG_UPPER} ${openPMD_PDB_OUTPUT_DIRECTORY}/${CFG} COMPILE_PDB_OUTPUT_DIRECTORY_${CFG_UPPER} ${openPMD_COMPILE_PDB_OUTPUT_DIRECTORY}/${CFG} ) - set_target_properties(CatchMain PROPERTIES - COMPILE_PDB_NAME_${CFG_UPPER} CatchMain - ARCHIVE_OUTPUT_DIRECTORY_${CFG_UPPER} ${openPMD_ARCHIVE_OUTPUT_DIRECTORY}/${CFG} - LIBRARY_OUTPUT_DIRECTORY_${CFG_UPPER} ${openPMD_LIBRARY_OUTPUT_DIRECTORY}/${CFG} - RUNTIME_OUTPUT_DIRECTORY_${CFG_UPPER} ${openPMD_RUNTIME_OUTPUT_DIRECTORY}/${CFG} - PDB_OUTPUT_DIRECTORY_${CFG_UPPER} ${openPMD_PDB_OUTPUT_DIRECTORY}/${CFG} - COMPILE_PDB_OUTPUT_DIRECTORY_${CFG_UPPER} ${openPMD_COMPILE_PDB_OUTPUT_DIRECTORY}/${CFG} - ) endforeach() endif() target_compile_options(CatchRunner PUBLIC ${_msvc_options}) - target_compile_options(CatchMain PUBLIC ${_msvc_options}) target_link_libraries(CatchRunner PUBLIC openPMD::thirdparty::Catch2) - target_link_libraries(CatchMain PUBLIC openPMD::thirdparty::Catch2) if(openPMD_HAVE_MPI) target_link_libraries(CatchRunner PUBLIC ${openPMD_MPI_TARGETS}) target_compile_definitions(CatchRunner PUBLIC openPMD_HAVE_MPI=1) @@ -875,9 +864,11 @@ if(openPMD_BUILD_TESTING) endif() target_link_libraries(${testname}Tests PRIVATE openPMD) if(${testname} MATCHES "Parallel.+$") + # Parallel tests use CatchRunner (custom main with MPI) target_link_libraries(${testname}Tests PRIVATE CatchRunner) else() - target_link_libraries(${testname}Tests PRIVATE CatchMain) + # Serial tests use Catch2WithMain (provides default main) + target_link_libraries(${testname}Tests PRIVATE openPMD::thirdparty::Catch2WithMain) endif() if(${testname} STREQUAL JSON) diff --git a/cmake/dependencies/catch.cmake b/cmake/dependencies/catch.cmake index bc7a3dcc89..39323b3bab 100644 --- a/cmake/dependencies/catch.cmake +++ b/cmake/dependencies/catch.cmake @@ -45,7 +45,7 @@ function(find_catch2) mark_as_advanced(FETCHCONTENT_UPDATES_DISCONNECTED) #mark_as_advanced(FETCHCONTENT_UPDATES_DISCONNECTED_FETCHEDCatch2) elseif(NOT openPMD_USE_INTERNAL_CATCH) - find_package(Catch2 2.13.10 CONFIG REQUIRED) + find_package(Catch2 3.0.0 CONFIG REQUIRED) message(STATUS "Catch2: Found version '${Catch2_VERSION}'") endif() endfunction() @@ -56,10 +56,10 @@ set(openPMD_catch_src "" "Local path to Catch2 source directory (preferred if set)") # tarball fetcher -set(openPMD_catch_tar "https://github.com/catchorg/Catch2/archive/refs/tags/v2.13.10.tar.gz" +set(openPMD_catch_tar "https://github.com/catchorg/Catch2/archive/refs/tags/v3.11.0.tar.gz" CACHE STRING "Remote tarball link to pull and build Catch2 from if(openPMD_USE_INTERNAL_CATCH)") -set(openPMD_catch_tar_hash "SHA256=d54a712b7b1d7708bc7a819a8e6e47b2fde9536f487b89ccbca295072a7d9943" +set(openPMD_catch_tar_hash "SHA256=82fa1cb59dc28bab220935923f7469b997b259eb192fb9355db62da03c2a3137" CACHE STRING "Hash checksum of the tarball of Catch2 if(openPMD_USE_INTERNAL_CATCH)") @@ -67,7 +67,7 @@ set(openPMD_catch_tar_hash "SHA256=d54a712b7b1d7708bc7a819a8e6e47b2fde9536f487b8 set(openPMD_catch_repo "https://github.com/catchorg/Catch2.git" CACHE STRING "Repository URI to pull and build Catch2 from if(openPMD_USE_INTERNAL_CATCH)") -set(openPMD_catch_branch "v2.13.10" +set(openPMD_catch_branch "v3.11.0" CACHE STRING "Repository branch for openPMD_catch_repo if(openPMD_USE_INTERNAL_CATCH)") diff --git a/test/AuxiliaryTest.cpp b/test/AuxiliaryTest.cpp index e3b4635185..ee0b029473 100644 --- a/test/AuxiliaryTest.cpp +++ b/test/AuxiliaryTest.cpp @@ -37,7 +37,7 @@ #include "openPMD/backend/Writable.hpp" #include "openPMD/config.hpp" -#include +#include #include #include diff --git a/test/CatchMain.cpp b/test/CatchMain.cpp deleted file mode 100644 index a1b9d178f1..0000000000 --- a/test/CatchMain.cpp +++ /dev/null @@ -1,22 +0,0 @@ -/* Copyright 2025 Axel Huebl - * - * This file is part of openPMD-api. - * - * openPMD-api is free software: you can redistribute it and/or modify - * it under the terms of of either the GNU General Public License or - * the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * openPMD-api is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License and the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU General Public License - * and the GNU Lesser General Public License along with openPMD-api. - * If not, see . - */ -#define CATCH_CONFIG_MAIN -#include diff --git a/test/CatchRunner.cpp b/test/CatchRunner.cpp index e6af24a576..cc8fc0c9c6 100644 --- a/test/CatchRunner.cpp +++ b/test/CatchRunner.cpp @@ -18,8 +18,7 @@ * and the GNU Lesser General Public License along with openPMD-api. * If not, see . */ -#define CATCH_CONFIG_RUNNER -#include +#include #if openPMD_HAVE_MPI #include @@ -29,13 +28,10 @@ int main(int argc, char *argv[]) MPI_Init(&argc, &argv); Catch::Session session; - int result = 0; + int result = session.applyCommandLine(argc, argv); + if (result == 0) { - // Indicates a command line parsing - result = session.applyCommandLine(argc, argv); - // RT tests - if (result == 0) - result = session.run(); + result = session.run(); } MPI_Finalize(); return result; @@ -44,13 +40,10 @@ int main(int argc, char *argv[]) int main(int argc, char *argv[]) { Catch::Session session; - int result = 0; + int result = session.applyCommandLine(argc, argv); + if (result == 0) { - // Indicates a command line parsing - result = session.applyCommandLine(argc, argv); - // RT tests - if (result == 0) - result = session.run(); + result = session.run(); } return result; } diff --git a/test/CoreTest.cpp b/test/CoreTest.cpp index ad22ae1cf8..ee266ff56e 100644 --- a/test/CoreTest.cpp +++ b/test/CoreTest.cpp @@ -34,7 +34,8 @@ #include "openPMD/auxiliary/Memory_internal.hpp" #include "openPMD/auxiliary/UniquePtr.hpp" -#include +#include +#include #include #include @@ -1100,7 +1101,7 @@ TEST_CASE("wrapper_test", "[core]") // it once? value = 43.; mrc2.makeConstant(value); REQUIRE_THROWS_WITH( mrc2.makeConstant(value), - Catch::Equals( + Catch::Matchers::Equals( "A recordComponent can not (yet) be made constant after " "it has been written.")); std::array moreData = {{112233.}}; @@ -1121,7 +1122,7 @@ TEST_CASE("wrapper_test", "[core]") int wrongData = 42; REQUIRE_THROWS_WITH( o.iterations[5].meshes["E"]["y"].storeChunkRaw(&wrongData, {0}, {1}), - Catch::Equals( + Catch::Matchers::Equals( "Datatypes of chunk data (INT) and record component " "(DOUBLE) do not match.")); std::shared_ptr storeData = std::make_shared(44); @@ -1192,7 +1193,7 @@ TEST_CASE("wrapper_test", "[core]") .particles["electrons"] .particlePatches["numParticles"][RecordComponent::SCALAR] .store(idx + 1, 42.), - Catch::Equals( + Catch::Matchers::Equals( "Datatypes of patch data (DOUBLE) and dataset (" + u64str.str() + ") do not match.")); o.iterations[6] @@ -1285,7 +1286,7 @@ TEST_CASE("empty_record_test", "[core]") "No assumption about contained RecordComponents will be made"); REQUIRE_THROWS_WITH( o.flush(), - Catch::Equals( + Catch::Matchers::Equals( "A Record can not be written without any contained " "RecordComponents: E")); o.iterations[1].meshes["E"][RecordComponent::SCALAR].resetDataset( @@ -1302,10 +1303,11 @@ TEST_CASE("zero_extent_component", "[core]") auto E_x = o.iterations[1].meshes["E"]["x"]; E_x.setComment("Datasets must contain dimensions."); // REQUIRE_THROWS_WITH(E_x.resetDataset(Dataset(Datatype::LONG, {})), - // Catch::Equals("Dataset extent must be at least 1D.")); + // Catch::Matchers::Equals("Dataset extent must be at + // least 1D.")); REQUIRE_THROWS_WITH( E_x.makeEmpty(0), - Catch::Equals("Dataset extent must be at least 1D.")); + Catch::Matchers::Equals("Dataset extent must be at least 1D.")); E_x.resetDataset(Dataset(Datatype::DOUBLE, {1})); } @@ -1313,17 +1315,17 @@ TEST_CASE("no_file_ending", "[core]") { REQUIRE_THROWS_WITH( Series("./new_openpmd_output", Access::CREATE), - Catch::Equals( + Catch::Matchers::Equals( "Unknown file format! Did you specify a file ending? " "Specified file name was './new_openpmd_output'.")); REQUIRE_THROWS_WITH( Series("./new_openpmd_output_%T", Access::CREATE), - Catch::Equals( + Catch::Matchers::Equals( "Unknown file format! Did you specify a file ending? " "Specified file name was './new_openpmd_output_%T'.")); REQUIRE_THROWS_WITH( Series("./new_openpmd_output_%05T", Access::CREATE), - Catch::Equals( + Catch::Matchers::Equals( "Unknown file format! Did you specify a file ending? " "Specified file name was './new_openpmd_output_%05T'.")); { @@ -1625,7 +1627,7 @@ TEST_CASE("load_chunk_wrong_datatype", "[core]") read.iterations[0] .meshes["rho"][RecordComponent::SCALAR] .loadChunk({0}, {10}), - Catch::Equals(err_msg)); + Catch::Matchers::Equals(err_msg)); } } @@ -1694,7 +1696,7 @@ TEST_CASE("DoConvert_single_value_to_vector", "[core]") REQUIRE(attr.get>() == arrayint); REQUIRE_THROWS_WITH( (attr.get>()), - Catch::Equals( + Catch::Matchers::Equals( "getCast: no vector to array conversion possible " "(wrong requested array size).")); #endif diff --git a/test/Files_Core/automatic_variable_encoding.cpp b/test/Files_Core/automatic_variable_encoding.cpp index 95371f50f4..d4e0f07b18 100644 --- a/test/Files_Core/automatic_variable_encoding.cpp +++ b/test/Files_Core/automatic_variable_encoding.cpp @@ -20,7 +20,7 @@ */ #include "CoreTests.hpp" -#include +#include namespace automatic_variable_encoding { diff --git a/test/Files_ParallelIO/iterate_nonstreaming_series.cpp b/test/Files_ParallelIO/iterate_nonstreaming_series.cpp index 4b6a3746c2..8ddadfc40a 100644 --- a/test/Files_ParallelIO/iterate_nonstreaming_series.cpp +++ b/test/Files_ParallelIO/iterate_nonstreaming_series.cpp @@ -22,7 +22,7 @@ #include "openPMD/IO/ADIOS/macros.hpp" -#include +#include #include namespace iterate_nonstreaming_series diff --git a/test/Files_ParallelIO/read_variablebased_randomaccess.cpp b/test/Files_ParallelIO/read_variablebased_randomaccess.cpp index b0d5f51f05..a186181bb4 100644 --- a/test/Files_ParallelIO/read_variablebased_randomaccess.cpp +++ b/test/Files_ParallelIO/read_variablebased_randomaccess.cpp @@ -25,7 +25,7 @@ #include -#include +#include #include #if openPMD_HAVE_ADIOS2 && openPMD_HAVE_MPI diff --git a/test/Files_SerialIO/close_and_reopen_test.cpp b/test/Files_SerialIO/close_and_reopen_test.cpp index e09c0ac1e3..676e707c7e 100644 --- a/test/Files_SerialIO/close_and_reopen_test.cpp +++ b/test/Files_SerialIO/close_and_reopen_test.cpp @@ -24,7 +24,7 @@ #include "openPMD/Series.hpp" #include "openPMD/auxiliary/Filesystem.hpp" -#include +#include namespace close_and_reopen_test { diff --git a/test/Files_SerialIO/components_without_extent.cpp b/test/Files_SerialIO/components_without_extent.cpp index 0fcc3f9e3a..795ec001b5 100644 --- a/test/Files_SerialIO/components_without_extent.cpp +++ b/test/Files_SerialIO/components_without_extent.cpp @@ -2,7 +2,7 @@ #include "openPMD/openPMD.hpp" -#include +#include #include #include diff --git a/test/JSONTest.cpp b/test/JSONTest.cpp index 9e19d0b697..3ccd17e2a7 100644 --- a/test/JSONTest.cpp +++ b/test/JSONTest.cpp @@ -24,7 +24,8 @@ #include "openPMD/helper/list_series.hpp" #include "openPMD/openPMD.hpp" -#include +#include +#include #include #include diff --git a/test/ParallelIOTest.cpp b/test/ParallelIOTest.cpp index b05c778244..9c28f52945 100644 --- a/test/ParallelIOTest.cpp +++ b/test/ParallelIOTest.cpp @@ -33,7 +33,8 @@ #include "openPMD/openPMD.hpp" // @todo change includes #include "openPMD/auxiliary/OneDimensionalBlockSlicer.hpp" -#include +#include +#include #if !openPMD_HAVE_MPI TEST_CASE("none", "[parallel]") diff --git a/test/SerialIOTest.cpp b/test/SerialIOTest.cpp index 7fd13822f3..eb59695515 100644 --- a/test/SerialIOTest.cpp +++ b/test/SerialIOTest.cpp @@ -37,7 +37,8 @@ #include "openPMD/auxiliary/StringManip.hpp" #include "openPMD/openPMD.hpp" -#include +#include +#include #include #include @@ -2260,7 +2261,7 @@ inline void fileBased_write_test(const std::string &backend) Series( "../samples/subdir/serial_fileBased_write%T." + backend, Access::READ_WRITE), - Catch::Equals( + Catch::Matchers::Equals( "Cannot write to a series with inconsistent iteration padding. " "Please specify '%0T' or open as read-only.")); @@ -7227,7 +7228,7 @@ TEST_CASE("late_setting_of_iterationencoding", "[serial]") REQUIRE_THROWS_WITH( series.setIterationEncoding( ::openPMD::IterationEncoding::fileBased), - Catch::Equals( + Catch::Matchers::Equals( "Wrong API usage: For fileBased formats the " "iteration expansion pattern %T must " "be included in the file name")); From 418f1e6c3f15661b476f5d0f62d142529fc253c7 Mon Sep 17 00:00:00 2001 From: Axel Huebl Date: Thu, 18 Dec 2025 01:52:29 +0100 Subject: [PATCH 2/2] CMake: -fPIC for Catch2 --- cmake/dependencies/catch.cmake | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/cmake/dependencies/catch.cmake b/cmake/dependencies/catch.cmake index 39323b3bab..ccd726f62c 100644 --- a/cmake/dependencies/catch.cmake +++ b/cmake/dependencies/catch.cmake @@ -19,9 +19,16 @@ function(find_catch2) if(TARGET Catch2::Catch2) # nothing to do, target already exists in the superbuild elseif(openPMD_USE_INTERNAL_CATCH AND openPMD_catch_src) + # Ensure Catch2 is built with PIC so it can be linked into shared libraries + set(CMAKE_POSITION_INDEPENDENT_CODE ON) add_subdirectory(${openPMD_catch_src} _deps/localCatch2-build/) elseif(openPMD_USE_INTERNAL_CATCH AND (openPMD_catch_tar OR openPMD_catch_branch)) include(FetchContent) + # Ensure Catch2 is built with PIC so it can be linked into shared libraries + # Set as cache variable (only if not already set) so it's picked up by Catch2's CMakeLists.txt + if(NOT DEFINED CMAKE_POSITION_INDEPENDENT_CODE) + set(CMAKE_POSITION_INDEPENDENT_CODE ON CACHE BOOL "Position independent code") + endif() if(openPMD_catch_tar) FetchContent_Declare(fetchedCatch2 URL ${openPMD_catch_tar}