| 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 | 30 | } | |
| 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 | enum class GTestParamIndex : uint8_t { | ||
| 68 | kTaskGetter, | ||
| 69 | kNameTest, | ||
| 70 | kTestParams, | ||
| 71 | }; | ||
| 72 | |||
| 73 | std::string GetAbsoluteTaskPath(const std::string &id_path, const std::string &relative_path); | ||
| 74 | int GetNumThreads(); | ||
| 75 | int GetNumProc(); | ||
| 76 | double GetTaskMaxTime(); | ||
| 77 | double GetPerfMaxTime(); | ||
| 78 | |||
| 79 | template <typename T> | ||
| 80 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 944 times.
|
1998 | std::string GetNamespace() { |
| 81 | 1998 | std::string name = typeid(T).name(); | |
| 82 | #ifdef __GNUC__ | ||
| 83 |
1/2✓ Branch 1 taken 964 times.
✗ Branch 2 not taken.
|
1998 | int status = 0; |
| 84 |
1/2✓ Branch 1 taken 964 times.
✗ Branch 2 not taken.
|
1998 | std::unique_ptr<char, void (*)(void *)> demangled{abi::__cxa_demangle(name.c_str(), nullptr, nullptr, &status), |
| 85 | std::free}; | ||
| 86 |
4/8✓ Branch 0 taken 964 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 964 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 944 times.
✓ Branch 7 taken 20 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
|
3996 | name = (status == 0) ? demangled.get() : name; |
| 87 | #endif | ||
| 88 | #ifdef _MSC_VER | ||
| 89 | const std::string prefixes[] = {"class ", "struct ", "enum ", "union "}; | ||
| 90 | for (const auto &prefix : prefixes) { | ||
| 91 | if (name.starts_with(prefix)) { | ||
| 92 | name = name.substr(prefix.size()); | ||
| 93 | break; | ||
| 94 | } | ||
| 95 | } | ||
| 96 | name.erase(0, name.find_first_not_of(' ')); | ||
| 97 | #endif | ||
| 98 | auto pos = name.rfind("::"); | ||
| 99 |
3/4✓ Branch 0 taken 944 times.
✓ Branch 1 taken 20 times.
✓ Branch 3 taken 944 times.
✗ Branch 4 not taken.
|
3996 | return (pos != std::string::npos) ? name.substr(0, pos) : std::string{}; |
| 100 | } | ||
| 101 | |||
| 102 | inline std::shared_ptr<nlohmann::json> InitJSONPtr() { | ||
| 103 |
2/3✓ Branch 1 taken 924 times.
✓ Branch 2 taken 200 times.
✗ Branch 3 not taken.
|
1184 | return std::make_shared<nlohmann::json>(); |
| 104 | } | ||
| 105 | |||
| 106 | bool IsUnderMpirun(); | ||
| 107 | |||
| 108 | namespace test { | ||
| 109 | |||
| 110 | 168 | [[nodiscard]] inline std::string SanitizeToken(std::string_view token_sv) { | |
| 111 | 168 | std::string token{token_sv}; | |
| 112 | 17676 | auto is_allowed = [](char c) { | |
| 113 |
6/6✓ Branch 0 taken 1908 times.
✓ Branch 1 taken 15768 times.
✓ Branch 2 taken 1404 times.
✓ Branch 3 taken 504 times.
✓ Branch 4 taken 168 times.
✓ Branch 5 taken 336 times.
|
17676 | return std::isalnum(static_cast<unsigned char>(c)) || c == '_' || c == '-' || c == '.'; |
| 114 | }; | ||
| 115 | std::ranges::replace(token, ' ', '_'); | ||
| 116 |
2/2✓ Branch 0 taken 17676 times.
✓ Branch 1 taken 168 times.
|
17844 | for (char &ch : token) { |
| 117 |
2/2✓ Branch 0 taken 336 times.
✓ Branch 1 taken 17340 times.
|
17676 | if (!is_allowed(ch)) { |
| 118 | 336 | ch = '_'; | |
| 119 | } | ||
| 120 | } | ||
| 121 | 168 | return token; | |
| 122 | } | ||
| 123 | |||
| 124 | 168 | class ScopedPerTestEnv { | |
| 125 | public: | ||
| 126 | 168 | explicit ScopedPerTestEnv(const std::string &token) | |
| 127 |
2/4✓ Branch 2 taken 168 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 168 times.
✗ Branch 6 not taken.
|
168 | : set_uid_("PPC_TEST_UID", token), set_tmp_("PPC_TEST_TMPDIR", CreateTmpDir(token)) {} |
| 128 | |||
| 129 | private: | ||
| 130 | 168 | static std::string CreateTmpDir(const std::string &token) { | |
| 131 | namespace fs = std::filesystem; | ||
| 132 | 24 | auto make_rank_suffix = []() -> std::string { | |
| 133 | // Derive rank from common MPI env vars without including MPI headers | ||
| 134 | 24 | constexpr std::array<std::string_view, 5> kRankVars = {"OMPI_COMM_WORLD_RANK", "PMI_RANK", "PMIX_RANK", | |
| 135 | "SLURM_PROCID", "MSMPI_RANK"}; | ||
| 136 |
1/2✓ Branch 0 taken 24 times.
✗ Branch 1 not taken.
|
24 | for (auto name : kRankVars) { |
| 137 |
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) { |
| 138 |
3/8✓ Branch 2 taken 24 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 24 times.
✗ Branch 6 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 24 times.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
|
48 | return std::string("_rank_") + std::to_string(r.value()); |
| 139 | } | ||
| 140 | } | ||
| 141 | return std::string{}; | ||
| 142 | }; | ||
| 143 |
2/2✓ Branch 1 taken 24 times.
✓ Branch 2 taken 144 times.
|
168 | const std::string rank_suffix = IsUnderMpirun() ? make_rank_suffix() : std::string{}; |
| 144 |
5/10✓ 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.
|
504 | const fs::path tmp = fs::temp_directory_path() / (std::string("ppc_test_") + token + rank_suffix); |
| 145 | std::error_code ec; | ||
| 146 |
1/2✓ Branch 1 taken 168 times.
✗ Branch 2 not taken.
|
168 | fs::create_directories(tmp, ec); |
| 147 | (void)ec; | ||
| 148 | 168 | return tmp.string(); | |
| 149 | 168 | } | |
| 150 | |||
| 151 | env::detail::set_scoped_environment_variable set_uid_; | ||
| 152 | env::detail::set_scoped_environment_variable set_tmp_; | ||
| 153 | }; | ||
| 154 | |||
| 155 | 168 | [[nodiscard]] inline std::string MakeCurrentGTestToken(std::string_view fallback_name) { | |
| 156 | 168 | const auto *unit = ::testing::UnitTest::GetInstance(); | |
| 157 |
1/2✓ Branch 0 taken 168 times.
✗ Branch 1 not taken.
|
168 | const auto *info = (unit != nullptr) ? unit->current_test_info() : nullptr; |
| 158 | 168 | std::ostringstream os; | |
| 159 |
1/2✓ Branch 0 taken 168 times.
✗ Branch 1 not taken.
|
168 | if (info != nullptr) { |
| 160 |
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(); |
| 161 | } else { | ||
| 162 | os << fallback_name; | ||
| 163 | } | ||
| 164 |
1/2✓ Branch 1 taken 168 times.
✗ Branch 2 not taken.
|
336 | return SanitizeToken(os.str()); |
| 165 | 168 | } | |
| 166 | |||
| 167 | 168 | inline ScopedPerTestEnv MakePerTestEnvForCurrentGTest(std::string_view fallback_name) { | |
| 168 |
1/2✓ Branch 2 taken 168 times.
✗ Branch 3 not taken.
|
336 | return ScopedPerTestEnv(MakeCurrentGTestToken(fallback_name)); |
| 169 | } | ||
| 170 | |||
| 171 | } // namespace test | ||
| 172 | |||
| 173 | } // namespace ppc::util | ||
| 174 |