|
| 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