Skip to content

Commit 7a7a00b

Browse files
authored
Use typed descriptors for test task filtering (#825)
1 parent 5f2a588 commit 7a7a00b

10 files changed

Lines changed: 175 additions & 95 deletions

File tree

modules/task/include/task.hpp

Lines changed: 72 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,15 @@ constexpr std::string_view TypeOfTaskToString(TypeOfTask type) {
5656
return "unknown";
5757
}
5858

59+
constexpr TypeOfTask TypeOfTaskFromString(std::string_view type) {
60+
for (const auto &[key, value] : kTaskTypeMappings) {
61+
if (value == type) {
62+
return key;
63+
}
64+
}
65+
return TypeOfTask::kUnknown;
66+
}
67+
5968
/// @brief Indicates whether a task is enabled or disabled.
6069
enum class StatusOfTask : uint8_t {
6170
/// Task is enabled and should be executed
@@ -64,24 +73,70 @@ enum class StatusOfTask : uint8_t {
6473
kDisabled,
6574
};
6675

76+
constexpr std::string_view StatusOfTaskToString(StatusOfTask status_of_task) {
77+
return status_of_task == StatusOfTask::kDisabled ? "disabled" : "enabled";
78+
}
79+
80+
inline StatusOfTask StatusOfTaskFromString(std::string_view status_of_task) {
81+
if (status_of_task == "enabled") {
82+
return StatusOfTask::kEnabled;
83+
}
84+
if (status_of_task == "disabled") {
85+
return StatusOfTask::kDisabled;
86+
}
87+
throw std::runtime_error("Unknown task status: " + std::string(status_of_task));
88+
}
89+
6790
/// @brief Returns a string representation of the task status.
6891
/// @param status_of_task Task status (enabled or disabled).
6992
/// @return "enabled" if the task is enabled, otherwise "disabled".
7093
inline std::string GetStringTaskStatus(StatusOfTask status_of_task) {
71-
if (status_of_task == StatusOfTask::kDisabled) {
72-
return "disabled";
94+
return std::string(StatusOfTaskToString(status_of_task));
95+
}
96+
97+
enum class TaskCategory : uint8_t {
98+
kUnknown,
99+
kThreads,
100+
kProcesses,
101+
};
102+
103+
constexpr std::string_view TaskCategoryToString(TaskCategory category) {
104+
switch (category) {
105+
case TaskCategory::kThreads:
106+
return "threads";
107+
case TaskCategory::kProcesses:
108+
return "processes";
109+
case TaskCategory::kUnknown:
110+
return "";
73111
}
74-
return "enabled";
112+
return "";
75113
}
76114

115+
constexpr TaskCategory TaskCategoryFromSettingsPath(std::string_view settings_task_path) {
116+
if (settings_task_path.starts_with("threads")) {
117+
return TaskCategory::kThreads;
118+
}
119+
if (settings_task_path.starts_with("processes")) {
120+
return TaskCategory::kProcesses;
121+
}
122+
return TaskCategory::kUnknown;
123+
}
124+
125+
struct TaskDescriptor {
126+
TypeOfTask type = TypeOfTask::kUnknown;
127+
StatusOfTask status = StatusOfTask::kEnabled;
128+
TaskCategory category = TaskCategory::kUnknown;
129+
std::string display_name;
130+
};
131+
77132
/// @brief Returns a string representation of the task type based on the JSON settings file.
78133
/// @param type_of_task Type of the task.
79134
/// @param settings_file_path Path to the JSON file containing task type strings.
80135
/// @param settings_task_path Optional dot-separated nested path inside the `tasks` object.
81136
/// @return Formatted string combining the task type and its corresponding value from the file.
82137
/// @throws std::runtime_error If the file cannot be opened or the requested settings key is missing.
83-
inline std::string GetStringTaskType(TypeOfTask type_of_task, const std::string &settings_file_path,
84-
std::string_view settings_task_path = {}) {
138+
inline StatusOfTask GetTaskStatus(TypeOfTask type_of_task, const std::string &settings_file_path,
139+
std::string_view settings_task_path = {}) {
85140
std::ifstream file(settings_file_path);
86141
if (!file.is_open()) {
87142
throw std::runtime_error("Failed to open " + settings_file_path);
@@ -92,7 +147,7 @@ inline std::string GetStringTaskType(TypeOfTask type_of_task, const std::string
92147

93148
const std::string_view type_str = TypeOfTaskToString(type_of_task);
94149
if (type_str == "unknown") {
95-
return std::string(type_str);
150+
return StatusOfTask::kEnabled;
96151
}
97152

98153
auto get_required_node = [&settings_file_path](const nlohmann::json &node, const std::string &key,
@@ -124,7 +179,17 @@ inline std::string GetStringTaskType(TypeOfTask type_of_task, const std::string
124179
const std::string type_key(type_str);
125180
settings_key_path += "." + type_key;
126181
const auto &type_node = get_required_node(*settings_node, type_key, settings_key_path);
127-
return type_key + "_" + type_node.get<std::string>();
182+
return StatusOfTaskFromString(type_node.get<std::string>());
183+
}
184+
185+
inline std::string GetStringTaskType(TypeOfTask type_of_task, const std::string &settings_file_path,
186+
std::string_view settings_task_path = {}) {
187+
const StatusOfTask status = GetTaskStatus(type_of_task, settings_file_path, settings_task_path);
188+
const std::string_view type_str = TypeOfTaskToString(type_of_task);
189+
if (type_str == "unknown") {
190+
return std::string(type_str);
191+
}
192+
return std::string(type_str) + "_" + std::string(StatusOfTaskToString(status));
128193
}
129194

130195
enum class StateOfTesting : uint8_t {

modules/util/include/func_test_util.hpp

Lines changed: 32 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22

33
#include <gtest/gtest.h>
44

5-
#include <tbb/tick_count.h>
6-
75
#include <concepts>
86
#include <cstddef>
97
#include <functional>
@@ -15,12 +13,14 @@
1513
#include <utility>
1614

1715
#include "task/include/task.hpp"
16+
#include "util/include/task_descriptor_util.hpp"
1817
#include "util/include/util.hpp"
1918

2019
namespace ppc::util {
2120

2221
template <typename InType, typename OutType, typename TestType = void>
23-
using FuncTestParam = std::tuple<std::function<ppc::task::TaskPtr<InType, OutType>(InType)>, std::string, TestType>;
22+
using FuncTestParam = std::tuple<std::function<ppc::task::TaskPtr<InType, OutType>(InType)>, std::string, TestType,
23+
ppc::task::TaskDescriptor>;
2424

2525
template <typename InType, typename OutType, typename TestType = void>
2626
using GTestFuncParam = ::testing::TestParamInfo<FuncTestParam<InType, OutType, TestType>>;
@@ -50,8 +50,7 @@ class BaseRunFuncTests : public ::testing::TestWithParam<FuncTestParam<InType, O
5050
static std::string PrintFuncTestName(const GTestFuncParam<InType, OutType, TestType> &info) {
5151
RequireStaticInterface<Derived>();
5252
TestType test_param = std::get<static_cast<std::size_t>(ppc::util::GTestParamIndex::kTestParams)>(info.param);
53-
return std::get<static_cast<std::size_t>(GTestParamIndex::kNameTest)>(info.param) + "_" +
54-
Derived::PrintTestParam(test_param);
53+
return GetTaskDescriptor(info.param).display_name + "_" + Derived::PrintTestParam(test_param);
5554
}
5655

5756
protected:
@@ -71,36 +70,39 @@ class BaseRunFuncTests : public ::testing::TestWithParam<FuncTestParam<InType, O
7170
}
7271

7372
void ExecuteTest(FuncTestParam<InType, OutType, TestType> test_param) {
74-
const std::string &test_name = std::get<static_cast<std::size_t>(GTestParamIndex::kNameTest)>(test_param);
73+
const auto &descriptor = GetTaskDescriptor(test_param);
7574

76-
ValidateTestName(test_name);
75+
ValidateTaskDescriptor(descriptor);
7776

78-
const auto test_env_scope = ppc::util::test::MakePerTestEnvForCurrentGTest(test_name);
77+
const auto test_env_scope = ppc::util::test::MakePerTestEnvForCurrentGTest(descriptor.display_name);
7978

80-
if (IsTestDisabled(test_name)) {
79+
if (IsTestDisabled(descriptor)) {
8180
GTEST_SKIP();
8281
}
8382

84-
if (ShouldSkipNonMpiTask(test_name)) {
83+
if (ShouldSkipNonMpiTask(descriptor)) {
8584
std::cerr << "kALL and kMPI tasks are not under mpirun\n";
8685
GTEST_SKIP();
8786
}
8887

8988
InitializeAndRunTask(test_param);
9089
}
9190

92-
void ValidateTestName(const std::string &test_name) {
93-
EXPECT_FALSE(test_name.contains("unknown"));
91+
void ValidateTaskDescriptor(const ppc::task::TaskDescriptor &descriptor) {
92+
EXPECT_NE(descriptor.type, ppc::task::TypeOfTask::kUnknown);
9493
}
9594

96-
bool IsTestDisabled(const std::string &test_name) {
97-
return test_name.contains("disabled");
95+
bool IsTestDisabled(const ppc::task::TaskDescriptor &descriptor) {
96+
return descriptor.status == ppc::task::StatusOfTask::kDisabled;
9897
}
9998

100-
bool ShouldSkipNonMpiTask(const std::string &test_name) {
101-
auto contains_substring = [&](const std::string &substring) { return test_name.contains(substring); };
99+
bool ShouldSkipNonMpiTask(const ppc::task::TaskDescriptor &descriptor) {
100+
return !ppc::util::IsUnderMpirun() && IsMpiTaskType(descriptor.type);
101+
}
102102

103-
return !ppc::util::IsUnderMpirun() && (contains_substring("_all") || contains_substring("_mpi"));
103+
bool ShouldSkipTestCase(const FuncTestParam<InType, OutType, TestType> &test_param) {
104+
const auto &descriptor = GetTaskDescriptor(test_param);
105+
return IsTestDisabled(descriptor) || ShouldSkipNonMpiTask(descriptor);
104106
}
105107

106108
/// @brief Initializes task instance and runs it through the full pipeline.
@@ -136,15 +138,14 @@ class BaseRunFuncTests : public ::testing::TestWithParam<FuncTestParam<InType, O
136138

137139
namespace detail {
138140

139-
[[nodiscard]] inline std::string MakeFuncTestTaskTagPattern(std::string_view task_tag) {
140-
std::string tag_pattern{task_tag};
141-
if (!tag_pattern.starts_with('_')) {
142-
tag_pattern.insert(0, 1, '_');
141+
[[nodiscard]] inline ppc::task::TypeOfTask TaskTypeFromFuncTestTag(std::string_view task_tag) {
142+
while (task_tag.starts_with('_')) {
143+
task_tag.remove_prefix(1);
143144
}
144-
if (!tag_pattern.ends_with('_')) {
145-
tag_pattern.push_back('_');
145+
while (task_tag.ends_with('_')) {
146+
task_tag.remove_suffix(1);
146147
}
147-
return tag_pattern;
148+
return ppc::task::TypeOfTaskFromString(task_tag);
148149
}
149150

150151
} // namespace detail
@@ -156,12 +157,12 @@ void RunTestCasesWithTag(const TestTasksList &test_tasks_list, std::string_view
156157
return;
157158
}
158159

159-
const std::string task_tag_pattern = detail::MakeFuncTestTaskTagPattern(task_tag);
160+
const ppc::task::TypeOfTask task_type = detail::TaskTypeFromFuncTestTag(task_tag);
160161
bool has_matching_task = false;
161162
std::apply([&](const auto &...test_params) {
162163
auto run_if_tagged = [&](const auto &test_param) {
163-
const std::string &test_name = std::get<static_cast<std::size_t>(GTestParamIndex::kNameTest)>(test_param);
164-
if (test_name.contains(task_tag_pattern)) {
164+
const auto &descriptor = GetTaskDescriptor(test_param);
165+
if (descriptor.type == task_type) {
165166
has_matching_task = true;
166167
std::invoke(run_test_case, test_param);
167168
}
@@ -185,11 +186,10 @@ auto ExpandToValues(const Tuple &t) {
185186
template <typename Task, typename InType, typename SizesContainer, std::size_t... Is>
186187
auto GenTaskTuplesImpl(const SizesContainer &sizes, const std::string &settings_path,
187188
std::string_view settings_task_path, std::index_sequence<Is...> /*unused*/) {
188-
return std::make_tuple(
189-
std::make_tuple(ppc::task::TaskGetter<Task, InType>,
190-
std::string(GetNamespace<Task>()) + "_" +
191-
ppc::task::GetStringTaskType(Task::GetStaticTypeOfTask(), settings_path, settings_task_path),
192-
std::get<Is>(sizes))...);
189+
const auto descriptor =
190+
MakeTaskDescriptor(GetNamespace<Task>(), Task::GetStaticTypeOfTask(), settings_path, settings_task_path);
191+
return std::make_tuple(std::make_tuple(ppc::task::TaskGetter<Task, InType>, descriptor.display_name,
192+
std::get<Is>(sizes), descriptor)...);
193193
}
194194

195195
template <typename Task, typename InType, typename SizesContainer>

modules/util/include/perf_test_util.hpp

Lines changed: 19 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include <utility>
2222

2323
#include "task/include/task.hpp"
24+
#include "util/include/task_descriptor_util.hpp"
2425
#include "util/include/util.hpp"
2526

2627
namespace ppc::util {
@@ -40,38 +41,18 @@ struct PerfAttr {
4041

4142
namespace detail {
4243

43-
inline bool ContainsFilterToken(std::string_view value, std::string_view filter) {
44-
if (filter.empty()) {
45-
return true;
46-
}
47-
return value.contains(filter);
48-
}
49-
50-
inline bool MatchesCategoryFilter(std::string_view task_category, std::string_view category_filter) {
51-
if (category_filter.empty() || task_category.empty()) {
52-
return true;
53-
}
54-
return category_filter.contains(task_category);
44+
inline bool ContainsDescriptorToken(std::string_view filter, std::string_view descriptor_token) {
45+
return filter.empty() || descriptor_token.empty() || filter.contains(descriptor_token);
5546
}
5647

57-
inline bool ShouldRunBenchmark(std::string_view test_name, std::string_view task_category) {
48+
inline bool ShouldRunBenchmark(const ppc::task::TaskDescriptor &descriptor) {
5849
const auto impl_filter = env::get<std::string>("PPC_PERF_IMPL_FILTER");
5950
const auto category_filter = env::get<std::string>("PPC_PERF_CATEGORY_FILTER");
6051
const auto impl_filter_value = impl_filter.has_value() ? std::string_view(impl_filter.value()) : std::string_view{};
6152
const auto category_filter_value =
6253
category_filter.has_value() ? std::string_view(category_filter.value()) : std::string_view{};
63-
return ContainsFilterToken(test_name, impl_filter_value) &&
64-
MatchesCategoryFilter(task_category, category_filter_value);
65-
}
66-
67-
inline std::string GetPerfTaskCategory(std::string_view settings_task_path) {
68-
if (settings_task_path.starts_with("threads")) {
69-
return "threads";
70-
}
71-
if (settings_task_path.starts_with("processes")) {
72-
return "processes";
73-
}
74-
return {};
54+
return ContainsDescriptorToken(impl_filter_value, ppc::task::TypeOfTaskToString(descriptor.type)) &&
55+
ContainsDescriptorToken(category_filter_value, ppc::task::TaskCategoryToString(descriptor.category));
7556
}
7657

7758
template <typename InType, typename OutType>
@@ -188,7 +169,8 @@ class BenchmarkTaskBody final {
188169
} // namespace detail
189170

190171
template <typename InType, typename OutType>
191-
using PerfTestParam = std::tuple<std::function<ppc::task::TaskPtr<InType, OutType>(InType)>, std::string, std::string>;
172+
using PerfTestParam = std::tuple<std::function<ppc::task::TaskPtr<InType, OutType>(InType)>, std::string,
173+
ppc::task::TaskCategory, ppc::task::TaskDescriptor>;
192174

193175
template <typename InType, typename OutType>
194176
/// @brief Base class for performance testing of parallel tasks.
@@ -198,7 +180,7 @@ class BaseRunPerfTests : public ::testing::TestWithParam<PerfTestParam<InType, O
198180
public:
199181
/// @brief Generates a readable name for the performance test case.
200182
static std::string CustomPerfTestName(const ::testing::TestParamInfo<PerfTestParam<InType, OutType>> &info) {
201-
return std::get<static_cast<std::size_t>(GTestParamIndex::kNameTest)>(info.param);
183+
return GetTaskDescriptor(info.param).display_name;
202184
}
203185

204186
protected:
@@ -212,18 +194,17 @@ class BaseRunPerfTests : public ::testing::TestWithParam<PerfTestParam<InType, O
212194

213195
void ExecuteTest(const PerfTestParam<InType, OutType> &perf_test_param) {
214196
auto task_getter = std::get<static_cast<std::size_t>(GTestParamIndex::kTaskGetter)>(perf_test_param);
215-
auto test_name = std::get<static_cast<std::size_t>(GTestParamIndex::kNameTest)>(perf_test_param);
216-
auto task_category = std::get<static_cast<std::size_t>(GTestParamIndex::kTestParams)>(perf_test_param);
197+
const auto &descriptor = GetTaskDescriptor(perf_test_param);
217198

218-
ASSERT_FALSE(test_name.find("unknown") != std::string::npos);
219-
if (test_name.find("disabled") != std::string::npos) {
199+
ASSERT_NE(descriptor.type, ppc::task::TypeOfTask::kUnknown);
200+
if (descriptor.status == ppc::task::StatusOfTask::kDisabled) {
220201
return;
221202
}
222-
if (!detail::ShouldRunBenchmark(test_name, task_category)) {
203+
if (!detail::ShouldRunBenchmark(descriptor)) {
223204
return;
224205
}
225206

226-
const auto test_env_token = ppc::util::test::MakeCurrentGTestToken(test_name);
207+
const auto test_env_token = ppc::util::test::MakeCurrentGTestToken(descriptor.display_name);
227208
const auto test_env_scope = ppc::util::test::ScopedPerTestEnv(test_env_token);
228209

229210
const auto input_data = GetTestInputData();
@@ -243,7 +224,7 @@ class BaseRunPerfTests : public ::testing::TestWithParam<PerfTestParam<InType, O
243224
auto benchmark_body =
244225
detail::BenchmarkTaskBody<decltype(task_getter), BenchmarkInputType>(task_getter, input_data, test_env_token);
245226

246-
benchmark::RegisterBenchmark(test_name, std::move(benchmark_body))
227+
benchmark::RegisterBenchmark(descriptor.display_name, std::move(benchmark_body))
247228
->UseManualTime()
248229
->Unit(benchmark::kSecond)
249230
->Iterations(static_cast<std::int64_t>(num_iterations));
@@ -255,11 +236,11 @@ class BaseRunPerfTests : public ::testing::TestWithParam<PerfTestParam<InType, O
255236

256237
template <typename TaskType, typename InputType>
257238
auto MakePerfTaskTuples(const std::string &settings_path, std::string_view settings_task_path = {}) {
258-
const auto name = std::string(GetNamespace<TaskType>()) + "_" +
259-
ppc::task::GetStringTaskType(TaskType::GetStaticTypeOfTask(), settings_path, settings_task_path);
239+
const auto descriptor =
240+
MakeTaskDescriptor(GetNamespace<TaskType>(), TaskType::GetStaticTypeOfTask(), settings_path, settings_task_path);
260241

261-
return std::make_tuple(std::make_tuple(ppc::task::TaskGetter<TaskType, InputType>, name,
262-
detail::GetPerfTaskCategory(settings_task_path)));
242+
return std::make_tuple(std::make_tuple(ppc::task::TaskGetter<TaskType, InputType>, descriptor.display_name,
243+
descriptor.category, descriptor));
263244
}
264245

265246
template <typename Tuple, std::size_t... I>

0 commit comments

Comments
 (0)