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/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/tests/CMakeLists.txt b/tests/CMakeLists.txt index 1015ec89507040502b649b5a8b33760e0d83ae26..e38fbdd1241ba64f49b95f706fbc777bd0b87b4b 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -158,6 +158,7 @@ add_executable (unit_tests ) set(checkpointing_TESTS + test_checkpointing_PrintCheckpointInfo.cpp test_checkpointing_PrintScriptFrom.cpp test_checkpointing_ResumingUtils.cpp test_checkpointing_SetResumeFrom.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 +}