Skip to content

Commit 3d743d5

Browse files
committed
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
1 parent 17eaa57 commit 3d743d5

File tree

1 file changed

+368
-0
lines changed

1 file changed

+368
-0
lines changed

test/test_buffer.cpp

Lines changed: 368 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,368 @@
1+
/**
2+
* @file test_buffer.cpp
3+
* @brief Comprehensive unit tests for ipc::buffer class
4+
*
5+
* This test suite covers all public interfaces of the buffer class including:
6+
* - Constructors (default, with pointer and destructor, from array, from char)
7+
* - Move semantics
8+
* - Copy operations through assignment
9+
* - Basic operations (empty, data, size)
10+
* - Conversion methods (to_tuple, to_vector, get<T>)
11+
* - Comparison operators
12+
*/
13+
14+
#include <gtest/gtest.h>
15+
#include <cstring>
16+
#include <vector>
17+
#include "libipc/buffer.h"
18+
19+
using namespace ipc;
20+
21+
namespace {
22+
23+
// Custom destructor tracker for testing
24+
struct DestructorTracker {
25+
static int count;
26+
static void reset() { count = 0; }
27+
static void destructor(void* p, std::size_t) {
28+
++count;
29+
delete[] static_cast<char*>(p);
30+
}
31+
};
32+
int DestructorTracker::count = 0;
33+
34+
} // anonymous namespace
35+
36+
class BufferTest : public ::testing::Test {
37+
protected:
38+
void SetUp() override {
39+
DestructorTracker::reset();
40+
}
41+
};
42+
43+
// Test default constructor
44+
TEST_F(BufferTest, DefaultConstructor) {
45+
buffer buf;
46+
EXPECT_TRUE(buf.empty());
47+
EXPECT_EQ(buf.size(), 0u);
48+
EXPECT_EQ(buf.data(), nullptr);
49+
}
50+
51+
// Test constructor with pointer, size, and destructor
52+
TEST_F(BufferTest, ConstructorWithDestructor) {
53+
const char* test_data = "Hello, World!";
54+
std::size_t size = std::strlen(test_data) + 1;
55+
char* data = new char[size];
56+
std::strcpy(data, test_data);
57+
58+
buffer buf(data, size, DestructorTracker::destructor);
59+
60+
EXPECT_FALSE(buf.empty());
61+
EXPECT_EQ(buf.size(), size);
62+
EXPECT_NE(buf.data(), nullptr);
63+
EXPECT_STREQ(static_cast<const char*>(buf.data()), test_data);
64+
}
65+
66+
// Test destructor is called
67+
TEST_F(BufferTest, DestructorCalled) {
68+
{
69+
char* data = new char[100];
70+
buffer buf(data, 100, DestructorTracker::destructor);
71+
EXPECT_EQ(DestructorTracker::count, 0);
72+
}
73+
EXPECT_EQ(DestructorTracker::count, 1);
74+
}
75+
76+
// Test constructor with additional parameter
77+
TEST_F(BufferTest, ConstructorWithAdditional) {
78+
char* data = new char[50];
79+
int additional_value = 42;
80+
81+
buffer buf(data, 50, DestructorTracker::destructor, &additional_value);
82+
83+
EXPECT_FALSE(buf.empty());
84+
EXPECT_EQ(buf.size(), 50u);
85+
EXPECT_NE(buf.data(), nullptr);
86+
}
87+
88+
// Test constructor without destructor
89+
TEST_F(BufferTest, ConstructorWithoutDestructor) {
90+
char stack_data[20] = "Stack data";
91+
92+
buffer buf(stack_data, 20);
93+
94+
EXPECT_FALSE(buf.empty());
95+
EXPECT_EQ(buf.size(), 20u);
96+
EXPECT_EQ(buf.data(), stack_data);
97+
}
98+
99+
// Test constructor from byte array
100+
TEST_F(BufferTest, ConstructorFromByteArray) {
101+
byte_t data[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
102+
103+
buffer buf(data);
104+
105+
EXPECT_FALSE(buf.empty());
106+
EXPECT_EQ(buf.size(), 10u);
107+
108+
const byte_t* buf_data = buf.get<const byte_t*>();
109+
for (int i = 0; i < 10; ++i) {
110+
EXPECT_EQ(buf_data[i], i);
111+
}
112+
}
113+
114+
// Test constructor from single char
115+
TEST_F(BufferTest, ConstructorFromChar) {
116+
char c = 'X';
117+
118+
buffer buf(c);
119+
120+
EXPECT_FALSE(buf.empty());
121+
EXPECT_EQ(buf.size(), sizeof(char));
122+
EXPECT_EQ(*buf.get<const char*>(), 'X');
123+
}
124+
125+
// Test move constructor
126+
TEST_F(BufferTest, MoveConstructor) {
127+
char* data = new char[30];
128+
std::strcpy(data, "Move test");
129+
130+
buffer buf1(data, 30, DestructorTracker::destructor);
131+
void* original_ptr = buf1.data();
132+
std::size_t original_size = buf1.size();
133+
134+
buffer buf2(std::move(buf1));
135+
136+
// buf2 should have the original data
137+
EXPECT_EQ(buf2.data(), original_ptr);
138+
EXPECT_EQ(buf2.size(), original_size);
139+
EXPECT_FALSE(buf2.empty());
140+
141+
// buf1 should be empty after move
142+
EXPECT_TRUE(buf1.empty());
143+
EXPECT_EQ(buf1.size(), 0u);
144+
}
145+
146+
// Test swap
147+
TEST_F(BufferTest, Swap) {
148+
char* data1 = new char[20];
149+
char* data2 = new char[30];
150+
std::strcpy(data1, "Buffer 1");
151+
std::strcpy(data2, "Buffer 2");
152+
153+
buffer buf1(data1, 20, DestructorTracker::destructor);
154+
buffer buf2(data2, 30, DestructorTracker::destructor);
155+
156+
void* ptr1 = buf1.data();
157+
void* ptr2 = buf2.data();
158+
std::size_t size1 = buf1.size();
159+
std::size_t size2 = buf2.size();
160+
161+
buf1.swap(buf2);
162+
163+
EXPECT_EQ(buf1.data(), ptr2);
164+
EXPECT_EQ(buf1.size(), size2);
165+
EXPECT_EQ(buf2.data(), ptr1);
166+
EXPECT_EQ(buf2.size(), size1);
167+
}
168+
169+
// Test assignment operator (move semantics)
170+
TEST_F(BufferTest, AssignmentOperator) {
171+
char* data = new char[40];
172+
std::strcpy(data, "Assignment test");
173+
174+
buffer buf1(data, 40, DestructorTracker::destructor);
175+
void* original_ptr = buf1.data();
176+
177+
buffer buf2;
178+
buf2 = std::move(buf1);
179+
180+
EXPECT_EQ(buf2.data(), original_ptr);
181+
EXPECT_FALSE(buf2.empty());
182+
}
183+
184+
// Test empty() method
185+
TEST_F(BufferTest, EmptyMethod) {
186+
buffer buf1;
187+
EXPECT_TRUE(buf1.empty());
188+
189+
char* data = new char[10];
190+
buffer buf2(data, 10, DestructorTracker::destructor);
191+
EXPECT_FALSE(buf2.empty());
192+
}
193+
194+
// Test data() const method
195+
TEST_F(BufferTest, DataConstMethod) {
196+
const char* test_str = "Const data test";
197+
std::size_t size = std::strlen(test_str) + 1;
198+
char* data = new char[size];
199+
std::strcpy(data, test_str);
200+
201+
const buffer buf(data, size, DestructorTracker::destructor);
202+
203+
const void* const_data = buf.data();
204+
EXPECT_NE(const_data, nullptr);
205+
EXPECT_STREQ(static_cast<const char*>(const_data), test_str);
206+
}
207+
208+
// Test get<T>() template method
209+
TEST_F(BufferTest, GetTemplateMethod) {
210+
int* int_data = new int[5]{1, 2, 3, 4, 5};
211+
212+
buffer buf(int_data, 5 * sizeof(int), [](void* p, std::size_t) {
213+
delete[] static_cast<int*>(p);
214+
});
215+
216+
int* retrieved = buf.get<int*>();
217+
EXPECT_NE(retrieved, nullptr);
218+
EXPECT_EQ(retrieved[0], 1);
219+
EXPECT_EQ(retrieved[4], 5);
220+
}
221+
222+
// Test to_tuple() non-const version
223+
TEST_F(BufferTest, ToTupleNonConst) {
224+
char* data = new char[25];
225+
std::strcpy(data, "Tuple test");
226+
227+
buffer buf(data, 25, DestructorTracker::destructor);
228+
229+
auto [ptr, size] = buf.to_tuple();
230+
EXPECT_EQ(ptr, buf.data());
231+
EXPECT_EQ(size, buf.size());
232+
EXPECT_EQ(size, 25u);
233+
}
234+
235+
// Test to_tuple() const version
236+
TEST_F(BufferTest, ToTupleConst) {
237+
char* data = new char[30];
238+
std::strcpy(data, "Const tuple");
239+
240+
const buffer buf(data, 30, DestructorTracker::destructor);
241+
242+
auto [ptr, size] = buf.to_tuple();
243+
EXPECT_EQ(ptr, buf.data());
244+
EXPECT_EQ(size, buf.size());
245+
EXPECT_EQ(size, 30u);
246+
}
247+
248+
// Test to_vector() method
249+
TEST_F(BufferTest, ToVector) {
250+
byte_t data_arr[5] = {10, 20, 30, 40, 50};
251+
252+
buffer buf(data_arr, 5);
253+
254+
std::vector<byte_t> vec = buf.to_vector();
255+
ASSERT_EQ(vec.size(), 5u);
256+
EXPECT_EQ(vec[0], 10);
257+
EXPECT_EQ(vec[1], 20);
258+
EXPECT_EQ(vec[2], 30);
259+
EXPECT_EQ(vec[3], 40);
260+
EXPECT_EQ(vec[4], 50);
261+
}
262+
263+
// Test equality operator
264+
TEST_F(BufferTest, EqualityOperator) {
265+
byte_t data1[5] = {1, 2, 3, 4, 5};
266+
byte_t data2[5] = {1, 2, 3, 4, 5};
267+
byte_t data3[5] = {5, 4, 3, 2, 1};
268+
269+
buffer buf1(data1, 5);
270+
buffer buf2(data2, 5);
271+
buffer buf3(data3, 5);
272+
273+
EXPECT_TRUE(buf1 == buf2);
274+
EXPECT_FALSE(buf1 == buf3);
275+
}
276+
277+
// Test inequality operator
278+
TEST_F(BufferTest, InequalityOperator) {
279+
byte_t data1[5] = {1, 2, 3, 4, 5};
280+
byte_t data2[5] = {1, 2, 3, 4, 5};
281+
byte_t data3[5] = {5, 4, 3, 2, 1};
282+
283+
buffer buf1(data1, 5);
284+
buffer buf2(data2, 5);
285+
buffer buf3(data3, 5);
286+
287+
EXPECT_FALSE(buf1 != buf2);
288+
EXPECT_TRUE(buf1 != buf3);
289+
}
290+
291+
// Test size mismatch in equality
292+
TEST_F(BufferTest, EqualityWithDifferentSizes) {
293+
byte_t data1[5] = {1, 2, 3, 4, 5};
294+
byte_t data2[3] = {1, 2, 3};
295+
296+
buffer buf1(data1, 5);
297+
buffer buf2(data2, 3);
298+
299+
EXPECT_FALSE(buf1 == buf2);
300+
EXPECT_TRUE(buf1 != buf2);
301+
}
302+
303+
// Test empty buffers comparison
304+
TEST_F(BufferTest, EmptyBuffersComparison) {
305+
buffer buf1;
306+
buffer buf2;
307+
308+
EXPECT_TRUE(buf1 == buf2);
309+
EXPECT_FALSE(buf1 != buf2);
310+
}
311+
312+
// Test large buffer
313+
TEST_F(BufferTest, LargeBuffer) {
314+
const std::size_t large_size = 1024 * 1024; // 1MB
315+
char* large_data = new char[large_size];
316+
317+
// Fill with pattern
318+
for (std::size_t i = 0; i < large_size; ++i) {
319+
large_data[i] = static_cast<char>(i % 256);
320+
}
321+
322+
buffer buf(large_data, large_size, [](void* p, std::size_t) {
323+
delete[] static_cast<char*>(p);
324+
});
325+
326+
EXPECT_FALSE(buf.empty());
327+
EXPECT_EQ(buf.size(), large_size);
328+
329+
// Verify pattern
330+
const char* data_ptr = buf.get<const char*>();
331+
for (std::size_t i = 0; i < 100; ++i) { // Check first 100 bytes
332+
EXPECT_EQ(data_ptr[i], static_cast<char>(i % 256));
333+
}
334+
}
335+
336+
// Test multiple move operations
337+
TEST_F(BufferTest, MultipleMoves) {
338+
char* data = new char[15];
339+
std::strcpy(data, "Multi-move");
340+
void* original_ptr = data;
341+
342+
buffer buf1(data, 15, DestructorTracker::destructor);
343+
buffer buf2(std::move(buf1));
344+
buffer buf3(std::move(buf2));
345+
buffer buf4(std::move(buf3));
346+
347+
EXPECT_EQ(buf4.data(), original_ptr);
348+
EXPECT_TRUE(buf1.empty());
349+
EXPECT_TRUE(buf2.empty());
350+
EXPECT_TRUE(buf3.empty());
351+
EXPECT_FALSE(buf4.empty());
352+
}
353+
354+
// Test self-assignment safety
355+
TEST_F(BufferTest, SelfAssignment) {
356+
char* data = new char[20];
357+
std::strcpy(data, "Self-assign");
358+
359+
buffer buf(data, 20, DestructorTracker::destructor);
360+
void* original_ptr = buf.data();
361+
std::size_t original_size = buf.size();
362+
363+
buf = std::move(buf); // Self-assignment
364+
365+
// Should remain valid
366+
EXPECT_EQ(buf.data(), original_ptr);
367+
EXPECT_EQ(buf.size(), original_size);
368+
}

0 commit comments

Comments
 (0)