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
+}