cpp-ipc/test/test_buffer.cpp
木头云 3d743d57ac 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<T>)
- Test conversion methods (to_tuple, to_vector)
- Test comparison operators (==, !=)
- Test edge cases (empty buffers, large buffers, self-assignment)
- Verify destructor callback functionality
2025-11-30 04:10:07 +00:00

369 lines
9.5 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);
}