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