GCC Code Coverage Report


Directory: ./
File: modules/util/include/util.hpp
Date: 2026-07-02 01:34:19
Exec Total Coverage
Lines: 38 39 97.4%
Functions: 26 26 100.0%
Branches: 42 86 48.8%

Line Branch Exec Source
1 #pragma once
2
3 #include <algorithm>
4 #include <array>
5 #include <atomic>
6 #include <cctype>
7 #include <cstdint>
8 #include <cstdlib>
9 #include <filesystem>
10 #include <memory>
11 #include <sstream>
12 #include <string>
13 #include <string_view>
14 #include <system_error>
15 #include <typeinfo>
16 #ifdef __GNUG__
17 # include <cxxabi.h>
18 #endif
19
20 #include "nlohmann/json_fwd.hpp"
21
22 #ifdef _MSC_VER
23 # pragma warning(push)
24 # pragma warning(disable : 4459)
25 #endif
26
27 #include <gtest/gtest.h>
28
29 #include <libenvpp/detail/environment.hpp>
30 #include <libenvpp/detail/get.hpp>
31 #include <nlohmann/json.hpp>
32
33 /// @brief JSON namespace used for settings and config parsing.
34 using NlohmannJsonParseError = nlohmann::json::parse_error;
35 /// @brief JSON namespace used for settings and config typing.
36 using NlohmannJsonTypeError = nlohmann::json::type_error;
37 #ifdef _MSC_VER
38 # pragma warning(pop)
39 #endif
40
41 namespace ppc::util {
42
43 /// @brief Utility class for tracking destructor failure across tests.
44 /// @details Provides thread-safe methods to set, unset, and check the failure flag.
45 class DestructorFailureFlag {
46 public:
47 /// @brief Marks that a destructor failure has occurred.
48 static void Set() {
49 failure_flag.store(true);
50 20 }
51
52 /// @brief Clears the destructor failure flag.
53 static void Unset() {
54 failure_flag.store(false);
55 }
56
57 /// @brief Checks if a destructor failure was recorded.
58 /// @return True if failure occurred, false otherwise.
59 static bool Get() {
60 return failure_flag.load();
61 }
62
63 private:
64 inline static std::atomic<bool> failure_flag{false};
65 };
66
67 class PerformanceFailureFlag {
68 public:
69 static void Set() {
70 failure_flag.store(true);
71 }
72
73 static void Unset() {
74 failure_flag.store(false);
75 }
76
77 static bool Get() {
78 return failure_flag.load();
79 }
80
81 private:
82 inline static std::atomic<bool> failure_flag{false};
83 };
84
85 enum class GTestParamIndex : uint8_t {
86 kTaskGetter,
87 kNameTest,
88 kTestParams,
89 kTaskDescriptor,
90 };
91
92 std::string GetAbsoluteTaskPath(const std::string &id_path, const std::string &relative_path);
93 int GetNumThreads();
94 int GetNumProc();
95 double GetTaskMaxTime();
96 double GetPerfMaxTime();
97 double GetTimeMPI();
98 int GetMPIRank();
99 void ConfigureMpiEnvironment();
100 void SynchronizeMpiRanks();
101
102 template <typename T>
103
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 318 times.
776 std::string GetNamespace() {
104 776 std::string name = typeid(T).name();
105 #ifdef __GNUC__
106
1/2
✓ Branch 1 taken 388 times.
✗ Branch 2 not taken.
776 int status = 0;
107
1/2
✓ Branch 1 taken 388 times.
✗ Branch 2 not taken.
776 std::unique_ptr<char, void (*)(void *)> demangled{abi::__cxa_demangle(name.c_str(), nullptr, nullptr, &status),
108 std::free};
109
4/8
✓ Branch 0 taken 388 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 388 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 348 times.
✓ Branch 7 taken 40 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
1552 name = (status == 0) ? demangled.get() : name;
110 #endif
111 #ifdef _MSC_VER
112 const std::string prefixes[] = {"class ", "struct ", "enum ", "union "};
113 for (const auto &prefix : prefixes) {
114 if (name.starts_with(prefix)) {
115 name = name.substr(prefix.size());
116 break;
117 }
118 }
119 name.erase(0, name.find_first_not_of(' '));
120 #endif
121 auto pos = name.rfind("::");
122
3/4
✓ Branch 0 taken 348 times.
✓ Branch 1 taken 40 times.
✓ Branch 3 taken 348 times.
✗ Branch 4 not taken.
1552 return (pos != std::string::npos) ? name.substr(0, pos) : std::string{};
123 }
124
125 inline std::shared_ptr<nlohmann::json> InitJSONPtr() {
126 return std::make_shared<nlohmann::json>();
127 }
128
129 bool IsUnderMpirun();
130
131 namespace test {
132
133 168 [[nodiscard]] inline std::string SanitizeToken(std::string_view token_sv) {
134
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 168 times.
168 std::string token{token_sv};
135 8976 auto is_allowed = [](char c) -> bool {
136
4/6
✓ Branch 0 taken 168 times.
✓ Branch 1 taken 8808 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 168 times.
✓ Branch 4 taken 168 times.
✗ Branch 5 not taken.
8976 return std::isalnum(static_cast<unsigned char>(c)) || c == '_' || c == '-' || c == '.';
137 };
138 std::ranges::replace(token, ' ', '_');
139
2/2
✓ Branch 0 taken 8976 times.
✓ Branch 1 taken 168 times.
9144 for (char &ch : token) {
140
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8976 times.
8976 if (!is_allowed(ch)) {
141 ch = '_';
142 }
143 }
144 168 return token;
145 }
146
147
0/4
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
168 class ScopedPerTestEnv {
148 public:
149 168 explicit ScopedPerTestEnv(const std::string &token)
150
4/10
✗ Branch 0 not taken.
✓ Branch 1 taken 168 times.
✓ Branch 4 taken 168 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 168 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 168 times.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
504 : set_uid_("PPC_TEST_UID", token), set_tmp_("PPC_TEST_TMPDIR", CreateTmpDir(token)) {}
151
152 private:
153 168 static std::string CreateTmpDir(const std::string &token) {
154 namespace fs = std::filesystem;
155 24 auto make_rank_suffix = []() -> std::string {
156 // Derive rank from common MPI env vars without including MPI headers
157 24 constexpr std::array<std::string_view, 5> kRankVars = {"OMPI_COMM_WORLD_RANK", "PMI_RANK", "PMIX_RANK",
158 "SLURM_PROCID", "MSMPI_RANK"};
159
1/2
✓ Branch 0 taken 24 times.
✗ Branch 1 not taken.
24 for (auto name : kRankVars) {
160
2/4
✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 24 times.
✗ Branch 4 not taken.
24 if (auto r = env::get<int>(name); r.has_value() && r.value() >= 0) {
161
3/8
✓ Branch 2 taken 24 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 24 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 24 times.
✗ Branch 8 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
72 return std::string("_rank_") + std::to_string(r.value());
162 }
163 }
164 return std::string{};
165 };
166
2/2
✓ Branch 1 taken 24 times.
✓ Branch 2 taken 144 times.
168 const std::string rank_suffix = IsUnderMpirun() ? make_rank_suffix() : std::string{};
167
5/12
✓ Branch 1 taken 168 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 168 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 168 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 168 times.
✗ Branch 11 not taken.
✓ Branch 15 taken 168 times.
✗ Branch 16 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
504 const fs::path tmp = fs::temp_directory_path() / (std::string("ppc_test_") + token + rank_suffix);
168 std::error_code ec;
169
1/2
✓ Branch 1 taken 168 times.
✗ Branch 2 not taken.
168 fs::create_directories(tmp, ec);
170 (void)ec;
171 168 return tmp.string();
172 168 }
173
174 env::detail::set_scoped_environment_variable set_uid_;
175 env::detail::set_scoped_environment_variable set_tmp_;
176 };
177
178 168 [[nodiscard]] inline std::string MakeCurrentGTestToken(std::string_view fallback_name) {
179 168 const auto *unit = ::testing::UnitTest::GetInstance();
180
1/2
✓ Branch 0 taken 168 times.
✗ Branch 1 not taken.
168 const auto *info = (unit != nullptr) ? unit->current_test_info() : nullptr;
181 168 std::ostringstream os;
182
1/2
✓ Branch 0 taken 168 times.
✗ Branch 1 not taken.
168 if (info != nullptr) {
183
2/4
✓ Branch 1 taken 168 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 168 times.
✗ Branch 5 not taken.
336 os << info->test_suite_name() << "." << info->name();
184 } else {
185 os << fallback_name;
186 }
187
1/2
✓ Branch 1 taken 168 times.
✗ Branch 2 not taken.
336 return SanitizeToken(os.str());
188 168 }
189
190 168 inline ScopedPerTestEnv MakePerTestEnvForCurrentGTest(std::string_view fallback_name) {
191
1/2
✓ Branch 2 taken 168 times.
✗ Branch 3 not taken.
336 return ScopedPerTestEnv(MakeCurrentGTestToken(fallback_name));
192 }
193
194 } // namespace test
195
196 } // namespace ppc::util
197