mirror of
https://github.com/mutouyun/cpp-ipc.git
synced 2025-12-06 16:56:45 +08:00
style(test): change indentation from 4 spaces to 2 spaces
- Update all test files to use 2-space indentation - Affects: test_buffer.cpp, test_shm.cpp, test_mutex.cpp - Affects: test_semaphore.cpp, test_condition.cpp - Affects: test_locks.cpp, test_ipc_channel.cpp - Improves code consistency and readability
This commit is contained in:
parent
b5146655fa
commit
2cde78d692
@ -22,12 +22,12 @@ namespace {
|
|||||||
|
|
||||||
// Custom destructor tracker for testing
|
// Custom destructor tracker for testing
|
||||||
struct DestructorTracker {
|
struct DestructorTracker {
|
||||||
static int count;
|
static int count;
|
||||||
static void reset() { count = 0; }
|
static void reset() { count = 0; }
|
||||||
static void destructor(void* p, std::size_t) {
|
static void destructor(void* p, std::size_t) {
|
||||||
++count;
|
++count;
|
||||||
delete[] static_cast<char*>(p);
|
delete[] static_cast<char*>(p);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
int DestructorTracker::count = 0;
|
int DestructorTracker::count = 0;
|
||||||
|
|
||||||
@ -35,334 +35,334 @@ int DestructorTracker::count = 0;
|
|||||||
|
|
||||||
class BufferTest : public ::testing::Test {
|
class BufferTest : public ::testing::Test {
|
||||||
protected:
|
protected:
|
||||||
void SetUp() override {
|
void SetUp() override {
|
||||||
DestructorTracker::reset();
|
DestructorTracker::reset();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Test default constructor
|
// Test default constructor
|
||||||
TEST_F(BufferTest, DefaultConstructor) {
|
TEST_F(BufferTest, DefaultConstructor) {
|
||||||
buffer buf;
|
buffer buf;
|
||||||
EXPECT_TRUE(buf.empty());
|
EXPECT_TRUE(buf.empty());
|
||||||
EXPECT_EQ(buf.size(), 0u);
|
EXPECT_EQ(buf.size(), 0u);
|
||||||
EXPECT_EQ(buf.data(), nullptr);
|
EXPECT_EQ(buf.data(), nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test constructor with pointer, size, and destructor
|
// Test constructor with pointer, size, and destructor
|
||||||
TEST_F(BufferTest, ConstructorWithDestructor) {
|
TEST_F(BufferTest, ConstructorWithDestructor) {
|
||||||
const char* test_data = "Hello, World!";
|
const char* test_data = "Hello, World!";
|
||||||
std::size_t size = std::strlen(test_data) + 1;
|
std::size_t size = std::strlen(test_data) + 1;
|
||||||
char* data = new char[size];
|
char* data = new char[size];
|
||||||
std::strcpy(data, test_data);
|
std::strcpy(data, test_data);
|
||||||
|
|
||||||
buffer buf(data, size, DestructorTracker::destructor);
|
buffer buf(data, size, DestructorTracker::destructor);
|
||||||
|
|
||||||
EXPECT_FALSE(buf.empty());
|
EXPECT_FALSE(buf.empty());
|
||||||
EXPECT_EQ(buf.size(), size);
|
EXPECT_EQ(buf.size(), size);
|
||||||
EXPECT_NE(buf.data(), nullptr);
|
EXPECT_NE(buf.data(), nullptr);
|
||||||
EXPECT_STREQ(static_cast<const char*>(buf.data()), test_data);
|
EXPECT_STREQ(static_cast<const char*>(buf.data()), test_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test destructor is called
|
// Test destructor is called
|
||||||
TEST_F(BufferTest, DestructorCalled) {
|
TEST_F(BufferTest, DestructorCalled) {
|
||||||
{
|
{
|
||||||
char* data = new char[100];
|
char* data = new char[100];
|
||||||
buffer buf(data, 100, DestructorTracker::destructor);
|
buffer buf(data, 100, DestructorTracker::destructor);
|
||||||
EXPECT_EQ(DestructorTracker::count, 0);
|
EXPECT_EQ(DestructorTracker::count, 0);
|
||||||
}
|
}
|
||||||
EXPECT_EQ(DestructorTracker::count, 1);
|
EXPECT_EQ(DestructorTracker::count, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test constructor with additional parameter
|
// Test constructor with additional parameter
|
||||||
TEST_F(BufferTest, ConstructorWithAdditional) {
|
TEST_F(BufferTest, ConstructorWithAdditional) {
|
||||||
char* data = new char[50];
|
char* data = new char[50];
|
||||||
int additional_value = 42;
|
int additional_value = 42;
|
||||||
|
|
||||||
buffer buf(data, 50, DestructorTracker::destructor, &additional_value);
|
buffer buf(data, 50, DestructorTracker::destructor, &additional_value);
|
||||||
|
|
||||||
EXPECT_FALSE(buf.empty());
|
EXPECT_FALSE(buf.empty());
|
||||||
EXPECT_EQ(buf.size(), 50u);
|
EXPECT_EQ(buf.size(), 50u);
|
||||||
EXPECT_NE(buf.data(), nullptr);
|
EXPECT_NE(buf.data(), nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test constructor without destructor
|
// Test constructor without destructor
|
||||||
TEST_F(BufferTest, ConstructorWithoutDestructor) {
|
TEST_F(BufferTest, ConstructorWithoutDestructor) {
|
||||||
char stack_data[20] = "Stack data";
|
char stack_data[20] = "Stack data";
|
||||||
|
|
||||||
buffer buf(stack_data, 20);
|
buffer buf(stack_data, 20);
|
||||||
|
|
||||||
EXPECT_FALSE(buf.empty());
|
EXPECT_FALSE(buf.empty());
|
||||||
EXPECT_EQ(buf.size(), 20u);
|
EXPECT_EQ(buf.size(), 20u);
|
||||||
EXPECT_EQ(buf.data(), stack_data);
|
EXPECT_EQ(buf.data(), stack_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test constructor from byte array
|
// Test constructor from byte array
|
||||||
TEST_F(BufferTest, ConstructorFromByteArray) {
|
TEST_F(BufferTest, ConstructorFromByteArray) {
|
||||||
byte_t data[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
|
byte_t data[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
|
||||||
|
|
||||||
buffer buf(data);
|
buffer buf(data);
|
||||||
|
|
||||||
EXPECT_FALSE(buf.empty());
|
EXPECT_FALSE(buf.empty());
|
||||||
EXPECT_EQ(buf.size(), 10u);
|
EXPECT_EQ(buf.size(), 10u);
|
||||||
|
|
||||||
const byte_t* buf_data = buf.get<const byte_t*>();
|
const byte_t* buf_data = buf.get<const byte_t*>();
|
||||||
for (int i = 0; i < 10; ++i) {
|
for (int i = 0; i < 10; ++i) {
|
||||||
EXPECT_EQ(buf_data[i], i);
|
EXPECT_EQ(buf_data[i], i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test constructor from single char
|
// Test constructor from single char
|
||||||
TEST_F(BufferTest, ConstructorFromChar) {
|
TEST_F(BufferTest, ConstructorFromChar) {
|
||||||
char c = 'X';
|
char c = 'X';
|
||||||
|
|
||||||
buffer buf(c);
|
buffer buf(c);
|
||||||
|
|
||||||
EXPECT_FALSE(buf.empty());
|
EXPECT_FALSE(buf.empty());
|
||||||
EXPECT_EQ(buf.size(), sizeof(char));
|
EXPECT_EQ(buf.size(), sizeof(char));
|
||||||
EXPECT_EQ(*buf.get<const char*>(), 'X');
|
EXPECT_EQ(*buf.get<const char*>(), 'X');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test move constructor
|
// Test move constructor
|
||||||
TEST_F(BufferTest, MoveConstructor) {
|
TEST_F(BufferTest, MoveConstructor) {
|
||||||
char* data = new char[30];
|
char* data = new char[30];
|
||||||
std::strcpy(data, "Move test");
|
std::strcpy(data, "Move test");
|
||||||
|
|
||||||
buffer buf1(data, 30, DestructorTracker::destructor);
|
buffer buf1(data, 30, DestructorTracker::destructor);
|
||||||
void* original_ptr = buf1.data();
|
void* original_ptr = buf1.data();
|
||||||
std::size_t original_size = buf1.size();
|
std::size_t original_size = buf1.size();
|
||||||
|
|
||||||
buffer buf2(std::move(buf1));
|
buffer buf2(std::move(buf1));
|
||||||
|
|
||||||
// buf2 should have the original data
|
// buf2 should have the original data
|
||||||
EXPECT_EQ(buf2.data(), original_ptr);
|
EXPECT_EQ(buf2.data(), original_ptr);
|
||||||
EXPECT_EQ(buf2.size(), original_size);
|
EXPECT_EQ(buf2.size(), original_size);
|
||||||
EXPECT_FALSE(buf2.empty());
|
EXPECT_FALSE(buf2.empty());
|
||||||
|
|
||||||
// buf1 should be empty after move
|
// buf1 should be empty after move
|
||||||
EXPECT_TRUE(buf1.empty());
|
EXPECT_TRUE(buf1.empty());
|
||||||
EXPECT_EQ(buf1.size(), 0u);
|
EXPECT_EQ(buf1.size(), 0u);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test swap
|
// Test swap
|
||||||
TEST_F(BufferTest, Swap) {
|
TEST_F(BufferTest, Swap) {
|
||||||
char* data1 = new char[20];
|
char* data1 = new char[20];
|
||||||
char* data2 = new char[30];
|
char* data2 = new char[30];
|
||||||
std::strcpy(data1, "Buffer 1");
|
std::strcpy(data1, "Buffer 1");
|
||||||
std::strcpy(data2, "Buffer 2");
|
std::strcpy(data2, "Buffer 2");
|
||||||
|
|
||||||
buffer buf1(data1, 20, DestructorTracker::destructor);
|
buffer buf1(data1, 20, DestructorTracker::destructor);
|
||||||
buffer buf2(data2, 30, DestructorTracker::destructor);
|
buffer buf2(data2, 30, DestructorTracker::destructor);
|
||||||
|
|
||||||
void* ptr1 = buf1.data();
|
void* ptr1 = buf1.data();
|
||||||
void* ptr2 = buf2.data();
|
void* ptr2 = buf2.data();
|
||||||
std::size_t size1 = buf1.size();
|
std::size_t size1 = buf1.size();
|
||||||
std::size_t size2 = buf2.size();
|
std::size_t size2 = buf2.size();
|
||||||
|
|
||||||
buf1.swap(buf2);
|
buf1.swap(buf2);
|
||||||
|
|
||||||
EXPECT_EQ(buf1.data(), ptr2);
|
EXPECT_EQ(buf1.data(), ptr2);
|
||||||
EXPECT_EQ(buf1.size(), size2);
|
EXPECT_EQ(buf1.size(), size2);
|
||||||
EXPECT_EQ(buf2.data(), ptr1);
|
EXPECT_EQ(buf2.data(), ptr1);
|
||||||
EXPECT_EQ(buf2.size(), size1);
|
EXPECT_EQ(buf2.size(), size1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test assignment operator (move semantics)
|
// Test assignment operator (move semantics)
|
||||||
TEST_F(BufferTest, AssignmentOperator) {
|
TEST_F(BufferTest, AssignmentOperator) {
|
||||||
char* data = new char[40];
|
char* data = new char[40];
|
||||||
std::strcpy(data, "Assignment test");
|
std::strcpy(data, "Assignment test");
|
||||||
|
|
||||||
buffer buf1(data, 40, DestructorTracker::destructor);
|
buffer buf1(data, 40, DestructorTracker::destructor);
|
||||||
void* original_ptr = buf1.data();
|
void* original_ptr = buf1.data();
|
||||||
|
|
||||||
buffer buf2;
|
buffer buf2;
|
||||||
buf2 = std::move(buf1);
|
buf2 = std::move(buf1);
|
||||||
|
|
||||||
EXPECT_EQ(buf2.data(), original_ptr);
|
EXPECT_EQ(buf2.data(), original_ptr);
|
||||||
EXPECT_FALSE(buf2.empty());
|
EXPECT_FALSE(buf2.empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test empty() method
|
// Test empty() method
|
||||||
TEST_F(BufferTest, EmptyMethod) {
|
TEST_F(BufferTest, EmptyMethod) {
|
||||||
buffer buf1;
|
buffer buf1;
|
||||||
EXPECT_TRUE(buf1.empty());
|
EXPECT_TRUE(buf1.empty());
|
||||||
|
|
||||||
char* data = new char[10];
|
char* data = new char[10];
|
||||||
buffer buf2(data, 10, DestructorTracker::destructor);
|
buffer buf2(data, 10, DestructorTracker::destructor);
|
||||||
EXPECT_FALSE(buf2.empty());
|
EXPECT_FALSE(buf2.empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test data() const method
|
// Test data() const method
|
||||||
TEST_F(BufferTest, DataConstMethod) {
|
TEST_F(BufferTest, DataConstMethod) {
|
||||||
const char* test_str = "Const data test";
|
const char* test_str = "Const data test";
|
||||||
std::size_t size = std::strlen(test_str) + 1;
|
std::size_t size = std::strlen(test_str) + 1;
|
||||||
char* data = new char[size];
|
char* data = new char[size];
|
||||||
std::strcpy(data, test_str);
|
std::strcpy(data, test_str);
|
||||||
|
|
||||||
const buffer buf(data, size, DestructorTracker::destructor);
|
const buffer buf(data, size, DestructorTracker::destructor);
|
||||||
|
|
||||||
const void* const_data = buf.data();
|
const void* const_data = buf.data();
|
||||||
EXPECT_NE(const_data, nullptr);
|
EXPECT_NE(const_data, nullptr);
|
||||||
EXPECT_STREQ(static_cast<const char*>(const_data), test_str);
|
EXPECT_STREQ(static_cast<const char*>(const_data), test_str);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test get<T>() template method
|
// Test get<T>() template method
|
||||||
TEST_F(BufferTest, GetTemplateMethod) {
|
TEST_F(BufferTest, GetTemplateMethod) {
|
||||||
int* int_data = new int[5]{1, 2, 3, 4, 5};
|
int* int_data = new int[5]{1, 2, 3, 4, 5};
|
||||||
|
|
||||||
buffer buf(int_data, 5 * sizeof(int), [](void* p, std::size_t) {
|
buffer buf(int_data, 5 * sizeof(int), [](void* p, std::size_t) {
|
||||||
delete[] static_cast<int*>(p);
|
delete[] static_cast<int*>(p);
|
||||||
});
|
});
|
||||||
|
|
||||||
int* retrieved = buf.get<int*>();
|
int* retrieved = buf.get<int*>();
|
||||||
EXPECT_NE(retrieved, nullptr);
|
EXPECT_NE(retrieved, nullptr);
|
||||||
EXPECT_EQ(retrieved[0], 1);
|
EXPECT_EQ(retrieved[0], 1);
|
||||||
EXPECT_EQ(retrieved[4], 5);
|
EXPECT_EQ(retrieved[4], 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test to_tuple() non-const version
|
// Test to_tuple() non-const version
|
||||||
TEST_F(BufferTest, ToTupleNonConst) {
|
TEST_F(BufferTest, ToTupleNonConst) {
|
||||||
char* data = new char[25];
|
char* data = new char[25];
|
||||||
std::strcpy(data, "Tuple test");
|
std::strcpy(data, "Tuple test");
|
||||||
|
|
||||||
buffer buf(data, 25, DestructorTracker::destructor);
|
buffer buf(data, 25, DestructorTracker::destructor);
|
||||||
|
|
||||||
auto [ptr, size] = buf.to_tuple();
|
auto [ptr, size] = buf.to_tuple();
|
||||||
EXPECT_EQ(ptr, buf.data());
|
EXPECT_EQ(ptr, buf.data());
|
||||||
EXPECT_EQ(size, buf.size());
|
EXPECT_EQ(size, buf.size());
|
||||||
EXPECT_EQ(size, 25u);
|
EXPECT_EQ(size, 25u);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test to_tuple() const version
|
// Test to_tuple() const version
|
||||||
TEST_F(BufferTest, ToTupleConst) {
|
TEST_F(BufferTest, ToTupleConst) {
|
||||||
char* data = new char[30];
|
char* data = new char[30];
|
||||||
std::strcpy(data, "Const tuple");
|
std::strcpy(data, "Const tuple");
|
||||||
|
|
||||||
const buffer buf(data, 30, DestructorTracker::destructor);
|
const buffer buf(data, 30, DestructorTracker::destructor);
|
||||||
|
|
||||||
auto [ptr, size] = buf.to_tuple();
|
auto [ptr, size] = buf.to_tuple();
|
||||||
EXPECT_EQ(ptr, buf.data());
|
EXPECT_EQ(ptr, buf.data());
|
||||||
EXPECT_EQ(size, buf.size());
|
EXPECT_EQ(size, buf.size());
|
||||||
EXPECT_EQ(size, 30u);
|
EXPECT_EQ(size, 30u);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test to_vector() method
|
// Test to_vector() method
|
||||||
TEST_F(BufferTest, ToVector) {
|
TEST_F(BufferTest, ToVector) {
|
||||||
byte_t data_arr[5] = {10, 20, 30, 40, 50};
|
byte_t data_arr[5] = {10, 20, 30, 40, 50};
|
||||||
|
|
||||||
buffer buf(data_arr, 5);
|
buffer buf(data_arr, 5);
|
||||||
|
|
||||||
std::vector<byte_t> vec = buf.to_vector();
|
std::vector<byte_t> vec = buf.to_vector();
|
||||||
ASSERT_EQ(vec.size(), 5u);
|
ASSERT_EQ(vec.size(), 5u);
|
||||||
EXPECT_EQ(vec[0], 10);
|
EXPECT_EQ(vec[0], 10);
|
||||||
EXPECT_EQ(vec[1], 20);
|
EXPECT_EQ(vec[1], 20);
|
||||||
EXPECT_EQ(vec[2], 30);
|
EXPECT_EQ(vec[2], 30);
|
||||||
EXPECT_EQ(vec[3], 40);
|
EXPECT_EQ(vec[3], 40);
|
||||||
EXPECT_EQ(vec[4], 50);
|
EXPECT_EQ(vec[4], 50);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test equality operator
|
// Test equality operator
|
||||||
TEST_F(BufferTest, EqualityOperator) {
|
TEST_F(BufferTest, EqualityOperator) {
|
||||||
byte_t data1[5] = {1, 2, 3, 4, 5};
|
byte_t data1[5] = {1, 2, 3, 4, 5};
|
||||||
byte_t data2[5] = {1, 2, 3, 4, 5};
|
byte_t data2[5] = {1, 2, 3, 4, 5};
|
||||||
byte_t data3[5] = {5, 4, 3, 2, 1};
|
byte_t data3[5] = {5, 4, 3, 2, 1};
|
||||||
|
|
||||||
buffer buf1(data1, 5);
|
buffer buf1(data1, 5);
|
||||||
buffer buf2(data2, 5);
|
buffer buf2(data2, 5);
|
||||||
buffer buf3(data3, 5);
|
buffer buf3(data3, 5);
|
||||||
|
|
||||||
EXPECT_TRUE(buf1 == buf2);
|
EXPECT_TRUE(buf1 == buf2);
|
||||||
EXPECT_FALSE(buf1 == buf3);
|
EXPECT_FALSE(buf1 == buf3);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test inequality operator
|
// Test inequality operator
|
||||||
TEST_F(BufferTest, InequalityOperator) {
|
TEST_F(BufferTest, InequalityOperator) {
|
||||||
byte_t data1[5] = {1, 2, 3, 4, 5};
|
byte_t data1[5] = {1, 2, 3, 4, 5};
|
||||||
byte_t data2[5] = {1, 2, 3, 4, 5};
|
byte_t data2[5] = {1, 2, 3, 4, 5};
|
||||||
byte_t data3[5] = {5, 4, 3, 2, 1};
|
byte_t data3[5] = {5, 4, 3, 2, 1};
|
||||||
|
|
||||||
buffer buf1(data1, 5);
|
buffer buf1(data1, 5);
|
||||||
buffer buf2(data2, 5);
|
buffer buf2(data2, 5);
|
||||||
buffer buf3(data3, 5);
|
buffer buf3(data3, 5);
|
||||||
|
|
||||||
EXPECT_FALSE(buf1 != buf2);
|
EXPECT_FALSE(buf1 != buf2);
|
||||||
EXPECT_TRUE(buf1 != buf3);
|
EXPECT_TRUE(buf1 != buf3);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test size mismatch in equality
|
// Test size mismatch in equality
|
||||||
TEST_F(BufferTest, EqualityWithDifferentSizes) {
|
TEST_F(BufferTest, EqualityWithDifferentSizes) {
|
||||||
byte_t data1[5] = {1, 2, 3, 4, 5};
|
byte_t data1[5] = {1, 2, 3, 4, 5};
|
||||||
byte_t data2[3] = {1, 2, 3};
|
byte_t data2[3] = {1, 2, 3};
|
||||||
|
|
||||||
buffer buf1(data1, 5);
|
buffer buf1(data1, 5);
|
||||||
buffer buf2(data2, 3);
|
buffer buf2(data2, 3);
|
||||||
|
|
||||||
EXPECT_FALSE(buf1 == buf2);
|
EXPECT_FALSE(buf1 == buf2);
|
||||||
EXPECT_TRUE(buf1 != buf2);
|
EXPECT_TRUE(buf1 != buf2);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test empty buffers comparison
|
// Test empty buffers comparison
|
||||||
TEST_F(BufferTest, EmptyBuffersComparison) {
|
TEST_F(BufferTest, EmptyBuffersComparison) {
|
||||||
buffer buf1;
|
buffer buf1;
|
||||||
buffer buf2;
|
buffer buf2;
|
||||||
|
|
||||||
EXPECT_TRUE(buf1 == buf2);
|
EXPECT_TRUE(buf1 == buf2);
|
||||||
EXPECT_FALSE(buf1 != buf2);
|
EXPECT_FALSE(buf1 != buf2);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test large buffer
|
// Test large buffer
|
||||||
TEST_F(BufferTest, LargeBuffer) {
|
TEST_F(BufferTest, LargeBuffer) {
|
||||||
const std::size_t large_size = 1024 * 1024; // 1MB
|
const std::size_t large_size = 1024 * 1024; // 1MB
|
||||||
char* large_data = new char[large_size];
|
char* large_data = new char[large_size];
|
||||||
|
|
||||||
// Fill with pattern
|
// Fill with pattern
|
||||||
for (std::size_t i = 0; i < large_size; ++i) {
|
for (std::size_t i = 0; i < large_size; ++i) {
|
||||||
large_data[i] = static_cast<char>(i % 256);
|
large_data[i] = static_cast<char>(i % 256);
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer buf(large_data, large_size, [](void* p, std::size_t) {
|
buffer buf(large_data, large_size, [](void* p, std::size_t) {
|
||||||
delete[] static_cast<char*>(p);
|
delete[] static_cast<char*>(p);
|
||||||
});
|
});
|
||||||
|
|
||||||
EXPECT_FALSE(buf.empty());
|
EXPECT_FALSE(buf.empty());
|
||||||
EXPECT_EQ(buf.size(), large_size);
|
EXPECT_EQ(buf.size(), large_size);
|
||||||
|
|
||||||
// Verify pattern
|
// Verify pattern
|
||||||
const char* data_ptr = buf.get<const char*>();
|
const char* data_ptr = buf.get<const char*>();
|
||||||
for (std::size_t i = 0; i < 100; ++i) { // Check first 100 bytes
|
for (std::size_t i = 0; i < 100; ++i) { // Check first 100 bytes
|
||||||
EXPECT_EQ(data_ptr[i], static_cast<char>(i % 256));
|
EXPECT_EQ(data_ptr[i], static_cast<char>(i % 256));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test multiple move operations
|
// Test multiple move operations
|
||||||
TEST_F(BufferTest, MultipleMoves) {
|
TEST_F(BufferTest, MultipleMoves) {
|
||||||
char* data = new char[15];
|
char* data = new char[15];
|
||||||
std::strcpy(data, "Multi-move");
|
std::strcpy(data, "Multi-move");
|
||||||
void* original_ptr = data;
|
void* original_ptr = data;
|
||||||
|
|
||||||
buffer buf1(data, 15, DestructorTracker::destructor);
|
buffer buf1(data, 15, DestructorTracker::destructor);
|
||||||
buffer buf2(std::move(buf1));
|
buffer buf2(std::move(buf1));
|
||||||
buffer buf3(std::move(buf2));
|
buffer buf3(std::move(buf2));
|
||||||
buffer buf4(std::move(buf3));
|
buffer buf4(std::move(buf3));
|
||||||
|
|
||||||
EXPECT_EQ(buf4.data(), original_ptr);
|
EXPECT_EQ(buf4.data(), original_ptr);
|
||||||
EXPECT_TRUE(buf1.empty());
|
EXPECT_TRUE(buf1.empty());
|
||||||
EXPECT_TRUE(buf2.empty());
|
EXPECT_TRUE(buf2.empty());
|
||||||
EXPECT_TRUE(buf3.empty());
|
EXPECT_TRUE(buf3.empty());
|
||||||
EXPECT_FALSE(buf4.empty());
|
EXPECT_FALSE(buf4.empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test self-assignment safety
|
// Test self-assignment safety
|
||||||
TEST_F(BufferTest, SelfAssignment) {
|
TEST_F(BufferTest, SelfAssignment) {
|
||||||
char* data = new char[20];
|
char* data = new char[20];
|
||||||
std::strcpy(data, "Self-assign");
|
std::strcpy(data, "Self-assign");
|
||||||
|
|
||||||
buffer buf(data, 20, DestructorTracker::destructor);
|
buffer buf(data, 20, DestructorTracker::destructor);
|
||||||
void* original_ptr = buf.data();
|
void* original_ptr = buf.data();
|
||||||
std::size_t original_size = buf.size();
|
std::size_t original_size = buf.size();
|
||||||
|
|
||||||
buf = std::move(buf); // Self-assignment
|
buf = std::move(buf); // Self-assignment
|
||||||
|
|
||||||
// Should remain valid
|
// Should remain valid
|
||||||
EXPECT_EQ(buf.data(), original_ptr);
|
EXPECT_EQ(buf.data(), original_ptr);
|
||||||
EXPECT_EQ(buf.size(), original_size);
|
EXPECT_EQ(buf.size(), original_size);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -26,525 +26,525 @@ using namespace ipc::sync;
|
|||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
std::string generate_unique_cv_name(const char* prefix) {
|
std::string generate_unique_cv_name(const char* prefix) {
|
||||||
static int counter = 0;
|
static int counter = 0;
|
||||||
return std::string(prefix) + "_cv_" + std::to_string(++counter);
|
return std::string(prefix) + "_cv_" + std::to_string(++counter);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // anonymous namespace
|
} // anonymous namespace
|
||||||
|
|
||||||
class ConditionTest : public ::testing::Test {
|
class ConditionTest : public ::testing::Test {
|
||||||
protected:
|
protected:
|
||||||
void TearDown() override {
|
void TearDown() override {
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Test default constructor
|
// Test default constructor
|
||||||
TEST_F(ConditionTest, DefaultConstructor) {
|
TEST_F(ConditionTest, DefaultConstructor) {
|
||||||
condition cv;
|
condition cv;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test named constructor
|
// Test named constructor
|
||||||
TEST_F(ConditionTest, NamedConstructor) {
|
TEST_F(ConditionTest, NamedConstructor) {
|
||||||
std::string name = generate_unique_cv_name("named");
|
std::string name = generate_unique_cv_name("named");
|
||||||
|
|
||||||
condition cv(name.c_str());
|
condition cv(name.c_str());
|
||||||
EXPECT_TRUE(cv.valid());
|
EXPECT_TRUE(cv.valid());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test native() methods
|
// Test native() methods
|
||||||
TEST_F(ConditionTest, NativeHandle) {
|
TEST_F(ConditionTest, NativeHandle) {
|
||||||
std::string name = generate_unique_cv_name("native");
|
std::string name = generate_unique_cv_name("native");
|
||||||
|
|
||||||
condition cv(name.c_str());
|
condition cv(name.c_str());
|
||||||
ASSERT_TRUE(cv.valid());
|
ASSERT_TRUE(cv.valid());
|
||||||
|
|
||||||
const void* const_handle = static_cast<const condition&>(cv).native();
|
const void* const_handle = static_cast<const condition&>(cv).native();
|
||||||
void* handle = cv.native();
|
void* handle = cv.native();
|
||||||
|
|
||||||
EXPECT_NE(const_handle, nullptr);
|
EXPECT_NE(const_handle, nullptr);
|
||||||
EXPECT_NE(handle, nullptr);
|
EXPECT_NE(handle, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test valid() method
|
// Test valid() method
|
||||||
TEST_F(ConditionTest, Valid) {
|
TEST_F(ConditionTest, Valid) {
|
||||||
condition cv1;
|
condition cv1;
|
||||||
|
|
||||||
std::string name = generate_unique_cv_name("valid");
|
std::string name = generate_unique_cv_name("valid");
|
||||||
condition cv2(name.c_str());
|
condition cv2(name.c_str());
|
||||||
EXPECT_TRUE(cv2.valid());
|
EXPECT_TRUE(cv2.valid());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test open() method
|
// Test open() method
|
||||||
TEST_F(ConditionTest, Open) {
|
TEST_F(ConditionTest, Open) {
|
||||||
std::string name = generate_unique_cv_name("open");
|
std::string name = generate_unique_cv_name("open");
|
||||||
|
|
||||||
condition cv;
|
condition cv;
|
||||||
bool result = cv.open(name.c_str());
|
bool result = cv.open(name.c_str());
|
||||||
|
|
||||||
EXPECT_TRUE(result);
|
EXPECT_TRUE(result);
|
||||||
EXPECT_TRUE(cv.valid());
|
EXPECT_TRUE(cv.valid());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test close() method
|
// Test close() method
|
||||||
TEST_F(ConditionTest, Close) {
|
TEST_F(ConditionTest, Close) {
|
||||||
std::string name = generate_unique_cv_name("close");
|
std::string name = generate_unique_cv_name("close");
|
||||||
|
|
||||||
condition cv(name.c_str());
|
condition cv(name.c_str());
|
||||||
ASSERT_TRUE(cv.valid());
|
ASSERT_TRUE(cv.valid());
|
||||||
|
|
||||||
cv.close();
|
cv.close();
|
||||||
EXPECT_FALSE(cv.valid());
|
EXPECT_FALSE(cv.valid());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test clear() method
|
// Test clear() method
|
||||||
TEST_F(ConditionTest, Clear) {
|
TEST_F(ConditionTest, Clear) {
|
||||||
std::string name = generate_unique_cv_name("clear");
|
std::string name = generate_unique_cv_name("clear");
|
||||||
|
|
||||||
condition cv(name.c_str());
|
condition cv(name.c_str());
|
||||||
ASSERT_TRUE(cv.valid());
|
ASSERT_TRUE(cv.valid());
|
||||||
|
|
||||||
cv.clear();
|
cv.clear();
|
||||||
EXPECT_FALSE(cv.valid());
|
EXPECT_FALSE(cv.valid());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test clear_storage() static method
|
// Test clear_storage() static method
|
||||||
TEST_F(ConditionTest, ClearStorage) {
|
TEST_F(ConditionTest, ClearStorage) {
|
||||||
std::string name = generate_unique_cv_name("clear_storage");
|
std::string name = generate_unique_cv_name("clear_storage");
|
||||||
|
|
||||||
{
|
{
|
||||||
condition cv(name.c_str());
|
condition cv(name.c_str());
|
||||||
EXPECT_TRUE(cv.valid());
|
EXPECT_TRUE(cv.valid());
|
||||||
}
|
}
|
||||||
|
|
||||||
condition::clear_storage(name.c_str());
|
condition::clear_storage(name.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test basic wait and notify
|
// Test basic wait and notify
|
||||||
TEST_F(ConditionTest, WaitNotify) {
|
TEST_F(ConditionTest, WaitNotify) {
|
||||||
std::string cv_name = generate_unique_cv_name("wait_notify");
|
std::string cv_name = generate_unique_cv_name("wait_notify");
|
||||||
std::string mtx_name = generate_unique_cv_name("wait_notify_mtx");
|
std::string mtx_name = generate_unique_cv_name("wait_notify_mtx");
|
||||||
|
|
||||||
condition cv(cv_name.c_str());
|
condition cv(cv_name.c_str());
|
||||||
mutex mtx(mtx_name.c_str());
|
mutex mtx(mtx_name.c_str());
|
||||||
|
|
||||||
ASSERT_TRUE(cv.valid());
|
ASSERT_TRUE(cv.valid());
|
||||||
ASSERT_TRUE(mtx.valid());
|
ASSERT_TRUE(mtx.valid());
|
||||||
|
|
||||||
std::atomic<bool> notified{false};
|
std::atomic<bool> notified{false};
|
||||||
|
|
||||||
std::thread waiter([&]() {
|
std::thread waiter([&]() {
|
||||||
mtx.lock();
|
mtx.lock();
|
||||||
cv.wait(mtx);
|
cv.wait(mtx);
|
||||||
notified.store(true);
|
notified.store(true);
|
||||||
mtx.unlock();
|
mtx.unlock();
|
||||||
});
|
});
|
||||||
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||||
|
|
||||||
mtx.lock();
|
mtx.lock();
|
||||||
cv.notify(mtx);
|
cv.notify(mtx);
|
||||||
mtx.unlock();
|
mtx.unlock();
|
||||||
|
|
||||||
waiter.join();
|
waiter.join();
|
||||||
|
|
||||||
EXPECT_TRUE(notified.load());
|
EXPECT_TRUE(notified.load());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test broadcast to multiple waiters
|
// Test broadcast to multiple waiters
|
||||||
TEST_F(ConditionTest, Broadcast) {
|
TEST_F(ConditionTest, Broadcast) {
|
||||||
std::string cv_name = generate_unique_cv_name("broadcast");
|
std::string cv_name = generate_unique_cv_name("broadcast");
|
||||||
std::string mtx_name = generate_unique_cv_name("broadcast_mtx");
|
std::string mtx_name = generate_unique_cv_name("broadcast_mtx");
|
||||||
|
|
||||||
condition cv(cv_name.c_str());
|
condition cv(cv_name.c_str());
|
||||||
mutex mtx(mtx_name.c_str());
|
mutex mtx(mtx_name.c_str());
|
||||||
|
|
||||||
ASSERT_TRUE(cv.valid());
|
ASSERT_TRUE(cv.valid());
|
||||||
ASSERT_TRUE(mtx.valid());
|
ASSERT_TRUE(mtx.valid());
|
||||||
|
|
||||||
std::atomic<int> notified_count{0};
|
std::atomic<int> notified_count{0};
|
||||||
const int num_waiters = 5;
|
const int num_waiters = 5;
|
||||||
|
|
||||||
std::vector<std::thread> waiters;
|
std::vector<std::thread> waiters;
|
||||||
for (int i = 0; i < num_waiters; ++i) {
|
for (int i = 0; i < num_waiters; ++i) {
|
||||||
waiters.emplace_back([&]() {
|
waiters.emplace_back([&]() {
|
||||||
mtx.lock();
|
mtx.lock();
|
||||||
cv.wait(mtx);
|
cv.wait(mtx);
|
||||||
++notified_count;
|
++notified_count;
|
||||||
mtx.unlock();
|
mtx.unlock();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||||
|
|
||||||
mtx.lock();
|
mtx.lock();
|
||||||
cv.broadcast(mtx);
|
cv.broadcast(mtx);
|
||||||
mtx.unlock();
|
mtx.unlock();
|
||||||
|
|
||||||
for (auto& t : waiters) {
|
for (auto& t : waiters) {
|
||||||
t.join();
|
t.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
EXPECT_EQ(notified_count.load(), num_waiters);
|
EXPECT_EQ(notified_count.load(), num_waiters);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test timed wait with timeout
|
// Test timed wait with timeout
|
||||||
TEST_F(ConditionTest, TimedWait) {
|
TEST_F(ConditionTest, TimedWait) {
|
||||||
std::string cv_name = generate_unique_cv_name("timed_wait");
|
std::string cv_name = generate_unique_cv_name("timed_wait");
|
||||||
std::string mtx_name = generate_unique_cv_name("timed_wait_mtx");
|
std::string mtx_name = generate_unique_cv_name("timed_wait_mtx");
|
||||||
|
|
||||||
condition cv(cv_name.c_str());
|
condition cv(cv_name.c_str());
|
||||||
mutex mtx(mtx_name.c_str());
|
mutex mtx(mtx_name.c_str());
|
||||||
|
|
||||||
ASSERT_TRUE(cv.valid());
|
ASSERT_TRUE(cv.valid());
|
||||||
ASSERT_TRUE(mtx.valid());
|
ASSERT_TRUE(mtx.valid());
|
||||||
|
|
||||||
auto start = std::chrono::steady_clock::now();
|
auto start = std::chrono::steady_clock::now();
|
||||||
|
|
||||||
mtx.lock();
|
mtx.lock();
|
||||||
bool result = cv.wait(mtx, 100); // 100ms timeout
|
bool result = cv.wait(mtx, 100); // 100ms timeout
|
||||||
mtx.unlock();
|
mtx.unlock();
|
||||||
|
|
||||||
auto end = std::chrono::steady_clock::now();
|
auto end = std::chrono::steady_clock::now();
|
||||||
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
|
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
|
||||||
|
|
||||||
EXPECT_FALSE(result); // Should timeout
|
EXPECT_FALSE(result); // Should timeout
|
||||||
EXPECT_GE(elapsed, 80); // Allow some tolerance
|
EXPECT_GE(elapsed, 80); // Allow some tolerance
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test wait with immediate notify
|
// Test wait with immediate notify
|
||||||
TEST_F(ConditionTest, ImmediateNotify) {
|
TEST_F(ConditionTest, ImmediateNotify) {
|
||||||
std::string cv_name = generate_unique_cv_name("immediate");
|
std::string cv_name = generate_unique_cv_name("immediate");
|
||||||
std::string mtx_name = generate_unique_cv_name("immediate_mtx");
|
std::string mtx_name = generate_unique_cv_name("immediate_mtx");
|
||||||
|
|
||||||
condition cv(cv_name.c_str());
|
condition cv(cv_name.c_str());
|
||||||
mutex mtx(mtx_name.c_str());
|
mutex mtx(mtx_name.c_str());
|
||||||
|
|
||||||
ASSERT_TRUE(cv.valid());
|
ASSERT_TRUE(cv.valid());
|
||||||
ASSERT_TRUE(mtx.valid());
|
ASSERT_TRUE(mtx.valid());
|
||||||
|
|
||||||
std::atomic<bool> wait_started{false};
|
std::atomic<bool> wait_started{false};
|
||||||
std::atomic<bool> notified{false};
|
std::atomic<bool> notified{false};
|
||||||
|
|
||||||
std::thread waiter([&]() {
|
std::thread waiter([&]() {
|
||||||
mtx.lock();
|
mtx.lock();
|
||||||
wait_started.store(true);
|
wait_started.store(true);
|
||||||
cv.wait(mtx, 1000); // 1 second timeout
|
cv.wait(mtx, 1000); // 1 second timeout
|
||||||
notified.store(true);
|
notified.store(true);
|
||||||
mtx.unlock();
|
mtx.unlock();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Wait for waiter to start
|
// Wait for waiter to start
|
||||||
while (!wait_started.load()) {
|
while (!wait_started.load()) {
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||||
|
|
||||||
mtx.lock();
|
mtx.lock();
|
||||||
cv.notify(mtx);
|
cv.notify(mtx);
|
||||||
mtx.unlock();
|
mtx.unlock();
|
||||||
|
|
||||||
waiter.join();
|
waiter.join();
|
||||||
|
|
||||||
EXPECT_TRUE(notified.load());
|
EXPECT_TRUE(notified.load());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test producer-consumer with condition variable
|
// Test producer-consumer with condition variable
|
||||||
TEST_F(ConditionTest, ProducerConsumer) {
|
TEST_F(ConditionTest, ProducerConsumer) {
|
||||||
std::string cv_name = generate_unique_cv_name("prod_cons");
|
std::string cv_name = generate_unique_cv_name("prod_cons");
|
||||||
std::string mtx_name = generate_unique_cv_name("prod_cons_mtx");
|
std::string mtx_name = generate_unique_cv_name("prod_cons_mtx");
|
||||||
|
|
||||||
condition cv(cv_name.c_str());
|
condition cv(cv_name.c_str());
|
||||||
mutex mtx(mtx_name.c_str());
|
mutex mtx(mtx_name.c_str());
|
||||||
|
|
||||||
ASSERT_TRUE(cv.valid());
|
ASSERT_TRUE(cv.valid());
|
||||||
ASSERT_TRUE(mtx.valid());
|
ASSERT_TRUE(mtx.valid());
|
||||||
|
|
||||||
std::atomic<int> buffer{0};
|
std::atomic<int> buffer{0};
|
||||||
std::atomic<bool> ready{false};
|
std::atomic<bool> ready{false};
|
||||||
std::atomic<int> consumed_value{0};
|
std::atomic<int> consumed_value{0};
|
||||||
|
|
||||||
std::thread producer([&]() {
|
std::thread producer([&]() {
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||||
|
|
||||||
mtx.lock();
|
mtx.lock();
|
||||||
buffer.store(42);
|
buffer.store(42);
|
||||||
ready.store(true);
|
ready.store(true);
|
||||||
cv.notify(mtx);
|
cv.notify(mtx);
|
||||||
mtx.unlock();
|
mtx.unlock();
|
||||||
});
|
});
|
||||||
|
|
||||||
std::thread consumer([&]() {
|
std::thread consumer([&]() {
|
||||||
mtx.lock();
|
mtx.lock();
|
||||||
while (!ready.load()) {
|
while (!ready.load()) {
|
||||||
cv.wait(mtx, 2000);
|
cv.wait(mtx, 2000);
|
||||||
}
|
}
|
||||||
consumed_value.store(buffer.load());
|
consumed_value.store(buffer.load());
|
||||||
mtx.unlock();
|
mtx.unlock();
|
||||||
});
|
});
|
||||||
|
|
||||||
producer.join();
|
producer.join();
|
||||||
consumer.join();
|
consumer.join();
|
||||||
|
|
||||||
EXPECT_EQ(consumed_value.load(), 42);
|
EXPECT_EQ(consumed_value.load(), 42);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test multiple notify operations
|
// Test multiple notify operations
|
||||||
TEST_F(ConditionTest, MultipleNotify) {
|
TEST_F(ConditionTest, MultipleNotify) {
|
||||||
std::string cv_name = generate_unique_cv_name("multi_notify");
|
std::string cv_name = generate_unique_cv_name("multi_notify");
|
||||||
std::string mtx_name = generate_unique_cv_name("multi_notify_mtx");
|
std::string mtx_name = generate_unique_cv_name("multi_notify_mtx");
|
||||||
|
|
||||||
condition cv(cv_name.c_str());
|
condition cv(cv_name.c_str());
|
||||||
mutex mtx(mtx_name.c_str());
|
mutex mtx(mtx_name.c_str());
|
||||||
|
|
||||||
ASSERT_TRUE(cv.valid());
|
ASSERT_TRUE(cv.valid());
|
||||||
ASSERT_TRUE(mtx.valid());
|
ASSERT_TRUE(mtx.valid());
|
||||||
|
|
||||||
std::atomic<int> notify_count{0};
|
std::atomic<int> notify_count{0};
|
||||||
const int num_notifications = 3;
|
const int num_notifications = 3;
|
||||||
|
|
||||||
std::thread waiter([&]() {
|
std::thread waiter([&]() {
|
||||||
for (int i = 0; i < num_notifications; ++i) {
|
for (int i = 0; i < num_notifications; ++i) {
|
||||||
mtx.lock();
|
mtx.lock();
|
||||||
cv.wait(mtx, 1000);
|
cv.wait(mtx, 1000);
|
||||||
++notify_count;
|
++notify_count;
|
||||||
mtx.unlock();
|
mtx.unlock();
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
for (int i = 0; i < num_notifications; ++i) {
|
for (int i = 0; i < num_notifications; ++i) {
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||||
mtx.lock();
|
mtx.lock();
|
||||||
cv.notify(mtx);
|
cv.notify(mtx);
|
||||||
mtx.unlock();
|
mtx.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
waiter.join();
|
waiter.join();
|
||||||
|
|
||||||
EXPECT_EQ(notify_count.load(), num_notifications);
|
EXPECT_EQ(notify_count.load(), num_notifications);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test notify vs broadcast
|
// Test notify vs broadcast
|
||||||
TEST_F(ConditionTest, NotifyVsBroadcast) {
|
TEST_F(ConditionTest, NotifyVsBroadcast) {
|
||||||
std::string cv_name = generate_unique_cv_name("notify_vs_broadcast");
|
std::string cv_name = generate_unique_cv_name("notify_vs_broadcast");
|
||||||
std::string mtx_name = generate_unique_cv_name("notify_vs_broadcast_mtx");
|
std::string mtx_name = generate_unique_cv_name("notify_vs_broadcast_mtx");
|
||||||
|
|
||||||
condition cv(cv_name.c_str());
|
condition cv(cv_name.c_str());
|
||||||
mutex mtx(mtx_name.c_str());
|
mutex mtx(mtx_name.c_str());
|
||||||
|
|
||||||
ASSERT_TRUE(cv.valid());
|
ASSERT_TRUE(cv.valid());
|
||||||
ASSERT_TRUE(mtx.valid());
|
ASSERT_TRUE(mtx.valid());
|
||||||
|
|
||||||
// Test notify (should wake one)
|
// Test notify (should wake one)
|
||||||
std::atomic<int> notify_woken{0};
|
std::atomic<int> notify_woken{0};
|
||||||
|
|
||||||
std::vector<std::thread> notify_waiters;
|
std::vector<std::thread> notify_waiters;
|
||||||
for (int i = 0; i < 3; ++i) {
|
for (int i = 0; i < 3; ++i) {
|
||||||
notify_waiters.emplace_back([&]() {
|
notify_waiters.emplace_back([&]() {
|
||||||
mtx.lock();
|
mtx.lock();
|
||||||
cv.wait(mtx, 100);
|
cv.wait(mtx, 100);
|
||||||
++notify_woken;
|
++notify_woken;
|
||||||
mtx.unlock();
|
mtx.unlock();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||||
|
|
||||||
mtx.lock();
|
mtx.lock();
|
||||||
cv.notify(mtx); // Wake one
|
cv.notify(mtx); // Wake one
|
||||||
mtx.unlock();
|
mtx.unlock();
|
||||||
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(150));
|
std::this_thread::sleep_for(std::chrono::milliseconds(150));
|
||||||
|
|
||||||
for (auto& t : notify_waiters) {
|
for (auto& t : notify_waiters) {
|
||||||
t.join();
|
t.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
// At least one should be woken by notify
|
// At least one should be woken by notify
|
||||||
EXPECT_GE(notify_woken.load(), 1);
|
EXPECT_GE(notify_woken.load(), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test condition variable with spurious wakeups pattern
|
// Test condition variable with spurious wakeups pattern
|
||||||
TEST_F(ConditionTest, SpuriousWakeupPattern) {
|
TEST_F(ConditionTest, SpuriousWakeupPattern) {
|
||||||
std::string cv_name = generate_unique_cv_name("spurious");
|
std::string cv_name = generate_unique_cv_name("spurious");
|
||||||
std::string mtx_name = generate_unique_cv_name("spurious_mtx");
|
std::string mtx_name = generate_unique_cv_name("spurious_mtx");
|
||||||
|
|
||||||
condition cv(cv_name.c_str());
|
condition cv(cv_name.c_str());
|
||||||
mutex mtx(mtx_name.c_str());
|
mutex mtx(mtx_name.c_str());
|
||||||
|
|
||||||
ASSERT_TRUE(cv.valid());
|
ASSERT_TRUE(cv.valid());
|
||||||
ASSERT_TRUE(mtx.valid());
|
ASSERT_TRUE(mtx.valid());
|
||||||
|
|
||||||
std::atomic<bool> predicate{false};
|
std::atomic<bool> predicate{false};
|
||||||
std::atomic<bool> done{false};
|
std::atomic<bool> done{false};
|
||||||
|
|
||||||
std::thread waiter([&]() {
|
std::thread waiter([&]() {
|
||||||
mtx.lock();
|
mtx.lock();
|
||||||
while (!predicate.load()) {
|
while (!predicate.load()) {
|
||||||
if (!cv.wait(mtx, 100)) {
|
if (!cv.wait(mtx, 100)) {
|
||||||
// Timeout - check predicate again
|
// Timeout - check predicate again
|
||||||
if (predicate.load()) break;
|
if (predicate.load()) break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
done.store(true);
|
done.store(true);
|
||||||
mtx.unlock();
|
mtx.unlock();
|
||||||
});
|
});
|
||||||
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||||
|
|
||||||
mtx.lock();
|
mtx.lock();
|
||||||
predicate.store(true);
|
predicate.store(true);
|
||||||
cv.notify(mtx);
|
cv.notify(mtx);
|
||||||
mtx.unlock();
|
mtx.unlock();
|
||||||
|
|
||||||
waiter.join();
|
waiter.join();
|
||||||
|
|
||||||
EXPECT_TRUE(done.load());
|
EXPECT_TRUE(done.load());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test reopen after close
|
// Test reopen after close
|
||||||
TEST_F(ConditionTest, ReopenAfterClose) {
|
TEST_F(ConditionTest, ReopenAfterClose) {
|
||||||
std::string name = generate_unique_cv_name("reopen");
|
std::string name = generate_unique_cv_name("reopen");
|
||||||
|
|
||||||
condition cv;
|
condition cv;
|
||||||
|
|
||||||
ASSERT_TRUE(cv.open(name.c_str()));
|
ASSERT_TRUE(cv.open(name.c_str()));
|
||||||
EXPECT_TRUE(cv.valid());
|
EXPECT_TRUE(cv.valid());
|
||||||
|
|
||||||
cv.close();
|
cv.close();
|
||||||
EXPECT_FALSE(cv.valid());
|
EXPECT_FALSE(cv.valid());
|
||||||
|
|
||||||
ASSERT_TRUE(cv.open(name.c_str()));
|
ASSERT_TRUE(cv.open(name.c_str()));
|
||||||
EXPECT_TRUE(cv.valid());
|
EXPECT_TRUE(cv.valid());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test named condition variable sharing between threads
|
// Test named condition variable sharing between threads
|
||||||
TEST_F(ConditionTest, NamedSharing) {
|
TEST_F(ConditionTest, NamedSharing) {
|
||||||
std::string cv_name = generate_unique_cv_name("sharing");
|
std::string cv_name = generate_unique_cv_name("sharing");
|
||||||
std::string mtx_name = generate_unique_cv_name("sharing_mtx");
|
std::string mtx_name = generate_unique_cv_name("sharing_mtx");
|
||||||
|
|
||||||
std::atomic<int> value{0};
|
std::atomic<int> value{0};
|
||||||
|
|
||||||
std::thread t1([&]() {
|
std::thread t1([&]() {
|
||||||
condition cv(cv_name.c_str());
|
condition cv(cv_name.c_str());
|
||||||
mutex mtx(mtx_name.c_str());
|
mutex mtx(mtx_name.c_str());
|
||||||
|
|
||||||
ASSERT_TRUE(cv.valid());
|
ASSERT_TRUE(cv.valid());
|
||||||
ASSERT_TRUE(mtx.valid());
|
ASSERT_TRUE(mtx.valid());
|
||||||
|
|
||||||
mtx.lock();
|
mtx.lock();
|
||||||
cv.wait(mtx, 1000);
|
cv.wait(mtx, 1000);
|
||||||
value.store(100);
|
value.store(100);
|
||||||
mtx.unlock();
|
mtx.unlock();
|
||||||
});
|
});
|
||||||
|
|
||||||
std::thread t2([&]() {
|
std::thread t2([&]() {
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||||
|
|
||||||
condition cv(cv_name.c_str());
|
condition cv(cv_name.c_str());
|
||||||
mutex mtx(mtx_name.c_str());
|
mutex mtx(mtx_name.c_str());
|
||||||
|
|
||||||
ASSERT_TRUE(cv.valid());
|
ASSERT_TRUE(cv.valid());
|
||||||
ASSERT_TRUE(mtx.valid());
|
ASSERT_TRUE(mtx.valid());
|
||||||
|
|
||||||
mtx.lock();
|
mtx.lock();
|
||||||
cv.notify(mtx);
|
cv.notify(mtx);
|
||||||
mtx.unlock();
|
mtx.unlock();
|
||||||
});
|
});
|
||||||
|
|
||||||
t1.join();
|
t1.join();
|
||||||
t2.join();
|
t2.join();
|
||||||
|
|
||||||
EXPECT_EQ(value.load(), 100);
|
EXPECT_EQ(value.load(), 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test infinite wait
|
// Test infinite wait
|
||||||
TEST_F(ConditionTest, InfiniteWait) {
|
TEST_F(ConditionTest, InfiniteWait) {
|
||||||
std::string cv_name = generate_unique_cv_name("infinite");
|
std::string cv_name = generate_unique_cv_name("infinite");
|
||||||
std::string mtx_name = generate_unique_cv_name("infinite_mtx");
|
std::string mtx_name = generate_unique_cv_name("infinite_mtx");
|
||||||
|
|
||||||
condition cv(cv_name.c_str());
|
condition cv(cv_name.c_str());
|
||||||
mutex mtx(mtx_name.c_str());
|
mutex mtx(mtx_name.c_str());
|
||||||
|
|
||||||
ASSERT_TRUE(cv.valid());
|
ASSERT_TRUE(cv.valid());
|
||||||
ASSERT_TRUE(mtx.valid());
|
ASSERT_TRUE(mtx.valid());
|
||||||
|
|
||||||
std::atomic<bool> woken{false};
|
std::atomic<bool> woken{false};
|
||||||
|
|
||||||
std::thread waiter([&]() {
|
std::thread waiter([&]() {
|
||||||
mtx.lock();
|
mtx.lock();
|
||||||
cv.wait(mtx, invalid_value); // Infinite wait
|
cv.wait(mtx, invalid_value); // Infinite wait
|
||||||
woken.store(true);
|
woken.store(true);
|
||||||
mtx.unlock();
|
mtx.unlock();
|
||||||
});
|
});
|
||||||
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||||
|
|
||||||
mtx.lock();
|
mtx.lock();
|
||||||
cv.notify(mtx);
|
cv.notify(mtx);
|
||||||
mtx.unlock();
|
mtx.unlock();
|
||||||
|
|
||||||
waiter.join();
|
waiter.join();
|
||||||
|
|
||||||
EXPECT_TRUE(woken.load());
|
EXPECT_TRUE(woken.load());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test broadcast with sequential waiters
|
// Test broadcast with sequential waiters
|
||||||
TEST_F(ConditionTest, BroadcastSequential) {
|
TEST_F(ConditionTest, BroadcastSequential) {
|
||||||
std::string cv_name = generate_unique_cv_name("broadcast_seq");
|
std::string cv_name = generate_unique_cv_name("broadcast_seq");
|
||||||
std::string mtx_name = generate_unique_cv_name("broadcast_seq_mtx");
|
std::string mtx_name = generate_unique_cv_name("broadcast_seq_mtx");
|
||||||
|
|
||||||
condition cv(cv_name.c_str());
|
condition cv(cv_name.c_str());
|
||||||
mutex mtx(mtx_name.c_str());
|
mutex mtx(mtx_name.c_str());
|
||||||
|
|
||||||
ASSERT_TRUE(cv.valid());
|
ASSERT_TRUE(cv.valid());
|
||||||
ASSERT_TRUE(mtx.valid());
|
ASSERT_TRUE(mtx.valid());
|
||||||
|
|
||||||
std::atomic<int> processed{0};
|
std::atomic<int> processed{0};
|
||||||
const int num_threads = 4;
|
const int num_threads = 4;
|
||||||
|
|
||||||
std::vector<std::thread> threads;
|
std::vector<std::thread> threads;
|
||||||
for (int i = 0; i < num_threads; ++i) {
|
for (int i = 0; i < num_threads; ++i) {
|
||||||
threads.emplace_back([&]() {
|
threads.emplace_back([&]() {
|
||||||
mtx.lock();
|
mtx.lock();
|
||||||
cv.wait(mtx, 2000);
|
cv.wait(mtx, 2000);
|
||||||
++processed;
|
++processed;
|
||||||
mtx.unlock();
|
mtx.unlock();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||||
|
|
||||||
mtx.lock();
|
mtx.lock();
|
||||||
cv.broadcast(mtx);
|
cv.broadcast(mtx);
|
||||||
mtx.unlock();
|
mtx.unlock();
|
||||||
|
|
||||||
for (auto& t : threads) {
|
for (auto& t : threads) {
|
||||||
t.join();
|
t.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
EXPECT_EQ(processed.load(), num_threads);
|
EXPECT_EQ(processed.load(), num_threads);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test operations after clear
|
// Test operations after clear
|
||||||
TEST_F(ConditionTest, AfterClear) {
|
TEST_F(ConditionTest, AfterClear) {
|
||||||
std::string cv_name = generate_unique_cv_name("after_clear");
|
std::string cv_name = generate_unique_cv_name("after_clear");
|
||||||
std::string mtx_name = generate_unique_cv_name("after_clear_mtx");
|
std::string mtx_name = generate_unique_cv_name("after_clear_mtx");
|
||||||
|
|
||||||
condition cv(cv_name.c_str());
|
condition cv(cv_name.c_str());
|
||||||
mutex mtx(mtx_name.c_str());
|
mutex mtx(mtx_name.c_str());
|
||||||
|
|
||||||
ASSERT_TRUE(cv.valid());
|
ASSERT_TRUE(cv.valid());
|
||||||
|
|
||||||
cv.clear();
|
cv.clear();
|
||||||
EXPECT_FALSE(cv.valid());
|
EXPECT_FALSE(cv.valid());
|
||||||
|
|
||||||
// Operations after clear should fail gracefully
|
// Operations after clear should fail gracefully
|
||||||
mtx.lock();
|
mtx.lock();
|
||||||
EXPECT_FALSE(cv.wait(mtx, 10));
|
EXPECT_FALSE(cv.wait(mtx, 10));
|
||||||
EXPECT_FALSE(cv.notify(mtx));
|
EXPECT_FALSE(cv.notify(mtx));
|
||||||
EXPECT_FALSE(cv.broadcast(mtx));
|
EXPECT_FALSE(cv.broadcast(mtx));
|
||||||
mtx.unlock();
|
mtx.unlock();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -30,25 +30,25 @@ using namespace ipc;
|
|||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
std::string generate_unique_ipc_name(const char* prefix) {
|
std::string generate_unique_ipc_name(const char* prefix) {
|
||||||
static int counter = 0;
|
static int counter = 0;
|
||||||
return std::string(prefix) + "_ipc_" + std::to_string(++counter);
|
return std::string(prefix) + "_ipc_" + std::to_string(++counter);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper to create a test buffer with data
|
// Helper to create a test buffer with data
|
||||||
buffer make_test_buffer(const std::string& data) {
|
buffer make_test_buffer(const std::string& data) {
|
||||||
char* mem = new char[data.size() + 1];
|
char* mem = new char[data.size() + 1];
|
||||||
std::strcpy(mem, data.c_str());
|
std::strcpy(mem, data.c_str());
|
||||||
return buffer(mem, data.size() + 1, [](void* p, std::size_t) {
|
return buffer(mem, data.size() + 1, [](void* p, std::size_t) {
|
||||||
delete[] static_cast<char*>(p);
|
delete[] static_cast<char*>(p);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper to check buffer content
|
// Helper to check buffer content
|
||||||
bool check_buffer_content(const buffer& buf, const std::string& expected) {
|
bool check_buffer_content(const buffer& buf, const std::string& expected) {
|
||||||
if (buf.empty() || buf.size() != expected.size() + 1) {
|
if (buf.empty() || buf.size() != expected.size() + 1) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return std::strcmp(static_cast<const char*>(buf.data()), expected.c_str()) == 0;
|
return std::strcmp(static_cast<const char*>(buf.data()), expected.c_str()) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // anonymous namespace
|
} // anonymous namespace
|
||||||
@ -57,552 +57,552 @@ bool check_buffer_content(const buffer& buf, const std::string& expected) {
|
|||||||
|
|
||||||
class RouteTest : public ::testing::Test {
|
class RouteTest : public ::testing::Test {
|
||||||
protected:
|
protected:
|
||||||
void TearDown() override {
|
void TearDown() override {
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Test default construction
|
// Test default construction
|
||||||
TEST_F(RouteTest, DefaultConstruction) {
|
TEST_F(RouteTest, DefaultConstruction) {
|
||||||
route r;
|
route r;
|
||||||
EXPECT_FALSE(r.valid());
|
EXPECT_FALSE(r.valid());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test construction with name
|
// Test construction with name
|
||||||
TEST_F(RouteTest, ConstructionWithName) {
|
TEST_F(RouteTest, ConstructionWithName) {
|
||||||
std::string name = generate_unique_ipc_name("route_ctor");
|
std::string name = generate_unique_ipc_name("route_ctor");
|
||||||
|
|
||||||
route r(name.c_str(), sender);
|
route r(name.c_str(), sender);
|
||||||
EXPECT_TRUE(r.valid());
|
EXPECT_TRUE(r.valid());
|
||||||
EXPECT_STREQ(r.name(), name.c_str());
|
EXPECT_STREQ(r.name(), name.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test construction with prefix
|
// Test construction with prefix
|
||||||
TEST_F(RouteTest, ConstructionWithPrefix) {
|
TEST_F(RouteTest, ConstructionWithPrefix) {
|
||||||
std::string name = generate_unique_ipc_name("route_prefix");
|
std::string name = generate_unique_ipc_name("route_prefix");
|
||||||
|
|
||||||
route r(prefix{"my_prefix"}, name.c_str(), sender);
|
route r(prefix{"my_prefix"}, name.c_str(), sender);
|
||||||
EXPECT_TRUE(r.valid());
|
EXPECT_TRUE(r.valid());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test move constructor
|
// Test move constructor
|
||||||
TEST_F(RouteTest, MoveConstructor) {
|
TEST_F(RouteTest, MoveConstructor) {
|
||||||
std::string name = generate_unique_ipc_name("route_move");
|
std::string name = generate_unique_ipc_name("route_move");
|
||||||
|
|
||||||
route r1(name.c_str(), sender);
|
route r1(name.c_str(), sender);
|
||||||
ASSERT_TRUE(r1.valid());
|
ASSERT_TRUE(r1.valid());
|
||||||
|
|
||||||
const char* name_ptr = r1.name();
|
const char* name_ptr = r1.name();
|
||||||
|
|
||||||
route r2(std::move(r1));
|
route r2(std::move(r1));
|
||||||
|
|
||||||
EXPECT_TRUE(r2.valid());
|
EXPECT_TRUE(r2.valid());
|
||||||
EXPECT_STREQ(r2.name(), name_ptr);
|
EXPECT_STREQ(r2.name(), name_ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test assignment operator
|
// Test assignment operator
|
||||||
TEST_F(RouteTest, Assignment) {
|
TEST_F(RouteTest, Assignment) {
|
||||||
std::string name = generate_unique_ipc_name("route_assign");
|
std::string name = generate_unique_ipc_name("route_assign");
|
||||||
|
|
||||||
route r1(name.c_str(), sender);
|
route r1(name.c_str(), sender);
|
||||||
route r2;
|
route r2;
|
||||||
|
|
||||||
r2 = std::move(r1);
|
r2 = std::move(r1);
|
||||||
|
|
||||||
EXPECT_TRUE(r2.valid());
|
EXPECT_TRUE(r2.valid());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test connect method
|
// Test connect method
|
||||||
TEST_F(RouteTest, Connect) {
|
TEST_F(RouteTest, Connect) {
|
||||||
std::string name = generate_unique_ipc_name("route_connect");
|
std::string name = generate_unique_ipc_name("route_connect");
|
||||||
|
|
||||||
route r;
|
route r;
|
||||||
bool connected = r.connect(name.c_str(), sender);
|
bool connected = r.connect(name.c_str(), sender);
|
||||||
|
|
||||||
EXPECT_TRUE(connected);
|
EXPECT_TRUE(connected);
|
||||||
EXPECT_TRUE(r.valid());
|
EXPECT_TRUE(r.valid());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test connect with prefix
|
// Test connect with prefix
|
||||||
TEST_F(RouteTest, ConnectWithPrefix) {
|
TEST_F(RouteTest, ConnectWithPrefix) {
|
||||||
std::string name = generate_unique_ipc_name("route_connect_prefix");
|
std::string name = generate_unique_ipc_name("route_connect_prefix");
|
||||||
|
|
||||||
route r;
|
route r;
|
||||||
bool connected = r.connect(prefix{"test"}, name.c_str(), sender);
|
bool connected = r.connect(prefix{"test"}, name.c_str(), sender);
|
||||||
|
|
||||||
EXPECT_TRUE(connected);
|
EXPECT_TRUE(connected);
|
||||||
EXPECT_TRUE(r.valid());
|
EXPECT_TRUE(r.valid());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test reconnect
|
// Test reconnect
|
||||||
TEST_F(RouteTest, Reconnect) {
|
TEST_F(RouteTest, Reconnect) {
|
||||||
std::string name = generate_unique_ipc_name("route_reconnect");
|
std::string name = generate_unique_ipc_name("route_reconnect");
|
||||||
|
|
||||||
route r(name.c_str(), sender);
|
route r(name.c_str(), sender);
|
||||||
ASSERT_TRUE(r.valid());
|
ASSERT_TRUE(r.valid());
|
||||||
|
|
||||||
bool reconnected = r.reconnect(sender | receiver);
|
bool reconnected = r.reconnect(sender | receiver);
|
||||||
EXPECT_TRUE(reconnected);
|
EXPECT_TRUE(reconnected);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test disconnect
|
// Test disconnect
|
||||||
TEST_F(RouteTest, Disconnect) {
|
TEST_F(RouteTest, Disconnect) {
|
||||||
std::string name = generate_unique_ipc_name("route_disconnect");
|
std::string name = generate_unique_ipc_name("route_disconnect");
|
||||||
|
|
||||||
route r(name.c_str(), sender);
|
route r(name.c_str(), sender);
|
||||||
ASSERT_TRUE(r.valid());
|
ASSERT_TRUE(r.valid());
|
||||||
|
|
||||||
r.disconnect();
|
r.disconnect();
|
||||||
// After disconnect, behavior depends on implementation
|
// After disconnect, behavior depends on implementation
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test clone
|
// Test clone
|
||||||
TEST_F(RouteTest, Clone) {
|
TEST_F(RouteTest, Clone) {
|
||||||
std::string name = generate_unique_ipc_name("route_clone");
|
std::string name = generate_unique_ipc_name("route_clone");
|
||||||
|
|
||||||
route r1(name.c_str(), sender);
|
route r1(name.c_str(), sender);
|
||||||
ASSERT_TRUE(r1.valid());
|
ASSERT_TRUE(r1.valid());
|
||||||
|
|
||||||
route r2 = r1.clone();
|
route r2 = r1.clone();
|
||||||
|
|
||||||
EXPECT_TRUE(r2.valid());
|
EXPECT_TRUE(r2.valid());
|
||||||
EXPECT_STREQ(r1.name(), r2.name());
|
EXPECT_STREQ(r1.name(), r2.name());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test mode accessor
|
// Test mode accessor
|
||||||
TEST_F(RouteTest, Mode) {
|
TEST_F(RouteTest, Mode) {
|
||||||
std::string name = generate_unique_ipc_name("route_mode");
|
std::string name = generate_unique_ipc_name("route_mode");
|
||||||
|
|
||||||
route r(name.c_str(), sender);
|
route r(name.c_str(), sender);
|
||||||
EXPECT_EQ(r.mode(), sender);
|
EXPECT_EQ(r.mode(), sender);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test release
|
// Test release
|
||||||
TEST_F(RouteTest, Release) {
|
TEST_F(RouteTest, Release) {
|
||||||
std::string name = generate_unique_ipc_name("route_release");
|
std::string name = generate_unique_ipc_name("route_release");
|
||||||
|
|
||||||
route r(name.c_str(), sender);
|
route r(name.c_str(), sender);
|
||||||
ASSERT_TRUE(r.valid());
|
ASSERT_TRUE(r.valid());
|
||||||
|
|
||||||
r.release();
|
r.release();
|
||||||
EXPECT_FALSE(r.valid());
|
EXPECT_FALSE(r.valid());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test clear
|
// Test clear
|
||||||
TEST_F(RouteTest, Clear) {
|
TEST_F(RouteTest, Clear) {
|
||||||
std::string name = generate_unique_ipc_name("route_clear");
|
std::string name = generate_unique_ipc_name("route_clear");
|
||||||
|
|
||||||
route r(name.c_str(), sender);
|
route r(name.c_str(), sender);
|
||||||
ASSERT_TRUE(r.valid());
|
ASSERT_TRUE(r.valid());
|
||||||
|
|
||||||
r.clear();
|
r.clear();
|
||||||
EXPECT_FALSE(r.valid());
|
EXPECT_FALSE(r.valid());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test clear_storage static method
|
// Test clear_storage static method
|
||||||
TEST_F(RouteTest, ClearStorage) {
|
TEST_F(RouteTest, ClearStorage) {
|
||||||
std::string name = generate_unique_ipc_name("route_clear_storage");
|
std::string name = generate_unique_ipc_name("route_clear_storage");
|
||||||
|
|
||||||
{
|
{
|
||||||
route r(name.c_str(), sender);
|
route r(name.c_str(), sender);
|
||||||
EXPECT_TRUE(r.valid());
|
EXPECT_TRUE(r.valid());
|
||||||
}
|
}
|
||||||
|
|
||||||
route::clear_storage(name.c_str());
|
route::clear_storage(name.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test clear_storage with prefix
|
// Test clear_storage with prefix
|
||||||
TEST_F(RouteTest, ClearStorageWithPrefix) {
|
TEST_F(RouteTest, ClearStorageWithPrefix) {
|
||||||
std::string name = generate_unique_ipc_name("route_clear_prefix");
|
std::string name = generate_unique_ipc_name("route_clear_prefix");
|
||||||
|
|
||||||
{
|
{
|
||||||
route r(prefix{"test"}, name.c_str(), sender);
|
route r(prefix{"test"}, name.c_str(), sender);
|
||||||
EXPECT_TRUE(r.valid());
|
EXPECT_TRUE(r.valid());
|
||||||
}
|
}
|
||||||
|
|
||||||
route::clear_storage(prefix{"test"}, name.c_str());
|
route::clear_storage(prefix{"test"}, name.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test send without receiver (should fail)
|
// Test send without receiver (should fail)
|
||||||
TEST_F(RouteTest, SendWithoutReceiver) {
|
TEST_F(RouteTest, SendWithoutReceiver) {
|
||||||
std::string name = generate_unique_ipc_name("route_send_no_recv");
|
std::string name = generate_unique_ipc_name("route_send_no_recv");
|
||||||
|
|
||||||
route r(name.c_str(), sender);
|
route r(name.c_str(), sender);
|
||||||
ASSERT_TRUE(r.valid());
|
ASSERT_TRUE(r.valid());
|
||||||
|
|
||||||
buffer buf = make_test_buffer("test");
|
buffer buf = make_test_buffer("test");
|
||||||
bool sent = r.send(buf, 10); // 10ms timeout
|
bool sent = r.send(buf, 10); // 10ms timeout
|
||||||
|
|
||||||
EXPECT_FALSE(sent); // Should fail - no receiver
|
EXPECT_FALSE(sent); // Should fail - no receiver
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test try_send without receiver
|
// Test try_send without receiver
|
||||||
TEST_F(RouteTest, TrySendWithoutReceiver) {
|
TEST_F(RouteTest, TrySendWithoutReceiver) {
|
||||||
std::string name = generate_unique_ipc_name("route_try_send_no_recv");
|
std::string name = generate_unique_ipc_name("route_try_send_no_recv");
|
||||||
|
|
||||||
route r(name.c_str(), sender);
|
route r(name.c_str(), sender);
|
||||||
ASSERT_TRUE(r.valid());
|
ASSERT_TRUE(r.valid());
|
||||||
|
|
||||||
buffer buf = make_test_buffer("test");
|
buffer buf = make_test_buffer("test");
|
||||||
bool sent = r.try_send(buf, 10);
|
bool sent = r.try_send(buf, 10);
|
||||||
|
|
||||||
EXPECT_FALSE(sent);
|
EXPECT_FALSE(sent);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test send and receive with buffer
|
// Test send and receive with buffer
|
||||||
TEST_F(RouteTest, SendReceiveBuffer) {
|
TEST_F(RouteTest, SendReceiveBuffer) {
|
||||||
std::string name = generate_unique_ipc_name("route_send_recv_buf");
|
std::string name = generate_unique_ipc_name("route_send_recv_buf");
|
||||||
|
|
||||||
route sender_r(name.c_str(), sender);
|
route sender_r(name.c_str(), sender);
|
||||||
route receiver_r(name.c_str(), receiver);
|
route receiver_r(name.c_str(), receiver);
|
||||||
|
|
||||||
ASSERT_TRUE(sender_r.valid());
|
ASSERT_TRUE(sender_r.valid());
|
||||||
ASSERT_TRUE(receiver_r.valid());
|
ASSERT_TRUE(receiver_r.valid());
|
||||||
|
|
||||||
buffer send_buf = make_test_buffer("Hello Route");
|
buffer send_buf = make_test_buffer("Hello Route");
|
||||||
|
|
||||||
std::thread sender_thread([&]() {
|
std::thread sender_thread([&]() {
|
||||||
bool sent = sender_r.send(send_buf);
|
bool sent = sender_r.send(send_buf);
|
||||||
EXPECT_TRUE(sent);
|
EXPECT_TRUE(sent);
|
||||||
});
|
});
|
||||||
|
|
||||||
std::thread receiver_thread([&]() {
|
std::thread receiver_thread([&]() {
|
||||||
buffer recv_buf = receiver_r.recv();
|
buffer recv_buf = receiver_r.recv();
|
||||||
EXPECT_TRUE(check_buffer_content(recv_buf, "Hello Route"));
|
EXPECT_TRUE(check_buffer_content(recv_buf, "Hello Route"));
|
||||||
});
|
});
|
||||||
|
|
||||||
sender_thread.join();
|
sender_thread.join();
|
||||||
receiver_thread.join();
|
receiver_thread.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test send and receive with string
|
// Test send and receive with string
|
||||||
TEST_F(RouteTest, SendReceiveString) {
|
TEST_F(RouteTest, SendReceiveString) {
|
||||||
std::string name = generate_unique_ipc_name("route_send_recv_str");
|
std::string name = generate_unique_ipc_name("route_send_recv_str");
|
||||||
|
|
||||||
route sender_r(name.c_str(), sender);
|
route sender_r(name.c_str(), sender);
|
||||||
route receiver_r(name.c_str(), receiver);
|
route receiver_r(name.c_str(), receiver);
|
||||||
|
|
||||||
ASSERT_TRUE(sender_r.valid());
|
ASSERT_TRUE(sender_r.valid());
|
||||||
ASSERT_TRUE(receiver_r.valid());
|
ASSERT_TRUE(receiver_r.valid());
|
||||||
|
|
||||||
std::string test_str = "Test String";
|
std::string test_str = "Test String";
|
||||||
|
|
||||||
std::thread sender_thread([&]() {
|
std::thread sender_thread([&]() {
|
||||||
bool sent = sender_r.send(test_str);
|
bool sent = sender_r.send(test_str);
|
||||||
EXPECT_TRUE(sent);
|
EXPECT_TRUE(sent);
|
||||||
});
|
});
|
||||||
|
|
||||||
std::thread receiver_thread([&]() {
|
std::thread receiver_thread([&]() {
|
||||||
buffer recv_buf = receiver_r.recv();
|
buffer recv_buf = receiver_r.recv();
|
||||||
EXPECT_TRUE(check_buffer_content(recv_buf, test_str));
|
EXPECT_TRUE(check_buffer_content(recv_buf, test_str));
|
||||||
});
|
});
|
||||||
|
|
||||||
sender_thread.join();
|
sender_thread.join();
|
||||||
receiver_thread.join();
|
receiver_thread.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test send and receive with raw data
|
// Test send and receive with raw data
|
||||||
TEST_F(RouteTest, SendReceiveRawData) {
|
TEST_F(RouteTest, SendReceiveRawData) {
|
||||||
std::string name = generate_unique_ipc_name("route_send_recv_raw");
|
std::string name = generate_unique_ipc_name("route_send_recv_raw");
|
||||||
|
|
||||||
route sender_r(name.c_str(), sender);
|
route sender_r(name.c_str(), sender);
|
||||||
route receiver_r(name.c_str(), receiver);
|
route receiver_r(name.c_str(), receiver);
|
||||||
|
|
||||||
ASSERT_TRUE(sender_r.valid());
|
ASSERT_TRUE(sender_r.valid());
|
||||||
ASSERT_TRUE(receiver_r.valid());
|
ASSERT_TRUE(receiver_r.valid());
|
||||||
|
|
||||||
const char* data = "Raw Data Test";
|
const char* data = "Raw Data Test";
|
||||||
std::size_t size = std::strlen(data) + 1;
|
std::size_t size = std::strlen(data) + 1;
|
||||||
|
|
||||||
std::thread sender_thread([&]() {
|
std::thread sender_thread([&]() {
|
||||||
bool sent = sender_r.send(data, size);
|
bool sent = sender_r.send(data, size);
|
||||||
EXPECT_TRUE(sent);
|
EXPECT_TRUE(sent);
|
||||||
});
|
});
|
||||||
|
|
||||||
std::thread receiver_thread([&]() {
|
std::thread receiver_thread([&]() {
|
||||||
buffer recv_buf = receiver_r.recv();
|
buffer recv_buf = receiver_r.recv();
|
||||||
EXPECT_EQ(recv_buf.size(), size);
|
EXPECT_EQ(recv_buf.size(), size);
|
||||||
EXPECT_STREQ(static_cast<const char*>(recv_buf.data()), data);
|
EXPECT_STREQ(static_cast<const char*>(recv_buf.data()), data);
|
||||||
});
|
});
|
||||||
|
|
||||||
sender_thread.join();
|
sender_thread.join();
|
||||||
receiver_thread.join();
|
receiver_thread.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test try_recv when empty
|
// Test try_recv when empty
|
||||||
TEST_F(RouteTest, TryRecvEmpty) {
|
TEST_F(RouteTest, TryRecvEmpty) {
|
||||||
std::string name = generate_unique_ipc_name("route_try_recv_empty");
|
std::string name = generate_unique_ipc_name("route_try_recv_empty");
|
||||||
|
|
||||||
route r(name.c_str(), receiver);
|
route r(name.c_str(), receiver);
|
||||||
ASSERT_TRUE(r.valid());
|
ASSERT_TRUE(r.valid());
|
||||||
|
|
||||||
buffer buf = r.try_recv();
|
buffer buf = r.try_recv();
|
||||||
EXPECT_TRUE(buf.empty());
|
EXPECT_TRUE(buf.empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test recv_count
|
// Test recv_count
|
||||||
TEST_F(RouteTest, RecvCount) {
|
TEST_F(RouteTest, RecvCount) {
|
||||||
std::string name = generate_unique_ipc_name("route_recv_count");
|
std::string name = generate_unique_ipc_name("route_recv_count");
|
||||||
|
|
||||||
route sender_r(name.c_str(), sender);
|
route sender_r(name.c_str(), sender);
|
||||||
route receiver_r(name.c_str(), receiver);
|
route receiver_r(name.c_str(), receiver);
|
||||||
|
|
||||||
ASSERT_TRUE(sender_r.valid());
|
ASSERT_TRUE(sender_r.valid());
|
||||||
ASSERT_TRUE(receiver_r.valid());
|
ASSERT_TRUE(receiver_r.valid());
|
||||||
|
|
||||||
std::size_t count = sender_r.recv_count();
|
std::size_t count = sender_r.recv_count();
|
||||||
EXPECT_GE(count, 0u);
|
EXPECT_GE(count, 0u);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test wait_for_recv
|
// Test wait_for_recv
|
||||||
TEST_F(RouteTest, WaitForRecv) {
|
TEST_F(RouteTest, WaitForRecv) {
|
||||||
std::string name = generate_unique_ipc_name("route_wait_recv");
|
std::string name = generate_unique_ipc_name("route_wait_recv");
|
||||||
|
|
||||||
route sender_r(name.c_str(), sender);
|
route sender_r(name.c_str(), sender);
|
||||||
|
|
||||||
std::thread receiver_thread([&]() {
|
std::thread receiver_thread([&]() {
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||||
route receiver_r(name.c_str(), receiver);
|
route receiver_r(name.c_str(), receiver);
|
||||||
});
|
});
|
||||||
|
|
||||||
bool waited = sender_r.wait_for_recv(1, 500);
|
bool waited = sender_r.wait_for_recv(1, 500);
|
||||||
|
|
||||||
receiver_thread.join();
|
receiver_thread.join();
|
||||||
|
|
||||||
// Result depends on timing
|
// Result depends on timing
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test static wait_for_recv
|
// Test static wait_for_recv
|
||||||
TEST_F(RouteTest, StaticWaitForRecv) {
|
TEST_F(RouteTest, StaticWaitForRecv) {
|
||||||
std::string name = generate_unique_ipc_name("route_static_wait");
|
std::string name = generate_unique_ipc_name("route_static_wait");
|
||||||
|
|
||||||
std::thread receiver_thread([&]() {
|
std::thread receiver_thread([&]() {
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||||
route receiver_r(name.c_str(), receiver);
|
route receiver_r(name.c_str(), receiver);
|
||||||
});
|
});
|
||||||
|
|
||||||
bool waited = route::wait_for_recv(name.c_str(), 1, 500);
|
bool waited = route::wait_for_recv(name.c_str(), 1, 500);
|
||||||
|
|
||||||
receiver_thread.join();
|
receiver_thread.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test one sender, multiple receivers
|
// Test one sender, multiple receivers
|
||||||
TEST_F(RouteTest, OneSenderMultipleReceivers) {
|
TEST_F(RouteTest, OneSenderMultipleReceivers) {
|
||||||
std::string name = generate_unique_ipc_name("route_1_to_n");
|
std::string name = generate_unique_ipc_name("route_1_to_n");
|
||||||
|
|
||||||
route sender_r(name.c_str(), sender);
|
route sender_r(name.c_str(), sender);
|
||||||
ASSERT_TRUE(sender_r.valid());
|
ASSERT_TRUE(sender_r.valid());
|
||||||
|
|
||||||
const int num_receivers = 3;
|
const int num_receivers = 3;
|
||||||
std::vector<std::atomic<bool>> received(num_receivers);
|
std::vector<std::atomic<bool>> received(num_receivers);
|
||||||
for (auto& r : received) r.store(false);
|
for (auto& r : received) r.store(false);
|
||||||
|
|
||||||
std::vector<std::thread> receivers;
|
std::vector<std::thread> receivers;
|
||||||
for (int i = 0; i < num_receivers; ++i) {
|
for (int i = 0; i < num_receivers; ++i) {
|
||||||
receivers.emplace_back([&, i]() {
|
receivers.emplace_back([&, i]() {
|
||||||
route receiver_r(name.c_str(), receiver);
|
route receiver_r(name.c_str(), receiver);
|
||||||
buffer buf = receiver_r.recv(1000);
|
buffer buf = receiver_r.recv(1000);
|
||||||
if (check_buffer_content(buf, "Broadcast")) {
|
if (check_buffer_content(buf, "Broadcast")) {
|
||||||
received[i].store(true);
|
received[i].store(true);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||||
|
|
||||||
sender_r.send(std::string("Broadcast"));
|
sender_r.send(std::string("Broadcast"));
|
||||||
|
|
||||||
for (auto& t : receivers) {
|
for (auto& t : receivers) {
|
||||||
t.join();
|
t.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
// All receivers should receive the message (broadcast)
|
// All receivers should receive the message (broadcast)
|
||||||
for (const auto& r : received) {
|
for (const auto& r : received) {
|
||||||
EXPECT_TRUE(r.load());
|
EXPECT_TRUE(r.load());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ========== Channel Tests (Multiple Producer, Multiple Consumer) ==========
|
// ========== Channel Tests (Multiple Producer, Multiple Consumer) ==========
|
||||||
|
|
||||||
class ChannelTest : public ::testing::Test {
|
class ChannelTest : public ::testing::Test {
|
||||||
protected:
|
protected:
|
||||||
void TearDown() override {
|
void TearDown() override {
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Test default construction
|
// Test default construction
|
||||||
TEST_F(ChannelTest, DefaultConstruction) {
|
TEST_F(ChannelTest, DefaultConstruction) {
|
||||||
channel ch;
|
channel ch;
|
||||||
EXPECT_FALSE(ch.valid());
|
EXPECT_FALSE(ch.valid());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test construction with name
|
// Test construction with name
|
||||||
TEST_F(ChannelTest, ConstructionWithName) {
|
TEST_F(ChannelTest, ConstructionWithName) {
|
||||||
std::string name = generate_unique_ipc_name("channel_ctor");
|
std::string name = generate_unique_ipc_name("channel_ctor");
|
||||||
|
|
||||||
channel ch(name.c_str(), sender);
|
channel ch(name.c_str(), sender);
|
||||||
EXPECT_TRUE(ch.valid());
|
EXPECT_TRUE(ch.valid());
|
||||||
EXPECT_STREQ(ch.name(), name.c_str());
|
EXPECT_STREQ(ch.name(), name.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test send and receive
|
// Test send and receive
|
||||||
TEST_F(ChannelTest, SendReceive) {
|
TEST_F(ChannelTest, SendReceive) {
|
||||||
std::string name = generate_unique_ipc_name("channel_send_recv");
|
std::string name = generate_unique_ipc_name("channel_send_recv");
|
||||||
|
|
||||||
channel sender_ch(name.c_str(), sender);
|
channel sender_ch(name.c_str(), sender);
|
||||||
channel receiver_ch(name.c_str(), receiver);
|
channel receiver_ch(name.c_str(), receiver);
|
||||||
|
|
||||||
ASSERT_TRUE(sender_ch.valid());
|
ASSERT_TRUE(sender_ch.valid());
|
||||||
ASSERT_TRUE(receiver_ch.valid());
|
ASSERT_TRUE(receiver_ch.valid());
|
||||||
|
|
||||||
std::thread sender_thread([&]() {
|
std::thread sender_thread([&]() {
|
||||||
sender_ch.send(std::string("Channel Test"));
|
sender_ch.send(std::string("Channel Test"));
|
||||||
});
|
});
|
||||||
|
|
||||||
std::thread receiver_thread([&]() {
|
std::thread receiver_thread([&]() {
|
||||||
buffer buf = receiver_ch.recv();
|
buffer buf = receiver_ch.recv();
|
||||||
EXPECT_TRUE(check_buffer_content(buf, "Channel Test"));
|
EXPECT_TRUE(check_buffer_content(buf, "Channel Test"));
|
||||||
});
|
});
|
||||||
|
|
||||||
sender_thread.join();
|
sender_thread.join();
|
||||||
receiver_thread.join();
|
receiver_thread.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test multiple senders
|
// Test multiple senders
|
||||||
TEST_F(ChannelTest, MultipleSenders) {
|
TEST_F(ChannelTest, MultipleSenders) {
|
||||||
std::string name = generate_unique_ipc_name("channel_multi_send");
|
std::string name = generate_unique_ipc_name("channel_multi_send");
|
||||||
|
|
||||||
channel receiver_ch(name.c_str(), receiver);
|
channel receiver_ch(name.c_str(), receiver);
|
||||||
ASSERT_TRUE(receiver_ch.valid());
|
ASSERT_TRUE(receiver_ch.valid());
|
||||||
|
|
||||||
const int num_senders = 3;
|
const int num_senders = 3;
|
||||||
std::atomic<int> received_count{0};
|
std::atomic<int> received_count{0};
|
||||||
|
|
||||||
std::vector<std::thread> senders;
|
std::vector<std::thread> senders;
|
||||||
for (int i = 0; i < num_senders; ++i) {
|
for (int i = 0; i < num_senders; ++i) {
|
||||||
senders.emplace_back([&, i]() {
|
senders.emplace_back([&, i]() {
|
||||||
channel sender_ch(name.c_str(), sender);
|
channel sender_ch(name.c_str(), sender);
|
||||||
std::string msg = "Sender" + std::to_string(i);
|
std::string msg = "Sender" + std::to_string(i);
|
||||||
sender_ch.send(msg);
|
sender_ch.send(msg);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
std::thread receiver([&]() {
|
std::thread receiver([&]() {
|
||||||
for (int i = 0; i < num_senders; ++i) {
|
for (int i = 0; i < num_senders; ++i) {
|
||||||
buffer buf = receiver_ch.recv(1000);
|
buffer buf = receiver_ch.recv(1000);
|
||||||
if (!buf.empty()) {
|
if (!buf.empty()) {
|
||||||
++received_count;
|
++received_count;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
for (auto& t : senders) {
|
for (auto& t : senders) {
|
||||||
t.join();
|
t.join();
|
||||||
}
|
}
|
||||||
receiver.join();
|
receiver.join();
|
||||||
|
|
||||||
EXPECT_EQ(received_count.load(), num_senders);
|
EXPECT_EQ(received_count.load(), num_senders);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test multiple senders and receivers
|
// Test multiple senders and receivers
|
||||||
TEST_F(ChannelTest, MultipleSendersReceivers) {
|
TEST_F(ChannelTest, MultipleSendersReceivers) {
|
||||||
std::string name = generate_unique_ipc_name("channel_m_to_n");
|
std::string name = generate_unique_ipc_name("channel_m_to_n");
|
||||||
|
|
||||||
const int num_senders = 2;
|
const int num_senders = 2;
|
||||||
const int num_receivers = 2;
|
const int num_receivers = 2;
|
||||||
const int messages_per_sender = 5;
|
const int messages_per_sender = 5;
|
||||||
|
|
||||||
std::atomic<int> sent_count{0};
|
std::atomic<int> sent_count{0};
|
||||||
std::atomic<int> received_count{0};
|
std::atomic<int> received_count{0};
|
||||||
|
|
||||||
std::vector<std::thread> senders;
|
std::vector<std::thread> senders;
|
||||||
for (int i = 0; i < num_senders; ++i) {
|
for (int i = 0; i < num_senders; ++i) {
|
||||||
senders.emplace_back([&, i]() {
|
senders.emplace_back([&, i]() {
|
||||||
channel ch(name.c_str(), sender);
|
channel ch(name.c_str(), sender);
|
||||||
for (int j = 0; j < messages_per_sender; ++j) {
|
for (int j = 0; j < messages_per_sender; ++j) {
|
||||||
std::string msg = "S" + std::to_string(i) + "M" + std::to_string(j);
|
std::string msg = "S" + std::to_string(i) + "M" + std::to_string(j);
|
||||||
if (ch.send(msg, 1000)) {
|
if (ch.send(msg, 1000)) {
|
||||||
++sent_count;
|
++sent_count;
|
||||||
}
|
}
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::thread> receivers;
|
std::vector<std::thread> receivers;
|
||||||
for (int i = 0; i < num_receivers; ++i) {
|
for (int i = 0; i < num_receivers; ++i) {
|
||||||
receivers.emplace_back([&, i]() {
|
receivers.emplace_back([&, i]() {
|
||||||
channel ch(name.c_str(), receiver);
|
channel ch(name.c_str(), receiver);
|
||||||
for (int j = 0; j < messages_per_sender; ++j) {
|
for (int j = 0; j < messages_per_sender; ++j) {
|
||||||
buffer buf = ch.recv(2000);
|
buffer buf = ch.recv(2000);
|
||||||
if (!buf.empty()) {
|
if (!buf.empty()) {
|
||||||
++received_count;
|
++received_count;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto& t : senders) {
|
for (auto& t : senders) {
|
||||||
t.join();
|
t.join();
|
||||||
}
|
}
|
||||||
for (auto& t : receivers) {
|
for (auto& t : receivers) {
|
||||||
t.join();
|
t.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
EXPECT_EQ(sent_count.load(), num_senders * messages_per_sender);
|
EXPECT_EQ(sent_count.load(), num_senders * messages_per_sender);
|
||||||
// All messages should be received (broadcast mode)
|
// All messages should be received (broadcast mode)
|
||||||
EXPECT_EQ(received_count.load(), num_senders * messages_per_sender * num_receivers);
|
EXPECT_EQ(received_count.load(), num_senders * messages_per_sender * num_receivers);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test try_send and try_recv
|
// Test try_send and try_recv
|
||||||
TEST_F(ChannelTest, TrySendTryRecv) {
|
TEST_F(ChannelTest, TrySendTryRecv) {
|
||||||
std::string name = generate_unique_ipc_name("channel_try");
|
std::string name = generate_unique_ipc_name("channel_try");
|
||||||
|
|
||||||
channel sender_ch(name.c_str(), sender);
|
channel sender_ch(name.c_str(), sender);
|
||||||
channel receiver_ch(name.c_str(), receiver);
|
channel receiver_ch(name.c_str(), receiver);
|
||||||
|
|
||||||
ASSERT_TRUE(sender_ch.valid());
|
ASSERT_TRUE(sender_ch.valid());
|
||||||
ASSERT_TRUE(receiver_ch.valid());
|
ASSERT_TRUE(receiver_ch.valid());
|
||||||
|
|
||||||
bool sent = sender_ch.try_send(std::string("Try Test"));
|
bool sent = sender_ch.try_send(std::string("Try Test"));
|
||||||
|
|
||||||
if (sent) {
|
if (sent) {
|
||||||
buffer buf = receiver_ch.try_recv();
|
buffer buf = receiver_ch.try_recv();
|
||||||
EXPECT_FALSE(buf.empty());
|
EXPECT_FALSE(buf.empty());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test timeout scenarios
|
// Test timeout scenarios
|
||||||
TEST_F(ChannelTest, SendTimeout) {
|
TEST_F(ChannelTest, SendTimeout) {
|
||||||
std::string name = generate_unique_ipc_name("channel_timeout");
|
std::string name = generate_unique_ipc_name("channel_timeout");
|
||||||
|
|
||||||
channel ch(name.c_str(), sender);
|
channel ch(name.c_str(), sender);
|
||||||
ASSERT_TRUE(ch.valid());
|
ASSERT_TRUE(ch.valid());
|
||||||
|
|
||||||
// Send with very short timeout (may fail without receiver)
|
// Send with very short timeout (may fail without receiver)
|
||||||
bool sent = ch.send(std::string("Timeout Test"), 1);
|
bool sent = ch.send(std::string("Timeout Test"), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test clear and clear_storage
|
// Test clear and clear_storage
|
||||||
TEST_F(ChannelTest, ClearStorage) {
|
TEST_F(ChannelTest, ClearStorage) {
|
||||||
std::string name = generate_unique_ipc_name("channel_clear");
|
std::string name = generate_unique_ipc_name("channel_clear");
|
||||||
|
|
||||||
{
|
{
|
||||||
channel ch(name.c_str(), sender);
|
channel ch(name.c_str(), sender);
|
||||||
EXPECT_TRUE(ch.valid());
|
EXPECT_TRUE(ch.valid());
|
||||||
}
|
}
|
||||||
|
|
||||||
channel::clear_storage(name.c_str());
|
channel::clear_storage(name.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test handle() method
|
// Test handle() method
|
||||||
TEST_F(ChannelTest, Handle) {
|
TEST_F(ChannelTest, Handle) {
|
||||||
std::string name = generate_unique_ipc_name("channel_handle");
|
std::string name = generate_unique_ipc_name("channel_handle");
|
||||||
|
|
||||||
channel ch(name.c_str(), sender);
|
channel ch(name.c_str(), sender);
|
||||||
ASSERT_TRUE(ch.valid());
|
ASSERT_TRUE(ch.valid());
|
||||||
|
|
||||||
handle_t h = ch.handle();
|
handle_t h = ch.handle();
|
||||||
EXPECT_NE(h, nullptr);
|
EXPECT_NE(h, nullptr);
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -28,474 +28,474 @@ namespace {
|
|||||||
|
|
||||||
// Generate unique mutex names for tests
|
// Generate unique mutex names for tests
|
||||||
std::string generate_unique_mutex_name(const char* prefix) {
|
std::string generate_unique_mutex_name(const char* prefix) {
|
||||||
static int counter = 0;
|
static int counter = 0;
|
||||||
return std::string(prefix) + "_mutex_" + std::to_string(++counter);
|
return std::string(prefix) + "_mutex_" + std::to_string(++counter);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // anonymous namespace
|
} // anonymous namespace
|
||||||
|
|
||||||
class MutexTest : public ::testing::Test {
|
class MutexTest : public ::testing::Test {
|
||||||
protected:
|
protected:
|
||||||
void TearDown() override {
|
void TearDown() override {
|
||||||
// Allow time for cleanup
|
// Allow time for cleanup
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Test default constructor
|
// Test default constructor
|
||||||
TEST_F(MutexTest, DefaultConstructor) {
|
TEST_F(MutexTest, DefaultConstructor) {
|
||||||
mutex mtx;
|
mutex mtx;
|
||||||
// Default constructed mutex may or may not be valid depending on implementation
|
// Default constructed mutex may or may not be valid depending on implementation
|
||||||
// Just ensure it doesn't crash
|
// Just ensure it doesn't crash
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test named constructor
|
// Test named constructor
|
||||||
TEST_F(MutexTest, NamedConstructor) {
|
TEST_F(MutexTest, NamedConstructor) {
|
||||||
std::string name = generate_unique_mutex_name("named_ctor");
|
std::string name = generate_unique_mutex_name("named_ctor");
|
||||||
|
|
||||||
mutex mtx(name.c_str());
|
mutex mtx(name.c_str());
|
||||||
EXPECT_TRUE(mtx.valid());
|
EXPECT_TRUE(mtx.valid());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test native() const method
|
// Test native() const method
|
||||||
TEST_F(MutexTest, NativeConst) {
|
TEST_F(MutexTest, NativeConst) {
|
||||||
std::string name = generate_unique_mutex_name("native_const");
|
std::string name = generate_unique_mutex_name("native_const");
|
||||||
|
|
||||||
const mutex mtx(name.c_str());
|
const mutex mtx(name.c_str());
|
||||||
const void* native_handle = mtx.native();
|
const void* native_handle = mtx.native();
|
||||||
|
|
||||||
EXPECT_NE(native_handle, nullptr);
|
EXPECT_NE(native_handle, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test native() non-const method
|
// Test native() non-const method
|
||||||
TEST_F(MutexTest, NativeNonConst) {
|
TEST_F(MutexTest, NativeNonConst) {
|
||||||
std::string name = generate_unique_mutex_name("native_nonconst");
|
std::string name = generate_unique_mutex_name("native_nonconst");
|
||||||
|
|
||||||
mutex mtx(name.c_str());
|
mutex mtx(name.c_str());
|
||||||
void* native_handle = mtx.native();
|
void* native_handle = mtx.native();
|
||||||
|
|
||||||
EXPECT_NE(native_handle, nullptr);
|
EXPECT_NE(native_handle, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test valid() method
|
// Test valid() method
|
||||||
TEST_F(MutexTest, Valid) {
|
TEST_F(MutexTest, Valid) {
|
||||||
mutex mtx1;
|
mutex mtx1;
|
||||||
// May or may not be valid without open
|
// May or may not be valid without open
|
||||||
|
|
||||||
std::string name = generate_unique_mutex_name("valid");
|
std::string name = generate_unique_mutex_name("valid");
|
||||||
mutex mtx2(name.c_str());
|
mutex mtx2(name.c_str());
|
||||||
EXPECT_TRUE(mtx2.valid());
|
EXPECT_TRUE(mtx2.valid());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test open() method
|
// Test open() method
|
||||||
TEST_F(MutexTest, Open) {
|
TEST_F(MutexTest, Open) {
|
||||||
std::string name = generate_unique_mutex_name("open");
|
std::string name = generate_unique_mutex_name("open");
|
||||||
|
|
||||||
mutex mtx;
|
mutex mtx;
|
||||||
bool result = mtx.open(name.c_str());
|
bool result = mtx.open(name.c_str());
|
||||||
|
|
||||||
EXPECT_TRUE(result);
|
EXPECT_TRUE(result);
|
||||||
EXPECT_TRUE(mtx.valid());
|
EXPECT_TRUE(mtx.valid());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test close() method
|
// Test close() method
|
||||||
TEST_F(MutexTest, Close) {
|
TEST_F(MutexTest, Close) {
|
||||||
std::string name = generate_unique_mutex_name("close");
|
std::string name = generate_unique_mutex_name("close");
|
||||||
|
|
||||||
mutex mtx(name.c_str());
|
mutex mtx(name.c_str());
|
||||||
ASSERT_TRUE(mtx.valid());
|
ASSERT_TRUE(mtx.valid());
|
||||||
|
|
||||||
mtx.close();
|
mtx.close();
|
||||||
EXPECT_FALSE(mtx.valid());
|
EXPECT_FALSE(mtx.valid());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test clear() method
|
// Test clear() method
|
||||||
TEST_F(MutexTest, Clear) {
|
TEST_F(MutexTest, Clear) {
|
||||||
std::string name = generate_unique_mutex_name("clear");
|
std::string name = generate_unique_mutex_name("clear");
|
||||||
|
|
||||||
mutex mtx(name.c_str());
|
mutex mtx(name.c_str());
|
||||||
ASSERT_TRUE(mtx.valid());
|
ASSERT_TRUE(mtx.valid());
|
||||||
|
|
||||||
mtx.clear();
|
mtx.clear();
|
||||||
EXPECT_FALSE(mtx.valid());
|
EXPECT_FALSE(mtx.valid());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test clear_storage() static method
|
// Test clear_storage() static method
|
||||||
TEST_F(MutexTest, ClearStorage) {
|
TEST_F(MutexTest, ClearStorage) {
|
||||||
std::string name = generate_unique_mutex_name("clear_storage");
|
std::string name = generate_unique_mutex_name("clear_storage");
|
||||||
|
|
||||||
{
|
{
|
||||||
mutex mtx(name.c_str());
|
mutex mtx(name.c_str());
|
||||||
EXPECT_TRUE(mtx.valid());
|
EXPECT_TRUE(mtx.valid());
|
||||||
}
|
}
|
||||||
|
|
||||||
mutex::clear_storage(name.c_str());
|
mutex::clear_storage(name.c_str());
|
||||||
|
|
||||||
// Try to open after clear - should create new or fail gracefully
|
// Try to open after clear - should create new or fail gracefully
|
||||||
mutex mtx2(name.c_str());
|
mutex mtx2(name.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test basic lock and unlock
|
// Test basic lock and unlock
|
||||||
TEST_F(MutexTest, LockUnlock) {
|
TEST_F(MutexTest, LockUnlock) {
|
||||||
std::string name = generate_unique_mutex_name("lock_unlock");
|
std::string name = generate_unique_mutex_name("lock_unlock");
|
||||||
|
|
||||||
mutex mtx(name.c_str());
|
mutex mtx(name.c_str());
|
||||||
ASSERT_TRUE(mtx.valid());
|
ASSERT_TRUE(mtx.valid());
|
||||||
|
|
||||||
bool locked = mtx.lock();
|
bool locked = mtx.lock();
|
||||||
EXPECT_TRUE(locked);
|
EXPECT_TRUE(locked);
|
||||||
|
|
||||||
bool unlocked = mtx.unlock();
|
bool unlocked = mtx.unlock();
|
||||||
EXPECT_TRUE(unlocked);
|
EXPECT_TRUE(unlocked);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test try_lock
|
// Test try_lock
|
||||||
TEST_F(MutexTest, TryLock) {
|
TEST_F(MutexTest, TryLock) {
|
||||||
std::string name = generate_unique_mutex_name("try_lock");
|
std::string name = generate_unique_mutex_name("try_lock");
|
||||||
|
|
||||||
mutex mtx(name.c_str());
|
mutex mtx(name.c_str());
|
||||||
ASSERT_TRUE(mtx.valid());
|
ASSERT_TRUE(mtx.valid());
|
||||||
|
|
||||||
bool locked = mtx.try_lock();
|
bool locked = mtx.try_lock();
|
||||||
EXPECT_TRUE(locked);
|
EXPECT_TRUE(locked);
|
||||||
|
|
||||||
if (locked) {
|
if (locked) {
|
||||||
mtx.unlock();
|
mtx.unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test timed lock with infinite timeout
|
// Test timed lock with infinite timeout
|
||||||
TEST_F(MutexTest, TimedLockInfinite) {
|
TEST_F(MutexTest, TimedLockInfinite) {
|
||||||
std::string name = generate_unique_mutex_name("timed_lock_inf");
|
std::string name = generate_unique_mutex_name("timed_lock_inf");
|
||||||
|
|
||||||
mutex mtx(name.c_str());
|
mutex mtx(name.c_str());
|
||||||
ASSERT_TRUE(mtx.valid());
|
ASSERT_TRUE(mtx.valid());
|
||||||
|
|
||||||
bool locked = mtx.lock(invalid_value);
|
bool locked = mtx.lock(invalid_value);
|
||||||
EXPECT_TRUE(locked);
|
EXPECT_TRUE(locked);
|
||||||
|
|
||||||
if (locked) {
|
if (locked) {
|
||||||
mtx.unlock();
|
mtx.unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test timed lock with timeout
|
// Test timed lock with timeout
|
||||||
TEST_F(MutexTest, TimedLockTimeout) {
|
TEST_F(MutexTest, TimedLockTimeout) {
|
||||||
std::string name = generate_unique_mutex_name("timed_lock_timeout");
|
std::string name = generate_unique_mutex_name("timed_lock_timeout");
|
||||||
|
|
||||||
mutex mtx(name.c_str());
|
mutex mtx(name.c_str());
|
||||||
ASSERT_TRUE(mtx.valid());
|
ASSERT_TRUE(mtx.valid());
|
||||||
|
|
||||||
// Lock with 100ms timeout
|
// Lock with 100ms timeout
|
||||||
bool locked = mtx.lock(100);
|
bool locked = mtx.lock(100);
|
||||||
EXPECT_TRUE(locked);
|
EXPECT_TRUE(locked);
|
||||||
|
|
||||||
if (locked) {
|
if (locked) {
|
||||||
mtx.unlock();
|
mtx.unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test mutex protects critical section
|
// Test mutex protects critical section
|
||||||
TEST_F(MutexTest, CriticalSection) {
|
TEST_F(MutexTest, CriticalSection) {
|
||||||
std::string name = generate_unique_mutex_name("critical_section");
|
std::string name = generate_unique_mutex_name("critical_section");
|
||||||
|
|
||||||
mutex mtx(name.c_str());
|
mutex mtx(name.c_str());
|
||||||
ASSERT_TRUE(mtx.valid());
|
ASSERT_TRUE(mtx.valid());
|
||||||
|
|
||||||
int shared_counter = 0;
|
int shared_counter = 0;
|
||||||
const int iterations = 100;
|
const int iterations = 100;
|
||||||
|
|
||||||
auto increment_task = [&]() {
|
auto increment_task = [&]() {
|
||||||
for (int i = 0; i < iterations; ++i) {
|
for (int i = 0; i < iterations; ++i) {
|
||||||
mtx.lock();
|
mtx.lock();
|
||||||
++shared_counter;
|
++shared_counter;
|
||||||
mtx.unlock();
|
mtx.unlock();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
std::thread t1(increment_task);
|
std::thread t1(increment_task);
|
||||||
std::thread t2(increment_task);
|
std::thread t2(increment_task);
|
||||||
|
|
||||||
t1.join();
|
t1.join();
|
||||||
t2.join();
|
t2.join();
|
||||||
|
|
||||||
EXPECT_EQ(shared_counter, iterations * 2);
|
EXPECT_EQ(shared_counter, iterations * 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test concurrent try_lock
|
// Test concurrent try_lock
|
||||||
TEST_F(MutexTest, ConcurrentTryLock) {
|
TEST_F(MutexTest, ConcurrentTryLock) {
|
||||||
std::string name = generate_unique_mutex_name("concurrent_try");
|
std::string name = generate_unique_mutex_name("concurrent_try");
|
||||||
|
|
||||||
mutex mtx(name.c_str());
|
mutex mtx(name.c_str());
|
||||||
ASSERT_TRUE(mtx.valid());
|
ASSERT_TRUE(mtx.valid());
|
||||||
|
|
||||||
std::atomic<int> success_count{0};
|
std::atomic<int> success_count{0};
|
||||||
std::atomic<int> fail_count{0};
|
std::atomic<int> fail_count{0};
|
||||||
|
|
||||||
auto try_lock_task = [&]() {
|
auto try_lock_task = [&]() {
|
||||||
for (int i = 0; i < 10; ++i) {
|
for (int i = 0; i < 10; ++i) {
|
||||||
if (mtx.try_lock()) {
|
if (mtx.try_lock()) {
|
||||||
++success_count;
|
++success_count;
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||||
mtx.unlock();
|
mtx.unlock();
|
||||||
} else {
|
} else {
|
||||||
++fail_count;
|
++fail_count;
|
||||||
}
|
}
|
||||||
std::this_thread::yield();
|
std::this_thread::yield();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
std::thread t1(try_lock_task);
|
std::thread t1(try_lock_task);
|
||||||
std::thread t2(try_lock_task);
|
std::thread t2(try_lock_task);
|
||||||
std::thread t3(try_lock_task);
|
std::thread t3(try_lock_task);
|
||||||
|
|
||||||
t1.join();
|
t1.join();
|
||||||
t2.join();
|
t2.join();
|
||||||
t3.join();
|
t3.join();
|
||||||
|
|
||||||
EXPECT_GT(success_count.load(), 0);
|
EXPECT_GT(success_count.load(), 0);
|
||||||
// Some try_locks should succeed
|
// Some try_locks should succeed
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test lock contention
|
// Test lock contention
|
||||||
TEST_F(MutexTest, LockContention) {
|
TEST_F(MutexTest, LockContention) {
|
||||||
std::string name = generate_unique_mutex_name("contention");
|
std::string name = generate_unique_mutex_name("contention");
|
||||||
|
|
||||||
mutex mtx(name.c_str());
|
mutex mtx(name.c_str());
|
||||||
ASSERT_TRUE(mtx.valid());
|
ASSERT_TRUE(mtx.valid());
|
||||||
|
|
||||||
std::atomic<bool> thread1_in_cs{false};
|
std::atomic<bool> thread1_in_cs{false};
|
||||||
std::atomic<bool> thread2_in_cs{false};
|
std::atomic<bool> thread2_in_cs{false};
|
||||||
std::atomic<bool> violation{false};
|
std::atomic<bool> violation{false};
|
||||||
|
|
||||||
auto contention_task = [&](std::atomic<bool>& my_flag,
|
auto contention_task = [&](std::atomic<bool>& my_flag,
|
||||||
std::atomic<bool>& other_flag) {
|
std::atomic<bool>& other_flag) {
|
||||||
for (int i = 0; i < 50; ++i) {
|
for (int i = 0; i < 50; ++i) {
|
||||||
mtx.lock();
|
mtx.lock();
|
||||||
|
|
||||||
my_flag.store(true);
|
my_flag.store(true);
|
||||||
if (other_flag.load()) {
|
if (other_flag.load()) {
|
||||||
violation.store(true);
|
violation.store(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::this_thread::sleep_for(std::chrono::microseconds(10));
|
std::this_thread::sleep_for(std::chrono::microseconds(10));
|
||||||
|
|
||||||
my_flag.store(false);
|
my_flag.store(false);
|
||||||
mtx.unlock();
|
mtx.unlock();
|
||||||
|
|
||||||
std::this_thread::yield();
|
std::this_thread::yield();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
std::thread t1(contention_task, std::ref(thread1_in_cs), std::ref(thread2_in_cs));
|
std::thread t1(contention_task, std::ref(thread1_in_cs), std::ref(thread2_in_cs));
|
||||||
std::thread t2(contention_task, std::ref(thread2_in_cs), std::ref(thread1_in_cs));
|
std::thread t2(contention_task, std::ref(thread2_in_cs), std::ref(thread1_in_cs));
|
||||||
|
|
||||||
t1.join();
|
t1.join();
|
||||||
t2.join();
|
t2.join();
|
||||||
|
|
||||||
// Should never have both threads in critical section
|
// Should never have both threads in critical section
|
||||||
EXPECT_FALSE(violation.load());
|
EXPECT_FALSE(violation.load());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test multiple lock/unlock cycles
|
// Test multiple lock/unlock cycles
|
||||||
TEST_F(MutexTest, MultipleCycles) {
|
TEST_F(MutexTest, MultipleCycles) {
|
||||||
std::string name = generate_unique_mutex_name("cycles");
|
std::string name = generate_unique_mutex_name("cycles");
|
||||||
|
|
||||||
mutex mtx(name.c_str());
|
mutex mtx(name.c_str());
|
||||||
ASSERT_TRUE(mtx.valid());
|
ASSERT_TRUE(mtx.valid());
|
||||||
|
|
||||||
for (int i = 0; i < 100; ++i) {
|
for (int i = 0; i < 100; ++i) {
|
||||||
ASSERT_TRUE(mtx.lock());
|
ASSERT_TRUE(mtx.lock());
|
||||||
ASSERT_TRUE(mtx.unlock());
|
ASSERT_TRUE(mtx.unlock());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test timed lock timeout scenario
|
// Test timed lock timeout scenario
|
||||||
TEST_F(MutexTest, TimedLockTimeoutScenario) {
|
TEST_F(MutexTest, TimedLockTimeoutScenario) {
|
||||||
std::string name = generate_unique_mutex_name("timeout_scenario");
|
std::string name = generate_unique_mutex_name("timeout_scenario");
|
||||||
|
|
||||||
mutex mtx(name.c_str());
|
mutex mtx(name.c_str());
|
||||||
ASSERT_TRUE(mtx.valid());
|
ASSERT_TRUE(mtx.valid());
|
||||||
|
|
||||||
// Lock in main thread
|
// Lock in main thread
|
||||||
ASSERT_TRUE(mtx.lock());
|
ASSERT_TRUE(mtx.lock());
|
||||||
|
|
||||||
std::atomic<bool> timeout_occurred{false};
|
std::atomic<bool> timeout_occurred{false};
|
||||||
|
|
||||||
std::thread t([&]() {
|
std::thread t([&]() {
|
||||||
// Try to lock with short timeout - should timeout
|
// Try to lock with short timeout - should timeout
|
||||||
bool locked = mtx.lock(50); // 50ms timeout
|
bool locked = mtx.lock(50); // 50ms timeout
|
||||||
if (!locked) {
|
if (!locked) {
|
||||||
timeout_occurred.store(true);
|
timeout_occurred.store(true);
|
||||||
} else {
|
} else {
|
||||||
mtx.unlock();
|
mtx.unlock();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||||
mtx.unlock();
|
mtx.unlock();
|
||||||
|
|
||||||
t.join();
|
t.join();
|
||||||
|
|
||||||
// Timeout should have occurred since we held the lock
|
// Timeout should have occurred since we held the lock
|
||||||
EXPECT_TRUE(timeout_occurred.load());
|
EXPECT_TRUE(timeout_occurred.load());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test reopen after close
|
// Test reopen after close
|
||||||
TEST_F(MutexTest, ReopenAfterClose) {
|
TEST_F(MutexTest, ReopenAfterClose) {
|
||||||
std::string name = generate_unique_mutex_name("reopen");
|
std::string name = generate_unique_mutex_name("reopen");
|
||||||
|
|
||||||
mutex mtx;
|
mutex mtx;
|
||||||
|
|
||||||
ASSERT_TRUE(mtx.open(name.c_str()));
|
ASSERT_TRUE(mtx.open(name.c_str()));
|
||||||
EXPECT_TRUE(mtx.valid());
|
EXPECT_TRUE(mtx.valid());
|
||||||
|
|
||||||
mtx.close();
|
mtx.close();
|
||||||
EXPECT_FALSE(mtx.valid());
|
EXPECT_FALSE(mtx.valid());
|
||||||
|
|
||||||
ASSERT_TRUE(mtx.open(name.c_str()));
|
ASSERT_TRUE(mtx.open(name.c_str()));
|
||||||
EXPECT_TRUE(mtx.valid());
|
EXPECT_TRUE(mtx.valid());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test named mutex inter-thread synchronization
|
// Test named mutex inter-thread synchronization
|
||||||
TEST_F(MutexTest, NamedMutexInterThread) {
|
TEST_F(MutexTest, NamedMutexInterThread) {
|
||||||
std::string name = generate_unique_mutex_name("inter_thread");
|
std::string name = generate_unique_mutex_name("inter_thread");
|
||||||
|
|
||||||
int shared_data = 0;
|
int shared_data = 0;
|
||||||
std::atomic<bool> t1_done{false};
|
std::atomic<bool> t1_done{false};
|
||||||
std::atomic<bool> t2_done{false};
|
std::atomic<bool> t2_done{false};
|
||||||
|
|
||||||
std::thread t1([&]() {
|
std::thread t1([&]() {
|
||||||
mutex mtx(name.c_str());
|
mutex mtx(name.c_str());
|
||||||
ASSERT_TRUE(mtx.valid());
|
ASSERT_TRUE(mtx.valid());
|
||||||
|
|
||||||
mtx.lock();
|
mtx.lock();
|
||||||
shared_data = 100;
|
shared_data = 100;
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||||
mtx.unlock();
|
mtx.unlock();
|
||||||
|
|
||||||
t1_done.store(true);
|
t1_done.store(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
std::thread t2([&]() {
|
std::thread t2([&]() {
|
||||||
// Wait a bit to ensure t1 starts first
|
// Wait a bit to ensure t1 starts first
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||||
|
|
||||||
mutex mtx(name.c_str());
|
mutex mtx(name.c_str());
|
||||||
ASSERT_TRUE(mtx.valid());
|
ASSERT_TRUE(mtx.valid());
|
||||||
|
|
||||||
mtx.lock();
|
mtx.lock();
|
||||||
EXPECT_TRUE(t1_done.load() || shared_data == 100);
|
EXPECT_TRUE(t1_done.load() || shared_data == 100);
|
||||||
shared_data = 200;
|
shared_data = 200;
|
||||||
mtx.unlock();
|
mtx.unlock();
|
||||||
|
|
||||||
t2_done.store(true);
|
t2_done.store(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
t1.join();
|
t1.join();
|
||||||
t2.join();
|
t2.join();
|
||||||
|
|
||||||
EXPECT_EQ(shared_data, 200);
|
EXPECT_EQ(shared_data, 200);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test exception safety of try_lock
|
// Test exception safety of try_lock
|
||||||
TEST_F(MutexTest, TryLockExceptionSafety) {
|
TEST_F(MutexTest, TryLockExceptionSafety) {
|
||||||
std::string name = generate_unique_mutex_name("try_lock_exception");
|
std::string name = generate_unique_mutex_name("try_lock_exception");
|
||||||
|
|
||||||
mutex mtx(name.c_str());
|
mutex mtx(name.c_str());
|
||||||
ASSERT_TRUE(mtx.valid());
|
ASSERT_TRUE(mtx.valid());
|
||||||
|
|
||||||
bool exception_thrown = false;
|
bool exception_thrown = false;
|
||||||
try {
|
try {
|
||||||
mtx.try_lock();
|
mtx.try_lock();
|
||||||
} catch (const std::system_error&) {
|
} catch (const std::system_error&) {
|
||||||
exception_thrown = true;
|
exception_thrown = true;
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
FAIL() << "Unexpected exception type";
|
FAIL() << "Unexpected exception type";
|
||||||
}
|
}
|
||||||
|
|
||||||
// try_lock may throw system_error
|
// try_lock may throw system_error
|
||||||
// Just ensure we can handle it
|
// Just ensure we can handle it
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test concurrent open/close operations
|
// Test concurrent open/close operations
|
||||||
TEST_F(MutexTest, ConcurrentOpenClose) {
|
TEST_F(MutexTest, ConcurrentOpenClose) {
|
||||||
std::vector<std::thread> threads;
|
std::vector<std::thread> threads;
|
||||||
std::atomic<int> success_count{0};
|
std::atomic<int> success_count{0};
|
||||||
|
|
||||||
for (int i = 0; i < 5; ++i) {
|
for (int i = 0; i < 5; ++i) {
|
||||||
threads.emplace_back([&, i]() {
|
threads.emplace_back([&, i]() {
|
||||||
std::string name = generate_unique_mutex_name("concurrent");
|
std::string name = generate_unique_mutex_name("concurrent");
|
||||||
name += std::to_string(i);
|
name += std::to_string(i);
|
||||||
|
|
||||||
mutex mtx;
|
mutex mtx;
|
||||||
if (mtx.open(name.c_str())) {
|
if (mtx.open(name.c_str())) {
|
||||||
++success_count;
|
++success_count;
|
||||||
mtx.close();
|
mtx.close();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto& t : threads) {
|
for (auto& t : threads) {
|
||||||
t.join();
|
t.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
EXPECT_EQ(success_count.load(), 5);
|
EXPECT_EQ(success_count.load(), 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test mutex with zero timeout
|
// Test mutex with zero timeout
|
||||||
TEST_F(MutexTest, ZeroTimeout) {
|
TEST_F(MutexTest, ZeroTimeout) {
|
||||||
std::string name = generate_unique_mutex_name("zero_timeout");
|
std::string name = generate_unique_mutex_name("zero_timeout");
|
||||||
|
|
||||||
mutex mtx(name.c_str());
|
mutex mtx(name.c_str());
|
||||||
ASSERT_TRUE(mtx.valid());
|
ASSERT_TRUE(mtx.valid());
|
||||||
|
|
||||||
// Lock with zero timeout (should try once and return)
|
// Lock with zero timeout (should try once and return)
|
||||||
bool locked = mtx.lock(0);
|
bool locked = mtx.lock(0);
|
||||||
|
|
||||||
if (locked) {
|
if (locked) {
|
||||||
mtx.unlock();
|
mtx.unlock();
|
||||||
}
|
}
|
||||||
// Result may vary, just ensure it doesn't hang
|
// Result may vary, just ensure it doesn't hang
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test rapid lock/unlock sequence
|
// Test rapid lock/unlock sequence
|
||||||
TEST_F(MutexTest, RapidLockUnlock) {
|
TEST_F(MutexTest, RapidLockUnlock) {
|
||||||
std::string name = generate_unique_mutex_name("rapid");
|
std::string name = generate_unique_mutex_name("rapid");
|
||||||
|
|
||||||
mutex mtx(name.c_str());
|
mutex mtx(name.c_str());
|
||||||
ASSERT_TRUE(mtx.valid());
|
ASSERT_TRUE(mtx.valid());
|
||||||
|
|
||||||
auto rapid_task = [&]() {
|
auto rapid_task = [&]() {
|
||||||
for (int i = 0; i < 1000; ++i) {
|
for (int i = 0; i < 1000; ++i) {
|
||||||
mtx.lock();
|
mtx.lock();
|
||||||
mtx.unlock();
|
mtx.unlock();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
std::thread t1(rapid_task);
|
std::thread t1(rapid_task);
|
||||||
std::thread t2(rapid_task);
|
std::thread t2(rapid_task);
|
||||||
|
|
||||||
t1.join();
|
t1.join();
|
||||||
t2.join();
|
t2.join();
|
||||||
|
|
||||||
// Should complete without deadlock or crash
|
// Should complete without deadlock or crash
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test lock after clear
|
// Test lock after clear
|
||||||
TEST_F(MutexTest, LockAfterClear) {
|
TEST_F(MutexTest, LockAfterClear) {
|
||||||
std::string name = generate_unique_mutex_name("lock_after_clear");
|
std::string name = generate_unique_mutex_name("lock_after_clear");
|
||||||
|
|
||||||
mutex mtx(name.c_str());
|
mutex mtx(name.c_str());
|
||||||
ASSERT_TRUE(mtx.valid());
|
ASSERT_TRUE(mtx.valid());
|
||||||
|
|
||||||
mtx.lock();
|
mtx.lock();
|
||||||
mtx.unlock();
|
mtx.unlock();
|
||||||
|
|
||||||
mtx.clear();
|
mtx.clear();
|
||||||
EXPECT_FALSE(mtx.valid());
|
EXPECT_FALSE(mtx.valid());
|
||||||
|
|
||||||
// Attempting to lock after clear should fail gracefully
|
// Attempting to lock after clear should fail gracefully
|
||||||
bool locked = mtx.lock();
|
bool locked = mtx.lock();
|
||||||
EXPECT_FALSE(locked);
|
EXPECT_FALSE(locked);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -26,462 +26,462 @@ using namespace ipc::sync;
|
|||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
std::string generate_unique_sem_name(const char* prefix) {
|
std::string generate_unique_sem_name(const char* prefix) {
|
||||||
static int counter = 0;
|
static int counter = 0;
|
||||||
return std::string(prefix) + "_sem_" + std::to_string(++counter);
|
return std::string(prefix) + "_sem_" + std::to_string(++counter);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // anonymous namespace
|
} // anonymous namespace
|
||||||
|
|
||||||
class SemaphoreTest : public ::testing::Test {
|
class SemaphoreTest : public ::testing::Test {
|
||||||
protected:
|
protected:
|
||||||
void TearDown() override {
|
void TearDown() override {
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Test default constructor
|
// Test default constructor
|
||||||
TEST_F(SemaphoreTest, DefaultConstructor) {
|
TEST_F(SemaphoreTest, DefaultConstructor) {
|
||||||
semaphore sem;
|
semaphore sem;
|
||||||
// Default constructed semaphore
|
// Default constructed semaphore
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test named constructor with count
|
// Test named constructor with count
|
||||||
TEST_F(SemaphoreTest, NamedConstructorWithCount) {
|
TEST_F(SemaphoreTest, NamedConstructorWithCount) {
|
||||||
std::string name = generate_unique_sem_name("named_count");
|
std::string name = generate_unique_sem_name("named_count");
|
||||||
|
|
||||||
semaphore sem(name.c_str(), 5);
|
semaphore sem(name.c_str(), 5);
|
||||||
EXPECT_TRUE(sem.valid());
|
EXPECT_TRUE(sem.valid());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test named constructor with zero count
|
// Test named constructor with zero count
|
||||||
TEST_F(SemaphoreTest, NamedConstructorZeroCount) {
|
TEST_F(SemaphoreTest, NamedConstructorZeroCount) {
|
||||||
std::string name = generate_unique_sem_name("zero_count");
|
std::string name = generate_unique_sem_name("zero_count");
|
||||||
|
|
||||||
semaphore sem(name.c_str(), 0);
|
semaphore sem(name.c_str(), 0);
|
||||||
EXPECT_TRUE(sem.valid());
|
EXPECT_TRUE(sem.valid());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test native() methods
|
// Test native() methods
|
||||||
TEST_F(SemaphoreTest, NativeHandle) {
|
TEST_F(SemaphoreTest, NativeHandle) {
|
||||||
std::string name = generate_unique_sem_name("native");
|
std::string name = generate_unique_sem_name("native");
|
||||||
|
|
||||||
semaphore sem(name.c_str(), 1);
|
semaphore sem(name.c_str(), 1);
|
||||||
ASSERT_TRUE(sem.valid());
|
ASSERT_TRUE(sem.valid());
|
||||||
|
|
||||||
const void* const_handle = static_cast<const semaphore&>(sem).native();
|
const void* const_handle = static_cast<const semaphore&>(sem).native();
|
||||||
void* handle = sem.native();
|
void* handle = sem.native();
|
||||||
|
|
||||||
EXPECT_NE(const_handle, nullptr);
|
EXPECT_NE(const_handle, nullptr);
|
||||||
EXPECT_NE(handle, nullptr);
|
EXPECT_NE(handle, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test valid() method
|
// Test valid() method
|
||||||
TEST_F(SemaphoreTest, Valid) {
|
TEST_F(SemaphoreTest, Valid) {
|
||||||
semaphore sem1;
|
semaphore sem1;
|
||||||
|
|
||||||
std::string name = generate_unique_sem_name("valid");
|
std::string name = generate_unique_sem_name("valid");
|
||||||
semaphore sem2(name.c_str(), 1);
|
semaphore sem2(name.c_str(), 1);
|
||||||
EXPECT_TRUE(sem2.valid());
|
EXPECT_TRUE(sem2.valid());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test open() method
|
// Test open() method
|
||||||
TEST_F(SemaphoreTest, Open) {
|
TEST_F(SemaphoreTest, Open) {
|
||||||
std::string name = generate_unique_sem_name("open");
|
std::string name = generate_unique_sem_name("open");
|
||||||
|
|
||||||
semaphore sem;
|
semaphore sem;
|
||||||
bool result = sem.open(name.c_str(), 3);
|
bool result = sem.open(name.c_str(), 3);
|
||||||
|
|
||||||
EXPECT_TRUE(result);
|
EXPECT_TRUE(result);
|
||||||
EXPECT_TRUE(sem.valid());
|
EXPECT_TRUE(sem.valid());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test close() method
|
// Test close() method
|
||||||
TEST_F(SemaphoreTest, Close) {
|
TEST_F(SemaphoreTest, Close) {
|
||||||
std::string name = generate_unique_sem_name("close");
|
std::string name = generate_unique_sem_name("close");
|
||||||
|
|
||||||
semaphore sem(name.c_str(), 1);
|
semaphore sem(name.c_str(), 1);
|
||||||
ASSERT_TRUE(sem.valid());
|
ASSERT_TRUE(sem.valid());
|
||||||
|
|
||||||
sem.close();
|
sem.close();
|
||||||
EXPECT_FALSE(sem.valid());
|
EXPECT_FALSE(sem.valid());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test clear() method
|
// Test clear() method
|
||||||
TEST_F(SemaphoreTest, Clear) {
|
TEST_F(SemaphoreTest, Clear) {
|
||||||
std::string name = generate_unique_sem_name("clear");
|
std::string name = generate_unique_sem_name("clear");
|
||||||
|
|
||||||
semaphore sem(name.c_str(), 1);
|
semaphore sem(name.c_str(), 1);
|
||||||
ASSERT_TRUE(sem.valid());
|
ASSERT_TRUE(sem.valid());
|
||||||
|
|
||||||
sem.clear();
|
sem.clear();
|
||||||
EXPECT_FALSE(sem.valid());
|
EXPECT_FALSE(sem.valid());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test clear_storage() static method
|
// Test clear_storage() static method
|
||||||
TEST_F(SemaphoreTest, ClearStorage) {
|
TEST_F(SemaphoreTest, ClearStorage) {
|
||||||
std::string name = generate_unique_sem_name("clear_storage");
|
std::string name = generate_unique_sem_name("clear_storage");
|
||||||
|
|
||||||
{
|
{
|
||||||
semaphore sem(name.c_str(), 1);
|
semaphore sem(name.c_str(), 1);
|
||||||
EXPECT_TRUE(sem.valid());
|
EXPECT_TRUE(sem.valid());
|
||||||
}
|
}
|
||||||
|
|
||||||
semaphore::clear_storage(name.c_str());
|
semaphore::clear_storage(name.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test basic wait and post
|
// Test basic wait and post
|
||||||
TEST_F(SemaphoreTest, WaitPost) {
|
TEST_F(SemaphoreTest, WaitPost) {
|
||||||
std::string name = generate_unique_sem_name("wait_post");
|
std::string name = generate_unique_sem_name("wait_post");
|
||||||
|
|
||||||
semaphore sem(name.c_str(), 1);
|
semaphore sem(name.c_str(), 1);
|
||||||
ASSERT_TRUE(sem.valid());
|
ASSERT_TRUE(sem.valid());
|
||||||
|
|
||||||
bool waited = sem.wait();
|
bool waited = sem.wait();
|
||||||
EXPECT_TRUE(waited);
|
EXPECT_TRUE(waited);
|
||||||
|
|
||||||
bool posted = sem.post();
|
bool posted = sem.post();
|
||||||
EXPECT_TRUE(posted);
|
EXPECT_TRUE(posted);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test post with count
|
// Test post with count
|
||||||
TEST_F(SemaphoreTest, PostWithCount) {
|
TEST_F(SemaphoreTest, PostWithCount) {
|
||||||
std::string name = generate_unique_sem_name("post_count");
|
std::string name = generate_unique_sem_name("post_count");
|
||||||
|
|
||||||
semaphore sem(name.c_str(), 0);
|
semaphore sem(name.c_str(), 0);
|
||||||
ASSERT_TRUE(sem.valid());
|
ASSERT_TRUE(sem.valid());
|
||||||
|
|
||||||
bool posted = sem.post(5);
|
bool posted = sem.post(5);
|
||||||
EXPECT_TRUE(posted);
|
EXPECT_TRUE(posted);
|
||||||
|
|
||||||
// Now should be able to wait 5 times
|
// Now should be able to wait 5 times
|
||||||
for (int i = 0; i < 5; ++i) {
|
for (int i = 0; i < 5; ++i) {
|
||||||
EXPECT_TRUE(sem.wait(10)); // 10ms timeout
|
EXPECT_TRUE(sem.wait(10)); // 10ms timeout
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test timed wait with timeout
|
// Test timed wait with timeout
|
||||||
TEST_F(SemaphoreTest, TimedWait) {
|
TEST_F(SemaphoreTest, TimedWait) {
|
||||||
std::string name = generate_unique_sem_name("timed_wait");
|
std::string name = generate_unique_sem_name("timed_wait");
|
||||||
|
|
||||||
semaphore sem(name.c_str(), 1);
|
semaphore sem(name.c_str(), 1);
|
||||||
ASSERT_TRUE(sem.valid());
|
ASSERT_TRUE(sem.valid());
|
||||||
|
|
||||||
bool waited = sem.wait(100); // 100ms timeout
|
bool waited = sem.wait(100); // 100ms timeout
|
||||||
EXPECT_TRUE(waited);
|
EXPECT_TRUE(waited);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test wait timeout scenario
|
// Test wait timeout scenario
|
||||||
TEST_F(SemaphoreTest, WaitTimeout) {
|
TEST_F(SemaphoreTest, WaitTimeout) {
|
||||||
std::string name = generate_unique_sem_name("wait_timeout");
|
std::string name = generate_unique_sem_name("wait_timeout");
|
||||||
|
|
||||||
semaphore sem(name.c_str(), 0); // Zero count
|
semaphore sem(name.c_str(), 0); // Zero count
|
||||||
ASSERT_TRUE(sem.valid());
|
ASSERT_TRUE(sem.valid());
|
||||||
|
|
||||||
auto start = std::chrono::steady_clock::now();
|
auto start = std::chrono::steady_clock::now();
|
||||||
bool waited = sem.wait(50); // 50ms timeout
|
bool waited = sem.wait(50); // 50ms timeout
|
||||||
auto end = std::chrono::steady_clock::now();
|
auto end = std::chrono::steady_clock::now();
|
||||||
|
|
||||||
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
|
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
|
||||||
|
|
||||||
// Should timeout
|
// Should timeout
|
||||||
EXPECT_FALSE(waited);
|
EXPECT_FALSE(waited);
|
||||||
EXPECT_GE(elapsed, 40); // Allow some tolerance
|
EXPECT_GE(elapsed, 40); // Allow some tolerance
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test infinite wait
|
// Test infinite wait
|
||||||
TEST_F(SemaphoreTest, InfiniteWait) {
|
TEST_F(SemaphoreTest, InfiniteWait) {
|
||||||
std::string name = generate_unique_sem_name("infinite_wait");
|
std::string name = generate_unique_sem_name("infinite_wait");
|
||||||
|
|
||||||
semaphore sem(name.c_str(), 0);
|
semaphore sem(name.c_str(), 0);
|
||||||
ASSERT_TRUE(sem.valid());
|
ASSERT_TRUE(sem.valid());
|
||||||
|
|
||||||
std::atomic<bool> wait_started{false};
|
std::atomic<bool> wait_started{false};
|
||||||
std::atomic<bool> wait_succeeded{false};
|
std::atomic<bool> wait_succeeded{false};
|
||||||
|
|
||||||
std::thread waiter([&]() {
|
std::thread waiter([&]() {
|
||||||
wait_started.store(true);
|
wait_started.store(true);
|
||||||
bool result = sem.wait(invalid_value);
|
bool result = sem.wait(invalid_value);
|
||||||
wait_succeeded.store(result);
|
wait_succeeded.store(result);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Wait for thread to start waiting
|
// Wait for thread to start waiting
|
||||||
while (!wait_started.load()) {
|
while (!wait_started.load()) {
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||||
|
|
||||||
// Post to release the waiter
|
// Post to release the waiter
|
||||||
sem.post();
|
sem.post();
|
||||||
|
|
||||||
waiter.join();
|
waiter.join();
|
||||||
|
|
||||||
EXPECT_TRUE(wait_succeeded.load());
|
EXPECT_TRUE(wait_succeeded.load());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test producer-consumer pattern
|
// Test producer-consumer pattern
|
||||||
TEST_F(SemaphoreTest, ProducerConsumer) {
|
TEST_F(SemaphoreTest, ProducerConsumer) {
|
||||||
std::string name = generate_unique_sem_name("prod_cons");
|
std::string name = generate_unique_sem_name("prod_cons");
|
||||||
|
|
||||||
semaphore sem(name.c_str(), 0);
|
semaphore sem(name.c_str(), 0);
|
||||||
ASSERT_TRUE(sem.valid());
|
ASSERT_TRUE(sem.valid());
|
||||||
|
|
||||||
std::atomic<int> produced{0};
|
std::atomic<int> produced{0};
|
||||||
std::atomic<int> consumed{0};
|
std::atomic<int> consumed{0};
|
||||||
const int count = 10;
|
const int count = 10;
|
||||||
|
|
||||||
std::thread producer([&]() {
|
std::thread producer([&]() {
|
||||||
for (int i = 0; i < count; ++i) {
|
for (int i = 0; i < count; ++i) {
|
||||||
++produced;
|
++produced;
|
||||||
sem.post();
|
sem.post();
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
std::thread consumer([&]() {
|
std::thread consumer([&]() {
|
||||||
for (int i = 0; i < count; ++i) {
|
for (int i = 0; i < count; ++i) {
|
||||||
sem.wait();
|
sem.wait();
|
||||||
++consumed;
|
++consumed;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
producer.join();
|
producer.join();
|
||||||
consumer.join();
|
consumer.join();
|
||||||
|
|
||||||
EXPECT_EQ(produced.load(), count);
|
EXPECT_EQ(produced.load(), count);
|
||||||
EXPECT_EQ(consumed.load(), count);
|
EXPECT_EQ(consumed.load(), count);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test multiple producers and consumers
|
// Test multiple producers and consumers
|
||||||
TEST_F(SemaphoreTest, MultipleProducersConsumers) {
|
TEST_F(SemaphoreTest, MultipleProducersConsumers) {
|
||||||
std::string name = generate_unique_sem_name("multi_prod_cons");
|
std::string name = generate_unique_sem_name("multi_prod_cons");
|
||||||
|
|
||||||
semaphore sem(name.c_str(), 0);
|
semaphore sem(name.c_str(), 0);
|
||||||
ASSERT_TRUE(sem.valid());
|
ASSERT_TRUE(sem.valid());
|
||||||
|
|
||||||
std::atomic<int> total_produced{0};
|
std::atomic<int> total_produced{0};
|
||||||
std::atomic<int> total_consumed{0};
|
std::atomic<int> total_consumed{0};
|
||||||
const int items_per_producer = 5;
|
const int items_per_producer = 5;
|
||||||
const int num_producers = 3;
|
const int num_producers = 3;
|
||||||
const int num_consumers = 3;
|
const int num_consumers = 3;
|
||||||
|
|
||||||
std::vector<std::thread> producers;
|
std::vector<std::thread> producers;
|
||||||
for (int i = 0; i < num_producers; ++i) {
|
for (int i = 0; i < num_producers; ++i) {
|
||||||
producers.emplace_back([&]() {
|
producers.emplace_back([&]() {
|
||||||
for (int j = 0; j < items_per_producer; ++j) {
|
for (int j = 0; j < items_per_producer; ++j) {
|
||||||
++total_produced;
|
++total_produced;
|
||||||
sem.post();
|
sem.post();
|
||||||
std::this_thread::yield();
|
std::this_thread::yield();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::thread> consumers;
|
std::vector<std::thread> consumers;
|
||||||
for (int i = 0; i < num_consumers; ++i) {
|
for (int i = 0; i < num_consumers; ++i) {
|
||||||
consumers.emplace_back([&]() {
|
consumers.emplace_back([&]() {
|
||||||
for (int j = 0; j < items_per_producer; ++j) {
|
for (int j = 0; j < items_per_producer; ++j) {
|
||||||
if (sem.wait(1000)) {
|
if (sem.wait(1000)) {
|
||||||
++total_consumed;
|
++total_consumed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto& t : producers) t.join();
|
for (auto& t : producers) t.join();
|
||||||
for (auto& t : consumers) t.join();
|
for (auto& t : consumers) t.join();
|
||||||
|
|
||||||
EXPECT_EQ(total_produced.load(), items_per_producer * num_producers);
|
EXPECT_EQ(total_produced.load(), items_per_producer * num_producers);
|
||||||
EXPECT_EQ(total_consumed.load(), items_per_producer * num_producers);
|
EXPECT_EQ(total_consumed.load(), items_per_producer * num_producers);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test semaphore with initial count
|
// Test semaphore with initial count
|
||||||
TEST_F(SemaphoreTest, InitialCount) {
|
TEST_F(SemaphoreTest, InitialCount) {
|
||||||
std::string name = generate_unique_sem_name("initial_count");
|
std::string name = generate_unique_sem_name("initial_count");
|
||||||
const uint32_t initial = 3;
|
const uint32_t initial = 3;
|
||||||
|
|
||||||
semaphore sem(name.c_str(), initial);
|
semaphore sem(name.c_str(), initial);
|
||||||
ASSERT_TRUE(sem.valid());
|
ASSERT_TRUE(sem.valid());
|
||||||
|
|
||||||
// Should be able to wait 'initial' times without blocking
|
// Should be able to wait 'initial' times without blocking
|
||||||
for (uint32_t i = 0; i < initial; ++i) {
|
for (uint32_t i = 0; i < initial; ++i) {
|
||||||
EXPECT_TRUE(sem.wait(10));
|
EXPECT_TRUE(sem.wait(10));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Next wait should timeout
|
// Next wait should timeout
|
||||||
EXPECT_FALSE(sem.wait(10));
|
EXPECT_FALSE(sem.wait(10));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test rapid post operations
|
// Test rapid post operations
|
||||||
TEST_F(SemaphoreTest, RapidPost) {
|
TEST_F(SemaphoreTest, RapidPost) {
|
||||||
std::string name = generate_unique_sem_name("rapid_post");
|
std::string name = generate_unique_sem_name("rapid_post");
|
||||||
|
|
||||||
semaphore sem(name.c_str(), 0);
|
semaphore sem(name.c_str(), 0);
|
||||||
ASSERT_TRUE(sem.valid());
|
ASSERT_TRUE(sem.valid());
|
||||||
|
|
||||||
const int post_count = 100;
|
const int post_count = 100;
|
||||||
for (int i = 0; i < post_count; ++i) {
|
for (int i = 0; i < post_count; ++i) {
|
||||||
EXPECT_TRUE(sem.post());
|
EXPECT_TRUE(sem.post());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Should be able to wait post_count times
|
// Should be able to wait post_count times
|
||||||
int wait_count = 0;
|
int wait_count = 0;
|
||||||
for (int i = 0; i < post_count; ++i) {
|
for (int i = 0; i < post_count; ++i) {
|
||||||
if (sem.wait(10)) {
|
if (sem.wait(10)) {
|
||||||
++wait_count;
|
++wait_count;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
EXPECT_EQ(wait_count, post_count);
|
EXPECT_EQ(wait_count, post_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test concurrent post operations
|
// Test concurrent post operations
|
||||||
TEST_F(SemaphoreTest, ConcurrentPost) {
|
TEST_F(SemaphoreTest, ConcurrentPost) {
|
||||||
std::string name = generate_unique_sem_name("concurrent_post");
|
std::string name = generate_unique_sem_name("concurrent_post");
|
||||||
|
|
||||||
semaphore sem(name.c_str(), 0);
|
semaphore sem(name.c_str(), 0);
|
||||||
ASSERT_TRUE(sem.valid());
|
ASSERT_TRUE(sem.valid());
|
||||||
|
|
||||||
std::atomic<int> post_count{0};
|
std::atomic<int> post_count{0};
|
||||||
const int threads = 5;
|
const int threads = 5;
|
||||||
const int posts_per_thread = 10;
|
const int posts_per_thread = 10;
|
||||||
|
|
||||||
std::vector<std::thread> posters;
|
std::vector<std::thread> posters;
|
||||||
for (int i = 0; i < threads; ++i) {
|
for (int i = 0; i < threads; ++i) {
|
||||||
posters.emplace_back([&]() {
|
posters.emplace_back([&]() {
|
||||||
for (int j = 0; j < posts_per_thread; ++j) {
|
for (int j = 0; j < posts_per_thread; ++j) {
|
||||||
if (sem.post()) {
|
if (sem.post()) {
|
||||||
++post_count;
|
++post_count;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto& t : posters) t.join();
|
for (auto& t : posters) t.join();
|
||||||
|
|
||||||
EXPECT_EQ(post_count.load(), threads * posts_per_thread);
|
EXPECT_EQ(post_count.load(), threads * posts_per_thread);
|
||||||
|
|
||||||
// Verify by consuming
|
// Verify by consuming
|
||||||
int consumed = 0;
|
int consumed = 0;
|
||||||
for (int i = 0; i < threads * posts_per_thread; ++i) {
|
for (int i = 0; i < threads * posts_per_thread; ++i) {
|
||||||
if (sem.wait(10)) {
|
if (sem.wait(10)) {
|
||||||
++consumed;
|
++consumed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
EXPECT_EQ(consumed, threads * posts_per_thread);
|
EXPECT_EQ(consumed, threads * posts_per_thread);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test reopen after close
|
// Test reopen after close
|
||||||
TEST_F(SemaphoreTest, ReopenAfterClose) {
|
TEST_F(SemaphoreTest, ReopenAfterClose) {
|
||||||
std::string name = generate_unique_sem_name("reopen");
|
std::string name = generate_unique_sem_name("reopen");
|
||||||
|
|
||||||
semaphore sem;
|
semaphore sem;
|
||||||
|
|
||||||
ASSERT_TRUE(sem.open(name.c_str(), 2));
|
ASSERT_TRUE(sem.open(name.c_str(), 2));
|
||||||
EXPECT_TRUE(sem.valid());
|
EXPECT_TRUE(sem.valid());
|
||||||
|
|
||||||
sem.close();
|
sem.close();
|
||||||
EXPECT_FALSE(sem.valid());
|
EXPECT_FALSE(sem.valid());
|
||||||
|
|
||||||
ASSERT_TRUE(sem.open(name.c_str(), 3));
|
ASSERT_TRUE(sem.open(name.c_str(), 3));
|
||||||
EXPECT_TRUE(sem.valid());
|
EXPECT_TRUE(sem.valid());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test named semaphore sharing between threads
|
// Test named semaphore sharing between threads
|
||||||
TEST_F(SemaphoreTest, NamedSemaphoreSharing) {
|
TEST_F(SemaphoreTest, NamedSemaphoreSharing) {
|
||||||
std::string name = generate_unique_sem_name("sharing");
|
std::string name = generate_unique_sem_name("sharing");
|
||||||
|
|
||||||
std::atomic<int> value{0};
|
std::atomic<int> value{0};
|
||||||
|
|
||||||
std::thread t1([&]() {
|
std::thread t1([&]() {
|
||||||
semaphore sem(name.c_str(), 0);
|
semaphore sem(name.c_str(), 0);
|
||||||
ASSERT_TRUE(sem.valid());
|
ASSERT_TRUE(sem.valid());
|
||||||
|
|
||||||
sem.wait(); // Wait for signal
|
sem.wait(); // Wait for signal
|
||||||
value.store(100);
|
value.store(100);
|
||||||
});
|
});
|
||||||
|
|
||||||
std::thread t2([&]() {
|
std::thread t2([&]() {
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||||
|
|
||||||
semaphore sem(name.c_str(), 0);
|
semaphore sem(name.c_str(), 0);
|
||||||
ASSERT_TRUE(sem.valid());
|
ASSERT_TRUE(sem.valid());
|
||||||
|
|
||||||
sem.post(); // Signal t1
|
sem.post(); // Signal t1
|
||||||
});
|
});
|
||||||
|
|
||||||
t1.join();
|
t1.join();
|
||||||
t2.join();
|
t2.join();
|
||||||
|
|
||||||
EXPECT_EQ(value.load(), 100);
|
EXPECT_EQ(value.load(), 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test post multiple count at once
|
// Test post multiple count at once
|
||||||
TEST_F(SemaphoreTest, PostMultiple) {
|
TEST_F(SemaphoreTest, PostMultiple) {
|
||||||
std::string name = generate_unique_sem_name("post_multiple");
|
std::string name = generate_unique_sem_name("post_multiple");
|
||||||
|
|
||||||
semaphore sem(name.c_str(), 0);
|
semaphore sem(name.c_str(), 0);
|
||||||
ASSERT_TRUE(sem.valid());
|
ASSERT_TRUE(sem.valid());
|
||||||
|
|
||||||
const uint32_t count = 10;
|
const uint32_t count = 10;
|
||||||
bool posted = sem.post(count);
|
bool posted = sem.post(count);
|
||||||
EXPECT_TRUE(posted);
|
EXPECT_TRUE(posted);
|
||||||
|
|
||||||
// Consume all
|
// Consume all
|
||||||
for (uint32_t i = 0; i < count; ++i) {
|
for (uint32_t i = 0; i < count; ++i) {
|
||||||
EXPECT_TRUE(sem.wait(10));
|
EXPECT_TRUE(sem.wait(10));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Should be empty now
|
// Should be empty now
|
||||||
EXPECT_FALSE(sem.wait(10));
|
EXPECT_FALSE(sem.wait(10));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test semaphore after clear
|
// Test semaphore after clear
|
||||||
TEST_F(SemaphoreTest, AfterClear) {
|
TEST_F(SemaphoreTest, AfterClear) {
|
||||||
std::string name = generate_unique_sem_name("after_clear");
|
std::string name = generate_unique_sem_name("after_clear");
|
||||||
|
|
||||||
semaphore sem(name.c_str(), 5);
|
semaphore sem(name.c_str(), 5);
|
||||||
ASSERT_TRUE(sem.valid());
|
ASSERT_TRUE(sem.valid());
|
||||||
|
|
||||||
sem.wait();
|
sem.wait();
|
||||||
sem.clear();
|
sem.clear();
|
||||||
EXPECT_FALSE(sem.valid());
|
EXPECT_FALSE(sem.valid());
|
||||||
|
|
||||||
// Operations after clear should fail gracefully
|
// Operations after clear should fail gracefully
|
||||||
EXPECT_FALSE(sem.wait(10));
|
EXPECT_FALSE(sem.wait(10));
|
||||||
EXPECT_FALSE(sem.post());
|
EXPECT_FALSE(sem.post());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test zero timeout
|
// Test zero timeout
|
||||||
TEST_F(SemaphoreTest, ZeroTimeout) {
|
TEST_F(SemaphoreTest, ZeroTimeout) {
|
||||||
std::string name = generate_unique_sem_name("zero_timeout");
|
std::string name = generate_unique_sem_name("zero_timeout");
|
||||||
|
|
||||||
semaphore sem(name.c_str(), 0);
|
semaphore sem(name.c_str(), 0);
|
||||||
ASSERT_TRUE(sem.valid());
|
ASSERT_TRUE(sem.valid());
|
||||||
|
|
||||||
bool waited = sem.wait(0);
|
bool waited = sem.wait(0);
|
||||||
// Should return immediately (either success or timeout)
|
// Should return immediately (either success or timeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test high-frequency wait/post
|
// Test high-frequency wait/post
|
||||||
TEST_F(SemaphoreTest, HighFrequency) {
|
TEST_F(SemaphoreTest, HighFrequency) {
|
||||||
std::string name = generate_unique_sem_name("high_freq");
|
std::string name = generate_unique_sem_name("high_freq");
|
||||||
|
|
||||||
semaphore sem(name.c_str(), 0);
|
semaphore sem(name.c_str(), 0);
|
||||||
ASSERT_TRUE(sem.valid());
|
ASSERT_TRUE(sem.valid());
|
||||||
|
|
||||||
std::thread poster([&]() {
|
std::thread poster([&]() {
|
||||||
for (int i = 0; i < 1000; ++i) {
|
for (int i = 0; i < 1000; ++i) {
|
||||||
sem.post();
|
sem.post();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
std::thread waiter([&]() {
|
std::thread waiter([&]() {
|
||||||
for (int i = 0; i < 1000; ++i) {
|
for (int i = 0; i < 1000; ++i) {
|
||||||
sem.wait(100);
|
sem.wait(100);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
poster.join();
|
poster.join();
|
||||||
waiter.join();
|
waiter.join();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -23,477 +23,477 @@ namespace {
|
|||||||
|
|
||||||
// Generate unique shared memory names for tests
|
// Generate unique shared memory names for tests
|
||||||
std::string generate_unique_name(const char* prefix) {
|
std::string generate_unique_name(const char* prefix) {
|
||||||
static int counter = 0;
|
static int counter = 0;
|
||||||
return std::string(prefix) + "_test_" + std::to_string(++counter);
|
return std::string(prefix) + "_test_" + std::to_string(++counter);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // anonymous namespace
|
} // anonymous namespace
|
||||||
|
|
||||||
class ShmTest : public ::testing::Test {
|
class ShmTest : public ::testing::Test {
|
||||||
protected:
|
protected:
|
||||||
void TearDown() override {
|
void TearDown() override {
|
||||||
// Clean up any leftover shared memory segments
|
// Clean up any leftover shared memory segments
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// ========== Low-level API Tests ==========
|
// ========== Low-level API Tests ==========
|
||||||
|
|
||||||
// Test acquire with create mode
|
// Test acquire with create mode
|
||||||
TEST_F(ShmTest, AcquireCreate) {
|
TEST_F(ShmTest, AcquireCreate) {
|
||||||
std::string name = generate_unique_name("acquire_create");
|
std::string name = generate_unique_name("acquire_create");
|
||||||
const std::size_t size = 1024;
|
const std::size_t size = 1024;
|
||||||
|
|
||||||
id_t id = acquire(name.c_str(), size, create);
|
id_t id = acquire(name.c_str(), size, create);
|
||||||
ASSERT_NE(id, nullptr);
|
ASSERT_NE(id, nullptr);
|
||||||
|
|
||||||
std::size_t actual_size = 0;
|
std::size_t actual_size = 0;
|
||||||
void* mem = get_mem(id, &actual_size);
|
void* mem = get_mem(id, &actual_size);
|
||||||
EXPECT_NE(mem, nullptr);
|
EXPECT_NE(mem, nullptr);
|
||||||
EXPECT_GE(actual_size, size);
|
EXPECT_GE(actual_size, size);
|
||||||
|
|
||||||
release(id);
|
release(id);
|
||||||
remove(id);
|
remove(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test acquire with open mode (should fail if not exists)
|
// Test acquire with open mode (should fail if not exists)
|
||||||
TEST_F(ShmTest, AcquireOpenNonExistent) {
|
TEST_F(ShmTest, AcquireOpenNonExistent) {
|
||||||
std::string name = generate_unique_name("acquire_open_fail");
|
std::string name = generate_unique_name("acquire_open_fail");
|
||||||
|
|
||||||
id_t id = acquire(name.c_str(), 1024, open);
|
id_t id = acquire(name.c_str(), 1024, open);
|
||||||
// Opening non-existent shared memory should return nullptr or handle failure gracefully
|
// Opening non-existent shared memory should return nullptr or handle failure gracefully
|
||||||
if (id != nullptr) {
|
if (id != nullptr) {
|
||||||
release(id);
|
release(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test acquire with both create and open modes
|
// Test acquire with both create and open modes
|
||||||
TEST_F(ShmTest, AcquireCreateOrOpen) {
|
TEST_F(ShmTest, AcquireCreateOrOpen) {
|
||||||
std::string name = generate_unique_name("acquire_both");
|
std::string name = generate_unique_name("acquire_both");
|
||||||
const std::size_t size = 2048;
|
const std::size_t size = 2048;
|
||||||
|
|
||||||
id_t id = acquire(name.c_str(), size, create | open);
|
id_t id = acquire(name.c_str(), size, create | open);
|
||||||
ASSERT_NE(id, nullptr);
|
ASSERT_NE(id, nullptr);
|
||||||
|
|
||||||
std::size_t actual_size = 0;
|
std::size_t actual_size = 0;
|
||||||
void* mem = get_mem(id, &actual_size);
|
void* mem = get_mem(id, &actual_size);
|
||||||
EXPECT_NE(mem, nullptr);
|
EXPECT_NE(mem, nullptr);
|
||||||
EXPECT_GE(actual_size, size);
|
EXPECT_GE(actual_size, size);
|
||||||
|
|
||||||
release(id);
|
release(id);
|
||||||
remove(id);
|
remove(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test get_mem function
|
// Test get_mem function
|
||||||
TEST_F(ShmTest, GetMemory) {
|
TEST_F(ShmTest, GetMemory) {
|
||||||
std::string name = generate_unique_name("get_mem");
|
std::string name = generate_unique_name("get_mem");
|
||||||
const std::size_t size = 512;
|
const std::size_t size = 512;
|
||||||
|
|
||||||
id_t id = acquire(name.c_str(), size, create);
|
id_t id = acquire(name.c_str(), size, create);
|
||||||
ASSERT_NE(id, nullptr);
|
ASSERT_NE(id, nullptr);
|
||||||
|
|
||||||
std::size_t returned_size = 0;
|
std::size_t returned_size = 0;
|
||||||
void* mem = get_mem(id, &returned_size);
|
void* mem = get_mem(id, &returned_size);
|
||||||
|
|
||||||
EXPECT_NE(mem, nullptr);
|
EXPECT_NE(mem, nullptr);
|
||||||
EXPECT_GE(returned_size, size);
|
EXPECT_GE(returned_size, size);
|
||||||
|
|
||||||
// Write and read test data
|
// Write and read test data
|
||||||
const char* test_data = "Shared memory test data";
|
const char* test_data = "Shared memory test data";
|
||||||
std::strcpy(static_cast<char*>(mem), test_data);
|
std::strcpy(static_cast<char*>(mem), test_data);
|
||||||
EXPECT_STREQ(static_cast<char*>(mem), test_data);
|
EXPECT_STREQ(static_cast<char*>(mem), test_data);
|
||||||
|
|
||||||
release(id);
|
release(id);
|
||||||
remove(id);
|
remove(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test get_mem without size parameter
|
// Test get_mem without size parameter
|
||||||
TEST_F(ShmTest, GetMemoryNoSize) {
|
TEST_F(ShmTest, GetMemoryNoSize) {
|
||||||
std::string name = generate_unique_name("get_mem_no_size");
|
std::string name = generate_unique_name("get_mem_no_size");
|
||||||
|
|
||||||
id_t id = acquire(name.c_str(), 256, create);
|
id_t id = acquire(name.c_str(), 256, create);
|
||||||
ASSERT_NE(id, nullptr);
|
ASSERT_NE(id, nullptr);
|
||||||
|
|
||||||
void* mem = get_mem(id, nullptr);
|
void* mem = get_mem(id, nullptr);
|
||||||
EXPECT_NE(mem, nullptr);
|
EXPECT_NE(mem, nullptr);
|
||||||
|
|
||||||
release(id);
|
release(id);
|
||||||
remove(id);
|
remove(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test release function
|
// Test release function
|
||||||
TEST_F(ShmTest, ReleaseMemory) {
|
TEST_F(ShmTest, ReleaseMemory) {
|
||||||
std::string name = generate_unique_name("release");
|
std::string name = generate_unique_name("release");
|
||||||
|
|
||||||
id_t id = acquire(name.c_str(), 128, create);
|
id_t id = acquire(name.c_str(), 128, create);
|
||||||
ASSERT_NE(id, nullptr);
|
ASSERT_NE(id, nullptr);
|
||||||
|
|
||||||
std::int32_t ref_count = release(id);
|
std::int32_t ref_count = release(id);
|
||||||
EXPECT_GE(ref_count, 0);
|
EXPECT_GE(ref_count, 0);
|
||||||
|
|
||||||
remove(name.c_str());
|
remove(name.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test remove by id
|
// Test remove by id
|
||||||
TEST_F(ShmTest, RemoveById) {
|
TEST_F(ShmTest, RemoveById) {
|
||||||
std::string name = generate_unique_name("remove_by_id");
|
std::string name = generate_unique_name("remove_by_id");
|
||||||
|
|
||||||
id_t id = acquire(name.c_str(), 256, create);
|
id_t id = acquire(name.c_str(), 256, create);
|
||||||
ASSERT_NE(id, nullptr);
|
ASSERT_NE(id, nullptr);
|
||||||
|
|
||||||
release(id);
|
release(id);
|
||||||
remove(id); // Should succeed
|
remove(id); // Should succeed
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test remove by name
|
// Test remove by name
|
||||||
TEST_F(ShmTest, RemoveByName) {
|
TEST_F(ShmTest, RemoveByName) {
|
||||||
std::string name = generate_unique_name("remove_by_name");
|
std::string name = generate_unique_name("remove_by_name");
|
||||||
|
|
||||||
id_t id = acquire(name.c_str(), 256, create);
|
id_t id = acquire(name.c_str(), 256, create);
|
||||||
ASSERT_NE(id, nullptr);
|
ASSERT_NE(id, nullptr);
|
||||||
|
|
||||||
release(id);
|
release(id);
|
||||||
remove(name.c_str()); // Should succeed
|
remove(name.c_str()); // Should succeed
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test reference counting
|
// Test reference counting
|
||||||
TEST_F(ShmTest, ReferenceCount) {
|
TEST_F(ShmTest, ReferenceCount) {
|
||||||
std::string name = generate_unique_name("ref_count");
|
std::string name = generate_unique_name("ref_count");
|
||||||
|
|
||||||
id_t id1 = acquire(name.c_str(), 512, create);
|
id_t id1 = acquire(name.c_str(), 512, create);
|
||||||
ASSERT_NE(id1, nullptr);
|
ASSERT_NE(id1, nullptr);
|
||||||
|
|
||||||
std::int32_t ref1 = get_ref(id1);
|
std::int32_t ref1 = get_ref(id1);
|
||||||
EXPECT_GT(ref1, 0);
|
EXPECT_GT(ref1, 0);
|
||||||
|
|
||||||
// Acquire again (should increase reference count)
|
// Acquire again (should increase reference count)
|
||||||
id_t id2 = acquire(name.c_str(), 512, open);
|
id_t id2 = acquire(name.c_str(), 512, open);
|
||||||
if (id2 != nullptr) {
|
if (id2 != nullptr) {
|
||||||
std::int32_t ref2 = get_ref(id2);
|
std::int32_t ref2 = get_ref(id2);
|
||||||
EXPECT_GE(ref2, ref1);
|
EXPECT_GE(ref2, ref1);
|
||||||
|
|
||||||
release(id2);
|
release(id2);
|
||||||
}
|
}
|
||||||
|
|
||||||
release(id1);
|
release(id1);
|
||||||
remove(name.c_str());
|
remove(name.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test sub_ref function
|
// Test sub_ref function
|
||||||
TEST_F(ShmTest, SubtractReference) {
|
TEST_F(ShmTest, SubtractReference) {
|
||||||
std::string name = generate_unique_name("sub_ref");
|
std::string name = generate_unique_name("sub_ref");
|
||||||
|
|
||||||
id_t id = acquire(name.c_str(), 256, create);
|
id_t id = acquire(name.c_str(), 256, create);
|
||||||
ASSERT_NE(id, nullptr);
|
ASSERT_NE(id, nullptr);
|
||||||
|
|
||||||
std::int32_t ref_before = get_ref(id);
|
std::int32_t ref_before = get_ref(id);
|
||||||
sub_ref(id);
|
sub_ref(id);
|
||||||
std::int32_t ref_after = get_ref(id);
|
std::int32_t ref_after = get_ref(id);
|
||||||
|
|
||||||
EXPECT_EQ(ref_after, ref_before - 1);
|
EXPECT_EQ(ref_after, ref_before - 1);
|
||||||
|
|
||||||
release(id);
|
release(id);
|
||||||
remove(id);
|
remove(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ========== High-level handle class Tests ==========
|
// ========== High-level handle class Tests ==========
|
||||||
|
|
||||||
// Test default handle constructor
|
// Test default handle constructor
|
||||||
TEST_F(ShmTest, HandleDefaultConstructor) {
|
TEST_F(ShmTest, HandleDefaultConstructor) {
|
||||||
handle h;
|
handle h;
|
||||||
EXPECT_FALSE(h.valid());
|
EXPECT_FALSE(h.valid());
|
||||||
EXPECT_EQ(h.size(), 0u);
|
EXPECT_EQ(h.size(), 0u);
|
||||||
EXPECT_EQ(h.get(), nullptr);
|
EXPECT_EQ(h.get(), nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test handle constructor with name and size
|
// Test handle constructor with name and size
|
||||||
TEST_F(ShmTest, HandleConstructorWithParams) {
|
TEST_F(ShmTest, HandleConstructorWithParams) {
|
||||||
std::string name = generate_unique_name("handle_ctor");
|
std::string name = generate_unique_name("handle_ctor");
|
||||||
const std::size_t size = 1024;
|
const std::size_t size = 1024;
|
||||||
|
|
||||||
handle h(name.c_str(), size);
|
handle h(name.c_str(), size);
|
||||||
|
|
||||||
EXPECT_TRUE(h.valid());
|
EXPECT_TRUE(h.valid());
|
||||||
EXPECT_GE(h.size(), size);
|
EXPECT_GE(h.size(), size);
|
||||||
EXPECT_NE(h.get(), nullptr);
|
EXPECT_NE(h.get(), nullptr);
|
||||||
EXPECT_STREQ(h.name(), name.c_str());
|
EXPECT_STREQ(h.name(), name.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test handle move constructor
|
// Test handle move constructor
|
||||||
TEST_F(ShmTest, HandleMoveConstructor) {
|
TEST_F(ShmTest, HandleMoveConstructor) {
|
||||||
std::string name = generate_unique_name("handle_move");
|
std::string name = generate_unique_name("handle_move");
|
||||||
|
|
||||||
handle h1(name.c_str(), 512);
|
handle h1(name.c_str(), 512);
|
||||||
ASSERT_TRUE(h1.valid());
|
ASSERT_TRUE(h1.valid());
|
||||||
|
|
||||||
void* ptr1 = h1.get();
|
void* ptr1 = h1.get();
|
||||||
std::size_t size1 = h1.size();
|
std::size_t size1 = h1.size();
|
||||||
|
|
||||||
handle h2(std::move(h1));
|
handle h2(std::move(h1));
|
||||||
|
|
||||||
EXPECT_TRUE(h2.valid());
|
EXPECT_TRUE(h2.valid());
|
||||||
EXPECT_EQ(h2.get(), ptr1);
|
EXPECT_EQ(h2.get(), ptr1);
|
||||||
EXPECT_EQ(h2.size(), size1);
|
EXPECT_EQ(h2.size(), size1);
|
||||||
|
|
||||||
// h1 should be invalid after move
|
// h1 should be invalid after move
|
||||||
EXPECT_FALSE(h1.valid());
|
EXPECT_FALSE(h1.valid());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test handle swap
|
// Test handle swap
|
||||||
TEST_F(ShmTest, HandleSwap) {
|
TEST_F(ShmTest, HandleSwap) {
|
||||||
std::string name1 = generate_unique_name("handle_swap1");
|
std::string name1 = generate_unique_name("handle_swap1");
|
||||||
std::string name2 = generate_unique_name("handle_swap2");
|
std::string name2 = generate_unique_name("handle_swap2");
|
||||||
|
|
||||||
handle h1(name1.c_str(), 256);
|
handle h1(name1.c_str(), 256);
|
||||||
handle h2(name2.c_str(), 512);
|
handle h2(name2.c_str(), 512);
|
||||||
|
|
||||||
void* ptr1 = h1.get();
|
void* ptr1 = h1.get();
|
||||||
void* ptr2 = h2.get();
|
void* ptr2 = h2.get();
|
||||||
std::size_t size1 = h1.size();
|
std::size_t size1 = h1.size();
|
||||||
std::size_t size2 = h2.size();
|
std::size_t size2 = h2.size();
|
||||||
|
|
||||||
h1.swap(h2);
|
h1.swap(h2);
|
||||||
|
|
||||||
EXPECT_EQ(h1.get(), ptr2);
|
EXPECT_EQ(h1.get(), ptr2);
|
||||||
EXPECT_EQ(h1.size(), size2);
|
EXPECT_EQ(h1.size(), size2);
|
||||||
EXPECT_EQ(h2.get(), ptr1);
|
EXPECT_EQ(h2.get(), ptr1);
|
||||||
EXPECT_EQ(h2.size(), size1);
|
EXPECT_EQ(h2.size(), size1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test handle assignment operator
|
// Test handle assignment operator
|
||||||
TEST_F(ShmTest, HandleAssignment) {
|
TEST_F(ShmTest, HandleAssignment) {
|
||||||
std::string name = generate_unique_name("handle_assign");
|
std::string name = generate_unique_name("handle_assign");
|
||||||
|
|
||||||
handle h1(name.c_str(), 768);
|
handle h1(name.c_str(), 768);
|
||||||
void* ptr1 = h1.get();
|
void* ptr1 = h1.get();
|
||||||
|
|
||||||
handle h2;
|
handle h2;
|
||||||
h2 = std::move(h1);
|
h2 = std::move(h1);
|
||||||
|
|
||||||
EXPECT_TRUE(h2.valid());
|
EXPECT_TRUE(h2.valid());
|
||||||
EXPECT_EQ(h2.get(), ptr1);
|
EXPECT_EQ(h2.get(), ptr1);
|
||||||
EXPECT_FALSE(h1.valid());
|
EXPECT_FALSE(h1.valid());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test handle valid() method
|
// Test handle valid() method
|
||||||
TEST_F(ShmTest, HandleValid) {
|
TEST_F(ShmTest, HandleValid) {
|
||||||
handle h1;
|
handle h1;
|
||||||
EXPECT_FALSE(h1.valid());
|
EXPECT_FALSE(h1.valid());
|
||||||
|
|
||||||
std::string name = generate_unique_name("handle_valid");
|
std::string name = generate_unique_name("handle_valid");
|
||||||
handle h2(name.c_str(), 128);
|
handle h2(name.c_str(), 128);
|
||||||
EXPECT_TRUE(h2.valid());
|
EXPECT_TRUE(h2.valid());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test handle size() method
|
// Test handle size() method
|
||||||
TEST_F(ShmTest, HandleSize) {
|
TEST_F(ShmTest, HandleSize) {
|
||||||
std::string name = generate_unique_name("handle_size");
|
std::string name = generate_unique_name("handle_size");
|
||||||
const std::size_t requested_size = 2048;
|
const std::size_t requested_size = 2048;
|
||||||
|
|
||||||
handle h(name.c_str(), requested_size);
|
handle h(name.c_str(), requested_size);
|
||||||
|
|
||||||
EXPECT_GE(h.size(), requested_size);
|
EXPECT_GE(h.size(), requested_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test handle name() method
|
// Test handle name() method
|
||||||
TEST_F(ShmTest, HandleName) {
|
TEST_F(ShmTest, HandleName) {
|
||||||
std::string name = generate_unique_name("handle_name");
|
std::string name = generate_unique_name("handle_name");
|
||||||
|
|
||||||
handle h(name.c_str(), 256);
|
handle h(name.c_str(), 256);
|
||||||
|
|
||||||
EXPECT_STREQ(h.name(), name.c_str());
|
EXPECT_STREQ(h.name(), name.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test handle ref() method
|
// Test handle ref() method
|
||||||
TEST_F(ShmTest, HandleRef) {
|
TEST_F(ShmTest, HandleRef) {
|
||||||
std::string name = generate_unique_name("handle_ref");
|
std::string name = generate_unique_name("handle_ref");
|
||||||
|
|
||||||
handle h(name.c_str(), 256);
|
handle h(name.c_str(), 256);
|
||||||
|
|
||||||
std::int32_t ref = h.ref();
|
std::int32_t ref = h.ref();
|
||||||
EXPECT_GT(ref, 0);
|
EXPECT_GT(ref, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test handle sub_ref() method
|
// Test handle sub_ref() method
|
||||||
TEST_F(ShmTest, HandleSubRef) {
|
TEST_F(ShmTest, HandleSubRef) {
|
||||||
std::string name = generate_unique_name("handle_sub_ref");
|
std::string name = generate_unique_name("handle_sub_ref");
|
||||||
|
|
||||||
handle h(name.c_str(), 256);
|
handle h(name.c_str(), 256);
|
||||||
|
|
||||||
std::int32_t ref_before = h.ref();
|
std::int32_t ref_before = h.ref();
|
||||||
h.sub_ref();
|
h.sub_ref();
|
||||||
std::int32_t ref_after = h.ref();
|
std::int32_t ref_after = h.ref();
|
||||||
|
|
||||||
EXPECT_EQ(ref_after, ref_before - 1);
|
EXPECT_EQ(ref_after, ref_before - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test handle acquire() method
|
// Test handle acquire() method
|
||||||
TEST_F(ShmTest, HandleAcquire) {
|
TEST_F(ShmTest, HandleAcquire) {
|
||||||
handle h;
|
handle h;
|
||||||
EXPECT_FALSE(h.valid());
|
EXPECT_FALSE(h.valid());
|
||||||
|
|
||||||
std::string name = generate_unique_name("handle_acquire");
|
std::string name = generate_unique_name("handle_acquire");
|
||||||
bool result = h.acquire(name.c_str(), 512);
|
bool result = h.acquire(name.c_str(), 512);
|
||||||
|
|
||||||
EXPECT_TRUE(result);
|
EXPECT_TRUE(result);
|
||||||
EXPECT_TRUE(h.valid());
|
EXPECT_TRUE(h.valid());
|
||||||
EXPECT_GE(h.size(), 512u);
|
EXPECT_GE(h.size(), 512u);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test handle release() method
|
// Test handle release() method
|
||||||
TEST_F(ShmTest, HandleRelease) {
|
TEST_F(ShmTest, HandleRelease) {
|
||||||
std::string name = generate_unique_name("handle_release");
|
std::string name = generate_unique_name("handle_release");
|
||||||
|
|
||||||
handle h(name.c_str(), 256);
|
handle h(name.c_str(), 256);
|
||||||
ASSERT_TRUE(h.valid());
|
ASSERT_TRUE(h.valid());
|
||||||
|
|
||||||
std::int32_t ref_count = h.release();
|
std::int32_t ref_count = h.release();
|
||||||
EXPECT_GE(ref_count, 0);
|
EXPECT_GE(ref_count, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test handle clear() method
|
// Test handle clear() method
|
||||||
TEST_F(ShmTest, HandleClear) {
|
TEST_F(ShmTest, HandleClear) {
|
||||||
std::string name = generate_unique_name("handle_clear");
|
std::string name = generate_unique_name("handle_clear");
|
||||||
|
|
||||||
handle h(name.c_str(), 256);
|
handle h(name.c_str(), 256);
|
||||||
ASSERT_TRUE(h.valid());
|
ASSERT_TRUE(h.valid());
|
||||||
|
|
||||||
h.clear();
|
h.clear();
|
||||||
EXPECT_FALSE(h.valid());
|
EXPECT_FALSE(h.valid());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test handle clear_storage() static method
|
// Test handle clear_storage() static method
|
||||||
TEST_F(ShmTest, HandleClearStorage) {
|
TEST_F(ShmTest, HandleClearStorage) {
|
||||||
std::string name = generate_unique_name("handle_clear_storage");
|
std::string name = generate_unique_name("handle_clear_storage");
|
||||||
|
|
||||||
{
|
{
|
||||||
handle h(name.c_str(), 256);
|
handle h(name.c_str(), 256);
|
||||||
EXPECT_TRUE(h.valid());
|
EXPECT_TRUE(h.valid());
|
||||||
}
|
}
|
||||||
|
|
||||||
handle::clear_storage(name.c_str());
|
handle::clear_storage(name.c_str());
|
||||||
|
|
||||||
// Try to open - should fail or create new
|
// Try to open - should fail or create new
|
||||||
handle h2(name.c_str(), 256, open);
|
handle h2(name.c_str(), 256, open);
|
||||||
// Behavior depends on implementation
|
// Behavior depends on implementation
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test handle get() method
|
// Test handle get() method
|
||||||
TEST_F(ShmTest, HandleGet) {
|
TEST_F(ShmTest, HandleGet) {
|
||||||
std::string name = generate_unique_name("handle_get");
|
std::string name = generate_unique_name("handle_get");
|
||||||
|
|
||||||
handle h(name.c_str(), 512);
|
handle h(name.c_str(), 512);
|
||||||
|
|
||||||
void* mem = h.get();
|
void* mem = h.get();
|
||||||
EXPECT_NE(mem, nullptr);
|
EXPECT_NE(mem, nullptr);
|
||||||
|
|
||||||
// Write and read test
|
// Write and read test
|
||||||
const char* test_str = "Handle get test";
|
const char* test_str = "Handle get test";
|
||||||
std::strcpy(static_cast<char*>(mem), test_str);
|
std::strcpy(static_cast<char*>(mem), test_str);
|
||||||
EXPECT_STREQ(static_cast<char*>(mem), test_str);
|
EXPECT_STREQ(static_cast<char*>(mem), test_str);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test handle detach() and attach() methods
|
// Test handle detach() and attach() methods
|
||||||
TEST_F(ShmTest, HandleDetachAttach) {
|
TEST_F(ShmTest, HandleDetachAttach) {
|
||||||
std::string name = generate_unique_name("handle_detach_attach");
|
std::string name = generate_unique_name("handle_detach_attach");
|
||||||
|
|
||||||
handle h1(name.c_str(), 256);
|
handle h1(name.c_str(), 256);
|
||||||
ASSERT_TRUE(h1.valid());
|
ASSERT_TRUE(h1.valid());
|
||||||
|
|
||||||
id_t id = h1.detach();
|
id_t id = h1.detach();
|
||||||
EXPECT_NE(id, nullptr);
|
EXPECT_NE(id, nullptr);
|
||||||
EXPECT_FALSE(h1.valid()); // Should be invalid after detach
|
EXPECT_FALSE(h1.valid()); // Should be invalid after detach
|
||||||
|
|
||||||
handle h2;
|
handle h2;
|
||||||
h2.attach(id);
|
h2.attach(id);
|
||||||
EXPECT_TRUE(h2.valid());
|
EXPECT_TRUE(h2.valid());
|
||||||
|
|
||||||
// Clean up
|
// Clean up
|
||||||
h2.release();
|
h2.release();
|
||||||
remove(id);
|
remove(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test writing and reading data through shared memory
|
// Test writing and reading data through shared memory
|
||||||
TEST_F(ShmTest, WriteReadData) {
|
TEST_F(ShmTest, WriteReadData) {
|
||||||
std::string name = generate_unique_name("write_read");
|
std::string name = generate_unique_name("write_read");
|
||||||
const std::size_t size = 1024;
|
const std::size_t size = 1024;
|
||||||
|
|
||||||
handle h1(name.c_str(), size);
|
handle h1(name.c_str(), size);
|
||||||
ASSERT_TRUE(h1.valid());
|
ASSERT_TRUE(h1.valid());
|
||||||
|
|
||||||
// Write test data
|
// Write test data
|
||||||
struct TestData {
|
struct TestData {
|
||||||
int value;
|
int value;
|
||||||
char text[64];
|
char text[64];
|
||||||
};
|
};
|
||||||
|
|
||||||
TestData* data1 = static_cast<TestData*>(h1.get());
|
TestData* data1 = static_cast<TestData*>(h1.get());
|
||||||
data1->value = 42;
|
data1->value = 42;
|
||||||
std::strcpy(data1->text, "Shared memory data");
|
std::strcpy(data1->text, "Shared memory data");
|
||||||
|
|
||||||
// Open in another "handle" (simulating different process)
|
// Open in another "handle" (simulating different process)
|
||||||
handle h2(name.c_str(), size, open);
|
handle h2(name.c_str(), size, open);
|
||||||
if (h2.valid()) {
|
if (h2.valid()) {
|
||||||
TestData* data2 = static_cast<TestData*>(h2.get());
|
TestData* data2 = static_cast<TestData*>(h2.get());
|
||||||
EXPECT_EQ(data2->value, 42);
|
EXPECT_EQ(data2->value, 42);
|
||||||
EXPECT_STREQ(data2->text, "Shared memory data");
|
EXPECT_STREQ(data2->text, "Shared memory data");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test handle with different modes
|
// Test handle with different modes
|
||||||
TEST_F(ShmTest, HandleModes) {
|
TEST_F(ShmTest, HandleModes) {
|
||||||
std::string name = generate_unique_name("handle_modes");
|
std::string name = generate_unique_name("handle_modes");
|
||||||
|
|
||||||
// Create only
|
// Create only
|
||||||
handle h1(name.c_str(), 256, create);
|
handle h1(name.c_str(), 256, create);
|
||||||
EXPECT_TRUE(h1.valid());
|
EXPECT_TRUE(h1.valid());
|
||||||
|
|
||||||
// Open existing
|
// Open existing
|
||||||
handle h2(name.c_str(), 256, open);
|
handle h2(name.c_str(), 256, open);
|
||||||
EXPECT_TRUE(h2.valid());
|
EXPECT_TRUE(h2.valid());
|
||||||
|
|
||||||
// Both modes
|
// Both modes
|
||||||
handle h3(name.c_str(), 256, create | open);
|
handle h3(name.c_str(), 256, create | open);
|
||||||
EXPECT_TRUE(h3.valid());
|
EXPECT_TRUE(h3.valid());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test multiple handles to same shared memory
|
// Test multiple handles to same shared memory
|
||||||
TEST_F(ShmTest, MultipleHandles) {
|
TEST_F(ShmTest, MultipleHandles) {
|
||||||
std::string name = generate_unique_name("multiple_handles");
|
std::string name = generate_unique_name("multiple_handles");
|
||||||
const std::size_t size = 512;
|
const std::size_t size = 512;
|
||||||
|
|
||||||
handle h1(name.c_str(), size);
|
handle h1(name.c_str(), size);
|
||||||
handle h2(name.c_str(), size, open);
|
handle h2(name.c_str(), size, open);
|
||||||
|
|
||||||
ASSERT_TRUE(h1.valid());
|
ASSERT_TRUE(h1.valid());
|
||||||
ASSERT_TRUE(h2.valid());
|
ASSERT_TRUE(h2.valid());
|
||||||
|
|
||||||
// Should point to same memory
|
// Should point to same memory
|
||||||
int* data1 = static_cast<int*>(h1.get());
|
int* data1 = static_cast<int*>(h1.get());
|
||||||
int* data2 = static_cast<int*>(h2.get());
|
int* data2 = static_cast<int*>(h2.get());
|
||||||
|
|
||||||
*data1 = 12345;
|
*data1 = 12345;
|
||||||
EXPECT_EQ(*data2, 12345);
|
EXPECT_EQ(*data2, 12345);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test large shared memory segment
|
// Test large shared memory segment
|
||||||
TEST_F(ShmTest, LargeSegment) {
|
TEST_F(ShmTest, LargeSegment) {
|
||||||
std::string name = generate_unique_name("large_segment");
|
std::string name = generate_unique_name("large_segment");
|
||||||
const std::size_t size = 10 * 1024 * 1024; // 10 MB
|
const std::size_t size = 10 * 1024 * 1024; // 10 MB
|
||||||
|
|
||||||
handle h(name.c_str(), size);
|
handle h(name.c_str(), size);
|
||||||
|
|
||||||
if (h.valid()) {
|
if (h.valid()) {
|
||||||
EXPECT_GE(h.size(), size);
|
EXPECT_GE(h.size(), size);
|
||||||
|
|
||||||
// Write pattern to a portion of memory
|
// Write pattern to a portion of memory
|
||||||
char* mem = static_cast<char*>(h.get());
|
char* mem = static_cast<char*>(h.get());
|
||||||
for (std::size_t i = 0; i < 1024; ++i) {
|
for (std::size_t i = 0; i < 1024; ++i) {
|
||||||
mem[i] = static_cast<char>(i % 256);
|
mem[i] = static_cast<char>(i % 256);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify pattern
|
// Verify pattern
|
||||||
for (std::size_t i = 0; i < 1024; ++i) {
|
for (std::size_t i = 0; i < 1024; ++i) {
|
||||||
EXPECT_EQ(mem[i], static_cast<char>(i % 256));
|
EXPECT_EQ(mem[i], static_cast<char>(i % 256));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user