Skip to content

Commit f5ddebe

Browse files
committed
Replace testing macro for compatibility with coverage report
Signed-off-by: Julien Jerphanion <[email protected]>
1 parent 98b4877 commit f5ddebe

File tree

1 file changed

+194
-112
lines changed

1 file changed

+194
-112
lines changed

libmamba/tests/src/util/test_synchronized_value.cpp

Lines changed: 194 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -95,11 +95,82 @@ namespace
9595
auto operator<=>(const ValueType&) const noexcept = default;
9696
};
9797

98+
// NOTE: We do not use TEMPLATE_LIST_TEST_CASE here because code coverage tools (such as gcov/lcov)
99+
// do not properly attribute coverage to tests instantiated via TEMPLATE_LIST_TEST_CASE. Instead,
100+
// we use individual TEMPLATE_TEST_CASEs for each mutex type, and factorize the test logic into
101+
// function templates to avoid code duplication. This ensures accurate code coverage reporting.
102+
98103
using supported_mutex_types = std::tuple<std::mutex, std::shared_mutex, std::recursive_mutex>;
99104

100-
TEMPLATE_LIST_TEST_CASE("synchronized_value basics", "[template][thread-safe]", supported_mutex_types)
105+
template <mamba::util::Mutex M>
106+
auto test_concurrent_increment(
107+
std::invocable<mamba::util::synchronized_value<ValueType, M>&> auto increment_task
108+
)
101109
{
102-
using synchronized_value = mamba::util::synchronized_value<ValueType, TestType>;
110+
static constexpr auto arbitrary_number_of_executing_threads = 512;
111+
112+
mamba::util::synchronized_value<ValueType, M> current_value;
113+
static constexpr int expected_result = arbitrary_number_of_executing_threads;
114+
115+
std::atomic<bool> run_tasks = false; // used to launch tasks about the same time, simpler
116+
// than condition_variable
117+
std::vector<std::future<void>> tasks;
118+
119+
// Launch the reading and writing tasks (maybe threads, depends on how async is implemented)
120+
for (int i = 0; i < expected_result * 2; ++i)
121+
{
122+
if (i % 2) // intertwine reading and writing tasks
123+
{
124+
// add writing task
125+
tasks.push_back(std::async(
126+
std::launch::async,
127+
[&, increment_task]
128+
{
129+
// don't actually run until we get the green light
130+
mambatests::wait_condition([&] { return run_tasks == true; });
131+
increment_task(current_value);
132+
}
133+
));
134+
}
135+
else
136+
{
137+
// add reading task
138+
tasks.push_back(std::async(
139+
std::launch::async,
140+
[&]
141+
{
142+
// don't actually run until we get the green light
143+
mambatests::wait_condition([&] { return run_tasks == true; });
144+
const auto& readonly_value = std::as_const(current_value);
145+
static constexpr auto arbitrary_read_count = 100;
146+
long long sum = 0;
147+
for (int c = 0; c < arbitrary_read_count; ++c)
148+
{
149+
sum += readonly_value->x; // TODO: also try to mix reading and writing
150+
// using different kinds of access
151+
std::this_thread::yield(); // for timing randomness and limit
152+
// over-exhaustion
153+
}
154+
REQUIRE(sum != 0); // It is possible but extremely unlikely that all
155+
// reading tasks will read before any writing tasks.
156+
}
157+
));
158+
}
159+
}
160+
161+
run_tasks = true; // green light, tasks will run probably concurrently, worse case in
162+
// unpredictable order
163+
for (auto& task : tasks)
164+
{
165+
task.wait(); // wait all to be finished
166+
}
167+
168+
REQUIRE(current_value->x == expected_result);
169+
}
170+
171+
template <typename MutexType>
172+
void test_synchronized_value_basics() {
173+
using synchronized_value = mamba::util::synchronized_value<ValueType, MutexType>;
103174

104175
SECTION("default constructible")
105176
{
@@ -208,115 +279,53 @@ namespace
208279
}
209280
}
210281

211-
TEMPLATE_LIST_TEST_CASE(
212-
"synchronized_value initializer-list",
213-
"[template][thread-safe]",
214-
supported_mutex_types
215-
)
282+
TEMPLATE_TEST_CASE("synchronized_value basics with std::mutex", "[template][thread-safe]", std::mutex)
216283
{
217-
using synchronized_value = mamba::util::synchronized_value<std::vector<int>, TestType>;
218-
synchronized_value values{ 1, 2, 3, 4 };
284+
test_synchronized_value_basics<std::mutex>();
285+
}
286+
287+
TEMPLATE_TEST_CASE("synchronized_value basics with std::shared_mutex", "[template][thread-safe]", std::shared_mutex)
288+
{
289+
test_synchronized_value_basics<std::shared_mutex>();
219290
}
220291

221-
TEMPLATE_LIST_TEST_CASE("synchronized_value apply example", "[template][thread-safe]", supported_mutex_types)
292+
TEMPLATE_TEST_CASE("synchronized_value basics with std::recursive_mutex", "[template][thread-safe]", std::recursive_mutex)
222293
{
223-
using synchronized_value = mamba::util::synchronized_value<std::vector<int>, TestType>;
294+
test_synchronized_value_basics<std::recursive_mutex>();
295+
}
296+
297+
// Factorized initializer-list test
298+
template <typename MutexType>
299+
void test_synchronized_value_initializer_list() {
300+
using synchronized_value = mamba::util::synchronized_value<std::vector<int>, MutexType>;
301+
synchronized_value values{ 1, 2, 3, 4 };
302+
}
224303

304+
// Factorized apply example test
305+
template <typename MutexType>
306+
void test_synchronized_value_apply_example() {
307+
using synchronized_value = mamba::util::synchronized_value<std::vector<int>, MutexType>;
225308
const std::vector initial_values{ 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 };
226309
const std::vector sorted_values{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
227-
228310
synchronized_value values{ initial_values };
229311
values.apply(std::ranges::sort);
230312
REQUIRE(values == sorted_values);
231313
values.apply(std::ranges::sort, std::ranges::greater{});
232314
REQUIRE(values == initial_values);
233315
}
234316

235-
template <mamba::util::Mutex M>
236-
auto test_concurrent_increment(
237-
std::invocable<mamba::util::synchronized_value<ValueType, M>&> auto increment_task
238-
)
239-
{
240-
static constexpr auto arbitrary_number_of_executing_threads = 512;
241-
242-
mamba::util::synchronized_value<ValueType, M> current_value;
243-
static constexpr int expected_result = arbitrary_number_of_executing_threads;
244-
245-
std::atomic<bool> run_tasks = false; // used to launch tasks about the same time, simpler
246-
// than condition_variable
247-
std::vector<std::future<void>> tasks;
248-
249-
// Launch the reading and writing tasks (maybe threads, depends on how async is implemented)
250-
for (int i = 0; i < expected_result * 2; ++i)
251-
{
252-
if (i % 2) // intertwine reading and writing tasks
253-
{
254-
// add writing task
255-
tasks.push_back(std::async(
256-
std::launch::async,
257-
[&, increment_task]
258-
{
259-
// don't actually run until we get the green light
260-
mambatests::wait_condition([&] { return run_tasks == true; });
261-
increment_task(current_value);
262-
}
263-
));
264-
}
265-
else
266-
{
267-
// add reading task
268-
tasks.push_back(std::async(
269-
std::launch::async,
270-
[&]
271-
{
272-
// don't actually run until we get the green light
273-
mambatests::wait_condition([&] { return run_tasks == true; });
274-
const auto& readonly_value = std::as_const(current_value);
275-
static constexpr auto arbitrary_read_count = 100;
276-
long long sum = 0;
277-
for (int c = 0; c < arbitrary_read_count; ++c)
278-
{
279-
sum += readonly_value->x; // TODO: also try to mix reading and writing
280-
// using different kinds of access
281-
std::this_thread::yield(); // for timing randomness and limit
282-
// over-exhaustion
283-
}
284-
REQUIRE(sum != 0); // It is possible but extremely unlikely that all
285-
// reading tasks will read before any writing tasks.
286-
}
287-
));
288-
}
289-
}
290-
291-
292-
run_tasks = true; // green light, tasks will run probably concurrently, worse case in
293-
// unpredictable order
294-
for (auto& task : tasks)
295-
{
296-
task.wait(); // wait all to be finished
297-
}
298-
299-
REQUIRE(current_value->x == expected_result);
300-
}
301-
302-
TEMPLATE_LIST_TEST_CASE(
303-
"synchronized_value thread-safe direct_access",
304-
"[template][thread-safe]",
305-
supported_mutex_types
306-
)
307-
{
308-
using synchronized_value = mamba::util::synchronized_value<ValueType, TestType>;
309-
test_concurrent_increment<TestType>([](synchronized_value& sv) { sv->x += 1; });
317+
// Factorized thread-safe direct_access test
318+
template <typename MutexType>
319+
void test_synchronized_value_threadsafe_direct_access() {
320+
using synchronized_value = mamba::util::synchronized_value<ValueType, MutexType>;
321+
test_concurrent_increment<MutexType>([](synchronized_value& sv) { sv->x += 1; });
310322
}
311323

312-
TEMPLATE_LIST_TEST_CASE(
313-
"synchronized_value thread-safe synchronize",
314-
"[template][thread-safe]",
315-
supported_mutex_types
316-
)
317-
{
318-
using synchronized_value = mamba::util::synchronized_value<ValueType, TestType>;
319-
test_concurrent_increment<TestType>(
324+
// Factorized thread-safe synchronize test
325+
template <typename MutexType>
326+
void test_synchronized_value_threadsafe_synchronize() {
327+
using synchronized_value = mamba::util::synchronized_value<ValueType, MutexType>;
328+
test_concurrent_increment<MutexType>(
320329
[](synchronized_value& sv)
321330
{
322331
auto synched_sv = sv.synchronize();
@@ -325,26 +334,20 @@ namespace
325334
);
326335
}
327336

328-
TEMPLATE_LIST_TEST_CASE(
329-
"synchronized_value thread-safe apply",
330-
"[template][thread-safe]",
331-
supported_mutex_types
332-
)
333-
{
334-
using synchronized_value = mamba::util::synchronized_value<ValueType, TestType>;
335-
test_concurrent_increment<TestType>([](synchronized_value& sv)
336-
{ sv.apply([](ValueType& value) { value.x += 1; }); });
337+
// Factorized thread-safe apply test
338+
template <typename MutexType>
339+
void test_synchronized_value_threadsafe_apply() {
340+
using synchronized_value = mamba::util::synchronized_value<ValueType, MutexType>;
341+
test_concurrent_increment<MutexType>([](synchronized_value& sv)
342+
{ sv.apply([](ValueType& value) { value.x += 1; }); });
337343
}
338344

339-
TEMPLATE_LIST_TEST_CASE(
340-
"synchronized_value thread-safe multiple synchronize",
341-
"[template][thread-safe]",
342-
supported_mutex_types
343-
)
344-
{
345-
using synchronized_value = mamba::util::synchronized_value<ValueType, TestType>;
345+
// Factorized thread-safe multiple synchronize test
346+
template <typename MutexType>
347+
void test_synchronized_value_threadsafe_multiple_synchronize() {
348+
using synchronized_value = mamba::util::synchronized_value<ValueType, MutexType>;
346349
const mamba::util::synchronized_value<std::vector<int>, std::shared_mutex> extra_values{ 1 };
347-
test_concurrent_increment<TestType>(
350+
test_concurrent_increment<MutexType>(
348351
[&](synchronized_value& sv)
349352
{
350353
auto [ssv, sev] = synchronize(sv, extra_values);
@@ -353,6 +356,85 @@ namespace
353356
);
354357
}
355358

359+
// Now call these from the TEMPLATE_TEST_CASEs
360+
TEMPLATE_TEST_CASE("synchronized_value initializer-list with std::mutex", "[template][thread-safe]", std::mutex)
361+
{
362+
test_synchronized_value_initializer_list<std::mutex>();
363+
}
364+
TEMPLATE_TEST_CASE("synchronized_value initializer-list with std::shared_mutex", "[template][thread-safe]", std::shared_mutex)
365+
{
366+
test_synchronized_value_initializer_list<std::shared_mutex>();
367+
}
368+
TEMPLATE_TEST_CASE("synchronized_value initializer-list with std::recursive_mutex", "[template][thread-safe]", std::recursive_mutex)
369+
{
370+
test_synchronized_value_initializer_list<std::recursive_mutex>();
371+
}
372+
373+
TEMPLATE_TEST_CASE("synchronized_value apply example with std::mutex", "[template][thread-safe]", std::mutex)
374+
{
375+
test_synchronized_value_apply_example<std::mutex>();
376+
}
377+
TEMPLATE_TEST_CASE("synchronized_value apply example with std::shared_mutex", "[template][thread-safe]", std::shared_mutex)
378+
{
379+
test_synchronized_value_apply_example<std::shared_mutex>();
380+
}
381+
TEMPLATE_TEST_CASE("synchronized_value apply example with std::recursive_mutex", "[template][thread-safe]", std::recursive_mutex)
382+
{
383+
test_synchronized_value_apply_example<std::recursive_mutex>();
384+
}
385+
386+
TEMPLATE_TEST_CASE("synchronized_value thread-safe direct_access with std::mutex", "[template][thread-safe]", std::mutex)
387+
{
388+
test_synchronized_value_threadsafe_direct_access<std::mutex>();
389+
}
390+
TEMPLATE_TEST_CASE("synchronized_value thread-safe direct_access with std::shared_mutex", "[template][thread-safe]", std::shared_mutex)
391+
{
392+
test_synchronized_value_threadsafe_direct_access<std::shared_mutex>();
393+
}
394+
TEMPLATE_TEST_CASE("synchronized_value thread-safe direct_access with std::recursive_mutex", "[template][thread-safe]", std::recursive_mutex)
395+
{
396+
test_synchronized_value_threadsafe_direct_access<std::recursive_mutex>();
397+
}
398+
399+
TEMPLATE_TEST_CASE("synchronized_value thread-safe synchronize with std::mutex", "[template][thread-safe]", std::mutex)
400+
{
401+
test_synchronized_value_threadsafe_synchronize<std::mutex>();
402+
}
403+
TEMPLATE_TEST_CASE("synchronized_value thread-safe synchronize with std::shared_mutex", "[template][thread-safe]", std::shared_mutex)
404+
{
405+
test_synchronized_value_threadsafe_synchronize<std::shared_mutex>();
406+
}
407+
TEMPLATE_TEST_CASE("synchronized_value thread-safe synchronize with std::recursive_mutex", "[template][thread-safe]", std::recursive_mutex)
408+
{
409+
test_synchronized_value_threadsafe_synchronize<std::recursive_mutex>();
410+
}
411+
412+
TEMPLATE_TEST_CASE("synchronized_value thread-safe apply with std::mutex", "[template][thread-safe]", std::mutex)
413+
{
414+
test_synchronized_value_threadsafe_apply<std::mutex>();
415+
}
416+
TEMPLATE_TEST_CASE("synchronized_value thread-safe apply with std::shared_mutex", "[template][thread-safe]", std::shared_mutex)
417+
{
418+
test_synchronized_value_threadsafe_apply<std::shared_mutex>();
419+
}
420+
TEMPLATE_TEST_CASE("synchronized_value thread-safe apply with std::recursive_mutex", "[template][thread-safe]", std::recursive_mutex)
421+
{
422+
test_synchronized_value_threadsafe_apply<std::recursive_mutex>();
423+
}
424+
425+
TEMPLATE_TEST_CASE("synchronized_value thread-safe multiple synchronize with std::mutex", "[template][thread-safe]", std::mutex)
426+
{
427+
test_synchronized_value_threadsafe_multiple_synchronize<std::mutex>();
428+
}
429+
TEMPLATE_TEST_CASE("synchronized_value thread-safe multiple synchronize with std::shared_mutex", "[template][thread-safe]", std::shared_mutex)
430+
{
431+
test_synchronized_value_threadsafe_multiple_synchronize<std::shared_mutex>();
432+
}
433+
TEMPLATE_TEST_CASE("synchronized_value thread-safe multiple synchronize with std::recursive_mutex", "[template][thread-safe]", std::recursive_mutex)
434+
{
435+
test_synchronized_value_threadsafe_multiple_synchronize<std::recursive_mutex>();
436+
}
437+
356438
TEST_CASE("synchronized_value basics multiple synchronize")
357439
{
358440
using namespace mamba::util;

0 commit comments

Comments
 (0)