fix(test): correct test logic and semantics in multiple test cases

1. ChannelTest::MultipleSendersReceivers
   - Add C++14-compatible latch implementation (similar to C++20 std::latch)
   - Ensure receivers are ready before senders start sending messages
   - This prevents race condition where senders might send before receivers are listening

2. RWLockTest::ReadWriteReadPattern
   - Fix test logic: lock_shared allows multiple concurrent readers
   - Previous test had race condition where both threads could read same value
   - New test: each thread writes based on thread id (1 or 2), then reads
   - Expected result: 1*20 + 2*20 = 60

3. ShmTest::ReleaseMemory
   - Correct return value semantics: release() returns ref count before decrement, or -1 on error
   - Must call get_mem() to map memory and set ref count to 1 before release
   - Expected: release() returns 1 (ref count before decrement)

4. ShmTest::ReferenceCount
   - Correct semantics: ref count is 0 after acquire (memory not mapped)
   - get_mem() maps memory and sets ref count to 1
   - Second acquire+get_mem increases ref count to 2
   - Test now properly validates reference counting behavior

5. ShmTest::SubtractReference
   - sub_ref() only works after get_mem() has mapped the memory
   - Must call get_mem() first to initialize ref count to 1
   - sub_ref() then decrements it to 0
   - Test now follows correct API usage pattern
This commit is contained in:
木头云 2025-11-30 06:06:54 +00:00
parent d5f787596a
commit 78be284668
3 changed files with 92 additions and 31 deletions

View File

@ -22,6 +22,8 @@
#include <vector>
#include <string>
#include <cstring>
#include <mutex>
#include <condition_variable>
#include "libipc/ipc.h"
#include "libipc/buffer.h"
@ -29,6 +31,29 @@ using namespace ipc;
namespace {
// Simple latch implementation for C++14 (similar to C++20 std::latch)
class latch {
public:
explicit latch(std::ptrdiff_t count) : count_(count) {}
void count_down() {
std::unique_lock<std::mutex> lock(mutex_);
if (--count_ <= 0) {
cv_.notify_all();
}
}
void wait() {
std::unique_lock<std::mutex> lock(mutex_);
cv_.wait(lock, [this] { return count_ <= 0; });
}
private:
std::ptrdiff_t count_;
std::mutex mutex_;
std::condition_variable cv_;
};
std::string generate_unique_ipc_name(const char* prefix) {
static int counter = 0;
return std::string(prefix) + "_ipc_" + std::to_string(++counter);
@ -516,6 +541,27 @@ TEST_F(ChannelTest, MultipleSendersReceivers) {
std::atomic<int> sent_count{0};
std::atomic<int> received_count{0};
// Use latch to ensure receivers are ready before senders start
latch receivers_ready(num_receivers);
std::vector<std::thread> receivers;
for (int i = 0; i < num_receivers; ++i) {
receivers.emplace_back([&, i]() {
channel ch(name.c_str(), receiver);
receivers_ready.count_down(); // Signal this receiver is ready
for (int j = 0; j < messages_per_sender; ++j) {
buffer buf = ch.recv(2000);
if (!buf.empty()) {
++received_count;
}
}
});
}
// Wait for all receivers to be ready
receivers_ready.wait();
std::vector<std::thread> senders;
for (int i = 0; i < num_senders; ++i) {
senders.emplace_back([&, i]() {
@ -530,19 +576,6 @@ TEST_F(ChannelTest, MultipleSendersReceivers) {
});
}
std::vector<std::thread> receivers;
for (int i = 0; i < num_receivers; ++i) {
receivers.emplace_back([&, i]() {
channel ch(name.c_str(), receiver);
for (int j = 0; j < messages_per_sender; ++j) {
buffer buf = ch.recv(2000);
if (!buf.empty()) {
++received_count;
}
}
});
}
for (auto& t : senders) {
t.join();
}

View File

@ -383,20 +383,23 @@ TEST_F(RWLockTest, ReadersWritersNoOverlap) {
TEST_F(RWLockTest, ReadWriteReadPattern) {
rw_lock lock;
int data = 0;
std::atomic<int> iterations{0};
auto pattern_task = [&](int id) {
for (int i = 0; i < 20; ++i) {
// Read
lock.lock_shared();
int read_val = data;
lock.unlock_shared();
// Write: increment based on thread id
lock.lock();
data += id;
lock.unlock();
iterations.fetch_add(1);
std::this_thread::yield();
// Write
lock.lock();
data = read_val + 1;
lock.unlock();
// Read: verify data is consistent
lock.lock_shared();
int read_val = data;
EXPECT_GE(read_val, 0); // Data should be non-negative
lock.unlock_shared();
std::this_thread::yield();
}
@ -408,7 +411,10 @@ TEST_F(RWLockTest, ReadWriteReadPattern) {
t1.join();
t2.join();
EXPECT_EQ(data, 40);
// Each thread increments by its id (1 or 2), 20 times each
// Total = 1*20 + 2*20 = 20 + 40 = 60
EXPECT_EQ(data, 60);
EXPECT_EQ(iterations.load(), 40);
}
// Test many readers, one writer

View File

@ -126,8 +126,13 @@ TEST_F(ShmTest, ReleaseMemory) {
shm::id_t id = shm::acquire(name.c_str(), 128, shm::create);
ASSERT_NE(id, nullptr);
// Must call get_mem to map memory and set reference count
void* mem = shm::get_mem(id, nullptr);
ASSERT_NE(mem, nullptr);
// release returns the reference count before decrement, or -1 on error
std::int32_t ref_count = shm::release(id);
EXPECT_GE(ref_count, 0);
EXPECT_EQ(ref_count, 1); // Should be 1 (set by get_mem, before decrement)
shm::remove(name.c_str());
}
@ -161,14 +166,25 @@ TEST_F(ShmTest, ReferenceCount) {
shm::id_t id1 = shm::acquire(name.c_str(), 512, shm::create);
ASSERT_NE(id1, nullptr);
std::int32_t ref1 = shm::get_ref(id1);
EXPECT_GT(ref1, 0);
// Reference count is 0 after acquire (memory not mapped yet)
std::int32_t ref_before_get_mem = shm::get_ref(id1);
EXPECT_EQ(ref_before_get_mem, 0);
// Acquire again (should increase reference count)
// get_mem maps memory and sets reference count to 1
void* mem1 = shm::get_mem(id1, nullptr);
ASSERT_NE(mem1, nullptr);
std::int32_t ref1 = shm::get_ref(id1);
EXPECT_EQ(ref1, 1);
// Acquire again and get_mem (should increase reference count)
shm::id_t id2 = shm::acquire(name.c_str(), 512, shm::open);
if (id2 != nullptr) {
void* mem2 = shm::get_mem(id2, nullptr);
ASSERT_NE(mem2, nullptr);
std::int32_t ref2 = shm::get_ref(id2);
EXPECT_GE(ref2, ref1);
EXPECT_EQ(ref2, 2); // Should be 2 now
shm::release(id2);
}
@ -184,11 +200,17 @@ TEST_F(ShmTest, SubtractReference) {
shm::id_t id = shm::acquire(name.c_str(), 256, shm::create);
ASSERT_NE(id, nullptr);
std::int32_t ref_before = shm::get_ref(id);
shm::sub_ref(id);
std::int32_t ref_after = shm::get_ref(id);
// Must call get_mem first to map memory and initialize reference count
void* mem = shm::get_mem(id, nullptr);
ASSERT_NE(mem, nullptr);
EXPECT_EQ(ref_after, ref_before - 1);
std::int32_t ref_before = shm::get_ref(id);
EXPECT_EQ(ref_before, 1); // Should be 1 after get_mem
shm::sub_ref(id);
std::int32_t ref_after = shm::get_ref(id);
EXPECT_EQ(ref_after, 0); // Should be 0 after sub_ref
// Use remove(id) to clean up - it internally calls release()
shm::remove(id);