From f14017cdf33c98b3f1eea8dfc0c33753245aed77 Mon Sep 17 00:00:00 2001 From: Alexander Nesterov Date: Sun, 29 Jun 2025 17:07:21 +0200 Subject: [PATCH 01/29] refactor GetNamespace function implementation --- modules/core/util/include/util.hpp | 63 ++++++------------------------ 1 file changed, 11 insertions(+), 52 deletions(-) diff --git a/modules/core/util/include/util.hpp b/modules/core/util/include/util.hpp index 9d48d5b0a..db0f67efe 100644 --- a/modules/core/util/include/util.hpp +++ b/modules/core/util/include/util.hpp @@ -50,60 +50,19 @@ int GetNumThreads(); template constexpr std::string_view GetNamespace() { -#if defined(__clang__) || defined(__GNUC__) - constexpr std::string_view kFunc = __PRETTY_FUNCTION__; - constexpr std::string_view kKey = "T = "; - - auto start = kFunc.find(kKey); - if (start == std::string_view::npos) { - return {}; - } - start += kKey.size(); - - auto end = kFunc.find_first_of(";]> ,", start); - if (end == std::string_view::npos) { - return {}; - } - - auto full_type = kFunc.substr(start, end - start); - - auto ns_end = full_type.rfind("::"); - if (ns_end == std::string_view::npos) { - return {}; - } - - return full_type.substr(0, ns_end); - -#elif defined(_MSC_VER) - constexpr std::string_view kFunc = __FUNCSIG__; - constexpr std::string_view kKey = "GetNamespace<"; - - auto start = kFunc.find(kKey); - if (start == std::string_view::npos) return {}; - start += kKey.size(); - - constexpr std::string_view prefixes[] = {"class ", "struct ", "enum ", "union "}; - for (auto prefix : prefixes) { - if (kFunc.substr(start, prefix.size()) == prefix) { - start += prefix.size(); - break; - } - } - - auto end = kFunc.find('>', start); - if (end == std::string_view::npos) return {}; - - auto full_type = kFunc.substr(start, end - start); - - auto ns_end = full_type.rfind("::"); - if (ns_end == std::string_view::npos) return {}; - - return full_type.substr(0, ns_end); - +#if defined(_MSC_VER) + constexpr auto func = std::string_view{__FUNCSIG__}; + auto start = func.find("GetNamespace<") + 13; + for (auto p : {"class ", "struct ", "enum ", "union "}) + if (func.substr(start).starts_with(p)) start += p.size(); #else - static_assert([] { return false; }(), "Unsupported compiler"); - return {}; + constexpr auto func = std::string_view{__PRETTY_FUNCTION__}; + auto start = func.find("T = ") + 4; #endif + auto end = func.find_first_of(";]> ,>", start); + if (end == std::string_view::npos) return {}; + auto ns_end = func.rfind("::", end); + return (ns_end != std::string_view::npos && ns_end > start) ? func.substr(start, ns_end - start) : std::string_view{}; } inline std::shared_ptr InitJSONPtr() { return std::make_shared(); } From 7cf5d51b1da194e6bb63a9558dda21fd4a325bfd Mon Sep 17 00:00:00 2001 From: Alexander Nesterov Date: Sun, 29 Jun 2025 17:09:20 +0200 Subject: [PATCH 02/29] comment out unused dependencies in ubuntu.yml --- .github/workflows/ubuntu.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index c0ba92605..29ede8744 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -332,9 +332,9 @@ jobs: PPC_NUM_PROC: 1 PPC_ASAN_RUN: 1 gcc-build-codecov: - needs: - - gcc-test-extended - - clang-test-extended +# needs: +# - gcc-test-extended +# - clang-test-extended runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 From 9abeb4504ca45c4157dc5440cb480b6f03d67f54 Mon Sep 17 00:00:00 2001 From: Alexander Nesterov Date: Sun, 29 Jun 2025 17:18:12 +0200 Subject: [PATCH 03/29] refactor GetNamespace to simplify logic and improve readability --- modules/core/util/include/util.hpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/modules/core/util/include/util.hpp b/modules/core/util/include/util.hpp index db0f67efe..625fd8cd4 100644 --- a/modules/core/util/include/util.hpp +++ b/modules/core/util/include/util.hpp @@ -51,18 +51,21 @@ int GetNumThreads(); template constexpr std::string_view GetNamespace() { #if defined(_MSC_VER) - constexpr auto func = std::string_view{__FUNCSIG__}; + constexpr std::string_view func{__FUNCSIG__}; auto start = func.find("GetNamespace<") + 13; for (auto p : {"class ", "struct ", "enum ", "union "}) - if (func.substr(start).starts_with(p)) start += p.size(); + if (func.substr(start).starts_with(p)) { + start += std::string_view{p}.size(); + break; + } #else - constexpr auto func = std::string_view{__PRETTY_FUNCTION__}; + constexpr std::string_view func{__PRETTY_FUNCTION__}; auto start = func.find("T = ") + 4; #endif auto end = func.find_first_of(";]> ,>", start); - if (end == std::string_view::npos) return {}; - auto ns_end = func.rfind("::", end); - return (ns_end != std::string_view::npos && ns_end > start) ? func.substr(start, ns_end - start) : std::string_view{}; + auto ns = func.substr(start, end - start); + auto pos = ns.rfind("::"); + return (pos != std::string_view::npos) ? ns.substr(0, pos) : std::string_view{}; } inline std::shared_ptr InitJSONPtr() { return std::make_shared(); } From 397dcb038dc7aa8f6af82fe941f4feea82a924ed Mon Sep 17 00:00:00 2001 From: Alexander Nesterov Date: Sun, 29 Jun 2025 17:32:21 +0200 Subject: [PATCH 04/29] refactor GetNamespace to enhance readability and handle missing keys gracefully --- modules/core/util/include/util.hpp | 31 ++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/modules/core/util/include/util.hpp b/modules/core/util/include/util.hpp index 625fd8cd4..0a3aef30b 100644 --- a/modules/core/util/include/util.hpp +++ b/modules/core/util/include/util.hpp @@ -50,24 +50,27 @@ int GetNumThreads(); template constexpr std::string_view GetNamespace() { -#if defined(_MSC_VER) - constexpr std::string_view func{__FUNCSIG__}; - auto start = func.find("GetNamespace<") + 13; - for (auto p : {"class ", "struct ", "enum ", "union "}) - if (func.substr(start).starts_with(p)) { - start += std::string_view{p}.size(); - break; - } +#ifdef _MSC_VER + constexpr std::string_view kFunc{__FUNCSIG__}; + constexpr std::string_view key = "GetNamespace<"; #else - constexpr std::string_view func{__PRETTY_FUNCTION__}; - auto start = func.find("T = ") + 4; + constexpr std::string_view kFunc{__PRETTY_FUNCTION__}; + constexpr std::string_view key = "T = "; #endif - auto end = func.find_first_of(";]> ,>", start); - auto ns = func.substr(start, end - start); - auto pos = ns.rfind("::"); - return (pos != std::string_view::npos) ? ns.substr(0, pos) : std::string_view{}; + + auto start = kFunc.find(key); + if (start == std::string_view::npos) return {}; + start += key.size(); + + for (auto p : {"class ", "struct ", "enum ", "union "}) + if (kFunc.substr(start).starts_with(p)) start += std::string_view{p}.size(); + + auto ns_type = kFunc.substr(start, kFunc.find(']', start) - start); + auto pos = ns_type.rfind("::"); + return (pos != std::string_view::npos) ? ns_type.substr(0, pos) : std::string_view{}; } + inline std::shared_ptr InitJSONPtr() { return std::make_shared(); } bool IsUnderMpirun(); From 6424cd797fb680da9c9c0bcd23e315250e566a79 Mon Sep 17 00:00:00 2001 From: Alexander Nesterov Date: Sun, 29 Jun 2025 17:34:51 +0200 Subject: [PATCH 05/29] refactor GetNamespace to standardize variable naming and streamline key handling --- modules/core/util/include/util.hpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/modules/core/util/include/util.hpp b/modules/core/util/include/util.hpp index 0a3aef30b..1f6ac1173 100644 --- a/modules/core/util/include/util.hpp +++ b/modules/core/util/include/util.hpp @@ -52,15 +52,14 @@ template constexpr std::string_view GetNamespace() { #ifdef _MSC_VER constexpr std::string_view kFunc{__FUNCSIG__}; - constexpr std::string_view key = "GetNamespace<"; + constexpr std::string_view kKey = "GetNamespace<"; #else constexpr std::string_view kFunc{__PRETTY_FUNCTION__}; - constexpr std::string_view key = "T = "; + constexpr std::string_view kKey = "T = "; #endif - auto start = kFunc.find(key); - if (start == std::string_view::npos) return {}; - start += key.size(); + auto start = kFunc.find(kKey); + start = (start == std::string_view::npos) ? kFunc.size() : start + kKey.size(); for (auto p : {"class ", "struct ", "enum ", "union "}) if (kFunc.substr(start).starts_with(p)) start += std::string_view{p}.size(); @@ -70,7 +69,6 @@ constexpr std::string_view GetNamespace() { return (pos != std::string_view::npos) ? ns_type.substr(0, pos) : std::string_view{}; } - inline std::shared_ptr InitJSONPtr() { return std::make_shared(); } bool IsUnderMpirun(); From 4375000f4ae1679cd623c78cc2235212adfa2ae4 Mon Sep 17 00:00:00 2001 From: Alexander Nesterov Date: Sun, 29 Jun 2025 17:44:56 +0200 Subject: [PATCH 06/29] refactor GetNamespace to replace string_view with string and use typeid for namespace extraction --- modules/core/performance/tests/perf_tests.cpp | 2 +- modules/core/util/include/util.hpp | 31 ++++++++----------- modules/core/util/tests/util.cpp | 16 +++++----- 3 files changed, 22 insertions(+), 27 deletions(-) diff --git a/modules/core/performance/tests/perf_tests.cpp b/modules/core/performance/tests/perf_tests.cpp index 69d8b3e6c..f88f1a0e0 100644 --- a/modules/core/performance/tests/perf_tests.cpp +++ b/modules/core/performance/tests/perf_tests.cpp @@ -263,7 +263,7 @@ using TestTypes = ::testing::Types; TYPED_TEST_SUITE(GetNamespaceTest, TestTypes); TYPED_TEST(GetNamespaceTest, ExtractsNamespaceCorrectly) { - constexpr std::string_view kNs = ppc::util::GetNamespace(); + std::string kNs = ppc::util::GetNamespace(); if constexpr (std::is_same_v) { EXPECT_EQ(kNs, "my::nested"); diff --git a/modules/core/util/include/util.hpp b/modules/core/util/include/util.hpp index 1f6ac1173..3388657bf 100644 --- a/modules/core/util/include/util.hpp +++ b/modules/core/util/include/util.hpp @@ -5,7 +5,10 @@ #include #include #include -#include +#include +#ifdef __GNUG__ +#include +#endif #include "nlohmann/json_fwd.hpp" @@ -49,24 +52,16 @@ std::string GetAbsoluteTaskPath(const std::string& id_path, const std::string& r int GetNumThreads(); template -constexpr std::string_view GetNamespace() { -#ifdef _MSC_VER - constexpr std::string_view kFunc{__FUNCSIG__}; - constexpr std::string_view kKey = "GetNamespace<"; -#else - constexpr std::string_view kFunc{__PRETTY_FUNCTION__}; - constexpr std::string_view kKey = "T = "; +std::string GetNamespace() { + std::string name = typeid(T).name(); +#ifdef __GNUG__ + int status = 0; + std::unique_ptr demangled{abi::__cxa_demangle(name.c_str(), nullptr, nullptr, &status), + std::free}; + name = (status == 0) ? demangled.get() : name; #endif - - auto start = kFunc.find(kKey); - start = (start == std::string_view::npos) ? kFunc.size() : start + kKey.size(); - - for (auto p : {"class ", "struct ", "enum ", "union "}) - if (kFunc.substr(start).starts_with(p)) start += std::string_view{p}.size(); - - auto ns_type = kFunc.substr(start, kFunc.find(']', start) - start); - auto pos = ns_type.rfind("::"); - return (pos != std::string_view::npos) ? ns_type.substr(0, pos) : std::string_view{}; + auto pos = name.rfind("::"); + return (pos != std::string::npos) ? name.substr(0, pos) : std::string{}; } inline std::shared_ptr InitJSONPtr() { return std::make_shared(); } diff --git a/modules/core/util/tests/util.cpp b/modules/core/util/tests/util.cpp index 56a0c5da3..3fa105004 100644 --- a/modules/core/util/tests/util.cpp +++ b/modules/core/util/tests/util.cpp @@ -12,7 +12,7 @@ struct Type {}; } // namespace my::nested TEST(util_tests, extracts_correct_namespace) { - constexpr std::string_view kNs = ppc::util::GetNamespace(); + std::string kNs = ppc::util::GetNamespace(); EXPECT_EQ(kNs, "my::nested"); } @@ -29,17 +29,17 @@ struct TypeInNamespace {}; struct PlainType {}; TEST(GetNamespaceTest, ReturnsExpectedNamespace) { - constexpr auto kNs = ppc::util::GetNamespace(); + std::string kNs = ppc::util::GetNamespace(); EXPECT_EQ(kNs, "test_ns"); } TEST(GetNamespaceTest, ReturnsEmptyIfNoNamespace_PrimitiveType) { - constexpr auto kNs = ppc::util::GetNamespace(); + std::string kNs = ppc::util::GetNamespace(); EXPECT_EQ(kNs, ""); } TEST(GetNamespaceTest, ReturnsEmptyIfNoNamespace_PlainStruct) { - constexpr auto kNs = ppc::util::GetNamespace(); + std::string kNs = ppc::util::GetNamespace(); EXPECT_EQ(kNs, ""); } @@ -48,14 +48,14 @@ struct Nested {}; } // namespace test_ns TEST(GetNamespaceTest, ReturnsNamespaceCorrectly) { - constexpr auto kNs = ppc::util::GetNamespace(); + std::string kNs = ppc::util::GetNamespace(); EXPECT_EQ(kNs, "test_ns"); } struct NoNamespaceType {}; TEST(GetNamespaceTest, NoNamespaceInType) { - constexpr auto kNs = ppc::util::GetNamespace(); + std::string kNs = ppc::util::GetNamespace(); EXPECT_EQ(kNs, ""); } @@ -63,7 +63,7 @@ template struct NotATemplate {}; TEST(GetNamespaceTest, NoKeyInPrettyFunction) { - constexpr auto kNs = ppc::util::GetNamespace>(); + std::string kNs = ppc::util::GetNamespace>(); EXPECT_EQ(kNs, ""); } @@ -72,6 +72,6 @@ struct VeryLongTypeNameWithOnlyLettersAndUnderscores {}; } // namespace crazy TEST(GetNamespaceTest, NoTerminatorCharactersInPrettyFunction) { - constexpr auto kNs = ppc::util::GetNamespace(); + std::string kNs = ppc::util::GetNamespace(); EXPECT_EQ(kNs, "crazy"); } From 4537e9e0684bcd04f27ef613449089e003f4d54e Mon Sep 17 00:00:00 2001 From: Alexander Nesterov Date: Sun, 29 Jun 2025 17:59:33 +0200 Subject: [PATCH 07/29] refactor GetNamespace tests and implementation to standardize variable naming and trim type prefixes --- modules/core/performance/tests/perf_tests.cpp | 8 ++--- modules/core/util/include/util.hpp | 9 ++++++ modules/core/util/tests/util.cpp | 32 +++++++++---------- 3 files changed, 29 insertions(+), 20 deletions(-) diff --git a/modules/core/performance/tests/perf_tests.cpp b/modules/core/performance/tests/perf_tests.cpp index f88f1a0e0..6de81e3e6 100644 --- a/modules/core/performance/tests/perf_tests.cpp +++ b/modules/core/performance/tests/perf_tests.cpp @@ -263,14 +263,14 @@ using TestTypes = ::testing::Types; TYPED_TEST_SUITE(GetNamespaceTest, TestTypes); TYPED_TEST(GetNamespaceTest, ExtractsNamespaceCorrectly) { - std::string kNs = ppc::util::GetNamespace(); + std::string k_ns = ppc::util::GetNamespace(); if constexpr (std::is_same_v) { - EXPECT_EQ(kNs, "my::nested"); + EXPECT_EQ(k_ns, "my::nested"); } else if constexpr (std::is_same_v) { - EXPECT_EQ(kNs, "my"); + EXPECT_EQ(k_ns, "my"); } else if constexpr (std::is_same_v) { - EXPECT_EQ(kNs, ""); + EXPECT_EQ(k_ns, ""); } else { FAIL() << "Unhandled type in test"; } diff --git a/modules/core/util/include/util.hpp b/modules/core/util/include/util.hpp index 3388657bf..09a5ac873 100644 --- a/modules/core/util/include/util.hpp +++ b/modules/core/util/include/util.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -60,6 +61,14 @@ std::string GetNamespace() { std::free}; name = (status == 0) ? demangled.get() : name; #endif + const std::string prefixes[] = {"class ", "struct ", "enum ", "union "}; + for (const auto& prefix : prefixes) { + if (name.starts_with(prefix)) { + name = name.substr(prefix.size()); + break; + } + } + name.erase(0, name.find_first_not_of(' ')); auto pos = name.rfind("::"); return (pos != std::string::npos) ? name.substr(0, pos) : std::string{}; } diff --git a/modules/core/util/tests/util.cpp b/modules/core/util/tests/util.cpp index 3fa105004..e2a60590a 100644 --- a/modules/core/util/tests/util.cpp +++ b/modules/core/util/tests/util.cpp @@ -12,8 +12,8 @@ struct Type {}; } // namespace my::nested TEST(util_tests, extracts_correct_namespace) { - std::string kNs = ppc::util::GetNamespace(); - EXPECT_EQ(kNs, "my::nested"); + std::string k_ns = ppc::util::GetNamespace(); + EXPECT_EQ(k_ns, "my::nested"); } TEST(util_tests, threads_control_check_openmp_disabled_valgrind) { @@ -29,18 +29,18 @@ struct TypeInNamespace {}; struct PlainType {}; TEST(GetNamespaceTest, ReturnsExpectedNamespace) { - std::string kNs = ppc::util::GetNamespace(); - EXPECT_EQ(kNs, "test_ns"); + std::string k_ns = ppc::util::GetNamespace(); + EXPECT_EQ(k_ns, "test_ns"); } TEST(GetNamespaceTest, ReturnsEmptyIfNoNamespace_PrimitiveType) { - std::string kNs = ppc::util::GetNamespace(); - EXPECT_EQ(kNs, ""); + std::string k_ns = ppc::util::GetNamespace(); + EXPECT_EQ(k_ns, ""); } TEST(GetNamespaceTest, ReturnsEmptyIfNoNamespace_PlainStruct) { - std::string kNs = ppc::util::GetNamespace(); - EXPECT_EQ(kNs, ""); + std::string k_ns = ppc::util::GetNamespace(); + EXPECT_EQ(k_ns, ""); } namespace test_ns { @@ -48,23 +48,23 @@ struct Nested {}; } // namespace test_ns TEST(GetNamespaceTest, ReturnsNamespaceCorrectly) { - std::string kNs = ppc::util::GetNamespace(); - EXPECT_EQ(kNs, "test_ns"); + std::string k_ns = ppc::util::GetNamespace(); + EXPECT_EQ(k_ns, "test_ns"); } struct NoNamespaceType {}; TEST(GetNamespaceTest, NoNamespaceInType) { - std::string kNs = ppc::util::GetNamespace(); - EXPECT_EQ(kNs, ""); + std::string k_ns = ppc::util::GetNamespace(); + EXPECT_EQ(k_ns, ""); } template struct NotATemplate {}; TEST(GetNamespaceTest, NoKeyInPrettyFunction) { - std::string kNs = ppc::util::GetNamespace>(); - EXPECT_EQ(kNs, ""); + std::string k_ns = ppc::util::GetNamespace>(); + EXPECT_EQ(k_ns, ""); } namespace crazy { @@ -72,6 +72,6 @@ struct VeryLongTypeNameWithOnlyLettersAndUnderscores {}; } // namespace crazy TEST(GetNamespaceTest, NoTerminatorCharactersInPrettyFunction) { - std::string kNs = ppc::util::GetNamespace(); - EXPECT_EQ(kNs, "crazy"); + std::string k_ns = ppc::util::GetNamespace(); + EXPECT_EQ(k_ns, "crazy"); } From ba1eb5ef5a76eae0b70c605ca5e7d04a5c8c0ed5 Mon Sep 17 00:00:00 2001 From: Alexander Nesterov Date: Sun, 29 Jun 2025 18:11:42 +0200 Subject: [PATCH 08/29] refactor util module: replace `string_view` with `string`, remove unused include, and add MSVC-specific prefix handling --- modules/core/util/include/util.hpp | 3 ++- modules/core/util/tests/util.cpp | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/modules/core/util/include/util.hpp b/modules/core/util/include/util.hpp index 09a5ac873..5f88ded59 100644 --- a/modules/core/util/include/util.hpp +++ b/modules/core/util/include/util.hpp @@ -1,6 +1,5 @@ #pragma once -#include #include #include #include @@ -61,6 +60,7 @@ std::string GetNamespace() { std::free}; name = (status == 0) ? demangled.get() : name; #endif +#if defined(_MSC_VER) const std::string prefixes[] = {"class ", "struct ", "enum ", "union "}; for (const auto& prefix : prefixes) { if (name.starts_with(prefix)) { @@ -69,6 +69,7 @@ std::string GetNamespace() { } } name.erase(0, name.find_first_not_of(' ')); +#endif auto pos = name.rfind("::"); return (pos != std::string::npos) ? name.substr(0, pos) : std::string{}; } diff --git a/modules/core/util/tests/util.cpp b/modules/core/util/tests/util.cpp index e2a60590a..913773b0d 100644 --- a/modules/core/util/tests/util.cpp +++ b/modules/core/util/tests/util.cpp @@ -3,7 +3,7 @@ #include #include -#include +#include #include "omp.h" From e0b2a65b539db1ceb4e49fc3687057e02a026464 Mon Sep 17 00:00:00 2001 From: Alexander Nesterov Date: Sun, 29 Jun 2025 18:29:09 +0200 Subject: [PATCH 09/29] add performance tests for PipelineRun, TaskRun, PrintPerfStatistic, and GetStringParamName --- modules/core/performance/tests/perf_tests.cpp | 51 ++++++++++++++++--- 1 file changed, 45 insertions(+), 6 deletions(-) diff --git a/modules/core/performance/tests/perf_tests.cpp b/modules/core/performance/tests/perf_tests.cpp index 6de81e3e6..01fd79b7c 100644 --- a/modules/core/performance/tests/perf_tests.cpp +++ b/modules/core/performance/tests/perf_tests.cpp @@ -239,12 +239,11 @@ TEST(TaskTest, GetDynamicTypeReturnsCorrectEnum) { TEST(TaskTest, DestructorTerminatesIfWrongOrder) { testing::FLAGS_gtest_death_test_style = "threadsafe"; - ASSERT_DEATH_IF_SUPPORTED( - { - DummyTask task; - task.Run(); - }, - ""); + auto test_func = [&] { + DummyTask task; + task.Run(); + }; + ASSERT_DEATH_IF_SUPPORTED({ test_func(); }, ""); } namespace my { @@ -275,3 +274,43 @@ TYPED_TEST(GetNamespaceTest, ExtractsNamespaceCorrectly) { FAIL() << "Unhandled type in test"; } } + +TEST(PerfTest, PipelineRunAndTaskRun) { + auto task_ptr = std::make_shared(); + ppc::core::Perf perf(task_ptr); + + ppc::core::PerfAttr attr; + double time = 0.0; + attr.num_running = 2; + attr.current_timer = [&time]() { + double t = time; + time += 1.0; + return t; + }; + + EXPECT_NO_THROW(perf.PipelineRun(attr)); + auto res_pipeline = perf.GetPerfResults(); + EXPECT_EQ(res_pipeline.type_of_running, ppc::core::PerfResults::kPipeline); + EXPECT_GT(res_pipeline.time_sec, 0.0); + + EXPECT_NO_THROW(perf.TaskRun(attr)); + auto res_taskrun = perf.GetPerfResults(); + EXPECT_EQ(res_taskrun.type_of_running, ppc::core::PerfResults::kTaskRun); + EXPECT_GT(res_taskrun.time_sec, 0.0); +} + +TEST(PerfTest, PrintPerfStatisticThrowsOnNone) { + testing::FLAGS_gtest_death_test_style = "threadsafe"; + auto test_func = [&] { + auto task_ptr = std::make_shared(); + ppc::core::Perf perf(task_ptr); + EXPECT_THROW(perf.PrintPerfStatistic("test"), std::runtime_error); + }; + ASSERT_DEATH_IF_SUPPORTED({ test_func(); }, ""); +} + +TEST(PerfTest, GetStringParamNameTest) { + EXPECT_EQ(GetStringParamName(ppc::core::PerfResults::kTaskRun), "task_run"); + EXPECT_EQ(GetStringParamName(ppc::core::PerfResults::kPipeline), "pipeline"); + EXPECT_EQ(GetStringParamName(ppc::core::PerfResults::kNone), "none"); +} From a7329e5eb45e7ad409c60b83b621ef23861cc373 Mon Sep 17 00:00:00 2001 From: Alexander Nesterov Date: Sun, 29 Jun 2025 18:34:37 +0200 Subject: [PATCH 10/29] add unit tests for Task status strings, type handling, and invalid pipeline order --- modules/core/task/tests/task_tests.cpp | 35 ++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/modules/core/task/tests/task_tests.cpp b/modules/core/task/tests/task_tests.cpp index 0b31ad425..6d1bf3bfe 100644 --- a/modules/core/task/tests/task_tests.cpp +++ b/modules/core/task/tests/task_tests.cpp @@ -172,6 +172,41 @@ TEST(task_tests, premature_postprocessing_after_preprocessing) { EXPECT_DEATH_IF_SUPPORTED(destroy_function(), ".*ORDER OF FUNCTIONS IS NOT RIGHT.*"); } +// Test for disabled status string +TEST(TaskTest, GetStringTaskStatus_Disabled) { + EXPECT_EQ(GetStringTaskStatus(ppc::core::StatusOfTask::kDisabled), "disabled"); +} + +// Test for enabled status string +TEST(TaskTest, GetStringTaskStatus_Enabled) { + EXPECT_EQ(GetStringTaskStatus(ppc::core::StatusOfTask::kEnabled), "enabled"); +} + +// Test for exception thrown when file cannot be opened +TEST(TaskTest, GetStringTaskType_InvalidFileThrows) { + EXPECT_THROW({ GetStringTaskType(ppc::core::TypeOfTask::kALL, "non_existing_file.json"); }, std::runtime_error); +} + +// Test unknown type case +TEST(TaskTest, GetStringTaskType_UnknownType) { + std::string path = "settings.json"; // Provide valid settings file + EXPECT_NO_THROW({ GetStringTaskType(ppc::core::TypeOfTask::kUnknown, path); }); +} + +// Death test for destructor when pipeline order is invalid +TEST(TaskTest, Destructor_InvalidPipelineOrderTerminates) { + testing::FLAGS_gtest_death_test_style = "threadsafe"; + auto test_func = [&] { + struct BadTask : ppc::core::Task { + bool ValidationImpl() override { return true; } + bool PreProcessingImpl() override { return true; } + bool RunImpl() override { return true; } + bool PostProcessingImpl() override { return true; } + } task; + }; + ASSERT_DEATH_IF_SUPPORTED({ test_func(); }, ""); +} + int main(int argc, char **argv) { testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); From 7cbb1f686b45be3acefb6a35e32f67c3371540fb Mon Sep 17 00:00:00 2001 From: Alexander Nesterov Date: Sun, 29 Jun 2025 18:38:08 +0200 Subject: [PATCH 11/29] add test case for GetStringTaskType to handle unknown type with valid file creation --- modules/core/task/tests/task_tests.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/modules/core/task/tests/task_tests.cpp b/modules/core/task/tests/task_tests.cpp index 6d1bf3bfe..7748df591 100644 --- a/modules/core/task/tests/task_tests.cpp +++ b/modules/core/task/tests/task_tests.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #include "core/task/tests/test_task.hpp" @@ -188,8 +189,12 @@ TEST(TaskTest, GetStringTaskType_InvalidFileThrows) { } // Test unknown type case -TEST(TaskTest, GetStringTaskType_UnknownType) { - std::string path = "settings.json"; // Provide valid settings file +TEST(TaskTest, GetStringTaskType_UnknownType_WithValidFile) { + std::string path = "settings_valid.json"; + std::ofstream file(path); + file + << R"({"tasks": {"all": "enabled", "stl": "enabled", "omp": "enabled", "mpi": "enabled", "tbb": "enabled", "seq": "enabled"}})"; + file.close(); EXPECT_NO_THROW({ GetStringTaskType(ppc::core::TypeOfTask::kUnknown, path); }); } From c097cec52f1faeafc1bce3c344213169839f694d Mon Sep 17 00:00:00 2001 From: Alexander Nesterov Date: Sun, 29 Jun 2025 18:48:11 +0200 Subject: [PATCH 12/29] add tests for GetStringTaskType to validate behavior with valid and invalid JSON input --- modules/core/task/tests/task_tests.cpp | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/modules/core/task/tests/task_tests.cpp b/modules/core/task/tests/task_tests.cpp index 7748df591..06629f15e 100644 --- a/modules/core/task/tests/task_tests.cpp +++ b/modules/core/task/tests/task_tests.cpp @@ -212,6 +212,26 @@ TEST(TaskTest, Destructor_InvalidPipelineOrderTerminates) { ASSERT_DEATH_IF_SUPPORTED({ test_func(); }, ""); } +// Make sure this sucker writes proper JSON so GetStringTaskType doesn't choke +TEST(TaskTest, GetStringTaskType_UnknownType_ValidJSON) { + std::string path = "settings_valid.json"; + std::ofstream file(path); + file << "{\"tasks\": {\"all\": \"enabled\", \"stl\": \"enabled\", \"omp\": \"enabled\", \"mpi\": \"enabled\", " + "\"tbb\": \"enabled\", \"seq\": \"enabled\"}}"; + file.close(); + auto result = GetStringTaskType(ppc::core::TypeOfTask::kALL, path); + EXPECT_TRUE(result.find("all") != std::string::npos); +} + +// Feed it garbage JSON and watch it crash like it should +TEST(TaskTest, GetStringTaskType_ThrowsOnBadJSON) { + std::string path = "bad_settings.json"; + std::ofstream file(path); + file << "{"; // broken trash + file.close(); + EXPECT_THROW({ GetStringTaskType(ppc::core::TypeOfTask::kALL, path); }, std::exception); +} + int main(int argc, char **argv) { testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); From 873c7dddf7e59a41b230b7528b075bba68b0b7ed Mon Sep 17 00:00:00 2001 From: Alexander Nesterov Date: Sun, 29 Jun 2025 19:23:23 +0200 Subject: [PATCH 13/29] add test for Task destructor to validate termination on invalid pipeline order --- modules/core/performance/tests/perf_tests.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/modules/core/performance/tests/perf_tests.cpp b/modules/core/performance/tests/perf_tests.cpp index 01fd79b7c..076e18d6a 100644 --- a/modules/core/performance/tests/perf_tests.cpp +++ b/modules/core/performance/tests/perf_tests.cpp @@ -314,3 +314,17 @@ TEST(PerfTest, GetStringParamNameTest) { EXPECT_EQ(GetStringParamName(ppc::core::PerfResults::kPipeline), "pipeline"); EXPECT_EQ(GetStringParamName(ppc::core::PerfResults::kNone), "none"); } + +TEST(TaskTest, Destructor_InvalidPipelineOrderTerminates_PartialPipeline) { + testing::FLAGS_gtest_death_test_style = "threadsafe"; + auto test_func = [&] { + struct BadTask : ppc::core::Task { + bool ValidationImpl() override { return true; } + bool PreProcessingImpl() override { return true; } + bool RunImpl() override { return true; } + bool PostProcessingImpl() override { return true; } + } task; + task.Validation(); + }; + ASSERT_DEATH_IF_SUPPORTED({ test_func(); }, "ORDER OF FUNCTIONS IS NOT RIGHT"); +} From 9958b4b9c23190e769e7797b096fa6c079c5f0a8 Mon Sep 17 00:00:00 2001 From: Alexander Nesterov Date: Sun, 29 Jun 2025 20:19:42 +0200 Subject: [PATCH 14/29] refactor Task class: replace manual pipeline stage tracking with enum-based `PipelineStage` implementation and remove deprecated `FuncName` utility --- modules/core/task/include/task.hpp | 68 ++++++++++-------------------- modules/core/util/include/util.hpp | 17 -------- 2 files changed, 22 insertions(+), 63 deletions(-) diff --git a/modules/core/task/include/task.hpp b/modules/core/task/include/task.hpp index 338b6f69a..a82eee3fd 100644 --- a/modules/core/task/include/task.hpp +++ b/modules/core/task/include/task.hpp @@ -39,6 +39,8 @@ enum TypeOfTask : uint8_t { kUnknown }; +enum class PipelineStage { None, Validation, PreProcessing, Run, Done }; + /// @brief Indicates whether a task is enabled or disabled. enum StatusOfTask : uint8_t { /// Task is enabled and should be executed @@ -104,22 +106,23 @@ template /// @tparam OutType Output data type. class Task { public: - /// @brief Constructs a new Task object. - explicit Task(StateOfTesting /*state_of_testing*/ = StateOfTesting::kFunc) { functions_order_.clear(); } - /// @brief Validates input data and task attributes before execution. /// @return True if validation is successful. virtual bool Validation() final { - InternalOrderTest(ppc::util::FuncName()); + if (stage_ == PipelineStage::None) { + stage_ = PipelineStage::Validation; + } return ValidationImpl(); } /// @brief Performs preprocessing on the input data. /// @return True if preprocessing is successful. virtual bool PreProcessing() final { - InternalOrderTest(ppc::util::FuncName()); + if (stage_ == PipelineStage::Validation) { + stage_ = PipelineStage::PreProcessing; + } if (state_of_testing_ == StateOfTesting::kFunc) { - InternalTimeTest(ppc::util::FuncName()); + InternalTimeTest(); } return PreProcessingImpl(); } @@ -127,16 +130,20 @@ class Task { /// @brief Executes the main logic of the task. /// @return True if execution is successful. virtual bool Run() final { - InternalOrderTest(ppc::util::FuncName()); + if (stage_ == PipelineStage::PreProcessing || stage_ == PipelineStage::Run) { + stage_ = PipelineStage::Run; + } return RunImpl(); } /// @brief Performs postprocessing on the output data. /// @return True if postprocessing is successful. virtual bool PostProcessing() final { - InternalOrderTest(ppc::util::FuncName()); + if (stage_ == PipelineStage::Run) { + stage_ = PipelineStage::Done; + } if (state_of_testing_ == StateOfTesting::kFunc) { - InternalTimeTest(ppc::util::FuncName()); + InternalTimeTest(); } return PostProcessingImpl(); } @@ -172,12 +179,9 @@ class Task { /// @brief Destructor. Verifies that the pipeline was executed in the correct order. /// @note Terminates the program if pipeline order is incorrect or incomplete. virtual ~Task() { - if (!functions_order_.empty() || !was_worked_) { - std::cerr << "ORDER OF FUNCTIONS IS NOT RIGHT! \n Expected - \"Validation\", \"PreProcessing\", \"Run\", " - "\"PostProcessing\" \n"; + if (stage_ != PipelineStage::Done) { + std::cerr << "ORDER OF FUNCTIONS IS NOT RIGHT" << std::endl; std::terminate(); - } else { - functions_order_.clear(); } #if _OPENMP >= 201811 omp_pause_resource_all(omp_pause_soft); @@ -185,26 +189,15 @@ class Task { } protected: - /// @brief Verifies the correct order of pipeline method calls. - /// @param str Name of the method being called. - virtual void InternalOrderTest(const std::string &str) final { - functions_order_.push_back(str); - if (str == "PostProcessing" && IsFullPipelineStage()) { - functions_order_.clear(); - } else { - was_worked_ = true; - } - } - /// @brief Measures execution time between preprocessing and postprocessing steps. /// @param str Name of the method being timed. /// @throws std::runtime_error If execution exceeds the allowed time limit. - virtual void InternalTimeTest(const std::string &str) final { - if (str == "PreProcessing") { + virtual void InternalTimeTest() final { + if (stage_ == PipelineStage::PreProcessing) { tmp_time_point_ = std::chrono::high_resolution_clock::now(); } - if (str == "PostProcessing") { + if (stage_ == PipelineStage::Done) { auto duration = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - tmp_time_point_) .count(); @@ -244,26 +237,9 @@ class Task { StateOfTesting state_of_testing_ = kFunc; TypeOfTask type_of_task_ = kUnknown; StatusOfTask status_of_task_ = kEnabled; - std::vector functions_order_; - std::vector right_functions_order_ = {"Validation", "PreProcessing", "Run", "PostProcessing"}; static constexpr double kMaxTestTime = 1.0; std::chrono::high_resolution_clock::time_point tmp_time_point_; - bool was_worked_ = false; - - bool IsFullPipelineStage() { - if (functions_order_.size() < 4) { - return false; - } - - auto it = std::adjacent_find(functions_order_.begin() + 2, - functions_order_.begin() + static_cast(functions_order_.size() - 2), - std::not_equal_to<>()); - - return (functions_order_[0] == "Validation" && functions_order_[1] == "PreProcessing" && - functions_order_[2] == "Run" && - it == (functions_order_.begin() + static_cast(functions_order_.size() - 2)) && - functions_order_[functions_order_.size() - 1] == "PostProcessing"); - } + PipelineStage stage_ = PipelineStage::None; }; /// @brief Smart pointer alias for Task. diff --git a/modules/core/util/include/util.hpp b/modules/core/util/include/util.hpp index 5f88ded59..49129b3cb 100644 --- a/modules/core/util/include/util.hpp +++ b/modules/core/util/include/util.hpp @@ -29,23 +29,6 @@ using NlohmannJsonTypeError = nlohmann::json::type_error; namespace ppc::util { -/** - * @brief Returns the unqualified name of the current function. - * - * @param loc Source location, defaults to the current function. - * @return Function name without namespaces or parameters. - */ -inline std::string FuncName(const std::source_location& loc = std::source_location::current()) { - std::string s{loc.function_name()}; - if (auto p = s.find('('); p != std::string::npos) { - s.resize(p); // drop “(…)” - } - if (auto p = s.rfind("::"); p != std::string::npos) { - s.erase(0, p + 2); // drop namespaces - } - return s; -} - enum GTestParamIndex : uint8_t { kTaskGetter, kNameTest, kTestParams }; std::string GetAbsoluteTaskPath(const std::string& id_path, const std::string& relative_path); From c6d74f8acb58bd550b0ba03f7d2f41344a7afb17 Mon Sep 17 00:00:00 2001 From: Alexander Nesterov Date: Sun, 29 Jun 2025 20:46:27 +0200 Subject: [PATCH 15/29] refactor task tests: remove redundant comments, consolidate assertions, and streamline test case code for readability --- modules/core/task/include/task.hpp | 33 ++-- modules/core/task/tests/task_tests.cpp | 199 ++++++++++--------------- 2 files changed, 91 insertions(+), 141 deletions(-) diff --git a/modules/core/task/include/task.hpp b/modules/core/task/include/task.hpp index a82eee3fd..54d9b98bb 100644 --- a/modules/core/task/include/task.hpp +++ b/modules/core/task/include/task.hpp @@ -77,25 +77,22 @@ inline std::string GetStringTaskType(TypeOfTask type_of_task, const std::string return type + "_" + std::string((*list_settings)["tasks"][type]); }; - if (type_of_task == TypeOfTask::kALL) { - return to_type_str("all"); + switch (type_of_task) { + case TypeOfTask::kALL: + return to_type_str("all"); + case TypeOfTask::kSTL: + return to_type_str("stl"); + case TypeOfTask::kOMP: + return to_type_str("omp"); + case TypeOfTask::kMPI: + return to_type_str("mpi"); + case TypeOfTask::kTBB: + return to_type_str("tbb"); + case TypeOfTask::kSEQ: + return to_type_str("seq"); + default: + return "unknown"; } - if (type_of_task == TypeOfTask::kSTL) { - return to_type_str("stl"); - } - if (type_of_task == TypeOfTask::kOMP) { - return to_type_str("omp"); - } - if (type_of_task == TypeOfTask::kMPI) { - return to_type_str("mpi"); - } - if (type_of_task == TypeOfTask::kTBB) { - return to_type_str("tbb"); - } - if (type_of_task == TypeOfTask::kSEQ) { - return to_type_str("seq"); - } - return "unknown"; } enum StateOfTesting : uint8_t { kFunc, kPerf }; diff --git a/modules/core/task/tests/task_tests.cpp b/modules/core/task/tests/task_tests.cpp index 06629f15e..955250bc0 100644 --- a/modules/core/task/tests/task_tests.cpp +++ b/modules/core/task/tests/task_tests.cpp @@ -8,152 +8,64 @@ #include "core/task/tests/test_task.hpp" TEST(task_tests, check_int32_t) { - // Create data std::vector in(20, 1); - - // Create and check Task ppc::test::task::TestTask, int32_t> test_task(in); - bool is_valid = test_task.Validation(); - ASSERT_EQ(is_valid, true); - - // Run Task + ASSERT_EQ(test_task.Validation(), true); test_task.PreProcessing(); test_task.Run(); test_task.PostProcessing(); - - // Check Result ASSERT_EQ(static_cast(test_task.GetOutput()), in.size()); } TEST(task_tests, check_int32_t_slow) { - // Create data std::vector in(20, 1); - - // Create and check Task ppc::test::task::FakeSlowTask, int32_t> test_task(in); - bool is_valid = test_task.Validation(); - ASSERT_EQ(is_valid, true); - - // Run Task + ASSERT_EQ(test_task.Validation(), true); test_task.PreProcessing(); test_task.Run(); ASSERT_ANY_THROW(test_task.PostProcessing()); } TEST(task_tests, check_validate_func) { - // Create data std::vector in; - - // Create and check Task ppc::test::task::TestTask, int32_t> test_task(in); - bool is_valid = test_task.Validation(); - - // Check Result - ASSERT_EQ(is_valid, false); - + ASSERT_EQ(test_task.Validation(), false); test_task.PreProcessing(); test_task.Run(); test_task.PostProcessing(); } TEST(task_tests, check_double) { - // Create data std::vector in(20, 1); - - // Create and check Task ppc::test::task::TestTask, double> test_task(in); - bool is_valid = test_task.Validation(); - ASSERT_EQ(is_valid, true); - - // Run Task + ASSERT_EQ(test_task.Validation(), true); test_task.PreProcessing(); test_task.Run(); test_task.PostProcessing(); - - // Check Result EXPECT_NEAR(test_task.GetOutput(), static_cast(in.size()), 1e-6); } -TEST(task_tests, check_uint8_t) { - // Create data - std::vector in(20, 1); - - // Create Task - ppc::test::task::TestTask, uint8_t> test_task(in); - bool is_valid = test_task.Validation(); - ASSERT_EQ(is_valid, true); - - // Run Task - test_task.PreProcessing(); - test_task.Run(); - test_task.PostProcessing(); - - // Check Result - ASSERT_EQ(static_cast(test_task.GetOutput()), in.size()); -} - -TEST(task_tests, check_int64_t) { - // Create data - std::vector in(20, 1); - - // Create Task - ppc::test::task::TestTask, int64_t> test_task(in); - bool is_valid = test_task.Validation(); - ASSERT_EQ(is_valid, true); - - // Run Task - test_task.PreProcessing(); - test_task.Run(); - test_task.PostProcessing(); - - // Check Result - ASSERT_EQ(static_cast(test_task.GetOutput()), in.size()); -} - TEST(task_tests, check_float) { - // Create data std::vector in(20, 1); - - // Create Task ppc::test::task::TestTask, float> test_task(in); - bool is_valid = test_task.Validation(); - ASSERT_EQ(is_valid, true); - - // Run Task + ASSERT_EQ(test_task.Validation(), true); test_task.PreProcessing(); test_task.Run(); test_task.PostProcessing(); - - // Check Result EXPECT_NEAR(test_task.GetOutput(), in.size(), 1e-3); } TEST(task_tests, check_wrong_order_disabled_valgrind) { auto destroy_function = [] { - // Create data std::vector in(20, 1); - - // Create Task ppc::test::task::TestTask, float> test_task(in); - bool is_valid = test_task.Validation(); - ASSERT_EQ(is_valid, true); + ASSERT_EQ(test_task.Validation(), true); test_task.PreProcessing(); test_task.PostProcessing(); }; EXPECT_DEATH_IF_SUPPORTED(destroy_function(), ".*ORDER OF FUNCTIONS IS NOT RIGHT.*"); } -TEST(task_tests, check_empty_order_disabled_valgrind) { - auto destroy_function = [] { - // Create data - std::vector in(20, 1); - - // Create Task - ppc::test::task::TestTask, float> test_task(in); - }; - EXPECT_DEATH_IF_SUPPORTED(destroy_function(), ".*ORDER OF FUNCTIONS IS NOT RIGHT.*"); -} - TEST(task_tests, premature_postprocessing_no_steps) { auto destroy_function = [] { std::vector in(20, 1); @@ -173,22 +85,18 @@ TEST(task_tests, premature_postprocessing_after_preprocessing) { EXPECT_DEATH_IF_SUPPORTED(destroy_function(), ".*ORDER OF FUNCTIONS IS NOT RIGHT.*"); } -// Test for disabled status string TEST(TaskTest, GetStringTaskStatus_Disabled) { EXPECT_EQ(GetStringTaskStatus(ppc::core::StatusOfTask::kDisabled), "disabled"); } -// Test for enabled status string TEST(TaskTest, GetStringTaskStatus_Enabled) { EXPECT_EQ(GetStringTaskStatus(ppc::core::StatusOfTask::kEnabled), "enabled"); } -// Test for exception thrown when file cannot be opened TEST(TaskTest, GetStringTaskType_InvalidFileThrows) { EXPECT_THROW({ GetStringTaskType(ppc::core::TypeOfTask::kALL, "non_existing_file.json"); }, std::runtime_error); } -// Test unknown type case TEST(TaskTest, GetStringTaskType_UnknownType_WithValidFile) { std::string path = "settings_valid.json"; std::ofstream file(path); @@ -198,41 +106,86 @@ TEST(TaskTest, GetStringTaskType_UnknownType_WithValidFile) { EXPECT_NO_THROW({ GetStringTaskType(ppc::core::TypeOfTask::kUnknown, path); }); } -// Death test for destructor when pipeline order is invalid -TEST(TaskTest, Destructor_InvalidPipelineOrderTerminates) { - testing::FLAGS_gtest_death_test_style = "threadsafe"; - auto test_func = [&] { - struct BadTask : ppc::core::Task { - bool ValidationImpl() override { return true; } - bool PreProcessingImpl() override { return true; } - bool RunImpl() override { return true; } - bool PostProcessingImpl() override { return true; } - } task; - }; - ASSERT_DEATH_IF_SUPPORTED({ test_func(); }, ""); +TEST(TaskTest, GetStringTaskType_ThrowsOnBadJSON) { + std::string path = "bad_settings.json"; + std::ofstream file(path); + file << "{"; + file.close(); + EXPECT_THROW({ GetStringTaskType(ppc::core::TypeOfTask::kALL, path); }, std::exception); } -// Make sure this sucker writes proper JSON so GetStringTaskType doesn't choke -TEST(TaskTest, GetStringTaskType_UnknownType_ValidJSON) { - std::string path = "settings_valid.json"; +TEST(TaskTest, GetStringTaskType_EachType_WithValidFile) { + std::string path = "settings_valid_all.json"; std::ofstream file(path); - file << "{\"tasks\": {\"all\": \"enabled\", \"stl\": \"enabled\", \"omp\": \"enabled\", \"mpi\": \"enabled\", " - "\"tbb\": \"enabled\", \"seq\": \"enabled\"}}"; + file + << R"({"tasks": {"all": "enabled", "stl": "enabled", "omp": "enabled", "mpi": "enabled", "tbb": "enabled", "seq": "enabled"}})"; file.close(); - auto result = GetStringTaskType(ppc::core::TypeOfTask::kALL, path); - EXPECT_TRUE(result.find("all") != std::string::npos); + + EXPECT_NO_THROW(ppc::core::GetStringTaskType(ppc::core::TypeOfTask::kALL, path)); + EXPECT_NO_THROW(ppc::core::GetStringTaskType(ppc::core::TypeOfTask::kSTL, path)); + EXPECT_NO_THROW(ppc::core::GetStringTaskType(ppc::core::TypeOfTask::kOMP, path)); + EXPECT_NO_THROW(ppc::core::GetStringTaskType(ppc::core::TypeOfTask::kMPI, path)); + EXPECT_NO_THROW(ppc::core::GetStringTaskType(ppc::core::TypeOfTask::kTBB, path)); + EXPECT_NO_THROW(ppc::core::GetStringTaskType(ppc::core::TypeOfTask::kSEQ, path)); } -// Feed it garbage JSON and watch it crash like it should -TEST(TaskTest, GetStringTaskType_ThrowsOnBadJSON) { - std::string path = "bad_settings.json"; +TEST(TaskTest, GetStringTaskType_ReturnsUnknown_OnDefault) { + std::string path = "settings_valid_unknown.json"; std::ofstream file(path); - file << "{"; // broken trash + file << R"({"tasks": {"all": "enabled"}})"; file.close(); - EXPECT_THROW({ GetStringTaskType(ppc::core::TypeOfTask::kALL, path); }, std::exception); + + auto result = ppc::core::GetStringTaskType(ppc::core::TypeOfTask::kUnknown, path); + EXPECT_EQ(result, "unknown"); +} + +TEST(TaskTest, GetStringTaskType_ThrowsIfKeyMissing) { + std::string path = "settings_partial.json"; + std::ofstream file(path); + file << R"({"tasks": {"all": "enabled"}})"; + file.close(); + + EXPECT_ANY_THROW(ppc::core::GetStringTaskType(ppc::core::TypeOfTask::kSTL, path)); +} + +TEST(TaskTest, TaskDestructor_ThrowsIfStageIncomplete) { + testing::FLAGS_gtest_death_test_style = "threadsafe"; + auto destroy_func = [] { + std::vector in(20, 1); + struct LocalTask : ppc::core::Task, int32_t> { + LocalTask(const std::vector& in) { this->GetInput() = in; } + bool ValidationImpl() override { return true; } + bool PreProcessingImpl() override { return true; } + bool RunImpl() override { return true; } + bool PostProcessingImpl() override { return true; } + } task(in); + task.Validation(); + }; + EXPECT_DEATH_IF_SUPPORTED(destroy_func(), ".*ORDER OF FUNCTIONS IS NOT RIGHT.*"); +} + +TEST(TaskTest, InternalTimeTest_ThrowsIfTimeoutExceeded) { + struct SlowTask : ppc::core::Task, int32_t> { + SlowTask(const std::vector& in) { this->GetInput() = in; } + bool ValidationImpl() override { return true; } + bool PreProcessingImpl() override { + std::this_thread::sleep_for(std::chrono::seconds(2)); + return true; + } + bool RunImpl() override { return true; } + bool PostProcessingImpl() override { return true; } + }; + + std::vector in(20, 1); + SlowTask task(in); + task.GetStateOfTesting() = ppc::core::StateOfTesting::kFunc; + task.Validation(); + EXPECT_NO_THROW(task.PreProcessing()); + task.Run(); + EXPECT_THROW(task.PostProcessing(), std::runtime_error); } -int main(int argc, char **argv) { +int main(int argc, char** argv) { testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } From 5093418590abd459de8bb68cbe3981639142320e Mon Sep 17 00:00:00 2001 From: Alexander Nesterov Date: Sun, 29 Jun 2025 21:07:11 +0200 Subject: [PATCH 16/29] refactor Task module: replace switch-case in `GetStringTaskType` with `TypeOfTaskToString` utility for improved readability and maintainability --- modules/core/task/include/task.hpp | 42 ++++++++++++++++-------------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/modules/core/task/include/task.hpp b/modules/core/task/include/task.hpp index 54d9b98bb..67fa26693 100644 --- a/modules/core/task/include/task.hpp +++ b/modules/core/task/include/task.hpp @@ -39,6 +39,22 @@ enum TypeOfTask : uint8_t { kUnknown }; +constexpr std::pair kTypeOfTaskStrings[] = { + {TypeOfTask::kALL, "all"}, + {TypeOfTask::kMPI, "mpi"}, + {TypeOfTask::kOMP, "omp"}, + {TypeOfTask::kSEQ, "seq"}, + {TypeOfTask::kSTL, "stl"}, + {TypeOfTask::kTBB, "tbb"}, +}; + +inline std::string_view TypeOfTaskToString(TypeOfTask type) { + for (const auto& [key, value] : kTypeOfTaskStrings) { + if (key == type) return value; + } + return "unknown"; +} + enum class PipelineStage { None, Validation, PreProcessing, Run, Done }; /// @brief Indicates whether a task is enabled or disabled. @@ -64,7 +80,7 @@ inline std::string GetStringTaskStatus(StatusOfTask status_of_task) { /// @param settings_file_path Path to the JSON file containing task type strings. /// @return Formatted string combining the task type and its corresponding value from the file. /// @throws std::runtime_error If the file cannot be opened. -inline std::string GetStringTaskType(TypeOfTask type_of_task, const std::string &settings_file_path) { +inline std::string GetStringTaskType(TypeOfTask type_of_task, const std::string& settings_file_path) { std::ifstream file(settings_file_path); if (!file.is_open()) { throw std::runtime_error("Failed to open " + settings_file_path); @@ -73,26 +89,12 @@ inline std::string GetStringTaskType(TypeOfTask type_of_task, const std::string auto list_settings = ppc::util::InitJSONPtr(); file >> *list_settings; - auto to_type_str = [&](const std::string &type) -> std::string { - return type + "_" + std::string((*list_settings)["tasks"][type]); - }; - - switch (type_of_task) { - case TypeOfTask::kALL: - return to_type_str("all"); - case TypeOfTask::kSTL: - return to_type_str("stl"); - case TypeOfTask::kOMP: - return to_type_str("omp"); - case TypeOfTask::kMPI: - return to_type_str("mpi"); - case TypeOfTask::kTBB: - return to_type_str("tbb"); - case TypeOfTask::kSEQ: - return to_type_str("seq"); - default: - return "unknown"; + std::string type_str = std::string(TypeOfTaskToString(type_of_task)); + if (type_str == "unknown") { + return "unknown"; } + + return type_str + "_" + std::string((*list_settings)["tasks"][type_str]); } enum StateOfTesting : uint8_t { kFunc, kPerf }; From b9efa0593430be71f1f9cec5ce1a31dbd8eb41bf Mon Sep 17 00:00:00 2001 From: Alexander Nesterov Date: Sun, 29 Jun 2025 21:10:53 +0200 Subject: [PATCH 17/29] refactor Task module: replace `kTypeOfTaskStrings` with inline `TypeOfTaskToString` function, improve error handling for unknown task type --- modules/core/task/include/task.hpp | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/modules/core/task/include/task.hpp b/modules/core/task/include/task.hpp index 67fa26693..941679c2d 100644 --- a/modules/core/task/include/task.hpp +++ b/modules/core/task/include/task.hpp @@ -39,17 +39,13 @@ enum TypeOfTask : uint8_t { kUnknown }; -constexpr std::pair kTypeOfTaskStrings[] = { - {TypeOfTask::kALL, "all"}, - {TypeOfTask::kMPI, "mpi"}, - {TypeOfTask::kOMP, "omp"}, - {TypeOfTask::kSEQ, "seq"}, - {TypeOfTask::kSTL, "stl"}, - {TypeOfTask::kTBB, "tbb"}, -}; +inline std::string TypeOfTaskToString(TypeOfTask type) { + constexpr std::pair kTypeOfTaskStrings[] = { + {TypeOfTask::kALL, "all"}, {TypeOfTask::kMPI, "mpi"}, {TypeOfTask::kOMP, "omp"}, + {TypeOfTask::kSEQ, "seq"}, {TypeOfTask::kSTL, "stl"}, {TypeOfTask::kTBB, "tbb"}, + }; -inline std::string_view TypeOfTaskToString(TypeOfTask type) { - for (const auto& [key, value] : kTypeOfTaskStrings) { + for (const auto &[key, value] : kTypeOfTaskStrings) { if (key == type) return value; } return "unknown"; @@ -80,7 +76,7 @@ inline std::string GetStringTaskStatus(StatusOfTask status_of_task) { /// @param settings_file_path Path to the JSON file containing task type strings. /// @return Formatted string combining the task type and its corresponding value from the file. /// @throws std::runtime_error If the file cannot be opened. -inline std::string GetStringTaskType(TypeOfTask type_of_task, const std::string& settings_file_path) { +inline std::string GetStringTaskType(TypeOfTask type_of_task, const std::string &settings_file_path) { std::ifstream file(settings_file_path); if (!file.is_open()) { throw std::runtime_error("Failed to open " + settings_file_path); @@ -89,9 +85,9 @@ inline std::string GetStringTaskType(TypeOfTask type_of_task, const std::string& auto list_settings = ppc::util::InitJSONPtr(); file >> *list_settings; - std::string type_str = std::string(TypeOfTaskToString(type_of_task)); + std::string type_str = TypeOfTaskToString(type_of_task); if (type_str == "unknown") { - return "unknown"; + throw std::runtime_error("Unknown task type"); } return type_str + "_" + std::string((*list_settings)["tasks"][type_str]); From 4c5032b9db97c6038b961b66bcb7025b0814a862 Mon Sep 17 00:00:00 2001 From: Alexander Nesterov Date: Mon, 30 Jun 2025 00:25:29 +0200 Subject: [PATCH 18/29] refactor Task module: replace runtime error for unknown task type with safe fallback string "unknown" --- modules/core/task/include/task.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/core/task/include/task.hpp b/modules/core/task/include/task.hpp index 941679c2d..306ba804a 100644 --- a/modules/core/task/include/task.hpp +++ b/modules/core/task/include/task.hpp @@ -87,7 +87,7 @@ inline std::string GetStringTaskType(TypeOfTask type_of_task, const std::string std::string type_str = TypeOfTaskToString(type_of_task); if (type_str == "unknown") { - throw std::runtime_error("Unknown task type"); + return type_str; } return type_str + "_" + std::string((*list_settings)["tasks"][type_str]); From ff28fc4b1beeaeac7673449f3839ff5353fef6ff Mon Sep 17 00:00:00 2001 From: Alexander Nesterov Date: Mon, 30 Jun 2025 00:35:19 +0200 Subject: [PATCH 19/29] refactor Task module and tests: enforce explicit constructors, replace `PipelineStage` enums with `k`-prefixed constants, and optimize type-to-string mappings for readability and maintainability --- modules/core/task/include/task.hpp | 54 ++++++++++++++------------ modules/core/task/tests/task_tests.cpp | 4 +- 2 files changed, 31 insertions(+), 27 deletions(-) diff --git a/modules/core/task/include/task.hpp b/modules/core/task/include/task.hpp index 306ba804a..b788c2c44 100644 --- a/modules/core/task/include/task.hpp +++ b/modules/core/task/include/task.hpp @@ -39,20 +39,25 @@ enum TypeOfTask : uint8_t { kUnknown }; -inline std::string TypeOfTaskToString(TypeOfTask type) { - constexpr std::pair kTypeOfTaskStrings[] = { - {TypeOfTask::kALL, "all"}, {TypeOfTask::kMPI, "mpi"}, {TypeOfTask::kOMP, "omp"}, - {TypeOfTask::kSEQ, "seq"}, {TypeOfTask::kSTL, "stl"}, {TypeOfTask::kTBB, "tbb"}, - }; +using TaskMapping = std::pair; +using TaskMappingArray = std::array; + +constexpr TaskMappingArray kTaskTypeMappings = {{{TypeOfTask::kALL, "all"}, + {TypeOfTask::kMPI, "mpi"}, + {TypeOfTask::kOMP, "omp"}, + {TypeOfTask::kSEQ, "seq"}, + {TypeOfTask::kSTL, "stl"}, + {TypeOfTask::kTBB, "tbb"}}}; - for (const auto &[key, value] : kTypeOfTaskStrings) { - if (key == type) return value; +inline std::string TypeOfTaskToString(TypeOfTask type) { + for (const auto &[key, value] : kTaskTypeMappings) { + if (key == type) { + return value; + } } return "unknown"; } -enum class PipelineStage { None, Validation, PreProcessing, Run, Done }; - /// @brief Indicates whether a task is enabled or disabled. enum StatusOfTask : uint8_t { /// Task is enabled and should be executed @@ -104,8 +109,8 @@ class Task { /// @brief Validates input data and task attributes before execution. /// @return True if validation is successful. virtual bool Validation() final { - if (stage_ == PipelineStage::None) { - stage_ = PipelineStage::Validation; + if (stage_ == PipelineStage::kNone) { + stage_ = PipelineStage::kValidation; } return ValidationImpl(); } @@ -113,8 +118,8 @@ class Task { /// @brief Performs preprocessing on the input data. /// @return True if preprocessing is successful. virtual bool PreProcessing() final { - if (stage_ == PipelineStage::Validation) { - stage_ = PipelineStage::PreProcessing; + if (stage_ == PipelineStage::kValidation) { + stage_ = PipelineStage::kPreProcessing; } if (state_of_testing_ == StateOfTesting::kFunc) { InternalTimeTest(); @@ -125,8 +130,8 @@ class Task { /// @brief Executes the main logic of the task. /// @return True if execution is successful. virtual bool Run() final { - if (stage_ == PipelineStage::PreProcessing || stage_ == PipelineStage::Run) { - stage_ = PipelineStage::Run; + if (stage_ == PipelineStage::kPreProcessing || stage_ == PipelineStage::kRun) { + stage_ = PipelineStage::kRun; } return RunImpl(); } @@ -134,8 +139,8 @@ class Task { /// @brief Performs postprocessing on the output data. /// @return True if postprocessing is successful. virtual bool PostProcessing() final { - if (stage_ == PipelineStage::Run) { - stage_ = PipelineStage::Done; + if (stage_ == PipelineStage::kRun) { + stage_ = PipelineStage::kDone; } if (state_of_testing_ == StateOfTesting::kFunc) { InternalTimeTest(); @@ -172,10 +177,10 @@ class Task { OutType &GetOutput() { return output_; } /// @brief Destructor. Verifies that the pipeline was executed in the correct order. - /// @note Terminates the program if pipeline order is incorrect or incomplete. + /// @note Terminates the program if the pipeline order is incorrect or incomplete. virtual ~Task() { - if (stage_ != PipelineStage::Done) { - std::cerr << "ORDER OF FUNCTIONS IS NOT RIGHT" << std::endl; + if (stage_ != PipelineStage::kDone) { + std::cerr << "ORDER OF FUNCTIONS IS NOT RIGHT" << '\n'; std::terminate(); } #if _OPENMP >= 201811 @@ -185,14 +190,13 @@ class Task { protected: /// @brief Measures execution time between preprocessing and postprocessing steps. - /// @param str Name of the method being timed. /// @throws std::runtime_error If execution exceeds the allowed time limit. virtual void InternalTimeTest() final { - if (stage_ == PipelineStage::PreProcessing) { + if (stage_ == PipelineStage::kPreProcessing) { tmp_time_point_ = std::chrono::high_resolution_clock::now(); } - if (stage_ == PipelineStage::Done) { + if (stage_ == PipelineStage::kDone) { auto duration = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - tmp_time_point_) .count(); @@ -234,7 +238,7 @@ class Task { StatusOfTask status_of_task_ = kEnabled; static constexpr double kMaxTestTime = 1.0; std::chrono::high_resolution_clock::time_point tmp_time_point_; - PipelineStage stage_ = PipelineStage::None; + enum class PipelineStage : uint8_t { kNone, kValidation, kPreProcessing, kRun, kDone } stage_ = PipelineStage::kNone; }; /// @brief Smart pointer alias for Task. @@ -247,7 +251,7 @@ using TaskPtr = std::shared_ptr>; /// @tparam TaskType Type of the task to create. /// @tparam InType Type of the input. /// @param in Input to pass to the task constructor. -/// @return Shared pointer to the newly created task. +/// @return Shared a pointer to the newly created task. template std::shared_ptr TaskGetter(InType in) { return std::make_shared(in); diff --git a/modules/core/task/tests/task_tests.cpp b/modules/core/task/tests/task_tests.cpp index 955250bc0..4c3668147 100644 --- a/modules/core/task/tests/task_tests.cpp +++ b/modules/core/task/tests/task_tests.cpp @@ -153,7 +153,7 @@ TEST(TaskTest, TaskDestructor_ThrowsIfStageIncomplete) { auto destroy_func = [] { std::vector in(20, 1); struct LocalTask : ppc::core::Task, int32_t> { - LocalTask(const std::vector& in) { this->GetInput() = in; } + explicit LocalTask(const std::vector& in) { this->GetInput() = in; } bool ValidationImpl() override { return true; } bool PreProcessingImpl() override { return true; } bool RunImpl() override { return true; } @@ -166,7 +166,7 @@ TEST(TaskTest, TaskDestructor_ThrowsIfStageIncomplete) { TEST(TaskTest, InternalTimeTest_ThrowsIfTimeoutExceeded) { struct SlowTask : ppc::core::Task, int32_t> { - SlowTask(const std::vector& in) { this->GetInput() = in; } + explicit SlowTask(const std::vector& in) { this->GetInput() = in; } bool ValidationImpl() override { return true; } bool PreProcessingImpl() override { std::this_thread::sleep_for(std::chrono::seconds(2)); From d8d96f8f6cc523b007c3d2cdf3c08e397d153265 Mon Sep 17 00:00:00 2001 From: Alexander Nesterov Date: Mon, 30 Jun 2025 00:43:20 +0200 Subject: [PATCH 20/29] update Task module and tests: add missing includes, adjust test utilities, and increase cognitive complexity threshold in .clang-tidy --- modules/core/performance/tests/.clang-tidy | 2 +- modules/core/task/include/task.hpp | 2 ++ modules/core/task/tests/task_tests.cpp | 5 +++++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/modules/core/performance/tests/.clang-tidy b/modules/core/performance/tests/.clang-tidy index 508219a92..95659f936 100644 --- a/modules/core/performance/tests/.clang-tidy +++ b/modules/core/performance/tests/.clang-tidy @@ -38,4 +38,4 @@ Checks: > CheckOptions: - key: readability-function-cognitive-complexity.Threshold - value: 50 # default: 25 + value: 70 # default: 25 diff --git a/modules/core/task/include/task.hpp b/modules/core/task/include/task.hpp index b788c2c44..4860246c9 100644 --- a/modules/core/task/include/task.hpp +++ b/modules/core/task/include/task.hpp @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -16,6 +17,7 @@ #include #include #include +#include #include namespace ppc::core { diff --git a/modules/core/task/tests/task_tests.cpp b/modules/core/task/tests/task_tests.cpp index 4c3668147..b8b627b86 100644 --- a/modules/core/task/tests/task_tests.cpp +++ b/modules/core/task/tests/task_tests.cpp @@ -1,10 +1,15 @@ #include +#include #include #include +#include #include +#include +#include #include +#include "core/task/include/task.hpp" #include "core/task/tests/test_task.hpp" TEST(task_tests, check_int32_t) { From e1e2960e988b1dcd9fbaefd39daa19bfecd6945b Mon Sep 17 00:00:00 2001 From: Alexander Nesterov Date: Mon, 30 Jun 2025 00:56:53 +0200 Subject: [PATCH 21/29] refactor Task module: replace `constexpr` with `const` for `kTaskTypeMappings` to improve consistency and align with mutable array requirements --- modules/core/task/include/task.hpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/modules/core/task/include/task.hpp b/modules/core/task/include/task.hpp index 4860246c9..f01551976 100644 --- a/modules/core/task/include/task.hpp +++ b/modules/core/task/include/task.hpp @@ -44,12 +44,12 @@ enum TypeOfTask : uint8_t { using TaskMapping = std::pair; using TaskMappingArray = std::array; -constexpr TaskMappingArray kTaskTypeMappings = {{{TypeOfTask::kALL, "all"}, - {TypeOfTask::kMPI, "mpi"}, - {TypeOfTask::kOMP, "omp"}, - {TypeOfTask::kSEQ, "seq"}, - {TypeOfTask::kSTL, "stl"}, - {TypeOfTask::kTBB, "tbb"}}}; +const TaskMappingArray kTaskTypeMappings = {{{TypeOfTask::kALL, "all"}, + {TypeOfTask::kMPI, "mpi"}, + {TypeOfTask::kOMP, "omp"}, + {TypeOfTask::kSEQ, "seq"}, + {TypeOfTask::kSTL, "stl"}, + {TypeOfTask::kTBB, "tbb"}}}; inline std::string TypeOfTaskToString(TypeOfTask type) { for (const auto &[key, value] : kTaskTypeMappings) { From 9d3430c1775e547619471aa3e8689572cb9e9980 Mon Sep 17 00:00:00 2001 From: Alexander Nesterov Date: Mon, 30 Jun 2025 01:17:00 +0200 Subject: [PATCH 22/29] add tests for Task pipeline stage order validation and update Task methods to enforce correct call sequence --- modules/core/task/include/task.hpp | 10 +++++- modules/core/task/tests/task_tests.cpp | 45 ++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/modules/core/task/include/task.hpp b/modules/core/task/include/task.hpp index f01551976..2283b2a34 100644 --- a/modules/core/task/include/task.hpp +++ b/modules/core/task/include/task.hpp @@ -111,8 +111,10 @@ class Task { /// @brief Validates input data and task attributes before execution. /// @return True if validation is successful. virtual bool Validation() final { - if (stage_ == PipelineStage::kNone) { + if (stage_ == PipelineStage::kNone || stage_ == PipelineStage::kDone) { stage_ = PipelineStage::kValidation; + } else { + throw std::runtime_error("Validation should be called before preprocessing"); } return ValidationImpl(); } @@ -122,6 +124,8 @@ class Task { virtual bool PreProcessing() final { if (stage_ == PipelineStage::kValidation) { stage_ = PipelineStage::kPreProcessing; + } else { + throw std::runtime_error("Preprocessing should be called after validation"); } if (state_of_testing_ == StateOfTesting::kFunc) { InternalTimeTest(); @@ -134,6 +138,8 @@ class Task { virtual bool Run() final { if (stage_ == PipelineStage::kPreProcessing || stage_ == PipelineStage::kRun) { stage_ = PipelineStage::kRun; + } else { + throw std::runtime_error("Run should be called after preprocessing"); } return RunImpl(); } @@ -143,6 +149,8 @@ class Task { virtual bool PostProcessing() final { if (stage_ == PipelineStage::kRun) { stage_ = PipelineStage::kDone; + } else { + throw std::runtime_error("Postprocessing should be called after run"); } if (state_of_testing_ == StateOfTesting::kFunc) { InternalTimeTest(); diff --git a/modules/core/task/tests/task_tests.cpp b/modules/core/task/tests/task_tests.cpp index b8b627b86..55c686991 100644 --- a/modules/core/task/tests/task_tests.cpp +++ b/modules/core/task/tests/task_tests.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -190,6 +191,50 @@ TEST(TaskTest, InternalTimeTest_ThrowsIfTimeoutExceeded) { EXPECT_THROW(task.PostProcessing(), std::runtime_error); } +class DummyTask : public ppc::core::Task { + public: + using Task::Task; + bool ValidationImpl() override { return true; } + bool PreProcessingImpl() override { return true; } + bool RunImpl() override { return true; } + bool PostProcessingImpl() override { return true; } +}; + +TEST(TaskTest, ValidationThrowsIfCalledTwice) { + auto test_func = [&] { + auto task = std::make_shared(); + task->Validation(); + EXPECT_THROW(task->Validation(), std::runtime_error); + }; + EXPECT_DEATH_IF_SUPPORTED({ test_func(); }, "ORDER OF FUNCTIONS IS NOT RIGHT"); +} + +TEST(TaskTest, PreProcessingThrowsIfCalledBeforeValidation) { + auto test_func = [&] { + auto task = std::make_shared(); + EXPECT_THROW(task->PreProcessing(), std::runtime_error); + }; + EXPECT_DEATH_IF_SUPPORTED({ test_func(); }, "ORDER OF FUNCTIONS IS NOT RIGHT"); +} + +TEST(TaskTest, RunThrowsIfCalledBeforePreProcessing) { + auto test_func = [&] { + auto task = std::make_shared(); + EXPECT_THROW(task->Run(), std::runtime_error); + }; + EXPECT_DEATH_IF_SUPPORTED({ test_func(); }, "ORDER OF FUNCTIONS IS NOT RIGHT"); +} + +TEST(TaskTest, PostProcessingThrowsIfCalledBeforeRun) { + auto test_func = [&] { + auto task = std::make_shared(); + task->Validation(); + task->PreProcessing(); + EXPECT_THROW(task->PostProcessing(), std::runtime_error); + }; + EXPECT_DEATH_IF_SUPPORTED({ test_func(); }, "ORDER OF FUNCTIONS IS NOT RIGHT"); +} + int main(int argc, char** argv) { testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); From 99a8a83f90ccf8c646a69891b5ca97abca0ce307 Mon Sep 17 00:00:00 2001 From: Alexander Nesterov Date: Mon, 30 Jun 2025 01:41:01 +0200 Subject: [PATCH 23/29] update Task tests and implementation: replace `EXPECT_DEATH_IF_SUPPORTED` with `EXPECT_THROW`, add `kException` pipeline stage, and improve error handling in Task destructor --- modules/core/performance/tests/perf_tests.cpp | 8 +-- modules/core/task/include/task.hpp | 15 ++++- modules/core/task/tests/task_tests.cpp | 67 +++++++------------ 3 files changed, 38 insertions(+), 52 deletions(-) diff --git a/modules/core/performance/tests/perf_tests.cpp b/modules/core/performance/tests/perf_tests.cpp index 076e18d6a..d56c46cc0 100644 --- a/modules/core/performance/tests/perf_tests.cpp +++ b/modules/core/performance/tests/perf_tests.cpp @@ -238,12 +238,8 @@ TEST(TaskTest, GetDynamicTypeReturnsCorrectEnum) { } TEST(TaskTest, DestructorTerminatesIfWrongOrder) { - testing::FLAGS_gtest_death_test_style = "threadsafe"; - auto test_func = [&] { - DummyTask task; - task.Run(); - }; - ASSERT_DEATH_IF_SUPPORTED({ test_func(); }, ""); + DummyTask task; + EXPECT_THROW(task.Run(), std::runtime_error); } namespace my { diff --git a/modules/core/task/include/task.hpp b/modules/core/task/include/task.hpp index 2283b2a34..553811708 100644 --- a/modules/core/task/include/task.hpp +++ b/modules/core/task/include/task.hpp @@ -114,6 +114,7 @@ class Task { if (stage_ == PipelineStage::kNone || stage_ == PipelineStage::kDone) { stage_ = PipelineStage::kValidation; } else { + stage_ = PipelineStage::kException; throw std::runtime_error("Validation should be called before preprocessing"); } return ValidationImpl(); @@ -125,6 +126,7 @@ class Task { if (stage_ == PipelineStage::kValidation) { stage_ = PipelineStage::kPreProcessing; } else { + stage_ = PipelineStage::kException; throw std::runtime_error("Preprocessing should be called after validation"); } if (state_of_testing_ == StateOfTesting::kFunc) { @@ -139,6 +141,7 @@ class Task { if (stage_ == PipelineStage::kPreProcessing || stage_ == PipelineStage::kRun) { stage_ = PipelineStage::kRun; } else { + stage_ = PipelineStage::kException; throw std::runtime_error("Run should be called after preprocessing"); } return RunImpl(); @@ -150,6 +153,7 @@ class Task { if (stage_ == PipelineStage::kRun) { stage_ = PipelineStage::kDone; } else { + stage_ = PipelineStage::kException; throw std::runtime_error("Postprocessing should be called after run"); } if (state_of_testing_ == StateOfTesting::kFunc) { @@ -189,7 +193,7 @@ class Task { /// @brief Destructor. Verifies that the pipeline was executed in the correct order. /// @note Terminates the program if the pipeline order is incorrect or incomplete. virtual ~Task() { - if (stage_ != PipelineStage::kDone) { + if (stage_ != PipelineStage::kDone && stage_ != PipelineStage::kException) { std::cerr << "ORDER OF FUNCTIONS IS NOT RIGHT" << '\n'; std::terminate(); } @@ -248,7 +252,14 @@ class Task { StatusOfTask status_of_task_ = kEnabled; static constexpr double kMaxTestTime = 1.0; std::chrono::high_resolution_clock::time_point tmp_time_point_; - enum class PipelineStage : uint8_t { kNone, kValidation, kPreProcessing, kRun, kDone } stage_ = PipelineStage::kNone; + enum class PipelineStage : uint8_t { + kNone, + kValidation, + kPreProcessing, + kRun, + kDone, + kException + } stage_ = PipelineStage::kNone; }; /// @brief Smart pointer alias for Task. diff --git a/modules/core/task/tests/task_tests.cpp b/modules/core/task/tests/task_tests.cpp index 55c686991..f6e199578 100644 --- a/modules/core/task/tests/task_tests.cpp +++ b/modules/core/task/tests/task_tests.cpp @@ -62,33 +62,24 @@ TEST(task_tests, check_float) { } TEST(task_tests, check_wrong_order_disabled_valgrind) { - auto destroy_function = [] { - std::vector in(20, 1); - ppc::test::task::TestTask, float> test_task(in); - ASSERT_EQ(test_task.Validation(), true); - test_task.PreProcessing(); - test_task.PostProcessing(); - }; - EXPECT_DEATH_IF_SUPPORTED(destroy_function(), ".*ORDER OF FUNCTIONS IS NOT RIGHT.*"); + std::vector in(20, 1); + ppc::test::task::TestTask, float> test_task(in); + ASSERT_EQ(test_task.Validation(), true); + test_task.PreProcessing(); + EXPECT_THROW(test_task.PostProcessing(), std::runtime_error); } TEST(task_tests, premature_postprocessing_no_steps) { - auto destroy_function = [] { - std::vector in(20, 1); - ppc::test::task::TestTask, float> test_task(in); - ASSERT_NO_THROW(test_task.PostProcessing()); - }; - EXPECT_DEATH_IF_SUPPORTED(destroy_function(), ".*ORDER OF FUNCTIONS IS NOT RIGHT.*"); + std::vector in(20, 1); + ppc::test::task::TestTask, float> test_task(in); + EXPECT_THROW(test_task.PostProcessing(), std::runtime_error); } TEST(task_tests, premature_postprocessing_after_preprocessing) { - auto destroy_function = [] { - std::vector in(20, 1); - ppc::test::task::TestTask, float> test_task(in); - ASSERT_NO_THROW(test_task.PreProcessing()); - ASSERT_NO_THROW(test_task.PostProcessing()); - }; - EXPECT_DEATH_IF_SUPPORTED(destroy_function(), ".*ORDER OF FUNCTIONS IS NOT RIGHT.*"); + std::vector in(20, 1); + ppc::test::task::TestTask, float> test_task(in); + EXPECT_THROW(test_task.PreProcessing(), std::runtime_error); + EXPECT_THROW(test_task.PostProcessing(), std::runtime_error); } TEST(TaskTest, GetStringTaskStatus_Disabled) { @@ -201,38 +192,26 @@ class DummyTask : public ppc::core::Task { }; TEST(TaskTest, ValidationThrowsIfCalledTwice) { - auto test_func = [&] { - auto task = std::make_shared(); - task->Validation(); - EXPECT_THROW(task->Validation(), std::runtime_error); - }; - EXPECT_DEATH_IF_SUPPORTED({ test_func(); }, "ORDER OF FUNCTIONS IS NOT RIGHT"); + auto task = std::make_shared(); + task->Validation(); + EXPECT_THROW(task->Validation(), std::runtime_error); } TEST(TaskTest, PreProcessingThrowsIfCalledBeforeValidation) { - auto test_func = [&] { - auto task = std::make_shared(); - EXPECT_THROW(task->PreProcessing(), std::runtime_error); - }; - EXPECT_DEATH_IF_SUPPORTED({ test_func(); }, "ORDER OF FUNCTIONS IS NOT RIGHT"); + auto task = std::make_shared(); + EXPECT_THROW(task->PreProcessing(), std::runtime_error); } TEST(TaskTest, RunThrowsIfCalledBeforePreProcessing) { - auto test_func = [&] { - auto task = std::make_shared(); - EXPECT_THROW(task->Run(), std::runtime_error); - }; - EXPECT_DEATH_IF_SUPPORTED({ test_func(); }, "ORDER OF FUNCTIONS IS NOT RIGHT"); + auto task = std::make_shared(); + EXPECT_THROW(task->Run(), std::runtime_error); } TEST(TaskTest, PostProcessingThrowsIfCalledBeforeRun) { - auto test_func = [&] { - auto task = std::make_shared(); - task->Validation(); - task->PreProcessing(); - EXPECT_THROW(task->PostProcessing(), std::runtime_error); - }; - EXPECT_DEATH_IF_SUPPORTED({ test_func(); }, "ORDER OF FUNCTIONS IS NOT RIGHT"); + auto task = std::make_shared(); + task->Validation(); + task->PreProcessing(); + EXPECT_THROW(task->PostProcessing(), std::runtime_error); } int main(int argc, char** argv) { From 1a12d0570b03974d781ab4fea96950b3f71f22b7 Mon Sep 17 00:00:00 2001 From: Alexander Nesterov Date: Mon, 30 Jun 2025 01:59:53 +0200 Subject: [PATCH 24/29] add test for Task destructor to validate behavior when called with empty input --- modules/core/task/tests/task_tests.cpp | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/modules/core/task/tests/task_tests.cpp b/modules/core/task/tests/task_tests.cpp index f6e199578..7dfce3563 100644 --- a/modules/core/task/tests/task_tests.cpp +++ b/modules/core/task/tests/task_tests.cpp @@ -158,7 +158,22 @@ TEST(TaskTest, TaskDestructor_ThrowsIfStageIncomplete) { } task(in); task.Validation(); }; - EXPECT_DEATH_IF_SUPPORTED(destroy_func(), ".*ORDER OF FUNCTIONS IS NOT RIGHT.*"); + EXPECT_DEATH_IF_SUPPORTED({ destroy_func(); }, ".*ORDER OF FUNCTIONS IS NOT RIGHT.*"); +} + +TEST(TaskTest, TaskDestructor_ThrowsIfEmpty) { + testing::FLAGS_gtest_death_test_style = "threadsafe"; + auto destroy_func = [] { + std::vector in(20, 1); + struct LocalTask : ppc::core::Task, int32_t> { + explicit LocalTask(const std::vector& in) { this->GetInput() = in; } + bool ValidationImpl() override { return true; } + bool PreProcessingImpl() override { return true; } + bool RunImpl() override { return true; } + bool PostProcessingImpl() override { return true; } + } task(in); + }; + EXPECT_DEATH_IF_SUPPORTED({ destroy_func(); }, ".*ORDER OF FUNCTIONS IS NOT RIGHT.*"); } TEST(TaskTest, InternalTimeTest_ThrowsIfTimeoutExceeded) { From a7eaf77137d97072234b4b846da59b7d05b5ed03 Mon Sep 17 00:00:00 2001 From: Alexander Nesterov Date: Mon, 30 Jun 2025 13:07:00 +0200 Subject: [PATCH 25/29] add SimpleInit utility for gtest, refactor tests to replace `EXPECT_DEATH_IF_SUPPORTED` with `EXPECT_TRUE` using `DestructorFailureFlag`, and update runner initialization methods for consistency --- modules/core/performance/tests/perf_tests.cpp | 16 +++++++------- modules/core/runners/include/runners.hpp | 6 +++++ modules/core/runners/src/runners.cpp | 19 ++++++++++++++++ modules/core/task/include/task.hpp | 3 +-- modules/core/task/tests/task_tests.cpp | 22 +++++++++---------- modules/core/util/include/util.hpp | 19 ++++++++++++++++ tasks/common/runners/functional.cpp | 7 +----- 7 files changed, 64 insertions(+), 28 deletions(-) diff --git a/modules/core/performance/tests/perf_tests.cpp b/modules/core/performance/tests/perf_tests.cpp index d56c46cc0..175ebeda3 100644 --- a/modules/core/performance/tests/perf_tests.cpp +++ b/modules/core/performance/tests/perf_tests.cpp @@ -296,13 +296,13 @@ TEST(PerfTest, PipelineRunAndTaskRun) { } TEST(PerfTest, PrintPerfStatisticThrowsOnNone) { - testing::FLAGS_gtest_death_test_style = "threadsafe"; - auto test_func = [&] { + { auto task_ptr = std::make_shared(); ppc::core::Perf perf(task_ptr); EXPECT_THROW(perf.PrintPerfStatistic("test"), std::runtime_error); - }; - ASSERT_DEATH_IF_SUPPORTED({ test_func(); }, ""); + } + EXPECT_TRUE(ppc::util::DestructorFailureFlag::Get()); + ppc::util::DestructorFailureFlag::Unset(); } TEST(PerfTest, GetStringParamNameTest) { @@ -312,8 +312,7 @@ TEST(PerfTest, GetStringParamNameTest) { } TEST(TaskTest, Destructor_InvalidPipelineOrderTerminates_PartialPipeline) { - testing::FLAGS_gtest_death_test_style = "threadsafe"; - auto test_func = [&] { + { struct BadTask : ppc::core::Task { bool ValidationImpl() override { return true; } bool PreProcessingImpl() override { return true; } @@ -321,6 +320,7 @@ TEST(TaskTest, Destructor_InvalidPipelineOrderTerminates_PartialPipeline) { bool PostProcessingImpl() override { return true; } } task; task.Validation(); - }; - ASSERT_DEATH_IF_SUPPORTED({ test_func(); }, "ORDER OF FUNCTIONS IS NOT RIGHT"); + } + EXPECT_TRUE(ppc::util::DestructorFailureFlag::Get()); + ppc::util::DestructorFailureFlag::Unset(); } diff --git a/modules/core/runners/include/runners.hpp b/modules/core/runners/include/runners.hpp index 1a09c302b..15d50941d 100644 --- a/modules/core/runners/include/runners.hpp +++ b/modules/core/runners/include/runners.hpp @@ -43,4 +43,10 @@ class WorkerTestFailurePrinter : public ::testing::EmptyTestEventListener { /// finalization fails. int Init(int argc, char** argv); +/// @brief Initializes the testing environment only for gtest. +/// @param argc Argument count. +/// @param argv Argument vector. +/// @return Exit code from RUN_ALL_TESTS. +int SimpleInit(int argc, char** argv); + } // namespace ppc::core diff --git a/modules/core/runners/src/runners.cpp b/modules/core/runners/src/runners.cpp index b0db49dea..6a2bc1bea 100644 --- a/modules/core/runners/src/runners.cpp +++ b/modules/core/runners/src/runners.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include "core/util/include/util.hpp" @@ -82,6 +83,11 @@ int Init(int argc, char** argv) { listeners.Append(new ppc::core::UnreadMessagesDetector()); auto status = RUN_ALL_TESTS(); + if (ppc::util::DestructorFailureFlag::Get()) { + throw std::runtime_error( + std::format("[ ERROR ] Destructor failed with code {}", ppc::util::DestructorFailureFlag::Get())); + } + const int finalize_res = MPI_Finalize(); if (finalize_res != MPI_SUCCESS) { std::cerr << std::format("[ ERROR ] MPI_Finalize failed with code {}", finalize_res) << '\n'; @@ -91,4 +97,17 @@ int Init(int argc, char** argv) { return status; } +int SimpleInit(int argc, char** argv) { + // Limit the number of threads in TBB + tbb::global_control control(tbb::global_control::max_allowed_parallelism, ppc::util::GetNumThreads()); + + testing::InitGoogleTest(&argc, argv); + auto status = RUN_ALL_TESTS(); + if (ppc::util::DestructorFailureFlag::Get()) { + throw std::runtime_error( + std::format("[ ERROR ] Destructor failed with code {}", ppc::util::DestructorFailureFlag::Get())); + } + return status; +} + } // namespace ppc::core diff --git a/modules/core/task/include/task.hpp b/modules/core/task/include/task.hpp index 553811708..05fcd0b26 100644 --- a/modules/core/task/include/task.hpp +++ b/modules/core/task/include/task.hpp @@ -194,8 +194,7 @@ class Task { /// @note Terminates the program if the pipeline order is incorrect or incomplete. virtual ~Task() { if (stage_ != PipelineStage::kDone && stage_ != PipelineStage::kException) { - std::cerr << "ORDER OF FUNCTIONS IS NOT RIGHT" << '\n'; - std::terminate(); + ppc::util::DestructorFailureFlag::Set(); } #if _OPENMP >= 201811 omp_pause_resource_all(omp_pause_soft); diff --git a/modules/core/task/tests/task_tests.cpp b/modules/core/task/tests/task_tests.cpp index 7dfce3563..49e77a8cb 100644 --- a/modules/core/task/tests/task_tests.cpp +++ b/modules/core/task/tests/task_tests.cpp @@ -10,6 +10,7 @@ #include #include +#include "core/runners/include/runners.hpp" #include "core/task/include/task.hpp" #include "core/task/tests/test_task.hpp" @@ -146,8 +147,7 @@ TEST(TaskTest, GetStringTaskType_ThrowsIfKeyMissing) { } TEST(TaskTest, TaskDestructor_ThrowsIfStageIncomplete) { - testing::FLAGS_gtest_death_test_style = "threadsafe"; - auto destroy_func = [] { + { std::vector in(20, 1); struct LocalTask : ppc::core::Task, int32_t> { explicit LocalTask(const std::vector& in) { this->GetInput() = in; } @@ -157,13 +157,13 @@ TEST(TaskTest, TaskDestructor_ThrowsIfStageIncomplete) { bool PostProcessingImpl() override { return true; } } task(in); task.Validation(); - }; - EXPECT_DEATH_IF_SUPPORTED({ destroy_func(); }, ".*ORDER OF FUNCTIONS IS NOT RIGHT.*"); + } + EXPECT_TRUE(ppc::util::DestructorFailureFlag::Get()); + ppc::util::DestructorFailureFlag::Unset(); } TEST(TaskTest, TaskDestructor_ThrowsIfEmpty) { - testing::FLAGS_gtest_death_test_style = "threadsafe"; - auto destroy_func = [] { + { std::vector in(20, 1); struct LocalTask : ppc::core::Task, int32_t> { explicit LocalTask(const std::vector& in) { this->GetInput() = in; } @@ -172,8 +172,9 @@ TEST(TaskTest, TaskDestructor_ThrowsIfEmpty) { bool RunImpl() override { return true; } bool PostProcessingImpl() override { return true; } } task(in); - }; - EXPECT_DEATH_IF_SUPPORTED({ destroy_func(); }, ".*ORDER OF FUNCTIONS IS NOT RIGHT.*"); + } + EXPECT_TRUE(ppc::util::DestructorFailureFlag::Get()); + ppc::util::DestructorFailureFlag::Unset(); } TEST(TaskTest, InternalTimeTest_ThrowsIfTimeoutExceeded) { @@ -229,7 +230,4 @@ TEST(TaskTest, PostProcessingThrowsIfCalledBeforeRun) { EXPECT_THROW(task->PostProcessing(), std::runtime_error); } -int main(int argc, char** argv) { - testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -} +int main(int argc, char** argv) { return ppc::core::SimpleInit(argc, argv); } diff --git a/modules/core/util/include/util.hpp b/modules/core/util/include/util.hpp index 49129b3cb..e98a91b24 100644 --- a/modules/core/util/include/util.hpp +++ b/modules/core/util/include/util.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -29,6 +30,24 @@ using NlohmannJsonTypeError = nlohmann::json::type_error; namespace ppc::util { +/// @brief Utility class for tracking destructor failure across tests. +/// @details Provides thread-safe methods to set, unset, and check the failure flag. +class DestructorFailureFlag { + public: + /// @brief Marks that a destructor failure has occurred. + static void Set() { failure_flag.store(true); } + + /// @brief Clears the destructor failure flag. + static void Unset() { failure_flag.store(false); } + + /// @brief Checks if a destructor failure was recorded. + /// @return True if failure occurred, false otherwise. + static bool Get() { return failure_flag.load(); } + + private: + inline static std::atomic failure_flag{false}; +}; + enum GTestParamIndex : uint8_t { kTaskGetter, kNameTest, kTestParams }; std::string GetAbsoluteTaskPath(const std::string& id_path, const std::string& relative_path); diff --git a/tasks/common/runners/functional.cpp b/tasks/common/runners/functional.cpp index 4a8b0e286..443721579 100644 --- a/tasks/common/runners/functional.cpp +++ b/tasks/common/runners/functional.cpp @@ -8,10 +8,5 @@ int main(int argc, char** argv) { if (ppc::util::IsUnderMpirun()) { return ppc::core::Init(argc, argv); } - - // Limit the number of threads in TBB - tbb::global_control control(tbb::global_control::max_allowed_parallelism, ppc::util::GetNumThreads()); - - ::testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); + return ppc::core::SimpleInit(argc, argv); } From 010d1b1c539ebe5cc8c5dba8d8d1fb388ef24b3b Mon Sep 17 00:00:00 2001 From: Alexander Nesterov Date: Mon, 30 Jun 2025 14:31:10 +0200 Subject: [PATCH 26/29] add missing include for `util.hpp` in Task tests --- modules/core/task/tests/task_tests.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/core/task/tests/task_tests.cpp b/modules/core/task/tests/task_tests.cpp index 49e77a8cb..17f5485d7 100644 --- a/modules/core/task/tests/task_tests.cpp +++ b/modules/core/task/tests/task_tests.cpp @@ -13,6 +13,7 @@ #include "core/runners/include/runners.hpp" #include "core/task/include/task.hpp" #include "core/task/tests/test_task.hpp" +#include "core/util/include/util.hpp" TEST(task_tests, check_int32_t) { std::vector in(20, 1); From 44246e46ec3f9354677c3d6a2d8b6ea15af2a9f2 Mon Sep 17 00:00:00 2001 From: Alexander Nesterov Date: Mon, 30 Jun 2025 14:35:04 +0200 Subject: [PATCH 27/29] uncomment dependencies for `gcc-build-codecov` in Ubuntu workflow --- .github/workflows/ubuntu.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index 3d9943886..9bb41cf48 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -359,9 +359,9 @@ jobs: PPC_NUM_PROC: 1 PPC_ASAN_RUN: 1 gcc-build-codecov: -# needs: -# - gcc-test-extended -# - clang-test-extended + needs: + - gcc-test-extended + - clang-test-extended runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 From d3902236ea59814449295e253827744dbef03f6c Mon Sep 17 00:00:00 2001 From: Alexander Nesterov Date: Mon, 30 Jun 2025 14:36:54 +0200 Subject: [PATCH 28/29] reduce cognitive complexity threshold in `.clang-tidy` and simplify JSON dump in performance --- modules/core/performance/tests/.clang-tidy | 2 +- modules/core/performance/tests/perf_tests.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/core/performance/tests/.clang-tidy b/modules/core/performance/tests/.clang-tidy index 95659f936..508219a92 100644 --- a/modules/core/performance/tests/.clang-tidy +++ b/modules/core/performance/tests/.clang-tidy @@ -38,4 +38,4 @@ Checks: > CheckOptions: - key: readability-function-cognitive-complexity.Threshold - value: 70 # default: 25 + value: 50 # default: 25 diff --git a/modules/core/performance/tests/perf_tests.cpp b/modules/core/performance/tests/perf_tests.cpp index 175ebeda3..b90d6537c 100644 --- a/modules/core/performance/tests/perf_tests.cpp +++ b/modules/core/performance/tests/perf_tests.cpp @@ -141,7 +141,7 @@ class GetStringTaskTypeTest : public ::testing::TestWithParam (*j)["tasks"]["tbb"] = "TBB"; (*j)["tasks"]["seq"] = "SEQ"; - std::ofstream(temp_path) << (*j).dump(); + std::ofstream(temp_path) << j->dump(); } void TearDown() override { std::filesystem::remove(temp_path); } From 4e768484ba91462b152715959f6e2c67cc37ca07 Mon Sep 17 00:00:00 2001 From: Arseniy Obolenskiy Date: Mon, 30 Jun 2025 14:54:12 +0200 Subject: [PATCH 29/29] Update modules/core/util/include/util.hpp --- modules/core/util/include/util.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/core/util/include/util.hpp b/modules/core/util/include/util.hpp index e98a91b24..de86faa90 100644 --- a/modules/core/util/include/util.hpp +++ b/modules/core/util/include/util.hpp @@ -56,7 +56,7 @@ int GetNumThreads(); template std::string GetNamespace() { std::string name = typeid(T).name(); -#ifdef __GNUG__ +#ifdef __GNUC__ int status = 0; std::unique_ptr demangled{abi::__cxa_demangle(name.c_str(), nullptr, nullptr, &status), std::free};