TestArena.cpp (5043B)
1 /* vim:set ts=2 sw=2 sts=2 et: */ 2 /* Any copyright is dedicated to the Public Domain. 3 * http://creativecommons.org/publicdomain/zero/1.0/ 4 */ 5 6 #include "gtest/gtest.h" 7 #include "gmock/gmock.h" 8 9 #include "mozilla/gfx/IterableArena.h" 10 #include <string> 11 12 using namespace mozilla; 13 using namespace mozilla::gfx; 14 15 #ifdef A 16 # undef A 17 #endif 18 19 #ifdef B 20 # undef B 21 #endif 22 23 // to avoid having symbols that collide easily like A and B in the global 24 // namespace 25 namespace test_arena { 26 27 class A; 28 class B; 29 30 class Base { 31 public: 32 virtual ~Base() = default; 33 virtual A* AsA() { return nullptr; } 34 virtual B* AsB() { return nullptr; } 35 }; 36 37 static int sDtorItemA = 0; 38 static int sDtorItemB = 0; 39 40 class A : public Base { 41 public: 42 virtual A* AsA() override { return this; } 43 44 explicit A(uint64_t val) : mVal(val) {} 45 ~A() { ++sDtorItemA; } 46 47 uint64_t mVal; 48 }; 49 50 class B : public Base { 51 public: 52 virtual B* AsB() override { return this; } 53 54 explicit B(const std::string& str) : mVal(str) {} 55 ~B() { ++sDtorItemB; } 56 57 std::string mVal; 58 }; 59 60 struct BigStruct { 61 uint64_t mVal; 62 uint8_t data[120]; 63 64 explicit BigStruct(uint64_t val) : mVal(val) {} 65 }; 66 67 static void TestArenaAlloc(IterableArena::ArenaType aType) { 68 sDtorItemA = 0; 69 sDtorItemB = 0; 70 IterableArena arena(aType, 256); 71 72 // An empty arena has no items to iterate over. 73 { 74 int iterations = 0; 75 arena.ForEach([&](void* item) { iterations++; }); 76 ASSERT_EQ(iterations, 0); 77 } 78 79 auto a1 = arena.Alloc<A>(42); 80 auto b1 = arena.Alloc<B>("Obladi oblada"); 81 auto a2 = arena.Alloc<A>(1337); 82 auto b2 = arena.Alloc<B>("Yellow submarine"); 83 auto b3 = arena.Alloc<B>("She's got a ticket to ride"); 84 85 // Alloc returns a non-negative offset if the allocation succeeded. 86 ASSERT_TRUE(a1 >= 0); 87 ASSERT_TRUE(a2 >= 0); 88 ASSERT_TRUE(b1 >= 0); 89 ASSERT_TRUE(b2 >= 0); 90 ASSERT_TRUE(b3 >= 0); 91 92 ASSERT_TRUE(arena.GetStorage(a1) != nullptr); 93 ASSERT_TRUE(arena.GetStorage(a2) != nullptr); 94 ASSERT_TRUE(arena.GetStorage(b1) != nullptr); 95 ASSERT_TRUE(arena.GetStorage(b2) != nullptr); 96 ASSERT_TRUE(arena.GetStorage(b3) != nullptr); 97 98 ASSERT_TRUE(((Base*)arena.GetStorage(a1))->AsA() != nullptr); 99 ASSERT_TRUE(((Base*)arena.GetStorage(a2))->AsA() != nullptr); 100 101 ASSERT_TRUE(((Base*)arena.GetStorage(b1))->AsB() != nullptr); 102 ASSERT_TRUE(((Base*)arena.GetStorage(b2))->AsB() != nullptr); 103 ASSERT_TRUE(((Base*)arena.GetStorage(b3))->AsB() != nullptr); 104 105 ASSERT_EQ(((Base*)arena.GetStorage(a1))->AsA()->mVal, (uint64_t)42); 106 ASSERT_EQ(((Base*)arena.GetStorage(a2))->AsA()->mVal, (uint64_t)1337); 107 108 ASSERT_EQ(((Base*)arena.GetStorage(b1))->AsB()->mVal, 109 std::string("Obladi oblada")); 110 ASSERT_EQ(((Base*)arena.GetStorage(b2))->AsB()->mVal, 111 std::string("Yellow submarine")); 112 ASSERT_EQ(((Base*)arena.GetStorage(b3))->AsB()->mVal, 113 std::string("She's got a ticket to ride")); 114 115 { 116 int iterations = 0; 117 arena.ForEach([&](void* item) { iterations++; }); 118 ASSERT_EQ(iterations, 5); 119 } 120 121 // Typically, running the destructors of the elements in the arena will is 122 // done manually like this: 123 arena.ForEach([](void* item) { ((Base*)item)->~Base(); }); 124 arena.Clear(); 125 ASSERT_EQ(sDtorItemA, 2); 126 ASSERT_EQ(sDtorItemB, 3); 127 128 // An empty arena has no items to iterate over (we just cleared it). 129 { 130 int iterations = 0; 131 arena.ForEach([&](void* item) { iterations++; }); 132 ASSERT_EQ(iterations, 0); 133 } 134 } 135 136 static void TestArenaLimit(IterableArena::ArenaType aType, 137 bool aShouldReachLimit) { 138 IterableArena arena(aType, 128); 139 140 // A non-growable arena should return a negative offset when running out 141 // of space, without crashing. 142 // We should not run out of space with a growable arena (unless the os is 143 // running out of memory but this isn't expected for this test). 144 bool reachedLimit = false; 145 for (int i = 0; i < 100; ++i) { 146 auto offset = arena.Alloc<A>(42); 147 if (offset < 0) { 148 reachedLimit = true; 149 break; 150 } 151 } 152 ASSERT_EQ(reachedLimit, aShouldReachLimit); 153 } 154 155 } // namespace test_arena 156 157 using namespace test_arena; 158 159 TEST(Moz2D, FixedArena) 160 { 161 TestArenaAlloc(IterableArena::FIXED_SIZE); 162 TestArenaLimit(IterableArena::FIXED_SIZE, true); 163 } 164 165 TEST(Moz2D, GrowableArena) 166 { 167 TestArenaAlloc(IterableArena::GROWABLE); 168 TestArenaLimit(IterableArena::GROWABLE, false); 169 170 IterableArena arena(IterableArena::GROWABLE, 16); 171 // sizeof(BigStruct) is more than twice the initial capacity, make sure that 172 // this doesn't blow everything up, since the arena doubles its storage size 173 // each time it grows (until it finds a size that fits). 174 auto a = arena.Alloc<BigStruct>(1); 175 auto b = arena.Alloc<BigStruct>(2); 176 auto c = arena.Alloc<BigStruct>(3); 177 178 // Offsets should also still point to the appropriate values after 179 // reallocation. 180 ASSERT_EQ(((BigStruct*)arena.GetStorage(a))->mVal, (uint64_t)1); 181 ASSERT_EQ(((BigStruct*)arena.GetStorage(b))->mVal, (uint64_t)2); 182 ASSERT_EQ(((BigStruct*)arena.GetStorage(c))->mVal, (uint64_t)3); 183 184 arena.Clear(); 185 }