mirror of
https://github.com/mutouyun/cpp-ipc.git
synced 2025-12-06 16:56:45 +08:00
- 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
369 lines
9.0 KiB
C++
369 lines
9.0 KiB
C++
/**
|
|
* @file test_buffer.cpp
|
|
* @brief Comprehensive unit tests for ipc::buffer class
|
|
*
|
|
* This test suite covers all public interfaces of the buffer class including:
|
|
* - Constructors (default, with pointer and destructor, from array, from char)
|
|
* - Move semantics
|
|
* - Copy operations through assignment
|
|
* - Basic operations (empty, data, size)
|
|
* - Conversion methods (to_tuple, to_vector, get<T>)
|
|
* - Comparison operators
|
|
*/
|
|
|
|
#include <gtest/gtest.h>
|
|
#include <cstring>
|
|
#include <vector>
|
|
#include "libipc/buffer.h"
|
|
|
|
using namespace ipc;
|
|
|
|
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<char*>(p);
|
|
}
|
|
};
|
|
int DestructorTracker::count = 0;
|
|
|
|
} // anonymous namespace
|
|
|
|
class BufferTest : public ::testing::Test {
|
|
protected:
|
|
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);
|
|
}
|
|
|
|
// 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<const char*>(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);
|
|
}
|
|
|
|
// 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);
|
|
}
|
|
|
|
// 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);
|
|
}
|
|
|
|
// 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<const byte_t*>();
|
|
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<const char*>(), '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);
|
|
}
|
|
|
|
// 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);
|
|
}
|
|
|
|
// 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());
|
|
}
|
|
|
|
// 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());
|
|
}
|
|
|
|
// 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 char*>(const_data), test_str);
|
|
}
|
|
|
|
// Test get<T>() 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<int*>(p);
|
|
});
|
|
|
|
int* retrieved = buf.get<int*>();
|
|
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);
|
|
}
|
|
|
|
// 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);
|
|
}
|
|
|
|
// 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<byte_t> 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);
|
|
}
|
|
|
|
// 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);
|
|
}
|
|
|
|
// 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);
|
|
}
|
|
|
|
// Test empty buffers comparison
|
|
TEST_F(BufferTest, EmptyBuffersComparison) {
|
|
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<char>(i % 256);
|
|
}
|
|
|
|
buffer buf(large_data, large_size, [](void* p, std::size_t) {
|
|
delete[] static_cast<char*>(p);
|
|
});
|
|
|
|
EXPECT_FALSE(buf.empty());
|
|
EXPECT_EQ(buf.size(), large_size);
|
|
|
|
// Verify pattern
|
|
const char* data_ptr = buf.get<const char*>();
|
|
for (std::size_t i = 0; i < 100; ++i) { // Check first 100 bytes
|
|
EXPECT_EQ(data_ptr[i], static_cast<char>(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());
|
|
}
|
|
|
|
// 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);
|
|
}
|