From 3d743d57acbf5303d4d15ed3ca61e27ad20d4714 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:10:07 +0000 Subject: [PATCH] test(buffer): add comprehensive unit tests for ipc::buffer - Test all constructors (default, with destructor, from array, from char) - Test move semantics and assignment operators - Test all accessor methods (data, size, empty, get) - Test conversion methods (to_tuple, to_vector) - Test comparison operators (==, !=) - Test edge cases (empty buffers, large buffers, self-assignment) - Verify destructor callback functionality --- test/test_buffer.cpp | 368 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 368 insertions(+) create mode 100644 test/test_buffer.cpp diff --git a/test/test_buffer.cpp b/test/test_buffer.cpp new file mode 100644 index 0000000..5903875 --- /dev/null +++ b/test/test_buffer.cpp @@ -0,0 +1,368 @@ +/** + * @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) + * - Comparison operators + */ + +#include +#include +#include +#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(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(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(); + 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'); +} + +// 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_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); +} + +// 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 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(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()); +} + +// 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); +}