feat: add race condition tests for consumer and producer interactions
This commit is contained in:
parent
2fe983d288
commit
4a743f26a4
@ -73,3 +73,18 @@ target_link_libraries(test_main_window
|
|||||||
)
|
)
|
||||||
|
|
||||||
add_test(NAME test_main_window COMMAND 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)
|
||||||
|
|||||||
83
tests/test_race_conditions.cxx
Normal file
83
tests/test_race_conditions.cxx
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
// test_race_conditions.cxx
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
// Author: Unai Blazquez <unaibg2000@gmail.com>
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#include <atomic>
|
||||||
|
#include <chrono>
|
||||||
|
#include <iostream>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
#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<bool> 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<bool> 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();
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user