diff --git a/src/utils/checkpointing/Checkpoint.cpp b/src/utils/checkpointing/Checkpoint.cpp index aeeb8688c89fb2e1475895d31a7d62c2e976e2d8..dccd47085cd4a79fc3e719dc9881daa798215fed 100644 --- a/src/utils/checkpointing/Checkpoint.cpp +++ b/src/utils/checkpointing/Checkpoint.cpp @@ -32,6 +32,7 @@ void checkpoint() { try { + HighFive::SilenceHDF5 m_silence_hdf5{true}; auto create_props = HighFive::FileCreateProps{}; create_props.add(HighFive::FileSpaceStrategy(H5F_FSPACE_STRATEGY_FSM_AGGR, true, 0)); diff --git a/src/utils/checkpointing/PrintCheckpointInfo.cpp b/src/utils/checkpointing/PrintCheckpointInfo.cpp index 43dbcdc55ab07a59e7747eeb3f5540f82e592cc8..bc7cdcfdd1730921d950318b727cb14f4825c0e0 100644 --- a/src/utils/checkpointing/PrintCheckpointInfo.cpp +++ b/src/utils/checkpointing/PrintCheckpointInfo.cpp @@ -14,7 +14,6 @@ #include <algebra/TinyVector.hpp> #include <iomanip> -#include <iostream> #include <regex> #endif // PUGS_HAS_HDF5 @@ -23,7 +22,7 @@ template <typename T> void -printAttributeValue(const HighFive::Attribute& attribute) +printAttributeValue(const HighFive::Attribute& attribute, std::ostream& os) { std::string delim = ""; @@ -33,24 +32,25 @@ printAttributeValue(const HighFive::Attribute& attribute) HighFive::DataSpace data_space = attribute.getSpace(); if (data_space.getNumberDimensions() == 0) { - std::cout << std::boolalpha << delim << attribute.read<T>() << delim; + os << std::boolalpha << delim << attribute.read<T>() << delim; } else if (data_space.getNumberDimensions() == 1) { std::vector value = attribute.read<std::vector<T>>(); if (value.size() > 0) { - std::cout << '(' << std::boolalpha << delim << value[0] << delim; + os << '(' << std::boolalpha << delim << value[0] << delim; for (size_t i = 1; i < value.size(); ++i) { - std::cout << ", " << std::boolalpha << delim << value[i] << delim; + os << ", " << std::boolalpha << delim << value[i] << delim; } - std::cout << ')'; + os << ')'; } } } void -printCheckpointInfo(const std::string& filename) +printCheckpointInfo(const std::string& filename, std::ostream& os) { if (parallel::rank() == 0) { try { + HighFive::SilenceHDF5 m_silence_hdf5{true}; HighFive::File file(filename, HighFive::File::ReadOnly); std::map<size_t, std::string> checkpoint_name_list; @@ -59,11 +59,11 @@ printCheckpointInfo(const std::string& filename) std::smatch number_string; const std::regex checkpoint_regex("checkpoint_([0-9]+)"); if (std::regex_match(name, number_string, checkpoint_regex)) { - std::stringstream os; - os << number_string[1].str(); + std::stringstream ss; + ss << number_string[1].str(); size_t id = 0; - os >> id; + ss >> id; checkpoint_name_list[id] = name; } @@ -73,8 +73,8 @@ printCheckpointInfo(const std::string& filename) HighFive::Group checkpoint = file.getGroup(checkpoint_name); const std::string creation_date = checkpoint.getAttribute("creation_date").read<std::string>(); - std::cout << rang::fgB::yellow << " * " << rang::fg::reset << rang::fgB::magenta << checkpoint_name - << rang::fg::reset << " [" << rang::fg::green << creation_date << rang::fg::reset << "]\n"; + os << rang::fgB::yellow << " * " << rang::fg::reset << rang::fgB::magenta << checkpoint_name << rang::fg::reset + << " [" << rang::fg::green << creation_date << rang::fg::reset << "]\n"; HighFive::Group saved_symbol_table = checkpoint.getGroup("symbol table"); @@ -86,21 +86,20 @@ printCheckpointInfo(const std::string& filename) HighFive::Attribute attribute = saved_symbol_table.getAttribute(symbol_name); HighFive::DataType data_type = attribute.getDataType(); - std::cout << " "; - std::cout << std::setw(25) << std::setfill('.') // << rang::style::bold; - << std::left << symbol_name + ' ' << std::setfill(' '); - std::cout << ' '; + os << " "; + os << std::setw(25) << std::setfill('.') << std::left << symbol_name + ' ' << std::setfill(' '); + os << ' '; switch (data_type.getClass()) { case HighFive::DataTypeClass::Float: { - printAttributeValue<double>(attribute); + printAttributeValue<double>(attribute, os); break; } case HighFive::DataTypeClass::Integer: { if (data_type == HighFive::AtomicType<uint64_t>()) { - printAttributeValue<uint64_t>(attribute); + printAttributeValue<uint64_t>(attribute, os); } else if (data_type == HighFive::AtomicType<int64_t>()) { - printAttributeValue<int64_t>(attribute); + printAttributeValue<int64_t>(attribute, os); } break; } @@ -108,51 +107,52 @@ printCheckpointInfo(const std::string& filename) HighFive::DataSpace data_space = attribute.getSpace(); if (data_type == HighFive::AtomicType<TinyVector<1>>()) { - printAttributeValue<TinyVector<1>>(attribute); + printAttributeValue<TinyVector<1>>(attribute, os); } else if (data_type == HighFive::AtomicType<TinyVector<2>>()) { - printAttributeValue<TinyVector<2>>(attribute); + printAttributeValue<TinyVector<2>>(attribute, os); } else if (data_type == HighFive::AtomicType<TinyVector<3>>()) { - printAttributeValue<TinyVector<3>>(attribute); + printAttributeValue<TinyVector<3>>(attribute, os); } else if (data_type == HighFive::AtomicType<TinyMatrix<1>>()) { - printAttributeValue<TinyMatrix<1>>(attribute); + printAttributeValue<TinyMatrix<1>>(attribute, os); } else if (data_type == HighFive::AtomicType<TinyMatrix<2>>()) { - printAttributeValue<TinyMatrix<2>>(attribute); + printAttributeValue<TinyMatrix<2>>(attribute, os); } else if (data_type == HighFive::AtomicType<TinyMatrix<3>>()) { - printAttributeValue<TinyMatrix<3>>(attribute); + printAttributeValue<TinyMatrix<3>>(attribute, os); } break; } case HighFive::DataTypeClass::Enum: { if (data_type == HighFive::create_datatype<bool>()) { - printAttributeValue<bool>(attribute); + printAttributeValue<bool>(attribute, os); } else { - std::cout << "????"; + os << "????"; // LCOV_EXCL_LINE } break; } case HighFive::DataTypeClass::String: { - printAttributeValue<std::string>(attribute); + printAttributeValue<std::string>(attribute, os); break; } + // LCOV_EXCL_START default: { std::ostringstream error_msg; error_msg << "invalid data type class '" << rang::fgB::yellow << data_type.string() << rang::fg::reset << "' for symbol " << rang::fgB::cyan << symbol_name << rang::fg::reset; throw UnexpectedError(error_msg.str()); } + // LCOV_EXCL_STOP } - std::cout << rang::style::reset << '\n'; + os << rang::style::reset << '\n'; } if (saved_symbol_table.exist("embedded")) { HighFive::Group embedded_data_list = saved_symbol_table.getGroup("embedded"); for (auto name : embedded_data_list.listObjectNames()) { - std::cout << " "; - std::cout << std::setw(25) << std::setfill('.') // << rang::style::bold; - << std::left << name + ' ' << std::setfill(' '); - std::cout << ' '; - std::cout << embedded_data_list.getGroup(name).getAttribute("type").read<std::string>() << '\n'; + os << " "; + os << std::setw(25) << std::setfill('.') << std::left << name + ' ' << std::setfill(' '); + os << ' '; + os << embedded_data_list.getGroup(name).getAttribute("type").read<std::string>() << '\n'; } } @@ -166,24 +166,26 @@ printCheckpointInfo(const std::string& filename) } while (not finished); } - std::cout << "-------------------------------------------------------\n"; + os << "-------------------------------------------------------\n"; for (auto path : std::array{"resuming_checkpoint", "last_checkpoint"}) { - std::cout << rang::fgB::yellow << " * " << rang::fg::reset << rang::style::bold << path << rang::style::reset - << " -> " << rang::fgB::green << file.getGroup(path).getAttribute("name").read<std::string>() - << rang::style::reset << '\n'; + os << rang::fgB::yellow << " * " << rang::fg::reset << rang::style::bold << path << rang::style::reset << " -> " + << rang::fgB::green << file.getGroup(path).getAttribute("name").read<std::string>() << rang::style::reset + << '\n'; } } + // LCOV_EXCL_START catch (HighFive::Exception& e) { std::cerr << rang::fgB::red << "error: " << rang::fg::reset << rang::style::bold << e.what() << rang::style::reset << '\n'; } + // LCOV_EXCL_STOP } } #else // PUGS_HAS_HDF5 void -printCheckpointInfo(const std::string&) +printCheckpointInfo(const std::string&, std::ostream&) { std::cerr << rang::fgB::red << "error: " << rang::fg::reset << "checkpoint info requires HDF5\n"; } diff --git a/src/utils/checkpointing/PrintCheckpointInfo.hpp b/src/utils/checkpointing/PrintCheckpointInfo.hpp index bbe5b4860b18e50df524b26671704e852ccce7c0..fcfb800622fcc2b03ab2770c3abc6c3a23bf8fc8 100644 --- a/src/utils/checkpointing/PrintCheckpointInfo.hpp +++ b/src/utils/checkpointing/PrintCheckpointInfo.hpp @@ -1,8 +1,9 @@ #ifndef PRINT_CHECKPOINT_INFO_HPP #define PRINT_CHECKPOINT_INFO_HPP +#include <iostream> #include <string> -void printCheckpointInfo(const std::string& filename); +void printCheckpointInfo(const std::string& filename, std::ostream& os = std::cout); #endif // PRINT_CHECKPOINT_INFO_HPP diff --git a/src/utils/checkpointing/PrintScriptFrom.cpp b/src/utils/checkpointing/PrintScriptFrom.cpp index 9e324d1132cbe25989d89b61325df28b1bad2735..41c80c1b767945db0ade360d84bce4fbee8d62bf 100644 --- a/src/utils/checkpointing/PrintScriptFrom.cpp +++ b/src/utils/checkpointing/PrintScriptFrom.cpp @@ -11,9 +11,10 @@ #include <utils/HighFivePugsUtils.hpp> void -printScriptFrom(const std::string& filename, const uint64_t& checkpoint_number) +printScriptFrom(const std::string& filename, const uint64_t& checkpoint_number, std::ostream& os) { try { + HighFive::SilenceHDF5 m_silence_hdf5{true}; HighFive::File file(filename, HighFive::File::ReadWrite); const std::string checkpoint_name = "checkpoint_" + std::to_string(checkpoint_number); @@ -25,7 +26,7 @@ printScriptFrom(const std::string& filename, const uint64_t& checkpoint_number) } HighFive::Group checkpoint = file.getGroup(checkpoint_name); - std::cout << checkpoint.getAttribute("data.pgs").read<std::string>(); + os << checkpoint.getAttribute("data.pgs").read<std::string>(); } catch (HighFive::Exception& e) { throw NormalError(e.what()); @@ -35,7 +36,7 @@ printScriptFrom(const std::string& filename, const uint64_t& checkpoint_number) #else // PUGS_HAS_HDF5 void -printScriptFrom(const std::string&, const uint64_t&) +printScriptFrom(const std::string&, const uint64_t&, std::ostream&) { std::cerr << rang::fgB::red << "error: " << rang::fg::reset << "printing checkpoint's script requires HDF5\n"; } diff --git a/src/utils/checkpointing/PrintScriptFrom.hpp b/src/utils/checkpointing/PrintScriptFrom.hpp index c3807c7af000280698406050ffa3320effb05eb6..416a0c76dc5126e19699d22bba5926050f9bbb33 100644 --- a/src/utils/checkpointing/PrintScriptFrom.hpp +++ b/src/utils/checkpointing/PrintScriptFrom.hpp @@ -2,8 +2,9 @@ #define PRINT_SCRIPT_FROM_HPP #include <cstdint> +#include <iostream> #include <string> -void printScriptFrom(const std::string& filename, const uint64_t& checkpoint_number); +void printScriptFrom(const std::string& filename, const uint64_t& checkpoint_number, std::ostream& os = std::cout); #endif // PRINT_SCRIPT_FROM_HPP diff --git a/src/utils/checkpointing/ReadItemArrayVariant.cpp b/src/utils/checkpointing/ReadItemArrayVariant.cpp index 6420d9331becba07271b12d646aa810aeecc15eb..d14087366ac79b7e47acb14b4b8310cf46e4786d 100644 --- a/src/utils/checkpointing/ReadItemArrayVariant.cpp +++ b/src/utils/checkpointing/ReadItemArrayVariant.cpp @@ -54,7 +54,9 @@ readItemArrayVariant(const HighFive::Group& item_array_variant_group) p_item_array = std::make_shared<ItemArrayVariant>( readItemArray<TinyMatrix<3>, item_type>(item_array_variant_group, "arrays", connectivity)); } else { + // LCOV_EXCL_START throw UnexpectedError("unexpected discrete function data type: " + data_type); + // LCOV_EXCL_STOP } return p_item_array; } diff --git a/src/utils/checkpointing/Resume.cpp b/src/utils/checkpointing/Resume.cpp index dbb539771547fc485cbb3642a9a3b24216fd907c..184011049c31febd92f53439bf1389b49fbbd6f2 100644 --- a/src/utils/checkpointing/Resume.cpp +++ b/src/utils/checkpointing/Resume.cpp @@ -25,6 +25,7 @@ void resume() { try { + HighFive::SilenceHDF5 m_silence_hdf5{true}; checkpointing::ResumingData::create(); HighFive::File file(ResumingManager::getInstance().filename(), HighFive::File::ReadOnly); diff --git a/src/utils/checkpointing/SetResumeFrom.cpp b/src/utils/checkpointing/SetResumeFrom.cpp index 2d1f5e5ca361a0378161024f9f4c367dc6bf1bb7..81502da2f74e9d0e71d812d8692d7318b3135e71 100644 --- a/src/utils/checkpointing/SetResumeFrom.cpp +++ b/src/utils/checkpointing/SetResumeFrom.cpp @@ -11,9 +11,10 @@ #include <utils/HighFivePugsUtils.hpp> void -setResumeFrom(const std::string& filename, const uint64_t& checkpoint_number) +setResumeFrom(const std::string& filename, const uint64_t& checkpoint_number, std::ostream& os) { try { + HighFive::SilenceHDF5 m_silence_hdf5{true}; HighFive::File file(filename, HighFive::File::ReadWrite); const std::string checkpoint_name = "checkpoint_" + std::to_string(checkpoint_number); @@ -29,18 +30,20 @@ setResumeFrom(const std::string& filename, const uint64_t& checkpoint_number) file.unlink("resuming_checkpoint"); } file.createHardLink("resuming_checkpoint", checkpoint); - std::cout << "Resuming checkpoint " << rang::style::bold << "successfully" << rang::style::reset << " set to " - << rang::fgB::yellow << checkpoint_number << rang::fg::reset << '\n'; + os << "Resuming checkpoint " << rang::style::bold << "successfully" << rang::style::reset << " set to " + << rang::fgB::yellow << checkpoint_number << rang::fg::reset << '\n'; } + // LCOV_EXCL_START catch (HighFive::Exception& e) { throw NormalError(e.what()); } + // LCOV_EXCL_STOP } #else // PUGS_HAS_HDF5 void -setResumeFrom(const std::string&, const uint64_t&) +setResumeFrom(const std::string&, const uint64_t&, std::ostream&) { std::cerr << rang::fgB::red << "error: " << rang::fg::reset << "setting resuming checkpoint requires HDF5\n"; } diff --git a/src/utils/checkpointing/SetResumeFrom.hpp b/src/utils/checkpointing/SetResumeFrom.hpp index c8e44e6e4319832d9018a1569f003bed509ab5d2..842c638933a7ee3083b44aebc234d8797542185a 100644 --- a/src/utils/checkpointing/SetResumeFrom.hpp +++ b/src/utils/checkpointing/SetResumeFrom.hpp @@ -2,8 +2,9 @@ #define SET_RESUME_FROM_HPP #include <cstdint> +#include <iostream> #include <string> -void setResumeFrom(const std::string& filename, const uint64_t& checkpoint_number); +void setResumeFrom(const std::string& filename, const uint64_t& checkpoint_number, std::ostream& os = std::cout); #endif // SET_RESUME_FROM_HPP diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index f533f3194b9011212b833ca0489fb05e150a3c74..b043734c94b6202c3bd08410866699d95666d397 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -157,7 +157,13 @@ add_executable (unit_tests test_WhileProcessor.cpp ) -set(checkpointing_TESTS) + set(checkpointing_TESTS + test_checkpointing_PrintCheckpointInfo.cpp + test_checkpointing_PrintScriptFrom.cpp + test_checkpointing_ResumingManager.cpp + test_checkpointing_ResumingUtils.cpp + test_checkpointing_SetResumeFrom.cpp + ) if(PUGS_HAS_HDF5) list(APPEND checkpointing_TESTS @@ -165,21 +171,21 @@ if(PUGS_HAS_HDF5) test_checkpointing_Connectivity.cpp test_checkpointing_DiscreteFunctionVariant.cpp test_checkpointing_HFTypes.cpp - test_checkpointing_ItemArray.cpp - test_checkpointing_ItemArrayVariant.cpp - test_checkpointing_ItemValue.cpp - test_checkpointing_ItemValueVariant.cpp - test_checkpointing_OStream.cpp test_checkpointing_IBoundaryDescriptor.cpp test_checkpointing_IBoundaryConditionDescriptor.cpp test_checkpointing_IQuadratureDescriptor.cpp test_checkpointing_IDiscreteFunctionDescriptor.cpp test_checkpointing_IInterfaceDescriptor.cpp test_checkpointing_INamedDiscreteData.cpp + test_checkpointing_ItemArray.cpp + test_checkpointing_ItemArrayVariant.cpp test_checkpointing_ItemType.cpp + test_checkpointing_ItemValue.cpp + test_checkpointing_ItemValueVariant.cpp test_checkpointing_IWriter.cpp test_checkpointing_IZoneDescriptor.cpp test_checkpointing_Mesh.cpp + test_checkpointing_OStream.cpp test_checkpointing_SubItemArrayPerItemVariant.cpp test_checkpointing_SubItemValuePerItemVariant.cpp test_checkpointing_Table.cpp diff --git a/tests/test_checkpointing_PrintCheckpointInfo.cpp b/tests/test_checkpointing_PrintCheckpointInfo.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8f3d28ecfddf97ab8162c0ad60e878ea67f5ff3d --- /dev/null +++ b/tests/test_checkpointing_PrintCheckpointInfo.cpp @@ -0,0 +1,162 @@ +#include <catch2/catch_test_macros.hpp> +#include <catch2/matchers/catch_matchers_all.hpp> + +#include <algebra/TinyMatrix.hpp> +#include <algebra/TinyVector.hpp> +#include <utils/HighFivePugsUtils.hpp> +#include <utils/Messenger.hpp> +#include <utils/checkpointing/PrintCheckpointInfo.hpp> + +#include <filesystem> + +// clazy:excludeall=non-pod-global-static + +TEST_CASE("PrintCheckpointInfo", "[utils/checkpointing]") +{ +#ifdef PUGS_HAS_HDF5 + + std::string tmp_dirname; + { + { + if (parallel::rank() == 0) { + tmp_dirname = [&]() -> std::string { + std::string temp_filename = std::filesystem::temp_directory_path() / "pugs_checkpointing_XXXXXX"; + return std::string{mkdtemp(&temp_filename[0])}; + }(); + } + parallel::broadcast(tmp_dirname, 0); + } + std::filesystem::path path = tmp_dirname; + const std::string filename = path / "checkpoint.h5"; + + const std::string data_file0 = R"(Un tiens vaut mieux que deux tu l'auras, +Un tiens vaut mieux que deux tu l'auras,...)"; + const std::string data_file1 = R"(All work and no play makes Jack a dull boy, +All work and no play makes Jack a dull boy,...)"; + const std::string data_file2 = R"(solo trabajo y nada de juego hacen de Jack un chico aburrido, +solo trabajo y nada de juego hacen de Jack un chico aburrido,...)"; + + std::string info = R"( * checkpoint_0 [Today] + X1 ...................... [2.7] + X2 ...................... [-2.3,4.1] + a ....................... 1.3 + b ....................... true + n ....................... 12 + z ....................... -3 + mesh0 ................... mesh + * checkpoint_1 [Tomorrow] + X3 ...................... [1.2,1.4,-1] + m ....................... 8 + z ....................... -6 + tv ...................... ([1,2], [-1.2,3]) + * checkpoint_2 [Yesterday] + M1 ...................... [[2.7]] + M2 ...................... [[-2.3,4.1],[-1.8,1.5]] + M3 ...................... [[-2.3,4.1,3.2],[-1.8,1.5,1.7],[-1.2,0.7,1.2]] + s ....................... "foo" + ts ...................... ("foo", "bar") +------------------------------------------------------- + * resuming_checkpoint -> checkpoint_1 + * last_checkpoint -> checkpoint_2 +)"; + + { + HighFive::FileAccessProps fapl; + fapl.add(HighFive::MPIOFileAccess{MPI_COMM_WORLD, MPI_INFO_NULL}); + fapl.add(HighFive::MPIOCollectiveMetadata{}); + HighFive::File file = HighFive::File(filename, HighFive::File::Truncate, fapl); + + { + HighFive::Group cp = file.createGroup("/checkpoint_0"); + cp.createAttribute("data.pgs", data_file0); + cp.createAttribute("id", 0ul); + cp.createAttribute("creation_date", std::string{"Today"}); + cp.createAttribute("checkpoint_number", 0ul); + cp.createAttribute("name", std::string{"checkpoint_0"}); + HighFive::Group symbol_table = cp.createGroup("symbol table"); + symbol_table.createAttribute("a", 1.3); + symbol_table.createAttribute("n", uint64_t{12}); + symbol_table.createAttribute("z", int64_t{-3}); + symbol_table.createAttribute("X1", TinyVector<1>{2.7}); + symbol_table.createAttribute("X2", TinyVector<2>{-2.3, 4.1}); + symbol_table.createAttribute("b", true); + HighFive::Group embedded = symbol_table.createGroup("embedded"); + HighFive::Group mesh0 = embedded.createGroup("mesh0"); + mesh0.createAttribute("type", std::string{"mesh"}); + } + + { + HighFive::Group cp = file.createGroup("/checkpoint_1"); + cp.createAttribute("data.pgs", data_file1); + cp.createAttribute("id", 1ul); + cp.createAttribute("creation_date", std::string{"Tomorrow"}); + cp.createAttribute("checkpoint_number", 1ul); + cp.createAttribute("name", std::string{"checkpoint_1"}); + HighFive::Group symbol_table = cp.createGroup("symbol table"); + symbol_table.createAttribute("m", uint64_t{8}); + symbol_table.createAttribute("z", int64_t{-6}); + symbol_table.createAttribute("X3", TinyVector<3>{1.2, 1.4, -1}); + + HighFive::Group sub_symbol_table = symbol_table.createGroup("symbol table"); + + sub_symbol_table.createAttribute("tv", std::vector{TinyVector<2>{1, 2}, TinyVector<2>{-1.2, 3}}); + + file.createHardLink("resuming_checkpoint", cp); + } + + { + HighFive::Group cp = file.createGroup("/checkpoint_2"); + cp.createAttribute("data.pgs", data_file2); + cp.createAttribute("id", 2ul); + cp.createAttribute("creation_date", std::string{"Yesterday"}); + cp.createAttribute("checkpoint_number", 2ul); + cp.createAttribute("name", std::string{"checkpoint_2"}); + HighFive::Group symbol_table = cp.createGroup("symbol table"); + symbol_table.createAttribute("s", std::string{"foo"}); + symbol_table.createAttribute("M1", TinyMatrix<1>{2.7}); + symbol_table.createAttribute("M2", TinyMatrix<2>{-2.3, 4.1, // + -1.8, 1.5}); + symbol_table.createAttribute("M3", TinyMatrix<3>{-2.3, 4.1, 3.2, // + -1.8, 1.5, 1.7, // + -1.2, 0.7, 1.2}); + + symbol_table.createAttribute("ts", std::vector<std::string>{"foo", "bar"}); + + file.createHardLink("last_checkpoint", cp); + } + } + + { + std::ostringstream os; + printCheckpointInfo(filename, os); + + if (parallel::rank() == 0) { + REQUIRE(os.str() == info); + } else { + REQUIRE(os.str() == ""); + } + } + } + + parallel::barrier(); + if (parallel::rank() == 0) { + std::filesystem::remove_all(std::filesystem::path{tmp_dirname}); + } + +#else // PUGS_HAS_HDF5 + + if (parallel::rank() == 0) { + std::cerr.setstate(std::ios::badbit); + } + + std::ostringstream os; + REQUIRE_NOTHROW(printCheckpointInfo("foo.h5", os)); + + if (parallel::rank() == 0) { + std::cerr.clear(); + } + + REQUIRE(os.str() == ""); + +#endif // PUGS_HAS_HDF5 +} diff --git a/tests/test_checkpointing_PrintScriptFrom.cpp b/tests/test_checkpointing_PrintScriptFrom.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0896124064e6aa2542f9d4f0996b961485845be2 --- /dev/null +++ b/tests/test_checkpointing_PrintScriptFrom.cpp @@ -0,0 +1,109 @@ +#include <catch2/catch_test_macros.hpp> +#include <catch2/matchers/catch_matchers_all.hpp> + +#include <utils/HighFivePugsUtils.hpp> +#include <utils/Messenger.hpp> +#include <utils/checkpointing/PrintScriptFrom.hpp> + +#include <filesystem> + +// clazy:excludeall=non-pod-global-static + +TEST_CASE("checkpointing_PrintScriptFrom", "[utils/checkpointing]") +{ +#ifdef PUGS_HAS_HDF5 + + std::string tmp_dirname; + { + { + if (parallel::rank() == 0) { + tmp_dirname = [&]() -> std::string { + std::string temp_filename = std::filesystem::temp_directory_path() / "pugs_checkpointing_XXXXXX"; + return std::string{mkdtemp(&temp_filename[0])}; + }(); + } + parallel::broadcast(tmp_dirname, 0); + } + std::filesystem::path path = tmp_dirname; + const std::string filename = path / "checkpoint.h5"; + + const std::string data_file0 = R"(Un tiens vaut mieux que deux tu l'auras, +Un tiens vaut mieux que deux tu l'auras,...)"; + const std::string data_file1 = R"(All work and no play makes Jack a dull boy, +All work and no play makes Jack a dull boy,...)"; + const std::string data_file2 = R"(solo trabajo y nada de juego hacen de Jack un chico aburrido, +solo trabajo y nada de juego hacen de Jack un chico aburrido,...)"; + + { + HighFive::FileAccessProps fapl; + fapl.add(HighFive::MPIOFileAccess{MPI_COMM_WORLD, MPI_INFO_NULL}); + fapl.add(HighFive::MPIOCollectiveMetadata{}); + HighFive::File file = HighFive::File(filename, HighFive::File::Truncate, fapl); + + file.createGroup("/checkpoint_0").createAttribute("data.pgs", data_file0); + file.createGroup("/checkpoint_1").createAttribute("data.pgs", data_file1); + file.createGroup("/checkpoint_2").createAttribute("data.pgs", data_file2); + } + + { + std::ostringstream os; + printScriptFrom(filename, 0, os); + REQUIRE(os.str() == data_file0); + } + + { + std::ostringstream os; + printScriptFrom(filename, 1, os); + REQUIRE(os.str() == data_file1); + } + + { + std::ostringstream os; + printScriptFrom(filename, 2, os); + REQUIRE(os.str() == data_file2); + } + + { + std::ostringstream error_msg; + error_msg << "error: cannot find checkpoint " << 12 << " in " << filename; + REQUIRE_THROWS_WITH(printScriptFrom(filename, 12), error_msg.str()); + } + + { + const std::string malformed_filename = path / "malformed.h5"; + + { + HighFive::FileAccessProps fapl; + fapl.add(HighFive::MPIOFileAccess{MPI_COMM_WORLD, MPI_INFO_NULL}); + fapl.add(HighFive::MPIOCollectiveMetadata{}); + HighFive::File file = HighFive::File(malformed_filename, HighFive::File::Truncate, fapl); + + file.createGroup("/checkpoint_0"); + } + REQUIRE_THROWS_WITH(printScriptFrom(malformed_filename, 0), + "error: Unable to open the attribute \"data.pgs\": (Attribute) Object not found"); + } + } + + parallel::barrier(); + if (parallel::rank() == 0) { + std::filesystem::remove_all(std::filesystem::path{tmp_dirname}); + } + +#else // PUGS_HAS_HDF5 + + if (parallel::rank() == 0) { + std::cerr.setstate(std::ios::badbit); + } + + std::ostringstream os; + REQUIRE_NOTHROW(printScriptFrom("foo.h5", 0, os)); + + if (parallel::rank() == 0) { + std::cerr.clear(); + } + + REQUIRE(os.str() == ""); + +#endif // PUGS_HAS_HDF5 +} diff --git a/tests/test_checkpointing_ResumingManager.cpp b/tests/test_checkpointing_ResumingManager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1d5da95081d179894d66fe04306af877e0af676b --- /dev/null +++ b/tests/test_checkpointing_ResumingManager.cpp @@ -0,0 +1,95 @@ +#include <catch2/catch_test_macros.hpp> +#include <catch2/matchers/catch_matchers_all.hpp> + +#include <utils/HighFivePugsUtils.hpp> +#include <utils/Messenger.hpp> +#include <utils/checkpointing/ResumingManager.hpp> + +#include <filesystem> + +// clazy:excludeall=non-pod-global-static + +TEST_CASE("checkpointing_ResumingManager", "[utils/checkpointing]") +{ + // Need to destroy the instance created in the main for tests + REQUIRE_NOTHROW(ResumingManager::destroy()); + +#ifndef NDEBUG + REQUIRE_THROWS_WITH(ResumingManager::destroy(), "Resuming manager was not created"); + REQUIRE_THROWS_WITH(ResumingManager::getInstance(), "instance was not created"); +#endif // NDEBUG + + REQUIRE_NOTHROW(ResumingManager::create()); + +#ifndef NDEBUG + REQUIRE_THROWS_WITH(ResumingManager::create(), "Resuming manager was already created"); +#endif // NDEBUG + +#ifdef PUGS_HAS_HDF5 + std::string tmp_dirname; + { + { + if (parallel::rank() == 0) { + tmp_dirname = [&]() -> std::string { + std::string temp_filename = std::filesystem::temp_directory_path() / "pugs_checkpointing_XXXXXX"; + return std::string{mkdtemp(&temp_filename[0])}; + }(); + } + parallel::broadcast(tmp_dirname, 0); + } + std::filesystem::path path = tmp_dirname; + const std::string filename = path / "checkpoint.h5"; + + { + HighFive::FileAccessProps fapl; + fapl.add(HighFive::MPIOFileAccess{MPI_COMM_WORLD, MPI_INFO_NULL}); + fapl.add(HighFive::MPIOCollectiveMetadata{}); + HighFive::File file = HighFive::File(filename, HighFive::File::Truncate, fapl); + + { + HighFive::Group cp = file.createGroup("/resuming_checkpoint"); + cp.createAttribute("id", 13ul); + } + } + + parallel::barrier(); + REQUIRE_NOTHROW(ResumingManager::getInstance().setFilename(filename)); + REQUIRE(ResumingManager::getInstance().filename() == filename); + + REQUIRE(ResumingManager::getInstance().checkpointId() == 13); + } + + parallel::barrier(); + if (parallel::rank() == 0) { + std::filesystem::remove_all(std::filesystem::path{tmp_dirname}); + } + +#else // PUGS_HAS_HDF5 + + ResumingManager::getInstance().setFilename("useless"); + REQUIRE(ResumingManager::getInstance().filename() == "useless"); + REQUIRE(ResumingManager::getInstance().checkpointId() == 0); + +#endif // PUGS_HAS_HDF5 + + ResumingManager::getInstance().currentASTLevel() = 3; + ResumingManager::getInstance().checkpointNumber() = 7; + ResumingManager::getInstance().setIsResuming(false); + + REQUIRE(ResumingManager::getInstance().currentASTLevel() == 3); + REQUIRE(ResumingManager::getInstance().checkpointNumber() == 7); + REQUIRE(not ResumingManager::getInstance().isResuming()); + + ResumingManager::getInstance().currentASTLevel() = 1; + ResumingManager::getInstance().checkpointNumber() = 5; + ResumingManager::getInstance().setIsResuming(true); + + REQUIRE(ResumingManager::getInstance().currentASTLevel() == 1); + REQUIRE(ResumingManager::getInstance().checkpointNumber() == 5); + REQUIRE(ResumingManager::getInstance().isResuming()); + + REQUIRE_NOTHROW(ResumingManager::destroy()); + + // Recreate ResumingManager for remaining tests + REQUIRE_NOTHROW(ResumingManager::create()); +} diff --git a/tests/test_checkpointing_ResumingUtils.cpp b/tests/test_checkpointing_ResumingUtils.cpp new file mode 100644 index 0000000000000000000000000000000000000000..23c8e7e99d36e009a2f8f6b04d53da4c28183331 --- /dev/null +++ b/tests/test_checkpointing_ResumingUtils.cpp @@ -0,0 +1,77 @@ +#include <catch2/catch_test_macros.hpp> +#include <catch2/matchers/catch_matchers_all.hpp> + +#include <utils/HighFivePugsUtils.hpp> +#include <utils/Messenger.hpp> +#include <utils/checkpointing/ResumingUtils.hpp> + +#include <filesystem> + +// clazy:excludeall=non-pod-global-static + +TEST_CASE("checkpointing_ResumingUtils", "[utils/checkpointing]") +{ +#ifdef PUGS_HAS_HDF5 + + std::string tmp_dirname; + { + { + if (parallel::rank() == 0) { + tmp_dirname = [&]() -> std::string { + std::string temp_filename = std::filesystem::temp_directory_path() / "pugs_checkpointing_XXXXXX"; + return std::string{mkdtemp(&temp_filename[0])}; + }(); + } + parallel::broadcast(tmp_dirname, 0); + } + std::filesystem::path path = tmp_dirname; + const std::string filename = path / "checkpoint.h5"; + + const std::string data_file0 = R"(Un tiens vaut mieux que deux tu l'auras, +Un tiens vaut mieux que deux tu l'auras,...)"; + const std::string data_file1 = R"(All work and no play makes Jack a dull boy, +All work and no play makes Jack a dull boy,...)"; + const std::string data_file2 = R"(solo trabajo y nada de juego hacen de Jack un chico aburrido, +solo trabajo y nada de juego hacen de Jack un chico aburrido,...)"; + + HighFive::FileAccessProps fapl; + fapl.add(HighFive::MPIOFileAccess{MPI_COMM_WORLD, MPI_INFO_NULL}); + fapl.add(HighFive::MPIOCollectiveMetadata{}); + HighFive::File file = HighFive::File(filename, HighFive::File::Truncate, fapl); + + file.createGroup("/checkpoint_0").createAttribute("data.pgs", data_file0); + file.createGroup("/checkpoint_1").createAttribute("data.pgs", data_file1); + file.createGroup("/checkpoint_2").createAttribute("data.pgs", data_file2); + file.createHardLink("last_checkpoint", file.getGroup("/checkpoint_2")); + + file.createHardLink("resuming_checkpoint", file.getGroup("/checkpoint_0")); + file.flush(); + parallel::barrier(); + REQUIRE(resumingDatafile(filename) == data_file0); + parallel::barrier(); + + file.unlink("resuming_checkpoint"); + file.createHardLink("resuming_checkpoint", file.getGroup("/checkpoint_1")); + file.flush(); + parallel::barrier(); + REQUIRE(resumingDatafile(filename) == data_file1); + parallel::barrier(); + + file.unlink("resuming_checkpoint"); + file.createHardLink("resuming_checkpoint", file.getGroup("/checkpoint_2")); + file.flush(); + parallel::barrier(); + REQUIRE(resumingDatafile(filename) == data_file2); + } + + parallel::barrier(); + if (parallel::rank() == 0) { + std::filesystem::remove_all(std::filesystem::path{tmp_dirname}); + } + +#else // PUGS_HAS_HDF5 + + REQUIRE_THROWS_WITH(resumingDatafile("foo.h5"), "error: Resuming requires HDF5"); + +#endif // PUGS_HAS_HDF5 +} diff --git a/tests/test_checkpointing_SetResumeFrom.cpp b/tests/test_checkpointing_SetResumeFrom.cpp new file mode 100644 index 0000000000000000000000000000000000000000..bead609386049914cc3a55edcab77651f67812e4 --- /dev/null +++ b/tests/test_checkpointing_SetResumeFrom.cpp @@ -0,0 +1,112 @@ +#include <catch2/catch_test_macros.hpp> +#include <catch2/matchers/catch_matchers_all.hpp> + +#include <utils/HighFivePugsUtils.hpp> +#include <utils/Messenger.hpp> +#include <utils/checkpointing/SetResumeFrom.hpp> + +#include <filesystem> + +// clazy:excludeall=non-pod-global-static + +TEST_CASE("checkpointing_SetResumeFrom", "[utils/checkpointing]") +{ +#ifdef PUGS_HAS_HDF5 + + std::string tmp_dirname; + { + { + if (parallel::rank() == 0) { + tmp_dirname = [&]() -> std::string { + std::string temp_filename = std::filesystem::temp_directory_path() / "pugs_checkpointing_XXXXXX"; + return std::string{mkdtemp(&temp_filename[0])}; + }(); + } + parallel::broadcast(tmp_dirname, 0); + } + std::filesystem::path path = tmp_dirname; + const std::string filename = path / "checkpoint.h5"; + + const std::string data_file0 = R"(Un tiens vaut mieux que deux tu l'auras, +Un tiens vaut mieux que deux tu l'auras,...)"; + const std::string data_file1 = R"(All work and no play makes Jack a dull boy, +All work and no play makes Jack a dull boy,...)"; + const std::string data_file2 = R"(solo trabajo y nada de juego hacen de Jack un chico aburrido, +solo trabajo y nada de juego hacen de Jack un chico aburrido,...)"; + + { + HighFive::FileAccessProps fapl; + fapl.add(HighFive::MPIOFileAccess{MPI_COMM_WORLD, MPI_INFO_NULL}); + fapl.add(HighFive::MPIOCollectiveMetadata{}); + HighFive::File file = HighFive::File(filename, HighFive::File::Truncate, fapl); + + file.createGroup("/checkpoint_0").createAttribute("data.pgs", data_file0); + file.createGroup("/checkpoint_1").createAttribute("data.pgs", data_file1); + file.createGroup("/checkpoint_2").createAttribute("data.pgs", data_file2); + } + + { + std::ostringstream os; + setResumeFrom(filename, 0, os); + REQUIRE(os.str() == "Resuming checkpoint successfully set to 0\n"); + + HighFive::FileAccessProps fapl; + fapl.add(HighFive::MPIOFileAccess{MPI_COMM_WORLD, MPI_INFO_NULL}); + fapl.add(HighFive::MPIOCollectiveMetadata{}); + HighFive::File file = HighFive::File(filename, HighFive::File::ReadOnly, fapl); + REQUIRE(file.getGroup("/resuming_checkpoint").getAttribute("data.pgs").read<std::string>() == data_file0); + } + + { + std::ostringstream os; + setResumeFrom(filename, 1, os); + REQUIRE(os.str() == "Resuming checkpoint successfully set to 1\n"); + + HighFive::FileAccessProps fapl; + fapl.add(HighFive::MPIOFileAccess{MPI_COMM_WORLD, MPI_INFO_NULL}); + fapl.add(HighFive::MPIOCollectiveMetadata{}); + HighFive::File file = HighFive::File(filename, HighFive::File::ReadOnly, fapl); + REQUIRE(file.getGroup("/resuming_checkpoint").getAttribute("data.pgs").read<std::string>() == data_file1); + } + + { + std::ostringstream os; + setResumeFrom(filename, 2, os); + REQUIRE(os.str() == "Resuming checkpoint successfully set to 2\n"); + + HighFive::FileAccessProps fapl; + fapl.add(HighFive::MPIOFileAccess{MPI_COMM_WORLD, MPI_INFO_NULL}); + fapl.add(HighFive::MPIOCollectiveMetadata{}); + HighFive::File file = HighFive::File(filename, HighFive::File::ReadOnly, fapl); + REQUIRE(file.getGroup("/resuming_checkpoint").getAttribute("data.pgs").read<std::string>() == data_file2); + } + + { + std::ostringstream error_msg; + error_msg << "error: cannot find checkpoint " << 12 << " in " << filename; + REQUIRE_THROWS_WITH(setResumeFrom(filename, 12), error_msg.str()); + } + } + + parallel::barrier(); + if (parallel::rank() == 0) { + std::filesystem::remove_all(std::filesystem::path{tmp_dirname}); + } + +#else // PUGS_HAS_HDF5 + + if (parallel::rank() == 0) { + std::cerr.setstate(std::ios::badbit); + } + + std::ostringstream os; + REQUIRE_NOTHROW(setResumeFrom("foo.h5", 0, os)); + + if (parallel::rank() == 0) { + std::cerr.clear(); + } + + REQUIRE(os.str() == ""); + +#endif // PUGS_HAS_HDF5 +}