From 4a743f26a4b59d0df41ab3816337e7c18205fbe6 Mon Sep 17 00:00:00 2001 From: unai_71 Date: Tue, 10 Mar 2026 20:05:19 +0000 Subject: [PATCH] feat: add race condition tests for consumer and producer interactions --- tests/CMakeLists.txt | 15 ++++++ tests/test_race_conditions.cxx | 83 ++++++++++++++++++++++++++++++++++ 2 files changed, 98 insertions(+) create mode 100644 tests/test_race_conditions.cxx diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 30538ed..aa8f19e 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -73,3 +73,18 @@ target_link_libraries(test_main_window ) add_test(NAME test_main_window COMMAND test_main_window) + +add_executable(test_race_conditions + test_race_conditions.cxx +) + +target_link_libraries(test_race_conditions + PRIVATE + core + gtest + gtest_main + Qt5::Core + Qt5::Test +) + +add_test(NAME test_race_conditions COMMAND test_race_conditions) diff --git a/tests/test_race_conditions.cxx b/tests/test_race_conditions.cxx new file mode 100644 index 0000000..b367719 --- /dev/null +++ b/tests/test_race_conditions.cxx @@ -0,0 +1,83 @@ +// test_race_conditions.cxx +// SPDX-License-Identifier: GPL-3.0-or-later +// Author: Unai Blazquez + +#include + +#include +#include +#include +#include +#include +#include + +#include "Consumer.hpp" +#include "UnixIpcBridge.hpp" + +static int argc_ = 0; +static QCoreApplication app_(argc_, nullptr); + + +TEST(RaceConditionTest, RepeatedStartStopWhileProducerSends) +{ + const std::string sock = "/tmp/test_race.sock"; + constexpr int kCycles = 20; + + // Watchdog: if the test takes longer than 15s, declare deadlock. + std::atomic test_done{false}; + std::thread watchdog([&test_done]() { + for (int i = 0; i < 150 && !test_done.load(); ++i) + { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + if (!test_done.load()) + { + std::cerr + << "DEADLOCK DETECTED: RepeatedStartStopWhileProducerSends timed out" + << std::endl; + std::abort(); + } + }); + + // Producer thread: keeps trying to send values. connect() failures + // (consumer mid-restart) are expected and silently ignored. + + std::atomic producer_running{true}; + std::thread producer([&]() { + while (producer_running.load()) + { + try + { + UnixIpcBridge bridge(sock); + bridge.send(42); + } + catch (const std::runtime_error&) + { + // Expected: consumer socket not ready or just torn down. + } + std::this_thread::sleep_for(std::chrono::milliseconds(5)); + } + }); + + // Main thread: repeatedly start/stop the consumer. + for (int i = 0; i < kCycles; ++i) + { + ConsumerThread consumer(sock); + consumer.start(); + + // Let it run briefly so the producer can connect during some cycles. + std::this_thread::sleep_for(std::chrono::milliseconds(10 + (i % 5) * 5)); + + // stop() must return without deadlock every single time. + consumer.stop(); + } + + producer_running.store(false); + producer.join(); + + test_done.store(true); + watchdog.join(); + + // If we reach here, no deadlock across kCycles start/stop cycles. + SUCCEED(); +}