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