summaryrefslogtreecommitdiffstats
path: root/gfx/tests/gtest/TestArena.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/tests/gtest/TestArena.cpp')
-rw-r--r--gfx/tests/gtest/TestArena.cpp185
1 files changed, 185 insertions, 0 deletions
diff --git a/gfx/tests/gtest/TestArena.cpp b/gfx/tests/gtest/TestArena.cpp
new file mode 100644
index 0000000000..7f9aad6592
--- /dev/null
+++ b/gfx/tests/gtest/TestArena.cpp
@@ -0,0 +1,185 @@
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+#include "gtest/gtest.h"
+#include "gmock/gmock.h"
+
+#include "mozilla/gfx/IterableArena.h"
+#include <string>
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+
+#ifdef A
+# undef A
+#endif
+
+#ifdef B
+# undef B
+#endif
+
+// to avoid having symbols that collide easily like A and B in the global
+// namespace
+namespace test_arena {
+
+class A;
+class B;
+
+class Base {
+ public:
+ virtual ~Base() = default;
+ virtual A* AsA() { return nullptr; }
+ virtual B* AsB() { return nullptr; }
+};
+
+static int sDtorItemA = 0;
+static int sDtorItemB = 0;
+
+class A : public Base {
+ public:
+ virtual A* AsA() override { return this; }
+
+ explicit A(uint64_t val) : mVal(val) {}
+ ~A() { ++sDtorItemA; }
+
+ uint64_t mVal;
+};
+
+class B : public Base {
+ public:
+ virtual B* AsB() override { return this; }
+
+ explicit B(const std::string& str) : mVal(str) {}
+ ~B() { ++sDtorItemB; }
+
+ std::string mVal;
+};
+
+struct BigStruct {
+ uint64_t mVal;
+ uint8_t data[120];
+
+ explicit BigStruct(uint64_t val) : mVal(val) {}
+};
+
+static void TestArenaAlloc(IterableArena::ArenaType aType) {
+ sDtorItemA = 0;
+ sDtorItemB = 0;
+ IterableArena arena(aType, 256);
+
+ // An empty arena has no items to iterate over.
+ {
+ int iterations = 0;
+ arena.ForEach([&](void* item) { iterations++; });
+ ASSERT_EQ(iterations, 0);
+ }
+
+ auto a1 = arena.Alloc<A>(42);
+ auto b1 = arena.Alloc<B>("Obladi oblada");
+ auto a2 = arena.Alloc<A>(1337);
+ auto b2 = arena.Alloc<B>("Yellow submarine");
+ auto b3 = arena.Alloc<B>("She's got a ticket to ride");
+
+ // Alloc returns a non-negative offset if the allocation succeeded.
+ ASSERT_TRUE(a1 >= 0);
+ ASSERT_TRUE(a2 >= 0);
+ ASSERT_TRUE(b1 >= 0);
+ ASSERT_TRUE(b2 >= 0);
+ ASSERT_TRUE(b3 >= 0);
+
+ ASSERT_TRUE(arena.GetStorage(a1) != nullptr);
+ ASSERT_TRUE(arena.GetStorage(a2) != nullptr);
+ ASSERT_TRUE(arena.GetStorage(b1) != nullptr);
+ ASSERT_TRUE(arena.GetStorage(b2) != nullptr);
+ ASSERT_TRUE(arena.GetStorage(b3) != nullptr);
+
+ ASSERT_TRUE(((Base*)arena.GetStorage(a1))->AsA() != nullptr);
+ ASSERT_TRUE(((Base*)arena.GetStorage(a2))->AsA() != nullptr);
+
+ ASSERT_TRUE(((Base*)arena.GetStorage(b1))->AsB() != nullptr);
+ ASSERT_TRUE(((Base*)arena.GetStorage(b2))->AsB() != nullptr);
+ ASSERT_TRUE(((Base*)arena.GetStorage(b3))->AsB() != nullptr);
+
+ ASSERT_EQ(((Base*)arena.GetStorage(a1))->AsA()->mVal, (uint64_t)42);
+ ASSERT_EQ(((Base*)arena.GetStorage(a2))->AsA()->mVal, (uint64_t)1337);
+
+ ASSERT_EQ(((Base*)arena.GetStorage(b1))->AsB()->mVal,
+ std::string("Obladi oblada"));
+ ASSERT_EQ(((Base*)arena.GetStorage(b2))->AsB()->mVal,
+ std::string("Yellow submarine"));
+ ASSERT_EQ(((Base*)arena.GetStorage(b3))->AsB()->mVal,
+ std::string("She's got a ticket to ride"));
+
+ {
+ int iterations = 0;
+ arena.ForEach([&](void* item) { iterations++; });
+ ASSERT_EQ(iterations, 5);
+ }
+
+ // Typically, running the destructors of the elements in the arena will is
+ // done manually like this:
+ arena.ForEach([](void* item) { ((Base*)item)->~Base(); });
+ arena.Clear();
+ ASSERT_EQ(sDtorItemA, 2);
+ ASSERT_EQ(sDtorItemB, 3);
+
+ // An empty arena has no items to iterate over (we just cleared it).
+ {
+ int iterations = 0;
+ arena.ForEach([&](void* item) { iterations++; });
+ ASSERT_EQ(iterations, 0);
+ }
+}
+
+static void TestArenaLimit(IterableArena::ArenaType aType,
+ bool aShouldReachLimit) {
+ IterableArena arena(aType, 128);
+
+ // A non-growable arena should return a negative offset when running out
+ // of space, without crashing.
+ // We should not run out of space with a growable arena (unless the os is
+ // running out of memory but this isn't expected for this test).
+ bool reachedLimit = false;
+ for (int i = 0; i < 100; ++i) {
+ auto offset = arena.Alloc<A>(42);
+ if (offset < 0) {
+ reachedLimit = true;
+ break;
+ }
+ }
+ ASSERT_EQ(reachedLimit, aShouldReachLimit);
+}
+
+} // namespace test_arena
+
+using namespace test_arena;
+
+TEST(Moz2D, FixedArena)
+{
+ TestArenaAlloc(IterableArena::FIXED_SIZE);
+ TestArenaLimit(IterableArena::FIXED_SIZE, true);
+}
+
+TEST(Moz2D, GrowableArena)
+{
+ TestArenaAlloc(IterableArena::GROWABLE);
+ TestArenaLimit(IterableArena::GROWABLE, false);
+
+ IterableArena arena(IterableArena::GROWABLE, 16);
+ // sizeof(BigStruct) is more than twice the initial capacity, make sure that
+ // this doesn't blow everything up, since the arena doubles its storage size
+ // each time it grows (until it finds a size that fits).
+ auto a = arena.Alloc<BigStruct>(1);
+ auto b = arena.Alloc<BigStruct>(2);
+ auto c = arena.Alloc<BigStruct>(3);
+
+ // Offsets should also still point to the appropriate values after
+ // reallocation.
+ ASSERT_EQ(((BigStruct*)arena.GetStorage(a))->mVal, (uint64_t)1);
+ ASSERT_EQ(((BigStruct*)arena.GetStorage(b))->mVal, (uint64_t)2);
+ ASSERT_EQ(((BigStruct*)arena.GetStorage(c))->mVal, (uint64_t)3);
+
+ arena.Clear();
+}