Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
ah-map-arena_test.cc
Go to the documentation of this file.
1
2/*
3 Aleph_w
4
5 Data structures & Algorithms
6 version 2.0.0b
7 https://github.com/lrleon/Aleph-w
8
9 This file is part of Aleph-w library
10
11 Copyright (c) 2002-2026 Leandro Rabindranath Leon
12
13 Permission is hereby granted, free of charge, to any person obtaining a copy
14 of this software and associated documentation files (the "Software"), to deal
15 in the Software without restriction, including without limitation the rights
16 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17 copies of the Software, and to permit persons to whom the Software is
18 furnished to do so, subject to the following conditions:
19
20 The above copyright notice and this permission notice shall be included in all
21 copies or substantial portions of the Software.
22
23 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29 SOFTWARE.
30*/
31
32
33
38#include <gtest/gtest.h>
39#include <cstring>
40#include <string>
41#include <vector>
42#include <filesystem>
43#include <ah-map-arena.H>
44
45#if !ALEPH_HAS_MAP_ARENA
46// MapArena is Linux-only (depends on `mremap` and `MAP_POPULATE`).
47// On other platforms the test binary still exists so CTest can list it,
48// but every test reports SKIPPED instead of failing the build.
49TEST(MapArena, NotAvailableOnThisPlatform)
50{
51 GTEST_SKIP()
52 << "MapArena requires Linux (mremap / MAP_POPULATE). "
53 "See ah-map-arena.H for the platform gate.";
54}
55
56int main(int argc, char ** argv)
57{
58 ::testing::InitGoogleTest(&argc, argv);
59 return RUN_ALL_TESTS();
60}
61
62#else // ALEPH_HAS_MAP_ARENA
63
64using namespace Aleph;
65namespace fs = std::filesystem;
66
67// =============================================================================
68// Test Fixtures
69// =============================================================================
70
71class MapArenaTest : public ::testing::Test
72{
73protected:
74 std::string test_file;
75
76 void SetUp() override
77 {
78 // Create a unique test file name
79 test_file = "/tmp/map_arena_test_" + std::to_string(getpid()) + ".dat";
80 // Ensure file doesn't exist
81 fs::remove(test_file);
82 }
83
84 void TearDown() override
85 {
86 // Clean up test file
87 fs::remove(test_file);
88 }
89};
90
91// =============================================================================
92// Construction Tests
93// =============================================================================
94
95TEST_F(MapArenaTest, DefaultConstruction)
96{
97 MapArena arena;
98 EXPECT_FALSE(arena.is_initialized());
99 EXPECT_EQ(arena.mapped_addr(), nullptr);
100 EXPECT_EQ(arena.file_descriptor(), -1);
101}
102
103TEST_F(MapArenaTest, ConstructWithFile)
104{
105 MapArena arena(test_file);
106
107 EXPECT_TRUE(arena.is_initialized());
108 EXPECT_NE(arena.mapped_addr(), nullptr);
109 EXPECT_GE(arena.file_descriptor(), 0);
110 EXPECT_EQ(arena.size(), 0u);
111 EXPECT_EQ(arena.capacity(), MapArena::initial_rgn_size);
112 EXPECT_TRUE(fs::exists(test_file));
113}
114
115TEST_F(MapArenaTest, InitMethod)
116{
117 MapArena arena;
118 arena.init(test_file);
119
120 EXPECT_TRUE(arena.is_initialized());
121 EXPECT_NE(arena.mapped_addr(), nullptr);
122 EXPECT_EQ(arena.size(), 0u);
123}
124
125TEST_F(MapArenaTest, InitAndErase)
126{
127 // First create an arena with some data
128 {
129 MapArena arena(test_file);
130 char * ptr = arena.reserve(100);
131 ASSERT_NE(ptr, nullptr);
132 std::memcpy(ptr, "test data", 10);
133 arena.commit(10);
134 arena.sync();
135 }
136
137 // Now init_and_erase should clear it
138 MapArena arena;
139 arena.init_and_erase(test_file);
140
141 EXPECT_TRUE(arena.is_initialized());
142 EXPECT_EQ(arena.size(), 0u);
143 EXPECT_TRUE(arena.empty());
144}
145
146// =============================================================================
147// Move Semantics Tests
148// =============================================================================
149
150TEST_F(MapArenaTest, MoveConstruction)
151{
152 MapArena original(test_file);
153 char * ptr = original.reserve(50);
154 ASSERT_NE(ptr, nullptr);
155 std::memcpy(ptr, "hello", 6);
156 original.commit(6);
157
158 void * orig_addr = original.mapped_addr();
159
160 MapArena moved(std::move(original));
161
162 // Moved-to arena should have the resources
163 EXPECT_TRUE(moved.is_initialized());
164 EXPECT_EQ(moved.mapped_addr(), orig_addr);
165 EXPECT_EQ(moved.size(), 6u);
166
167 // Original should be empty
168 EXPECT_FALSE(original.is_initialized());
169 EXPECT_EQ(original.mapped_addr(), nullptr);
170 EXPECT_EQ(original.file_descriptor(), -1);
171}
172
173TEST_F(MapArenaTest, MoveAssignment)
174{
175 MapArena original(test_file);
176 char * ptr = original.reserve(50);
177 ASSERT_NE(ptr, nullptr);
178 std::memcpy(ptr, "data", 5);
179 original.commit(5);
180
181 MapArena target;
182 target = std::move(original);
183
184 EXPECT_TRUE(target.is_initialized());
185 EXPECT_EQ(target.size(), 5u);
186 EXPECT_FALSE(original.is_initialized());
187}
188
189TEST_F(MapArenaTest, MoveAssignmentRoundtrip)
190{
191 MapArena arena(test_file);
192 arena.reserve(10);
193 arena.commit(10);
194
195 MapArena tmp;
196 tmp = std::move(arena);
197 arena = std::move(tmp);
198
199 // Should still be valid after roundtrip move
200 EXPECT_TRUE(arena.is_initialized());
201 EXPECT_EQ(arena.size(), 10u);
202}
203
204// =============================================================================
205// Reserve and Commit Tests
206// =============================================================================
207
208TEST_F(MapArenaTest, BasicReserveCommit)
209{
210 MapArena arena(test_file);
211
212 char * ptr = arena.reserve(100);
213 ASSERT_NE(ptr, nullptr);
214 EXPECT_EQ(arena.size(), 0u); // Not committed yet
215
216 arena.commit(100);
217 EXPECT_EQ(arena.size(), 100u);
218}
219
220TEST_F(MapArenaTest, MultipleReserveCommit)
221{
222 MapArena arena(test_file);
223
224 for (int i = 0; i < 10; ++i)
225 {
226 char * ptr = arena.reserve(50);
227 ASSERT_NE(ptr, nullptr);
228 arena.commit(50);
229 }
230
231 EXPECT_EQ(arena.size(), 500u);
232}
233
234TEST_F(MapArenaTest, PartialCommit)
235{
236 MapArena arena(test_file);
237
238 char * ptr = arena.reserve(1000);
239 ASSERT_NE(ptr, nullptr);
240
241 // Only commit part of what was reserved
242 arena.commit(500);
243 EXPECT_EQ(arena.size(), 500u);
244}
245
246TEST_F(MapArenaTest, ReserveWithData)
247{
248 MapArena arena(test_file);
249
250 const char * message = "Hello, MapArena!";
251 size_t len = std::strlen(message) + 1;
252
253 char * ptr = arena.reserve(len);
254 ASSERT_NE(ptr, nullptr);
255
256 std::memcpy(ptr, message, len);
257 arena.commit(len);
258
259 // Verify data
260 EXPECT_STREQ(arena.base(), message);
261}
262
263TEST_F(MapArenaTest, ReserveTriggersRemap)
264{
265 MapArena arena(test_file);
266
267 size_t initial_capacity = arena.capacity();
268
269 // Reserve more than initial capacity
270 char * ptr = arena.reserve(initial_capacity + 1000);
271 ASSERT_NE(ptr, nullptr);
272
273 EXPECT_GT(arena.capacity(), initial_capacity);
274 arena.commit(initial_capacity + 1000);
275 EXPECT_EQ(arena.size(), initial_capacity + 1000);
276}
277
278TEST_F(MapArenaTest, LargeReservation)
279{
280 MapArena arena(test_file);
281
282 // Reserve 1 MB
283 size_t large_size = 1024 * 1024;
284 char * ptr = arena.reserve(large_size);
285 ASSERT_NE(ptr, nullptr);
286
287 // Write pattern
288 std::memset(ptr, 'X', large_size);
289 arena.commit(large_size);
290
291 EXPECT_EQ(arena.size(), large_size);
292 EXPECT_GE(arena.capacity(), large_size);
293}
294
295// =============================================================================
296// Iterator Tests
297// =============================================================================
298
299TEST_F(MapArenaTest, IteratorOnEmpty)
300{
301 MapArena arena(test_file);
302
303 EXPECT_EQ(arena.begin(), arena.end());
304}
305
306TEST_F(MapArenaTest, IteratorTraversal)
307{
308 MapArena arena(test_file);
309
310 const char * data = "ABCDEFGHIJ";
311 char * ptr = arena.reserve(10);
312 ASSERT_NE(ptr, nullptr);
313 std::memcpy(ptr, data, 10);
314 arena.commit(10);
315
316 // Traverse with iterators
317 int i = 0;
318 for (auto it = arena.begin(); it != arena.end(); ++it, ++i)
319 EXPECT_EQ(*it, data[i]);
320
321 EXPECT_EQ(i, 10);
322}
323
324TEST_F(MapArenaTest, ConstIterators)
325{
326 MapArena arena(test_file);
327 char * ptr = arena.reserve(5);
328 std::memcpy(ptr, "test", 5);
329 arena.commit(5);
330
331 const MapArena & const_arena = arena;
332 EXPECT_EQ(const_arena.end() - const_arena.begin(), 5);
333}
334
335TEST_F(MapArenaTest, RangeBasedFor)
336{
337 MapArena arena(test_file);
338
339 char * ptr = arena.reserve(26);
340 for (int i = 0; i < 26; ++i)
341 ptr[i] = 'a' + i;
342 arena.commit(26);
343
344 std::string result;
345 for (char c : arena)
346 result += c;
347
348 EXPECT_EQ(result, "abcdefghijklmnopqrstuvwxyz");
349}
350
351// =============================================================================
352// Size and Capacity Tests
353// =============================================================================
354
355TEST_F(MapArenaTest, InitialCapacity)
356{
357 MapArena arena(test_file);
358 EXPECT_EQ(arena.capacity(), MapArena::initial_rgn_size);
359}
360
361TEST_F(MapArenaTest, AvailableMemory)
362{
363 MapArena arena(test_file);
364
365 EXPECT_EQ(arena.avail(), arena.capacity());
366
367 arena.reserve(100);
368 arena.commit(100);
369
370 EXPECT_EQ(arena.avail(), arena.capacity() - 100);
371}
372
373TEST_F(MapArenaTest, SizeAfterCommits)
374{
375 MapArena arena(test_file);
376
377 EXPECT_EQ(arena.size(), 0u);
378 EXPECT_TRUE(arena.empty());
379
380 arena.reserve(50);
381 arena.commit(50);
382 EXPECT_EQ(arena.size(), 50u);
383 EXPECT_FALSE(arena.empty());
384
385 arena.reserve(30);
386 arena.commit(30);
387 EXPECT_EQ(arena.size(), 80u);
388}
389
390// =============================================================================
391// Persistence Tests
392// =============================================================================
393
394TEST_F(MapArenaTest, DataPersistsAcrossInstances)
395{
396 const char * message = "Persistent data!";
397 size_t len = std::strlen(message) + 1;
398
399 // Write data
400 {
401 MapArena arena(test_file);
402 char * ptr = arena.reserve(len);
403 ASSERT_NE(ptr, nullptr);
404 std::memcpy(ptr, message, len);
405 arena.commit(len);
406 arena.sync();
407 }
408
409 // Read data in new instance
410 {
411 MapArena arena(test_file);
412 // Note: Recovery reads end_ from file start, but our simple version
413 // doesn't store it. This test verifies basic file creation.
414 EXPECT_TRUE(arena.is_initialized());
415 }
416}
417
418TEST_F(MapArenaTest, SyncMethod)
419{
420 MapArena arena(test_file);
421
422 char * ptr = arena.reserve(100);
423 ASSERT_NE(ptr, nullptr);
424 std::memset(ptr, 'A', 100);
425 arena.commit(100);
426
427 // sync() should not throw
428 EXPECT_NO_THROW(arena.sync());
429}
430
431// =============================================================================
432// Query Method Tests
433// =============================================================================
434
435TEST_F(MapArenaTest, BaseMethod)
436{
437 MapArena arena(test_file);
438
439 EXPECT_EQ(arena.base(), arena.begin());
440 EXPECT_EQ(arena.base(), arena.mapped_addr());
441}
442
443TEST_F(MapArenaTest, EmptyMethod)
444{
445 MapArena arena(test_file);
446
447 EXPECT_TRUE(arena.empty());
448
449 arena.reserve(1);
450 arena.commit(1);
451
452 EXPECT_FALSE(arena.empty());
453}
454
455TEST_F(MapArenaTest, IsInitializedMethod)
456{
457 MapArena arena;
458 EXPECT_FALSE(arena.is_initialized());
459
460 arena.init(test_file);
461 EXPECT_TRUE(arena.is_initialized());
462}
463
464TEST_F(MapArenaTest, FileDescriptor)
465{
466 MapArena arena(test_file);
467 int fd = arena.file_descriptor();
468
469 EXPECT_GE(fd, 0);
470
471 // Verify it's a valid fd by checking with fcntl
472 int flags = fcntl(fd, F_GETFD);
473 EXPECT_NE(flags, -1);
474}
475
476// =============================================================================
477// Edge Cases
478// =============================================================================
479
480TEST_F(MapArenaTest, ZeroSizeReserve)
481{
482 MapArena arena(test_file);
483
484 char * ptr = arena.reserve(0);
485 EXPECT_NE(ptr, nullptr); // Should return current end pointer
486 arena.commit(0);
487 EXPECT_EQ(arena.size(), 0u);
488}
489
490TEST_F(MapArenaTest, MultipleRemapGrowth)
491{
492 MapArena arena(test_file);
493
494 size_t total = 0;
495 size_t allocation_size = MapArena::initial_rgn_size / 2;
496
497 // Force multiple remaps
498 for (int i = 0; i < 10; ++i)
499 {
500 char * ptr = arena.reserve(allocation_size);
501 ASSERT_NE(ptr, nullptr) << "Failed at iteration " << i;
502 arena.commit(allocation_size);
503 total += allocation_size;
504 }
505
506 EXPECT_EQ(arena.size(), total);
507 EXPECT_GE(arena.capacity(), total);
508}
509
510// =============================================================================
511// Output Operator Tests
512// =============================================================================
513
514TEST_F(MapArenaTest, OutputOperator)
515{
516 MapArena arena(test_file);
517 arena.reserve(100);
518 arena.commit(100);
519
520 std::ostringstream oss;
521 oss << arena;
522
523 std::string output = oss.str();
524 EXPECT_NE(output.find("MapArena"), std::string::npos);
525 EXPECT_NE(output.find("size"), std::string::npos);
526 EXPECT_NE(output.find("capacity"), std::string::npos);
527}
528
529// =============================================================================
530// Destructor Safety Tests
531// =============================================================================
532
533TEST_F(MapArenaTest, DestructorOnUninitialized)
534{
535 // Should not crash when destroying uninitialized arena
536 MapArena arena;
537 // Destructor called automatically
538}
539
540TEST_F(MapArenaTest, DestructorAfterMove)
541{
542 MapArena arena(test_file);
543 MapArena other = std::move(arena);
544 // arena destructor should safely handle moved-from state
545}
546
547// =============================================================================
548// Stress Tests
549// =============================================================================
550
551TEST_F(MapArenaTest, ManySmallAllocations)
552{
553 MapArena arena(test_file);
554
555 constexpr int N = 10000;
556 for (int i = 0; i < N; ++i)
557 {
558 char * ptr = arena.reserve(8);
559 ASSERT_NE(ptr, nullptr) << "Failed at allocation " << i;
560 arena.commit(8);
561 }
562
563 EXPECT_EQ(arena.size(), N * 8u);
564}
565
566TEST_F(MapArenaTest, AlternatingAllocations)
567{
568 MapArena arena(test_file);
569
570 size_t total = 0;
571 for (int i = 0; i < 100; ++i)
572 {
573 size_t sz = (i % 2 == 0) ? 16 : 256;
574 char * ptr = arena.reserve(sz);
575 ASSERT_NE(ptr, nullptr);
576 arena.commit(sz);
577 total += sz;
578 }
579
580 EXPECT_EQ(arena.size(), total);
581}
582
583// =============================================================================
584// Type Alias Tests
585// =============================================================================
586
587TEST_F(MapArenaTest, TypeAliases)
588{
589 static_assert(std::is_same_v<MapArena::iterator, char*>);
590 static_assert(std::is_same_v<MapArena::const_iterator, const char*>);
591 static_assert(std::is_same_v<MapArena::size_type, size_t>);
592}
593
594// =============================================================================
595// Static Constants Tests
596// =============================================================================
597
598TEST(MapArenaConstants, InitialRegionSize)
599{
600 EXPECT_EQ(MapArena::initial_rgn_size, 4 * 1024u);
601}
602
603// =============================================================================
604// Main
605// =============================================================================
606
607int main(int argc, char ** argv)
608{
609 ::testing::InitGoogleTest(&argc, argv);
610 return RUN_ALL_TESTS();
611}
612
613#endif // ALEPH_HAS_MAP_ARENA
TEST_F(StaticArenaFixture, simple_fail)
Definition ah-arena.cc:59
Memory-mapped file arena allocator.
int main()
#define TEST(name)
#define N
Definition fib.C:294
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
void message(const char *file, int line, const char *format,...)
Print an informational message with file and line info.
Definition ahDefs.C:95
ofstream output
Definition writeHeap.C:215