summaryrefslogtreecommitdiffstats
path: root/gfx/tests/gtest
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
commit2aa4a82499d4becd2284cdb482213d541b8804dd (patch)
treeb80bf8bf13c3766139fbacc530efd0dd9d54394c /gfx/tests/gtest
parentInitial commit. (diff)
downloadfirefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz
firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'gfx/tests/gtest')
-rw-r--r--gfx/tests/gtest/MockWidget.cpp8
-rw-r--r--gfx/tests/gtest/MockWidget.h94
-rw-r--r--gfx/tests/gtest/PolygonTestUtils.cpp163
-rw-r--r--gfx/tests/gtest/PolygonTestUtils.h40
-rw-r--r--gfx/tests/gtest/TestArena.cpp185
-rw-r--r--gfx/tests/gtest/TestArrayView.cpp20
-rw-r--r--gfx/tests/gtest/TestBSPTree.cpp694
-rw-r--r--gfx/tests/gtest/TestBufferRotation.cpp162
-rw-r--r--gfx/tests/gtest/TestColorNames.cpp91
-rw-r--r--gfx/tests/gtest/TestCompositor.cpp212
-rw-r--r--gfx/tests/gtest/TestConfigManager.cpp921
-rw-r--r--gfx/tests/gtest/TestGfxWidgets.cpp109
-rw-r--r--gfx/tests/gtest/TestLayers.cpp505
-rw-r--r--gfx/tests/gtest/TestLayers.h48
-rw-r--r--gfx/tests/gtest/TestMatrix.cpp145
-rw-r--r--gfx/tests/gtest/TestMoz2D.cpp41
-rw-r--r--gfx/tests/gtest/TestPolygon.cpp104
-rw-r--r--gfx/tests/gtest/TestQcms.cpp506
-rw-r--r--gfx/tests/gtest/TestRect.cpp645
-rw-r--r--gfx/tests/gtest/TestRegion.cpp1533
-rw-r--r--gfx/tests/gtest/TestSkipChars.cpp156
-rw-r--r--gfx/tests/gtest/TestSwizzle.cpp325
-rw-r--r--gfx/tests/gtest/TestTextureCompatibility.cpp125
-rw-r--r--gfx/tests/gtest/TestTextures.cpp307
-rw-r--r--gfx/tests/gtest/TestTreeTraversal.cpp1413
-rw-r--r--gfx/tests/gtest/TestVsync.cpp203
-rw-r--r--gfx/tests/gtest/TextureHelper.h161
-rw-r--r--gfx/tests/gtest/gfxSurfaceRefCountTest.cpp155
-rw-r--r--gfx/tests/gtest/moz.build91
29 files changed, 9162 insertions, 0 deletions
diff --git a/gfx/tests/gtest/MockWidget.cpp b/gfx/tests/gtest/MockWidget.cpp
new file mode 100644
index 0000000000..696845641e
--- /dev/null
+++ b/gfx/tests/gtest/MockWidget.cpp
@@ -0,0 +1,8 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#include "MockWidget.h"
+
+NS_IMPL_ISUPPORTS_INHERITED0(MockWidget, nsBaseWidget)
diff --git a/gfx/tests/gtest/MockWidget.h b/gfx/tests/gtest/MockWidget.h
new file mode 100644
index 0000000000..ae9a529a4c
--- /dev/null
+++ b/gfx/tests/gtest/MockWidget.h
@@ -0,0 +1,94 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GTEST_MOCKWIDGET_H
+#define GTEST_MOCKWIDGET_H
+
+#include "mozilla/gfx/Point.h"
+#include "mozilla/widget/InProcessCompositorWidget.h"
+#include "nsBaseWidget.h"
+#include "GLContext.h"
+#include "GLContextProvider.h"
+
+using mozilla::gl::CreateContextFlags;
+using mozilla::gl::GLContext;
+using mozilla::gl::GLContextProvider;
+
+using mozilla::gfx::IntSize;
+
+class MockWidget : public nsBaseWidget {
+ public:
+ MockWidget() : mCompWidth(0), mCompHeight(0) {}
+ MockWidget(int aWidth, int aHeight)
+ : mCompWidth(aWidth), mCompHeight(aHeight) {}
+ NS_DECL_ISUPPORTS_INHERITED
+
+ virtual LayoutDeviceIntRect GetClientBounds() override {
+ return LayoutDeviceIntRect(0, 0, mCompWidth, mCompHeight);
+ }
+ virtual LayoutDeviceIntRect GetBounds() override { return GetClientBounds(); }
+
+ void* GetNativeData(uint32_t aDataType) override {
+ if (aDataType == NS_NATIVE_OPENGL_CONTEXT) {
+ nsCString discardFailureId;
+ RefPtr<GLContext> context = GLContextProvider::CreateHeadless(
+ {CreateContextFlags::REQUIRE_COMPAT_PROFILE}, &discardFailureId);
+ if (!context) {
+ return nullptr;
+ }
+ if (!context->CreateOffscreenDefaultFb({mCompWidth, mCompHeight})) {
+ return nullptr;
+ }
+ return context.forget().take();
+ }
+ return nullptr;
+ }
+
+ virtual nsresult Create(nsIWidget* aParent, nsNativeWidget aNativeParent,
+ const LayoutDeviceIntRect& aRect,
+ nsWidgetInitData* aInitData = nullptr) override {
+ return NS_OK;
+ }
+ virtual nsresult Create(nsIWidget* aParent, nsNativeWidget aNativeParent,
+ const DesktopIntRect& aRect,
+ nsWidgetInitData* aInitData = nullptr) override {
+ return NS_OK;
+ }
+ virtual void Show(bool aState) override {}
+ virtual bool IsVisible() const override { return true; }
+ virtual void Move(double aX, double aY) override {}
+ virtual void Resize(double aWidth, double aHeight, bool aRepaint) override {}
+ virtual void Resize(double aX, double aY, double aWidth, double aHeight,
+ bool aRepaint) override {}
+
+ virtual void Enable(bool aState) override {}
+ virtual bool IsEnabled() const override { return true; }
+ virtual void SetFocus(Raise, mozilla::dom::CallerType aCallerType) override {}
+ virtual nsresult ConfigureChildren(
+ const nsTArray<Configuration>& aConfigurations) override {
+ return NS_OK;
+ }
+ virtual void Invalidate(const LayoutDeviceIntRect& aRect) override {}
+ virtual nsresult SetTitle(const nsAString& title) override { return NS_OK; }
+ virtual LayoutDeviceIntPoint WidgetToScreenOffset() override {
+ return LayoutDeviceIntPoint(0, 0);
+ }
+ virtual nsresult DispatchEvent(mozilla::WidgetGUIEvent* aEvent,
+ nsEventStatus& aStatus) override {
+ return NS_OK;
+ }
+ virtual void SetInputContext(const InputContext& aContext,
+ const InputContextAction& aAction) override {}
+ virtual InputContext GetInputContext() override { abort(); }
+
+ private:
+ ~MockWidget() = default;
+
+ int mCompWidth;
+ int mCompHeight;
+};
+
+#endif
diff --git a/gfx/tests/gtest/PolygonTestUtils.cpp b/gfx/tests/gtest/PolygonTestUtils.cpp
new file mode 100644
index 0000000000..04a2f47498
--- /dev/null
+++ b/gfx/tests/gtest/PolygonTestUtils.cpp
@@ -0,0 +1,163 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "PolygonTestUtils.h"
+
+#include <cmath>
+
+#include "Point.h"
+#include "Triangle.h"
+
+typedef mozilla::gfx::Polygon MozPolygon;
+
+using mozilla::gfx::Point;
+using mozilla::gfx::Point4D;
+using mozilla::gfx::Triangle;
+
+namespace mozilla {
+namespace gfx {
+
+const float kEpsilon = 0.001f;
+
+// Compares two points while allowing some numerical inaccuracy.
+bool FuzzyEquals(const Point4D& lhs, const Point4D& rhs) {
+ const auto d = lhs - rhs;
+
+ return std::abs(d.x) < kEpsilon && std::abs(d.y) < kEpsilon &&
+ std::abs(d.z) < kEpsilon && std::abs(d.w) < kEpsilon;
+}
+
+bool FuzzyEquals(const Point3D& lhs, const Point3D& rhs) {
+ const auto d = lhs - rhs;
+
+ return std::abs(d.x) < kEpsilon && std::abs(d.y) < kEpsilon &&
+ std::abs(d.z) < kEpsilon;
+}
+
+bool FuzzyEquals(const Point& lhs, const Point& rhs) {
+ const auto d = lhs - rhs;
+
+ return std::abs(d.x) < kEpsilon && std::abs(d.y) < kEpsilon;
+}
+
+bool operator==(const Triangle& lhs, const Triangle& rhs) {
+ return FuzzyEquals(lhs.p1, rhs.p1) && FuzzyEquals(lhs.p2, rhs.p2) &&
+ FuzzyEquals(lhs.p3, rhs.p3);
+}
+
+// Compares the points of two polygons and ensures
+// that the points are in the same winding order.
+bool operator==(const MozPolygon& lhs, const MozPolygon& rhs) {
+ const auto& left = lhs.GetPoints();
+ const auto& right = rhs.GetPoints();
+
+ // Polygons do not have the same amount of points.
+ if (left.Length() != right.Length()) {
+ return false;
+ }
+
+ const size_t pointCount = left.Length();
+
+ // Find the first vertex of the first polygon from the second polygon.
+ // This assumes that the polygons do not contain duplicate vertices.
+ int start = -1;
+ for (size_t i = 0; i < pointCount; ++i) {
+ if (FuzzyEquals(left[0], right[i])) {
+ start = i;
+ break;
+ }
+ }
+
+ // Found at least one different vertex.
+ if (start == -1) {
+ return false;
+ }
+
+ // Verify that the polygons have the same points.
+ for (size_t i = 0; i < pointCount; ++i) {
+ size_t j = (start + i) % pointCount;
+
+ if (!FuzzyEquals(left[i], right[j])) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+} // namespace gfx
+} // namespace mozilla
+
+TEST(PolygonTestUtils, TestSanity)
+{
+ EXPECT_TRUE(FuzzyEquals(Point4D(0.0f, 0.0f, 0.0f, 1.0f),
+ Point4D(0.0f, 0.0f, 0.0f, 1.0f)));
+
+ EXPECT_TRUE(FuzzyEquals(Point4D(0.0f, 0.0f, 0.0f, 1.0f),
+ Point4D(0.00001f, 0.00001f, 0.00001f, 1.0f)));
+
+ EXPECT_TRUE(FuzzyEquals(Point4D(0.00001f, 0.00001f, 0.00001f, 1.0f),
+ Point4D(0.0f, 0.0f, 0.0f, 1.0f)));
+
+ EXPECT_FALSE(FuzzyEquals(Point4D(0.0f, 0.0f, 0.0f, 1.0f),
+ Point4D(0.01f, 0.01f, 0.01f, 1.0f)));
+
+ EXPECT_FALSE(FuzzyEquals(Point4D(0.01f, 0.01f, 0.01f, 1.0f),
+ Point4D(0.0f, 0.0f, 0.0f, 1.0f)));
+
+ MozPolygon p1{
+ Point4D(0.0f, 0.0f, 1.0f, 1.0f), Point4D(1.0f, 0.0f, 1.0f, 1.0f),
+ Point4D(1.0f, 1.0f, 1.0f, 1.0f), Point4D(0.0f, 1.0f, 1.0f, 1.0f)};
+
+ // Same points as above shifted forward by one position.
+ MozPolygon shifted{
+ Point4D(0.0f, 1.0f, 1.0f, 1.0f), Point4D(0.0f, 0.0f, 1.0f, 1.0f),
+ Point4D(1.0f, 0.0f, 1.0f, 1.0f), Point4D(1.0f, 1.0f, 1.0f, 1.0f)};
+
+ MozPolygon p2{Point4D(0.00001f, 0.00001f, 1.00001f, 1.0f),
+ Point4D(1.00001f, 0.00001f, 1.00001f, 1.0f),
+ Point4D(1.00001f, 1.00001f, 1.00001f, 1.0f),
+ Point4D(0.00001f, 1.00001f, 1.00001f, 1.0f)};
+
+ MozPolygon p3{
+ Point4D(0.01f, 0.01f, 1.01f, 1.0f), Point4D(1.01f, 0.01f, 1.01f, 1.0f),
+ Point4D(1.01f, 1.01f, 1.01f, 1.0f), Point4D(0.01f, 1.01f, 1.01f, 1.0f)};
+
+ // Trivial equals
+ EXPECT_TRUE(p1 == p1);
+ EXPECT_TRUE(p2 == p2);
+ EXPECT_TRUE(p3 == p3);
+ EXPECT_TRUE(shifted == shifted);
+
+ // Polygons with the same point order
+ EXPECT_TRUE(p1 == p2);
+ EXPECT_TRUE(p1 == shifted);
+
+ // Polygons containing different points
+ EXPECT_FALSE(p1 == p3);
+ EXPECT_FALSE(p2 == p3);
+ EXPECT_FALSE(shifted == p3);
+
+ const nsTArray<Triangle> t1{
+ Triangle(Point(0.0f, 0.0f), Point(0.0f, 1.0f), Point(1.0f, 1.0f))};
+
+ const nsTArray<Triangle> t2{
+ Triangle(Point(0.0f, 0.0f), Point(1.0f, 1.0f), Point(1.0f, 0.0f))};
+
+ const nsTArray<Triangle> t3{Triangle(Point(0.00001f, 0.00001f),
+ Point(0.00001f, 1.00001f),
+ Point(1.00001f, 1.00001f))};
+
+ EXPECT_TRUE(t1[0] == t1[0]);
+ EXPECT_TRUE(t2[0] == t2[0]);
+ EXPECT_TRUE(t3[0] == t1[0]);
+
+ EXPECT_FALSE(t1[0] == t2[0]);
+ EXPECT_FALSE(t2[0] == t3[0]);
+
+ AssertArrayEQ(t1, t1);
+ AssertArrayEQ(t2, t2);
+}
diff --git a/gfx/tests/gtest/PolygonTestUtils.h b/gfx/tests/gtest/PolygonTestUtils.h
new file mode 100644
index 0000000000..e860b6a8d1
--- /dev/null
+++ b/gfx/tests/gtest/PolygonTestUtils.h
@@ -0,0 +1,40 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_TEST_POLYGONUTILS_H
+#define GFX_TEST_POLYGONUTILS_H
+
+#include "gtest/gtest.h"
+
+#include "nsTArray.h"
+#include "Point.h"
+#include "Polygon.h"
+#include "Triangle.h"
+
+namespace mozilla {
+namespace gfx {
+
+bool FuzzyEquals(const Point4D& lhs, const Point4D& rhs);
+bool FuzzyEquals(const Point3D& lhs, const Point3D& rhs);
+bool FuzzyEquals(const Point& lhs, const Point& rhs);
+
+bool operator==(const Triangle& lhs, const Triangle& rhs);
+bool operator==(const Polygon& lhs, const Polygon& rhs);
+
+// Compares two arrays with the equality operator.
+template <typename T>
+void AssertArrayEQ(const nsTArray<T>& rhs, const nsTArray<T>& lhs) {
+ ASSERT_EQ(lhs.Length(), rhs.Length());
+
+ for (size_t i = 0; i < lhs.Length(); ++i) {
+ EXPECT_TRUE(lhs[i] == rhs[i]);
+ }
+}
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* GFX_TEST_POLYGONUTILS_H */
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();
+}
diff --git a/gfx/tests/gtest/TestArrayView.cpp b/gfx/tests/gtest/TestArrayView.cpp
new file mode 100644
index 0000000000..209f286845
--- /dev/null
+++ b/gfx/tests/gtest/TestArrayView.cpp
@@ -0,0 +1,20 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#include <limits>
+
+#include "gtest/gtest.h"
+
+#include "mozilla/ArrayView.h"
+
+using namespace mozilla::gfx;
+
+TEST(Gfx, ArrayView)
+{
+ nsTArray<int> p = {5, 6};
+ ArrayView<int> pv(p);
+ ASSERT_EQ(pv[1], 6);
+ ASSERT_EQ(*pv.Data(), 5);
+}
diff --git a/gfx/tests/gtest/TestBSPTree.cpp b/gfx/tests/gtest/TestBSPTree.cpp
new file mode 100644
index 0000000000..b5c6cda5dd
--- /dev/null
+++ b/gfx/tests/gtest/TestBSPTree.cpp
@@ -0,0 +1,694 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gtest/gtest.h"
+
+#include "BSPTree.h"
+#include "Polygon.h"
+#include "PolygonTestUtils.h"
+
+#include <deque>
+#include <list>
+
+using namespace mozilla::gfx;
+using namespace mozilla::layers;
+typedef mozilla::gfx::Polygon MozPolygon;
+
+namespace {
+
+static void RunTest(std::deque<MozPolygon> aPolygons,
+ std::deque<MozPolygon> aExpected) {
+ std::list<LayerPolygon> layers;
+ for (MozPolygon& polygon : aPolygons) {
+ layers.push_back(LayerPolygon(nullptr, std::move(polygon)));
+ }
+
+ const BSPTree tree(layers);
+ const nsTArray<LayerPolygon> order = tree.GetDrawOrder();
+
+ EXPECT_EQ(aExpected.size(), order.Length());
+
+ for (size_t i = 0; i < order.Length(); ++i) {
+ EXPECT_TRUE(aExpected[i] == *order[i].geometry);
+ }
+}
+
+} // namespace
+
+TEST(BSPTree, SameNode)
+{
+ const std::deque<MozPolygon> polygons{
+ MozPolygon{
+ Point4D(0.0f, 0.0f, 0.0f, 1.0f), Point4D(1.0f, 0.0f, 0.0f, 1.0f),
+ Point4D(1.0f, 1.0f, 0.0f, 1.0f), Point4D(0.0f, 1.0f, 0.0f, 1.0f)},
+ MozPolygon{
+ Point4D(0.0f, 0.0f, 0.0f, 1.0f), Point4D(1.0f, 0.0f, 0.0f, 1.0f),
+ Point4D(1.0f, 1.0f, 0.0f, 1.0f), Point4D(0.0f, 1.0f, 0.0f, 1.0f)}};
+
+ ::RunTest(polygons, polygons);
+}
+
+TEST(BSPTree, OneChild)
+{
+ const MozPolygon p1{
+ Point4D(0.0f, 0.0f, 0.0f, 1.0f), Point4D(1.0f, 0.0f, 0.0f, 1.0f),
+ Point4D(1.0f, 1.0f, 0.0f, 1.0f), Point4D(0.0f, 1.0f, 0.0f, 1.0f)};
+
+ const MozPolygon p2{
+ Point4D(0.0f, 0.0f, 1.0f, 1.0f), Point4D(1.0f, 0.0f, 1.0f, 1.0f),
+ Point4D(1.0f, 1.0f, 1.0f, 1.0f), Point4D(0.0f, 1.0f, 1.0f, 1.0f)};
+
+ ::RunTest({p1, p2}, {p1, p2});
+ ::RunTest({p2, p1}, {p1, p2});
+}
+
+TEST(BSPTree, SharedEdge1)
+{
+ MozPolygon p1{
+ Point4D(1.0f, 0.0f, 1.0f, 1.0f), Point4D(0.0f, 0.0f, 1.0f, 1.0f),
+ Point4D(0.0f, 1.0f, 1.0f, 1.0f), Point4D(1.0f, 1.0f, 1.0f, 1.0f)};
+
+ MozPolygon p2{
+ Point4D(1.0f, 0.0f, 1.0f, 1.0f), Point4D(1.0f, 1.0f, 1.0f, 1.0f),
+ Point4D(2.0f, 2.0f, 1.0f, 1.0f), Point4D(2.0f, 0.0f, 1.0f, 1.0f)};
+
+ ::RunTest({p1, p2}, {p1, p2});
+}
+
+TEST(BSPTree, SharedEdge2)
+{
+ MozPolygon p1{
+ Point4D(1.0f, 0.0f, 1.0f, 1.0f), Point4D(0.0f, 0.0f, 1.0f, 1.0f),
+ Point4D(0.0f, 1.0f, 1.0f, 1.0f), Point4D(1.0f, 1.0f, 1.0f, 1.0f)};
+
+ MozPolygon p2{
+ Point4D(1.0f, 0.0f, 1.0f, 1.0f), Point4D(1.0f, 1.0f, 1.0f, 1.0f),
+ Point4D(2.0f, 2.0f, 0.0f, 1.0f), Point4D(2.0f, 0.0f, 0.0f, 1.0f)};
+
+ ::RunTest({p1, p2}, {p2, p1});
+}
+
+TEST(BSPTree, SplitSharedEdge)
+{
+ MozPolygon p1{
+ Point4D(1.0f, 0.0f, 1.0f, 1.0f), Point4D(0.0f, 0.0f, 1.0f, 1.0f),
+ Point4D(0.0f, 1.0f, 1.0f, 1.0f), Point4D(1.0f, 1.0f, 1.0f, 1.0f)};
+
+ MozPolygon p2{
+ Point4D(1.0f, 0.0f, 2.0f, 1.0f), Point4D(1.0f, 1.0f, 2.0f, 1.0f),
+ Point4D(1.0f, 1.0f, 0.0f, 1.0f), Point4D(1.0f, 0.0f, 0.0f, 1.0f)};
+
+ const std::deque<MozPolygon> expected{
+ MozPolygon{
+ Point4D(1.0f, 1.0f, 1.0f, 1.0f), Point4D(1.0f, 1.0f, 0.0f, 1.0f),
+ Point4D(1.0f, 0.0f, 0.0f, 1.0f), Point4D(1.0f, 0.0f, 1.0f, 1.0f)},
+ MozPolygon{
+ Point4D(1.0f, 0.0f, 1.0f, 1.0f), Point4D(0.0f, 0.0f, 1.0f, 1.0f),
+ Point4D(0.0f, 1.0f, 1.0f, 1.0f), Point4D(1.0f, 1.0f, 1.0f, 1.0f)},
+ MozPolygon{
+ Point4D(1.0f, 0.0f, 2.0f, 1.0f), Point4D(1.0f, 1.0f, 2.0f, 1.0f),
+ Point4D(1.0f, 1.0f, 1.0f, 1.0f), Point4D(1.0f, 0.0f, 1.0f, 1.0f)}};
+
+ ::RunTest({p1, p2}, expected);
+}
+
+TEST(BSPTree, SplitSimple1)
+{
+ MozPolygon p1{
+ Point4D(0.0f, 0.0f, 1.0f, 1.0f), Point4D(1.0f, 0.0f, 1.0f, 1.0f),
+ Point4D(1.0f, 1.0f, 1.0f, 1.0f), Point4D(0.0f, 1.0f, 1.0f, 1.0f)};
+
+ MozPolygon p2{
+ Point4D(0.0f, 0.0f, 2.0f, 1.0f), Point4D(1.0f, 0.0f, 2.0f, 1.0f),
+ Point4D(1.0f, 1.0f, 0.0f, 1.0f), Point4D(0.0f, 1.0f, 0.0f, 1.0f)};
+
+ const std::deque<MozPolygon> expected{
+ MozPolygon{
+ Point4D(0.0f, 1.0f, 0.0f, 1.0f), Point4D(0.0f, 0.5f, 1.0f, 1.0f),
+ Point4D(1.0f, 0.5f, 1.0f, 1.0f), Point4D(1.0f, 1.0f, 0.0f, 1.0f)},
+ p1,
+ MozPolygon{
+ Point4D(0.0f, 0.0f, 2.0f, 1.0f), Point4D(1.0f, 0.0f, 2.0f, 1.0f),
+ Point4D(1.0f, 0.5f, 1.0f, 1.0f), Point4D(0.0f, 0.5f, 1.0f, 1.0f)}};
+
+ ::RunTest({p1, p2}, expected);
+}
+
+TEST(BSPTree, SplitSimple2)
+{
+ const std::deque<MozPolygon> polygons{
+ MozPolygon{Point4D(-5.00000f, -5.00000f, 0.00000f, 1.0f),
+ Point4D(-5.00000f, 5.00000f, 0.00000f, 1.0f),
+ Point4D(5.00000f, 5.00000f, 0.00000f, 1.0f),
+ Point4D(5.00000f, -5.00000f, 0.00000f, 1.0f)},
+ MozPolygon{Point4D(0.00000f, -5.00000f, -5.00000f, 1.0f),
+ Point4D(0.00000f, 5.00000f, -5.00000f, 1.0f),
+ Point4D(0.00000f, 5.00000f, 5.00000f, 1.0f),
+ Point4D(0.00000f, -5.00000f, 5.00000f, 1.0f)}};
+
+ const std::deque<MozPolygon> expected{
+ MozPolygon{Point4D(0.00000f, -5.00000f, 0.00000f, 1.0f),
+ Point4D(0.00000f, -5.00000f, -5.00000f, 1.0f),
+ Point4D(0.00000f, 5.00000f, -5.00000f, 1.0f),
+ Point4D(0.00000f, 5.00000f, 0.00000f, 1.0f)},
+ MozPolygon{Point4D(-5.00000f, -5.00000f, 0.00000f, 1.0f),
+ Point4D(-5.00000f, 5.00000f, 0.00000f, 1.0f),
+ Point4D(5.00000f, 5.00000f, 0.00000f, 1.0f),
+ Point4D(5.00000f, -5.00000f, 0.00000f, 1.0f)},
+ MozPolygon{Point4D(0.00000f, 5.00000f, 0.00000f, 1.0f),
+ Point4D(0.00000f, 5.00000f, 5.00000f, 1.0f),
+ Point4D(0.00000f, -5.00000f, 5.00000f, 1.0f),
+ Point4D(0.00000f, -5.00000f, 0.00000f, 1.0f)}};
+ ::RunTest(polygons, expected);
+}
+
+TEST(BSPTree, NoSplit1)
+{
+ const std::deque<MozPolygon> polygons{
+ MozPolygon{Point4D(0.00000f, 10.00000f, 0.00000f, 1.0f),
+ Point4D(0.00000f, 0.00000f, 0.00000f, 1.0f),
+ Point4D(10.00000f, 0.00000f, 0.00000f, 1.0f),
+ Point4D(10.00000f, 10.00000f, 0.00000f, 1.0f)},
+ MozPolygon{Point4D(0.00000f, 10.00000f, -5.00000f, 1.0f),
+ Point4D(0.00000f, 0.00000f, -5.00000f, 1.0f),
+ Point4D(10.00000f, 0.00000f, -5.00000f, 1.0f),
+ Point4D(10.00000f, 10.00000f, -5.00000f, 1.0f)},
+ MozPolygon{Point4D(0.00000f, 10.00000f, 5.00000f, 1.0f),
+ Point4D(0.00000f, 0.00000f, 5.00000f, 1.0f),
+ Point4D(10.00000f, 0.00000f, 5.00000f, 1.0f),
+ Point4D(10.00000f, 10.00000f, 5.00000f, 1.0f)}};
+
+ const std::deque<MozPolygon> expected{
+ MozPolygon{Point4D(0.00000f, 10.00000f, -5.00000f, 1.0f),
+ Point4D(0.00000f, 0.00000f, -5.00000f, 1.0f),
+ Point4D(10.00000f, 0.00000f, -5.00000f, 1.0f),
+ Point4D(10.00000f, 10.00000f, -5.00000f, 1.0f)},
+ MozPolygon{Point4D(0.00000f, 10.00000f, 0.00000f, 1.0f),
+ Point4D(0.00000f, 0.00000f, 0.00000f, 1.0f),
+ Point4D(10.00000f, 0.00000f, 0.00000f, 1.0f),
+ Point4D(10.00000f, 10.00000f, 0.00000f, 1.0f)},
+ MozPolygon{Point4D(0.00000f, 10.00000f, 5.00000f, 1.0f),
+ Point4D(0.00000f, 0.00000f, 5.00000f, 1.0f),
+ Point4D(10.00000f, 0.00000f, 5.00000f, 1.0f),
+ Point4D(10.00000f, 10.00000f, 5.00000f, 1.0f)}};
+ ::RunTest(polygons, expected);
+}
+
+TEST(BSPTree, NoSplit2)
+{
+ const std::deque<MozPolygon> polygons{
+ MozPolygon{Point4D(-5.00000f, -5.00000f, 0.00000f, 1.0f),
+ Point4D(-5.00000f, 5.00000f, 0.00000f, 1.0f),
+ Point4D(5.00000f, 5.00000f, 0.00000f, 1.0f),
+ Point4D(5.00000f, -5.00000f, 0.00000f, 1.0f)},
+ MozPolygon{Point4D(0.00000f, 5.00000f, -15.00000f, 1.0f),
+ Point4D(0.00000f, -5.00000f, -15.00000f, 1.0f),
+ Point4D(0.00000f, -5.00000f, -10.00000f, 1.0f),
+ Point4D(0.00000f, 5.00000f, -10.00000f, 1.0f)}};
+
+ const std::deque<MozPolygon> expected{
+ MozPolygon{Point4D(0.00000f, 5.00000f, -15.00000f, 1.0f),
+ Point4D(0.00000f, -5.00000f, -15.00000f, 1.0f),
+ Point4D(0.00000f, -5.00000f, -10.00000f, 1.0f),
+ Point4D(0.00000f, 5.00000f, -10.00000f, 1.0f)},
+ MozPolygon{Point4D(-5.00000f, -5.00000f, 0.00000f, 1.0f),
+ Point4D(-5.00000f, 5.00000f, 0.00000f, 1.0f),
+ Point4D(5.00000f, 5.00000f, 0.00000f, 1.0f),
+ Point4D(5.00000f, -5.00000f, 0.00000f, 1.0f)}};
+ ::RunTest(polygons, expected);
+}
+
+TEST(BSPTree, TwoPlaneIntersectRotate0degrees)
+{
+ const std::deque<MozPolygon> polygons{
+ MozPolygon{Point4D(-0.00000f, 2.00000f, 2.00000f, 1.0f),
+ Point4D(-0.00000f, -2.00000f, 2.00000f, 1.0f),
+ Point4D(0.00010f, -2.00000f, -2.00000f, 1.0f),
+ Point4D(0.00010f, 2.00000f, -2.00000f, 1.0f)},
+ MozPolygon{Point4D(2.00000f, 0.00000f, 2.00000f, 1.0f),
+ Point4D(2.00000f, -0.00000f, -2.00000f, 1.0f),
+ Point4D(-2.00000f, 0.00000f, -2.00000f, 1.0f),
+ Point4D(-2.00000f, 0.00010f, 2.00000f, 1.0f)}};
+
+ const std::deque<MozPolygon> expected{
+ MozPolygon{Point4D(2.00000f, 0.00000f, 2.00000f, 1.0f),
+ Point4D(2.00000f, -0.00000f, -2.00000f, 1.0f),
+ Point4D(-2.00000f, 0.00000f, -2.00000f, 1.0f),
+ Point4D(-2.00000f, 0.00010f, 2.00000f, 1.0f)},
+ MozPolygon{Point4D(-0.00000f, 2.00000f, 2.00000f, 1.0f),
+ Point4D(-0.00000f, -2.00000f, 2.00000f, 1.0f),
+ Point4D(0.00010f, -2.00000f, -2.00000f, 1.0f),
+ Point4D(0.00010f, 2.00000f, -2.00000f, 1.0f)}};
+ ::RunTest(polygons, expected);
+}
+
+TEST(BSPTree, TwoPlaneIntersectRotate20degrees)
+{
+ const std::deque<MozPolygon> polygons{
+ MozPolygon{Point4D(-0.00000f, 1.19540f, 2.56350f, 1.0f),
+ Point4D(-0.00000f, -2.56340f, 1.19540f, 1.0f),
+ Point4D(0.00010f, -1.19530f, -2.56340f, 1.0f),
+ Point4D(0.00010f, 2.56350f, -1.19530f, 1.0f)},
+ MozPolygon{Point4D(2.00000f, -0.68400f, 1.87940f, 1.0f),
+ Point4D(2.00000f, 0.68410f, -1.87930f, 1.0f),
+ Point4D(-2.00000f, 0.68410f, -1.87930f, 1.0f),
+ Point4D(-2.00000f, -0.68400f, 1.87940f, 1.0f)}};
+
+ const std::deque<MozPolygon> expected{
+ MozPolygon{Point4D(2.00000f, -0.68400f, 1.87940f, 1.0f),
+ Point4D(2.00000f, 0.68410f, -1.87930f, 1.0f),
+ Point4D(-2.00000f, 0.68410f, -1.87930f, 1.0f),
+ Point4D(-2.00000f, -0.68400f, 1.87940f, 1.0f)},
+ MozPolygon{Point4D(-0.00000f, 1.19540f, 2.56350f, 1.0f),
+ Point4D(-0.00000f, -2.56340f, 1.19540f, 1.0f),
+ Point4D(0.00010f, -1.19530f, -2.56340f, 1.0f),
+ Point4D(0.00010f, 2.56350f, -1.19530f, 1.0f)}};
+ ::RunTest(polygons, expected);
+}
+
+TEST(BSPTree, TwoPlaneIntersectRotate40degrees)
+{
+ const std::deque<MozPolygon> polygons{
+ MozPolygon{Point4D(-0.00000f, -0.73200f, 2.73210f, 1.0f),
+ Point4D(-0.00000f, -2.73200f, -0.73200f, 1.0f),
+ Point4D(0.00010f, 0.73210f, -2.73200f, 1.0f),
+ Point4D(0.00010f, 2.73210f, 0.73210f, 1.0f)},
+ MozPolygon{Point4D(2.00000f, -1.73200f, 1.00000f, 1.0f),
+ Point4D(2.00000f, 1.73210f, -0.99990f, 1.0f),
+ Point4D(-2.00000f, 1.73210f, -0.99990f, 1.0f),
+ Point4D(-2.00000f, -1.73200f, 1.00000f, 1.0f)}};
+
+ const std::deque<MozPolygon> expected{
+ MozPolygon{Point4D(2.00000f, -1.73200f, 1.00000f, 1.0f),
+ Point4D(2.00000f, 1.73210f, -0.99990f, 1.0f),
+ Point4D(-2.00000f, 1.73210f, -0.99990f, 1.0f),
+ Point4D(-2.00000f, -1.73200f, 1.00000f, 1.0f)},
+ MozPolygon{Point4D(-0.00000f, -0.73200f, 2.73210f, 1.0f),
+ Point4D(-0.00000f, -2.73200f, -0.73200f, 1.0f),
+ Point4D(0.00010f, 0.73210f, -2.73200f, 1.0f),
+ Point4D(0.00010f, 2.73210f, 0.73210f, 1.0f)}};
+ ::RunTest(polygons, expected);
+}
+
+TEST(BSPTree, TwoPlaneIntersectRotate60degrees)
+{
+ const std::deque<MozPolygon> polygons{
+ MozPolygon{Point4D(-0.00000f, -2.73200f, 0.73210f, 1.0f),
+ Point4D(-0.00000f, -0.73200f, -2.73200f, 1.0f),
+ Point4D(0.00010f, 2.73210f, -0.73200f, 1.0f),
+ Point4D(0.00010f, 0.73210f, 2.73210f, 1.0f)},
+ MozPolygon{Point4D(2.00000f, -1.73200f, -1.00000f, 1.0f),
+ Point4D(2.00000f, 1.73210f, 1.00010f, 1.0f),
+ Point4D(-2.00000f, 1.73210f, 1.00010f, 1.0f),
+ Point4D(-2.00000f, -1.73200f, -1.00000f, 1.0f)}};
+
+ const std::deque<MozPolygon> expected{
+ MozPolygon{Point4D(-2.00000f, 1.26793f, 0.73210f, 1.0f),
+ Point4D(-2.00000f, -1.73200f, -1.00000f, 1.0f),
+ Point4D(2.00000f, -1.73200f, -1.00000f, 1.0f),
+ Point4D(2.00000f, 1.26793f, 0.73210f, 1.0f)},
+ MozPolygon{Point4D(-0.00000f, -2.73200f, 0.73210f, 1.0f),
+ Point4D(-0.00000f, -0.73200f, -2.73200f, 1.0f),
+ Point4D(0.00010f, 2.73210f, -0.73200f, 1.0f),
+ Point4D(0.00010f, 0.73210f, 2.73210f, 1.0f)},
+ MozPolygon{Point4D(2.00000f, 1.26793f, 0.73210f, 1.0f),
+ Point4D(2.00000f, 1.73210f, 1.00010f, 1.0f),
+ Point4D(-2.00000f, 1.73210f, 1.00010f, 1.0f),
+ Point4D(-2.00000f, 1.26793f, 0.73210f, 1.0f)}};
+ ::RunTest(polygons, expected);
+}
+
+TEST(BSPTree, TwoPlaneIntersectRotate80degrees)
+{
+ const std::deque<MozPolygon> polygons{
+ MozPolygon{Point4D(-0.00000f, -1.19530f, -2.56340f, 1.0f),
+ Point4D(-0.00000f, 2.56350f, -1.19530f, 1.0f),
+ Point4D(0.00010f, 1.19540f, 2.56350f, 1.0f),
+ Point4D(0.00010f, -2.56340f, 1.19540f, 1.0f)},
+ MozPolygon{Point4D(2.00000f, 0.68410f, -1.87930f, 1.0f),
+ Point4D(2.00000f, -0.68400f, 1.87940f, 1.0f),
+ Point4D(-2.00000f, -0.68400f, 1.87940f, 1.0f),
+ Point4D(-2.00000f, 0.68410f, -1.87930f, 1.0f)}};
+
+ const std::deque<MozPolygon> expected{
+ MozPolygon{Point4D(-0.00000f, -1.19530f, -2.56340f, 1.0f),
+ Point4D(-0.00000f, 2.56350f, -1.19530f, 1.0f),
+ Point4D(0.00010f, 1.19540f, 2.56350f, 1.0f),
+ Point4D(0.00010f, -2.56340f, 1.19540f, 1.0f)},
+ MozPolygon{Point4D(2.00000f, 0.68410f, -1.87930f, 1.0f),
+ Point4D(2.00000f, -0.68400f, 1.87940f, 1.0f),
+ Point4D(-2.00000f, -0.68400f, 1.87940f, 1.0f),
+ Point4D(-2.00000f, 0.68410f, -1.87930f, 1.0f)}};
+ ::RunTest(polygons, expected);
+}
+
+TEST(BSPTree, TwoPlaneIntersectRotate100degrees)
+{
+ const std::deque<MozPolygon> polygons{
+ MozPolygon{Point4D(-0.00000f, 2.73210f, -0.73200f, 1.0f),
+ Point4D(-0.00000f, 0.73210f, 2.73210f, 1.0f),
+ Point4D(0.00010f, -2.73200f, 0.73210f, 1.0f),
+ Point4D(0.00010f, -0.73200f, -2.73200f, 1.0f)},
+ MozPolygon{Point4D(2.00000f, 1.73210f, 1.00010f, 1.0f),
+ Point4D(2.00000f, -1.73200f, -1.00000f, 1.0f),
+ Point4D(-2.00000f, -1.73200f, -1.00000f, 1.0f),
+ Point4D(-2.00000f, 1.73210f, 1.00010f, 1.0f)}};
+
+ const std::deque<MozPolygon> expected{
+ MozPolygon{Point4D(2.00000f, -1.26783f, -0.73200f, 1.0f),
+ Point4D(2.00000f, -1.73200f, -1.00000f, 1.0f),
+ Point4D(-2.00000f, -1.73200f, -1.00000f, 1.0f),
+ Point4D(-2.00000f, -1.26783f, -0.73200f, 1.0f)},
+ MozPolygon{Point4D(-0.00000f, 2.73210f, -0.73200f, 1.0f),
+ Point4D(-0.00000f, 0.73210f, 2.73210f, 1.0f),
+ Point4D(0.00010f, -2.73200f, 0.73210f, 1.0f),
+ Point4D(0.00010f, -0.73200f, -2.73200f, 1.0f)},
+ MozPolygon{Point4D(-2.00000f, -1.26783f, -0.73200f, 1.0f),
+ Point4D(-2.00000f, 1.73210f, 1.00010f, 1.0f),
+ Point4D(2.00000f, 1.73210f, 1.00010f, 1.0f),
+ Point4D(2.00000f, -1.26783f, -0.73200f, 1.0f)}};
+ ::RunTest(polygons, expected);
+}
+
+TEST(BSPTree, TwoPlaneIntersectRotate120degrees)
+{
+ const std::deque<MozPolygon> polygons{
+ MozPolygon{Point4D(-0.00000f, -0.73200f, 2.73210f, 1.0f),
+ Point4D(-0.00000f, -2.73200f, -0.73200f, 1.0f),
+ Point4D(0.00010f, 0.73210f, -2.73200f, 1.0f),
+ Point4D(0.00010f, 2.73210f, 0.73210f, 1.0f)},
+ MozPolygon{Point4D(2.00000f, -1.73200f, 1.00000f, 1.0f),
+ Point4D(2.00000f, 1.73210f, -0.99990f, 1.0f),
+ Point4D(-2.00000f, 1.73210f, -0.99990f, 1.0f),
+ Point4D(-2.00000f, -1.73200f, 1.00000f, 1.0f)}};
+
+ const std::deque<MozPolygon> expected{
+ MozPolygon{Point4D(2.00000f, -1.73200f, 1.00000f, 1.0f),
+ Point4D(2.00000f, 1.73210f, -0.99990f, 1.0f),
+ Point4D(-2.00000f, 1.73210f, -0.99990f, 1.0f),
+ Point4D(-2.00000f, -1.73200f, 1.00000f, 1.0f)},
+ MozPolygon{Point4D(-0.00000f, -0.73200f, 2.73210f, 1.0f),
+ Point4D(-0.00000f, -2.73200f, -0.73200f, 1.0f),
+ Point4D(0.00010f, 0.73210f, -2.73200f, 1.0f),
+ Point4D(0.00010f, 2.73210f, 0.73210f, 1.0f)}};
+ ::RunTest(polygons, expected);
+}
+
+TEST(BSPTree, TwoPlaneIntersectRotate140degrees)
+{
+ const std::deque<MozPolygon> polygons{
+ MozPolygon{Point4D(-0.00000f, -1.19530f, -2.56340f, 1.0f),
+ Point4D(-0.00000f, 2.56350f, -1.19530f, 1.0f),
+ Point4D(0.00010f, 1.19540f, 2.56350f, 1.0f),
+ Point4D(0.00010f, -2.56340f, 1.19540f, 1.0f)},
+ MozPolygon{Point4D(2.00000f, 0.68410f, -1.87930f, 1.0f),
+ Point4D(2.00000f, -0.68400f, 1.87940f, 1.0f),
+ Point4D(-2.00000f, -0.68400f, 1.87940f, 1.0f),
+ Point4D(-2.00000f, 0.68410f, -1.87930f, 1.0f)}};
+
+ const std::deque<MozPolygon> expected{
+ MozPolygon{Point4D(-0.00000f, -1.19530f, -2.56340f, 1.0f),
+ Point4D(-0.00000f, 2.56350f, -1.19530f, 1.0f),
+ Point4D(0.00010f, 1.19540f, 2.56350f, 1.0f),
+ Point4D(0.00010f, -2.56340f, 1.19540f, 1.0f)},
+ MozPolygon{Point4D(2.00000f, 0.68410f, -1.87930f, 1.0f),
+ Point4D(2.00000f, -0.68400f, 1.87940f, 1.0f),
+ Point4D(-2.00000f, -0.68400f, 1.87940f, 1.0f),
+ Point4D(-2.00000f, 0.68410f, -1.87930f, 1.0f)}};
+ ::RunTest(polygons, expected);
+}
+
+TEST(BSPTree, TwoPlaneIntersectRotate160degrees)
+{
+ const std::deque<MozPolygon> polygons{
+ MozPolygon{Point4D(-0.00000f, 2.00000f, 2.00000f, 1.0f),
+ Point4D(-0.00000f, -2.00000f, 2.00000f, 1.0f),
+ Point4D(0.00010f, -2.00000f, -2.00000f, 1.0f),
+ Point4D(0.00010f, 2.00000f, -2.00000f, 1.0f)},
+ MozPolygon{Point4D(2.00000f, -0.00000f, 2.00000f, 1.0f),
+ Point4D(2.00000f, 0.00010f, -2.00000f, 1.0f),
+ Point4D(-2.00000f, 0.00010f, -2.00000f, 1.0f),
+ Point4D(-2.00000f, -0.00000f, 2.00000f, 1.0f)}};
+
+ const std::deque<MozPolygon> expected{
+ MozPolygon{Point4D(2.00000f, -0.00000f, 2.00000f, 1.0f),
+ Point4D(2.00000f, 0.00010f, -2.00000f, 1.0f),
+ Point4D(-2.00000f, 0.00010f, -2.00000f, 1.0f),
+ Point4D(-2.00000f, -0.00000f, 2.00000f, 1.0f)},
+ MozPolygon{Point4D(-0.00000f, 2.00000f, 2.00000f, 1.0f),
+ Point4D(-0.00000f, -2.00000f, 2.00000f, 1.0f),
+ Point4D(0.00010f, -2.00000f, -2.00000f, 1.0f),
+ Point4D(0.00010f, 2.00000f, -2.00000f, 1.0f)}};
+ ::RunTest(polygons, expected);
+}
+
+TEST(BSPTree, TwoPlaneIntersectRotate180degrees)
+{
+ const std::deque<MozPolygon> polygons{
+ MozPolygon{Point4D(-0.00000f, -2.00000f, -2.00000f, 1.0f),
+ Point4D(-0.00000f, 2.00000f, -2.00000f, 1.0f),
+ Point4D(0.00010f, 2.00000f, 2.00000f, 1.0f),
+ Point4D(0.00010f, -2.00000f, 2.00000f, 1.0f)},
+ MozPolygon{Point4D(2.00000f, 0.00010f, -2.00000f, 1.0f),
+ Point4D(2.00000f, -0.00000f, 2.00000f, 1.0f),
+ Point4D(-2.00000f, -0.00000f, 2.00000f, 1.0f),
+ Point4D(-2.00000f, 0.00010f, -2.00000f, 1.0f)}};
+
+ const std::deque<MozPolygon> expected{
+ MozPolygon{Point4D(-0.00000f, -2.00000f, -2.00000f, 1.0f),
+ Point4D(-0.00000f, 2.00000f, -2.00000f, 1.0f),
+ Point4D(0.00010f, 2.00000f, 2.00000f, 1.0f),
+ Point4D(0.00010f, -2.00000f, 2.00000f, 1.0f)},
+ MozPolygon{Point4D(2.00000f, 0.00010f, -2.00000f, 1.0f),
+ Point4D(2.00000f, -0.00000f, 2.00000f, 1.0f),
+ Point4D(-2.00000f, -0.00000f, 2.00000f, 1.0f),
+ Point4D(-2.00000f, 0.00010f, -2.00000f, 1.0f)}};
+ ::RunTest(polygons, expected);
+}
+
+TEST(BSPTree, TwoPlaneIntersectRotate200degrees)
+{
+ const std::deque<MozPolygon> polygons{
+ MozPolygon{Point4D(-0.00000f, 1.19540f, 2.56350f, 1.0f),
+ Point4D(-0.00000f, -2.56340f, 1.19540f, 1.0f),
+ Point4D(0.00010f, -1.19530f, -2.56340f, 1.0f),
+ Point4D(0.00010f, 2.56350f, -1.19530f, 1.0f)},
+ MozPolygon{Point4D(2.00000f, -0.68400f, 1.87940f, 1.0f),
+ Point4D(2.00000f, 0.68410f, -1.87930f, 1.0f),
+ Point4D(-2.00000f, 0.68410f, -1.87930f, 1.0f),
+ Point4D(-2.00000f, -0.68400f, 1.87940f, 1.0f)}};
+
+ const std::deque<MozPolygon> expected{
+ MozPolygon{Point4D(2.00000f, -0.68400f, 1.87940f, 1.0f),
+ Point4D(2.00000f, 0.68410f, -1.87930f, 1.0f),
+ Point4D(-2.00000f, 0.68410f, -1.87930f, 1.0f),
+ Point4D(-2.00000f, -0.68400f, 1.87940f, 1.0f)},
+ MozPolygon{Point4D(-0.00000f, 1.19540f, 2.56350f, 1.0f),
+ Point4D(-0.00000f, -2.56340f, 1.19540f, 1.0f),
+ Point4D(0.00010f, -1.19530f, -2.56340f, 1.0f),
+ Point4D(0.00010f, 2.56350f, -1.19530f, 1.0f)}};
+ ::RunTest(polygons, expected);
+}
+
+TEST(BSPTree, TwoPlaneIntersectRotate220degrees)
+{
+ const std::deque<MozPolygon> polygons{
+ MozPolygon{Point4D(-0.00000f, 0.73210f, -2.73200f, 1.0f),
+ Point4D(-0.00000f, 2.73210f, 0.73210f, 1.0f),
+ Point4D(0.00010f, -0.73200f, 2.73210f, 1.0f),
+ Point4D(0.00010f, -2.73200f, -0.73200f, 1.0f)},
+ MozPolygon{Point4D(2.00000f, 1.73210f, -0.99990f, 1.0f),
+ Point4D(2.00000f, -1.73200f, 1.00000f, 1.0f),
+ Point4D(-2.00000f, -1.73200f, 1.00000f, 1.0f),
+ Point4D(-2.00000f, 1.73210f, -0.99990f, 1.0f)}};
+
+ const std::deque<MozPolygon> expected{
+ MozPolygon{Point4D(-0.00000f, 0.73210f, -2.73200f, 1.0f),
+ Point4D(-0.00000f, 2.73210f, 0.73210f, 1.0f),
+ Point4D(0.00010f, -0.73200f, 2.73210f, 1.0f),
+ Point4D(0.00010f, -2.73200f, -0.73200f, 1.0f)},
+ MozPolygon{Point4D(2.00000f, 1.73210f, -0.99990f, 1.0f),
+ Point4D(2.00000f, -1.73200f, 1.00000f, 1.0f),
+ Point4D(-2.00000f, -1.73200f, 1.00000f, 1.0f),
+ Point4D(-2.00000f, 1.73210f, -0.99990f, 1.0f)}};
+ ::RunTest(polygons, expected);
+}
+
+TEST(BSPTree, TwoPlaneIntersectRotate240degrees)
+{
+ const std::deque<MozPolygon> polygons{
+ MozPolygon{Point4D(-0.00000f, -2.73200f, 0.73210f, 1.0f),
+ Point4D(-0.00000f, -0.73200f, -2.73200f, 1.0f),
+ Point4D(0.00010f, 2.73210f, -0.73200f, 1.0f),
+ Point4D(0.00010f, 0.73210f, 2.73210f, 1.0f)},
+ MozPolygon{Point4D(2.00000f, -1.73200f, -1.00000f, 1.0f),
+ Point4D(2.00000f, 1.73210f, 1.00010f, 1.0f),
+ Point4D(-2.00000f, 1.73210f, 1.00010f, 1.0f),
+ Point4D(-2.00000f, -1.73200f, -1.00000f, 1.0f)}};
+
+ const std::deque<MozPolygon> expected{
+ MozPolygon{Point4D(-2.00000f, 1.26793f, 0.73210f, 1.0f),
+ Point4D(-2.00000f, -1.73200f, -1.00000f, 1.0f),
+ Point4D(2.00000f, -1.73200f, -1.00000f, 1.0f),
+ Point4D(2.00000f, 1.26793f, 0.73210f, 1.0f)},
+ MozPolygon{Point4D(-0.00000f, -2.73200f, 0.73210f, 1.0f),
+ Point4D(-0.00000f, -0.73200f, -2.73200f, 1.0f),
+ Point4D(0.00010f, 2.73210f, -0.73200f, 1.0f),
+ Point4D(0.00010f, 0.73210f, 2.73210f, 1.0f)},
+ MozPolygon{Point4D(2.00000f, 1.26793f, 0.73210f, 1.0f),
+ Point4D(2.00000f, 1.73210f, 1.00010f, 1.0f),
+ Point4D(-2.00000f, 1.73210f, 1.00010f, 1.0f),
+ Point4D(-2.00000f, 1.26793f, 0.73210f, 1.0f)}};
+ ::RunTest(polygons, expected);
+}
+
+TEST(BSPTree, TwoPlaneIntersectRotate260degrees)
+{
+ const std::deque<MozPolygon> polygons{
+ MozPolygon{Point4D(-0.00000f, 1.19540f, 2.56350f, 1.0f),
+ Point4D(-0.00000f, -2.56340f, 1.19540f, 1.0f),
+ Point4D(0.00010f, -1.19530f, -2.56340f, 1.0f),
+ Point4D(0.00010f, 2.56350f, -1.19530f, 1.0f)},
+ MozPolygon{Point4D(2.00000f, -0.68400f, 1.87940f, 1.0f),
+ Point4D(2.00000f, 0.68410f, -1.87930f, 1.0f),
+ Point4D(-2.00000f, 0.68410f, -1.87930f, 1.0f),
+ Point4D(-2.00000f, -0.68400f, 1.87940f, 1.0f)}};
+
+ const std::deque<MozPolygon> expected{
+ MozPolygon{Point4D(2.00000f, -0.68400f, 1.87940f, 1.0f),
+ Point4D(2.00000f, 0.68410f, -1.87930f, 1.0f),
+ Point4D(-2.00000f, 0.68410f, -1.87930f, 1.0f),
+ Point4D(-2.00000f, -0.68400f, 1.87940f, 1.0f)},
+ MozPolygon{Point4D(-0.00000f, 1.19540f, 2.56350f, 1.0f),
+ Point4D(-0.00000f, -2.56340f, 1.19540f, 1.0f),
+ Point4D(0.00010f, -1.19530f, -2.56340f, 1.0f),
+ Point4D(0.00010f, 2.56350f, -1.19530f, 1.0f)}};
+ ::RunTest(polygons, expected);
+}
+
+TEST(BSPTree, TwoPlaneIntersectRotate280degrees)
+{
+ const std::deque<MozPolygon> polygons{
+ MozPolygon{Point4D(-0.00000f, 2.73210f, -0.73200f, 1.0f),
+ Point4D(-0.00000f, 0.73210f, 2.73210f, 1.0f),
+ Point4D(0.00010f, -2.73200f, 0.73210f, 1.0f),
+ Point4D(0.00010f, -0.73200f, -2.73200f, 1.0f)},
+ MozPolygon{Point4D(2.00000f, 1.73210f, 1.00010f, 1.0f),
+ Point4D(2.00000f, -1.73200f, -1.00000f, 1.0f),
+ Point4D(-2.00000f, -1.73200f, -1.00000f, 1.0f),
+ Point4D(-2.00000f, 1.73210f, 1.00010f, 1.0f)}};
+
+ const std::deque<MozPolygon> expected{
+ MozPolygon{Point4D(2.00000f, -1.26783f, -0.73200f, 1.0f),
+ Point4D(2.00000f, -1.73200f, -1.00000f, 1.0f),
+ Point4D(-2.00000f, -1.73200f, -1.00000f, 1.0f),
+ Point4D(-2.00000f, -1.26783f, -0.73200f, 1.0f)},
+ MozPolygon{Point4D(-0.00000f, 2.73210f, -0.73200f, 1.0f),
+ Point4D(-0.00000f, 0.73210f, 2.73210f, 1.0f),
+ Point4D(0.00010f, -2.73200f, 0.73210f, 1.0f),
+ Point4D(0.00010f, -0.73200f, -2.73200f, 1.0f)},
+ MozPolygon{Point4D(-2.00000f, -1.26783f, -0.73200f, 1.0f),
+ Point4D(-2.00000f, 1.73210f, 1.00010f, 1.0f),
+ Point4D(2.00000f, 1.73210f, 1.00010f, 1.0f),
+ Point4D(2.00000f, -1.26783f, -0.73200f, 1.0f)}};
+ ::RunTest(polygons, expected);
+}
+
+TEST(BSPTree, TwoPlaneIntersectRotate300degrees)
+{
+ const std::deque<MozPolygon> polygons{
+ MozPolygon{Point4D(-0.00000f, 0.73210f, -2.73200f, 1.0f),
+ Point4D(-0.00000f, 2.73210f, 0.73210f, 1.0f),
+ Point4D(0.00010f, -0.73200f, 2.73210f, 1.0f),
+ Point4D(0.00010f, -2.73200f, -0.73200f, 1.0f)},
+ MozPolygon{Point4D(2.00000f, 1.73210f, -0.99990f, 1.0f),
+ Point4D(2.00000f, -1.73200f, 1.00000f, 1.0f),
+ Point4D(-2.00000f, -1.73200f, 1.00000f, 1.0f),
+ Point4D(-2.00000f, 1.73210f, -0.99990f, 1.0f)}};
+
+ const std::deque<MozPolygon> expected{
+ MozPolygon{Point4D(-0.00000f, 0.73210f, -2.73200f, 1.0f),
+ Point4D(-0.00000f, 2.73210f, 0.73210f, 1.0f),
+ Point4D(0.00010f, -0.73200f, 2.73210f, 1.0f),
+ Point4D(0.00010f, -2.73200f, -0.73200f, 1.0f)},
+ MozPolygon{Point4D(2.00000f, 1.73210f, -0.99990f, 1.0f),
+ Point4D(2.00000f, -1.73200f, 1.00000f, 1.0f),
+ Point4D(-2.00000f, -1.73200f, 1.00000f, 1.0f),
+ Point4D(-2.00000f, 1.73210f, -0.99990f, 1.0f)}};
+ ::RunTest(polygons, expected);
+}
+
+TEST(BSPTree, TwoPlaneIntersectRotate320degrees)
+{
+ const std::deque<MozPolygon> polygons{
+ MozPolygon{Point4D(-0.00000f, -1.19530f, -2.56340f, 1.0f),
+ Point4D(-0.00000f, 2.56350f, -1.19530f, 1.0f),
+ Point4D(0.00010f, 1.19540f, 2.56350f, 1.0f),
+ Point4D(0.00010f, -2.56340f, 1.19540f, 1.0f)},
+ MozPolygon{Point4D(2.00000f, 0.68410f, -1.87930f, 1.0f),
+ Point4D(2.00000f, -0.68400f, 1.87940f, 1.0f),
+ Point4D(-2.00000f, -0.68400f, 1.87940f, 1.0f),
+ Point4D(-2.00000f, 0.68410f, -1.87930f, 1.0f)}};
+
+ const std::deque<MozPolygon> expected{
+ MozPolygon{Point4D(-0.00000f, -1.19530f, -2.56340f, 1.0f),
+ Point4D(-0.00000f, 2.56350f, -1.19530f, 1.0f),
+ Point4D(0.00010f, 1.19540f, 2.56350f, 1.0f),
+ Point4D(0.00010f, -2.56340f, 1.19540f, 1.0f)},
+ MozPolygon{Point4D(2.00000f, 0.68410f, -1.87930f, 1.0f),
+ Point4D(2.00000f, -0.68400f, 1.87940f, 1.0f),
+ Point4D(-2.00000f, -0.68400f, 1.87940f, 1.0f),
+ Point4D(-2.00000f, 0.68410f, -1.87930f, 1.0f)}};
+ ::RunTest(polygons, expected);
+}
+
+TEST(BSPTree, TwoPlaneIntersectRotate340degrees)
+{
+ const std::deque<MozPolygon> polygons{
+ MozPolygon{Point4D(-0.00000f, -2.00000f, -2.00000f, 1.0f),
+ Point4D(-0.00000f, 2.00000f, -2.00000f, 1.0f),
+ Point4D(0.00010f, 2.00000f, 2.00000f, 1.0f),
+ Point4D(0.00010f, -2.00000f, 2.00000f, 1.0f)},
+ MozPolygon{Point4D(2.00000f, 0.00010f, -2.00000f, 1.0f),
+ Point4D(2.00000f, -0.00000f, 2.00000f, 1.0f),
+ Point4D(-2.00000f, -0.00000f, 2.00000f, 1.0f),
+ Point4D(-2.00000f, 0.00010f, -2.00000f, 1.0f)}};
+
+ const std::deque<MozPolygon> expected{
+ MozPolygon{Point4D(-0.00000f, -2.00000f, -2.00000f, 1.0f),
+ Point4D(-0.00000f, 2.00000f, -2.00000f, 1.0f),
+ Point4D(0.00010f, 2.00000f, 2.00000f, 1.0f),
+ Point4D(0.00010f, -2.00000f, 2.00000f, 1.0f)},
+ MozPolygon{Point4D(2.00000f, 0.00010f, -2.00000f, 1.0f),
+ Point4D(2.00000f, -0.00000f, 2.00000f, 1.0f),
+ Point4D(-2.00000f, -0.00000f, 2.00000f, 1.0f),
+ Point4D(-2.00000f, 0.00010f, -2.00000f, 1.0f)}};
+ ::RunTest(polygons, expected);
+}
+
+TEST(BSPTree, TwoPlaneIntersectRotate360degrees)
+{
+ const std::deque<MozPolygon> polygons{
+ MozPolygon{Point4D(-0.00000f, -2.00000f, -2.00000f, 1.0f),
+ Point4D(-0.00000f, 2.00000f, -2.00000f, 1.0f),
+ Point4D(0.00010f, 2.00000f, 2.00000f, 1.0f),
+ Point4D(0.00010f, -2.00000f, 2.00000f, 1.0f)},
+ MozPolygon{Point4D(2.00000f, 0.00010f, -2.00000f, 1.0f),
+ Point4D(2.00000f, -0.00000f, 2.00000f, 1.0f),
+ Point4D(-2.00000f, -0.00000f, 2.00000f, 1.0f),
+ Point4D(-2.00000f, 0.00010f, -2.00000f, 1.0f)}};
+
+ const std::deque<MozPolygon> expected{
+ MozPolygon{Point4D(-0.00000f, -2.00000f, -2.00000f, 1.0f),
+ Point4D(-0.00000f, 2.00000f, -2.00000f, 1.0f),
+ Point4D(0.00010f, 2.00000f, 2.00000f, 1.0f),
+ Point4D(0.00010f, -2.00000f, 2.00000f, 1.0f)},
+ MozPolygon{Point4D(2.00000f, 0.00010f, -2.00000f, 1.0f),
+ Point4D(2.00000f, -0.00000f, 2.00000f, 1.0f),
+ Point4D(-2.00000f, -0.00000f, 2.00000f, 1.0f),
+ Point4D(-2.00000f, 0.00010f, -2.00000f, 1.0f)}};
+ ::RunTest(polygons, expected);
+}
diff --git a/gfx/tests/gtest/TestBufferRotation.cpp b/gfx/tests/gtest/TestBufferRotation.cpp
new file mode 100644
index 0000000000..871295e68c
--- /dev/null
+++ b/gfx/tests/gtest/TestBufferRotation.cpp
@@ -0,0 +1,162 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gtest/gtest.h"
+
+#include "BufferUnrotate.h"
+
+using mozilla::gfx::BufferUnrotate;
+
+static unsigned char* GenerateBuffer(int bytesPerPixel, int width, int height,
+ int stride, int xBoundary, int yBoundary) {
+ unsigned char* buffer = new unsigned char[stride * height];
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+ int pos = ((yBoundary + y) % height) * stride +
+ ((xBoundary + x) % width) * bytesPerPixel;
+ for (int i = 0; i < bytesPerPixel; i++) {
+ buffer[pos + i] = (x + y + i * 2) % 256;
+ }
+ }
+ }
+ return buffer;
+}
+
+static bool CheckBuffer(unsigned char* buffer, int bytesPerPixel, int width,
+ int height, int stride) {
+ int xBoundary = 0;
+ int yBoundary = 0;
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+ int pos = ((yBoundary + y) % height) * stride +
+ ((xBoundary + x) % width) * bytesPerPixel;
+ for (int i = 0; i < bytesPerPixel; i++) {
+ if (buffer[pos + i] != (x + y + i * 2) % 256) {
+ printf("Buffer differs at %i, %i, is %i\n", x, y,
+ (int)buffer[pos + i]);
+ return false;
+ }
+ }
+ }
+ }
+ return true;
+}
+
+TEST(Gfx, BufferUnrotateHorizontal)
+{
+ const int NUM_OF_TESTS = 8;
+ int bytesPerPixelList[2] = {2, 4};
+ int width[NUM_OF_TESTS] = {100, 100, 99, 99, 100, 100, 99, 99};
+ int height[NUM_OF_TESTS] = {100, 99, 100, 99, 100, 99, 100, 99};
+ int xBoundary[NUM_OF_TESTS] = {30, 30, 30, 30, 31, 31, 31, 31};
+ int yBoundary[NUM_OF_TESTS] = {0, 0, 0, 0};
+
+ for (int bytesPerId = 0; bytesPerId < 2; bytesPerId++) {
+ int bytesPerPixel = bytesPerPixelList[bytesPerId];
+ int stride = 256 * bytesPerPixel;
+ for (int testId = 0; testId < NUM_OF_TESTS; testId++) {
+ unsigned char* buffer =
+ GenerateBuffer(bytesPerPixel, width[testId], height[testId], stride,
+ xBoundary[testId], yBoundary[testId]);
+ BufferUnrotate(buffer, width[testId] * bytesPerPixel, height[testId],
+ stride, xBoundary[testId] * bytesPerPixel,
+ yBoundary[testId]);
+
+ EXPECT_TRUE(CheckBuffer(buffer, bytesPerPixel, width[testId],
+ height[testId], stride));
+ delete[] buffer;
+ }
+ }
+}
+
+TEST(Gfx, BufferUnrotateVertical)
+{
+ const int NUM_OF_TESTS = 8;
+ int bytesPerPixelList[2] = {2, 4};
+ int width[NUM_OF_TESTS] = {100, 100, 99, 99, 100, 100, 99, 99};
+ int height[NUM_OF_TESTS] = {100, 99, 100, 99, 100, 99, 100, 99};
+ int xBoundary[NUM_OF_TESTS] = {0, 0, 0, 0};
+ int yBoundary[NUM_OF_TESTS] = {30, 30, 30, 30, 31, 31, 31, 31};
+
+ for (int bytesPerId = 0; bytesPerId < 2; bytesPerId++) {
+ int bytesPerPixel = bytesPerPixelList[bytesPerId];
+ int stride = 256 * bytesPerPixel;
+ for (int testId = 0; testId < NUM_OF_TESTS; testId++) {
+ unsigned char* buffer =
+ GenerateBuffer(bytesPerPixel, width[testId], height[testId], stride,
+ xBoundary[testId], yBoundary[testId]);
+ BufferUnrotate(buffer, width[testId] * bytesPerPixel, height[testId],
+ stride, xBoundary[testId] * bytesPerPixel,
+ yBoundary[testId]);
+
+ EXPECT_TRUE(CheckBuffer(buffer, bytesPerPixel, width[testId],
+ height[testId], stride));
+ delete[] buffer;
+ }
+ }
+}
+
+TEST(Gfx, BufferUnrotateBoth)
+{
+ const int NUM_OF_TESTS = 16;
+ int bytesPerPixelList[2] = {2, 4};
+ int width[NUM_OF_TESTS] = {100, 100, 99, 99, 100, 100, 99, 99,
+ 100, 100, 99, 99, 100, 100, 99, 99};
+ int height[NUM_OF_TESTS] = {100, 99, 100, 99, 100, 99, 100, 99,
+ 100, 99, 100, 99, 100, 99, 100, 99};
+ int xBoundary[NUM_OF_TESTS] = {30, 30, 30, 30, 31, 31, 31, 31,
+ 30, 30, 30, 30, 31, 31, 31, 31};
+ int yBoundary[NUM_OF_TESTS] = {30, 30, 30, 30, 30, 30, 30, 30,
+ 31, 31, 31, 31, 31, 31, 31, 31};
+
+ for (int bytesPerId = 0; bytesPerId < 2; bytesPerId++) {
+ int bytesPerPixel = bytesPerPixelList[bytesPerId];
+ int stride = 256 * bytesPerPixel;
+ for (int testId = 0; testId < NUM_OF_TESTS; testId++) {
+ unsigned char* buffer =
+ GenerateBuffer(bytesPerPixel, width[testId], height[testId], stride,
+ xBoundary[testId], yBoundary[testId]);
+ BufferUnrotate(buffer, width[testId] * bytesPerPixel, height[testId],
+ stride, xBoundary[testId] * bytesPerPixel,
+ yBoundary[testId]);
+
+ EXPECT_TRUE(CheckBuffer(buffer, bytesPerPixel, width[testId],
+ height[testId], stride));
+ delete[] buffer;
+ }
+ }
+}
+
+TEST(Gfx, BufferUnrotateUneven)
+{
+ const int NUM_OF_TESTS = 16;
+ int bytesPerPixelList[2] = {2, 4};
+ int width[NUM_OF_TESTS] = {10, 100, 99, 39, 100, 40, 99, 39,
+ 100, 50, 39, 99, 74, 60, 99, 39};
+ int height[NUM_OF_TESTS] = {100, 39, 10, 99, 10, 99, 40, 99,
+ 73, 39, 100, 39, 67, 99, 84, 99};
+ int xBoundary[NUM_OF_TESTS] = {0, 0, 30, 30, 99, 31, 0, 31,
+ 30, 30, 30, 30, 31, 31, 31, 38};
+ int yBoundary[NUM_OF_TESTS] = {30, 30, 0, 30, 0, 30, 0, 30,
+ 31, 31, 31, 31, 31, 31, 31, 98};
+
+ for (int bytesPerId = 0; bytesPerId < 2; bytesPerId++) {
+ int bytesPerPixel = bytesPerPixelList[bytesPerId];
+ int stride = 256 * bytesPerPixel;
+ for (int testId = 0; testId < NUM_OF_TESTS; testId++) {
+ unsigned char* buffer =
+ GenerateBuffer(bytesPerPixel, width[testId], height[testId], stride,
+ xBoundary[testId], yBoundary[testId]);
+ BufferUnrotate(buffer, width[testId] * bytesPerPixel, height[testId],
+ stride, xBoundary[testId] * bytesPerPixel,
+ yBoundary[testId]);
+
+ EXPECT_TRUE(CheckBuffer(buffer, bytesPerPixel, width[testId],
+ height[testId], stride));
+ delete[] buffer;
+ }
+ }
+}
diff --git a/gfx/tests/gtest/TestColorNames.cpp b/gfx/tests/gtest/TestColorNames.cpp
new file mode 100644
index 0000000000..dba82161a7
--- /dev/null
+++ b/gfx/tests/gtest/TestColorNames.cpp
@@ -0,0 +1,91 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gtest/gtest.h"
+
+#include <string.h>
+#include "nsColor.h"
+#include "nsColorNames.h"
+#include "mozilla/Sprintf.h"
+#include "nsString.h"
+#include "mozilla/ArrayUtils.h"
+
+// define an array of all color names
+#define GFX_COLOR(_name, _value) #_name,
+static const char* const kColorNames[] = {
+#include "nsColorNameList.h"
+};
+#undef GFX_COLOR
+
+// define an array of all color name values
+#define GFX_COLOR(_name, _value) _value,
+static const nscolor kColors[] = {
+#include "nsColorNameList.h"
+};
+#undef GFX_COLOR
+
+using namespace mozilla;
+
+static const char* kJunkNames[] = {nullptr, "", "123",
+ "backgroundz", "zzzzzz", "#@$&@#*@*$@$#"};
+
+static void RunColorTests() {
+ nscolor rgb;
+ // First make sure we can find all of the tags that are supposed to
+ // be in the table. Futz with the case to make sure any case will
+ // work
+
+ for (uint32_t index = 0; index < ArrayLength(kColorNames); index++) {
+ // Lookup color by name and make sure it has the right id
+ nsCString tagName(kColorNames[index]);
+
+ // Check that color lookup by name gets the right rgb value
+ ASSERT_TRUE(NS_ColorNameToRGB(NS_ConvertASCIItoUTF16(tagName), &rgb))
+ << "can't find '" << tagName.get() << "'";
+ ASSERT_TRUE((rgb == kColors[index]))
+ << "failed at index " << index << " out of " << ArrayLength(kColorNames);
+
+ // fiddle with the case to make sure we can still find it
+ tagName.SetCharAt(tagName.CharAt(0) - 32, 0);
+ ASSERT_TRUE(NS_ColorNameToRGB(NS_ConvertASCIItoUTF16(tagName), &rgb))
+ << "can't find '" << tagName.get() << "'";
+ ASSERT_TRUE((rgb == kColors[index]))
+ << "failed at index " << index << " out of " << ArrayLength(kColorNames);
+
+ // Check that parsing an RGB value in hex gets the right values
+ uint8_t r = NS_GET_R(rgb);
+ uint8_t g = NS_GET_G(rgb);
+ uint8_t b = NS_GET_B(rgb);
+ uint8_t a = NS_GET_A(rgb);
+ char cbuf[50];
+ if (a != UINT8_MAX) {
+ SprintfLiteral(cbuf, "%02x%02x%02x%02x", r, g, b, a);
+ } else {
+ SprintfLiteral(cbuf, "%02x%02x%02x", r, g, b);
+ }
+ nscolor hexrgb;
+ ASSERT_TRUE(NS_HexToRGBA(NS_ConvertASCIItoUTF16(cbuf),
+ nsHexColorType::AllowAlpha, &hexrgb))
+ << "hex conversion to color of '" << cbuf << "'";
+ ASSERT_TRUE(hexrgb == rgb);
+ }
+}
+
+static void RunJunkColorTests() {
+ nscolor rgb;
+ // Now make sure we don't find some garbage
+ for (uint32_t i = 0; i < ArrayLength(kJunkNames); i++) {
+ nsCString tag(kJunkNames[i]);
+ ASSERT_FALSE(NS_ColorNameToRGB(NS_ConvertASCIItoUTF16(tag), &rgb))
+ << "Failed at junk color " << kJunkNames[i];
+ }
+}
+
+TEST(Gfx, ColorNames)
+{ RunColorTests(); }
+
+TEST(Gfx, JunkColorNames)
+{ RunJunkColorTests(); }
diff --git a/gfx/tests/gtest/TestCompositor.cpp b/gfx/tests/gtest/TestCompositor.cpp
new file mode 100644
index 0000000000..fe8cce8d90
--- /dev/null
+++ b/gfx/tests/gtest/TestCompositor.cpp
@@ -0,0 +1,212 @@
+/* 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 "gfxUtils.h"
+#include "gtest/gtest.h"
+#include "TestLayers.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/layers/BasicCompositor.h" // for BasicCompositor
+#include "mozilla/layers/Compositor.h" // for Compositor
+#include "mozilla/layers/CompositorOGL.h" // for CompositorOGL
+#include "mozilla/layers/CompositorTypes.h"
+#include "mozilla/layers/LayerManagerComposite.h"
+#include "MockWidget.h"
+#include "nsBaseWidget.h"
+#include <vector>
+
+const int gCompWidth = 256;
+const int gCompHeight = 256;
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+using namespace mozilla::layers;
+using namespace mozilla::gl;
+
+struct LayerManagerData {
+ RefPtr<MockWidget> mWidget;
+ RefPtr<Compositor> mCompositor;
+ RefPtr<widget::CompositorWidget> mCompositorWidget;
+ RefPtr<LayerManagerComposite> mLayerManager;
+
+ LayerManagerData(Compositor* compositor, MockWidget* widget,
+ widget::CompositorWidget* aWidget,
+ LayerManagerComposite* layerManager)
+ : mWidget(widget),
+ mCompositor(compositor),
+ mCompositorWidget(aWidget),
+ mLayerManager(layerManager) {}
+};
+
+static already_AddRefed<Compositor> CreateTestCompositor(
+ LayersBackend backend, widget::CompositorWidget* widget) {
+ gfxPlatform::GetPlatform();
+
+ RefPtr<Compositor> compositor;
+
+ if (backend == LayersBackend::LAYERS_OPENGL) {
+ compositor =
+ new CompositorOGL(nullptr, widget, gCompWidth, gCompHeight, true);
+ compositor->SetDestinationSurfaceSize(IntSize(gCompWidth, gCompHeight));
+ } else if (backend == LayersBackend::LAYERS_BASIC) {
+ compositor = new BasicCompositor(nullptr, widget);
+#ifdef XP_WIN
+ } else if (backend == LayersBackend::LAYERS_D3D11) {
+ // compositor = new CompositorD3D11();
+ MOZ_CRASH(); // No support yet
+#endif
+ }
+ nsCString failureReason;
+ if (!compositor || !compositor->Initialize(&failureReason)) {
+ printf_stderr(
+ "Failed to construct layer manager for the requested backend\n");
+ abort();
+ }
+
+ return compositor.forget();
+}
+
+/**
+ * Get a list of layers managers for the platform to run the test on.
+ */
+static std::vector<LayerManagerData> GetLayerManagers(
+ std::vector<LayersBackend> aBackends) {
+ std::vector<LayerManagerData> managers;
+
+ for (size_t i = 0; i < aBackends.size(); i++) {
+ auto backend = aBackends[i];
+
+ RefPtr<MockWidget> widget = new MockWidget(gCompWidth, gCompHeight);
+ CompositorOptions options;
+ RefPtr<widget::CompositorWidget> proxy =
+ new widget::InProcessCompositorWidget(options, widget);
+ RefPtr<Compositor> compositor = CreateTestCompositor(backend, proxy);
+
+ RefPtr<LayerManagerComposite> layerManager =
+ new LayerManagerComposite(compositor);
+
+ managers.push_back(
+ LayerManagerData(compositor, widget, proxy, layerManager));
+ }
+
+ return managers;
+}
+
+/**
+ * This will return the default list of backends that
+ * units test should run against.
+ */
+static std::vector<LayersBackend> GetPlatformBackends() {
+ std::vector<LayersBackend> backends;
+
+ // For now we only support Basic for gtest
+ backends.push_back(LayersBackend::LAYERS_BASIC);
+
+#ifdef XP_MACOSX
+ backends.push_back(LayersBackend::LAYERS_OPENGL);
+#endif
+
+ // TODO Support OGL/D3D backends with unit test
+ return backends;
+}
+
+static already_AddRefed<DrawTarget> CreateDT() {
+ return gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(
+ IntSize(gCompWidth, gCompHeight), SurfaceFormat::B8G8R8A8);
+}
+
+static bool CompositeAndCompare(RefPtr<LayerManagerComposite> layerManager,
+ DrawTarget* refDT) {
+ RefPtr<DrawTarget> drawTarget = CreateDT();
+
+ layerManager->BeginTransactionWithDrawTarget(
+ drawTarget, IntRect(0, 0, gCompWidth, gCompHeight));
+ layerManager->EndTransaction(TimeStamp::Now());
+
+ RefPtr<SourceSurface> ss = drawTarget->Snapshot();
+ RefPtr<DataSourceSurface> dss = ss->GetDataSurface();
+ DataSourceSurface::ScopedMap dssMap(dss, DataSourceSurface::READ);
+ uint8_t* bitmap = dssMap.GetData();
+
+ RefPtr<SourceSurface> ssRef = refDT->Snapshot();
+ RefPtr<DataSourceSurface> dssRef = ssRef->GetDataSurface();
+ DataSourceSurface::ScopedMap dssRefMap(dssRef, DataSourceSurface::READ);
+ uint8_t* bitmapRef = dssRefMap.GetData();
+
+ for (int y = 0; y < gCompHeight; y++) {
+ for (int x = 0; x < gCompWidth; x++) {
+ for (size_t channel = 0; channel < 4; channel++) {
+ uint8_t bit = bitmap[y * dssMap.GetStride() + x * 4 + channel];
+ uint8_t bitRef = bitmapRef[y * dssRefMap.GetStride() + x * 4 + channel];
+ if (bit != bitRef) {
+ printf("Layer Tree:\n");
+ layerManager->Dump();
+ printf("Original:\n");
+ gfxUtils::DumpAsDataURI(drawTarget);
+ printf("\n\n");
+
+ printf("Reference:\n");
+ gfxUtils::DumpAsDataURI(refDT);
+ printf("\n\n");
+
+ return false;
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+TEST(Gfx, CompositorConstruct)
+{ auto layerManagers = GetLayerManagers(GetPlatformBackends()); }
+
+TEST(Gfx, CompositorSimpleTree)
+{
+ auto layerManagers = GetLayerManagers(GetPlatformBackends());
+ for (size_t i = 0; i < layerManagers.size(); i++) {
+ RefPtr<LayerManagerComposite> layerManager = layerManagers[i].mLayerManager;
+ RefPtr<LayerManager> lmBase = layerManager.get();
+ nsTArray<RefPtr<Layer>> layers;
+ nsIntRegion layerVisibleRegion[] = {
+ nsIntRegion(IntRect(0, 0, gCompWidth, gCompHeight)),
+ nsIntRegion(IntRect(0, 0, gCompWidth, gCompHeight)),
+ nsIntRegion(IntRect(0, 0, 100, 100)),
+ nsIntRegion(IntRect(0, 50, 100, 100)),
+ };
+ RefPtr<Layer> root =
+ CreateLayerTree("c(ooo)", layerVisibleRegion, nullptr, lmBase, layers);
+
+ { // background
+ ColorLayer* colorLayer = layers[1]->AsColorLayer();
+ colorLayer->SetColor(DeviceColor(1.f, 0.f, 1.f, 1.f));
+ colorLayer->SetBounds(
+ colorLayer->GetVisibleRegion().GetBounds().ToUnknownRect());
+ }
+
+ {
+ ColorLayer* colorLayer = layers[2]->AsColorLayer();
+ colorLayer->SetColor(DeviceColor(1.f, 0.f, 0.f, 1.f));
+ colorLayer->SetBounds(
+ colorLayer->GetVisibleRegion().GetBounds().ToUnknownRect());
+ }
+
+ {
+ ColorLayer* colorLayer = layers[3]->AsColorLayer();
+ colorLayer->SetColor(DeviceColor(0.f, 0.f, 1.f, 1.f));
+ colorLayer->SetBounds(
+ colorLayer->GetVisibleRegion().GetBounds().ToUnknownRect());
+ }
+
+ RefPtr<DrawTarget> refDT = CreateDT();
+ refDT->FillRect(Rect(0, 0, gCompWidth, gCompHeight),
+ ColorPattern(DeviceColor(1.f, 0.f, 1.f, 1.f)));
+ refDT->FillRect(Rect(0, 0, 100, 100),
+ ColorPattern(DeviceColor(1.f, 0.f, 0.f, 1.f)));
+ refDT->FillRect(Rect(0, 50, 100, 100),
+ ColorPattern(DeviceColor(0.f, 0.f, 1.f, 1.f)));
+ EXPECT_TRUE(CompositeAndCompare(layerManager, refDT));
+ }
+}
diff --git a/gfx/tests/gtest/TestConfigManager.cpp b/gfx/tests/gtest/TestConfigManager.cpp
new file mode 100644
index 0000000000..6843723e04
--- /dev/null
+++ b/gfx/tests/gtest/TestConfigManager.cpp
@@ -0,0 +1,921 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#include "gtest/gtest.h"
+
+#include "gfxFeature.h"
+#include "mozilla/gfx/gfxConfigManager.h"
+#include "nsIGfxInfo.h"
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+
+namespace mozilla {
+namespace gfx {
+
+class MockGfxInfo final : public nsIGfxInfo {
+ public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ int32_t mStatusWr;
+ int32_t mStatusWrCompositor;
+ int32_t mStatusWrSoftware;
+ int32_t mMaxRefreshRate;
+ bool mHasMixedRefreshRate;
+ Maybe<bool> mHasBattery;
+ const char* mVendorId;
+
+ // Default allows WebRender + compositor, and is desktop NVIDIA.
+ MockGfxInfo()
+ : mStatusWr(nsIGfxInfo::FEATURE_ALLOW_ALWAYS),
+ mStatusWrCompositor(nsIGfxInfo::FEATURE_STATUS_OK),
+ mStatusWrSoftware(nsIGfxInfo::FEATURE_DENIED),
+ mMaxRefreshRate(-1),
+ mHasMixedRefreshRate(false),
+ mHasBattery(Some(false)),
+ mVendorId("0x10de") {}
+
+ NS_IMETHOD GetFeatureStatus(int32_t aFeature, nsACString& aFailureId,
+ int32_t* _retval) override {
+ switch (aFeature) {
+ case nsIGfxInfo::FEATURE_WEBRENDER:
+ *_retval = mStatusWr;
+ break;
+ case nsIGfxInfo::FEATURE_WEBRENDER_COMPOSITOR:
+ *_retval = mStatusWrCompositor;
+ break;
+ case nsIGfxInfo::FEATURE_WEBRENDER_SOFTWARE:
+ *_retval = mStatusWrSoftware;
+ break;
+ default:
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ return NS_OK;
+ }
+
+ NS_IMETHOD GetHasBattery(bool* aHasBattery) override {
+ if (mHasBattery.isNothing()) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ *aHasBattery = *mHasBattery;
+ return NS_OK;
+ }
+
+ NS_IMETHOD GetAdapterVendorID(nsAString& aAdapterVendorID) override {
+ if (!mVendorId) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ aAdapterVendorID.AssignASCII(mVendorId);
+ return NS_OK;
+ }
+
+ NS_IMETHOD_(int32_t) GetMaxRefreshRate(bool* aMixed) override {
+ if (aMixed) {
+ *aMixed = mHasMixedRefreshRate;
+ }
+ return mMaxRefreshRate;
+ }
+
+ NS_IMETHODIMP GetEmbeddedInFirefoxReality(
+ bool* aEmbeddedInFirefoxReality) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+
+ // The following methods we don't need for testing gfxConfigManager.
+ NS_IMETHOD GetFeatureSuggestedDriverVersion(int32_t aFeature,
+ nsAString& _retval) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+
+ NS_IMETHOD GetMonitors(JSContext* cx,
+ JS::MutableHandleValue _retval) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ NS_IMETHOD GetFailures(nsTArray<int32_t>& indices,
+ nsTArray<nsCString>& failures) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ NS_IMETHOD_(void) LogFailure(const nsACString& failure) override {}
+ NS_IMETHOD GetInfo(JSContext*, JS::MutableHandle<JS::Value>) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ NS_IMETHOD GetFeatures(JSContext*, JS::MutableHandle<JS::Value>) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ NS_IMETHOD GetFeatureLog(JSContext*, JS::MutableHandle<JS::Value>) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ NS_IMETHOD GetActiveCrashGuards(JSContext*,
+ JS::MutableHandle<JS::Value>) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ NS_IMETHOD GetContentBackend(nsAString& aContentBackend) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ NS_IMETHOD GetUsingGPUProcess(bool* aOutValue) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ NS_IMETHOD GetWebRenderEnabled(bool* aWebRenderEnabled) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ NS_IMETHOD GetIsHeadless(bool* aIsHeadless) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ NS_IMETHOD GetUsesTiling(bool* aUsesTiling) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ NS_IMETHOD GetContentUsesTiling(bool* aUsesTiling) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ NS_IMETHOD GetOffMainThreadPaintEnabled(
+ bool* aOffMainThreadPaintEnabled) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ NS_IMETHOD GetOffMainThreadPaintWorkerCount(
+ int32_t* aOffMainThreadPaintWorkerCount) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ NS_IMETHOD GetTargetFrameRate(uint32_t* aTargetFrameRate) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ NS_IMETHOD GetD2DEnabled(bool* aD2DEnabled) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ NS_IMETHOD GetDWriteEnabled(bool* aDWriteEnabled) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ NS_IMETHOD GetDWriteVersion(nsAString& aDwriteVersion) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ NS_IMETHOD GetCleartypeParameters(nsAString& aCleartypeParams) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ NS_IMETHOD GetWindowProtocol(nsAString& aWindowProtocol) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ NS_IMETHOD GetDesktopEnvironment(nsAString& aDesktopEnvironment) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ NS_IMETHOD GetAdapterDescription(nsAString& aAdapterDescription) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ NS_IMETHOD GetAdapterDriver(nsAString& aAdapterDriver) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ NS_IMETHOD GetAdapterDeviceID(nsAString& aAdapterDeviceID) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ NS_IMETHOD GetAdapterSubsysID(nsAString& aAdapterSubsysID) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ NS_IMETHOD GetAdapterRAM(uint32_t* aAdapterRAM) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ NS_IMETHOD GetAdapterDriverVendor(nsAString& aAdapterDriverVendor) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ NS_IMETHOD GetAdapterDriverVersion(
+ nsAString& aAdapterDriverVersion) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ NS_IMETHOD GetAdapterDriverDate(nsAString& aAdapterDriverDate) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ NS_IMETHOD GetAdapterDescription2(nsAString& aAdapterDescription) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ NS_IMETHOD GetAdapterDriver2(nsAString& aAdapterDriver) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ NS_IMETHOD GetAdapterVendorID2(nsAString& aAdapterVendorID) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ NS_IMETHOD GetAdapterDeviceID2(nsAString& aAdapterDeviceID) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ NS_IMETHOD GetAdapterSubsysID2(nsAString& aAdapterSubsysID) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ NS_IMETHOD GetAdapterRAM2(uint32_t* aAdapterRAM) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ NS_IMETHOD GetAdapterDriverVendor2(nsAString& aAdapterDriverVendor) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ NS_IMETHOD GetAdapterDriverVersion2(
+ nsAString& aAdapterDriverVersion) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ NS_IMETHOD GetAdapterDriverDate2(nsAString& aAdapterDriverDate) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ NS_IMETHOD GetIsGPU2Active(bool* aIsGPU2Active) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ NS_IMETHOD GetDisplayInfo(nsTArray<nsString>& aDisplayInfo) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ NS_IMETHOD GetDisplayWidth(nsTArray<uint32_t>& aDisplayWidth) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ NS_IMETHOD GetDisplayHeight(nsTArray<uint32_t>& aDisplayHeight) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ NS_IMETHOD GetDrmRenderDevice(nsACString& aDrmRenderDevice) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ NS_IMETHOD ControlGPUProcessForXPCShell(bool aEnable,
+ bool* _retval) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ NS_IMETHOD_(void) GetData() override {}
+
+ private:
+ virtual ~MockGfxInfo() = default;
+};
+
+NS_IMPL_ISUPPORTS(MockGfxInfo, nsIGfxInfo)
+
+class GfxConfigManager : public ::testing::Test, public gfxConfigManager {
+ public:
+ GfxConfigManager() {
+ mFeatureWr = &mFeatures.mWr;
+ mFeatureWrQualified = &mFeatures.mWrQualified;
+ mFeatureWrCompositor = &mFeatures.mWrCompositor;
+ mFeatureWrAngle = &mFeatures.mWrAngle;
+ mFeatureWrDComp = &mFeatures.mWrDComp;
+ mFeatureWrPartial = &mFeatures.mWrPartial;
+ mFeatureHwCompositing = &mFeatures.mHwCompositing;
+ mFeatureD3D11HwAngle = &mFeatures.mD3D11HwAngle;
+ mFeatureGPUProcess = &mFeatures.mGPUProcess;
+ mFeatureWrSoftware = &mFeatures.mWrSoftware;
+ }
+
+ void SetUp() override {
+ mMockGfxInfo = new MockGfxInfo();
+ mGfxInfo = mMockGfxInfo;
+
+ // By default, turn everything on. This effectively assumes we are on
+ // qualified nightly Windows 10 with DirectComposition support.
+ mFeatureHwCompositing->EnableByDefault();
+ mFeatureD3D11HwAngle->EnableByDefault();
+ mFeatureGPUProcess->EnableByDefault();
+
+ mWrCompositorEnabled.emplace(true);
+ mWrPartialPresent = true;
+ mWrForceAngle = true;
+ mWrDCompWinEnabled = true;
+ mWrCompositorDCompRequired = true;
+ ++mHwStretchingSupport.mBoth;
+ mIsWin10OrLater = true;
+ mIsNightly = true;
+ }
+
+ void TearDown() override {
+ mFeatures.mWr.Reset();
+ mFeatures.mWrQualified.Reset();
+ mFeatures.mWrCompositor.Reset();
+ mFeatures.mWrAngle.Reset();
+ mFeatures.mWrDComp.Reset();
+ mFeatures.mWrPartial.Reset();
+ mFeatures.mHwCompositing.Reset();
+ mFeatures.mD3D11HwAngle.Reset();
+ mFeatures.mGPUProcess.Reset();
+ mFeatures.mWrSoftware.Reset();
+ }
+
+ struct {
+ FeatureState mWr;
+ FeatureState mWrQualified;
+ FeatureState mWrCompositor;
+ FeatureState mWrAngle;
+ FeatureState mWrDComp;
+ FeatureState mWrPartial;
+ FeatureState mHwCompositing;
+ FeatureState mD3D11HwAngle;
+ FeatureState mGPUProcess;
+ FeatureState mWrSoftware;
+ } mFeatures;
+
+ RefPtr<MockGfxInfo> mMockGfxInfo;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+TEST_F(GfxConfigManager, WebRenderDefault) {
+ ConfigureWebRender();
+
+ EXPECT_TRUE(mFeatures.mWrQualified.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWr.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrCompositor.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrAngle.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrDComp.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrPartial.IsEnabled());
+ EXPECT_TRUE(mFeatures.mHwCompositing.IsEnabled());
+ EXPECT_TRUE(mFeatures.mGPUProcess.IsEnabled());
+ EXPECT_TRUE(mFeatures.mD3D11HwAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrSoftware.IsEnabled());
+}
+
+TEST_F(GfxConfigManager, WebRenderNoPartialPresent) {
+ mWrPartialPresent = false;
+ ConfigureWebRender();
+
+ EXPECT_TRUE(mFeatures.mWrQualified.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWr.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrCompositor.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrAngle.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrDComp.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrPartial.IsEnabled());
+ EXPECT_TRUE(mFeatures.mHwCompositing.IsEnabled());
+ EXPECT_TRUE(mFeatures.mGPUProcess.IsEnabled());
+ EXPECT_TRUE(mFeatures.mD3D11HwAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrSoftware.IsEnabled());
+}
+
+TEST_F(GfxConfigManager, WebRenderScaledResolutionWithHwStretching) {
+ mScaledResolution = true;
+ ConfigureWebRender();
+
+ EXPECT_TRUE(mFeatures.mWrQualified.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWr.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrCompositor.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrAngle.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrDComp.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrPartial.IsEnabled());
+ EXPECT_TRUE(mFeatures.mHwCompositing.IsEnabled());
+ EXPECT_TRUE(mFeatures.mGPUProcess.IsEnabled());
+ EXPECT_TRUE(mFeatures.mD3D11HwAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrSoftware.IsEnabled());
+}
+
+TEST_F(GfxConfigManager, WebRenderScaledResolutionNoHwStretching) {
+ ++mHwStretchingSupport.mNone;
+ mScaledResolution = true;
+ ConfigureWebRender();
+
+ EXPECT_TRUE(mFeatures.mWrQualified.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWr.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrCompositor.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrAngle.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrDComp.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrPartial.IsEnabled());
+ EXPECT_TRUE(mFeatures.mHwCompositing.IsEnabled());
+ EXPECT_TRUE(mFeatures.mGPUProcess.IsEnabled());
+ EXPECT_TRUE(mFeatures.mD3D11HwAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrSoftware.IsEnabled());
+}
+
+TEST_F(GfxConfigManager, WebRenderEnabledWithDisableHwCompositingNoWr) {
+ mDisableHwCompositingNoWr = true;
+ ConfigureWebRender();
+
+ EXPECT_TRUE(mFeatures.mWrQualified.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWr.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrCompositor.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrAngle.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrDComp.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrPartial.IsEnabled());
+ EXPECT_TRUE(mFeatures.mHwCompositing.IsEnabled());
+ EXPECT_TRUE(mFeatures.mGPUProcess.IsEnabled());
+ EXPECT_TRUE(mFeatures.mD3D11HwAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrSoftware.IsEnabled());
+}
+
+TEST_F(GfxConfigManager, WebRenderDisabledWithDisableHwCompositingNoWr) {
+ mDisableHwCompositingNoWr = true;
+ mMockGfxInfo->mStatusWr = nsIGfxInfo::FEATURE_DENIED;
+ ConfigureWebRender();
+
+ EXPECT_FALSE(mFeatures.mWrQualified.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWr.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrCompositor.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrDComp.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrPartial.IsEnabled());
+ EXPECT_FALSE(mFeatures.mHwCompositing.IsEnabled());
+ EXPECT_FALSE(mFeatures.mGPUProcess.IsEnabled());
+ EXPECT_TRUE(mFeatures.mD3D11HwAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrSoftware.IsEnabled());
+}
+
+TEST_F(GfxConfigManager, WebRenderDisabledWithAllowSoftwareGPUProcess) {
+ mDisableHwCompositingNoWr = true;
+ mMockGfxInfo->mStatusWr = nsIGfxInfo::FEATURE_DENIED;
+ mGPUProcessAllowSoftware = true;
+ ConfigureWebRender();
+
+ EXPECT_FALSE(mFeatures.mWrQualified.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWr.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrCompositor.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrDComp.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrPartial.IsEnabled());
+ EXPECT_FALSE(mFeatures.mHwCompositing.IsEnabled());
+ EXPECT_TRUE(mFeatures.mGPUProcess.IsEnabled());
+ EXPECT_TRUE(mFeatures.mD3D11HwAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrSoftware.IsEnabled());
+}
+
+TEST_F(GfxConfigManager, WebRenderSafeMode) {
+ mSafeMode = true;
+ mMockGfxInfo->mStatusWrSoftware = nsIGfxInfo::FEATURE_ALLOW_ALWAYS;
+ ConfigureWebRender();
+
+ EXPECT_TRUE(mFeatures.mWrQualified.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWr.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrCompositor.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrDComp.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrPartial.IsEnabled());
+ EXPECT_TRUE(mFeatures.mHwCompositing.IsEnabled());
+ EXPECT_TRUE(mFeatures.mGPUProcess.IsEnabled());
+ EXPECT_TRUE(mFeatures.mD3D11HwAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrSoftware.IsEnabled());
+}
+
+TEST_F(GfxConfigManager, WebRenderEarlierThanWindows10) {
+ mIsWin10OrLater = false;
+ ConfigureWebRender();
+
+ EXPECT_TRUE(mFeatures.mWrQualified.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWr.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrCompositor.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrDComp.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrPartial.IsEnabled());
+ EXPECT_TRUE(mFeatures.mHwCompositing.IsEnabled());
+ EXPECT_TRUE(mFeatures.mGPUProcess.IsEnabled());
+ EXPECT_TRUE(mFeatures.mD3D11HwAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrSoftware.IsEnabled());
+}
+
+TEST_F(GfxConfigManager, WebRenderDCompDisabled) {
+ mWrDCompWinEnabled = false;
+ ConfigureWebRender();
+
+ EXPECT_TRUE(mFeatures.mWrQualified.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWr.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrCompositor.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrDComp.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrPartial.IsEnabled());
+ EXPECT_TRUE(mFeatures.mHwCompositing.IsEnabled());
+ EXPECT_TRUE(mFeatures.mGPUProcess.IsEnabled());
+ EXPECT_TRUE(mFeatures.mD3D11HwAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrSoftware.IsEnabled());
+}
+
+TEST_F(GfxConfigManager, WebRenderDCompNotRequired) {
+ mWrDCompWinEnabled = false;
+ mWrCompositorDCompRequired = false;
+ ConfigureWebRender();
+
+ EXPECT_TRUE(mFeatures.mWrQualified.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWr.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrCompositor.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrDComp.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrPartial.IsEnabled());
+ EXPECT_TRUE(mFeatures.mHwCompositing.IsEnabled());
+ EXPECT_TRUE(mFeatures.mGPUProcess.IsEnabled());
+ EXPECT_TRUE(mFeatures.mD3D11HwAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrSoftware.IsEnabled());
+}
+
+TEST_F(GfxConfigManager, WebRenderForceAngleDisabled) {
+ mWrForceAngle = false;
+ ConfigureWebRender();
+
+ EXPECT_TRUE(mFeatures.mWrQualified.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWr.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrCompositor.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrDComp.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrPartial.IsEnabled());
+ EXPECT_TRUE(mFeatures.mHwCompositing.IsEnabled());
+ EXPECT_TRUE(mFeatures.mGPUProcess.IsEnabled());
+ EXPECT_TRUE(mFeatures.mD3D11HwAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrSoftware.IsEnabled());
+}
+
+TEST_F(GfxConfigManager, WebRenderD3D11HwAngleDisabled) {
+ mFeatures.mD3D11HwAngle.UserDisable("", ""_ns);
+ ConfigureWebRender();
+
+ EXPECT_TRUE(mFeatures.mWrQualified.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWr.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrCompositor.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrDComp.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrPartial.IsEnabled());
+ EXPECT_TRUE(mFeatures.mHwCompositing.IsEnabled());
+ EXPECT_TRUE(mFeatures.mGPUProcess.IsEnabled());
+ EXPECT_FALSE(mFeatures.mD3D11HwAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrSoftware.IsEnabled());
+}
+
+TEST_F(GfxConfigManager, WebRenderD3D11HwAngleAndForceAngleDisabled) {
+ mWrForceAngle = false;
+ mFeatures.mD3D11HwAngle.UserDisable("", ""_ns);
+ ConfigureWebRender();
+
+ EXPECT_TRUE(mFeatures.mWrQualified.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWr.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrCompositor.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrDComp.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrPartial.IsEnabled());
+ EXPECT_TRUE(mFeatures.mHwCompositing.IsEnabled());
+ EXPECT_TRUE(mFeatures.mGPUProcess.IsEnabled());
+ EXPECT_FALSE(mFeatures.mD3D11HwAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrSoftware.IsEnabled());
+}
+
+TEST_F(GfxConfigManager, WebRenderGPUProcessDisabled) {
+ mFeatures.mGPUProcess.UserDisable("", ""_ns);
+ ConfigureWebRender();
+
+ EXPECT_TRUE(mFeatures.mWrQualified.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWr.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrCompositor.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrDComp.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrPartial.IsEnabled());
+ EXPECT_TRUE(mFeatures.mHwCompositing.IsEnabled());
+ EXPECT_FALSE(mFeatures.mGPUProcess.IsEnabled());
+ EXPECT_TRUE(mFeatures.mD3D11HwAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrSoftware.IsEnabled());
+}
+
+TEST_F(GfxConfigManager, WebRenderQualifiedAndBlocklistAllowQualified) {
+ mMockGfxInfo->mStatusWr = nsIGfxInfo::FEATURE_ALLOW_QUALIFIED;
+ ConfigureWebRender();
+
+ EXPECT_TRUE(mFeatures.mWrQualified.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWr.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrCompositor.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrAngle.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrDComp.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrPartial.IsEnabled());
+ EXPECT_TRUE(mFeatures.mHwCompositing.IsEnabled());
+ EXPECT_TRUE(mFeatures.mGPUProcess.IsEnabled());
+ EXPECT_TRUE(mFeatures.mD3D11HwAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrSoftware.IsEnabled());
+}
+
+TEST_F(GfxConfigManager, WebRenderQualifiedAndBlocklistAllowAlways) {
+ mIsNightly = false;
+ mMockGfxInfo->mStatusWr = nsIGfxInfo::FEATURE_ALLOW_ALWAYS;
+ ConfigureWebRender();
+
+ EXPECT_TRUE(mFeatures.mWrQualified.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWr.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrCompositor.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrAngle.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrDComp.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrPartial.IsEnabled());
+ EXPECT_TRUE(mFeatures.mHwCompositing.IsEnabled());
+ EXPECT_TRUE(mFeatures.mGPUProcess.IsEnabled());
+ EXPECT_TRUE(mFeatures.mD3D11HwAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrSoftware.IsEnabled());
+}
+
+TEST_F(GfxConfigManager, WebRenderIntelBatteryNoHwStretchingNotNightly) {
+ mIsNightly = false;
+ ++mHwStretchingSupport.mNone;
+ mScaledResolution = true;
+ mMockGfxInfo->mHasBattery.ref() = true;
+ mMockGfxInfo->mVendorId = "0x8086";
+ ConfigureWebRender();
+
+ EXPECT_TRUE(mFeatures.mWrQualified.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWr.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrCompositor.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrAngle.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrDComp.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrPartial.IsEnabled());
+ EXPECT_TRUE(mFeatures.mHwCompositing.IsEnabled());
+ EXPECT_TRUE(mFeatures.mGPUProcess.IsEnabled());
+ EXPECT_TRUE(mFeatures.mD3D11HwAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrSoftware.IsEnabled());
+}
+
+TEST_F(GfxConfigManager, WebRenderIntelHighRefreshRateNightly) {
+ mIsNightly = true;
+ mMockGfxInfo->mMaxRefreshRate = 120;
+ mMockGfxInfo->mVendorId = "0x8086";
+ ConfigureWebRender();
+
+ EXPECT_TRUE(mFeatures.mWrQualified.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWr.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrCompositor.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrAngle.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrDComp.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrPartial.IsEnabled());
+ EXPECT_TRUE(mFeatures.mHwCompositing.IsEnabled());
+ EXPECT_TRUE(mFeatures.mGPUProcess.IsEnabled());
+ EXPECT_TRUE(mFeatures.mD3D11HwAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrSoftware.IsEnabled());
+}
+
+TEST_F(GfxConfigManager, WebRenderIntelHighRefreshRateNotNightly) {
+ mIsNightly = false;
+ mMockGfxInfo->mMaxRefreshRate = 120;
+ mMockGfxInfo->mVendorId = "0x8086";
+ ConfigureWebRender();
+
+ EXPECT_FALSE(mFeatures.mWrQualified.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWr.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrCompositor.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrDComp.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrPartial.IsEnabled());
+ EXPECT_TRUE(mFeatures.mHwCompositing.IsEnabled());
+ EXPECT_TRUE(mFeatures.mGPUProcess.IsEnabled());
+ EXPECT_TRUE(mFeatures.mD3D11HwAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrSoftware.IsEnabled());
+}
+
+TEST_F(GfxConfigManager, WebRenderIntelAtRefreshRateThreshold) {
+ mIsNightly = false;
+ mMockGfxInfo->mMaxRefreshRate = 75;
+ mMockGfxInfo->mVendorId = "0x8086";
+ ConfigureWebRender();
+
+ EXPECT_TRUE(mFeatures.mWrQualified.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWr.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrCompositor.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrAngle.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrDComp.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrPartial.IsEnabled());
+ EXPECT_TRUE(mFeatures.mHwCompositing.IsEnabled());
+ EXPECT_TRUE(mFeatures.mGPUProcess.IsEnabled());
+ EXPECT_TRUE(mFeatures.mD3D11HwAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrSoftware.IsEnabled());
+}
+
+TEST_F(GfxConfigManager, WebRenderNvidiaHighMixedRefreshRateNightly) {
+ mIsNightly = true;
+ mMockGfxInfo->mMaxRefreshRate = 120;
+ mMockGfxInfo->mHasMixedRefreshRate = true;
+ ConfigureWebRender();
+
+ EXPECT_TRUE(mFeatures.mWrQualified.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWr.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrCompositor.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrAngle.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrDComp.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrPartial.IsEnabled());
+ EXPECT_TRUE(mFeatures.mHwCompositing.IsEnabled());
+ EXPECT_TRUE(mFeatures.mGPUProcess.IsEnabled());
+ EXPECT_TRUE(mFeatures.mD3D11HwAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrSoftware.IsEnabled());
+}
+
+TEST_F(GfxConfigManager, WebRenderNvidiaHighMixedRefreshRateNotNightly) {
+ mIsNightly = false;
+ mMockGfxInfo->mMaxRefreshRate = 120;
+ mMockGfxInfo->mHasMixedRefreshRate = true;
+ ConfigureWebRender();
+
+ EXPECT_FALSE(mFeatures.mWrQualified.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWr.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrCompositor.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrDComp.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrPartial.IsEnabled());
+ EXPECT_TRUE(mFeatures.mHwCompositing.IsEnabled());
+ EXPECT_TRUE(mFeatures.mGPUProcess.IsEnabled());
+ EXPECT_TRUE(mFeatures.mD3D11HwAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrSoftware.IsEnabled());
+}
+
+TEST_F(GfxConfigManager, WebRenderNvidiaHighRefreshRateNotNightly) {
+ mMockGfxInfo->mMaxRefreshRate = 120;
+ ConfigureWebRender();
+
+ EXPECT_TRUE(mFeatures.mWrQualified.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWr.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrCompositor.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrAngle.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrDComp.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrPartial.IsEnabled());
+ EXPECT_TRUE(mFeatures.mHwCompositing.IsEnabled());
+ EXPECT_TRUE(mFeatures.mGPUProcess.IsEnabled());
+ EXPECT_TRUE(mFeatures.mD3D11HwAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrSoftware.IsEnabled());
+}
+
+TEST_F(GfxConfigManager, WebRenderNvidiaLowMixedRefreshRateNotNightly) {
+ mMockGfxInfo->mMaxRefreshRate = 60;
+ mMockGfxInfo->mHasMixedRefreshRate = true;
+ ConfigureWebRender();
+
+ EXPECT_TRUE(mFeatures.mWrQualified.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWr.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrCompositor.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrAngle.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrDComp.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrPartial.IsEnabled());
+ EXPECT_TRUE(mFeatures.mHwCompositing.IsEnabled());
+ EXPECT_TRUE(mFeatures.mGPUProcess.IsEnabled());
+ EXPECT_TRUE(mFeatures.mD3D11HwAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrSoftware.IsEnabled());
+}
+
+TEST_F(GfxConfigManager, WebRenderWhenXRenderEnabled) {
+ mXRenderEnabled = true;
+ mMockGfxInfo->mStatusWrSoftware = nsIGfxInfo::FEATURE_ALLOW_ALWAYS;
+ ConfigureWebRender();
+
+ EXPECT_TRUE(mFeatures.mWrQualified.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWr.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrCompositor.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrDComp.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrPartial.IsEnabled());
+ EXPECT_TRUE(mFeatures.mHwCompositing.IsEnabled());
+ EXPECT_TRUE(mFeatures.mGPUProcess.IsEnabled());
+ EXPECT_TRUE(mFeatures.mD3D11HwAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrSoftware.IsEnabled());
+}
+
+TEST_F(GfxConfigManager, WebRenderSofwareAndQualified) {
+ // Enabling software in gfxInfo gives the same results
+ // as the default configuration, since qualified hardware
+ // takes precedence, but we still enable the software feature.
+ mMockGfxInfo->mStatusWrSoftware = nsIGfxInfo::FEATURE_ALLOW_ALWAYS;
+ ConfigureWebRender();
+
+ EXPECT_TRUE(mFeatures.mWrQualified.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWr.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrCompositor.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrAngle.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrDComp.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrPartial.IsEnabled());
+ EXPECT_TRUE(mFeatures.mHwCompositing.IsEnabled());
+ EXPECT_TRUE(mFeatures.mGPUProcess.IsEnabled());
+ EXPECT_TRUE(mFeatures.mD3D11HwAngle.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrSoftware.IsEnabled());
+}
+
+TEST_F(GfxConfigManager, WebRenderSofwareAndNotQualified) {
+ // Enabling software in gfxInfo when we're not qualified
+ // results in WR software being enabled instead.
+ mMockGfxInfo->mStatusWr = nsIGfxInfo::FEATURE_DENIED;
+ mMockGfxInfo->mStatusWrSoftware = nsIGfxInfo::FEATURE_ALLOW_ALWAYS;
+ ConfigureWebRender();
+
+ EXPECT_FALSE(mFeatures.mWrQualified.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWr.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrCompositor.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrDComp.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrPartial.IsEnabled());
+ EXPECT_TRUE(mFeatures.mHwCompositing.IsEnabled());
+ EXPECT_TRUE(mFeatures.mGPUProcess.IsEnabled());
+ EXPECT_TRUE(mFeatures.mD3D11HwAngle.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrSoftware.IsEnabled());
+}
+
+TEST_F(GfxConfigManager, WebRenderForceDisabledEnvvar) {
+ mWrEnvForceDisabled = true;
+ ConfigureWebRender();
+
+ EXPECT_TRUE(mFeatures.mWrQualified.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWr.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrCompositor.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrDComp.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrPartial.IsEnabled());
+ EXPECT_TRUE(mFeatures.mHwCompositing.IsEnabled());
+ EXPECT_TRUE(mFeatures.mGPUProcess.IsEnabled());
+ EXPECT_TRUE(mFeatures.mD3D11HwAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrSoftware.IsEnabled());
+}
+
+TEST_F(GfxConfigManager, WebRenderSoftwareAllowedForceDisabledEnvvar) {
+ mWrEnvForceDisabled = true;
+ mMockGfxInfo->mStatusWrSoftware = nsIGfxInfo::FEATURE_ALLOW_ALWAYS;
+ ConfigureWebRender();
+
+ EXPECT_TRUE(mFeatures.mWrQualified.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWr.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrCompositor.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrDComp.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrPartial.IsEnabled());
+ EXPECT_TRUE(mFeatures.mHwCompositing.IsEnabled());
+ EXPECT_TRUE(mFeatures.mGPUProcess.IsEnabled());
+ EXPECT_TRUE(mFeatures.mD3D11HwAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrSoftware.IsEnabled());
+}
+
+TEST_F(GfxConfigManager, WebRenderSoftwareAllowedForceDisabledPref) {
+ mWrForceDisabled = true;
+ mMockGfxInfo->mStatusWrSoftware = nsIGfxInfo::FEATURE_ALLOW_ALWAYS;
+ ConfigureWebRender();
+
+ EXPECT_TRUE(mFeatures.mWrQualified.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWr.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrCompositor.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrDComp.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrPartial.IsEnabled());
+ EXPECT_TRUE(mFeatures.mHwCompositing.IsEnabled());
+ EXPECT_TRUE(mFeatures.mGPUProcess.IsEnabled());
+ EXPECT_TRUE(mFeatures.mD3D11HwAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrSoftware.IsEnabled());
+}
+
+TEST_F(GfxConfigManager, WebRenderForceSoftwareForceDisabledEnvvar) {
+ mWrEnvForceDisabled = true;
+ mWrSoftwareForceEnabled = true;
+ ConfigureWebRender();
+
+ EXPECT_TRUE(mFeatures.mWrQualified.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWr.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrCompositor.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrDComp.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrPartial.IsEnabled());
+ EXPECT_TRUE(mFeatures.mHwCompositing.IsEnabled());
+ EXPECT_TRUE(mFeatures.mGPUProcess.IsEnabled());
+ EXPECT_TRUE(mFeatures.mD3D11HwAngle.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrSoftware.IsEnabled());
+}
+
+TEST_F(GfxConfigManager, WebRenderForceEnabledEnvvar) {
+ mWrEnvForceEnabled = true;
+ mMockGfxInfo->mStatusWr = nsIGfxInfo::FEATURE_DENIED;
+ ConfigureWebRender();
+
+ EXPECT_FALSE(mFeatures.mWrQualified.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWr.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrCompositor.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrAngle.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrDComp.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrPartial.IsEnabled());
+ EXPECT_TRUE(mFeatures.mHwCompositing.IsEnabled());
+ EXPECT_TRUE(mFeatures.mGPUProcess.IsEnabled());
+ EXPECT_TRUE(mFeatures.mD3D11HwAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrSoftware.IsEnabled());
+}
+
+TEST_F(GfxConfigManager, WebRenderSoftwareAllowedForceEnabledEnvvar) {
+ mWrEnvForceEnabled = true;
+ mMockGfxInfo->mStatusWr = nsIGfxInfo::FEATURE_DENIED;
+ mMockGfxInfo->mStatusWrSoftware = nsIGfxInfo::FEATURE_ALLOW_ALWAYS;
+ ConfigureWebRender();
+
+ EXPECT_FALSE(mFeatures.mWrQualified.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWr.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrCompositor.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrAngle.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrDComp.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrPartial.IsEnabled());
+ EXPECT_TRUE(mFeatures.mHwCompositing.IsEnabled());
+ EXPECT_TRUE(mFeatures.mGPUProcess.IsEnabled());
+ EXPECT_TRUE(mFeatures.mD3D11HwAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrSoftware.IsEnabled());
+}
+
+TEST_F(GfxConfigManager, WebRenderSoftwareAllowedForceEnabledPref) {
+ mWrForceEnabled = true;
+ mMockGfxInfo->mStatusWr = nsIGfxInfo::FEATURE_DENIED;
+ mMockGfxInfo->mStatusWrSoftware = nsIGfxInfo::FEATURE_ALLOW_ALWAYS;
+ ConfigureWebRender();
+
+ EXPECT_FALSE(mFeatures.mWrQualified.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWr.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrCompositor.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrAngle.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrDComp.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrPartial.IsEnabled());
+ EXPECT_TRUE(mFeatures.mHwCompositing.IsEnabled());
+ EXPECT_TRUE(mFeatures.mGPUProcess.IsEnabled());
+ EXPECT_TRUE(mFeatures.mD3D11HwAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrSoftware.IsEnabled());
+}
+
+TEST_F(GfxConfigManager, WebRenderForceSoftwareForceEnabledEnvvar) {
+ mWrEnvForceEnabled = true;
+ mWrSoftwareForceEnabled = true;
+ mMockGfxInfo->mStatusWr = nsIGfxInfo::FEATURE_DENIED;
+ ConfigureWebRender();
+
+ EXPECT_FALSE(mFeatures.mWrQualified.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWr.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrCompositor.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrDComp.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrPartial.IsEnabled());
+ EXPECT_TRUE(mFeatures.mHwCompositing.IsEnabled());
+ EXPECT_TRUE(mFeatures.mGPUProcess.IsEnabled());
+ EXPECT_TRUE(mFeatures.mD3D11HwAngle.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrSoftware.IsEnabled());
+}
diff --git a/gfx/tests/gtest/TestGfxWidgets.cpp b/gfx/tests/gtest/TestGfxWidgets.cpp
new file mode 100644
index 0000000000..90bfce863a
--- /dev/null
+++ b/gfx/tests/gtest/TestGfxWidgets.cpp
@@ -0,0 +1,109 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gtest/gtest.h"
+#include "GfxDriverInfo.h"
+#include "nsVersionComparator.h"
+
+using namespace mozilla::widget;
+
+TEST(GfxWidgets, Split)
+{
+ char aStr[8], bStr[8], cStr[8], dStr[8];
+
+ ASSERT_TRUE(SplitDriverVersion("33.4.3.22", aStr, bStr, cStr, dStr));
+ ASSERT_TRUE(atoi(aStr) == 33 && atoi(bStr) == 4 && atoi(cStr) == 3 &&
+ atoi(dStr) == 22);
+
+ ASSERT_TRUE(SplitDriverVersion("28.74.0.0", aStr, bStr, cStr, dStr));
+ ASSERT_TRUE(atoi(aStr) == 28 && atoi(bStr) == 74 && atoi(cStr) == 0 &&
+ atoi(dStr) == 0);
+
+ ASSERT_TRUE(SplitDriverVersion("132.0.0.0", aStr, bStr, cStr, dStr));
+ ASSERT_TRUE(atoi(aStr) == 132 && atoi(bStr) == 0 && atoi(cStr) == 0 &&
+ atoi(dStr) == 0);
+
+ ASSERT_TRUE(SplitDriverVersion("2.3.0.0", aStr, bStr, cStr, dStr));
+ ASSERT_TRUE(atoi(aStr) == 2 && atoi(bStr) == 3 && atoi(cStr) == 0 &&
+ atoi(dStr) == 0);
+
+ ASSERT_TRUE(SplitDriverVersion("25.4.0.8", aStr, bStr, cStr, dStr));
+ ASSERT_TRUE(atoi(aStr) == 25 && atoi(bStr) == 4 && atoi(cStr) == 0 &&
+ atoi(dStr) == 8);
+
+ ASSERT_TRUE(SplitDriverVersion("424.143.84437.3", aStr, bStr, cStr, dStr));
+ ASSERT_TRUE(atoi(aStr) == 424 && atoi(bStr) == 143 && atoi(cStr) == 8443 &&
+ atoi(dStr) == 3);
+
+ ASSERT_FALSE(SplitDriverVersion("25.4.0.8.", aStr, bStr, cStr, dStr));
+ ASSERT_TRUE(atoi(aStr) == 25 && atoi(bStr) == 4 && atoi(cStr) == 0 &&
+ atoi(dStr) == 8);
+
+ ASSERT_TRUE(SplitDriverVersion("424.143.8.3143243", aStr, bStr, cStr, dStr));
+ ASSERT_TRUE(atoi(aStr) == 424 && atoi(bStr) == 143 && atoi(cStr) == 8 &&
+ atoi(dStr) == 3143);
+
+ ASSERT_FALSE(SplitDriverVersion("25.4.0.8..", aStr, bStr, cStr, dStr));
+ ASSERT_TRUE(atoi(aStr) == 25 && atoi(bStr) == 4 && atoi(cStr) == 0 &&
+ atoi(dStr) == 8);
+
+ ASSERT_FALSE(
+ SplitDriverVersion("424.143.8.3143243.", aStr, bStr, cStr, dStr));
+ ASSERT_TRUE(atoi(aStr) == 424 && atoi(bStr) == 143 && atoi(cStr) == 8 &&
+ atoi(dStr) == 3143);
+
+ ASSERT_FALSE(SplitDriverVersion("25.4.0.8.13", aStr, bStr, cStr, dStr));
+ ASSERT_TRUE(atoi(aStr) == 25 && atoi(bStr) == 4 && atoi(cStr) == 0 &&
+ atoi(dStr) == 8);
+
+ ASSERT_FALSE(SplitDriverVersion("4.1.8.13.24.35", aStr, bStr, cStr, dStr));
+ ASSERT_TRUE(atoi(aStr) == 4 && atoi(bStr) == 1 && atoi(cStr) == 8 &&
+ atoi(dStr) == 13);
+
+ ASSERT_TRUE(SplitDriverVersion("28...74", aStr, bStr, cStr, dStr));
+ ASSERT_TRUE(atoi(aStr) == 28 && atoi(bStr) == 0 && atoi(cStr) == 0 &&
+ atoi(dStr) == 74);
+
+ ASSERT_FALSE(SplitDriverVersion("4.1.8.13.24.35", aStr, bStr, cStr, dStr));
+ ASSERT_TRUE(atoi(aStr) == 4 && atoi(bStr) == 1 && atoi(cStr) == 8 &&
+ atoi(dStr) == 13);
+
+ ASSERT_TRUE(SplitDriverVersion("35..42.0", aStr, bStr, cStr, dStr));
+ ASSERT_TRUE(atoi(aStr) == 35 && atoi(bStr) == 0 && atoi(cStr) == 42 &&
+ atoi(dStr) == 0);
+}
+
+TEST(GfxWidgets, Versioning)
+{
+ ASSERT_TRUE(mozilla::Version("0") < mozilla::Version("41.0a1"));
+ ASSERT_TRUE(mozilla::Version("39.0.5b7") < mozilla::Version("41.0a1"));
+ ASSERT_TRUE(mozilla::Version("18.0.5b7") < mozilla::Version("18.2"));
+ ASSERT_TRUE(mozilla::Version("30.0.5b7") < mozilla::Version("41.0b9"));
+ ASSERT_TRUE(mozilla::Version("100") > mozilla::Version("43.0a1"));
+ ASSERT_FALSE(mozilla::Version("42.0") < mozilla::Version("42.0"));
+ ASSERT_TRUE(mozilla::Version("42.0b2") < mozilla::Version("42.0"));
+ ASSERT_TRUE(mozilla::Version("42.0b2") < mozilla::Version("42"));
+ ASSERT_TRUE(mozilla::Version("42.0b2") < mozilla::Version("43.0a1"));
+ ASSERT_TRUE(mozilla::Version("42") < mozilla::Version("43.0a1"));
+ ASSERT_TRUE(mozilla::Version("42.0") < mozilla::Version("43.0a1"));
+ ASSERT_TRUE(mozilla::Version("42.0.5") < mozilla::Version("43.0a1"));
+ ASSERT_TRUE(mozilla::Version("42.1") < mozilla::Version("43.0a1"));
+ ASSERT_TRUE(mozilla::Version("42.0a1") < mozilla::Version("42"));
+ ASSERT_TRUE(mozilla::Version("42.0a1") < mozilla::Version("42.0.5"));
+ ASSERT_TRUE(mozilla::Version("42.0b7") < mozilla::Version("42.0.5"));
+ ASSERT_TRUE(mozilla::Version("") == mozilla::Version("0"));
+ ASSERT_TRUE(mozilla::Version("1b1b") == mozilla::Version("1b1b"));
+ ASSERT_TRUE(mozilla::Version("1b1b") < mozilla::Version("1b1c"));
+ ASSERT_TRUE(mozilla::Version("1b1b") < mozilla::Version("1b1d"));
+ ASSERT_TRUE(mozilla::Version("1b1c") > mozilla::Version("1b1b"));
+ ASSERT_TRUE(mozilla::Version("1b1d") > mozilla::Version("1b1b"));
+
+ // Note these two; one would expect for 42.0b1 and 42b1 to compare the
+ // same, but they do not. If this ever changes, we want to know, so
+ // leave the test here to fail.
+ ASSERT_TRUE(mozilla::Version("42.0a1") < mozilla::Version("42.0b2"));
+ ASSERT_FALSE(mozilla::Version("42.0a1") < mozilla::Version("42b2"));
+}
diff --git a/gfx/tests/gtest/TestLayers.cpp b/gfx/tests/gtest/TestLayers.cpp
new file mode 100644
index 0000000000..cebd5a9622
--- /dev/null
+++ b/gfx/tests/gtest/TestLayers.cpp
@@ -0,0 +1,505 @@
+/* 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 "TestLayers.h"
+#include "gtest/gtest.h"
+#include "gmock/gmock.h"
+#include "LayerUserData.h"
+#include "mozilla/layers/LayerMetricsWrapper.h"
+#include "mozilla/layers/CompositorBridgeParent.h"
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+using namespace mozilla::layers;
+
+class TestContainerLayer : public ContainerLayer {
+ public:
+ explicit TestContainerLayer(LayerManager* aManager)
+ : ContainerLayer(aManager, nullptr) {}
+
+ virtual const char* Name() const { return "TestContainerLayer"; }
+
+ virtual LayerType GetType() const { return TYPE_CONTAINER; }
+
+ virtual void ComputeEffectiveTransforms(
+ const Matrix4x4& aTransformToSurface) {
+ DefaultComputeEffectiveTransforms(aTransformToSurface);
+ }
+};
+
+class TestPaintedLayer : public PaintedLayer {
+ public:
+ explicit TestPaintedLayer(LayerManager* aManager)
+ : PaintedLayer(aManager, nullptr) {}
+
+ virtual const char* Name() const { return "TestPaintedLayer"; }
+
+ virtual LayerType GetType() const { return TYPE_PAINTED; }
+
+ virtual void InvalidateRegion(const nsIntRegion& aRegion) { MOZ_CRASH(); }
+};
+
+class TestLayerManager : public LayerManager {
+ public:
+ TestLayerManager() : LayerManager() {}
+
+ virtual bool EndEmptyTransaction(EndTransactionFlags aFlags = END_DEFAULT) {
+ return false;
+ }
+ virtual already_AddRefed<ContainerLayer> CreateContainerLayer() {
+ RefPtr<ContainerLayer> layer = new TestContainerLayer(this);
+ return layer.forget();
+ }
+ virtual void GetBackendName(nsAString& aName) {}
+ virtual LayersBackend GetBackendType() { return LayersBackend::LAYERS_BASIC; }
+ virtual bool BeginTransaction(const nsCString& = nsCString()) { return true; }
+ virtual already_AddRefed<ImageLayer> CreateImageLayer() {
+ MOZ_CRASH("Not implemented.");
+ }
+ virtual already_AddRefed<PaintedLayer> CreatePaintedLayer() {
+ RefPtr<PaintedLayer> layer = new TestPaintedLayer(this);
+ return layer.forget();
+ }
+ virtual already_AddRefed<ColorLayer> CreateColorLayer() {
+ MOZ_CRASH("Not implemented.");
+ }
+ virtual void SetRoot(Layer* aLayer) {}
+ virtual bool BeginTransactionWithTarget(gfxContext* aTarget,
+ const nsCString& = nsCString()) {
+ return true;
+ }
+ virtual already_AddRefed<CanvasLayer> CreateCanvasLayer() {
+ MOZ_CRASH("Not implemented.");
+ }
+ virtual void EndTransaction(DrawPaintedLayerCallback aCallback,
+ void* aCallbackData,
+ EndTransactionFlags aFlags = END_DEFAULT) {}
+ virtual int32_t GetMaxTextureSize() const { return 0; }
+};
+
+class TestUserData : public LayerUserData {
+ public:
+ MOCK_METHOD0(Die, void());
+ virtual ~TestUserData() { Die(); }
+};
+
+TEST(Layers, LayerConstructor)
+{ TestContainerLayer layer(nullptr); }
+
+TEST(Layers, Defaults)
+{
+ TestContainerLayer layer(nullptr);
+ ASSERT_EQ(1.0, layer.GetOpacity());
+ ASSERT_EQ(1.0f, layer.GetPostXScale());
+ ASSERT_EQ(1.0f, layer.GetPostYScale());
+
+ ASSERT_EQ(nullptr, layer.GetNextSibling());
+ ASSERT_EQ(nullptr, layer.GetPrevSibling());
+ ASSERT_EQ(nullptr, layer.GetFirstChild());
+ ASSERT_EQ(nullptr, layer.GetLastChild());
+}
+
+TEST(Layers, Transform)
+{
+ TestContainerLayer layer(nullptr);
+
+ Matrix4x4 identity;
+ ASSERT_EQ(true, identity.IsIdentity());
+
+ ASSERT_EQ(identity, layer.GetTransform());
+}
+
+TEST(Layers, Type)
+{
+ TestContainerLayer layer(nullptr);
+ ASSERT_EQ(nullptr, layer.AsPaintedLayer());
+ ASSERT_EQ(nullptr, layer.AsRefLayer());
+ ASSERT_EQ(nullptr, layer.AsColorLayer());
+}
+
+TEST(Layers, UserData)
+{
+ UniquePtr<TestContainerLayer> layerPtr(new TestContainerLayer(nullptr));
+ TestContainerLayer& layer = *layerPtr;
+
+ void* key1 = (void*)1;
+ void* key2 = (void*)2;
+ void* key3 = (void*)3;
+
+ ASSERT_EQ(nullptr, layer.GetUserData(key1));
+ ASSERT_EQ(nullptr, layer.GetUserData(key2));
+ ASSERT_EQ(nullptr, layer.GetUserData(key3));
+
+ TestUserData* data1 = new TestUserData;
+ TestUserData* data2 = new TestUserData;
+ TestUserData* data3 = new TestUserData;
+
+ layer.SetUserData(key1, data1);
+ layer.SetUserData(key2, data2);
+ layer.SetUserData(key3, data3);
+
+ // Also checking that the user data is returned but not free'd
+ UniquePtr<LayerUserData> d1(layer.RemoveUserData(key1));
+ UniquePtr<LayerUserData> d2(layer.RemoveUserData(key2));
+ UniquePtr<LayerUserData> d3(layer.RemoveUserData(key3));
+ ASSERT_EQ(data1, d1.get());
+ ASSERT_EQ(data2, d2.get());
+ ASSERT_EQ(data3, d3.get());
+
+ layer.SetUserData(key1, d1.release());
+ layer.SetUserData(key2, d2.release());
+ layer.SetUserData(key3, d3.release());
+
+ // Layer has ownership of data1-3, check that they are destroyed
+ EXPECT_CALL(*data1, Die());
+ EXPECT_CALL(*data2, Die());
+ EXPECT_CALL(*data3, Die());
+}
+
+static already_AddRefed<Layer> CreateLayer(char aLayerType,
+ LayerManager* aManager) {
+ RefPtr<Layer> layer = nullptr;
+ if (aLayerType == 'c') {
+ layer = aManager->CreateContainerLayer();
+ } else if (aLayerType == 't') {
+ layer = aManager->CreatePaintedLayer();
+ } else if (aLayerType == 'o') {
+ layer = aManager->CreateColorLayer();
+ }
+ return layer.forget();
+}
+
+already_AddRefed<Layer> CreateLayerTree(const char* aLayerTreeDescription,
+ nsIntRegion* aVisibleRegions,
+ const Matrix4x4* aTransforms,
+ RefPtr<LayerManager>& manager,
+ nsTArray<RefPtr<Layer> >& aLayersOut) {
+ aLayersOut.Clear();
+
+ if (!manager) {
+ manager = new TestLayerManager();
+ }
+
+ RefPtr<Layer> rootLayer = nullptr;
+ RefPtr<ContainerLayer> parentContainerLayer = nullptr;
+ RefPtr<Layer> lastLayer = nullptr;
+ int layerNumber = 0;
+ for (size_t i = 0; i < strlen(aLayerTreeDescription); i++) {
+ if (aLayerTreeDescription[i] == '(') {
+ if (!lastLayer) {
+ printf(
+ "Syntax error, likely '(' character isn't preceded by a "
+ "container.\n");
+ MOZ_CRASH();
+ }
+ parentContainerLayer = lastLayer->AsContainerLayer();
+ if (!parentContainerLayer) {
+ printf("Layer before '(' must be a container.\n");
+ MOZ_CRASH();
+ }
+ } else if (aLayerTreeDescription[i] == ')') {
+ parentContainerLayer = parentContainerLayer->GetParent();
+ lastLayer = nullptr;
+ } else {
+ RefPtr<Layer> layer =
+ CreateLayer(aLayerTreeDescription[i], manager.get());
+ if (aVisibleRegions) {
+ layer->SetVisibleRegion(
+ LayerIntRegion::FromUnknownRegion(aVisibleRegions[layerNumber]));
+ layer->SetEventRegions(EventRegions(aVisibleRegions[layerNumber]));
+ }
+ if (aTransforms) {
+ layer->SetBaseTransform(aTransforms[layerNumber]);
+ }
+ aLayersOut.AppendElement(layer);
+ layerNumber++;
+ if (rootLayer && !parentContainerLayer) {
+ MOZ_CRASH();
+ }
+ if (!rootLayer) {
+ rootLayer = layer;
+ }
+ if (parentContainerLayer) {
+ parentContainerLayer->InsertAfter(layer,
+ parentContainerLayer->GetLastChild());
+ layer->SetParent(parentContainerLayer);
+ }
+ lastLayer = layer;
+ }
+ }
+ if (rootLayer) {
+ rootLayer->ComputeEffectiveTransforms(Matrix4x4());
+ manager->SetRoot(rootLayer);
+ if (rootLayer->AsHostLayer()) {
+ // Only perform this for LayerManagerComposite
+ CompositorBridgeParent::SetShadowProperties(rootLayer);
+ }
+ }
+ return rootLayer.forget();
+}
+
+TEST(Layers, LayerTree)
+{
+ const char* layerTreeSyntax = "c(c(tt))";
+ nsIntRegion layerVisibleRegion[] = {
+ nsIntRegion(IntRect(0, 0, 100, 100)),
+ nsIntRegion(IntRect(0, 0, 100, 100)),
+ nsIntRegion(IntRect(0, 0, 100, 100)),
+ nsIntRegion(IntRect(10, 10, 20, 20)),
+ };
+ Matrix4x4 transforms[] = {
+ Matrix4x4(),
+ Matrix4x4(),
+ Matrix4x4(),
+ Matrix4x4(),
+ };
+ nsTArray<RefPtr<Layer> > layers;
+
+ RefPtr<LayerManager> lm;
+ RefPtr<Layer> root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion,
+ transforms, lm, layers);
+
+ // B2G g++ doesn't like ASSERT_NE with nullptr directly. It thinks it's
+ // an int.
+ Layer* nullLayer = nullptr;
+ ASSERT_NE(nullLayer, layers[0]->AsContainerLayer());
+ ASSERT_NE(nullLayer, layers[1]->AsContainerLayer());
+ ASSERT_NE(nullLayer, layers[2]->AsPaintedLayer());
+ ASSERT_NE(nullLayer, layers[3]->AsPaintedLayer());
+}
+
+static void ValidateTreePointers(Layer* aLayer) {
+ if (aLayer->GetNextSibling()) {
+ ASSERT_EQ(aLayer, aLayer->GetNextSibling()->GetPrevSibling());
+ } else if (aLayer->GetParent()) {
+ ASSERT_EQ(aLayer, aLayer->GetParent()->GetLastChild());
+ }
+ if (aLayer->GetPrevSibling()) {
+ ASSERT_EQ(aLayer, aLayer->GetPrevSibling()->GetNextSibling());
+ } else if (aLayer->GetParent()) {
+ ASSERT_EQ(aLayer, aLayer->GetParent()->GetFirstChild());
+ }
+ if (aLayer->GetFirstChild()) {
+ ASSERT_EQ(aLayer, aLayer->GetFirstChild()->GetParent());
+ }
+ if (aLayer->GetLastChild()) {
+ ASSERT_EQ(aLayer, aLayer->GetLastChild()->GetParent());
+ }
+}
+
+static void ValidateTreePointers(nsTArray<RefPtr<Layer> >& aLayers) {
+ for (uint32_t i = 0; i < aLayers.Length(); i++) {
+ ValidateTreePointers(aLayers[i]);
+ }
+}
+
+TEST(Layers, RepositionChild)
+{
+ const char* layerTreeSyntax = "c(ttt)";
+
+ nsTArray<RefPtr<Layer> > layers;
+ RefPtr<LayerManager> lm;
+ RefPtr<Layer> root =
+ CreateLayerTree(layerTreeSyntax, nullptr, nullptr, lm, layers);
+ ContainerLayer* parent = root->AsContainerLayer();
+ ValidateTreePointers(layers);
+
+ // tree is currently like this (using indexes into layers):
+ // 0
+ // 1 2 3
+ ASSERT_EQ(layers[2], layers[1]->GetNextSibling());
+ ASSERT_EQ(layers[3], layers[2]->GetNextSibling());
+ ASSERT_EQ(nullptr, layers[3]->GetNextSibling());
+
+ parent->RepositionChild(layers[1], layers[3]);
+ ValidateTreePointers(layers);
+
+ // now the tree is like this:
+ // 0
+ // 2 3 1
+ ASSERT_EQ(layers[3], layers[2]->GetNextSibling());
+ ASSERT_EQ(layers[1], layers[3]->GetNextSibling());
+ ASSERT_EQ(nullptr, layers[1]->GetNextSibling());
+
+ parent->RepositionChild(layers[3], layers[2]);
+ ValidateTreePointers(layers);
+
+ // no change
+ ASSERT_EQ(layers[3], layers[2]->GetNextSibling());
+ ASSERT_EQ(layers[1], layers[3]->GetNextSibling());
+ ASSERT_EQ(nullptr, layers[1]->GetNextSibling());
+
+ parent->RepositionChild(layers[3], layers[1]);
+ ValidateTreePointers(layers);
+
+ // 0
+ // 2 1 3
+ ASSERT_EQ(layers[1], layers[2]->GetNextSibling());
+ ASSERT_EQ(layers[3], layers[1]->GetNextSibling());
+ ASSERT_EQ(nullptr, layers[3]->GetNextSibling());
+
+ parent->RepositionChild(layers[3], nullptr);
+ ValidateTreePointers(layers);
+
+ // 0
+ // 3 2 1
+ ASSERT_EQ(layers[2], layers[3]->GetNextSibling());
+ ASSERT_EQ(layers[1], layers[2]->GetNextSibling());
+ ASSERT_EQ(nullptr, layers[1]->GetNextSibling());
+}
+
+class LayerMetricsWrapperTester : public ::testing::Test {
+ protected:
+ virtual void SetUp() {
+ // This ensures ScrollMetadata::sNullMetadata is initialized.
+ gfxPlatform::GetPlatform();
+ }
+};
+
+TEST_F(LayerMetricsWrapperTester, SimpleTree) {
+ nsTArray<RefPtr<Layer> > layers;
+ RefPtr<LayerManager> lm;
+ RefPtr<Layer> root =
+ CreateLayerTree("c(c(c(tt)c(t)))", nullptr, nullptr, lm, layers);
+ LayerMetricsWrapper wrapper(root);
+
+ ASSERT_EQ(root.get(), wrapper.GetLayer());
+ wrapper = wrapper.GetFirstChild();
+ ASSERT_EQ(layers[1].get(), wrapper.GetLayer());
+ ASSERT_FALSE(wrapper.GetNextSibling().IsValid());
+ wrapper = wrapper.GetFirstChild();
+ ASSERT_EQ(layers[2].get(), wrapper.GetLayer());
+ wrapper = wrapper.GetFirstChild();
+ ASSERT_EQ(layers[3].get(), wrapper.GetLayer());
+ ASSERT_FALSE(wrapper.GetFirstChild().IsValid());
+ wrapper = wrapper.GetNextSibling();
+ ASSERT_EQ(layers[4].get(), wrapper.GetLayer());
+ ASSERT_FALSE(wrapper.GetNextSibling().IsValid());
+ wrapper = wrapper.GetParent();
+ ASSERT_EQ(layers[2].get(), wrapper.GetLayer());
+ wrapper = wrapper.GetNextSibling();
+ ASSERT_EQ(layers[5].get(), wrapper.GetLayer());
+ ASSERT_FALSE(wrapper.GetNextSibling().IsValid());
+ wrapper = wrapper.GetLastChild();
+ ASSERT_EQ(layers[6].get(), wrapper.GetLayer());
+ wrapper = wrapper.GetParent();
+ ASSERT_EQ(layers[5].get(), wrapper.GetLayer());
+ LayerMetricsWrapper layer5 = wrapper;
+ wrapper = wrapper.GetPrevSibling();
+ ASSERT_EQ(layers[2].get(), wrapper.GetLayer());
+ wrapper = wrapper.GetParent();
+ ASSERT_EQ(layers[1].get(), wrapper.GetLayer());
+ ASSERT_TRUE(layer5 == wrapper.GetLastChild());
+ LayerMetricsWrapper rootWrapper(root);
+ ASSERT_TRUE(rootWrapper == wrapper.GetParent());
+}
+
+static ScrollMetadata MakeMetadata(ScrollableLayerGuid::ViewID aId) {
+ ScrollMetadata metadata;
+ metadata.GetMetrics().SetScrollId(aId);
+ return metadata;
+}
+
+TEST_F(LayerMetricsWrapperTester, MultiFramemetricsTree) {
+ nsTArray<RefPtr<Layer> > layers;
+ RefPtr<LayerManager> lm;
+ RefPtr<Layer> root =
+ CreateLayerTree("c(c(c(tt)c(t)))", nullptr, nullptr, lm, layers);
+
+ nsTArray<ScrollMetadata> metadata;
+ metadata.InsertElementAt(0,
+ MakeMetadata(ScrollableLayerGuid::START_SCROLL_ID +
+ 0)); // topmost of root layer
+ metadata.InsertElementAt(0,
+ MakeMetadata(ScrollableLayerGuid::NULL_SCROLL_ID));
+ metadata.InsertElementAt(
+ 0, MakeMetadata(ScrollableLayerGuid::START_SCROLL_ID + 1));
+ metadata.InsertElementAt(
+ 0, MakeMetadata(ScrollableLayerGuid::START_SCROLL_ID + 2));
+ metadata.InsertElementAt(0,
+ MakeMetadata(ScrollableLayerGuid::NULL_SCROLL_ID));
+ metadata.InsertElementAt(
+ 0, MakeMetadata(
+ ScrollableLayerGuid::NULL_SCROLL_ID)); // bottom of root layer
+ root->SetScrollMetadata(metadata);
+
+ metadata.Clear();
+ metadata.InsertElementAt(
+ 0, MakeMetadata(ScrollableLayerGuid::START_SCROLL_ID + 3));
+ layers[1]->SetScrollMetadata(metadata);
+
+ metadata.Clear();
+ metadata.InsertElementAt(0,
+ MakeMetadata(ScrollableLayerGuid::NULL_SCROLL_ID));
+ metadata.InsertElementAt(
+ 0, MakeMetadata(ScrollableLayerGuid::START_SCROLL_ID + 4));
+ layers[2]->SetScrollMetadata(metadata);
+
+ metadata.Clear();
+ metadata.InsertElementAt(
+ 0, MakeMetadata(ScrollableLayerGuid::START_SCROLL_ID + 5));
+ layers[4]->SetScrollMetadata(metadata);
+
+ metadata.Clear();
+ metadata.InsertElementAt(
+ 0, MakeMetadata(ScrollableLayerGuid::START_SCROLL_ID + 6));
+ layers[5]->SetScrollMetadata(metadata);
+
+ LayerMetricsWrapper wrapper(root, LayerMetricsWrapper::StartAt::TOP);
+ nsTArray<Layer*> expectedLayers;
+ expectedLayers.AppendElement(layers[0].get());
+ expectedLayers.AppendElement(layers[0].get());
+ expectedLayers.AppendElement(layers[0].get());
+ expectedLayers.AppendElement(layers[0].get());
+ expectedLayers.AppendElement(layers[0].get());
+ expectedLayers.AppendElement(layers[0].get());
+ expectedLayers.AppendElement(layers[1].get());
+ expectedLayers.AppendElement(layers[2].get());
+ expectedLayers.AppendElement(layers[2].get());
+ expectedLayers.AppendElement(layers[3].get());
+ nsTArray<ScrollableLayerGuid::ViewID> expectedIds;
+ expectedIds.AppendElement(ScrollableLayerGuid::START_SCROLL_ID + 0);
+ expectedIds.AppendElement(ScrollableLayerGuid::NULL_SCROLL_ID);
+ expectedIds.AppendElement(ScrollableLayerGuid::START_SCROLL_ID + 1);
+ expectedIds.AppendElement(ScrollableLayerGuid::START_SCROLL_ID + 2);
+ expectedIds.AppendElement(ScrollableLayerGuid::NULL_SCROLL_ID);
+ expectedIds.AppendElement(ScrollableLayerGuid::NULL_SCROLL_ID);
+ expectedIds.AppendElement(ScrollableLayerGuid::START_SCROLL_ID + 3);
+ expectedIds.AppendElement(ScrollableLayerGuid::NULL_SCROLL_ID);
+ expectedIds.AppendElement(ScrollableLayerGuid::START_SCROLL_ID + 4);
+ expectedIds.AppendElement(ScrollableLayerGuid::NULL_SCROLL_ID);
+ for (int i = 0; i < 10; i++) {
+ ASSERT_EQ(expectedLayers[i], wrapper.GetLayer());
+ ASSERT_EQ(expectedIds[i], wrapper.Metrics().GetScrollId());
+ wrapper = wrapper.GetFirstChild();
+ }
+ ASSERT_FALSE(wrapper.IsValid());
+
+ wrapper = LayerMetricsWrapper(root, LayerMetricsWrapper::StartAt::BOTTOM);
+ for (int i = 5; i < 10; i++) {
+ ASSERT_EQ(expectedLayers[i], wrapper.GetLayer());
+ ASSERT_EQ(expectedIds[i], wrapper.Metrics().GetScrollId());
+ wrapper = wrapper.GetFirstChild();
+ }
+ ASSERT_FALSE(wrapper.IsValid());
+
+ wrapper =
+ LayerMetricsWrapper(layers[4], LayerMetricsWrapper::StartAt::BOTTOM);
+ ASSERT_EQ(ScrollableLayerGuid::START_SCROLL_ID + 5,
+ wrapper.Metrics().GetScrollId());
+ wrapper = wrapper.GetParent();
+ ASSERT_EQ(ScrollableLayerGuid::START_SCROLL_ID + 4,
+ wrapper.Metrics().GetScrollId());
+ ASSERT_EQ(layers[2].get(), wrapper.GetLayer());
+ ASSERT_FALSE(wrapper.GetNextSibling().IsValid());
+ wrapper = wrapper.GetParent();
+ ASSERT_EQ(ScrollableLayerGuid::NULL_SCROLL_ID,
+ wrapper.Metrics().GetScrollId());
+ ASSERT_EQ(layers[2].get(), wrapper.GetLayer());
+ wrapper = wrapper.GetNextSibling();
+ ASSERT_EQ(ScrollableLayerGuid::START_SCROLL_ID + 6,
+ wrapper.Metrics().GetScrollId());
+ ASSERT_EQ(layers[5].get(), wrapper.GetLayer());
+}
diff --git a/gfx/tests/gtest/TestLayers.h b/gfx/tests/gtest/TestLayers.h
new file mode 100644
index 0000000000..9812a39ff5
--- /dev/null
+++ b/gfx/tests/gtest/TestLayers.h
@@ -0,0 +1,48 @@
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+#ifndef GFX_TEST_LAYERS_H
+#define GFX_TEST_LAYERS_H
+
+#include "Layers.h"
+#include "nsTArray.h"
+#include "mozilla/layers/ISurfaceAllocator.h"
+
+namespace mozilla {
+namespace layers {
+
+class TestSurfaceAllocator final : public ISurfaceAllocator {
+ public:
+ TestSurfaceAllocator() = default;
+ virtual ~TestSurfaceAllocator() = default;
+
+ bool IsSameProcess() const override { return true; }
+};
+
+} // namespace layers
+} // namespace mozilla
+
+/* Create layer tree from a simple layer tree description syntax.
+ * Each index is either the first letter of the layer type or
+ * a '(',')' to indicate the start/end of the child layers.
+ * The aim of this function is to remove hard to read
+ * layer tree creation code.
+ *
+ * Example "c(c(c(tt)t))" would yield:
+ * c
+ * |
+ * c
+ * / \
+ * c t
+ * / \
+ * t t
+ */
+already_AddRefed<mozilla::layers::Layer> CreateLayerTree(
+ const char* aLayerTreeDescription, nsIntRegion* aVisibleRegions,
+ const mozilla::gfx::Matrix4x4* aTransforms,
+ RefPtr<mozilla::layers::LayerManager>& aLayerManager,
+ nsTArray<RefPtr<mozilla::layers::Layer> >& aLayersOut);
+
+#endif
diff --git a/gfx/tests/gtest/TestMatrix.cpp b/gfx/tests/gtest/TestMatrix.cpp
new file mode 100644
index 0000000000..e55e8e793b
--- /dev/null
+++ b/gfx/tests/gtest/TestMatrix.cpp
@@ -0,0 +1,145 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gtest/gtest.h"
+#include "mozilla/gfx/Matrix.h"
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+
+TEST(Matrix, TransformAndClipRect)
+{
+ Rect c(100, 100, 100, 100);
+ Matrix4x4 m;
+ EXPECT_TRUE(m.TransformAndClipBounds(Rect(50, 50, 20, 20), c).IsEmpty());
+ EXPECT_TRUE(m.TransformAndClipBounds(Rect(250, 50, 20, 20), c).IsEmpty());
+ EXPECT_TRUE(m.TransformAndClipBounds(Rect(250, 250, 20, 20), c).IsEmpty());
+ EXPECT_TRUE(m.TransformAndClipBounds(Rect(50, 250, 20, 20), c).IsEmpty());
+
+ EXPECT_TRUE(m.TransformAndClipBounds(Rect(50, 50, 100, 20), c).IsEmpty());
+ EXPECT_TRUE(m.TransformAndClipBounds(Rect(150, 50, 100, 20), c).IsEmpty());
+ EXPECT_TRUE(m.TransformAndClipBounds(Rect(50, 250, 100, 20), c).IsEmpty());
+ EXPECT_TRUE(m.TransformAndClipBounds(Rect(150, 250, 100, 20), c).IsEmpty());
+
+ EXPECT_TRUE(m.TransformAndClipBounds(Rect(50, 50, 20, 100), c).IsEmpty());
+ EXPECT_TRUE(m.TransformAndClipBounds(Rect(50, 150, 20, 100), c).IsEmpty());
+ EXPECT_TRUE(m.TransformAndClipBounds(Rect(250, 50, 20, 100), c).IsEmpty());
+ EXPECT_TRUE(m.TransformAndClipBounds(Rect(250, 150, 20, 100), c).IsEmpty());
+
+ EXPECT_TRUE(m.TransformAndClipBounds(Rect(50, 50, 100, 100), c)
+ .IsEqualInterior(Rect(100, 100, 50, 50)));
+ EXPECT_TRUE(m.TransformAndClipBounds(Rect(150, 50, 100, 100), c)
+ .IsEqualInterior(Rect(150, 100, 50, 50)));
+ EXPECT_TRUE(m.TransformAndClipBounds(Rect(150, 150, 100, 100), c)
+ .IsEqualInterior(Rect(150, 150, 50, 50)));
+ EXPECT_TRUE(m.TransformAndClipBounds(Rect(50, 150, 100, 100), c)
+ .IsEqualInterior(Rect(100, 150, 50, 50)));
+
+ EXPECT_TRUE(m.TransformAndClipBounds(Rect(110, 110, 80, 80), c)
+ .IsEqualInterior(Rect(110, 110, 80, 80)));
+
+ EXPECT_TRUE(m.TransformAndClipBounds(Rect(50, 50, 200, 200), c)
+ .IsEqualInterior(Rect(100, 100, 100, 100)));
+
+ EXPECT_TRUE(m.TransformAndClipBounds(Rect(50, 50, 200, 100), c)
+ .IsEqualInterior(Rect(100, 100, 100, 50)));
+ EXPECT_TRUE(m.TransformAndClipBounds(Rect(50, 150, 200, 100), c)
+ .IsEqualInterior(Rect(100, 150, 100, 50)));
+ EXPECT_TRUE(m.TransformAndClipBounds(Rect(50, 50, 100, 200), c)
+ .IsEqualInterior(Rect(100, 100, 50, 100)));
+ EXPECT_TRUE(m.TransformAndClipBounds(Rect(150, 50, 100, 200), c)
+ .IsEqualInterior(Rect(150, 100, 50, 100)));
+
+ Matrix4x4 m2 = Matrix4x4::From2D(Matrix(22.68, 0, 0, 12, 16, 164));
+ EXPECT_TRUE(
+ m2.TransformAndClipBounds(Rect(0, 0, 100, 100), Rect(1024, 1024, 0, 0))
+ .IsEmpty());
+
+ // Empty rectangles should still have meaningful corners.
+ EXPECT_TRUE(m.TransformAndClipBounds(Rect(150, 50, 0, 200), c)
+ .IsEqualEdges(Rect(150, 100, 0, 100)));
+ EXPECT_TRUE(m.TransformAndClipBounds(Rect(150, 150, 0, 0), c)
+ .IsEqualEdges(Rect(150, 150, 0, 0)));
+ EXPECT_TRUE(m.TransformAndClipBounds(Rect(150, 100, 300, 0), c)
+ .IsEqualEdges(Rect(150, 100, 50, 0)));
+}
+
+TEST(Matrix, RotateTransform)
+{
+ gfx::Matrix4x4 transformMatrix;
+ gfx::Point3D trans, scale;
+ gfx::Quaternion orient;
+
+ auto floor = [&](float aValue, int aDecimal) {
+ const int digit = pow(10, aDecimal);
+ const float result = (int)(aValue * digit);
+ return result / digit;
+ };
+
+ // Test rotate 45 degree on x-axis.
+ gfx::Quaternion expectedOrient(0.382f, 0.0f, 0.0f, 0.923f);
+ transformMatrix.RotateX(0.785f);
+ // the orient would be (x:0.3825, y:0, z:0, w: 0.9239)
+ transformMatrix.Decompose(trans, orient, scale);
+ EXPECT_EQ(floor(orient.x, 3), expectedOrient.x);
+ EXPECT_EQ(floor(orient.y, 3), expectedOrient.y);
+ EXPECT_EQ(floor(orient.z, 3), expectedOrient.z);
+ EXPECT_EQ(floor(orient.w, 3), expectedOrient.w);
+
+ // Test set rotate matrix from a quaternion and
+ // compare it with the result from decompose.
+ transformMatrix = gfx::Matrix4x4();
+ transformMatrix.SetRotationFromQuaternion(orient);
+ transformMatrix.Decompose(trans, orient, scale);
+ EXPECT_EQ(floor(orient.x, 3), expectedOrient.x);
+ EXPECT_EQ(floor(orient.y, 3), expectedOrient.y);
+ EXPECT_EQ(floor(orient.z, 3), expectedOrient.z);
+ EXPECT_EQ(floor(orient.w, 3), expectedOrient.w);
+
+ // Test rotate -45 degree on axis: (0.577f, 0.577f, 0.577f).
+ transformMatrix = gfx::Matrix4x4();
+ transformMatrix.SetRotateAxisAngle(0.577f, 0.577f, 0.577f, -0.785f);
+ // the orient would be (x:-0.2208, y:-0.2208, z:-0.2208, w: 0.9239)
+ transformMatrix.Decompose(trans, orient, scale);
+
+ expectedOrient.Set(-0.220f, -0.220f, -0.220f, 0.923f);
+ EXPECT_EQ(floor(orient.x, 3), expectedOrient.x);
+ EXPECT_EQ(floor(orient.y, 3), expectedOrient.y);
+ EXPECT_EQ(floor(orient.z, 3), expectedOrient.z);
+ EXPECT_EQ(floor(orient.w, 3), expectedOrient.w);
+
+ // Test set rotate matrix from a quaternion and
+ // compare it with the result from decompose.
+ transformMatrix = gfx::Matrix4x4();
+ transformMatrix.SetRotationFromQuaternion(orient);
+ transformMatrix.Decompose(trans, orient, scale);
+ EXPECT_EQ(floor(orient.x, 3), expectedOrient.x);
+ EXPECT_EQ(floor(orient.y, 3), expectedOrient.y);
+ EXPECT_EQ(floor(orient.z, 3), expectedOrient.z);
+ EXPECT_EQ(floor(orient.w, 3), expectedOrient.w);
+}
+
+TEST(Matrix4x4Flagged, Mult)
+{
+ Matrix4x4Flagged simple =
+ Matrix4x4::Translation(Point(42, 42)) * Matrix4x4::Scaling(3, 3, 1);
+ // For the general matrix, put a value in every field to make sure
+ // nothing gets dropped.
+ Matrix4x4 general(2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2);
+
+ // Use Matrix4x4::operator*(Matrix4x4).
+ // For the purposes of this test, assume that's correct.
+ Matrix4x4Flagged realResult = Matrix4x4Flagged(simple.GetMatrix() * general);
+
+ // Check that Matrix4x4Flagged::operator*(Matrix4x4Flagged) produces the same
+ // result.
+ Matrix4x4Flagged flaggedResult = simple * Matrix4x4Flagged(general);
+ EXPECT_EQ(realResult, flaggedResult);
+
+ // Check that Matrix4x4Flagged::operator*(Matrix4x4) produces the same result.
+ Matrix4x4Flagged mixedResult = simple * general;
+ EXPECT_EQ(realResult, mixedResult);
+}
diff --git a/gfx/tests/gtest/TestMoz2D.cpp b/gfx/tests/gtest/TestMoz2D.cpp
new file mode 100644
index 0000000000..50854cdc98
--- /dev/null
+++ b/gfx/tests/gtest/TestMoz2D.cpp
@@ -0,0 +1,41 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gtest/gtest.h"
+#include "TestBase.h"
+#include "TestPoint.h"
+#include "TestScaling.h"
+#include "TestBugs.h"
+
+TEST(Moz2D, Bugs)
+{
+ TestBugs* test = new TestBugs();
+ int failures = 0;
+ test->RunTests(&failures);
+ delete test;
+
+ ASSERT_EQ(failures, 0);
+}
+
+TEST(Moz2D, Point)
+{
+ TestBase* test = new TestPoint();
+ int failures = 0;
+ test->RunTests(&failures);
+ delete test;
+
+ ASSERT_EQ(failures, 0);
+}
+
+TEST(Moz2D, Scaling)
+{
+ TestBase* test = new TestScaling();
+ int failures = 0;
+ test->RunTests(&failures);
+ delete test;
+
+ ASSERT_EQ(failures, 0);
+}
diff --git a/gfx/tests/gtest/TestPolygon.cpp b/gfx/tests/gtest/TestPolygon.cpp
new file mode 100644
index 0000000000..168f7b46d9
--- /dev/null
+++ b/gfx/tests/gtest/TestPolygon.cpp
@@ -0,0 +1,104 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gtest/gtest.h"
+
+#include "PolygonTestUtils.h"
+
+#include "nsTArray.h"
+#include "Point.h"
+#include "Polygon.h"
+#include "Triangle.h"
+
+using namespace mozilla::gfx;
+typedef mozilla::gfx::Polygon MozPolygon;
+
+TEST(MozPolygon, TriangulateRectangle)
+{
+ const MozPolygon p{
+ Point4D(0.0f, 0.0f, 1.0f, 1.0f), Point4D(0.0f, 1.0f, 1.0f, 1.0f),
+ Point4D(1.0f, 1.0f, 1.0f, 1.0f), Point4D(1.0f, 0.0f, 1.0f, 1.0f)};
+
+ const nsTArray<Triangle> triangles = p.ToTriangles();
+ const nsTArray<Triangle> expected = {
+ Triangle(Point(0.0f, 0.0f), Point(0.0f, 1.0f), Point(1.0f, 1.0f)),
+ Triangle(Point(0.0f, 0.0f), Point(1.0f, 1.0f), Point(1.0f, 0.0f))};
+
+ AssertArrayEQ(triangles, expected);
+}
+
+TEST(MozPolygon, TriangulatePentagon)
+{
+ const MozPolygon p{
+ Point4D(0.0f, 0.0f, 1.0f, 1.0f), Point4D(0.0f, 1.0f, 1.0f, 1.0f),
+ Point4D(0.5f, 1.5f, 1.0f, 1.0f), Point4D(1.0f, 1.0f, 1.0f, 1.0f),
+ Point4D(1.0f, 0.0f, 1.0f, 1.0f)};
+
+ const nsTArray<Triangle> triangles = p.ToTriangles();
+ const nsTArray<Triangle> expected = {
+ Triangle(Point(0.0f, 0.0f), Point(0.0f, 1.0f), Point(0.5f, 1.5f)),
+ Triangle(Point(0.0f, 0.0f), Point(0.5f, 1.5f), Point(1.0f, 1.0f)),
+ Triangle(Point(0.0f, 0.0f), Point(1.0f, 1.0f), Point(1.0f, 0.0f))};
+
+ AssertArrayEQ(triangles, expected);
+}
+
+static void TestClipRect(const MozPolygon& aPolygon,
+ const MozPolygon& aExpected, const Rect& aRect) {
+ const MozPolygon res = aPolygon.ClipPolygon(MozPolygon::FromRect(aRect));
+ EXPECT_TRUE(res == aExpected);
+}
+
+TEST(MozPolygon, ClipRectangle)
+{
+ MozPolygon polygon{
+ Point4D(0.0f, 0.0f, 0.0f, 1.0f), Point4D(0.0f, 1.0f, 0.0f, 1.0f),
+ Point4D(1.0f, 1.0f, 0.0f, 1.0f), Point4D(1.0f, 0.0f, 0.0f, 1.0f)};
+ TestClipRect(polygon, polygon, Rect(0.0f, 0.0f, 1.0f, 1.0f));
+
+ MozPolygon expected = MozPolygon{
+ Point4D(0.0f, 0.0f, 0.0f, 1.0f), Point4D(0.0f, 0.8f, 0.0f, 1.0f),
+ Point4D(0.8f, 0.8f, 0.0f, 1.0f), Point4D(0.8f, 0.0f, 0.0f, 1.0f)};
+ TestClipRect(polygon, expected, Rect(0.0f, 0.0f, 0.8f, 0.8f));
+
+ expected = MozPolygon{
+ Point4D(0.2f, 0.2f, 0.0f, 1.0f), Point4D(0.2f, 1.0f, 0.0f, 1.0f),
+ Point4D(1.0f, 1.0f, 0.0f, 1.0f), Point4D(1.0f, 0.2f, 0.0f, 1.0f)};
+ TestClipRect(polygon, expected, Rect(0.2f, 0.2f, 0.8f, 0.8f));
+
+ expected = MozPolygon{
+ Point4D(0.2f, 0.2f, 0.0f, 1.0f), Point4D(0.2f, 0.8f, 0.0f, 1.0f),
+ Point4D(0.8f, 0.8f, 0.0f, 1.0f), Point4D(0.8f, 0.2f, 0.0f, 1.0f)};
+ TestClipRect(polygon, expected, Rect(0.2f, 0.2f, 0.6f, 0.6f));
+}
+
+TEST(MozPolygon, ClipTriangle)
+{
+ MozPolygon clipped, expected;
+ const MozPolygon polygon{Point4D(0.0f, 0.0f, 0.0f, 1.0f),
+ Point4D(0.0f, 1.0f, 0.0f, 1.0f),
+ Point4D(1.0f, 1.0f, 0.0f, 1.0f)};
+
+ expected = MozPolygon{Point4D(0.0f, 0.0f, 0.0f, 1.0f),
+ Point4D(0.0f, 1.0f, 0.0f, 1.0f),
+ Point4D(1.0f, 1.0f, 0.0f, 1.0f)};
+ TestClipRect(polygon, expected, Rect(0.0f, 0.0f, 1.0f, 1.0f));
+
+ expected = MozPolygon{Point4D(0.0f, 0.0f, 0.0f, 1.0f),
+ Point4D(0.0f, 0.8f, 0.0f, 1.0f),
+ Point4D(0.8f, 0.8f, 0.0f, 1.0f)};
+ TestClipRect(polygon, expected, Rect(0.0f, 0.0f, 0.8f, 0.8f));
+
+ expected = MozPolygon{Point4D(0.2f, 0.2f, 0.0f, 1.0f),
+ Point4D(0.2f, 1.0f, 0.0f, 1.0f),
+ Point4D(1.0f, 1.0f, 0.0f, 1.0f)};
+ TestClipRect(polygon, expected, Rect(0.2f, 0.2f, 0.8f, 0.8f));
+
+ expected = MozPolygon{Point4D(0.2f, 0.2f, 0.0f, 1.0f),
+ Point4D(0.2f, 0.8f, 0.0f, 1.0f),
+ Point4D(0.8f, 0.8f, 0.0f, 1.0f)};
+ TestClipRect(polygon, expected, Rect(0.2f, 0.2f, 0.6f, 0.6f));
+}
diff --git a/gfx/tests/gtest/TestQcms.cpp b/gfx/tests/gtest/TestQcms.cpp
new file mode 100644
index 0000000000..54bb349ea8
--- /dev/null
+++ b/gfx/tests/gtest/TestQcms.cpp
@@ -0,0 +1,506 @@
+/* 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 "gtest/MozGTestBench.h"
+#include "gmock/gmock.h"
+
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/SSE.h"
+#include "mozilla/arm.h"
+#include "qcms.h"
+#include "qcmsint.h"
+
+#include <cmath>
+
+/* SSEv1 is only included in non-Windows or non-x86-64-bit builds. */
+#if defined(MOZILLA_MAY_SUPPORT_SSE) && \
+ (!(defined(_MSC_VER) && defined(_M_AMD64)))
+# define QCMS_MAY_SUPPORT_SSE
+#endif
+
+using namespace mozilla;
+
+static bool CmpRgbChannel(const uint8_t* aRef, const uint8_t* aTest,
+ size_t aIndex) {
+ return std::abs(static_cast<int8_t>(aRef[aIndex] - aTest[aIndex])) <= 1;
+}
+
+template <bool kSwapRB, bool kHasAlpha>
+static bool CmpRgbBufferImpl(const uint8_t* aRefBuffer,
+ const uint8_t* aTestBuffer, size_t aPixels) {
+ const size_t pixelSize = kHasAlpha ? 4 : 3;
+ if (memcmp(aRefBuffer, aTestBuffer, aPixels * pixelSize) == 0) {
+ return true;
+ }
+
+ const size_t kRIndex = kSwapRB ? 2 : 0;
+ const size_t kGIndex = 1;
+ const size_t kBIndex = kSwapRB ? 0 : 2;
+ const size_t kAIndex = 3;
+
+ size_t remaining = aPixels;
+ const uint8_t* ref = aRefBuffer;
+ const uint8_t* test = aTestBuffer;
+ while (remaining > 0) {
+ if (!CmpRgbChannel(ref, test, kRIndex) ||
+ !CmpRgbChannel(ref, test, kGIndex) ||
+ !CmpRgbChannel(ref, test, kBIndex) ||
+ (kHasAlpha && ref[kAIndex] != test[kAIndex])) {
+ EXPECT_EQ(test[kRIndex], ref[kRIndex]);
+ EXPECT_EQ(test[kGIndex], ref[kGIndex]);
+ EXPECT_EQ(test[kBIndex], ref[kBIndex]);
+ if (kHasAlpha) {
+ EXPECT_EQ(test[kAIndex], ref[kAIndex]);
+ }
+ return false;
+ }
+
+ --remaining;
+ ref += pixelSize;
+ test += pixelSize;
+ }
+
+ return true;
+}
+
+template <bool kSwapRB, bool kHasAlpha>
+static size_t GetRgbInputBufferImpl(UniquePtr<uint8_t[]>& aOutBuffer) {
+ const uint8_t colorSamples[] = {0, 5, 16, 43, 101, 127, 182, 255};
+ const size_t colorSampleMax = sizeof(colorSamples) / sizeof(uint8_t);
+ const size_t pixelSize = kHasAlpha ? 4 : 3;
+ const size_t pixelCount = colorSampleMax * colorSampleMax * 256 * 3;
+
+ aOutBuffer = MakeUnique<uint8_t[]>(pixelCount * pixelSize);
+ if (!aOutBuffer) {
+ return 0;
+ }
+
+ const size_t kRIndex = kSwapRB ? 2 : 0;
+ const size_t kGIndex = 1;
+ const size_t kBIndex = kSwapRB ? 0 : 2;
+ const size_t kAIndex = 3;
+
+ // Sample every red pixel value with a subset of green and blue.
+ uint8_t* color = aOutBuffer.get();
+ for (uint16_t r = 0; r < 256; ++r) {
+ for (uint8_t g : colorSamples) {
+ for (uint8_t b : colorSamples) {
+ color[kRIndex] = r;
+ color[kGIndex] = g;
+ color[kBIndex] = b;
+ if (kHasAlpha) {
+ color[kAIndex] = 0x80;
+ }
+ color += pixelSize;
+ }
+ }
+ }
+
+ // Sample every green pixel value with a subset of red and blue.
+ for (uint8_t r : colorSamples) {
+ for (uint16_t g = 0; g < 256; ++g) {
+ for (uint8_t b : colorSamples) {
+ color[kRIndex] = r;
+ color[kGIndex] = g;
+ color[kBIndex] = b;
+ if (kHasAlpha) {
+ color[kAIndex] = 0x80;
+ }
+ color += pixelSize;
+ }
+ }
+ }
+
+ // Sample every blue pixel value with a subset of red and green.
+ for (uint8_t r : colorSamples) {
+ for (uint8_t g : colorSamples) {
+ for (uint16_t b = 0; b < 256; ++b) {
+ color[kRIndex] = r;
+ color[kGIndex] = g;
+ color[kBIndex] = b;
+ if (kHasAlpha) {
+ color[kAIndex] = 0x80;
+ }
+ color += pixelSize;
+ }
+ }
+ }
+
+ return pixelCount;
+}
+
+static size_t GetRgbInputBuffer(UniquePtr<uint8_t[]>& aOutBuffer) {
+ return GetRgbInputBufferImpl<false, false>(aOutBuffer);
+}
+
+static size_t GetRgbaInputBuffer(UniquePtr<uint8_t[]>& aOutBuffer) {
+ return GetRgbInputBufferImpl<false, true>(aOutBuffer);
+}
+
+static size_t GetBgraInputBuffer(UniquePtr<uint8_t[]>& aOutBuffer) {
+ return GetRgbInputBufferImpl<true, true>(aOutBuffer);
+}
+
+static bool CmpRgbBuffer(const uint8_t* aRefBuffer, const uint8_t* aTestBuffer,
+ size_t aPixels) {
+ return CmpRgbBufferImpl<false, false>(aRefBuffer, aTestBuffer, aPixels);
+}
+
+static bool CmpRgbaBuffer(const uint8_t* aRefBuffer, const uint8_t* aTestBuffer,
+ size_t aPixels) {
+ return CmpRgbBufferImpl<false, true>(aRefBuffer, aTestBuffer, aPixels);
+}
+
+static bool CmpBgraBuffer(const uint8_t* aRefBuffer, const uint8_t* aTestBuffer,
+ size_t aPixels) {
+ return CmpRgbBufferImpl<true, true>(aRefBuffer, aTestBuffer, aPixels);
+}
+
+static void ClearRgbBuffer(uint8_t* aBuffer, size_t aPixels) {
+ if (aBuffer) {
+ memset(aBuffer, 0, aPixels * 3);
+ }
+}
+
+static void ClearRgbaBuffer(uint8_t* aBuffer, size_t aPixels) {
+ if (aBuffer) {
+ memset(aBuffer, 0, aPixels * 4);
+ }
+}
+
+static UniquePtr<uint8_t[]> GetRgbOutputBuffer(size_t aPixels) {
+ UniquePtr<uint8_t[]> buffer = MakeUnique<uint8_t[]>(aPixels * 3);
+ ClearRgbBuffer(buffer.get(), aPixels);
+ return buffer;
+}
+
+static UniquePtr<uint8_t[]> GetRgbaOutputBuffer(size_t aPixels) {
+ UniquePtr<uint8_t[]> buffer = MakeUnique<uint8_t[]>(aPixels * 4);
+ ClearRgbaBuffer(buffer.get(), aPixels);
+ return buffer;
+}
+
+class GfxQcms_ProfilePairBase : public ::testing::Test {
+ protected:
+ GfxQcms_ProfilePairBase()
+ : mInProfile(nullptr),
+ mOutProfile(nullptr),
+ mTransform(nullptr),
+ mPixels(0),
+ mStorageType(QCMS_DATA_RGB_8),
+ mPrecache(false) {}
+
+ void TransformPrecache();
+ void TransformPrecachePlatformExt();
+
+ void SetUp() override {
+ // XXX: This means that we can't have qcms v2 unit test
+ // without changing the qcms API.
+ qcms_enable_iccv4();
+ }
+
+ void TearDown() override {
+ if (mInProfile) {
+ qcms_profile_release(mInProfile);
+ }
+ if (mOutProfile) {
+ qcms_profile_release(mOutProfile);
+ }
+ if (mTransform) {
+ qcms_transform_release(mTransform);
+ }
+ }
+
+ bool SetTransform(qcms_transform* aTransform) {
+ if (mTransform) {
+ qcms_transform_release(mTransform);
+ }
+ mTransform = aTransform;
+ return !!mTransform;
+ }
+
+ bool SetTransform(qcms_data_type aType) {
+ return SetTransform(qcms_transform_create(mInProfile, aType, mOutProfile,
+ aType, QCMS_INTENT_DEFAULT));
+ }
+
+ bool SetBuffers(qcms_data_type aType) {
+ switch (aType) {
+ case QCMS_DATA_RGB_8:
+ mPixels = GetRgbInputBuffer(mInput);
+ mRef = GetRgbOutputBuffer(mPixels);
+ mOutput = GetRgbOutputBuffer(mPixels);
+ break;
+ case QCMS_DATA_RGBA_8:
+ mPixels = GetRgbaInputBuffer(mInput);
+ mRef = GetRgbaOutputBuffer(mPixels);
+ mOutput = GetRgbaOutputBuffer(mPixels);
+ break;
+ case QCMS_DATA_BGRA_8:
+ mPixels = GetBgraInputBuffer(mInput);
+ mRef = GetRgbaOutputBuffer(mPixels);
+ mOutput = GetRgbaOutputBuffer(mPixels);
+ break;
+ default:
+ MOZ_ASSERT_UNREACHABLE("Unknown type!");
+ break;
+ }
+
+ mStorageType = aType;
+ return mInput && mOutput && mRef && mPixels > 0u;
+ }
+
+ void ClearOutputBuffer() {
+ switch (mStorageType) {
+ case QCMS_DATA_RGB_8:
+ ClearRgbBuffer(mOutput.get(), mPixels);
+ break;
+ case QCMS_DATA_RGBA_8:
+ case QCMS_DATA_BGRA_8:
+ ClearRgbaBuffer(mOutput.get(), mPixels);
+ break;
+ default:
+ MOZ_ASSERT_UNREACHABLE("Unknown type!");
+ break;
+ }
+ }
+
+ void ProduceRef(transform_fn_t aFn) {
+ aFn(mTransform, mInput.get(), mRef.get(), mPixels);
+ }
+
+ void CopyInputToRef() {
+ size_t pixelSize = 0;
+ switch (mStorageType) {
+ case QCMS_DATA_RGB_8:
+ pixelSize = 3;
+ break;
+ case QCMS_DATA_RGBA_8:
+ case QCMS_DATA_BGRA_8:
+ pixelSize = 4;
+ break;
+ default:
+ MOZ_ASSERT_UNREACHABLE("Unknown type!");
+ break;
+ }
+
+ memcpy(mRef.get(), mInput.get(), mPixels * pixelSize);
+ }
+
+ void ProduceOutput(transform_fn_t aFn) {
+ ClearOutputBuffer();
+ aFn(mTransform, mInput.get(), mOutput.get(), mPixels);
+ }
+
+ bool VerifyOutput(const UniquePtr<uint8_t[]>& aBuf) {
+ switch (mStorageType) {
+ case QCMS_DATA_RGB_8:
+ return CmpRgbBuffer(aBuf.get(), mOutput.get(), mPixels);
+ case QCMS_DATA_RGBA_8:
+ return CmpRgbaBuffer(aBuf.get(), mOutput.get(), mPixels);
+ case QCMS_DATA_BGRA_8:
+ return CmpBgraBuffer(aBuf.get(), mOutput.get(), mPixels);
+ default:
+ MOZ_ASSERT_UNREACHABLE("Unknown type!");
+ break;
+ }
+
+ return false;
+ }
+
+ bool ProduceVerifyOutput(transform_fn_t aFn) {
+ ProduceOutput(aFn);
+ return VerifyOutput(mRef);
+ }
+
+ void PrecacheOutput() {
+ qcms_profile_precache_output_transform(mOutProfile);
+ mPrecache = true;
+ }
+
+ qcms_profile* mInProfile;
+ qcms_profile* mOutProfile;
+ qcms_transform* mTransform;
+
+ UniquePtr<uint8_t[]> mInput;
+ UniquePtr<uint8_t[]> mOutput;
+ UniquePtr<uint8_t[]> mRef;
+ size_t mPixels;
+ qcms_data_type mStorageType;
+ bool mPrecache;
+};
+
+void GfxQcms_ProfilePairBase::TransformPrecache() {
+ // Produce reference using interpolation and the lookup tables.
+ ASSERT_FALSE(mPrecache);
+ ASSERT_TRUE(SetBuffers(QCMS_DATA_RGB_8));
+ ASSERT_TRUE(SetTransform(QCMS_DATA_RGB_8));
+ ProduceRef(qcms_transform_data_rgb_out_lut);
+
+ // Produce output using lut and precaching.
+ PrecacheOutput();
+ ASSERT_TRUE(SetTransform(QCMS_DATA_RGB_8));
+ EXPECT_TRUE(ProduceVerifyOutput(qcms_transform_data_rgb_out_lut_precache));
+}
+
+void GfxQcms_ProfilePairBase::TransformPrecachePlatformExt() {
+ PrecacheOutput();
+
+ // Verify RGB transforms.
+ ASSERT_TRUE(SetBuffers(QCMS_DATA_RGB_8));
+ ASSERT_TRUE(SetTransform(QCMS_DATA_RGB_8));
+ ProduceRef(qcms_transform_data_rgb_out_lut_precache);
+#ifdef MOZILLA_MAY_SUPPORT_SSE2
+ if (mozilla::supports_sse2()) {
+ EXPECT_TRUE(ProduceVerifyOutput(qcms_transform_data_rgb_out_lut_sse2));
+ }
+#endif
+#ifdef MOZILLA_MAY_SUPPORT_AVX
+ if (mozilla::supports_avx()) {
+ EXPECT_TRUE(ProduceVerifyOutput(qcms_transform_data_rgb_out_lut_avx));
+ }
+#endif
+#ifdef MOZILLA_MAY_SUPPORT_NEON
+ if (mozilla::supports_neon()) {
+ EXPECT_TRUE(ProduceVerifyOutput(qcms_transform_data_rgb_out_lut_neon));
+ }
+#endif
+
+ // Verify RGBA transforms.
+ ASSERT_TRUE(SetBuffers(QCMS_DATA_RGBA_8));
+ ASSERT_TRUE(SetTransform(QCMS_DATA_RGBA_8));
+ ProduceRef(qcms_transform_data_rgba_out_lut_precache);
+#ifdef MOZILLA_MAY_SUPPORT_SSE2
+ if (mozilla::supports_sse2()) {
+ EXPECT_TRUE(ProduceVerifyOutput(qcms_transform_data_rgba_out_lut_sse2));
+ }
+#endif
+#ifdef MOZILLA_MAY_SUPPORT_AVX
+ if (mozilla::supports_avx()) {
+ EXPECT_TRUE(ProduceVerifyOutput(qcms_transform_data_rgba_out_lut_avx));
+ }
+#endif
+#ifdef MOZILLA_MAY_SUPPORT_NEON
+ if (mozilla::supports_neon()) {
+ EXPECT_TRUE(ProduceVerifyOutput(qcms_transform_data_rgba_out_lut_neon));
+ }
+#endif
+
+ // Verify BGRA transforms.
+ ASSERT_TRUE(SetBuffers(QCMS_DATA_BGRA_8));
+ ASSERT_TRUE(SetTransform(QCMS_DATA_BGRA_8));
+ ProduceRef(qcms_transform_data_bgra_out_lut_precache);
+#ifdef MOZILLA_MAY_SUPPORT_SSE2
+ if (mozilla::supports_sse2()) {
+ EXPECT_TRUE(ProduceVerifyOutput(qcms_transform_data_bgra_out_lut_sse2));
+ }
+#endif
+#ifdef MOZILLA_MAY_SUPPORT_AVX
+ if (mozilla::supports_avx()) {
+ EXPECT_TRUE(ProduceVerifyOutput(qcms_transform_data_bgra_out_lut_avx));
+ }
+#endif
+#ifdef MOZILLA_MAY_SUPPORT_NEON
+ if (mozilla::supports_neon()) {
+ EXPECT_TRUE(ProduceVerifyOutput(qcms_transform_data_bgra_out_lut_neon));
+ }
+#endif
+}
+
+class GfxQcms_sRGB_To_sRGB : public GfxQcms_ProfilePairBase {
+ protected:
+ void SetUp() override {
+ GfxQcms_ProfilePairBase::SetUp();
+ mInProfile = qcms_profile_sRGB();
+ mOutProfile = qcms_profile_sRGB();
+ }
+};
+
+class GfxQcms_sRGB_To_SamsungSyncmaster : public GfxQcms_ProfilePairBase {
+ protected:
+ void SetUp() override {
+ GfxQcms_ProfilePairBase::SetUp();
+ mInProfile = qcms_profile_sRGB();
+ mOutProfile = qcms_profile_from_path("lcms_samsung_syncmaster.icc");
+ }
+};
+
+class GfxQcms_sRGB_To_ThinkpadW540 : public GfxQcms_ProfilePairBase {
+ protected:
+ void SetUp() override {
+ GfxQcms_ProfilePairBase::SetUp();
+ mInProfile = qcms_profile_sRGB();
+ mOutProfile = qcms_profile_from_path("lcms_thinkpad_w540.icc");
+ }
+};
+
+#define TEST_QCMS_PROFILE_F(test_fixture) \
+ TEST_F(test_fixture, TransformPrecachePlatformExt) { \
+ GfxQcms_ProfilePairBase::TransformPrecachePlatformExt(); \
+ }
+
+TEST_F(GfxQcms_sRGB_To_sRGB, TransformPrecache) {
+ // TODO(aosmond): This doesn't pass for the non-identity transform. Should
+ // they produce the same results?
+ GfxQcms_ProfilePairBase::TransformPrecache();
+}
+
+TEST_QCMS_PROFILE_F(GfxQcms_sRGB_To_sRGB)
+
+TEST_F(GfxQcms_sRGB_To_sRGB, TransformIdentity) {
+ PrecacheOutput();
+ SetBuffers(QCMS_DATA_RGB_8);
+ SetTransform(QCMS_DATA_RGB_8);
+ qcms_transform_data(mTransform, mInput.get(), mOutput.get(), mPixels);
+ EXPECT_TRUE(VerifyOutput(mInput));
+}
+
+TEST_QCMS_PROFILE_F(GfxQcms_sRGB_To_SamsungSyncmaster)
+TEST_QCMS_PROFILE_F(GfxQcms_sRGB_To_ThinkpadW540)
+
+class GfxQcmsPerf_Base : public GfxQcms_sRGB_To_ThinkpadW540 {
+ protected:
+ explicit GfxQcmsPerf_Base(qcms_data_type aType) { mStorageType = aType; }
+
+ void TransformPerf() { ProduceRef(qcms_transform_data_rgb_out_lut_precache); }
+
+ void TransformPlatformPerf() {
+ qcms_transform_data(mTransform, mInput.get(), mRef.get(), mPixels);
+ }
+
+ void SetUp() override {
+ GfxQcms_sRGB_To_ThinkpadW540::SetUp();
+ PrecacheOutput();
+ SetBuffers(mStorageType);
+ SetTransform(mStorageType);
+ }
+};
+
+class GfxQcmsPerf_Rgb : public GfxQcmsPerf_Base {
+ protected:
+ GfxQcmsPerf_Rgb() : GfxQcmsPerf_Base(QCMS_DATA_RGB_8) {}
+};
+
+class GfxQcmsPerf_Rgba : public GfxQcmsPerf_Base {
+ protected:
+ GfxQcmsPerf_Rgba() : GfxQcmsPerf_Base(QCMS_DATA_RGBA_8) {}
+};
+
+class GfxQcmsPerf_Bgra : public GfxQcmsPerf_Base {
+ protected:
+ GfxQcmsPerf_Bgra() : GfxQcmsPerf_Base(QCMS_DATA_BGRA_8) {}
+};
+
+MOZ_GTEST_BENCH_F(GfxQcmsPerf_Rgb, TransformC, [this] { TransformPerf(); });
+MOZ_GTEST_BENCH_F(GfxQcmsPerf_Rgb, TransformPlatform,
+ [this] { TransformPlatformPerf(); });
+MOZ_GTEST_BENCH_F(GfxQcmsPerf_Rgba, TransformC, [this] { TransformPerf(); });
+MOZ_GTEST_BENCH_F(GfxQcmsPerf_Rgba, TransformPlatform,
+ [this] { TransformPlatformPerf(); });
+MOZ_GTEST_BENCH_F(GfxQcmsPerf_Bgra, TransformC, [this] { TransformPerf(); });
+MOZ_GTEST_BENCH_F(GfxQcmsPerf_Bgra, TransformPlatform,
+ [this] { TransformPlatformPerf(); });
diff --git a/gfx/tests/gtest/TestRect.cpp b/gfx/tests/gtest/TestRect.cpp
new file mode 100644
index 0000000000..6c3d102884
--- /dev/null
+++ b/gfx/tests/gtest/TestRect.cpp
@@ -0,0 +1,645 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#include <limits>
+
+#include "gtest/gtest.h"
+
+#include "gfxTypes.h"
+#include "nsRect.h"
+#include "gfxRect.h"
+#include "mozilla/gfx/Rect.h"
+#include "mozilla/gfx/RectAbsolute.h"
+#include "mozilla/WritingModes.h"
+#ifdef XP_WIN
+# include <windows.h>
+#endif
+
+using mozilla::gfx::IntRect;
+using mozilla::gfx::IntRectAbsolute;
+
+template <class RectType>
+static bool TestConstructors() {
+ // Create a rectangle
+ RectType rect1(10, 20, 30, 40);
+
+ // Make sure the rectangle was properly initialized
+ EXPECT_TRUE(rect1.IsEqualRect(10, 20, 30, 40) && rect1.IsEqualXY(10, 20) &&
+ rect1.IsEqualSize(30, 40))
+ << "[1] Make sure the rectangle was properly initialized with "
+ "constructor";
+
+ // Create a second rect using the copy constructor
+ RectType rect2(rect1);
+
+ // Make sure the rectangle was properly initialized
+ EXPECT_TRUE(rect2.IsEqualEdges(rect1) &&
+ rect2.IsEqualXY(rect1.X(), rect1.Y()) &&
+ rect2.IsEqualSize(rect1.Width(), rect1.Height()))
+ << "[2] Make sure the rectangle was properly initialized with copy "
+ "constructor";
+
+ EXPECT_TRUE(!rect1.IsEmpty() && !rect1.IsZeroArea() && rect1.IsFinite() &&
+ !rect2.IsEmpty() && !rect2.IsZeroArea() && rect2.IsFinite())
+ << "[3] These rectangles are not empty and are finite";
+
+ rect1.SetRect(1, 2, 30, 40);
+ EXPECT_TRUE(rect1.X() == 1 && rect1.Y() == 2 && rect1.Width() == 30 &&
+ rect1.Height() == 40 && rect1.XMost() == 31 &&
+ rect1.YMost() == 42);
+
+ rect1.SetRectX(11, 50);
+ EXPECT_TRUE(rect1.X() == 11 && rect1.Y() == 2 && rect1.Width() == 50 &&
+ rect1.Height() == 40 && rect1.XMost() == 61 &&
+ rect1.YMost() == 42);
+
+ rect1.SetRectY(22, 60);
+ EXPECT_TRUE(rect1.X() == 11 && rect1.Y() == 22 && rect1.Width() == 50 &&
+ rect1.Height() == 60 && rect1.XMost() == 61 &&
+ rect1.YMost() == 82);
+
+ rect1.SetBox(1, 2, 31, 42);
+ EXPECT_TRUE(rect1.X() == 1 && rect1.Y() == 2 && rect1.Width() == 30 &&
+ rect1.Height() == 40 && rect1.XMost() == 31 &&
+ rect1.YMost() == 42);
+
+ rect1.SetBoxX(11, 61);
+ EXPECT_TRUE(rect1.X() == 11 && rect1.Y() == 2 && rect1.Width() == 50 &&
+ rect1.Height() == 40 && rect1.XMost() == 61 &&
+ rect1.YMost() == 42);
+
+ rect1.SetBoxY(22, 82);
+ EXPECT_TRUE(rect1.X() == 11 && rect1.Y() == 22 && rect1.Width() == 50 &&
+ rect1.Height() == 60 && rect1.XMost() == 61 &&
+ rect1.YMost() == 82);
+
+ rect1.SetRect(1, 2, 30, 40);
+ EXPECT_TRUE(rect1.X() == 1 && rect1.Y() == 2 && rect1.Width() == 30 &&
+ rect1.Height() == 40 && rect1.XMost() == 31 &&
+ rect1.YMost() == 42);
+
+ rect1.MoveByX(10);
+ EXPECT_TRUE(rect1.X() == 11 && rect1.Y() == 2 && rect1.Width() == 30 &&
+ rect1.Height() == 40 && rect1.XMost() == 41 &&
+ rect1.YMost() == 42);
+
+ rect1.MoveByY(20);
+ EXPECT_TRUE(rect1.X() == 11 && rect1.Y() == 22 && rect1.Width() == 30 &&
+ rect1.Height() == 40 && rect1.XMost() == 41 &&
+ rect1.YMost() == 62);
+
+ return true;
+}
+
+template <class RectType>
+static bool TestEqualityOperator() {
+ RectType rect1(10, 20, 30, 40);
+ RectType rect2(rect1);
+
+ // Test the equality operator
+ EXPECT_TRUE(rect1 == rect2) << "[1] Test the equality operator";
+
+ EXPECT_FALSE(!rect1.IsEqualInterior(rect2))
+ << "[2] Test the inequality operator";
+
+ // Make sure that two empty rects are equal
+ rect1.SetEmpty();
+ rect2.SetEmpty();
+ EXPECT_TRUE(rect1 == rect2) << "[3] Make sure that two empty rects are equal";
+
+ return true;
+}
+
+template <class RectType>
+static bool TestContainment() {
+ RectType rect1(10, 10, 50, 50);
+
+ // Test the point containment methods
+ //
+
+ // Basic test of a point in the middle of the rect
+ EXPECT_TRUE(rect1.Contains(rect1.Center()) &&
+ rect1.ContainsX(rect1.Center().x) &&
+ rect1.ContainsY(rect1.Center().y))
+ << "[1] Basic test of a point in the middle of the rect";
+
+ // Test against a point at the left/top edges
+ EXPECT_TRUE(rect1.Contains(rect1.X(), rect1.Y()) &&
+ rect1.ContainsX(rect1.X()) && rect1.ContainsY(rect1.Y()))
+ << "[2] Test against a point at the left/top edges";
+
+ // Test against a point at the right/bottom extents
+ EXPECT_FALSE(rect1.Contains(rect1.XMost(), rect1.YMost()) ||
+ rect1.ContainsX(rect1.XMost()) || rect1.ContainsY(rect1.YMost()))
+ << "[3] Test against a point at the right/bottom extents";
+
+ // Test the rect containment methods
+ //
+ RectType rect2(rect1);
+
+ // Test against a rect that's the same as rect1
+ EXPECT_FALSE(!rect1.Contains(rect2))
+ << "[4] Test against a rect that's the same as rect1";
+
+ // Test against a rect whose left edge (only) is outside of rect1
+ rect2.MoveByX(-1);
+ EXPECT_FALSE(rect1.Contains(rect2))
+ << "[5] Test against a rect whose left edge (only) is outside of rect1";
+ rect2.MoveByX(1);
+
+ // Test against a rect whose top edge (only) is outside of rect1
+ rect2.MoveByY(-1);
+ EXPECT_FALSE(rect1.Contains(rect2))
+ << "[6] Test against a rect whose top edge (only) is outside of rect1";
+ rect2.MoveByY(1);
+
+ // Test against a rect whose right edge (only) is outside of rect1
+ rect2.MoveByX(1);
+ EXPECT_FALSE(rect1.Contains(rect2))
+ << "[7] Test against a rect whose right edge (only) is outside of rect1";
+ rect2.MoveByX(-1);
+
+ // Test against a rect whose bottom edge (only) is outside of rect1
+ rect2.MoveByY(1);
+ EXPECT_FALSE(rect1.Contains(rect2))
+ << "[8] Test against a rect whose bottom edge (only) is outside of rect1";
+ rect2.MoveByY(-1);
+
+ return true;
+}
+
+// Test the method that returns a boolean result but doesn't return a
+// a rectangle
+template <class RectType>
+static bool TestIntersects() {
+ RectType rect1(10, 10, 50, 50);
+ RectType rect2(rect1);
+
+ // Test against a rect that's the same as rect1
+ EXPECT_FALSE(!rect1.Intersects(rect2))
+ << "[1] Test against a rect that's the same as rect1";
+
+ // Test against a rect that's enclosed by rect1
+ rect2.Inflate(-1, -1);
+ EXPECT_FALSE(!rect1.Contains(rect2) || !rect1.Intersects(rect2))
+ << "[2] Test against a rect that's enclosed by rect1";
+ rect2.Inflate(1, 1);
+
+ // Make sure inflate and deflate worked correctly
+ EXPECT_TRUE(rect1.IsEqualInterior(rect2))
+ << "[3] Make sure inflate and deflate worked correctly";
+
+ // Test against a rect that overlaps the left edge of rect1
+ rect2.MoveByX(-1);
+ EXPECT_FALSE(!rect1.Intersects(rect2))
+ << "[4] Test against a rect that overlaps the left edge of rect1";
+ rect2.MoveByX(1);
+
+ // Test against a rect that's outside of rect1 on the left
+ rect2.MoveByX(-rect2.Width());
+ EXPECT_FALSE(rect1.Intersects(rect2))
+ << "[5] Test against a rect that's outside of rect1 on the left";
+ rect2.MoveByX(rect2.Width());
+
+ // Test against a rect that overlaps the top edge of rect1
+ rect2.MoveByY(-1);
+ EXPECT_FALSE(!rect1.Intersects(rect2))
+ << "[6] Test against a rect that overlaps the top edge of rect1";
+ rect2.MoveByY(1);
+
+ // Test against a rect that's outside of rect1 on the top
+ rect2.MoveByY(-rect2.Height());
+ EXPECT_FALSE(rect1.Intersects(rect2))
+ << "[7] Test against a rect that's outside of rect1 on the top";
+ rect2.MoveByY(rect2.Height());
+
+ // Test against a rect that overlaps the right edge of rect1
+ rect2.MoveByX(1);
+ EXPECT_FALSE(!rect1.Intersects(rect2))
+ << "[8] Test against a rect that overlaps the right edge of rect1";
+ rect2.MoveByX(-1);
+
+ // Test against a rect that's outside of rect1 on the right
+ rect2.MoveByX(rect2.Width());
+ EXPECT_FALSE(rect1.Intersects(rect2))
+ << "[9] Test against a rect that's outside of rect1 on the right";
+ rect2.MoveByX(-rect2.Width());
+
+ // Test against a rect that overlaps the bottom edge of rect1
+ rect2.MoveByY(1);
+ EXPECT_FALSE(!rect1.Intersects(rect2))
+ << "[10] Test against a rect that overlaps the bottom edge of rect1";
+ rect2.MoveByY(-1);
+
+ // Test against a rect that's outside of rect1 on the bottom
+ rect2.MoveByY(rect2.Height());
+ EXPECT_FALSE(rect1.Intersects(rect2))
+ << "[11] Test against a rect that's outside of rect1 on the bottom";
+ rect2.MoveByY(-rect2.Height());
+
+ return true;
+}
+
+// Test the method that returns a boolean result and an intersection rect
+template <class RectType>
+static bool TestIntersection() {
+ RectType rect1(10, 10, 50, 50);
+ RectType rect2(rect1);
+ RectType dest;
+
+ // Test against a rect that's the same as rect1
+ EXPECT_FALSE(!dest.IntersectRect(rect1, rect2) ||
+ !(dest.IsEqualInterior(rect1)))
+ << "[1] Test against a rect that's the same as rect1";
+
+ // Test against a rect that's enclosed by rect1
+ rect2.Inflate(-1, -1);
+ EXPECT_FALSE(!dest.IntersectRect(rect1, rect2) ||
+ !(dest.IsEqualInterior(rect2)))
+ << "[2] Test against a rect that's enclosed by rect1";
+ rect2.Inflate(1, 1);
+
+ // Test against a rect that overlaps the left edge of rect1
+ rect2.MoveByX(-1);
+ EXPECT_FALSE(!dest.IntersectRect(rect1, rect2) ||
+ !(dest.IsEqualInterior(RectType(
+ rect1.X(), rect1.Y(), rect1.Width() - 1, rect1.Height()))))
+ << "[3] Test against a rect that overlaps the left edge of rect1";
+ rect2.MoveByX(1);
+
+ // Test against a rect that's outside of rect1 on the left
+ rect2.MoveByX(-rect2.Width());
+ EXPECT_FALSE(dest.IntersectRect(rect1, rect2))
+ << "[4] Test against a rect that's outside of rect1 on the left";
+ // Make sure an empty rect is returned
+ EXPECT_TRUE(dest.IsEmpty() && dest.IsZeroArea())
+ << "[4] Make sure an empty rect is returned";
+ EXPECT_TRUE(dest.IsFinite()) << "[4b] Should be finite";
+ rect2.MoveByX(rect2.Width());
+
+ // Test against a rect that overlaps the top edge of rect1
+ rect2.MoveByY(-1);
+ EXPECT_FALSE(!dest.IntersectRect(rect1, rect2) ||
+ !(dest.IsEqualInterior(RectType(
+ rect1.X(), rect1.Y(), rect1.Width(), rect1.Height() - 1))))
+ << "[5] Test against a rect that overlaps the top edge of rect1";
+ EXPECT_TRUE(dest.IsFinite()) << "[5b] Should be finite";
+ rect2.MoveByY(1);
+
+ // Test against a rect that's outside of rect1 on the top
+ rect2.MoveByY(-rect2.Height());
+ EXPECT_FALSE(dest.IntersectRect(rect1, rect2))
+ << "[6] Test against a rect that's outside of rect1 on the top";
+ // Make sure an empty rect is returned
+ EXPECT_TRUE(dest.IsEmpty() && dest.IsZeroArea())
+ << "[6] Make sure an empty rect is returned";
+ EXPECT_TRUE(dest.IsFinite()) << "[6b] Should be finite";
+ rect2.MoveByY(rect2.Height());
+
+ // Test against a rect that overlaps the right edge of rect1
+ rect2.MoveByX(1);
+ EXPECT_FALSE(
+ !dest.IntersectRect(rect1, rect2) ||
+ !(dest.IsEqualInterior(RectType(rect1.X() + 1, rect1.Y(),
+ rect1.Width() - 1, rect1.Height()))))
+ << "[7] Test against a rect that overlaps the right edge of rect1";
+ rect2.MoveByX(-1);
+
+ // Test against a rect that's outside of rect1 on the right
+ rect2.MoveByX(rect2.Width());
+ EXPECT_FALSE(dest.IntersectRect(rect1, rect2))
+ << "[8] Test against a rect that's outside of rect1 on the right";
+ // Make sure an empty rect is returned
+ EXPECT_TRUE(dest.IsEmpty() && dest.IsZeroArea())
+ << "[8] Make sure an empty rect is returned";
+ EXPECT_TRUE(dest.IsFinite()) << "[8b] Should be finite";
+ rect2.MoveByX(-rect2.Width());
+
+ // Test against a rect that overlaps the bottom edge of rect1
+ rect2.MoveByY(1);
+ EXPECT_FALSE(
+ !dest.IntersectRect(rect1, rect2) ||
+ !(dest.IsEqualInterior(RectType(rect1.X(), rect1.Y() + 1, rect1.Width(),
+ rect1.Height() - 1))))
+ << "[9] Test against a rect that overlaps the bottom edge of rect1";
+ EXPECT_TRUE(dest.IsFinite()) << "[9b] Should be finite";
+ rect2.MoveByY(-1);
+
+ // Test against a rect that's outside of rect1 on the bottom
+ rect2.MoveByY(rect2.Height());
+ EXPECT_FALSE(dest.IntersectRect(rect1, rect2))
+ << "[10] Test against a rect that's outside of rect1 on the bottom";
+ // Make sure an empty rect is returned
+ EXPECT_TRUE(dest.IsEmpty() && dest.IsZeroArea())
+ << "[10] Make sure an empty rect is returned";
+ EXPECT_TRUE(dest.IsFinite()) << "[10b] Should be finite";
+ rect2.MoveByY(-rect2.Height());
+
+ // Test against a rect with zero width or height
+ rect1.SetRect(100, 100, 100, 100);
+ rect2.SetRect(150, 100, 0, 100);
+ EXPECT_TRUE(!dest.IntersectRect(rect1, rect2) && dest.IsEmpty() &&
+ dest.IsZeroArea())
+ << "[11] Intersection of rects with zero width or height should be empty";
+ EXPECT_TRUE(dest.IsFinite()) << "[11b] Should be finite";
+
+ // Tests against a rect with negative width or height
+ //
+
+ // Test against a rect with negative width
+ rect1.SetRect(100, 100, 100, 100);
+ rect2.SetRect(100, 100, -100, 100);
+ EXPECT_TRUE(!dest.IntersectRect(rect1, rect2) && dest.IsEmpty() &&
+ dest.IsZeroArea())
+ << "[12] Intersection of rects with negative width or height should be "
+ "empty";
+ EXPECT_TRUE(dest.IsFinite()) << "[12b] Should be finite";
+
+ // Those two rects exactly overlap in some way...
+ // but we still want to return an empty rect
+ rect1.SetRect(100, 100, 100, 100);
+ rect2.SetRect(200, 200, -100, -100);
+ EXPECT_TRUE(!dest.IntersectRect(rect1, rect2) && dest.IsEmpty() &&
+ dest.IsZeroArea())
+ << "[13] Intersection of rects with negative width or height should be "
+ "empty";
+ EXPECT_TRUE(dest.IsFinite()) << "[13b] Should be finite";
+
+ // Test against two identical rects with negative height
+ rect1.SetRect(100, 100, 100, -100);
+ rect2.SetRect(100, 100, 100, -100);
+ EXPECT_TRUE(!dest.IntersectRect(rect1, rect2) && dest.IsEmpty() &&
+ dest.IsZeroArea())
+ << "[14] Intersection of rects with negative width or height should be "
+ "empty";
+ EXPECT_TRUE(dest.IsFinite()) << "[14b] Should be finite";
+
+ return true;
+}
+
+template <class RectType>
+static bool TestUnion() {
+ RectType rect1;
+ RectType rect2(10, 10, 50, 50);
+ RectType dest;
+
+ // Check the case where the receiver is an empty rect
+ rect1.SetEmpty();
+ dest.UnionRect(rect1, rect2);
+ EXPECT_TRUE(!dest.IsEmpty() && !dest.IsZeroArea() &&
+ dest.IsEqualInterior(rect2))
+ << "[1] Check the case where the receiver is an empty rect";
+ EXPECT_TRUE(dest.IsFinite()) << "[1b] Should be finite";
+
+ // Check the case where the source rect is an empty rect
+ rect1 = rect2;
+ rect2.SetEmpty();
+ dest.UnionRect(rect1, rect2);
+ EXPECT_TRUE(!dest.IsEmpty() && !dest.IsZeroArea() &&
+ dest.IsEqualInterior(rect1))
+ << "[2] Check the case where the source rect is an empty rect";
+ EXPECT_TRUE(dest.IsFinite()) << "[2b] Should be finite";
+
+ // Test the case where both rects are empty
+ rect1.SetEmpty();
+ rect2.SetEmpty();
+ dest.UnionRect(rect1, rect2);
+ EXPECT_TRUE(dest.IsEmpty() && dest.IsZeroArea())
+ << "[3] Test the case where both rects are empty";
+ EXPECT_TRUE(dest.IsFinite()) << "[3b] Should be finite";
+
+ // Test union case where the two rects don't overlap at all
+ rect1.SetRect(10, 10, 50, 50);
+ rect2.SetRect(100, 100, 50, 50);
+ dest.UnionRect(rect1, rect2);
+ EXPECT_TRUE(!dest.IsEmpty() && !dest.IsZeroArea() &&
+ (dest.IsEqualInterior(RectType(rect1.X(), rect1.Y(),
+ rect2.XMost() - rect1.X(),
+ rect2.YMost() - rect1.Y()))))
+ << "[4] Test union case where the two rects don't overlap at all";
+ EXPECT_TRUE(dest.IsFinite()) << "[4b] Should be finite";
+
+ // Test union case where the two rects overlap
+ rect1.SetRect(30, 30, 50, 50);
+ rect2.SetRect(10, 10, 50, 50);
+ dest.UnionRect(rect1, rect2);
+ EXPECT_TRUE(!dest.IsEmpty() && !dest.IsZeroArea() &&
+ (dest.IsEqualInterior(RectType(rect2.X(), rect2.Y(),
+ rect1.XMost() - rect2.X(),
+ rect1.YMost() - rect2.Y()))))
+ << "[5] Test union case where the two rects overlap";
+ EXPECT_TRUE(dest.IsFinite()) << "[5b] Should be finite";
+
+ return true;
+}
+
+static bool TestFiniteGfx() {
+ float posInf = std::numeric_limits<float>::infinity();
+ float negInf = -std::numeric_limits<float>::infinity();
+ float justNaN = std::numeric_limits<float>::quiet_NaN();
+
+ gfxFloat values[4] = {5.0, 10.0, 15.0, 20.0};
+
+ // Try the "non-finite" values for x, y, width, height, one at a time
+ for (int i = 0; i < 4; i += 1) {
+ values[i] = posInf;
+ gfxRect rectPosInf(values[0], values[1], values[2], values[3]);
+ EXPECT_FALSE(rectPosInf.IsFinite())
+ << "For +inf (" << values[0] << "," << values[1] << "," << values[2]
+ << "," << values[3] << ")";
+
+ values[i] = negInf;
+ gfxRect rectNegInf(values[0], values[1], values[2], values[3]);
+ EXPECT_FALSE(rectNegInf.IsFinite())
+ << "For -inf (" << values[0] << "," << values[1] << "," << values[2]
+ << "," << values[3] << ")";
+
+ values[i] = justNaN;
+ gfxRect rectNaN(values[0], values[1], values[2], values[3]);
+ EXPECT_FALSE(rectNaN.IsFinite())
+ << "For NaN (" << values[0] << "," << values[1] << "," << values[2]
+ << "," << values[3] << ")";
+
+ // Reset to a finite value...
+ values[i] = 5.0 * i;
+ }
+
+ return true;
+}
+
+// We want to test nsRect values that are still in range but where
+// the implementation is at risk of overflowing
+template <class RectType>
+static bool TestBug1135677() {
+ RectType rect1(1073741344, 1073741344, 1073756696, 1073819936);
+ RectType rect2(1073741820, 1073741820, 14400, 77640);
+ RectType dest;
+
+ dest = rect1.Intersect(rect2);
+
+ EXPECT_TRUE(dest.IsEqualRect(1073741820, 1073741820, 14400, 77640))
+ << "[1] Operation should not overflow internally.";
+
+ return true;
+}
+
+template <class RectType>
+static bool TestSetWH() {
+ RectType rect(1, 2, 3, 4);
+ EXPECT_TRUE(rect.IsEqualRect(1, 2, 3, 4));
+ rect.SetWidth(13);
+ EXPECT_TRUE(rect.IsEqualRect(1, 2, 13, 4));
+ rect.SetHeight(14);
+ EXPECT_TRUE(rect.IsEqualRect(1, 2, 13, 14));
+ rect.SizeTo(23, 24);
+ EXPECT_TRUE(rect.IsEqualRect(1, 2, 23, 24));
+ return true;
+}
+
+template <class RectType>
+static bool TestSwap() {
+ RectType rect(1, 2, 3, 4);
+ EXPECT_TRUE(rect.IsEqualRect(1, 2, 3, 4));
+ rect.Swap();
+ EXPECT_TRUE(rect.IsEqualRect(2, 1, 4, 3));
+ return true;
+}
+
+static void TestIntersectionLogicalHelper(nscoord x1, nscoord y1, nscoord w1,
+ nscoord h1, nscoord x2, nscoord y2,
+ nscoord w2, nscoord h2, nscoord xR,
+ nscoord yR, nscoord wR, nscoord hR,
+ bool isNonEmpty) {
+ nsRect rect1(x1, y1, w1, h1);
+ nsRect rect2(x2, y2, w2, h2);
+ nsRect rectDebug;
+ EXPECT_TRUE(isNonEmpty == rectDebug.IntersectRect(rect1, rect2));
+ EXPECT_TRUE(rectDebug.IsEqualEdges(nsRect(xR, yR, wR, hR)));
+
+ mozilla::LogicalRect r1(mozilla::WritingMode(), rect1.X(), rect1.Y(),
+ rect1.Width(), rect1.Height());
+ mozilla::LogicalRect r2(mozilla::WritingMode(), rect2.X(), rect2.Y(),
+ rect2.Width(), rect2.Height());
+ EXPECT_TRUE(isNonEmpty == r1.IntersectRect(r1, r2));
+ EXPECT_TRUE(rectDebug.IsEqualEdges(nsRect(
+ r1.IStart(mozilla::WritingMode()), r1.BStart(mozilla::WritingMode()),
+ r1.ISize(mozilla::WritingMode()), r1.BSize(mozilla::WritingMode()))));
+
+ mozilla::LogicalRect r3(mozilla::WritingMode(), rect1.X(), rect1.Y(),
+ rect1.Width(), rect1.Height());
+ mozilla::LogicalRect r4(mozilla::WritingMode(), rect2.X(), rect2.Y(),
+ rect2.Width(), rect2.Height());
+ EXPECT_TRUE(isNonEmpty == r4.IntersectRect(r3, r4));
+ EXPECT_TRUE(rectDebug.IsEqualEdges(nsRect(
+ r4.IStart(mozilla::WritingMode()), r4.BStart(mozilla::WritingMode()),
+ r4.ISize(mozilla::WritingMode()), r4.BSize(mozilla::WritingMode()))));
+
+ mozilla::LogicalRect r5(mozilla::WritingMode(), rect1.X(), rect1.Y(),
+ rect1.Width(), rect1.Height());
+ mozilla::LogicalRect r6(mozilla::WritingMode(), rect2.X(), rect2.Y(),
+ rect2.Width(), rect2.Height());
+ mozilla::LogicalRect r7(mozilla::WritingMode(), 0, 0, 1, 1);
+ EXPECT_TRUE(isNonEmpty == r7.IntersectRect(r5, r6));
+ EXPECT_TRUE(rectDebug.IsEqualEdges(nsRect(
+ r7.IStart(mozilla::WritingMode()), r7.BStart(mozilla::WritingMode()),
+ r7.ISize(mozilla::WritingMode()), r7.BSize(mozilla::WritingMode()))));
+}
+
+static void TestIntersectionLogical(nscoord x1, nscoord y1, nscoord w1,
+ nscoord h1, nscoord x2, nscoord y2,
+ nscoord w2, nscoord h2, nscoord xR,
+ nscoord yR, nscoord wR, nscoord hR,
+ bool isNonEmpty) {
+ TestIntersectionLogicalHelper(x1, y1, w1, h1, x2, y2, w2, h2, xR, yR, wR, hR,
+ isNonEmpty);
+ TestIntersectionLogicalHelper(x2, y2, w2, h2, x1, y1, w1, h1, xR, yR, wR, hR,
+ isNonEmpty);
+}
+
+TEST(Gfx, Logical)
+{
+ TestIntersectionLogical(578, 0, 2650, 1152, 1036, 0, 2312, 1, 1036, 0, 2192,
+ 1, true);
+ TestIntersectionLogical(0, 0, 1000, 1000, 500, 500, 1000, 1000, 500, 500, 500,
+ 500, true);
+ TestIntersectionLogical(100, 200, 300, 400, 50, 250, 100, 100, 100, 250, 50,
+ 100, true);
+ TestIntersectionLogical(0, 100, 200, 300, 300, 100, 100, 300, 300, 100, 0, 0,
+ false);
+}
+
+TEST(Gfx, nsRect)
+{
+ TestConstructors<nsRect>();
+ TestEqualityOperator<nsRect>();
+ TestContainment<nsRect>();
+ TestIntersects<nsRect>();
+ TestIntersection<nsRect>();
+ TestUnion<nsRect>();
+ TestBug1135677<nsRect>();
+ TestSetWH<nsRect>();
+ TestSwap<nsRect>();
+}
+
+TEST(Gfx, nsIntRect)
+{
+ TestConstructors<nsIntRect>();
+ TestEqualityOperator<nsIntRect>();
+ TestContainment<nsIntRect>();
+ TestIntersects<nsIntRect>();
+ TestIntersection<nsIntRect>();
+ TestUnion<nsIntRect>();
+ TestBug1135677<nsIntRect>();
+ TestSetWH<nsIntRect>();
+ TestSwap<nsIntRect>();
+}
+
+TEST(Gfx, gfxRect)
+{
+ TestConstructors<gfxRect>();
+ // Skip TestEqualityOperator<gfxRect>(); as gfxRect::operator== is private
+ TestContainment<gfxRect>();
+ TestIntersects<gfxRect>();
+ TestIntersection<gfxRect>();
+ TestUnion<gfxRect>();
+ TestBug1135677<gfxRect>();
+ TestFiniteGfx();
+ TestSetWH<gfxRect>();
+ TestSwap<gfxRect>();
+}
+
+static void TestMoveInsideAndClamp(IntRect aSrc, IntRect aTarget,
+ IntRect aExpected) {
+ // Test the implementation in BaseRect (x/y/width/height representation)
+ IntRect result = aSrc.MoveInsideAndClamp(aTarget);
+ EXPECT_TRUE(result.IsEqualEdges(aExpected))
+ << "Source " << aSrc << " Target " << aTarget << " Expected " << aExpected
+ << " Actual " << result;
+
+ // Also test the implementation in RectAbsolute (left/top/right/bottom
+ // representation)
+ IntRectAbsolute absSrc = IntRectAbsolute::FromRect(aSrc);
+ IntRectAbsolute absTarget = IntRectAbsolute::FromRect(aTarget);
+ IntRectAbsolute absExpected = IntRectAbsolute::FromRect(aExpected);
+
+ IntRectAbsolute absResult = absSrc.MoveInsideAndClamp(absTarget);
+ EXPECT_TRUE(absResult.IsEqualEdges(absExpected))
+ << "AbsSource " << absSrc << " AbsTarget " << absTarget << " AbsExpected "
+ << absExpected << " AbsActual " << absResult;
+}
+
+TEST(Gfx, MoveInsideAndClamp)
+{
+ TestMoveInsideAndClamp(IntRect(0, 0, 10, 10), IntRect(1, -1, 10, 10),
+ IntRect(1, -1, 10, 10));
+ TestMoveInsideAndClamp(IntRect(0, 0, 10, 10), IntRect(-1, -1, 12, 5),
+ IntRect(0, -1, 10, 5));
+ TestMoveInsideAndClamp(IntRect(0, 0, 10, 10), IntRect(10, 11, 10, 0),
+ IntRect(10, 11, 10, 0));
+ TestMoveInsideAndClamp(IntRect(0, 0, 10, 10), IntRect(-10, -1, 10, 0),
+ IntRect(-10, -1, 10, 0));
+ TestMoveInsideAndClamp(IntRect(0, 0, 0, 0), IntRect(10, -10, 10, 10),
+ IntRect(10, 0, 0, 0));
+}
diff --git a/gfx/tests/gtest/TestRegion.cpp b/gfx/tests/gtest/TestRegion.cpp
new file mode 100644
index 0000000000..23ac24f51e
--- /dev/null
+++ b/gfx/tests/gtest/TestRegion.cpp
@@ -0,0 +1,1533 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <algorithm>
+
+#include "gtest/gtest.h"
+#include "nsRegion.h"
+#include "RegionBuilder.h"
+#include "mozilla/gfx/TiledRegion.h"
+#include "mozilla/UniquePtr.h"
+
+using namespace mozilla::gfx;
+
+//#define REGION_RANDOM_STRESS_TESTS
+
+class TestLargestRegion {
+ public:
+ static void TestSingleRect(nsRect r) {
+ nsRegion region(r);
+ EXPECT_TRUE(region.GetLargestRectangle().IsEqualInterior(r));
+ }
+ // Construct a rectangle, remove part of it, then check the remainder
+ static void TestNonRectangular() {
+ nsRegion r(nsRect(0, 0, 30, 30));
+
+ const int nTests = 19;
+ struct {
+ nsRect rect;
+ int64_t expectedArea;
+ } tests[nTests] = {// Remove a 20x10 chunk from the square
+ {nsRect(0, 0, 20, 10), 600},
+ {nsRect(10, 0, 20, 10), 600},
+ {nsRect(10, 20, 20, 10), 600},
+ {nsRect(0, 20, 20, 10), 600},
+ // Remove a 10x20 chunk from the square
+ {nsRect(0, 0, 10, 20), 600},
+ {nsRect(20, 0, 10, 20), 600},
+ {nsRect(20, 10, 10, 20), 600},
+ {nsRect(0, 10, 10, 20), 600},
+ // Remove the center 10x10
+ {nsRect(10, 10, 10, 10), 300},
+ // Remove the middle column
+ {nsRect(10, 0, 10, 30), 300},
+ // Remove the middle row
+ {nsRect(0, 10, 30, 10), 300},
+ // Remove the corners 10x10
+ {nsRect(0, 0, 10, 10), 600},
+ {nsRect(20, 20, 10, 10), 600},
+ {nsRect(20, 0, 10, 10), 600},
+ {nsRect(0, 20, 10, 10), 600},
+ // Remove the corners 20x20
+ {nsRect(0, 0, 20, 20), 300},
+ {nsRect(10, 10, 20, 20), 300},
+ {nsRect(10, 0, 20, 20), 300},
+ {nsRect(0, 10, 20, 20), 300}};
+
+ for (int32_t i = 0; i < nTests; i++) {
+ nsRegion r2;
+ r2.Sub(r, tests[i].rect);
+
+ EXPECT_TRUE(r2.IsComplex()) << "nsRegion code got unexpectedly smarter!";
+
+ nsRect largest = r2.GetLargestRectangle();
+ EXPECT_TRUE(largest.Width() * largest.Height() == tests[i].expectedArea)
+ << "Did not successfully find largest rectangle in non-rectangular "
+ "region on iteration "
+ << i;
+ }
+ }
+ static void TwoRectTest() {
+ nsRegion r(nsRect(0, 0, 100, 100));
+ const int nTests = 4;
+ struct {
+ nsRect rect1, rect2;
+ int64_t expectedArea;
+ } tests[nTests] = {
+ {nsRect(0, 0, 75, 40), nsRect(0, 60, 75, 40), 2500},
+ {nsRect(25, 0, 75, 40), nsRect(25, 60, 75, 40), 2500},
+ {nsRect(25, 0, 75, 40), nsRect(0, 60, 75, 40), 2000},
+ {nsRect(0, 0, 75, 40), nsRect(25, 60, 75, 40), 2000},
+ };
+ for (int32_t i = 0; i < nTests; i++) {
+ nsRegion r2;
+
+ r2.Sub(r, tests[i].rect1);
+ r2.Sub(r2, tests[i].rect2);
+
+ EXPECT_TRUE(r2.IsComplex()) << "nsRegion code got unexpectedly smarter!";
+
+ nsRect largest = r2.GetLargestRectangle();
+ EXPECT_TRUE(largest.Width() * largest.Height() == tests[i].expectedArea)
+ << "Did not successfully find largest rectangle in two-rect-subtract "
+ "region on iteration "
+ << i;
+ }
+ }
+ static void TestContainsSpecifiedRect() {
+ nsRegion r(nsRect(0, 0, 100, 100));
+ r.Or(r, nsRect(0, 300, 50, 50));
+ EXPECT_TRUE(r.GetLargestRectangle(nsRect(0, 300, 10, 10))
+ .IsEqualInterior(nsRect(0, 300, 50, 50)))
+ << "Chose wrong rectangle";
+ }
+ static void TestContainsSpecifiedOverflowingRect() {
+ nsRegion r(nsRect(0, 0, 100, 100));
+ r.Or(r, nsRect(0, 300, 50, 50));
+ EXPECT_TRUE(r.GetLargestRectangle(nsRect(0, 290, 10, 20))
+ .IsEqualInterior(nsRect(0, 300, 50, 50)))
+ << "Chose wrong rectangle";
+ }
+};
+
+TEST(Gfx, RegionSingleRect)
+{
+ TestLargestRegion::TestSingleRect(nsRect(0, 52, 720, 480));
+ TestLargestRegion::TestSingleRect(nsRect(-20, 40, 50, 20));
+ TestLargestRegion::TestSingleRect(nsRect(-20, 40, 10, 8));
+ TestLargestRegion::TestSingleRect(nsRect(-20, -40, 10, 8));
+ TestLargestRegion::TestSingleRect(nsRect(-10, -10, 20, 20));
+}
+
+TEST(Gfx, RegionNonRectangular)
+{ TestLargestRegion::TestNonRectangular(); }
+
+TEST(Gfx, RegionTwoRectTest)
+{ TestLargestRegion::TwoRectTest(); }
+
+TEST(Gfx, RegionContainsSpecifiedRect)
+{ TestLargestRegion::TestContainsSpecifiedRect(); }
+
+TEST(Gfx, RegionTestContainsSpecifiedOverflowingRect)
+{ TestLargestRegion::TestContainsSpecifiedOverflowingRect(); }
+
+TEST(Gfx, RegionScaleToInside)
+{
+ { // no rectangles
+ nsRegion r;
+
+ nsIntRegion scaled = r.ScaleToInsidePixels(1, 1, 60);
+ nsIntRegion result;
+
+ EXPECT_TRUE(result.IsEqual(scaled)) << "scaled result incorrect";
+ }
+
+ { // one rectangle
+ nsRegion r(nsRect(0, 44760, 19096, 264));
+
+ nsIntRegion scaled = r.ScaleToInsidePixels(1, 1, 60);
+ nsIntRegion result(mozilla::gfx::IntRect(0, 746, 318, 4));
+
+ EXPECT_TRUE(result.IsEqual(scaled)) << "scaled result incorrect";
+ }
+
+ { // the first rectangle gets adjusted
+ nsRegion r(nsRect(0, 44760, 19096, 264));
+ r.Or(r, nsRect(0, 45024, 19360, 1056));
+
+ nsIntRegion scaled = r.ScaleToInsidePixels(1, 1, 60);
+ nsIntRegion result(mozilla::gfx::IntRect(0, 746, 318, 5));
+ result.Or(result, mozilla::gfx::IntRect(0, 751, 322, 17));
+
+ EXPECT_TRUE(result.IsEqual(scaled)) << "scaled result incorrect";
+ }
+
+ { // the second rectangle gets adjusted
+ nsRegion r(nsRect(0, 44760, 19360, 264));
+ r.Or(r, nsRect(0, 45024, 19096, 1056));
+
+ nsIntRegion scaled = r.ScaleToInsidePixels(1, 1, 60);
+ nsIntRegion result(mozilla::gfx::IntRect(0, 746, 322, 4));
+ result.Or(result, mozilla::gfx::IntRect(0, 750, 318, 18));
+
+ EXPECT_TRUE(result.IsEqual(scaled)) << "scaled result incorrect";
+ }
+}
+
+TEST(Gfx, RegionIsEqual)
+{
+ {
+ nsRegion r(nsRect(0, 0, 50, 50));
+ EXPECT_FALSE(nsRegion().IsEqual(r));
+ }
+ {
+ nsRegion r1(nsRect(0, 0, 50, 50));
+ nsRegion r2(nsRect(0, 0, 50, 50));
+ EXPECT_TRUE(r1.IsEqual(r2));
+ }
+ {
+ nsRegion r1(nsRect(0, 0, 50, 50));
+ nsRegion r2(nsRect(0, 0, 60, 50));
+ EXPECT_FALSE(r1.IsEqual(r2));
+ }
+ {
+ nsRegion r1(nsRect(0, 0, 50, 50));
+ r1.OrWith(nsRect(0, 60, 50, 50));
+ nsRegion r2(nsRect(0, 0, 50, 50));
+ r2.OrWith(nsRect(0, 60, 50, 50));
+ EXPECT_TRUE(r1.IsEqual(r2));
+ }
+ {
+ nsRegion r1(nsRect(0, 0, 50, 50));
+ r1.OrWith(nsRect(0, 60, 50, 50));
+ nsRegion r2(nsRect(0, 0, 50, 50));
+ r2.OrWith(nsRect(0, 70, 50, 50));
+ EXPECT_FALSE(r1.IsEqual(r2));
+ }
+ {
+ nsRegion r1(nsRect(0, 0, 50, 50));
+ r1.OrWith(nsRect(0, 60, 50, 50));
+ r1.OrWith(nsRect(100, 60, 50, 50));
+ nsRegion r2(nsRect(0, 0, 50, 50));
+ r2.OrWith(nsRect(0, 60, 50, 50));
+ EXPECT_FALSE(r1.IsEqual(r2));
+ }
+}
+
+TEST(Gfx, RegionOrWith)
+{
+ PR_Sleep(PR_SecondsToInterval(10));
+ {
+ nsRegion r(nsRect(11840, 11840, 4640, -10880));
+ r.OrWith(nsRect(160, 160, 7720, 880));
+ }
+ {
+ nsRegion r(nsRect(79, 31, 75, 12));
+ r.OrWith(nsRect(22, 43, 132, 5));
+ r.OrWith(nsRect(22, 48, 125, 3));
+ r.OrWith(nsRect(22, 51, 96, 20));
+ r.OrWith(nsRect(34, 71, 1, 14));
+ r.OrWith(nsRect(26, 85, 53, 1));
+ r.OrWith(nsRect(26, 86, 53, 4));
+ r.OrWith(nsRect(96, 86, 30, 4));
+ r.OrWith(nsRect(34, 90, 1, 2));
+ r.OrWith(nsRect(96, 90, 30, 2));
+ r.OrWith(nsRect(34, 92, 1, 3));
+ r.OrWith(nsRect(49, 92, 34, 3));
+ r.OrWith(nsRect(96, 92, 30, 3));
+ r.OrWith(nsRect(34, 95, 1, 17));
+ r.OrWith(nsRect(49, 95, 77, 17));
+ r.OrWith(nsRect(34, 112, 1, 12));
+ r.OrWith(nsRect(75, 112, 51, 12));
+ r.OrWith(nsRect(34, 124, 1, 10));
+ r.OrWith(nsRect(75, 124, 44, 10));
+ r.OrWith(nsRect(34, 134, 1, 19));
+ r.OrWith(nsRect(22, 17, 96, 27));
+ }
+ {
+ nsRegion r(nsRect(0, 8, 257, 32));
+ r.OrWith(nsRect(3702, 8, 138, 32));
+ r.OrWith(nsRect(0, 40, 225, 1));
+ r.OrWith(nsRect(3702, 40, 138, 1));
+ r.OrWith(nsRect(0, 41, 101, 40));
+ r.OrWith(nsRect(69, 41, 32, 40));
+ }
+ {
+ nsRegion r(nsRect(79, 56, 8, 32));
+ r.OrWith(nsRect(5, 94, 23, 81));
+ r.OrWith(nsRect(56, 29, 91, 81));
+ }
+ {
+ nsRegion r(nsRect(0, 82, 3840, 2046));
+ r.OrWith(nsRect(0, 0, 3840, 82));
+ }
+ {
+ nsRegion r(nsRect(2, 5, 600, 28));
+ r.OrWith(nsRect(2, 82, 600, 19));
+ r.OrWith(nsRect(2, 33, 600, 49));
+ }
+ {
+ nsRegion r(nsRect(3823, 0, 17, 17));
+ r.OrWith(nsRect(3823, 2029, 17, 17));
+ r.OrWith(nsRect(3823, 0, 17, 2046));
+ }
+ {
+ nsRegion r(nsRect(1036, 4, 32, 21));
+ r.OrWith(nsRect(1070, 4, 66, 21));
+ r.OrWith(nsRect(40, 5, 0, 33));
+ }
+ {
+ nsRegion r(nsRect(0, 0, 1024, 1152));
+ r.OrWith(nsRect(-335802, -1073741824, 1318851, 1860043520));
+ }
+ {
+ nsRegion r(nsRect(0, 0, 800, 1000));
+ r.OrWith(nsRect(0, 0, 536870912, 1073741824));
+ }
+ {
+ nsRegion r(nsRect(53, 2, 52, 3));
+ r.OrWith(nsRect(45, 5, 60, 16));
+ r.OrWith(nsRect(16, 21, 8, 1));
+ r.OrWith(nsRect(45, 21, 12, 1));
+ r.OrWith(nsRect(16, 22, 8, 5));
+ r.OrWith(nsRect(33, 22, 52, 5));
+ r.OrWith(nsRect(16, 27, 8, 7));
+ r.OrWith(nsRect(33, 27, 66, 7));
+ r.OrWith(nsRect(0, 34, 99, 1));
+ r.OrWith(nsRect(0, 35, 159, 27));
+ r.OrWith(nsRect(0, 62, 122, 3));
+ r.OrWith(nsRect(0, 65, 85, 11));
+ r.OrWith(nsRect(91, 65, 97, 11));
+ r.OrWith(nsRect(11, 76, 74, 2));
+ r.OrWith(nsRect(91, 76, 97, 2));
+ r.OrWith(nsRect(11, 78, 74, 12));
+ r.OrWith(nsRect(11, 90, 13, 3));
+ r.OrWith(nsRect(33, 90, 108, 3));
+ r.OrWith(nsRect(16, 93, 8, 22));
+ r.OrWith(nsRect(33, 93, 108, 22));
+ r.OrWith(nsRect(16, 115, 8, 1));
+ r.OrWith(nsRect(58, 115, 83, 1));
+ r.OrWith(nsRect(58, 116, 83, 25));
+ r.OrWith(nsRect(59, 37, 88, 92));
+ }
+#ifdef REGION_RANDOM_STRESS_TESTS
+ const uint32_t TestIterations = 100000;
+ const uint32_t RectsPerTest = 100;
+
+ nsRect rects[RectsPerTest];
+
+ for (uint32_t i = 0; i < TestIterations; i++) {
+ nsRegion r;
+ for (uint32_t n = 0; n < RectsPerTest; n++) {
+ rects[n].SetRect(rand() % 100, rand() % 100, rand() % 99 + 1,
+ rand() % 99 + 1);
+ }
+ r.SetEmpty();
+ for (uint32_t n = 0; n < RectsPerTest; n++) {
+ r.OrWith(rects[n]);
+ }
+ for (uint32_t n = 0; n < RectsPerTest; n++) {
+ EXPECT_TRUE(r.Contains(rects[n]));
+ }
+ }
+#endif
+}
+
+TEST(Gfx, RegionSubWith)
+{
+ {
+ nsRegion r1(nsRect(0, 0, 100, 50));
+ r1.OrWith(nsRect(50, 50, 50, 50));
+ nsRegion r2(nsRect(0, 0, 100, 50));
+ r2.OrWith(nsRect(50, 50, 50, 50));
+ r1.SubWith(r2);
+ EXPECT_FALSE(r1.Contains(1, 1));
+ }
+ {
+ nsRegion r1(nsRect(0, 0, 800, 1000));
+ nsRegion r2(nsRect(8, 108, 22, 20));
+ r2.OrWith(nsRect(91, 138, 17, 18));
+ r1.SubWith(r2);
+ EXPECT_TRUE(r1.Contains(400, 130));
+ }
+ {
+ nsRegion r1(nsRect(392, 2, 28, 7));
+ r1.OrWith(nsRect(115, 9, 305, 16));
+ r1.OrWith(nsRect(392, 25, 28, 5));
+ r1.OrWith(nsRect(0, 32, 1280, 41));
+ nsRegion r2(nsRect(0, 0, 1280, 9));
+ r2.OrWith(nsRect(0, 9, 115, 16));
+ r2.OrWith(nsRect(331, 9, 949, 16));
+ r2.OrWith(nsRect(0, 25, 1280, 7));
+ r2.OrWith(nsRect(331, 32, 124, 1));
+ r1.SubWith(r2);
+ EXPECT_FALSE(r1.Contains(350, 15));
+ }
+ {
+ nsRegion r1(nsRect(552, 0, 2, 2));
+ r1.OrWith(nsRect(362, 2, 222, 28));
+ r1.OrWith(nsRect(552, 30, 2, 2));
+ r1.OrWith(nsRect(0, 32, 1280, 41));
+ nsRegion r2(nsRect(512, 0, 146, 9));
+ r2.OrWith(nsRect(340, 9, 318, 16));
+ r2.OrWith(nsRect(512, 25, 146, 8));
+ r1.SubWith(r2);
+ EXPECT_FALSE(r1.Contains(350, 15));
+ }
+ {
+ nsRegion r(nsRect(0, 0, 229380, 6780));
+ r.OrWith(nsRect(76800, 6780, 76800, 4440));
+ r.OrWith(nsRect(76800, 11220, 44082, 1800));
+ r.OrWith(nsRect(122682, 11220, 30918, 1800));
+ r.OrWith(nsRect(76800, 13020, 76800, 2340));
+ r.OrWith(nsRect(85020, 15360, 59340, 75520));
+ r.OrWith(nsRect(85020, 90880, 38622, 11332));
+ r.OrWith(nsRect(143789, 90880, 571, 11332));
+ r.OrWith(nsRect(85020, 102212, 59340, 960));
+ r.OrWith(nsRect(85020, 103172, 38622, 1560));
+ r.OrWith(nsRect(143789, 103172, 571, 1560));
+ r.OrWith(nsRect(85020, 104732, 59340, 12292));
+ r.OrWith(nsRect(85020, 117024, 38622, 1560));
+ r.OrWith(nsRect(143789, 117024, 571, 1560));
+ r.OrWith(nsRect(85020, 118584, 59340, 11976));
+ r.SubWith(nsRect(123642, 89320, 20147, 1560));
+ }
+ {
+ nsRegion r(nsRect(0, 0, 9480, 12900));
+ r.OrWith(nsRect(0, 12900, 8460, 1020));
+ r.SubWith(nsRect(8460, 0, 1020, 12900));
+ }
+ {
+ nsRegion r1(nsRect(99, 1, 51, 2));
+ r1.OrWith(nsRect(85, 3, 65, 1));
+ r1.OrWith(nsRect(10, 4, 66, 5));
+ r1.OrWith(nsRect(85, 4, 37, 5));
+ r1.OrWith(nsRect(10, 9, 112, 3));
+ r1.OrWith(nsRect(1, 12, 121, 1));
+ r1.OrWith(nsRect(1, 13, 139, 3));
+ r1.OrWith(nsRect(0, 16, 140, 3));
+ r1.OrWith(nsRect(0, 19, 146, 3));
+ r1.OrWith(nsRect(0, 22, 149, 2));
+ r1.OrWith(nsRect(0, 24, 154, 2));
+ r1.OrWith(nsRect(0, 26, 160, 23));
+ r1.OrWith(nsRect(0, 49, 162, 31));
+ r1.OrWith(nsRect(0, 80, 171, 19));
+ r1.OrWith(nsRect(0, 99, 173, 11));
+ r1.OrWith(nsRect(2, 110, 171, 6));
+ r1.OrWith(nsRect(6, 116, 165, 5));
+ r1.OrWith(nsRect(8, 121, 163, 1));
+ r1.OrWith(nsRect(13, 122, 158, 11));
+ r1.OrWith(nsRect(14, 133, 157, 23));
+ r1.OrWith(nsRect(29, 156, 142, 10));
+ r1.OrWith(nsRect(37, 166, 134, 6));
+ r1.OrWith(nsRect(55, 172, 4, 4));
+ r1.OrWith(nsRect(83, 172, 88, 4));
+ r1.OrWith(nsRect(55, 176, 4, 2));
+ r1.OrWith(nsRect(89, 176, 6, 2));
+ r1.OrWith(nsRect(89, 178, 6, 4));
+ nsRegion r2(nsRect(63, 11, 39, 11));
+ r2.OrWith(nsRect(63, 22, 99, 16));
+ r2.OrWith(nsRect(37, 38, 125, 61));
+ r2.OrWith(nsRect(45, 99, 117, 8));
+ r2.OrWith(nsRect(47, 107, 115, 7));
+ r2.OrWith(nsRect(47, 114, 66, 1));
+ r2.OrWith(nsRect(49, 115, 64, 2));
+ r2.OrWith(nsRect(49, 117, 54, 30));
+ r1.SubWith(r2);
+ }
+ {
+ nsRegion r1(nsRect(95, 2, 47, 1));
+ r1.OrWith(nsRect(62, 3, 80, 2));
+ r1.OrWith(nsRect(1, 5, 18, 3));
+ r1.OrWith(nsRect(48, 5, 94, 3));
+ r1.OrWith(nsRect(1, 8, 18, 3));
+ r1.OrWith(nsRect(23, 8, 119, 3));
+ r1.OrWith(nsRect(1, 11, 172, 9));
+ r1.OrWith(nsRect(1, 20, 18, 8));
+ r1.OrWith(nsRect(20, 20, 153, 8));
+ r1.OrWith(nsRect(1, 28, 172, 13));
+ r1.OrWith(nsRect(1, 41, 164, 1));
+ r1.OrWith(nsRect(1, 42, 168, 1));
+ r1.OrWith(nsRect(0, 43, 169, 15));
+ r1.OrWith(nsRect(1, 58, 168, 26));
+ r1.OrWith(nsRect(1, 84, 162, 2));
+ r1.OrWith(nsRect(1, 86, 165, 23));
+ r1.OrWith(nsRect(1, 109, 162, 23));
+ r1.OrWith(nsRect(1, 132, 152, 4));
+ r1.OrWith(nsRect(1, 136, 150, 12));
+ r1.OrWith(nsRect(12, 148, 139, 4));
+ r1.OrWith(nsRect(12, 152, 113, 2));
+ r1.OrWith(nsRect(14, 154, 31, 3));
+ r1.OrWith(nsRect(82, 154, 43, 3));
+ r1.OrWith(nsRect(17, 157, 13, 19));
+ r1.OrWith(nsRect(82, 157, 43, 19));
+ r1.OrWith(nsRect(17, 176, 13, 16));
+ nsRegion r2(nsRect(97, 9, 6, 10));
+ r2.OrWith(nsRect(71, 19, 32, 2));
+ r2.OrWith(nsRect(20, 21, 83, 2));
+ r2.OrWith(nsRect(2, 23, 101, 9));
+ r2.OrWith(nsRect(2, 32, 98, 1));
+ r2.OrWith(nsRect(2, 33, 104, 5));
+ r2.OrWith(nsRect(2, 38, 118, 2));
+ r2.OrWith(nsRect(15, 40, 9, 11));
+ r2.OrWith(nsRect(36, 40, 84, 11));
+ r2.OrWith(nsRect(4, 51, 116, 33));
+ r2.OrWith(nsRect(4, 84, 159, 8));
+ r2.OrWith(nsRect(4, 92, 116, 13));
+ r2.OrWith(nsRect(15, 105, 9, 7));
+ r2.OrWith(nsRect(36, 105, 84, 7));
+ r2.OrWith(nsRect(36, 112, 84, 22));
+ r2.OrWith(nsRect(71, 134, 39, 46));
+ r1.SubWith(r2);
+ }
+#ifdef REGION_RANDOM_STRESS_TESTS
+ const uint32_t TestIterations = 100000;
+ const uint32_t RectsPerTest = 100;
+ const uint32_t SubRectsPerTest = 10;
+
+ nsRect rects[RectsPerTest];
+ nsRect subRects[SubRectsPerTest];
+
+ for (uint32_t i = 0; i < TestIterations; i++) {
+ nsRegion r;
+ for (uint32_t n = 0; n < RectsPerTest; n++) {
+ rects[n].SetRect(rand() % 100, rand() % 100, rand() % 99 + 1,
+ rand() % 99 + 1);
+ }
+ r.SetEmpty();
+ for (uint32_t n = 0; n < RectsPerTest; n++) {
+ r.OrWith(rects[n]);
+ }
+ for (uint32_t n = 0; n < RectsPerTest; n++) {
+ EXPECT_TRUE(r.Contains(rects[n]));
+ }
+ for (uint32_t n = 0; n < SubRectsPerTest; n++) {
+ subRects[n].SetRect(rand() % 100, rand() % 100, rand() % 99 + 1,
+ rand() % 99 + 1);
+ }
+ for (uint32_t n = 0; n < SubRectsPerTest; n++) {
+ r.SubWith(subRects[n]);
+ }
+ for (uint32_t n = 0; n < SubRectsPerTest; n++) {
+ EXPECT_FALSE(r.Contains(subRects[n].x, subRects[n].y));
+ EXPECT_FALSE(r.Contains(subRects[n].x, subRects[n].YMost() - 1));
+ EXPECT_FALSE(
+ r.Contains(subRects[n].XMost() - 1, subRects[n].YMost() - 1));
+ EXPECT_FALSE(r.Contains(subRects[n].XMost() - 1, subRects[n].Y()));
+ }
+ }
+ for (uint32_t i = 0; i < TestIterations; i++) {
+ nsRegion r;
+ for (uint32_t n = 0; n < RectsPerTest; n++) {
+ rects[n].SetRect(rand() % 100, rand() % 100, rand() % 99 + 1,
+ rand() % 99 + 1);
+ }
+ r.SetEmpty();
+ for (uint32_t n = 0; n < RectsPerTest; n++) {
+ r.OrWith(rects[n]);
+ }
+ for (uint32_t n = 0; n < RectsPerTest; n++) {
+ EXPECT_TRUE(r.Contains(rects[n]));
+ }
+ for (uint32_t n = 0; n < SubRectsPerTest; n++) {
+ subRects[n].SetRect(rand() % 100, rand() % 100, rand() % 99 + 1,
+ rand() % 99 + 1);
+ }
+ nsRegion r2;
+ for (uint32_t n = 0; n < SubRectsPerTest; n++) {
+ r2.OrWith(subRects[n]);
+ }
+ r.SubWith(r2);
+
+ for (uint32_t n = 0; n < SubRectsPerTest; n++) {
+ EXPECT_FALSE(r.Contains(subRects[n].x, subRects[n].y));
+ EXPECT_FALSE(r.Contains(subRects[n].x, subRects[n].YMost() - 1));
+ EXPECT_FALSE(
+ r.Contains(subRects[n].XMost() - 1, subRects[n].YMost() - 1));
+ EXPECT_FALSE(r.Contains(subRects[n].XMost() - 1, subRects[n].Y()));
+ }
+ }
+ for (uint32_t i = 0; i < TestIterations; i++) {
+ nsRegion r(nsRect(-1, -1, 202, 202));
+ for (uint32_t n = 0; n < SubRectsPerTest; n++) {
+ subRects[n].SetRect(rand() % 100, rand() % 100, rand() % 99 + 1,
+ rand() % 99 + 1);
+ r.SubWith(subRects[n]);
+ }
+ for (uint32_t n = 0; n < SubRectsPerTest; n++) {
+ EXPECT_FALSE(r.Contains(subRects[n].x, subRects[n].y));
+ EXPECT_FALSE(r.Contains(subRects[n].x, subRects[n].YMost() - 1));
+ EXPECT_FALSE(
+ r.Contains(subRects[n].XMost() - 1, subRects[n].YMost() - 1));
+ EXPECT_FALSE(r.Contains(subRects[n].XMost() - 1, subRects[n].Y()));
+ }
+ EXPECT_TRUE(r.Contains(-1, -1));
+ EXPECT_TRUE(r.Contains(-1, 200));
+ EXPECT_TRUE(r.Contains(200, -1));
+ EXPECT_TRUE(r.Contains(200, 200));
+ }
+ for (uint32_t i = 0; i < TestIterations; i++) {
+ nsRegion r(nsRect(-1, -1, 202, 202));
+ nsRegion r2;
+ for (uint32_t n = 0; n < SubRectsPerTest; n++) {
+ subRects[n].SetRect(rand() % 100, rand() % 100, rand() % 99 + 1,
+ rand() % 99 + 1);
+ r2.OrWith(subRects[n]);
+ }
+ r.SubWith(r2);
+ for (uint32_t n = 0; n < SubRectsPerTest; n++) {
+ EXPECT_FALSE(r.Contains(subRects[n].x, subRects[n].y));
+ EXPECT_FALSE(r.Contains(subRects[n].x, subRects[n].YMost() - 1));
+ EXPECT_FALSE(
+ r.Contains(subRects[n].XMost() - 1, subRects[n].YMost() - 1));
+ EXPECT_FALSE(r.Contains(subRects[n].XMost() - 1, subRects[n].Y()));
+ }
+ EXPECT_TRUE(r.Contains(-1, -1));
+ EXPECT_TRUE(r.Contains(-1, 200));
+ EXPECT_TRUE(r.Contains(200, -1));
+ EXPECT_TRUE(r.Contains(200, 200));
+ }
+#endif
+}
+TEST(Gfx, RegionSub)
+{
+ {
+ nsRegion r1(nsRect(0, 0, 100, 50));
+ r1.OrWith(nsRect(50, 50, 50, 50));
+ nsRegion r2(nsRect(0, 0, 100, 50));
+ r2.OrWith(nsRect(50, 50, 50, 50));
+ nsRegion r3;
+ r3.Sub(r1, r2);
+ EXPECT_FALSE(r3.Contains(1, 1));
+ }
+ {
+ nsRegion r1(nsRect(0, 0, 800, 1000));
+ nsRegion r2(nsRect(8, 108, 22, 20));
+ r2.OrWith(nsRect(91, 138, 17, 18));
+ nsRegion r3;
+ r3.Sub(r1, r2);
+ EXPECT_TRUE(r3.Contains(400, 130));
+ }
+ {
+ nsRegion r1(nsRect(392, 2, 28, 7));
+ r1.OrWith(nsRect(115, 9, 305, 16));
+ r1.OrWith(nsRect(392, 25, 28, 5));
+ r1.OrWith(nsRect(0, 32, 1280, 41));
+ nsRegion r2(nsRect(0, 0, 1280, 9));
+ r2.OrWith(nsRect(0, 9, 115, 16));
+ r2.OrWith(nsRect(331, 9, 949, 16));
+ r2.OrWith(nsRect(0, 25, 1280, 7));
+ r2.OrWith(nsRect(331, 32, 124, 1));
+ nsRegion r3;
+ r3.Sub(r1, r2);
+ EXPECT_FALSE(r3.Contains(350, 15));
+ }
+ {
+ nsRegion r1(nsRect(552, 0, 2, 2));
+ r1.OrWith(nsRect(362, 2, 222, 28));
+ r1.OrWith(nsRect(552, 30, 2, 2));
+ r1.OrWith(nsRect(0, 32, 1280, 41));
+ nsRegion r2(nsRect(512, 0, 146, 9));
+ r2.OrWith(nsRect(340, 9, 318, 16));
+ r2.OrWith(nsRect(512, 25, 146, 8));
+ nsRegion r3;
+ r3.Sub(r1, r2);
+ EXPECT_FALSE(r3.Contains(350, 15));
+ }
+ {
+ nsRegion r1(nsRect(0, 0, 1265, 1024));
+ nsRegion r2(nsRect(1265, 0, 15, 685));
+ r2.OrWith(nsRect(0, 714, 1280, 221));
+ nsRegion r3;
+ r3.Sub(r1, r2);
+ }
+ {
+ nsRegion r1(nsRect(6, 0, 64, 1));
+ r1.OrWith(nsRect(6, 1, 67, 1));
+ r1.OrWith(nsRect(6, 2, 67, 2));
+ r1.OrWith(nsRect(79, 2, 67, 2));
+ r1.OrWith(nsRect(6, 4, 67, 1));
+ r1.OrWith(nsRect(79, 4, 98, 1));
+ r1.OrWith(nsRect(6, 5, 171, 18));
+ r1.OrWith(nsRect(1, 23, 176, 3));
+ r1.OrWith(nsRect(1, 26, 178, 5));
+ r1.OrWith(nsRect(1, 31, 176, 9));
+ r1.OrWith(nsRect(0, 40, 177, 57));
+ r1.OrWith(nsRect(0, 97, 176, 33));
+ r1.OrWith(nsRect(0, 130, 12, 17));
+ r1.OrWith(nsRect(15, 130, 161, 17));
+ r1.OrWith(nsRect(0, 147, 12, 5));
+ r1.OrWith(nsRect(15, 147, 111, 5));
+ r1.OrWith(nsRect(0, 152, 12, 7));
+ r1.OrWith(nsRect(17, 152, 109, 7));
+ r1.OrWith(nsRect(0, 159, 12, 2));
+ r1.OrWith(nsRect(17, 159, 98, 2));
+ r1.OrWith(nsRect(17, 161, 98, 9));
+ r1.OrWith(nsRect(27, 170, 63, 21));
+ nsRegion r2(nsRect(9, 9, 37, 17));
+ r2.OrWith(nsRect(92, 9, 26, 17));
+ r2.OrWith(nsRect(9, 26, 37, 9));
+ r2.OrWith(nsRect(84, 26, 65, 9));
+ r2.OrWith(nsRect(9, 35, 37, 2));
+ r2.OrWith(nsRect(51, 35, 98, 2));
+ r2.OrWith(nsRect(51, 37, 98, 11));
+ r2.OrWith(nsRect(51, 48, 78, 4));
+ r2.OrWith(nsRect(87, 52, 42, 7));
+ r2.OrWith(nsRect(19, 59, 12, 5));
+ r2.OrWith(nsRect(87, 59, 42, 5));
+ r2.OrWith(nsRect(19, 64, 12, 9));
+ r2.OrWith(nsRect(32, 64, 97, 9));
+ r2.OrWith(nsRect(19, 73, 12, 2));
+ r2.OrWith(nsRect(32, 73, 104, 2));
+ r2.OrWith(nsRect(19, 75, 117, 5));
+ r2.OrWith(nsRect(18, 80, 118, 5));
+ r2.OrWith(nsRect(18, 85, 111, 38));
+ r2.OrWith(nsRect(87, 123, 42, 11));
+ nsRegion r3;
+ r3.Sub(r1, r2);
+ }
+ {
+ nsRegion r1(nsRect(27, 0, 39, 1));
+ r1.OrWith(nsRect(86, 0, 22, 1));
+ r1.OrWith(nsRect(27, 1, 43, 1));
+ r1.OrWith(nsRect(86, 1, 22, 1));
+ r1.OrWith(nsRect(27, 2, 43, 1));
+ r1.OrWith(nsRect(86, 2, 75, 1));
+ r1.OrWith(nsRect(12, 3, 58, 1));
+ r1.OrWith(nsRect(86, 3, 75, 1));
+ r1.OrWith(nsRect(12, 4, 149, 5));
+ r1.OrWith(nsRect(0, 9, 161, 9));
+ r1.OrWith(nsRect(0, 18, 167, 17));
+ r1.OrWith(nsRect(0, 35, 171, 5));
+ r1.OrWith(nsRect(0, 40, 189, 28));
+ r1.OrWith(nsRect(0, 68, 171, 16));
+ r1.OrWith(nsRect(4, 84, 167, 5));
+ r1.OrWith(nsRect(4, 89, 177, 9));
+ r1.OrWith(nsRect(1, 98, 180, 59));
+ r1.OrWith(nsRect(4, 157, 177, 1));
+ r1.OrWith(nsRect(4, 158, 139, 15));
+ r1.OrWith(nsRect(17, 173, 126, 2));
+ r1.OrWith(nsRect(20, 175, 123, 2));
+ r1.OrWith(nsRect(20, 177, 118, 6));
+ r1.OrWith(nsRect(20, 183, 84, 2));
+ nsRegion r2(nsRect(64, 2, 30, 6));
+ r2.OrWith(nsRect(26, 11, 41, 17));
+ r2.OrWith(nsRect(19, 28, 48, 23));
+ r2.OrWith(nsRect(19, 51, 76, 8));
+ r2.OrWith(nsRect(4, 59, 91, 31));
+ r2.OrWith(nsRect(19, 90, 76, 29));
+ r2.OrWith(nsRect(33, 119, 62, 25));
+ r2.OrWith(nsRect(33, 144, 4, 21));
+ nsRegion r3;
+ r3.Sub(r1, r2);
+ }
+#ifdef REGION_RANDOM_STRESS_TESTS
+ const uint32_t TestIterations = 100000;
+ const uint32_t RectsPerTest = 100;
+ const uint32_t SubRectsPerTest = 10;
+
+ nsRect rects[RectsPerTest];
+ nsRect subRects[SubRectsPerTest];
+
+ for (uint32_t i = 0; i < TestIterations; i++) {
+ nsRegion r;
+ for (uint32_t n = 0; n < RectsPerTest; n++) {
+ rects[n].SetRect(rand() % 100, rand() % 100, rand() % 99 + 1,
+ rand() % 99 + 1);
+ }
+ r.SetEmpty();
+ for (uint32_t n = 0; n < RectsPerTest; n++) {
+ r.OrWith(rects[n]);
+ }
+ for (uint32_t n = 0; n < RectsPerTest; n++) {
+ EXPECT_TRUE(r.Contains(rects[n]));
+ }
+ for (uint32_t n = 0; n < SubRectsPerTest; n++) {
+ subRects[n].SetRect(rand() % 100, rand() % 100, rand() % 99 + 1,
+ rand() % 99 + 1);
+ }
+ nsRegion r2;
+ for (uint32_t n = 0; n < SubRectsPerTest; n++) {
+ r2.OrWith(subRects[n]);
+ }
+ nsRegion r3;
+ r3.Sub(r, r2);
+
+ for (uint32_t n = 0; n < SubRectsPerTest; n++) {
+ EXPECT_FALSE(r3.Contains(subRects[n].x, subRects[n].y));
+ EXPECT_FALSE(r3.Contains(subRects[n].x, subRects[n].YMost() - 1));
+ EXPECT_FALSE(
+ r3.Contains(subRects[n].XMost() - 1, subRects[n].YMost() - 1));
+ EXPECT_FALSE(r3.Contains(subRects[n].XMost() - 1, subRects[n].Y()));
+ }
+ }
+ for (uint32_t i = 0; i < TestIterations; i++) {
+ nsRegion r(nsRect(-1, -1, 202, 202));
+ nsRegion r2;
+ for (uint32_t n = 0; n < SubRectsPerTest; n++) {
+ subRects[n].SetRect(rand() % 100, rand() % 100, rand() % 99 + 1,
+ rand() % 99 + 1);
+ r2.OrWith(subRects[n]);
+ }
+ nsRegion r3;
+ r3.Sub(r, r2);
+ for (uint32_t n = 0; n < SubRectsPerTest; n++) {
+ EXPECT_FALSE(r3.Contains(subRects[n].x, subRects[n].y));
+ EXPECT_FALSE(r3.Contains(subRects[n].x, subRects[n].YMost() - 1));
+ EXPECT_FALSE(
+ r3.Contains(subRects[n].XMost() - 1, subRects[n].YMost() - 1));
+ EXPECT_FALSE(r3.Contains(subRects[n].XMost() - 1, subRects[n].Y()));
+ }
+ EXPECT_TRUE(r3.Contains(-1, -1));
+ EXPECT_TRUE(r3.Contains(-1, 200));
+ EXPECT_TRUE(r3.Contains(200, -1));
+ EXPECT_TRUE(r3.Contains(200, 200));
+ }
+#endif
+}
+
+TEST(Gfx, RegionAndWith)
+{
+ {
+ nsRegion r(nsRect(20, 0, 20, 20));
+ r.OrWith(nsRect(0, 20, 40, 20));
+ r.AndWith(nsRect(0, 0, 5, 5));
+ EXPECT_FALSE(r.Contains(1, 1));
+ }
+ {
+ nsRegion r1(nsRect(512, 1792, 256, 256));
+ nsRegion r2(nsRect(17, 1860, 239, 35));
+ r2.OrWith(nsRect(17, 1895, 239, 7));
+ r2.OrWith(nsRect(768, 1895, 154, 7));
+ r2.OrWith(nsRect(17, 1902, 905, 483));
+ r1.AndWith(r2);
+ }
+#ifdef REGION_RANDOM_STRESS_TESTS
+ const uint32_t TestIterations = 100000;
+ const uint32_t RectsPerTest = 50;
+ const uint32_t pointsTested = 100;
+
+ {
+ nsRect rectsSet1[RectsPerTest];
+ nsRect rectsSet2[RectsPerTest];
+
+ for (uint32_t i = 0; i < TestIterations; i++) {
+ nsRegion r1;
+ nsRegion r2;
+ for (uint32_t n = 0; n < RectsPerTest; n++) {
+ rectsSet1[n].SetRect(rand() % 100, rand() % 100, rand() % 99 + 1,
+ rand() % 99 + 1);
+ rectsSet2[n].SetRect(rand() % 100, rand() % 100, rand() % 99 + 1,
+ rand() % 99 + 1);
+ r1.OrWith(rectsSet1[n]);
+ r2.OrWith(rectsSet1[n]);
+ }
+
+ nsRegion r3 = r1;
+ r3.AndWith(r2);
+
+ for (uint32_t n = 0; n < pointsTested; n++) {
+ nsPoint p(rand() % 200, rand() % 200);
+ if (r1.Contains(p.x, p.y) && r2.Contains(p.x, p.y)) {
+ EXPECT_TRUE(r3.Contains(p.x, p.y));
+ } else {
+ EXPECT_FALSE(r3.Contains(p.x, p.y));
+ }
+ }
+ }
+ }
+
+ {
+ nsRect rectsSet[RectsPerTest];
+ nsRect testRect;
+ for (uint32_t i = 0; i < TestIterations; i++) {
+ nsRegion r;
+ for (uint32_t n = 0; n < RectsPerTest; n++) {
+ rectsSet[n].SetRect(rand() % 100, rand() % 100, rand() % 99 + 1,
+ rand() % 99 + 1);
+ }
+ for (uint32_t n = 0; n < RectsPerTest; n++) {
+ r.OrWith(rectsSet[n]);
+ }
+ testRect.SetRect(rand() % 100, rand() % 100, rand() % 99 + 1,
+ rand() % 99 + 1);
+
+ nsRegion r2 = r;
+ r2.AndWith(testRect);
+
+ for (uint32_t n = 0; n < pointsTested; n++) {
+ nsPoint p(rand() % 200, rand() % 200);
+ if (r.Contains(p.x, p.y) && testRect.Contains(p.x, p.y)) {
+ EXPECT_TRUE(r2.Contains(p.x, p.y));
+ } else {
+ EXPECT_FALSE(r2.Contains(p.x, p.y));
+ }
+ }
+ }
+ }
+#endif
+}
+
+TEST(Gfx, RegionAnd)
+{
+ {
+ nsRegion r(nsRect(20, 0, 20, 20));
+ r.OrWith(nsRect(0, 20, 40, 20));
+ nsRegion r2;
+ r2.And(r, nsRect(0, 0, 5, 5));
+ EXPECT_FALSE(r.Contains(1, 1));
+ }
+ {
+ nsRegion r(nsRect(51, 2, 57, 5));
+ r.OrWith(nsRect(36, 7, 72, 4));
+ r.OrWith(nsRect(36, 11, 25, 1));
+ r.OrWith(nsRect(69, 12, 6, 4));
+ r.OrWith(nsRect(37, 16, 54, 2));
+ r.OrWith(nsRect(37, 18, 82, 2));
+ r.OrWith(nsRect(10, 20, 109, 3));
+ r.OrWith(nsRect(1, 23, 136, 21));
+ r.OrWith(nsRect(1, 44, 148, 2));
+ r.OrWith(nsRect(1, 46, 176, 31));
+ r.OrWith(nsRect(6, 77, 171, 1));
+ r.OrWith(nsRect(5, 78, 172, 30));
+ r.OrWith(nsRect(5, 108, 165, 45));
+ r.OrWith(nsRect(5, 153, 61, 5));
+ r.OrWith(nsRect(72, 153, 98, 5));
+ r.OrWith(nsRect(38, 158, 25, 4));
+ r.OrWith(nsRect(72, 158, 98, 4));
+ r.OrWith(nsRect(58, 162, 5, 8));
+ r.OrWith(nsRect(72, 162, 98, 8));
+ r.OrWith(nsRect(72, 170, 98, 5));
+ nsRegion r2;
+ r.And(r2, nsRect(18, 78, 53, 45));
+ }
+#ifdef REGION_RANDOM_STRESS_TESTS
+ const uint32_t TestIterations = 100000;
+ const uint32_t RectsPerTest = 50;
+ const uint32_t pointsTested = 100;
+
+ {
+ nsRect rectsSet1[RectsPerTest];
+ nsRect rectsSet2[RectsPerTest];
+
+ for (uint32_t i = 0; i < TestIterations; i++) {
+ nsRegion r1;
+ nsRegion r2;
+ for (uint32_t n = 0; n < RectsPerTest; n++) {
+ rectsSet1[n].SetRect(rand() % 100, rand() % 100, rand() % 99 + 1,
+ rand() % 99 + 1);
+ rectsSet2[n].SetRect(rand() % 100, rand() % 100, rand() % 99 + 1,
+ rand() % 99 + 1);
+ r1.OrWith(rectsSet1[n]);
+ r2.OrWith(rectsSet1[n]);
+ }
+
+ nsRegion r3;
+ r3.And(r1, r2);
+
+ for (uint32_t n = 0; n < pointsTested; n++) {
+ nsPoint p(rand() % 200, rand() % 200);
+ if (r1.Contains(p.x, p.y) && r2.Contains(p.x, p.y)) {
+ EXPECT_TRUE(r3.Contains(p.x, p.y));
+ } else {
+ EXPECT_FALSE(r3.Contains(p.x, p.y));
+ }
+ }
+ }
+ }
+
+ {
+ nsRect rectsSet[RectsPerTest];
+ nsRect testRect;
+ for (uint32_t i = 0; i < TestIterations; i++) {
+ nsRegion r;
+ for (uint32_t n = 0; n < RectsPerTest; n++) {
+ rectsSet[n].SetRect(rand() % 100, rand() % 100, rand() % 99 + 1,
+ rand() % 99 + 1);
+ }
+ for (uint32_t n = 0; n < RectsPerTest; n++) {
+ r.OrWith(rectsSet[n]);
+ }
+ testRect.SetRect(rand() % 100, rand() % 100, rand() % 99 + 1,
+ rand() % 99 + 1);
+
+ nsRegion r2;
+ r2.And(r, testRect);
+
+ for (uint32_t n = 0; n < pointsTested; n++) {
+ nsPoint p(rand() % 200, rand() % 200);
+ if (r.Contains(p.x, p.y) && testRect.Contains(p.x, p.y)) {
+ EXPECT_TRUE(r2.Contains(p.x, p.y));
+ } else {
+ EXPECT_FALSE(r2.Contains(p.x, p.y));
+ }
+ }
+ }
+ }
+#endif
+}
+
+TEST(Gfx, RegionSimplify)
+{
+ { // ensure simplify works on a single rect
+ nsRegion r(nsRect(0, 100, 200, 100));
+
+ r.SimplifyOutwardByArea(100 * 100);
+
+ nsRegion result(nsRect(0, 100, 200, 100));
+
+ EXPECT_TRUE(r.IsEqual(result)) << "regions not the same";
+ }
+
+ { // the rectangles will be merged
+ nsRegion r(nsRect(0, 100, 200, 100));
+ r.Or(r, nsRect(0, 200, 300, 200));
+
+ r.SimplifyOutwardByArea(100 * 100);
+
+ nsRegion result(nsRect(0, 100, 300, 300));
+
+ EXPECT_TRUE(r.IsEqual(result)) << "regions not merged";
+ }
+
+ { // two rectangle on the first span
+ // one on the second
+ nsRegion r(nsRect(0, 100, 200, 100));
+ r.Or(r, nsRect(0, 200, 300, 200));
+ r.Or(r, nsRect(250, 100, 50, 100));
+
+ EXPECT_TRUE(r.GetNumRects() == 3) << "wrong number of rects";
+
+ r.SimplifyOutwardByArea(100 * 100);
+
+ nsRegion result(nsRect(0, 100, 300, 300));
+
+ EXPECT_TRUE(r.IsEqual(result)) << "regions not merged";
+ }
+
+ { // the rectangles will be merged
+ nsRegion r(nsRect(0, 100, 200, 100));
+ r.Or(r, nsRect(0, 200, 300, 200));
+ r.Or(r, nsRect(250, 100, 50, 100));
+ r.Sub(r, nsRect(200, 200, 40, 200));
+
+ EXPECT_TRUE(r.GetNumRects() == 4) << "wrong number of rects";
+
+ r.SimplifyOutwardByArea(100 * 100);
+
+ nsRegion result(nsRect(0, 100, 300, 300));
+ result.Sub(result, nsRect(200, 100, 40, 300));
+
+ EXPECT_TRUE(r.IsEqual(result)) << "regions not merged";
+ }
+
+ { // three spans of rectangles
+ nsRegion r(nsRect(0, 100, 200, 100));
+ r.Or(r, nsRect(0, 200, 300, 200));
+ r.Or(r, nsRect(250, 100, 50, 50));
+ r.Sub(r, nsRect(200, 200, 40, 200));
+
+ r.SimplifyOutwardByArea(100 * 100);
+
+ nsRegion result(nsRect(0, 100, 300, 300));
+ result.Sub(result, nsRect(200, 100, 40, 300));
+
+ EXPECT_TRUE(r.IsEqual(result)) << "regions not merged";
+ }
+
+ { // three spans of rectangles and an unmerged rectangle
+ nsRegion r(nsRect(0, 100, 200, 100));
+ r.Or(r, nsRect(0, 200, 300, 200));
+ r.Or(r, nsRect(250, 100, 50, 50));
+ r.Sub(r, nsRect(200, 200, 40, 200));
+ r.Or(r, nsRect(250, 900, 150, 50));
+
+ r.SimplifyOutwardByArea(100 * 100);
+
+ nsRegion result(nsRect(0, 100, 300, 300));
+ result.Sub(result, nsRect(200, 100, 40, 300));
+ result.Or(result, nsRect(250, 900, 150, 50));
+
+ EXPECT_TRUE(r.IsEqual(result)) << "regions not merged";
+ }
+
+ { // unmerged regions
+ nsRegion r(nsRect(0, 100, 200, 100));
+ r.Or(r, nsRect(0, 200, 300, 200));
+
+ r.SimplifyOutwardByArea(100);
+
+ nsRegion result(nsRect(0, 100, 200, 100));
+ result.Or(result, nsRect(0, 200, 300, 200));
+
+ EXPECT_TRUE(r.IsEqual(result)) << "regions not merged";
+ }
+
+ { // empty region
+ // just make sure this doesn't crash.
+ nsRegion r;
+ r.SimplifyOutwardByArea(100);
+ }
+}
+
+TEST(Gfx, RegionContains)
+{
+ { // ensure Contains works on a simple region
+ nsRegion r(nsRect(0, 0, 100, 100));
+
+ EXPECT_TRUE(r.Contains(0, 0));
+ EXPECT_TRUE(r.Contains(0, 99));
+ EXPECT_TRUE(r.Contains(99, 0));
+ EXPECT_TRUE(r.Contains(99, 99));
+
+ EXPECT_FALSE(r.Contains(-1, 50));
+ EXPECT_FALSE(r.Contains(100, 50));
+ EXPECT_FALSE(r.Contains(50, -1));
+ EXPECT_FALSE(r.Contains(50, 100));
+
+ EXPECT_TRUE(r.Contains(nsRect(0, 0, 100, 100)));
+ EXPECT_TRUE(r.Contains(nsRect(99, 99, 1, 1)));
+
+ EXPECT_FALSE(r.Contains(nsRect(100, 100, 1, 1)));
+ EXPECT_FALSE(r.Contains(nsRect(100, 100, 0, 0)));
+ }
+
+ { // empty regions contain nothing
+ nsRegion r(nsRect(100, 100, 0, 0));
+
+ EXPECT_FALSE(r.Contains(0, 0));
+ EXPECT_FALSE(r.Contains(100, 100));
+ EXPECT_FALSE(r.Contains(nsRect(100, 100, 0, 0)));
+ EXPECT_FALSE(r.Contains(nsRect(100, 100, 1, 1)));
+ }
+
+ { // complex region contain tests
+ // The region looks like this, with two squares that overlap.
+ // (hard to do accurately with ASCII art)
+ // +------+
+ // | |
+ // | +--+
+ // | |
+ // +--+ |
+ // | |
+ // +------+
+ nsRegion r(nsRect(0, 0, 100, 100));
+ r.OrWith(nsRect(50, 50, 100, 100));
+
+ EXPECT_TRUE(r.Contains(0, 0));
+ EXPECT_TRUE(r.Contains(99, 99));
+ EXPECT_TRUE(r.Contains(50, 100));
+ EXPECT_TRUE(r.Contains(100, 50));
+ EXPECT_TRUE(r.Contains(149, 149));
+
+ EXPECT_FALSE(r.Contains(49, 100));
+ EXPECT_FALSE(r.Contains(100, 49));
+ EXPECT_FALSE(r.Contains(150, 150));
+
+ EXPECT_TRUE(r.Contains(nsRect(100, 100, 1, 1)));
+ EXPECT_FALSE(r.Contains(nsRect(49, 99, 2, 2)));
+ }
+
+ { // region with a hole
+ nsRegion r(nsRect(0, 0, 100, 100));
+ r.SubOut(nsRect(40, 40, 10, 10));
+
+ EXPECT_TRUE(r.Contains(0, 0));
+ EXPECT_TRUE(r.Contains(39, 39));
+ EXPECT_FALSE(r.Contains(40, 40));
+ EXPECT_FALSE(r.Contains(49, 49));
+ EXPECT_TRUE(r.Contains(50, 50));
+
+ EXPECT_FALSE(r.Contains(nsRect(40, 40, 10, 10)));
+ EXPECT_FALSE(r.Contains(nsRect(39, 39, 2, 2)));
+ }
+}
+
+#define DILATE_VALUE 0x88
+#define REGION_VALUE 0xff
+
+struct RegionBitmap {
+ RegionBitmap(unsigned char* bitmap, int width, int height)
+ : bitmap(bitmap), width(width), height(height) {}
+
+ void clear() {
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+ bitmap[x + y * width] = 0;
+ }
+ }
+ }
+
+ void set(nsRegion& region) {
+ clear();
+ for (auto iter = region.RectIter(); !iter.Done(); iter.Next()) {
+ const nsRect& r = iter.Get();
+ for (int y = r.Y(); y < r.YMost(); y++) {
+ for (int x = r.X(); x < r.XMost(); x++) {
+ bitmap[x + y * width] = REGION_VALUE;
+ }
+ }
+ }
+ }
+
+ void dilate() {
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+ if (bitmap[x + y * width] == REGION_VALUE) {
+ for (int yn = std::max(y - 1, 0); yn <= std::min(y + 1, height - 1);
+ yn++) {
+ for (int xn = std::max(x - 1, 0); xn <= std::min(x + 1, width - 1);
+ xn++) {
+ if (bitmap[xn + yn * width] == 0)
+ bitmap[xn + yn * width] = DILATE_VALUE;
+ }
+ }
+ }
+ }
+ }
+ }
+ void compare(RegionBitmap& reference) {
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+ EXPECT_EQ(bitmap[x + y * width], reference.bitmap[x + y * width]);
+ }
+ }
+ }
+
+ unsigned char* bitmap;
+ int width;
+ int height;
+};
+
+static void VisitEdge(void* closure, VisitSide side, int x1, int y1, int x2,
+ int y2) {
+ EXPECT_GE(x2, x1);
+ RegionBitmap* visitor = static_cast<RegionBitmap*>(closure);
+ unsigned char* bitmap = visitor->bitmap;
+ const int width = visitor->width;
+
+ if (side == VisitSide::TOP) {
+ while (x1 != x2) {
+ bitmap[x1 + (y1 - 1) * width] = DILATE_VALUE;
+ x1++;
+ }
+ } else if (side == VisitSide::BOTTOM) {
+ while (x1 != x2) {
+ bitmap[x1 + y1 * width] = DILATE_VALUE;
+ x1++;
+ }
+ } else if (side == VisitSide::LEFT) {
+ while (y1 != y2) {
+ bitmap[x1 - 1 + y1 * width] = DILATE_VALUE;
+ y1++;
+ }
+ } else if (side == VisitSide::RIGHT) {
+ while (y1 != y2) {
+ bitmap[x1 + y1 * width] = DILATE_VALUE;
+ y1++;
+ }
+ }
+}
+
+static void TestVisit(nsRegion& r) {
+ auto reference = mozilla::MakeUnique<unsigned char[]>(600 * 600);
+ auto result = mozilla::MakeUnique<unsigned char[]>(600 * 600);
+ RegionBitmap ref(reference.get(), 600, 600);
+ RegionBitmap res(result.get(), 600, 600);
+
+ ref.set(r);
+ ref.dilate();
+
+ res.set(r);
+ r.VisitEdges(VisitEdge, &res);
+ res.compare(ref);
+}
+
+TEST(Gfx, RegionVisitEdges)
+{
+ { // visit edges
+ nsRegion r(nsRect(20, 20, 100, 100));
+ r.Or(r, nsRect(20, 120, 200, 100));
+ TestVisit(r);
+ }
+
+ { // two rects side by side - 1 pixel inbetween
+ nsRegion r(nsRect(20, 20, 100, 100));
+ r.Or(r, nsRect(121, 20, 100, 100));
+ TestVisit(r);
+ }
+
+ { // two rects side by side - 2 pixels inbetween
+ nsRegion r(nsRect(20, 20, 100, 100));
+ r.Or(r, nsRect(122, 20, 100, 100));
+ TestVisit(r);
+ }
+
+ {
+ // only corner of the rects are touching
+ nsRegion r(nsRect(20, 20, 100, 100));
+ r.Or(r, nsRect(120, 120, 100, 100));
+
+ TestVisit(r);
+ }
+
+ {
+ // corners are 1 pixel away
+ nsRegion r(nsRect(20, 20, 100, 100));
+ r.Or(r, nsRect(121, 120, 100, 100));
+
+ TestVisit(r);
+ }
+
+ {
+ // vertically separated
+ nsRegion r(nsRect(20, 20, 100, 100));
+ r.Or(r, nsRect(120, 125, 100, 100));
+
+ TestVisit(r);
+ }
+
+ {
+ // not touching
+ nsRegion r(nsRect(20, 20, 100, 100));
+ r.Or(r, nsRect(130, 120, 100, 100));
+ r.Or(r, nsRect(240, 20, 100, 100));
+
+ TestVisit(r);
+ }
+
+ { // rect with a hole in it
+ nsRegion r(nsRect(20, 20, 100, 100));
+ r.Sub(r, nsRect(40, 40, 10, 10));
+
+ TestVisit(r);
+ }
+ {
+ // left overs
+ nsRegion r(nsRect(20, 20, 10, 10));
+ r.Or(r, nsRect(50, 20, 10, 10));
+ r.Or(r, nsRect(90, 20, 10, 10));
+ r.Or(r, nsRect(24, 30, 10, 10));
+ r.Or(r, nsRect(20, 40, 15, 10));
+ r.Or(r, nsRect(50, 40, 15, 10));
+ r.Or(r, nsRect(90, 40, 15, 10));
+
+ TestVisit(r);
+ }
+
+ {
+ // vertically separated
+ nsRegion r(nsRect(20, 20, 100, 100));
+ r.Or(r, nsRect(120, 125, 100, 100));
+
+ TestVisit(r);
+ }
+
+ {
+ // two upper rects followed by a lower one
+ // on the same line
+ nsRegion r(nsRect(5, 5, 50, 50));
+ r.Or(r, nsRect(100, 5, 50, 50));
+ r.Or(r, nsRect(200, 50, 50, 50));
+
+ TestVisit(r);
+ }
+
+ {
+ // bug 1130978.
+ nsRegion r(nsRect(4, 1, 61, 49));
+ r.Or(r, nsRect(115, 1, 99, 49));
+ r.Or(r, nsRect(115, 49, 99, 1));
+ r.Or(r, nsRect(12, 50, 11, 5));
+ r.Or(r, nsRect(25, 50, 28, 5));
+ r.Or(r, nsRect(115, 50, 99, 5));
+ r.Or(r, nsRect(115, 55, 99, 12));
+
+ TestVisit(r);
+ }
+}
+
+// The TiledRegion tests use nsIntRect / IntRegion because nsRect doesn't have
+// InflateToMultiple which is required by TiledRegion.
+TEST(Gfx, TiledRegionNoSimplification2Rects)
+{
+ // Add two rectangles, both rectangles are completely inside
+ // different tiles.
+ nsIntRegion region;
+ region.OrWith(nsIntRect(50, 50, 50, 50));
+ region.OrWith(nsIntRect(300, 50, 50, 50));
+
+ TiledIntRegion tiledRegion;
+ tiledRegion.Add(nsIntRect(50, 50, 50, 50));
+ tiledRegion.Add(nsIntRect(300, 50, 50, 50));
+
+ // No simplification should have happened.
+ EXPECT_TRUE(region.IsEqual(tiledRegion.GetRegion()));
+}
+
+TEST(Gfx, TiledRegionNoSimplification1Region)
+{
+ // Add two rectangles, both rectangles are completely inside
+ // different tiles.
+ nsIntRegion region;
+ region.OrWith(nsIntRect(50, 50, 50, 50));
+ region.OrWith(nsIntRect(300, 50, 50, 50));
+
+ TiledIntRegion tiledRegion;
+ tiledRegion.Add(region);
+
+ // No simplification should have happened.
+ EXPECT_TRUE(region.IsEqual(tiledRegion.GetRegion()));
+}
+
+TEST(Gfx, TiledRegionWithSimplification3Rects)
+{
+ // Add three rectangles. The first two rectangles are completely inside
+ // different tiles, but the third rectangle intersects both tiles.
+ TiledIntRegion tiledRegion;
+ tiledRegion.Add(nsIntRect(50, 50, 50, 50));
+ tiledRegion.Add(nsIntRect(300, 50, 50, 50));
+ tiledRegion.Add(nsIntRect(250, 70, 10, 10));
+
+ // Both tiles should have simplified their rectangles, and those two
+ // rectangles are adjacent to each other, so they just build up one rect.
+ EXPECT_TRUE(tiledRegion.GetRegion().IsEqual(nsIntRect(50, 50, 300, 50)));
+}
+
+TEST(Gfx, TiledRegionWithSimplification1Region)
+{
+ // Add three rectangles. The first two rectangles are completely inside
+ // different tiles, but the third rectangle intersects both tiles.
+ nsIntRegion region;
+ region.OrWith(nsIntRect(50, 50, 50, 50));
+ region.OrWith(nsIntRect(300, 50, 50, 50));
+ region.OrWith(nsIntRect(250, 70, 10, 10));
+
+ TiledIntRegion tiledRegion;
+ tiledRegion.Add(region);
+
+ // Both tiles should have simplified their rectangles, and those two
+ // rectangles are adjacent to each other, so they just build up one rect.
+ EXPECT_TRUE(tiledRegion.GetRegion().IsEqual(nsIntRect(50, 50, 300, 50)));
+}
+
+TEST(Gfx, TiledRegionContains)
+{
+ // Add three rectangles. The first two rectangles are completely inside
+ // different tiles, but the third rectangle intersects both tiles.
+ TiledIntRegion tiledRegion;
+ tiledRegion.Add(nsIntRect(50, 50, 50, 50));
+ tiledRegion.Add(nsIntRect(300, 50, 50, 50));
+ tiledRegion.Add(nsIntRect(250, 70, 10, 10));
+
+ // Both tiles should have simplified their rectangles, and those two
+ // rectangles are adjacent to each other, so they just build up one rect.
+ EXPECT_TRUE(tiledRegion.Contains(nsIntRect(50, 50, 300, 50)));
+ EXPECT_TRUE(tiledRegion.Contains(nsIntRect(50, 50, 50, 50)));
+ EXPECT_FALSE(tiledRegion.Contains(nsIntRect(50, 50, 301, 50)));
+}
+
+TEST(Gfx, TiledRegionIntersects)
+{
+ // Add three rectangles. The first two rectangles are completely inside
+ // different tiles, but the third rectangle intersects both tiles.
+ TiledIntRegion tiledRegion;
+ tiledRegion.Add(nsIntRect(50, 50, 50, 50));
+ tiledRegion.Add(nsIntRect(300, 50, 50, 50));
+ tiledRegion.Add(nsIntRect(250, 70, 10, 10));
+
+ // Both tiles should have simplified their rectangles, and those two
+ // rectangles are adjacent to each other, so they just build up one rect.
+ EXPECT_TRUE(tiledRegion.Intersects(nsIntRect(50, 50, 300, 50)));
+ EXPECT_TRUE(tiledRegion.Intersects(nsIntRect(200, 10, 10, 50)));
+ EXPECT_TRUE(tiledRegion.Intersects(nsIntRect(50, 50, 301, 50)));
+ EXPECT_FALSE(tiledRegion.Intersects(nsIntRect(0, 0, 50, 500)));
+}
+
+TEST(Gfx, TiledRegionBoundaryConditions1)
+{
+ TiledIntRegion tiledRegion;
+ // This one works fine
+ tiledRegion.Add(nsIntRegion(nsIntRect(INT_MIN, INT_MIN, 1, 1)));
+ EXPECT_TRUE(tiledRegion.Contains(nsIntRect(INT_MIN, INT_MIN, 1, 1)));
+
+ // This causes the tiledRegion.mBounds to overflow, so it is ignored
+ tiledRegion.Add(nsIntRegion(nsIntRect(INT_MAX - 1, INT_MAX - 1, 1, 1)));
+
+ // Verify that the tiledRegion contains only things we expect
+ EXPECT_TRUE(tiledRegion.Contains(nsIntRect(INT_MIN, INT_MIN, 1, 1)));
+ EXPECT_FALSE(tiledRegion.Contains(nsIntRect(INT_MAX - 1, INT_MAX - 1, 1, 1)));
+ EXPECT_FALSE(tiledRegion.Contains(nsIntRect(0, 0, 1, 1)));
+}
+
+TEST(Gfx, TiledRegionBoundaryConditions2)
+{
+ TiledIntRegion tiledRegion;
+ // This one works fine
+ tiledRegion.Add(nsIntRegion(nsIntRect(INT_MAX - 1, INT_MIN, 1, 1)));
+ EXPECT_TRUE(tiledRegion.Contains(nsIntRect(INT_MAX - 1, INT_MIN, 1, 1)));
+
+ // As with TiledRegionBoundaryConditions1, this overflows, so it is ignored
+ tiledRegion.Add(nsIntRegion(nsIntRect(INT_MIN, INT_MAX - 1, 1, 1)));
+ EXPECT_TRUE(tiledRegion.Contains(nsIntRect(INT_MAX - 1, INT_MIN, 1, 1)));
+ EXPECT_FALSE(tiledRegion.Contains(nsIntRect(INT_MIN, INT_MAX - 1, 1, 1)));
+ EXPECT_FALSE(tiledRegion.Contains(nsIntRect(0, 0, 1, 1)));
+}
+
+TEST(Gfx, TiledRegionBigRects)
+{
+ TiledIntRegion tiledRegion;
+ // Super wide region, forces simplification into bounds mode
+ tiledRegion.Add(nsIntRegion(nsIntRect(INT_MIN, INT_MIN, INT_MAX, 100)));
+ EXPECT_TRUE(tiledRegion.Contains(nsIntRect(INT_MIN, INT_MIN, 1, 1)));
+ EXPECT_TRUE(tiledRegion.Contains(nsIntRect(-2, INT_MIN + 99, 1, 1)));
+ EXPECT_FALSE(tiledRegion.Contains(nsIntRect(-2, INT_MIN + 100, 1, 1)));
+ EXPECT_FALSE(tiledRegion.Contains(nsIntRect(-1, INT_MIN + 99, 1, 1)));
+
+ // Add another rect, verify that simplification caused the entire bounds
+ // to expand by a lot more.
+ tiledRegion.Add(nsIntRegion(nsIntRect(INT_MIN, INT_MIN + 200, 1, 1)));
+ EXPECT_TRUE(tiledRegion.Contains(nsIntRect(-2, INT_MIN + 100, 1, 1)));
+ EXPECT_TRUE(tiledRegion.Contains(nsIntRect(-2, INT_MIN + 200, 1, 1)));
+ EXPECT_FALSE(tiledRegion.Contains(nsIntRect(-2, INT_MIN + 201, 1, 1)));
+}
+
+TEST(Gfx, TiledRegionBoundaryOverflow)
+{
+ TiledIntRegion tiledRegion;
+ tiledRegion.Add(nsIntRegion(nsIntRect(100, 100, 1, 1)));
+ EXPECT_TRUE(tiledRegion.Contains(nsIntRect(100, 100, 1, 1)));
+
+ // The next region is invalid, so it gets ignored
+ tiledRegion.Add(nsIntRegion(nsIntRect(INT_MAX, INT_MAX, 1, 1)));
+ EXPECT_FALSE(tiledRegion.Contains(nsIntRect(INT_MAX, INT_MAX, 1, 1)));
+
+ // Try that again as a rect, it will also get ignored
+ tiledRegion.Add(nsIntRect(INT_MAX, INT_MAX, 1, 1));
+ EXPECT_FALSE(tiledRegion.Contains(nsIntRect(INT_MAX, INT_MAX, 1, 1)));
+
+ // Try with a bigger overflowing rect
+ tiledRegion.Add(nsIntRect(INT_MAX, INT_MAX, 500, 500));
+ EXPECT_FALSE(tiledRegion.Contains(nsIntRect(INT_MIN, INT_MIN, 10, 10)));
+ EXPECT_FALSE(tiledRegion.Contains(nsIntRect(INT_MAX, INT_MAX, 100, 100)));
+
+ EXPECT_FALSE(tiledRegion.Contains(nsIntRect(0, 0, 1, 1)));
+}
+
+TEST(Gfx, TiledRegionNegativeRect)
+{
+ TiledIntRegion tiledRegion;
+ // The next region is invalid, so it gets ignored
+ tiledRegion.Add(nsIntRegion(nsIntRect(0, 0, -500, -500)));
+ EXPECT_FALSE(tiledRegion.Contains(nsIntRect(-50, -50, 1, 1)));
+ // Rects with negative widths/heights are treated as empty and ignored
+ tiledRegion.Add(nsIntRect(0, 0, -500, -500));
+ EXPECT_FALSE(tiledRegion.Contains(nsIntRect(-1, -1, 1, 1)));
+ EXPECT_FALSE(tiledRegion.Contains(nsIntRect(0, 0, 1, 1)));
+ // Empty rects are always contained
+ EXPECT_TRUE(tiledRegion.Contains(nsIntRect(0, 0, -1, -1)));
+ EXPECT_TRUE(tiledRegion.Contains(nsIntRect(100, 100, -1, -1)));
+}
diff --git a/gfx/tests/gtest/TestSkipChars.cpp b/gfx/tests/gtest/TestSkipChars.cpp
new file mode 100644
index 0000000000..8f31777619
--- /dev/null
+++ b/gfx/tests/gtest/TestSkipChars.cpp
@@ -0,0 +1,156 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gtest/gtest.h"
+
+#include "gfxSkipChars.h"
+#include "mozilla/ArrayUtils.h"
+
+static bool TestConstructor() {
+ gfxSkipChars skipChars;
+
+ EXPECT_TRUE(skipChars.GetOriginalCharCount() == 0)
+ << "[1] Make sure the gfxSkipChars was properly initialized with "
+ "constructor";
+
+ return true;
+}
+
+static bool TestLength() {
+ gfxSkipChars skipChars;
+
+ skipChars.KeepChars(100);
+
+ EXPECT_TRUE(skipChars.GetOriginalCharCount() == 100)
+ << "[1] Check length after keeping chars";
+
+ skipChars.SkipChars(50);
+
+ EXPECT_TRUE(skipChars.GetOriginalCharCount() == 150)
+ << "[2] Check length after skipping chars";
+
+ skipChars.SkipChars(50);
+
+ EXPECT_TRUE(skipChars.GetOriginalCharCount() == 200)
+ << "[3] Check length after skipping more chars";
+
+ skipChars.KeepChar();
+
+ EXPECT_TRUE(skipChars.GetOriginalCharCount() == 201)
+ << "[4] Check length after keeping a final char";
+
+ return true;
+}
+
+static bool TestIterator() {
+ // Test a gfxSkipChars that starts with kept chars
+ gfxSkipChars skipChars1;
+
+ skipChars1.KeepChars(9);
+ skipChars1.SkipChar();
+ skipChars1.KeepChars(9);
+ skipChars1.SkipChar();
+ skipChars1.KeepChars(9);
+
+ EXPECT_TRUE(skipChars1.GetOriginalCharCount() == 29) << "[1] Check length";
+
+ gfxSkipCharsIterator iter1(skipChars1);
+
+ EXPECT_TRUE(iter1.GetOriginalOffset() == 0)
+ << "[2] Check initial original offset";
+ EXPECT_TRUE(iter1.GetSkippedOffset() == 0)
+ << "[3] Check initial skipped offset";
+
+ EXPECT_TRUE(iter1.IsOriginalCharSkipped() == false)
+ << "[3a] Check IsOriginalCharSkipped for initial position";
+
+ uint32_t expectSkipped1[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
+ 18, 19, 20, 21, 22, 23, 24, 25, 26, 27};
+
+ for (uint32_t i = 0; i < mozilla::ArrayLength(expectSkipped1); i++) {
+ EXPECT_TRUE(iter1.ConvertOriginalToSkipped(i) == expectSkipped1[i])
+ << "[4] Check mapping of original to skipped for " << i;
+ }
+
+ int32_t expectOriginal1[] = {0, 1, 2, 3, 4, 5, 6, 7, 8,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18,
+ 20, 21, 22, 23, 24, 25, 26, 27, 28};
+
+ for (uint32_t i = 0; i < mozilla::ArrayLength(expectOriginal1); i++) {
+ EXPECT_TRUE(iter1.ConvertSkippedToOriginal(i) == expectOriginal1[i])
+ << "[5] Check mapping of skipped to original for " << i;
+ }
+
+ bool expectIsOriginalSkipped1[] = {
+ false, false, false, false, false, false, false, false, false, true,
+ false, false, false, false, false, false, false, false, false, true,
+ false, false, false, false, false, false, false, false, false};
+
+ for (uint32_t i = 0; i < mozilla::ArrayLength(expectIsOriginalSkipped1);
+ i++) {
+ iter1.SetOriginalOffset(i);
+ EXPECT_TRUE(iter1.IsOriginalCharSkipped() == expectIsOriginalSkipped1[i])
+ << "[5.a] Check IsOriginalCharSkipped for " << i;
+ }
+
+ // Test a gfxSkipChars that starts with skipped chars
+ gfxSkipChars skipChars2;
+
+ skipChars2.SkipChars(9);
+ skipChars2.KeepChar();
+ skipChars2.SkipChars(9);
+ skipChars2.KeepChar();
+ skipChars2.SkipChars(9);
+
+ EXPECT_TRUE(skipChars2.GetOriginalCharCount() == 29) << "[6] Check length";
+
+ gfxSkipCharsIterator iter2(skipChars2);
+
+ EXPECT_TRUE(iter2.GetOriginalOffset() == 0)
+ << "[7] Check initial original offset";
+ EXPECT_TRUE(iter2.GetSkippedOffset() == 0)
+ << "[8] Check initial skipped offset";
+
+ EXPECT_TRUE(iter2.IsOriginalCharSkipped() == true)
+ << "[8a] Check IsOriginalCharSkipped for initial position";
+
+ uint32_t expectSkipped2[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2};
+
+ for (uint32_t i = 0; i < mozilla::ArrayLength(expectSkipped2); i++) {
+ EXPECT_TRUE(iter2.ConvertOriginalToSkipped(i) == expectSkipped2[i])
+ << "[9] Check mapping of original to skipped for " << i;
+ }
+
+ int32_t expectOriginal2[] = {9, 19, 29};
+
+ for (uint32_t i = 0; i < mozilla::ArrayLength(expectOriginal2); i++) {
+ EXPECT_TRUE(iter2.ConvertSkippedToOriginal(i) == expectOriginal2[i])
+ << "[10] Check mapping of skipped to original for " << i;
+ }
+
+ bool expectIsOriginalSkipped2[] = {
+ true, true, true, true, true, true, true, true, true, false,
+ true, true, true, true, true, true, true, true, true, false,
+ true, true, true, true, true, true, true, true, true};
+
+ for (uint32_t i = 0; i < mozilla::ArrayLength(expectIsOriginalSkipped2);
+ i++) {
+ iter2.SetOriginalOffset(i);
+ EXPECT_TRUE(iter2.IsOriginalCharSkipped() == expectIsOriginalSkipped2[i])
+ << "[10.a] Check IsOriginalCharSkipped for " << i;
+ }
+
+ return true;
+}
+
+TEST(Gfx, gfxSkipChars)
+{
+ TestConstructor();
+ TestLength();
+ TestIterator();
+}
diff --git a/gfx/tests/gtest/TestSwizzle.cpp b/gfx/tests/gtest/TestSwizzle.cpp
new file mode 100644
index 0000000000..cc91840ebf
--- /dev/null
+++ b/gfx/tests/gtest/TestSwizzle.cpp
@@ -0,0 +1,325 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gtest/gtest.h"
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/gfx/Swizzle.h"
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+
+TEST(Moz2D, PremultiplyData)
+{
+ const uint8_t in_bgra[5 * 4] = {
+ 255, 255, 0, 255, // verify 255 alpha leaves RGB unchanged
+ 0, 0, 255, 255,
+ 0, 255, 255, 0, // verify 0 alpha zeroes out RGB
+ 0, 0, 0, 0,
+ 255, 0, 0, 128, // verify that 255 RGB maps to alpha
+ };
+ uint8_t out[5 * 4];
+ const uint8_t check_bgra[5 * 4] = {
+ 255, 255, 0, 255, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 128,
+ };
+ // check swizzled output
+ const uint8_t check_rgba[5 * 4] = {
+ 0, 255, 255, 255, 255, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128,
+ };
+ const uint8_t check_argb[5 * 4] = {
+ 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 128,
+ };
+
+ PremultiplyData(in_bgra, sizeof(in_bgra), SurfaceFormat::B8G8R8A8, out,
+ sizeof(in_bgra), SurfaceFormat::B8G8R8A8, IntSize(5, 1));
+ EXPECT_TRUE(ArrayEqual(out, check_bgra));
+
+ PremultiplyData(in_bgra, sizeof(in_bgra), SurfaceFormat::B8G8R8A8, out,
+ sizeof(in_bgra), SurfaceFormat::R8G8B8A8, IntSize(5, 1));
+ EXPECT_TRUE(ArrayEqual(out, check_rgba));
+
+ PremultiplyData(in_bgra, sizeof(in_bgra), SurfaceFormat::B8G8R8A8, out,
+ sizeof(in_bgra), SurfaceFormat::A8R8G8B8, IntSize(5, 1));
+ EXPECT_TRUE(ArrayEqual(out, check_argb));
+}
+
+TEST(Moz2D, PremultiplyRow)
+{
+ const uint8_t in_bgra[5 * 4] = {
+ 255, 255, 0, 255, // verify 255 alpha leaves RGB unchanged
+ 0, 0, 255, 255,
+ 0, 255, 255, 0, // verify 0 alpha zeroes out RGB
+ 0, 0, 0, 0,
+ 255, 0, 0, 128, // verify that 255 RGB maps to alpha
+ };
+ uint8_t out[5 * 4];
+ const uint8_t check_bgra[5 * 4] = {
+ 255, 255, 0, 255, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 128,
+ };
+ // check swizzled output
+ const uint8_t check_rgba[5 * 4] = {
+ 0, 255, 255, 255, 255, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128,
+ };
+ const uint8_t check_argb[5 * 4] = {
+ 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 128,
+ };
+
+ SwizzleRowFn func =
+ PremultiplyRow(SurfaceFormat::B8G8R8A8, SurfaceFormat::B8G8R8A8);
+ func(in_bgra, out, 5);
+ EXPECT_TRUE(ArrayEqual(out, check_bgra));
+
+ func = PremultiplyRow(SurfaceFormat::B8G8R8A8, SurfaceFormat::R8G8B8A8);
+ func(in_bgra, out, 5);
+ EXPECT_TRUE(ArrayEqual(out, check_rgba));
+
+ func = PremultiplyRow(SurfaceFormat::B8G8R8A8, SurfaceFormat::A8R8G8B8);
+ func(in_bgra, out, 5);
+ EXPECT_TRUE(ArrayEqual(out, check_argb));
+}
+
+TEST(Moz2D, UnpremultiplyData)
+{
+ const uint8_t in_bgra[5 * 4] = {
+ 255, 255, 0, 255, // verify 255 alpha leaves RGB unchanged
+ 0, 0, 255, 255, 0, 0, 0, 0, // verify 0 alpha leaves RGB at 0
+ 0, 0, 0, 64, // verify 0 RGB stays 0 with non-zero alpha
+ 128, 0, 0, 128, // verify that RGB == alpha maps to 255
+
+ };
+ uint8_t out[5 * 4];
+ const uint8_t check_bgra[5 * 4] = {
+ 255, 255, 0, 255, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 64, 255, 0, 0, 128,
+ };
+ // check swizzled output
+ const uint8_t check_rgba[5 * 4] = {
+ 0, 255, 255, 255, 255, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 255, 128,
+ };
+ const uint8_t check_argb[5 * 4] = {
+ 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 128, 0, 0, 255,
+ };
+
+ UnpremultiplyData(in_bgra, sizeof(in_bgra), SurfaceFormat::B8G8R8A8, out,
+ sizeof(in_bgra), SurfaceFormat::B8G8R8A8, IntSize(5, 1));
+ EXPECT_TRUE(ArrayEqual(out, check_bgra));
+
+ UnpremultiplyData(in_bgra, sizeof(in_bgra), SurfaceFormat::B8G8R8A8, out,
+ sizeof(in_bgra), SurfaceFormat::R8G8B8A8, IntSize(5, 1));
+ EXPECT_TRUE(ArrayEqual(out, check_rgba));
+
+ UnpremultiplyData(in_bgra, sizeof(in_bgra), SurfaceFormat::B8G8R8A8, out,
+ sizeof(in_bgra), SurfaceFormat::A8R8G8B8, IntSize(5, 1));
+ EXPECT_TRUE(ArrayEqual(out, check_argb));
+}
+
+TEST(Moz2D, UnpremultiplyRow)
+{
+ const uint8_t in_bgra[5 * 4] = {
+ 255, 255, 0, 255, // verify 255 alpha leaves RGB unchanged
+ 0, 0, 255, 255, 0, 0, 0, 0, // verify 0 alpha leaves RGB at 0
+ 0, 0, 0, 64, // verify 0 RGB stays 0 with non-zero alpha
+ 128, 0, 0, 128, // verify that RGB == alpha maps to 255
+
+ };
+ uint8_t out[5 * 4];
+ const uint8_t check_bgra[5 * 4] = {
+ 255, 255, 0, 255, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 64, 255, 0, 0, 128,
+ };
+ // check swizzled output
+ const uint8_t check_rgba[5 * 4] = {
+ 0, 255, 255, 255, 255, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 255, 128,
+ };
+ const uint8_t check_argb[5 * 4] = {
+ 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 128, 0, 0, 255,
+ };
+
+ SwizzleRowFn func =
+ UnpremultiplyRow(SurfaceFormat::B8G8R8A8, SurfaceFormat::B8G8R8A8);
+ func(in_bgra, out, 5);
+ EXPECT_TRUE(ArrayEqual(out, check_bgra));
+
+ func = UnpremultiplyRow(SurfaceFormat::B8G8R8A8, SurfaceFormat::R8G8B8A8);
+ func(in_bgra, out, 5);
+ EXPECT_TRUE(ArrayEqual(out, check_rgba));
+
+ func = UnpremultiplyRow(SurfaceFormat::B8G8R8A8, SurfaceFormat::A8R8G8B8);
+ func(in_bgra, out, 5);
+ EXPECT_TRUE(ArrayEqual(out, check_argb));
+}
+
+TEST(Moz2D, SwizzleData)
+{
+ const uint8_t in_bgra[5 * 4] = {
+ 253, 254, 0, 255, 0, 0, 255, 255, 0, 0, 0, 0, 1, 2, 3, 64, 127, 0, 9, 128,
+
+ };
+ uint8_t out[5 * 4];
+ // check copy
+ const uint8_t check_bgra[5 * 4] = {
+ 253, 254, 0, 255, 0, 0, 255, 255, 0, 0, 0, 0, 1, 2, 3, 64, 127, 0, 9, 128,
+ };
+ // check swaps
+ const uint8_t check_rgba[5 * 4] = {
+ 0, 254, 253, 255, 255, 0, 0, 255, 0, 0, 0, 0, 3, 2, 1, 64, 9, 0, 127, 128,
+ };
+ const uint8_t check_argb[5 * 4] = {
+ 255, 0, 254, 253, 255, 255, 0, 0, 0, 0, 0, 0, 64, 3, 2, 1, 128, 9, 0, 127,
+ };
+ // check opaquifying
+ const uint8_t check_rgbx[5 * 4] = {
+ 0, 254, 253, 255, 255, 0, 0, 255, 0, 0,
+ 0, 255, 3, 2, 1, 255, 9, 0, 127, 255,
+ };
+ // check packing
+ uint8_t out24[5 * 3];
+ const uint8_t check_bgr[5 * 3] = {253, 254, 0, 0, 0, 255, 0, 0,
+ 0, 1, 2, 3, 127, 0, 9};
+ const uint8_t check_rgb[5 * 3] = {
+ 0, 254, 253, 255, 0, 0, 0, 0, 0, 3, 2, 1, 9, 0, 127,
+ };
+ uint8_t out8[5];
+ const uint8_t check_a[5] = {255, 255, 0, 64, 128};
+ uint16_t out16[5];
+#define PACK_RGB565(b, g, r) \
+ (((b & 0xF8) >> 3) | ((g & 0xFC) << 3) | ((r & 0xF8) << 8))
+ const uint16_t check_16[5] = {
+ PACK_RGB565(253, 254, 0), PACK_RGB565(0, 0, 255), PACK_RGB565(0, 0, 0),
+ PACK_RGB565(1, 2, 3), PACK_RGB565(127, 0, 9),
+ };
+
+ SwizzleData(in_bgra, sizeof(in_bgra), SurfaceFormat::B8G8R8A8, out,
+ sizeof(out), SurfaceFormat::B8G8R8A8, IntSize(5, 1));
+ EXPECT_TRUE(ArrayEqual(out, check_bgra));
+
+ SwizzleData(in_bgra, sizeof(in_bgra), SurfaceFormat::B8G8R8A8, out,
+ sizeof(out), SurfaceFormat::R8G8B8A8, IntSize(5, 1));
+ EXPECT_TRUE(ArrayEqual(out, check_rgba));
+
+ SwizzleData(in_bgra, sizeof(in_bgra), SurfaceFormat::B8G8R8A8, out,
+ sizeof(out), SurfaceFormat::A8R8G8B8, IntSize(5, 1));
+ EXPECT_TRUE(ArrayEqual(out, check_argb));
+
+ SwizzleData(in_bgra, sizeof(in_bgra), SurfaceFormat::B8G8R8A8, out,
+ sizeof(out), SurfaceFormat::R8G8B8X8, IntSize(5, 1));
+ EXPECT_TRUE(ArrayEqual(out, check_rgbx));
+
+ SwizzleData(in_bgra, sizeof(in_bgra), SurfaceFormat::B8G8R8A8, out24,
+ sizeof(out24), SurfaceFormat::B8G8R8, IntSize(5, 1));
+ EXPECT_TRUE(ArrayEqual(out24, check_bgr));
+
+ SwizzleData(in_bgra, sizeof(in_bgra), SurfaceFormat::B8G8R8A8, out24,
+ sizeof(out24), SurfaceFormat::R8G8B8, IntSize(5, 1));
+ EXPECT_TRUE(ArrayEqual(out24, check_rgb));
+
+ SwizzleData(in_bgra, sizeof(in_bgra), SurfaceFormat::B8G8R8A8, out8,
+ sizeof(out8), SurfaceFormat::A8, IntSize(5, 1));
+ EXPECT_TRUE(ArrayEqual(out8, check_a));
+
+ SwizzleData(SurfaceFormat::A8R8G8B8_UINT32 == SurfaceFormat::A8R8G8B8
+ ? check_argb
+ : check_bgra,
+ sizeof(in_bgra), SurfaceFormat::A8R8G8B8_UINT32,
+ reinterpret_cast<uint8_t*>(out16), sizeof(out16),
+ SurfaceFormat::R5G6B5_UINT16, IntSize(5, 1));
+ EXPECT_TRUE(ArrayEqual(out16, check_16));
+}
+
+TEST(Moz2D, SwizzleRow)
+{
+ const uint8_t in_bgra[5 * 4] = {
+ 253, 254, 0, 255, 0, 0, 255, 255, 0, 0, 0, 0, 1, 2, 3, 64, 127, 0, 9, 128,
+
+ };
+ uint8_t out[5 * 4];
+ // check swaps
+ const uint8_t check_rgba[5 * 4] = {
+ 0, 254, 253, 255, 255, 0, 0, 255, 0, 0, 0, 0, 3, 2, 1, 64, 9, 0, 127, 128,
+ };
+ // check opaquifying
+ const uint8_t check_rgbx[5 * 4] = {
+ 0, 254, 253, 255, 255, 0, 0, 255, 0, 0,
+ 0, 255, 3, 2, 1, 255, 9, 0, 127, 255,
+ };
+ // check packing
+ uint8_t out24[5 * 3];
+ const uint8_t check_bgr[5 * 3] = {253, 254, 0, 0, 0, 255, 0, 0,
+ 0, 1, 2, 3, 127, 0, 9};
+ const uint8_t check_rgb[5 * 3] = {
+ 0, 254, 253, 255, 0, 0, 0, 0, 0, 3, 2, 1, 9, 0, 127,
+ };
+ // check unpacking
+ uint8_t out_unpack[16 * 4];
+ const uint8_t in_rgb[16 * 3] = {
+ 0, 254, 253, 255, 0, 0, 0, 0, 0, 3, 2, 1, 9, 0, 127, 4,
+ 5, 6, 9, 8, 7, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
+ 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
+ };
+ const uint8_t check_unpack_rgbx[16 * 4] = {
+ 0, 254, 253, 255, 255, 0, 0, 255, 0, 0, 0, 255, 3, 2, 1, 255,
+ 9, 0, 127, 255, 4, 5, 6, 255, 9, 8, 7, 255, 10, 11, 12, 255,
+ 13, 14, 15, 255, 16, 17, 18, 255, 19, 20, 21, 255, 22, 23, 24, 255,
+ 25, 26, 27, 255, 28, 29, 30, 255, 31, 32, 33, 255, 34, 35, 36, 255,
+ };
+ const uint8_t check_unpack_bgrx[16 * 4] = {
+ 253, 254, 0, 255, 0, 0, 255, 255, 0, 0, 0, 255, 1, 2, 3, 255,
+ 127, 0, 9, 255, 6, 5, 4, 255, 7, 8, 9, 255, 12, 11, 10, 255,
+ 15, 14, 13, 255, 18, 17, 16, 255, 21, 20, 19, 255, 24, 23, 22, 255,
+ 27, 26, 25, 255, 30, 29, 28, 255, 33, 32, 31, 255, 36, 35, 34, 255,
+ };
+ const uint8_t check_unpack_xrgb[16 * 4] = {
+ 255, 0, 254, 253, 255, 255, 0, 0, 255, 0, 0, 0, 255, 3, 2, 1,
+ 255, 9, 0, 127, 255, 4, 5, 6, 255, 9, 8, 7, 255, 10, 11, 12,
+ 255, 13, 14, 15, 255, 16, 17, 18, 255, 19, 20, 21, 255, 22, 23, 24,
+ 255, 25, 26, 27, 255, 28, 29, 30, 255, 31, 32, 33, 255, 34, 35, 36,
+ };
+
+ SwizzleRowFn func =
+ SwizzleRow(SurfaceFormat::B8G8R8A8, SurfaceFormat::R8G8B8A8);
+ func(in_bgra, out, 5);
+ EXPECT_TRUE(ArrayEqual(out, check_rgba));
+
+ func = SwizzleRow(SurfaceFormat::B8G8R8A8, SurfaceFormat::R8G8B8X8);
+ func(in_bgra, out, 5);
+ EXPECT_TRUE(ArrayEqual(out, check_rgbx));
+
+ func = SwizzleRow(SurfaceFormat::B8G8R8A8, SurfaceFormat::B8G8R8A8);
+ func(in_bgra, out, 5);
+ EXPECT_TRUE(ArrayEqual(out, in_bgra));
+
+ func = SwizzleRow(SurfaceFormat::B8G8R8A8, SurfaceFormat::B8G8R8);
+ func(in_bgra, out24, 5);
+ EXPECT_TRUE(ArrayEqual(out24, check_bgr));
+
+ func = SwizzleRow(SurfaceFormat::B8G8R8A8, SurfaceFormat::R8G8B8);
+ func(in_bgra, out24, 5);
+ EXPECT_TRUE(ArrayEqual(out24, check_rgb));
+
+ func = SwizzleRow(SurfaceFormat::R8G8B8, SurfaceFormat::B8G8R8X8);
+ func(in_rgb, out_unpack, 16);
+ EXPECT_TRUE(ArrayEqual(out_unpack, check_unpack_bgrx));
+
+ memset(out_unpack, 0xE5, sizeof(out_unpack));
+ memcpy(out_unpack, in_rgb, sizeof(in_rgb));
+ func(out_unpack, out_unpack, 16);
+ EXPECT_TRUE(ArrayEqual(out_unpack, check_unpack_bgrx));
+
+ func = SwizzleRow(SurfaceFormat::R8G8B8, SurfaceFormat::R8G8B8X8);
+ func(in_rgb, out_unpack, 16);
+ EXPECT_TRUE(ArrayEqual(out_unpack, check_unpack_rgbx));
+
+ memset(out_unpack, 0xE5, sizeof(out_unpack));
+ memcpy(out_unpack, in_rgb, sizeof(in_rgb));
+ func(out_unpack, out_unpack, 16);
+ EXPECT_TRUE(ArrayEqual(out_unpack, check_unpack_rgbx));
+
+ func = SwizzleRow(SurfaceFormat::R8G8B8, SurfaceFormat::X8R8G8B8);
+ func(in_rgb, out_unpack, 16);
+ EXPECT_TRUE(ArrayEqual(out_unpack, check_unpack_xrgb));
+
+ memset(out_unpack, 0xE5, sizeof(out_unpack));
+ memcpy(out_unpack, in_rgb, sizeof(in_rgb));
+ func(out_unpack, out_unpack, 16);
+ EXPECT_TRUE(ArrayEqual(out_unpack, check_unpack_xrgb));
+}
diff --git a/gfx/tests/gtest/TestTextureCompatibility.cpp b/gfx/tests/gtest/TestTextureCompatibility.cpp
new file mode 100644
index 0000000000..2e5236614d
--- /dev/null
+++ b/gfx/tests/gtest/TestTextureCompatibility.cpp
@@ -0,0 +1,125 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gfxConfig.h"
+#include "gfxPlatform.h"
+#include "gtest/gtest.h"
+#include "MockWidget.h"
+#include "mozilla/layers/BasicCompositor.h"
+#include "mozilla/layers/Compositor.h"
+#include "mozilla/layers/LayersTypes.h"
+#include "mozilla/layers/TextureClient.h"
+#include "mozilla/layers/TextureHost.h"
+#include "mozilla/RefPtr.h"
+#include "TestLayers.h"
+#include "TextureHelper.h"
+
+using mozilla::gfx::Feature;
+using mozilla::gfx::gfxConfig;
+using mozilla::layers::BasicCompositor;
+using mozilla::layers::Compositor;
+using mozilla::layers::CompositorOptions;
+using mozilla::layers::ISurfaceAllocator;
+using mozilla::layers::LayersBackend;
+using mozilla::layers::TestSurfaceAllocator;
+using mozilla::layers::TextureClient;
+using mozilla::layers::TextureHost;
+using mozilla::widget::CompositorWidget;
+using mozilla::widget::InProcessCompositorWidget;
+
+/**
+ * This function will create the possible TextureClient and TextureHost pairs
+ * according to the given backend.
+ */
+static void CreateTextureWithBackend(
+ LayersBackend& aLayersBackend, ISurfaceAllocator* aDeallocator,
+ nsTArray<RefPtr<TextureClient>>& aTextureClients,
+ nsTArray<RefPtr<TextureHost>>& aTextureHosts) {
+ aTextureClients.AppendElement(CreateTextureClientWithBackend(aLayersBackend));
+
+ aTextureClients.AppendElement(
+ CreateYCbCrTextureClientWithBackend(aLayersBackend));
+
+ for (uint32_t i = 0; i < aTextureClients.Length(); i++) {
+ aTextureHosts.AppendElement(CreateTextureHostWithBackend(
+ aTextureClients[i], aDeallocator, aLayersBackend));
+ }
+}
+
+/**
+ * This will return the default list of backends that units test should run
+ * against.
+ */
+static void GetPlatformBackends(nsTArray<LayersBackend>& aBackends) {
+ gfxPlatform* platform = gfxPlatform::GetPlatform();
+ MOZ_ASSERT(platform);
+
+ platform->GetCompositorBackends(gfxConfig::IsEnabled(Feature::HW_COMPOSITING),
+ aBackends);
+
+ if (aBackends.IsEmpty()) {
+ aBackends.AppendElement(LayersBackend::LAYERS_BASIC);
+ }
+}
+
+/**
+ * This function will return a BasicCompositor to caller.
+ */
+static already_AddRefed<Compositor> CreateBasicCompositor() {
+ RefPtr<Compositor> compositor;
+ // Init the platform.
+ if (gfxPlatform::GetPlatform()) {
+ RefPtr<MockWidget> widget = new MockWidget(256, 256);
+ CompositorOptions options;
+ RefPtr<CompositorWidget> proxy =
+ new InProcessCompositorWidget(options, widget);
+ compositor = new BasicCompositor(nullptr, proxy);
+ }
+ return compositor.forget();
+}
+
+/**
+ * This function checks if the textures react correctly when setting them to
+ * BasicCompositor.
+ */
+static void CheckCompatibilityWithBasicCompositor(
+ LayersBackend aBackends, nsTArray<RefPtr<TextureHost>>& aTextures) {
+ RefPtr<Compositor> compositor = CreateBasicCompositor();
+ for (uint32_t i = 0; i < aTextures.Length(); i++) {
+ if (!aTextures[i]) {
+ continue;
+ }
+ aTextures[i]->SetTextureSourceProvider(compositor);
+
+ // The lock function will fail if the texture is not compatible with
+ // BasicCompositor.
+ bool lockResult = aTextures[i]->Lock();
+ if (aBackends != LayersBackend::LAYERS_BASIC) {
+ EXPECT_FALSE(lockResult);
+ } else {
+ EXPECT_TRUE(lockResult);
+ }
+ if (lockResult) {
+ aTextures[i]->Unlock();
+ }
+ }
+}
+
+TEST(Gfx, TestTextureCompatibility)
+{
+ nsTArray<LayersBackend> backendHints;
+ RefPtr<TestSurfaceAllocator> deallocator = new TestSurfaceAllocator();
+
+ GetPlatformBackends(backendHints);
+ for (uint32_t i = 0; i < backendHints.Length(); i++) {
+ nsTArray<RefPtr<TextureClient>> textureClients;
+ nsTArray<RefPtr<TextureHost>> textureHosts;
+
+ CreateTextureWithBackend(backendHints[i], deallocator, textureClients,
+ textureHosts);
+ CheckCompatibilityWithBasicCompositor(backendHints[i], textureHosts);
+ }
+}
diff --git a/gfx/tests/gtest/TestTextures.cpp b/gfx/tests/gtest/TestTextures.cpp
new file mode 100644
index 0000000000..4d01ed4c9e
--- /dev/null
+++ b/gfx/tests/gtest/TestTextures.cpp
@@ -0,0 +1,307 @@
+/* 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 "TestLayers.h"
+
+#include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/Tools.h"
+#include "mozilla/layers/BufferTexture.h"
+#include "mozilla/layers/ImageBridgeChild.h" // for ImageBridgeChild
+#include "mozilla/layers/TextureClient.h"
+#include "mozilla/layers/TextureHost.h"
+#include "mozilla/RefPtr.h"
+#include "gfx2DGlue.h"
+#include "gfxImageSurface.h"
+#include "gfxTypes.h"
+#include "ImageContainer.h"
+#include "mozilla/layers/ImageDataSerializer.h"
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+using namespace mozilla::layers;
+
+/*
+ * This test performs the following actions:
+ * - creates a surface
+ * - initialize a texture client with it
+ * - serilaizes the texture client
+ * - deserializes the data into a texture host
+ * - reads the surface from the texture host.
+ *
+ * The surface in the end should be equal to the inital one.
+ * This test is run for different combinations of texture types and
+ * image formats.
+ */
+
+namespace mozilla {
+namespace layers {
+
+// fills the surface with values betwee 0 and 100.
+static void SetupSurface(gfxImageSurface* surface) {
+ int bpp = gfxASurface::BytePerPixelFromFormat(surface->Format());
+ int stride = surface->Stride();
+ uint8_t val = 0;
+ uint8_t* data = surface->Data();
+ for (int y = 0; y < surface->Height(); ++y) {
+ for (int x = 0; x < surface->Height(); ++x) {
+ for (int b = 0; b < bpp; ++b) {
+ data[y * stride + x * bpp + b] = val;
+ if (val == 100) {
+ val = 0;
+ } else {
+ ++val;
+ }
+ }
+ }
+ }
+}
+
+// return true if two surfaces contain the same data
+static void AssertSurfacesEqual(gfxImageSurface* surface1,
+ gfxImageSurface* surface2) {
+ ASSERT_EQ(surface1->GetSize(), surface2->GetSize());
+ ASSERT_EQ(surface1->Format(), surface2->Format());
+
+ uint8_t* data1 = surface1->Data();
+ uint8_t* data2 = surface2->Data();
+ int stride1 = surface1->Stride();
+ int stride2 = surface2->Stride();
+ int bpp = gfxASurface::BytePerPixelFromFormat(surface1->Format());
+
+ for (int y = 0; y < surface1->Height(); ++y) {
+ for (int x = 0; x < surface1->Width(); ++x) {
+ for (int b = 0; b < bpp; ++b) {
+ ASSERT_EQ(data1[y * stride1 + x * bpp + b],
+ data2[y * stride2 + x * bpp + b]);
+ }
+ }
+ }
+}
+
+static void AssertSurfacesEqual(SourceSurface* surface1,
+ SourceSurface* surface2) {
+ ASSERT_EQ(surface1->GetSize(), surface2->GetSize());
+ ASSERT_EQ(surface1->GetFormat(), surface2->GetFormat());
+
+ RefPtr<DataSourceSurface> dataSurface1 = surface1->GetDataSurface();
+ RefPtr<DataSourceSurface> dataSurface2 = surface2->GetDataSurface();
+ DataSourceSurface::MappedSurface map1;
+ DataSourceSurface::MappedSurface map2;
+ if (!dataSurface1->Map(DataSourceSurface::READ, &map1)) {
+ return;
+ }
+ if (!dataSurface2->Map(DataSourceSurface::READ, &map2)) {
+ dataSurface1->Unmap();
+ return;
+ }
+ uint8_t* data1 = map1.mData;
+ uint8_t* data2 = map2.mData;
+ int stride1 = map1.mStride;
+ int stride2 = map2.mStride;
+ int bpp = BytesPerPixel(surface1->GetFormat());
+ int width = surface1->GetSize().width;
+ int height = surface1->GetSize().height;
+
+ for (int y = 0; y < height; ++y) {
+ for (int x = 0; x < width; ++x) {
+ for (int b = 0; b < bpp; ++b) {
+ ASSERT_EQ(data1[y * stride1 + x * bpp + b],
+ data2[y * stride2 + x * bpp + b]);
+ }
+ }
+ }
+
+ dataSurface1->Unmap();
+ dataSurface2->Unmap();
+}
+
+// Run the test for a texture client and a surface
+void TestTextureClientSurface(TextureClient* texture,
+ gfxImageSurface* surface) {
+ // client allocation
+ ASSERT_TRUE(texture->CanExposeDrawTarget());
+
+ ASSERT_TRUE(texture->Lock(OpenMode::OPEN_READ_WRITE));
+ // client painting
+ RefPtr<DrawTarget> dt = texture->BorrowDrawTarget();
+ RefPtr<SourceSurface> source =
+ gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(dt, surface);
+ dt->CopySurface(source, IntRect(IntPoint(), source->GetSize()), IntPoint());
+
+ RefPtr<SourceSurface> snapshot = dt->Snapshot();
+
+ AssertSurfacesEqual(snapshot, source);
+
+ dt = nullptr; // drop reference before calling Unlock()
+ texture->Unlock();
+
+ // client serialization
+ SurfaceDescriptor descriptor;
+ ASSERT_TRUE(texture->ToSurfaceDescriptor(descriptor));
+
+ ASSERT_NE(descriptor.type(), SurfaceDescriptor::Tnull_t);
+
+ // host deserialization
+ RefPtr<TestSurfaceAllocator> deallocator = new TestSurfaceAllocator();
+ RefPtr<TextureHost> host = CreateBackendIndependentTextureHost(
+ descriptor, deallocator, LayersBackend::LAYERS_NONE, texture->GetFlags());
+
+ ASSERT_TRUE(host.get() != nullptr);
+ ASSERT_EQ(host->GetFlags(), texture->GetFlags());
+
+ // host read
+
+ // XXX - this can fail because lock tries to upload the texture but it needs a
+ // Compositor to do that. We could add a DummyComposior for testing but I am
+ // not sure it'll be worth it. Maybe always test against a BasicCompositor,
+ // but the latter needs a widget...
+ if (host->Lock()) {
+ RefPtr<mozilla::gfx::DataSourceSurface> hostDataSurface =
+ host->GetAsSurface();
+
+ DataSourceSurface::ScopedMap map(hostDataSurface, DataSourceSurface::READ);
+ RefPtr<gfxImageSurface> hostSurface = new gfxImageSurface(
+ map.GetData(), hostDataSurface->GetSize(), map.GetStride(),
+ SurfaceFormatToImageFormat(hostDataSurface->GetFormat()));
+ AssertSurfacesEqual(surface, hostSurface.get());
+ host->Unlock();
+ }
+}
+
+// Same as above, for YCbCr surfaces
+void TestTextureClientYCbCr(TextureClient* client, PlanarYCbCrData& ycbcrData) {
+ client->Lock(OpenMode::OPEN_READ_WRITE);
+ UpdateYCbCrTextureClient(client, ycbcrData);
+ client->Unlock();
+
+ // client serialization
+ SurfaceDescriptor descriptor;
+ ASSERT_TRUE(client->ToSurfaceDescriptor(descriptor));
+
+ ASSERT_EQ(descriptor.type(), SurfaceDescriptor::TSurfaceDescriptorBuffer);
+ auto bufferDesc = descriptor.get_SurfaceDescriptorBuffer();
+ ASSERT_EQ(bufferDesc.desc().type(), BufferDescriptor::TYCbCrDescriptor);
+ auto ycbcrDesc = bufferDesc.desc().get_YCbCrDescriptor();
+ ASSERT_EQ(ycbcrDesc.ySize(), ycbcrData.mYSize);
+ ASSERT_EQ(ycbcrDesc.cbCrSize(), ycbcrData.mCbCrSize);
+ ASSERT_EQ(ycbcrDesc.stereoMode(), ycbcrData.mStereoMode);
+
+ // host deserialization
+ RefPtr<TestSurfaceAllocator> deallocator = new TestSurfaceAllocator();
+ RefPtr<TextureHost> textureHost = CreateBackendIndependentTextureHost(
+ descriptor, deallocator, LayersBackend::LAYERS_NONE, client->GetFlags());
+
+ RefPtr<BufferTextureHost> host =
+ static_cast<BufferTextureHost*>(textureHost.get());
+
+ ASSERT_TRUE(host.get() != nullptr);
+ ASSERT_EQ(host->GetFlags(), client->GetFlags());
+
+ // host read
+
+ if (host->Lock()) {
+ // This will work iff the compositor is not BasicCompositor
+ ASSERT_EQ(host->GetFormat(), mozilla::gfx::SurfaceFormat::YUV);
+ host->Unlock();
+ }
+}
+
+} // namespace layers
+} // namespace mozilla
+
+TEST(Layers, TextureSerialization)
+{
+ // the test is run on all the following image formats
+ gfxImageFormat formats[3] = {
+ SurfaceFormat::A8R8G8B8_UINT32,
+ SurfaceFormat::X8R8G8B8_UINT32,
+ SurfaceFormat::A8,
+ };
+
+ for (int f = 0; f < 3; ++f) {
+ RefPtr<gfxImageSurface> surface =
+ new gfxImageSurface(IntSize(400, 300), formats[f]);
+ SetupSurface(surface.get());
+ AssertSurfacesEqual(surface, surface);
+
+ auto texData = BufferTextureData::Create(
+ surface->GetSize(), gfx::ImageFormatToSurfaceFormat(surface->Format()),
+ gfx::BackendType::CAIRO, LayersBackend::LAYERS_NONE,
+ TextureFlags::DEALLOCATE_CLIENT, ALLOC_DEFAULT, nullptr);
+ ASSERT_TRUE(!!texData);
+
+ RefPtr<TextureClient> client =
+ new TextureClient(texData, TextureFlags::DEALLOCATE_CLIENT, nullptr);
+
+ TestTextureClientSurface(client, surface);
+
+ // XXX - Test more texture client types.
+ }
+}
+
+TEST(Layers, TextureYCbCrSerialization)
+{
+ RefPtr<gfxImageSurface> ySurface =
+ new gfxImageSurface(IntSize(400, 300), SurfaceFormat::A8);
+ RefPtr<gfxImageSurface> cbSurface =
+ new gfxImageSurface(IntSize(200, 150), SurfaceFormat::A8);
+ RefPtr<gfxImageSurface> crSurface =
+ new gfxImageSurface(IntSize(200, 150), SurfaceFormat::A8);
+ SetupSurface(ySurface.get());
+ SetupSurface(cbSurface.get());
+ SetupSurface(crSurface.get());
+
+ PlanarYCbCrData clientData;
+ clientData.mYChannel = ySurface->Data();
+ clientData.mCbChannel = cbSurface->Data();
+ clientData.mCrChannel = crSurface->Data();
+ clientData.mYSize = ySurface->GetSize();
+ clientData.mPicSize = ySurface->GetSize();
+ clientData.mCbCrSize = cbSurface->GetSize();
+ clientData.mYStride = ySurface->Stride();
+ clientData.mCbCrStride = cbSurface->Stride();
+ clientData.mStereoMode = StereoMode::MONO;
+ clientData.mYUVColorSpace = YUVColorSpace::BT601;
+ clientData.mColorDepth = ColorDepth::COLOR_8;
+ clientData.mYSkip = 0;
+ clientData.mCbSkip = 0;
+ clientData.mCrSkip = 0;
+ clientData.mCrSkip = 0;
+ clientData.mPicX = 0;
+ clientData.mPicX = 0;
+
+ uint32_t namespaceId = 1;
+ ImageBridgeChild::InitSameProcess(namespaceId);
+
+ RefPtr<ImageBridgeChild> imageBridge = ImageBridgeChild::GetSingleton();
+ static int retry = 5;
+ while (!imageBridge->IPCOpen() && retry) {
+ // IPDL connection takes time especially in slow testing environment, like
+ // VM machines. Here we added retry mechanism to wait for IPDL connnection.
+#ifdef XP_WIN
+ Sleep(1);
+#else
+ sleep(1);
+#endif
+ retry--;
+ }
+
+ // Skip this testing if IPDL connection is not ready
+ if (!retry && !imageBridge->IPCOpen()) {
+ return;
+ }
+
+ RefPtr<TextureClient> client = TextureClient::CreateForYCbCr(
+ imageBridge, clientData.GetPictureRect(), clientData.mYSize,
+ clientData.mYStride, clientData.mCbCrSize, clientData.mCbCrStride,
+ StereoMode::MONO, ColorDepth::COLOR_8, YUVColorSpace::BT601,
+ ColorRange::LIMITED, TextureFlags::DEALLOCATE_CLIENT);
+
+ TestTextureClientYCbCr(client, clientData);
+
+ // XXX - Test more texture client types.
+}
diff --git a/gfx/tests/gtest/TestTreeTraversal.cpp b/gfx/tests/gtest/TestTreeTraversal.cpp
new file mode 100644
index 0000000000..215baeebf4
--- /dev/null
+++ b/gfx/tests/gtest/TestTreeTraversal.cpp
@@ -0,0 +1,1413 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <vector>
+#include "mozilla/RefPtr.h"
+#include "gtest/gtest.h"
+#include "nsRegion.h"
+#include "nsRect.h"
+#include "TreeTraversal.h"
+#include <stack>
+#include <queue>
+
+const int PERFORMANCE_TREE_DEPTH = 20;
+const int PERFORMANCE_TREE_CHILD_COUNT = 2;
+const int PERFORMANCE_TREE_LEAF_COUNT = 1048576; // 2 ** 20
+const int PERFORMANCE_REGION_XWRAP = 1024;
+
+using namespace mozilla::layers;
+using namespace mozilla;
+
+enum class SearchNodeType { Needle, Hay };
+enum class ForEachNodeType { Continue, Skip };
+
+template <class T>
+class TestNodeBase {
+ public:
+ NS_INLINE_DECL_REFCOUNTING(TestNodeBase<T>);
+ explicit TestNodeBase(T aType, int aExpectedTraversalRank = -1);
+ explicit TestNodeBase();
+ void SetActualTraversalRank(int aRank);
+ void SetValue(int aValue);
+ void SetType(T aType);
+ void SetRegion(nsRegion aRegion);
+ int GetExpectedTraversalRank();
+ int GetActualTraversalRank();
+ int GetValue();
+ T GetType();
+ nsRegion GetRegion();
+ virtual bool IsLeaf() = 0;
+
+ private:
+ MOZ_INIT_OUTSIDE_CTOR int mExpectedTraversalRank;
+ MOZ_INIT_OUTSIDE_CTOR int mActualTraversalRank;
+ MOZ_INIT_OUTSIDE_CTOR int mValue;
+ MOZ_INIT_OUTSIDE_CTOR nsRegion mRegion;
+ MOZ_INIT_OUTSIDE_CTOR T mType;
+
+ protected:
+ virtual ~TestNodeBase<T>() = default;
+};
+
+template <class T>
+class TestNodeReverse : public TestNodeBase<T> {
+ public:
+ explicit TestNodeReverse(T aType, int aExpectedTraversalRank = -1);
+ explicit TestNodeReverse();
+ void AddChild(RefPtr<TestNodeReverse<T>> aNode);
+ TestNodeReverse<T>* GetLastChild();
+ TestNodeReverse<T>* GetPrevSibling();
+ bool IsLeaf();
+
+ private:
+ void SetPrevSibling(RefPtr<TestNodeReverse<T>> aNode);
+ void SetLastChild(RefPtr<TestNodeReverse<T>> aNode);
+ RefPtr<TestNodeReverse<T>> mSiblingNode;
+ RefPtr<TestNodeReverse<T>> mLastChildNode;
+ ~TestNodeReverse<T>() = default;
+};
+
+template <class T>
+class TestNodeForward : public TestNodeBase<T> {
+ public:
+ explicit TestNodeForward(T aType, int aExpectedTraversalRank = -1);
+ explicit TestNodeForward();
+ void AddChild(RefPtr<TestNodeForward<T>> aNode);
+ TestNodeForward<T>* GetFirstChild();
+ TestNodeForward<T>* GetNextSibling();
+ bool IsLeaf();
+
+ private:
+ void SetNextSibling(RefPtr<TestNodeForward<T>> aNode);
+ void SetLastChild(RefPtr<TestNodeForward<T>> aNode);
+ void SetFirstChild(RefPtr<TestNodeForward<T>> aNode);
+ RefPtr<TestNodeForward<T>> mSiblingNode = nullptr;
+ RefPtr<TestNodeForward<T>> mFirstChildNode = nullptr;
+ // Track last child to facilitate appending children
+ RefPtr<TestNodeForward<T>> mLastChildNode = nullptr;
+ ~TestNodeForward<T>() = default;
+};
+
+template <class T>
+TestNodeReverse<T>::TestNodeReverse(T aType, int aExpectedTraversalRank)
+ : TestNodeBase<T>(aType, aExpectedTraversalRank) {}
+
+template <class T>
+TestNodeReverse<T>::TestNodeReverse() : TestNodeBase<T>() {}
+
+template <class T>
+void TestNodeReverse<T>::SetLastChild(RefPtr<TestNodeReverse<T>> aNode) {
+ mLastChildNode = aNode;
+}
+
+template <class T>
+void TestNodeReverse<T>::AddChild(RefPtr<TestNodeReverse<T>> aNode) {
+ aNode->SetPrevSibling(mLastChildNode);
+ SetLastChild(aNode);
+}
+
+template <class T>
+void TestNodeReverse<T>::SetPrevSibling(RefPtr<TestNodeReverse<T>> aNode) {
+ mSiblingNode = aNode;
+}
+
+template <class T>
+TestNodeReverse<T>* TestNodeReverse<T>::GetLastChild() {
+ return mLastChildNode;
+}
+
+template <class T>
+TestNodeReverse<T>* TestNodeReverse<T>::GetPrevSibling() {
+ return mSiblingNode;
+}
+
+template <class T>
+bool TestNodeReverse<T>::IsLeaf() {
+ return !mLastChildNode;
+}
+
+template <class T>
+TestNodeForward<T>::TestNodeForward(T aType, int aExpectedTraversalRank)
+ : TestNodeBase<T>(aType, aExpectedTraversalRank) {}
+
+template <class T>
+TestNodeForward<T>::TestNodeForward() : TestNodeBase<T>() {}
+
+template <class T>
+void TestNodeForward<T>::AddChild(RefPtr<TestNodeForward<T>> aNode) {
+ if (mFirstChildNode == nullptr) {
+ SetFirstChild(aNode);
+ SetLastChild(aNode);
+ } else {
+ mLastChildNode->SetNextSibling(aNode);
+ SetLastChild(aNode);
+ }
+}
+
+template <class T>
+void TestNodeForward<T>::SetLastChild(RefPtr<TestNodeForward<T>> aNode) {
+ mLastChildNode = aNode;
+}
+
+template <class T>
+void TestNodeForward<T>::SetFirstChild(RefPtr<TestNodeForward<T>> aNode) {
+ mFirstChildNode = aNode;
+}
+
+template <class T>
+void TestNodeForward<T>::SetNextSibling(RefPtr<TestNodeForward<T>> aNode) {
+ mSiblingNode = aNode;
+}
+
+template <class T>
+bool TestNodeForward<T>::IsLeaf() {
+ return !mFirstChildNode;
+}
+
+template <class T>
+TestNodeForward<T>* TestNodeForward<T>::GetFirstChild() {
+ return mFirstChildNode;
+}
+
+template <class T>
+TestNodeForward<T>* TestNodeForward<T>::GetNextSibling() {
+ return mSiblingNode;
+}
+
+template <class T>
+TestNodeBase<T>::TestNodeBase(T aType, int aExpectedTraversalRank)
+ : mExpectedTraversalRank(aExpectedTraversalRank),
+ mActualTraversalRank(-1),
+ mType(aType) {}
+
+template <class T>
+TestNodeBase<T>::TestNodeBase() = default;
+
+template <class T>
+int TestNodeBase<T>::GetActualTraversalRank() {
+ return mActualTraversalRank;
+}
+
+template <class T>
+void TestNodeBase<T>::SetActualTraversalRank(int aRank) {
+ mActualTraversalRank = aRank;
+}
+
+template <class T>
+int TestNodeBase<T>::GetExpectedTraversalRank() {
+ return mExpectedTraversalRank;
+}
+
+template <class T>
+T TestNodeBase<T>::GetType() {
+ return mType;
+}
+
+template <class T>
+void TestNodeBase<T>::SetType(T aType) {
+ mType = aType;
+}
+
+template <class T>
+nsRegion TestNodeBase<T>::GetRegion() {
+ return mRegion;
+}
+
+template <class T>
+void TestNodeBase<T>::SetRegion(nsRegion aRegion) {
+ mRegion = aRegion;
+}
+
+template <class T>
+int TestNodeBase<T>::GetValue() {
+ return mValue;
+}
+
+template <class T>
+void TestNodeBase<T>::SetValue(int aValue) {
+ mValue = aValue;
+}
+
+typedef TestNodeBase<SearchNodeType> SearchTestNode;
+typedef TestNodeBase<ForEachNodeType> ForEachTestNode;
+typedef TestNodeReverse<SearchNodeType> SearchTestNodeReverse;
+typedef TestNodeReverse<ForEachNodeType> ForEachTestNodeReverse;
+typedef TestNodeForward<SearchNodeType> SearchTestNodeForward;
+typedef TestNodeForward<ForEachNodeType> ForEachTestNodeForward;
+
+TEST(TreeTraversal, DepthFirstSearchNull)
+{
+ RefPtr<SearchTestNodeReverse> nullNode;
+ RefPtr<SearchTestNodeReverse> result =
+ DepthFirstSearch<layers::ReverseIterator>(
+ nullNode.get(), [](SearchTestNodeReverse* aNode) {
+ return aNode->GetType() == SearchNodeType::Needle;
+ });
+ ASSERT_EQ(result.get(), nullptr)
+ << "Null root did not return null search result.";
+}
+
+TEST(TreeTraversal, DepthFirstSearchValueExists)
+{
+ int visitCount = 0;
+ size_t expectedNeedleTraversalRank = 7;
+ RefPtr<SearchTestNodeForward> needleNode;
+ std::vector<RefPtr<SearchTestNodeForward>> nodeList;
+ nodeList.reserve(10);
+ for (size_t i = 0; i < 10; i++) {
+ if (i == expectedNeedleTraversalRank) {
+ needleNode = new SearchTestNodeForward(SearchNodeType::Needle, i);
+ nodeList.push_back(needleNode);
+ } else if (i < expectedNeedleTraversalRank) {
+ nodeList.push_back(new SearchTestNodeForward(SearchNodeType::Hay, i));
+ } else {
+ nodeList.push_back(new SearchTestNodeForward(SearchNodeType::Hay));
+ }
+ }
+
+ RefPtr<SearchTestNodeForward> root = nodeList[0];
+ nodeList[0]->AddChild(nodeList[1]);
+ nodeList[0]->AddChild(nodeList[4]);
+ nodeList[1]->AddChild(nodeList[2]);
+ nodeList[1]->AddChild(nodeList[3]);
+ nodeList[4]->AddChild(nodeList[5]);
+ nodeList[4]->AddChild(nodeList[6]);
+ nodeList[6]->AddChild(nodeList[7]);
+ nodeList[7]->AddChild(nodeList[8]);
+ nodeList[7]->AddChild(nodeList[9]);
+
+ RefPtr<SearchTestNodeForward> foundNode =
+ DepthFirstSearch<layers::ForwardIterator>(
+ root.get(), [&visitCount](SearchTestNodeForward* aNode) {
+ aNode->SetActualTraversalRank(visitCount);
+ visitCount++;
+ return aNode->GetType() == SearchNodeType::Needle;
+ });
+
+ for (size_t i = 0; i < nodeList.size(); i++) {
+ ASSERT_EQ(nodeList[i]->GetExpectedTraversalRank(),
+ nodeList[i]->GetActualTraversalRank())
+ << "Node at index " << i << " was hit out of order.";
+ }
+
+ ASSERT_EQ(foundNode, needleNode) << "Search did not return expected node.";
+ ASSERT_EQ(foundNode->GetType(), SearchNodeType::Needle)
+ << "Returned node does not match expected value (something odd "
+ "happened).";
+}
+
+TEST(TreeTraversal, DepthFirstSearchValueExistsReverse)
+{
+ int visitCount = 0;
+ size_t expectedNeedleTraversalRank = 7;
+ RefPtr<SearchTestNodeReverse> needleNode;
+ std::vector<RefPtr<SearchTestNodeReverse>> nodeList;
+ nodeList.reserve(10);
+ for (size_t i = 0; i < 10; i++) {
+ if (i == expectedNeedleTraversalRank) {
+ needleNode = new SearchTestNodeReverse(SearchNodeType::Needle, i);
+ nodeList.push_back(needleNode);
+ } else if (i < expectedNeedleTraversalRank) {
+ nodeList.push_back(new SearchTestNodeReverse(SearchNodeType::Hay, i));
+ } else {
+ nodeList.push_back(new SearchTestNodeReverse(SearchNodeType::Hay));
+ }
+ }
+
+ RefPtr<SearchTestNodeReverse> root = nodeList[0];
+ nodeList[0]->AddChild(nodeList[4]);
+ nodeList[0]->AddChild(nodeList[1]);
+ nodeList[1]->AddChild(nodeList[3]);
+ nodeList[1]->AddChild(nodeList[2]);
+ nodeList[4]->AddChild(nodeList[6]);
+ nodeList[4]->AddChild(nodeList[5]);
+ nodeList[6]->AddChild(nodeList[7]);
+ nodeList[7]->AddChild(nodeList[9]);
+ nodeList[7]->AddChild(nodeList[8]);
+
+ RefPtr<SearchTestNodeReverse> foundNode =
+ DepthFirstSearch<layers::ReverseIterator>(
+ root.get(), [&visitCount](SearchTestNodeReverse* aNode) {
+ aNode->SetActualTraversalRank(visitCount);
+ visitCount++;
+ return aNode->GetType() == SearchNodeType::Needle;
+ });
+
+ for (size_t i = 0; i < nodeList.size(); i++) {
+ ASSERT_EQ(nodeList[i]->GetExpectedTraversalRank(),
+ nodeList[i]->GetActualTraversalRank())
+ << "Node at index " << i << " was hit out of order.";
+ }
+
+ ASSERT_EQ(foundNode, needleNode) << "Search did not return expected node.";
+ ASSERT_EQ(foundNode->GetType(), SearchNodeType::Needle)
+ << "Returned node does not match expected value (something odd "
+ "happened).";
+}
+
+TEST(TreeTraversal, DepthFirstSearchRootIsNeedle)
+{
+ RefPtr<SearchTestNodeReverse> root =
+ new SearchTestNodeReverse(SearchNodeType::Needle, 0);
+ RefPtr<SearchTestNodeReverse> childNode1 =
+ new SearchTestNodeReverse(SearchNodeType::Hay);
+ RefPtr<SearchTestNodeReverse> childNode2 =
+ new SearchTestNodeReverse(SearchNodeType::Hay);
+ int visitCount = 0;
+ RefPtr<SearchTestNodeReverse> result =
+ DepthFirstSearch<layers::ReverseIterator>(
+ root.get(), [&visitCount](SearchTestNodeReverse* aNode) {
+ aNode->SetActualTraversalRank(visitCount);
+ visitCount++;
+ return aNode->GetType() == SearchNodeType::Needle;
+ });
+ ASSERT_EQ(result, root) << "Search starting at needle did not return needle.";
+ ASSERT_EQ(root->GetExpectedTraversalRank(), root->GetActualTraversalRank())
+ << "Search starting at needle did not return needle.";
+ ASSERT_EQ(childNode1->GetExpectedTraversalRank(),
+ childNode1->GetActualTraversalRank())
+ << "Search starting at needle continued past needle.";
+ ASSERT_EQ(childNode2->GetExpectedTraversalRank(),
+ childNode2->GetActualTraversalRank())
+ << "Search starting at needle continued past needle.";
+}
+
+TEST(TreeTraversal, DepthFirstSearchValueDoesNotExist)
+{
+ int visitCount = 0;
+ std::vector<RefPtr<SearchTestNodeForward>> nodeList;
+ nodeList.reserve(10);
+ for (int i = 0; i < 10; i++) {
+ nodeList.push_back(new SearchTestNodeForward(SearchNodeType::Hay, i));
+ }
+
+ RefPtr<SearchTestNodeForward> root = nodeList[0];
+ nodeList[0]->AddChild(nodeList[1]);
+ nodeList[0]->AddChild(nodeList[4]);
+ nodeList[1]->AddChild(nodeList[2]);
+ nodeList[1]->AddChild(nodeList[3]);
+ nodeList[4]->AddChild(nodeList[5]);
+ nodeList[4]->AddChild(nodeList[6]);
+ nodeList[6]->AddChild(nodeList[7]);
+ nodeList[7]->AddChild(nodeList[8]);
+ nodeList[7]->AddChild(nodeList[9]);
+
+ RefPtr<SearchTestNodeForward> foundNode =
+ DepthFirstSearch<layers::ForwardIterator>(
+ root.get(), [&visitCount](SearchTestNodeForward* aNode) {
+ aNode->SetActualTraversalRank(visitCount);
+ visitCount++;
+ return aNode->GetType() == SearchNodeType::Needle;
+ });
+
+ for (int i = 0; i < 10; i++) {
+ ASSERT_EQ(nodeList[i]->GetExpectedTraversalRank(),
+ nodeList[i]->GetActualTraversalRank())
+ << "Node at index " << i << " was hit out of order.";
+ }
+
+ ASSERT_EQ(foundNode.get(), nullptr)
+ << "Search found something that should not exist.";
+}
+
+TEST(TreeTraversal, DepthFirstSearchValueDoesNotExistReverse)
+{
+ int visitCount = 0;
+ std::vector<RefPtr<SearchTestNodeReverse>> nodeList;
+ nodeList.reserve(10);
+ for (int i = 0; i < 10; i++) {
+ nodeList.push_back(new SearchTestNodeReverse(SearchNodeType::Hay, i));
+ }
+
+ RefPtr<SearchTestNodeReverse> root = nodeList[0];
+ nodeList[0]->AddChild(nodeList[4]);
+ nodeList[0]->AddChild(nodeList[1]);
+ nodeList[1]->AddChild(nodeList[3]);
+ nodeList[1]->AddChild(nodeList[2]);
+ nodeList[4]->AddChild(nodeList[6]);
+ nodeList[4]->AddChild(nodeList[5]);
+ nodeList[6]->AddChild(nodeList[7]);
+ nodeList[7]->AddChild(nodeList[9]);
+ nodeList[7]->AddChild(nodeList[8]);
+
+ RefPtr<SearchTestNodeReverse> foundNode =
+ DepthFirstSearch<layers::ReverseIterator>(
+ root.get(), [&visitCount](SearchTestNodeReverse* aNode) {
+ aNode->SetActualTraversalRank(visitCount);
+ visitCount++;
+ return aNode->GetType() == SearchNodeType::Needle;
+ });
+
+ for (int i = 0; i < 10; i++) {
+ ASSERT_EQ(nodeList[i]->GetExpectedTraversalRank(),
+ nodeList[i]->GetActualTraversalRank())
+ << "Node at index " << i << " was hit out of order.";
+ }
+
+ ASSERT_EQ(foundNode.get(), nullptr)
+ << "Search found something that should not exist.";
+}
+
+TEST(TreeTraversal, DepthFirstSearchPostOrderNull)
+{
+ RefPtr<SearchTestNodeReverse> nullNode;
+ RefPtr<SearchTestNodeReverse> result =
+ DepthFirstSearchPostOrder<layers::ReverseIterator>(
+ nullNode.get(), [](SearchTestNodeReverse* aNode) {
+ return aNode->GetType() == SearchNodeType::Needle;
+ });
+ ASSERT_EQ(result.get(), nullptr)
+ << "Null root did not return null search result.";
+}
+
+TEST(TreeTraversal, DepthFirstSearchPostOrderValueExists)
+{
+ int visitCount = 0;
+ size_t expectedNeedleTraversalRank = 7;
+ RefPtr<SearchTestNodeForward> needleNode;
+ std::vector<RefPtr<SearchTestNodeForward>> nodeList;
+ for (size_t i = 0; i < 10; i++) {
+ if (i == expectedNeedleTraversalRank) {
+ needleNode = new SearchTestNodeForward(SearchNodeType::Needle, i);
+ nodeList.push_back(needleNode);
+ } else if (i < expectedNeedleTraversalRank) {
+ nodeList.push_back(new SearchTestNodeForward(SearchNodeType::Hay, i));
+ } else {
+ nodeList.push_back(new SearchTestNodeForward(SearchNodeType::Hay));
+ }
+ }
+
+ RefPtr<SearchTestNodeForward> root = nodeList[9];
+ nodeList[9]->AddChild(nodeList[2]);
+ nodeList[9]->AddChild(nodeList[8]);
+ nodeList[2]->AddChild(nodeList[0]);
+ nodeList[2]->AddChild(nodeList[1]);
+ nodeList[8]->AddChild(nodeList[6]);
+ nodeList[8]->AddChild(nodeList[7]);
+ nodeList[6]->AddChild(nodeList[5]);
+ nodeList[5]->AddChild(nodeList[3]);
+ nodeList[5]->AddChild(nodeList[4]);
+
+ RefPtr<SearchTestNodeForward> foundNode =
+ DepthFirstSearchPostOrder<layers::ForwardIterator>(
+ root.get(), [&visitCount](SearchTestNodeForward* aNode) {
+ aNode->SetActualTraversalRank(visitCount);
+ visitCount++;
+ return aNode->GetType() == SearchNodeType::Needle;
+ });
+
+ for (size_t i = 0; i < nodeList.size(); i++) {
+ ASSERT_EQ(nodeList[i]->GetExpectedTraversalRank(),
+ nodeList[i]->GetActualTraversalRank())
+ << "Node at index " << i << " was hit out of order.";
+ }
+
+ ASSERT_EQ(foundNode, needleNode) << "Search did not return expected node.";
+ ASSERT_EQ(foundNode->GetType(), SearchNodeType::Needle)
+ << "Returned node does not match expected value (something odd "
+ "happened).";
+}
+
+TEST(TreeTraversal, DepthFirstSearchPostOrderValueExistsReverse)
+{
+ int visitCount = 0;
+ size_t expectedNeedleTraversalRank = 7;
+ RefPtr<SearchTestNodeReverse> needleNode;
+ std::vector<RefPtr<SearchTestNodeReverse>> nodeList;
+ for (size_t i = 0; i < 10; i++) {
+ if (i == expectedNeedleTraversalRank) {
+ needleNode = new SearchTestNodeReverse(SearchNodeType::Needle, i);
+ nodeList.push_back(needleNode);
+ } else if (i < expectedNeedleTraversalRank) {
+ nodeList.push_back(new SearchTestNodeReverse(SearchNodeType::Hay, i));
+ } else {
+ nodeList.push_back(new SearchTestNodeReverse(SearchNodeType::Hay));
+ }
+ }
+
+ RefPtr<SearchTestNodeReverse> root = nodeList[9];
+ nodeList[9]->AddChild(nodeList[8]);
+ nodeList[9]->AddChild(nodeList[2]);
+ nodeList[2]->AddChild(nodeList[1]);
+ nodeList[2]->AddChild(nodeList[0]);
+ nodeList[8]->AddChild(nodeList[7]);
+ nodeList[8]->AddChild(nodeList[6]);
+ nodeList[6]->AddChild(nodeList[5]);
+ nodeList[5]->AddChild(nodeList[4]);
+ nodeList[5]->AddChild(nodeList[3]);
+
+ RefPtr<SearchTestNodeReverse> foundNode =
+ DepthFirstSearchPostOrder<layers::ReverseIterator>(
+ root.get(), [&visitCount](SearchTestNodeReverse* aNode) {
+ aNode->SetActualTraversalRank(visitCount);
+ visitCount++;
+ return aNode->GetType() == SearchNodeType::Needle;
+ });
+
+ for (size_t i = 0; i < nodeList.size(); i++) {
+ ASSERT_EQ(nodeList[i]->GetExpectedTraversalRank(),
+ nodeList[i]->GetActualTraversalRank())
+ << "Node at index " << i << " was hit out of order.";
+ }
+
+ ASSERT_EQ(foundNode, needleNode) << "Search did not return expected node.";
+ ASSERT_EQ(foundNode->GetType(), SearchNodeType::Needle)
+ << "Returned node does not match expected value (something odd "
+ "happened).";
+}
+
+TEST(TreeTraversal, DepthFirstSearchPostOrderRootIsNeedle)
+{
+ RefPtr<SearchTestNodeReverse> root =
+ new SearchTestNodeReverse(SearchNodeType::Needle, 0);
+ RefPtr<SearchTestNodeReverse> childNode1 =
+ new SearchTestNodeReverse(SearchNodeType::Hay);
+ RefPtr<SearchTestNodeReverse> childNode2 =
+ new SearchTestNodeReverse(SearchNodeType::Hay);
+ int visitCount = 0;
+ RefPtr<SearchTestNodeReverse> result =
+ DepthFirstSearchPostOrder<layers::ReverseIterator>(
+ root.get(), [&visitCount](SearchTestNodeReverse* aNode) {
+ aNode->SetActualTraversalRank(visitCount);
+ visitCount++;
+ return aNode->GetType() == SearchNodeType::Needle;
+ });
+ ASSERT_EQ(result, root) << "Search starting at needle did not return needle.";
+ ASSERT_EQ(root->GetExpectedTraversalRank(), root->GetActualTraversalRank())
+ << "Search starting at needle did not return needle.";
+ ASSERT_EQ(childNode1->GetExpectedTraversalRank(),
+ childNode1->GetActualTraversalRank())
+ << "Search starting at needle continued past needle.";
+ ASSERT_EQ(childNode2->GetExpectedTraversalRank(),
+ childNode2->GetActualTraversalRank())
+ << "Search starting at needle continued past needle.";
+}
+
+TEST(TreeTraversal, DepthFirstSearchPostOrderValueDoesNotExist)
+{
+ int visitCount = 0;
+ std::vector<RefPtr<SearchTestNodeForward>> nodeList;
+ nodeList.reserve(10);
+ for (int i = 0; i < 10; i++) {
+ nodeList.push_back(new SearchTestNodeForward(SearchNodeType::Hay, i));
+ }
+
+ RefPtr<SearchTestNodeForward> root = nodeList[9];
+ nodeList[9]->AddChild(nodeList[2]);
+ nodeList[9]->AddChild(nodeList[8]);
+ nodeList[2]->AddChild(nodeList[0]);
+ nodeList[2]->AddChild(nodeList[1]);
+ nodeList[8]->AddChild(nodeList[6]);
+ nodeList[8]->AddChild(nodeList[7]);
+ nodeList[6]->AddChild(nodeList[5]);
+ nodeList[5]->AddChild(nodeList[3]);
+ nodeList[5]->AddChild(nodeList[4]);
+
+ RefPtr<SearchTestNodeForward> foundNode =
+ DepthFirstSearchPostOrder<layers::ForwardIterator>(
+ root.get(), [&visitCount](SearchTestNodeForward* aNode) {
+ aNode->SetActualTraversalRank(visitCount);
+ visitCount++;
+ return aNode->GetType() == SearchNodeType::Needle;
+ });
+
+ for (int i = 0; i < 10; i++) {
+ ASSERT_EQ(nodeList[i]->GetExpectedTraversalRank(),
+ nodeList[i]->GetActualTraversalRank())
+ << "Node at index " << i << " was hit out of order.";
+ }
+
+ ASSERT_EQ(foundNode.get(), nullptr)
+ << "Search found something that should not exist.";
+}
+
+TEST(TreeTraversal, DepthFirstSearchPostOrderValueDoesNotExistReverse)
+{
+ int visitCount = 0;
+ std::vector<RefPtr<SearchTestNodeReverse>> nodeList;
+ nodeList.reserve(10);
+ for (int i = 0; i < 10; i++) {
+ nodeList.push_back(new SearchTestNodeReverse(SearchNodeType::Hay, i));
+ }
+
+ RefPtr<SearchTestNodeReverse> root = nodeList[9];
+ nodeList[9]->AddChild(nodeList[8]);
+ nodeList[9]->AddChild(nodeList[2]);
+ nodeList[2]->AddChild(nodeList[1]);
+ nodeList[2]->AddChild(nodeList[0]);
+ nodeList[8]->AddChild(nodeList[7]);
+ nodeList[8]->AddChild(nodeList[6]);
+ nodeList[6]->AddChild(nodeList[5]);
+ nodeList[5]->AddChild(nodeList[4]);
+ nodeList[5]->AddChild(nodeList[3]);
+
+ RefPtr<SearchTestNodeReverse> foundNode =
+ DepthFirstSearchPostOrder<layers::ReverseIterator>(
+ root.get(), [&visitCount](SearchTestNodeReverse* aNode) {
+ aNode->SetActualTraversalRank(visitCount);
+ visitCount++;
+ return aNode->GetType() == SearchNodeType::Needle;
+ });
+
+ for (int i = 0; i < 10; i++) {
+ ASSERT_EQ(nodeList[i]->GetExpectedTraversalRank(),
+ nodeList[i]->GetActualTraversalRank())
+ << "Node at index " << i << " was hit out of order.";
+ }
+
+ ASSERT_EQ(foundNode.get(), nullptr)
+ << "Search found something that should not exist.";
+}
+
+TEST(TreeTraversal, BreadthFirstSearchNull)
+{
+ RefPtr<SearchTestNodeReverse> nullNode;
+ RefPtr<SearchTestNodeReverse> result =
+ BreadthFirstSearch<layers::ReverseIterator>(
+ nullNode.get(), [](SearchTestNodeReverse* aNode) {
+ return aNode->GetType() == SearchNodeType::Needle;
+ });
+ ASSERT_EQ(result.get(), nullptr)
+ << "Null root did not return null search result.";
+}
+
+TEST(TreeTraversal, BreadthFirstSearchRootIsNeedle)
+{
+ RefPtr<SearchTestNodeReverse> root =
+ new SearchTestNodeReverse(SearchNodeType::Needle, 0);
+ RefPtr<SearchTestNodeReverse> childNode1 =
+ new SearchTestNodeReverse(SearchNodeType::Hay);
+ RefPtr<SearchTestNodeReverse> childNode2 =
+ new SearchTestNodeReverse(SearchNodeType::Hay);
+ int visitCount = 0;
+ RefPtr<SearchTestNodeReverse> result =
+ BreadthFirstSearch<layers::ReverseIterator>(
+ root.get(), [&visitCount](SearchTestNodeReverse* aNode) {
+ aNode->SetActualTraversalRank(visitCount);
+ visitCount++;
+ return aNode->GetType() == SearchNodeType::Needle;
+ });
+ ASSERT_EQ(result, root) << "Search starting at needle did not return needle.";
+ ASSERT_EQ(root->GetExpectedTraversalRank(), root->GetActualTraversalRank())
+ << "Search starting at needle did not return needle.";
+ ASSERT_EQ(childNode1->GetExpectedTraversalRank(),
+ childNode1->GetActualTraversalRank())
+ << "Search starting at needle continued past needle.";
+ ASSERT_EQ(childNode2->GetExpectedTraversalRank(),
+ childNode2->GetActualTraversalRank())
+ << "Search starting at needle continued past needle.";
+}
+
+TEST(TreeTraversal, BreadthFirstSearchValueExists)
+{
+ int visitCount = 0;
+ size_t expectedNeedleTraversalRank = 7;
+ RefPtr<SearchTestNodeForward> needleNode;
+ std::vector<RefPtr<SearchTestNodeForward>> nodeList;
+ nodeList.reserve(10);
+ for (size_t i = 0; i < 10; i++) {
+ if (i == expectedNeedleTraversalRank) {
+ needleNode = new SearchTestNodeForward(SearchNodeType::Needle, i);
+ nodeList.push_back(needleNode);
+ } else if (i < expectedNeedleTraversalRank) {
+ nodeList.push_back(new SearchTestNodeForward(SearchNodeType::Hay, i));
+ } else {
+ nodeList.push_back(new SearchTestNodeForward(SearchNodeType::Hay));
+ }
+ }
+
+ RefPtr<SearchTestNodeForward> root = nodeList[0];
+ nodeList[0]->AddChild(nodeList[1]);
+ nodeList[0]->AddChild(nodeList[2]);
+ nodeList[1]->AddChild(nodeList[3]);
+ nodeList[1]->AddChild(nodeList[4]);
+ nodeList[2]->AddChild(nodeList[5]);
+ nodeList[2]->AddChild(nodeList[6]);
+ nodeList[6]->AddChild(nodeList[7]);
+ nodeList[7]->AddChild(nodeList[8]);
+ nodeList[7]->AddChild(nodeList[9]);
+
+ RefPtr<SearchTestNodeForward> foundNode =
+ BreadthFirstSearch<layers::ForwardIterator>(
+ root.get(), [&visitCount](SearchTestNodeForward* aNode) {
+ aNode->SetActualTraversalRank(visitCount);
+ visitCount++;
+ return aNode->GetType() == SearchNodeType::Needle;
+ });
+
+ for (size_t i = 0; i < nodeList.size(); i++) {
+ ASSERT_EQ(nodeList[i]->GetExpectedTraversalRank(),
+ nodeList[i]->GetActualTraversalRank())
+ << "Node at index " << i << " was hit out of order.";
+ }
+
+ ASSERT_EQ(foundNode, needleNode) << "Search did not return expected node.";
+ ASSERT_EQ(foundNode->GetType(), SearchNodeType::Needle)
+ << "Returned node does not match expected value (something odd "
+ "happened).";
+}
+
+TEST(TreeTraversal, BreadthFirstSearchValueExistsReverse)
+{
+ int visitCount = 0;
+ size_t expectedNeedleTraversalRank = 7;
+ RefPtr<SearchTestNodeReverse> needleNode;
+ std::vector<RefPtr<SearchTestNodeReverse>> nodeList;
+ nodeList.reserve(10);
+ for (size_t i = 0; i < 10; i++) {
+ if (i == expectedNeedleTraversalRank) {
+ needleNode = new SearchTestNodeReverse(SearchNodeType::Needle, i);
+ nodeList.push_back(needleNode);
+ } else if (i < expectedNeedleTraversalRank) {
+ nodeList.push_back(new SearchTestNodeReverse(SearchNodeType::Hay, i));
+ } else {
+ nodeList.push_back(new SearchTestNodeReverse(SearchNodeType::Hay));
+ }
+ }
+
+ RefPtr<SearchTestNodeReverse> root = nodeList[0];
+ nodeList[0]->AddChild(nodeList[2]);
+ nodeList[0]->AddChild(nodeList[1]);
+ nodeList[1]->AddChild(nodeList[4]);
+ nodeList[1]->AddChild(nodeList[3]);
+ nodeList[2]->AddChild(nodeList[6]);
+ nodeList[2]->AddChild(nodeList[5]);
+ nodeList[6]->AddChild(nodeList[7]);
+ nodeList[7]->AddChild(nodeList[9]);
+ nodeList[7]->AddChild(nodeList[8]);
+
+ RefPtr<SearchTestNodeReverse> foundNode =
+ BreadthFirstSearch<layers::ReverseIterator>(
+ root.get(), [&visitCount](SearchTestNodeReverse* aNode) {
+ aNode->SetActualTraversalRank(visitCount);
+ visitCount++;
+ return aNode->GetType() == SearchNodeType::Needle;
+ });
+
+ for (size_t i = 0; i < nodeList.size(); i++) {
+ ASSERT_EQ(nodeList[i]->GetExpectedTraversalRank(),
+ nodeList[i]->GetActualTraversalRank())
+ << "Node at index " << i << " was hit out of order.";
+ }
+
+ ASSERT_EQ(foundNode, needleNode) << "Search did not return expected node.";
+ ASSERT_EQ(foundNode->GetType(), SearchNodeType::Needle)
+ << "Returned node does not match expected value (something odd "
+ "happened).";
+}
+
+TEST(TreeTraversal, BreadthFirstSearchValueDoesNotExist)
+{
+ int visitCount = 0;
+ std::vector<RefPtr<SearchTestNodeForward>> nodeList;
+ nodeList.reserve(10);
+ for (int i = 0; i < 10; i++) {
+ nodeList.push_back(new SearchTestNodeForward(SearchNodeType::Hay, i));
+ }
+
+ RefPtr<SearchTestNodeForward> root = nodeList[0];
+ nodeList[0]->AddChild(nodeList[1]);
+ nodeList[0]->AddChild(nodeList[2]);
+ nodeList[1]->AddChild(nodeList[3]);
+ nodeList[1]->AddChild(nodeList[4]);
+ nodeList[2]->AddChild(nodeList[5]);
+ nodeList[2]->AddChild(nodeList[6]);
+ nodeList[6]->AddChild(nodeList[7]);
+ nodeList[7]->AddChild(nodeList[8]);
+ nodeList[7]->AddChild(nodeList[9]);
+
+ RefPtr<SearchTestNodeForward> foundNode =
+ BreadthFirstSearch<layers::ForwardIterator>(
+ root.get(), [&visitCount](SearchTestNodeForward* aNode) {
+ aNode->SetActualTraversalRank(visitCount);
+ visitCount++;
+ return aNode->GetType() == SearchNodeType::Needle;
+ });
+
+ for (size_t i = 0; i < nodeList.size(); i++) {
+ ASSERT_EQ(nodeList[i]->GetExpectedTraversalRank(),
+ nodeList[i]->GetActualTraversalRank())
+ << "Node at index " << i << " was hit out of order.";
+ }
+
+ ASSERT_EQ(foundNode.get(), nullptr)
+ << "Search found something that should not exist.";
+}
+
+TEST(TreeTraversal, BreadthFirstSearchValueDoesNotExistReverse)
+{
+ int visitCount = 0;
+ std::vector<RefPtr<SearchTestNodeReverse>> nodeList;
+ nodeList.reserve(10);
+ for (int i = 0; i < 10; i++) {
+ nodeList.push_back(new SearchTestNodeReverse(SearchNodeType::Hay, i));
+ }
+
+ RefPtr<SearchTestNodeReverse> root = nodeList[0];
+ nodeList[0]->AddChild(nodeList[2]);
+ nodeList[0]->AddChild(nodeList[1]);
+ nodeList[1]->AddChild(nodeList[4]);
+ nodeList[1]->AddChild(nodeList[3]);
+ nodeList[2]->AddChild(nodeList[6]);
+ nodeList[2]->AddChild(nodeList[5]);
+ nodeList[6]->AddChild(nodeList[7]);
+ nodeList[7]->AddChild(nodeList[9]);
+ nodeList[7]->AddChild(nodeList[8]);
+
+ RefPtr<SearchTestNodeReverse> foundNode =
+ BreadthFirstSearch<layers::ReverseIterator>(
+ root.get(), [&visitCount](SearchTestNodeReverse* aNode) {
+ aNode->SetActualTraversalRank(visitCount);
+ visitCount++;
+ return aNode->GetType() == SearchNodeType::Needle;
+ });
+
+ for (size_t i = 0; i < nodeList.size(); i++) {
+ ASSERT_EQ(nodeList[i]->GetExpectedTraversalRank(),
+ nodeList[i]->GetActualTraversalRank())
+ << "Node at index " << i << " was hit out of order.";
+ }
+
+ ASSERT_EQ(foundNode.get(), nullptr)
+ << "Search found something that should not exist.";
+}
+
+TEST(TreeTraversal, ForEachNodeNullStillRuns)
+{
+ RefPtr<ForEachTestNodeReverse> nullNode;
+ ForEachNode<layers::ReverseIterator>(
+ nullNode.get(),
+ [](ForEachTestNodeReverse* aNode) { return TraversalFlag::Continue; });
+}
+
+TEST(TreeTraversal, ForEachNodeAllEligible)
+{
+ std::vector<RefPtr<ForEachTestNodeForward>> nodeList;
+ int visitCount = 0;
+ nodeList.reserve(10);
+ for (int i = 0; i < 10; i++) {
+ nodeList.push_back(
+ new ForEachTestNodeForward(ForEachNodeType::Continue, i));
+ }
+
+ RefPtr<ForEachTestNodeForward> root = nodeList[0];
+ nodeList[0]->AddChild(nodeList[1]);
+ nodeList[0]->AddChild(nodeList[4]);
+ nodeList[1]->AddChild(nodeList[2]);
+ nodeList[1]->AddChild(nodeList[3]);
+ nodeList[4]->AddChild(nodeList[5]);
+ nodeList[4]->AddChild(nodeList[6]);
+ nodeList[6]->AddChild(nodeList[7]);
+ nodeList[7]->AddChild(nodeList[8]);
+ nodeList[7]->AddChild(nodeList[9]);
+
+ ForEachNode<layers::ForwardIterator>(
+ root.get(), [&visitCount](ForEachTestNodeForward* aNode) {
+ aNode->SetActualTraversalRank(visitCount);
+ visitCount++;
+ return aNode->GetType() == ForEachNodeType::Continue
+ ? TraversalFlag::Continue
+ : TraversalFlag::Skip;
+ });
+
+ for (size_t i = 0; i < nodeList.size(); i++) {
+ ASSERT_EQ(nodeList[i]->GetExpectedTraversalRank(),
+ nodeList[i]->GetActualTraversalRank())
+ << "Node at index " << i << " was hit out of order.";
+ }
+}
+
+TEST(TreeTraversal, ForEachNodeAllEligibleReverse)
+{
+ std::vector<RefPtr<ForEachTestNodeReverse>> nodeList;
+ int visitCount = 0;
+ nodeList.reserve(10);
+ for (int i = 0; i < 10; i++) {
+ nodeList.push_back(
+ new ForEachTestNodeReverse(ForEachNodeType::Continue, i));
+ }
+
+ RefPtr<ForEachTestNodeReverse> root = nodeList[0];
+ nodeList[0]->AddChild(nodeList[4]);
+ nodeList[0]->AddChild(nodeList[1]);
+ nodeList[1]->AddChild(nodeList[3]);
+ nodeList[1]->AddChild(nodeList[2]);
+ nodeList[4]->AddChild(nodeList[6]);
+ nodeList[4]->AddChild(nodeList[5]);
+ nodeList[6]->AddChild(nodeList[7]);
+ nodeList[7]->AddChild(nodeList[9]);
+ nodeList[7]->AddChild(nodeList[8]);
+
+ ForEachNode<layers::ReverseIterator>(
+ root.get(), [&visitCount](ForEachTestNodeReverse* aNode) {
+ aNode->SetActualTraversalRank(visitCount);
+ visitCount++;
+ return aNode->GetType() == ForEachNodeType::Continue
+ ? TraversalFlag::Continue
+ : TraversalFlag::Skip;
+ });
+
+ for (size_t i = 0; i < nodeList.size(); i++) {
+ ASSERT_EQ(nodeList[i]->GetExpectedTraversalRank(),
+ nodeList[i]->GetActualTraversalRank())
+ << "Node at index " << i << " was hit out of order.";
+ }
+}
+
+TEST(TreeTraversal, ForEachNodeSomeIneligibleNodes)
+{
+ std::vector<RefPtr<ForEachTestNodeForward>> expectedVisitedNodeList;
+ std::vector<RefPtr<ForEachTestNodeForward>> expectedSkippedNodeList;
+ int visitCount = 0;
+
+ expectedVisitedNodeList.push_back(
+ new ForEachTestNodeForward(ForEachNodeType::Continue, 0));
+ expectedVisitedNodeList.push_back(
+ new ForEachTestNodeForward(ForEachNodeType::Skip, 1));
+ expectedVisitedNodeList.push_back(
+ new ForEachTestNodeForward(ForEachNodeType::Continue, 2));
+ expectedVisitedNodeList.push_back(
+ new ForEachTestNodeForward(ForEachNodeType::Skip, 3));
+
+ expectedSkippedNodeList.push_back(
+ new ForEachTestNodeForward(ForEachNodeType::Continue));
+ expectedSkippedNodeList.push_back(
+ new ForEachTestNodeForward(ForEachNodeType::Continue));
+ expectedSkippedNodeList.push_back(
+ new ForEachTestNodeForward(ForEachNodeType::Skip));
+ expectedSkippedNodeList.push_back(
+ new ForEachTestNodeForward(ForEachNodeType::Skip));
+
+ RefPtr<ForEachTestNodeForward> root = expectedVisitedNodeList[0];
+ expectedVisitedNodeList[0]->AddChild(expectedVisitedNodeList[1]);
+ expectedVisitedNodeList[0]->AddChild(expectedVisitedNodeList[2]);
+ expectedVisitedNodeList[1]->AddChild(expectedSkippedNodeList[0]);
+ expectedVisitedNodeList[1]->AddChild(expectedSkippedNodeList[1]);
+ expectedVisitedNodeList[2]->AddChild(expectedVisitedNodeList[3]);
+ expectedVisitedNodeList[3]->AddChild(expectedSkippedNodeList[2]);
+ expectedVisitedNodeList[3]->AddChild(expectedSkippedNodeList[3]);
+
+ ForEachNode<layers::ForwardIterator>(
+ root.get(), [&visitCount](ForEachTestNodeForward* aNode) {
+ aNode->SetActualTraversalRank(visitCount);
+ visitCount++;
+ return aNode->GetType() == ForEachNodeType::Continue
+ ? TraversalFlag::Continue
+ : TraversalFlag::Skip;
+ });
+
+ for (size_t i = 0; i < expectedVisitedNodeList.size(); i++) {
+ ASSERT_EQ(expectedVisitedNodeList[i]->GetExpectedTraversalRank(),
+ expectedVisitedNodeList[i]->GetActualTraversalRank())
+ << "Node at index " << i << " was hit out of order.";
+ }
+
+ for (size_t i = 0; i < expectedSkippedNodeList.size(); i++) {
+ ASSERT_EQ(expectedSkippedNodeList[i]->GetExpectedTraversalRank(),
+ expectedSkippedNodeList[i]->GetActualTraversalRank())
+ << "Node at index " << i << "was not expected to be hit.";
+ }
+}
+
+TEST(TreeTraversal, ForEachNodeSomeIneligibleNodesReverse)
+{
+ std::vector<RefPtr<ForEachTestNodeReverse>> expectedVisitedNodeList;
+ std::vector<RefPtr<ForEachTestNodeReverse>> expectedSkippedNodeList;
+ int visitCount = 0;
+
+ expectedVisitedNodeList.push_back(
+ new ForEachTestNodeReverse(ForEachNodeType::Continue, 0));
+ expectedVisitedNodeList.push_back(
+ new ForEachTestNodeReverse(ForEachNodeType::Skip, 1));
+ expectedVisitedNodeList.push_back(
+ new ForEachTestNodeReverse(ForEachNodeType::Continue, 2));
+ expectedVisitedNodeList.push_back(
+ new ForEachTestNodeReverse(ForEachNodeType::Skip, 3));
+
+ expectedSkippedNodeList.push_back(
+ new ForEachTestNodeReverse(ForEachNodeType::Continue));
+ expectedSkippedNodeList.push_back(
+ new ForEachTestNodeReverse(ForEachNodeType::Continue));
+ expectedSkippedNodeList.push_back(
+ new ForEachTestNodeReverse(ForEachNodeType::Skip));
+ expectedSkippedNodeList.push_back(
+ new ForEachTestNodeReverse(ForEachNodeType::Skip));
+
+ RefPtr<ForEachTestNodeReverse> root = expectedVisitedNodeList[0];
+ expectedVisitedNodeList[0]->AddChild(expectedVisitedNodeList[2]);
+ expectedVisitedNodeList[0]->AddChild(expectedVisitedNodeList[1]);
+ expectedVisitedNodeList[1]->AddChild(expectedSkippedNodeList[1]);
+ expectedVisitedNodeList[1]->AddChild(expectedSkippedNodeList[0]);
+ expectedVisitedNodeList[2]->AddChild(expectedVisitedNodeList[3]);
+ expectedVisitedNodeList[3]->AddChild(expectedSkippedNodeList[3]);
+ expectedVisitedNodeList[3]->AddChild(expectedSkippedNodeList[2]);
+
+ ForEachNode<layers::ReverseIterator>(
+ root.get(), [&visitCount](ForEachTestNodeReverse* aNode) {
+ aNode->SetActualTraversalRank(visitCount);
+ visitCount++;
+ return aNode->GetType() == ForEachNodeType::Continue
+ ? TraversalFlag::Continue
+ : TraversalFlag::Skip;
+ });
+
+ for (size_t i = 0; i < expectedVisitedNodeList.size(); i++) {
+ ASSERT_EQ(expectedVisitedNodeList[i]->GetExpectedTraversalRank(),
+ expectedVisitedNodeList[i]->GetActualTraversalRank())
+ << "Node at index " << i << " was hit out of order.";
+ }
+
+ for (size_t i = 0; i < expectedSkippedNodeList.size(); i++) {
+ ASSERT_EQ(expectedSkippedNodeList[i]->GetExpectedTraversalRank(),
+ expectedSkippedNodeList[i]->GetActualTraversalRank())
+ << "Node at index " << i << "was not expected to be hit.";
+ }
+}
+
+TEST(TreeTraversal, ForEachNodeIneligibleRoot)
+{
+ int visitCount = 0;
+
+ RefPtr<ForEachTestNodeReverse> root =
+ new ForEachTestNodeReverse(ForEachNodeType::Skip, 0);
+ RefPtr<ForEachTestNodeReverse> childNode1 =
+ new ForEachTestNodeReverse(ForEachNodeType::Continue);
+ RefPtr<ForEachTestNodeReverse> chlidNode2 =
+ new ForEachTestNodeReverse(ForEachNodeType::Skip);
+
+ ForEachNode<layers::ReverseIterator>(
+ root.get(), [&visitCount](ForEachTestNodeReverse* aNode) {
+ aNode->SetActualTraversalRank(visitCount);
+ visitCount++;
+ return aNode->GetType() == ForEachNodeType::Continue
+ ? TraversalFlag::Continue
+ : TraversalFlag::Skip;
+ });
+
+ ASSERT_EQ(root->GetExpectedTraversalRank(), root->GetActualTraversalRank())
+ << "Root was hit out of order.";
+ ASSERT_EQ(childNode1->GetExpectedTraversalRank(),
+ childNode1->GetActualTraversalRank())
+ << "Eligible child was still hit.";
+ ASSERT_EQ(chlidNode2->GetExpectedTraversalRank(),
+ chlidNode2->GetActualTraversalRank())
+ << "Ineligible child was still hit.";
+}
+
+TEST(TreeTraversal, ForEachNodeLeavesIneligible)
+{
+ std::vector<RefPtr<ForEachTestNodeForward>> nodeList;
+ nodeList.reserve(10);
+ int visitCount = 0;
+ for (int i = 0; i < 10; i++) {
+ if (i == 1 || i == 9) {
+ nodeList.push_back(new ForEachTestNodeForward(ForEachNodeType::Skip, i));
+ } else {
+ nodeList.push_back(
+ new ForEachTestNodeForward(ForEachNodeType::Continue, i));
+ }
+ }
+
+ RefPtr<ForEachTestNodeForward> root = nodeList[0];
+ nodeList[0]->AddChild(nodeList[1]);
+ nodeList[0]->AddChild(nodeList[2]);
+ nodeList[2]->AddChild(nodeList[3]);
+ nodeList[2]->AddChild(nodeList[4]);
+ nodeList[4]->AddChild(nodeList[5]);
+ nodeList[4]->AddChild(nodeList[6]);
+ nodeList[6]->AddChild(nodeList[7]);
+ nodeList[7]->AddChild(nodeList[8]);
+ nodeList[7]->AddChild(nodeList[9]);
+
+ ForEachNode<layers::ForwardIterator>(
+ root.get(), [&visitCount](ForEachTestNodeForward* aNode) {
+ aNode->SetActualTraversalRank(visitCount);
+ visitCount++;
+ return aNode->GetType() == ForEachNodeType::Continue
+ ? TraversalFlag::Continue
+ : TraversalFlag::Skip;
+ });
+
+ for (size_t i = 0; i < nodeList.size(); i++) {
+ ASSERT_EQ(nodeList[i]->GetExpectedTraversalRank(),
+ nodeList[i]->GetActualTraversalRank())
+ << "Node at index " << i << " was hit out of order.";
+ }
+}
+
+TEST(TreeTraversal, ForEachNodeLeavesIneligibleReverse)
+{
+ std::vector<RefPtr<ForEachTestNodeReverse>> nodeList;
+ nodeList.reserve(10);
+ int visitCount = 0;
+ for (int i = 0; i < 10; i++) {
+ if (i == 1 || i == 9) {
+ nodeList.push_back(new ForEachTestNodeReverse(ForEachNodeType::Skip, i));
+ } else {
+ nodeList.push_back(
+ new ForEachTestNodeReverse(ForEachNodeType::Continue, i));
+ }
+ }
+
+ RefPtr<ForEachTestNodeReverse> root = nodeList[0];
+ nodeList[0]->AddChild(nodeList[2]);
+ nodeList[0]->AddChild(nodeList[1]);
+ nodeList[2]->AddChild(nodeList[4]);
+ nodeList[2]->AddChild(nodeList[3]);
+ nodeList[4]->AddChild(nodeList[6]);
+ nodeList[4]->AddChild(nodeList[5]);
+ nodeList[6]->AddChild(nodeList[7]);
+ nodeList[7]->AddChild(nodeList[9]);
+ nodeList[7]->AddChild(nodeList[8]);
+
+ ForEachNode<layers::ReverseIterator>(
+ root.get(), [&visitCount](ForEachTestNodeReverse* aNode) {
+ aNode->SetActualTraversalRank(visitCount);
+ visitCount++;
+ return aNode->GetType() == ForEachNodeType::Continue
+ ? TraversalFlag::Continue
+ : TraversalFlag::Skip;
+ });
+
+ for (size_t i = 0; i < nodeList.size(); i++) {
+ ASSERT_EQ(nodeList[i]->GetExpectedTraversalRank(),
+ nodeList[i]->GetActualTraversalRank())
+ << "Node at index " << i << " was hit out of order.";
+ }
+}
+
+TEST(TreeTraversal, ForEachNodeLambdaReturnsVoid)
+{
+ std::vector<RefPtr<ForEachTestNodeReverse>> nodeList;
+ nodeList.reserve(10);
+ int visitCount = 0;
+ for (int i = 0; i < 10; i++) {
+ nodeList.push_back(
+ new ForEachTestNodeReverse(ForEachNodeType::Continue, i));
+ }
+
+ RefPtr<ForEachTestNodeReverse> root = nodeList[0];
+ nodeList[0]->AddChild(nodeList[4]);
+ nodeList[0]->AddChild(nodeList[1]);
+ nodeList[1]->AddChild(nodeList[3]);
+ nodeList[1]->AddChild(nodeList[2]);
+ nodeList[4]->AddChild(nodeList[6]);
+ nodeList[4]->AddChild(nodeList[5]);
+ nodeList[6]->AddChild(nodeList[7]);
+ nodeList[7]->AddChild(nodeList[9]);
+ nodeList[7]->AddChild(nodeList[8]);
+
+ ForEachNode<layers::ReverseIterator>(
+ root.get(), [&visitCount](ForEachTestNodeReverse* aNode) {
+ aNode->SetActualTraversalRank(visitCount);
+ visitCount++;
+ });
+
+ for (size_t i = 0; i < nodeList.size(); i++) {
+ ASSERT_EQ(nodeList[i]->GetExpectedTraversalRank(),
+ nodeList[i]->GetActualTraversalRank())
+ << "Node at index " << i << " was hit out of order.";
+ }
+}
+
+struct AssignSearchNodeTypesWithLastLeafAsNeedle {
+ RefPtr<SearchTestNodeForward>& node;
+ void operator()(SearchTestNodeForward* aNode) {
+ aNode->SetType(SearchNodeType::Hay);
+ if (aNode->IsLeaf()) {
+ node = aNode;
+ }
+ }
+};
+
+struct AssignSearchNodeTypesAllHay {
+ void operator()(SearchTestNode* aNode) {
+ aNode->SetType(SearchNodeType::Hay);
+ }
+};
+
+struct AssignSearchNodeTypesWithFirstLeafAsNeedle {
+ RefPtr<SearchTestNodeReverse>& needleNode;
+ void operator()(SearchTestNodeReverse* aNode) {
+ if (!needleNode && aNode->IsLeaf()) {
+ needleNode = aNode;
+ }
+ aNode->SetType(SearchNodeType::Hay);
+ }
+};
+
+struct AssignSearchNodeValuesAllFalseValuesReverse {
+ int falseValue;
+ RefPtr<SearchTestNodeReverse>& needleNode;
+ void operator()(SearchTestNodeReverse* aNode) {
+ aNode->SetValue(falseValue);
+ if (!needleNode && aNode->IsLeaf()) {
+ needleNode = aNode;
+ }
+ }
+};
+
+struct AssignSearchNodeValuesAllFalseValuesForward {
+ int falseValue;
+ RefPtr<SearchTestNodeForward>& needleNode;
+ void operator()(SearchTestNodeForward* aNode) {
+ aNode->SetValue(falseValue);
+ needleNode = aNode;
+ }
+};
+
+struct AllocateUnitRegionsToLeavesOnly {
+ int& xWrap;
+ int& squareCount;
+ void operator()(ForEachTestNode* aNode) {
+ if (aNode->IsLeaf()) {
+ int x = squareCount % xWrap;
+ int y = squareCount / xWrap;
+ aNode->SetRegion(nsRegion(nsRect(x, y, 1, 1)));
+ squareCount++;
+ }
+ }
+};
+
+template <typename Node>
+static RefPtr<Node> DepthFirstSearchForwardRecursive(RefPtr<Node> aNode) {
+ if (aNode->GetType() == SearchNodeType::Needle) {
+ return aNode;
+ }
+ for (RefPtr<Node> node = aNode->GetFirstChild(); node != nullptr;
+ node = node->GetNextSibling()) {
+ if (RefPtr<Node> foundNode = DepthFirstSearchForwardRecursive(node)) {
+ return foundNode;
+ }
+ }
+ return nullptr;
+}
+
+template <typename Node>
+static RefPtr<Node> DepthFirstSearchCaptureVariablesForwardRecursive(
+ RefPtr<Node> aNode, int a, int b, int c, int d, int e, int f, int g, int h,
+ int i, int j, int k, int l, int m, int& n, int& o, int& p, int& q, int& r,
+ int& s, int& t, int& u, int& v, int& w, int& x, int& y, int& z) {
+ if (aNode->GetValue() == a + b + c + d + e + f + g + h + i + j + k + l + m +
+ n + o + p + q + r + s + t + u + v + w + x + y +
+ z) {
+ return aNode;
+ }
+ for (RefPtr<Node> node = aNode->GetFirstChild(); node != nullptr;
+ node = node->GetNextSibling()) {
+ if (RefPtr<Node> foundNode =
+ DepthFirstSearchCaptureVariablesForwardRecursive(
+ node, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s,
+ t, u, v, w, x, y, z)) {
+ return foundNode;
+ }
+ }
+ return nullptr;
+}
+
+template <typename Node>
+static RefPtr<Node> DepthFirstSearchPostOrderForwardRecursive(
+ RefPtr<Node> aNode) {
+ for (RefPtr<Node> node = aNode->GetFirstChild(); node != nullptr;
+ node = node->GetNextSibling()) {
+ if (RefPtr<Node> foundNode =
+ DepthFirstSearchPostOrderForwardRecursive(node)) {
+ return foundNode;
+ }
+ }
+ if (aNode->GetType() == SearchNodeType::Needle) {
+ return aNode;
+ }
+ return nullptr;
+}
+
+template <typename Node>
+static RefPtr<Node> BreadthFirstSearchForwardQueue(RefPtr<Node> aNode) {
+ std::queue<RefPtr<Node>> nodes;
+ nodes.push(aNode);
+ while (!nodes.empty()) {
+ RefPtr<Node> node = nodes.front();
+ nodes.pop();
+ if (node->GetType() == SearchNodeType::Needle) {
+ return node;
+ }
+ for (RefPtr<Node> childNode = node->GetFirstChild(); childNode != nullptr;
+ childNode = childNode->GetNextSibling()) {
+ nodes.push(childNode);
+ }
+ }
+ return nullptr;
+}
+
+template <typename Node>
+static RefPtr<Node> DepthFirstSearchReverseRecursive(RefPtr<Node> aNode) {
+ if (aNode->GetType() == SearchNodeType::Needle) {
+ return aNode;
+ }
+ for (RefPtr<Node> node = aNode->GetLastChild(); node != nullptr;
+ node = node->GetPrevSibling()) {
+ if (RefPtr<Node> foundNode = DepthFirstSearchReverseRecursive(node)) {
+ return foundNode;
+ }
+ }
+ return nullptr;
+}
+
+template <typename Node>
+static RefPtr<Node> DepthFirstSearchCaptureVariablesReverseRecursive(
+ RefPtr<Node> aNode, int a, int b, int c, int d, int e, int f, int g, int h,
+ int i, int j, int k, int l, int m, int& n, int& o, int& p, int& q, int& r,
+ int& s, int& t, int& u, int& v, int& w, int& x, int& y, int& z) {
+ if (aNode->GetValue() == a + b + c + d + e + f + g + h + i + j + k + l + m +
+ n + o + p + q + r + s + t + u + v + w + x + y +
+ z) {
+ return aNode;
+ }
+ for (RefPtr<Node> node = aNode->GetLastChild(); node != nullptr;
+ node = node->GetPrevSibling()) {
+ if (RefPtr<Node> foundNode =
+ DepthFirstSearchCaptureVariablesReverseRecursive(
+ node, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s,
+ t, u, v, w, x, y, z)) {
+ return foundNode;
+ }
+ }
+ return nullptr;
+}
+
+template <typename Node>
+static RefPtr<Node> DepthFirstSearchPostOrderReverseRecursive(
+ RefPtr<Node> aNode) {
+ for (RefPtr<Node> node = aNode->GetLastChild(); node != nullptr;
+ node = node->GetPrevSibling()) {
+ if (RefPtr<Node> foundNode =
+ DepthFirstSearchPostOrderReverseRecursive(node)) {
+ return foundNode;
+ }
+ }
+ if (aNode->GetType() == SearchNodeType::Needle) {
+ return aNode;
+ }
+ return nullptr;
+}
+
+template <typename Node>
+static RefPtr<Node> BreadthFirstSearchReverseQueue(RefPtr<Node> aNode) {
+ std::queue<RefPtr<Node>> nodes;
+ nodes.push(aNode);
+ while (!nodes.empty()) {
+ RefPtr<Node> node = nodes.front();
+ nodes.pop();
+ if (node->GetType() == SearchNodeType::Needle) {
+ return node;
+ }
+ for (RefPtr<Node> childNode = node->GetLastChild(); childNode != nullptr;
+ childNode = childNode->GetPrevSibling()) {
+ nodes.push(childNode);
+ }
+ }
+ return nullptr;
+}
diff --git a/gfx/tests/gtest/TestVsync.cpp b/gfx/tests/gtest/TestVsync.cpp
new file mode 100644
index 0000000000..ed77c59b08
--- /dev/null
+++ b/gfx/tests/gtest/TestVsync.cpp
@@ -0,0 +1,203 @@
+/* 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 "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "gfxPlatform.h"
+
+#include "MainThreadUtils.h"
+#include "nsIThread.h"
+#include "mozilla/RefPtr.h"
+#include "SoftwareVsyncSource.h"
+#include "VsyncSource.h"
+#include "mozilla/Monitor.h"
+#include "mozilla/TimeStamp.h"
+#include "mozilla/VsyncDispatcher.h"
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+using namespace mozilla::layers;
+using ::testing::_;
+
+// Timeout for vsync events to occur in milliseconds
+// Windows 8.1 has intermittents at 50 ms. Raise limit to 5 vsync intervals.
+const int kVsyncTimeoutMS = 80;
+
+class TestVsyncObserver : public VsyncObserver {
+ public:
+ TestVsyncObserver()
+ : mDidGetVsyncNotification(false), mVsyncMonitor("VsyncMonitor") {}
+
+ virtual bool NotifyVsync(const VsyncEvent& aVsync) override {
+ MonitorAutoLock lock(mVsyncMonitor);
+ mDidGetVsyncNotification = true;
+ mVsyncMonitor.Notify();
+ return true;
+ }
+
+ void WaitForVsyncNotification() {
+ MOZ_ASSERT(NS_IsMainThread());
+ if (DidGetVsyncNotification()) {
+ return;
+ }
+
+ { // scope lock
+ MonitorAutoLock lock(mVsyncMonitor);
+ lock.Wait(TimeDuration::FromMilliseconds(kVsyncTimeoutMS));
+ }
+ }
+
+ bool DidGetVsyncNotification() {
+ MonitorAutoLock lock(mVsyncMonitor);
+ return mDidGetVsyncNotification;
+ }
+
+ void ResetVsyncNotification() {
+ MonitorAutoLock lock(mVsyncMonitor);
+ mDidGetVsyncNotification = false;
+ }
+
+ private:
+ bool mDidGetVsyncNotification;
+
+ private:
+ Monitor mVsyncMonitor;
+};
+
+class VsyncTester : public ::testing::Test {
+ protected:
+ explicit VsyncTester() {
+ gfxPlatform::GetPlatform();
+ mVsyncSource = gfxPlatform::GetPlatform()->GetHardwareVsync();
+ MOZ_RELEASE_ASSERT(mVsyncSource, "GFX: Vsync source not found.");
+ }
+
+ virtual ~VsyncTester() { mVsyncSource = nullptr; }
+
+ RefPtr<VsyncSource> mVsyncSource;
+};
+
+static void FlushMainThreadLoop() {
+ // Some tasks are pushed onto the main thread when adding vsync observers
+ // This function will ensure all tasks are executed on the main thread
+ // before returning.
+ nsCOMPtr<nsIThread> mainThread;
+ nsresult rv = NS_GetMainThread(getter_AddRefs(mainThread));
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+ rv = NS_OK;
+ bool processed = true;
+ while (processed && NS_SUCCEEDED(rv)) {
+ rv = mainThread->ProcessNextEvent(false, &processed);
+ }
+}
+
+// Tests that we can enable/disable vsync notifications
+TEST_F(VsyncTester, EnableVsync) {
+ VsyncSource::Display& globalDisplay = mVsyncSource->GetGlobalDisplay();
+ globalDisplay.DisableVsync();
+ ASSERT_FALSE(globalDisplay.IsVsyncEnabled());
+
+ globalDisplay.EnableVsync();
+ ASSERT_TRUE(globalDisplay.IsVsyncEnabled());
+
+ globalDisplay.DisableVsync();
+ ASSERT_FALSE(globalDisplay.IsVsyncEnabled());
+}
+
+// Test that if we have vsync enabled, the display should get vsync
+// notifications
+TEST_F(VsyncTester, CompositorGetVsyncNotifications) {
+ VsyncSource::Display& globalDisplay = mVsyncSource->GetGlobalDisplay();
+ globalDisplay.DisableVsync();
+ ASSERT_FALSE(globalDisplay.IsVsyncEnabled());
+
+ RefPtr<CompositorVsyncDispatcher> vsyncDispatcher =
+ new CompositorVsyncDispatcher();
+ RefPtr<TestVsyncObserver> testVsyncObserver = new TestVsyncObserver();
+
+ vsyncDispatcher->SetCompositorVsyncObserver(testVsyncObserver);
+ FlushMainThreadLoop();
+ ASSERT_TRUE(globalDisplay.IsVsyncEnabled());
+
+ testVsyncObserver->WaitForVsyncNotification();
+ ASSERT_TRUE(testVsyncObserver->DidGetVsyncNotification());
+
+ vsyncDispatcher = nullptr;
+ testVsyncObserver = nullptr;
+
+ globalDisplay.DisableVsync();
+ ASSERT_FALSE(globalDisplay.IsVsyncEnabled());
+}
+
+// Test that if we have vsync enabled, the parent refresh driver should get
+// notifications
+TEST_F(VsyncTester, ParentRefreshDriverGetVsyncNotifications) {
+ VsyncSource::Display& globalDisplay = mVsyncSource->GetGlobalDisplay();
+ globalDisplay.DisableVsync();
+ ASSERT_FALSE(globalDisplay.IsVsyncEnabled());
+
+ RefPtr<RefreshTimerVsyncDispatcher> vsyncDispatcher =
+ globalDisplay.GetRefreshTimerVsyncDispatcher();
+ ASSERT_TRUE(vsyncDispatcher != nullptr);
+
+ RefPtr<TestVsyncObserver> testVsyncObserver = new TestVsyncObserver();
+ vsyncDispatcher->SetParentRefreshTimer(testVsyncObserver);
+ ASSERT_TRUE(globalDisplay.IsVsyncEnabled());
+
+ testVsyncObserver->WaitForVsyncNotification();
+ ASSERT_TRUE(testVsyncObserver->DidGetVsyncNotification());
+ vsyncDispatcher->SetParentRefreshTimer(nullptr);
+
+ testVsyncObserver->ResetVsyncNotification();
+ testVsyncObserver->WaitForVsyncNotification();
+ ASSERT_FALSE(testVsyncObserver->DidGetVsyncNotification());
+
+ vsyncDispatcher = nullptr;
+ testVsyncObserver = nullptr;
+
+ globalDisplay.DisableVsync();
+ ASSERT_FALSE(globalDisplay.IsVsyncEnabled());
+}
+
+// Test that child refresh vsync observers get vsync notifications
+TEST_F(VsyncTester, ChildRefreshDriverGetVsyncNotifications) {
+ VsyncSource::Display& globalDisplay = mVsyncSource->GetGlobalDisplay();
+ globalDisplay.DisableVsync();
+ ASSERT_FALSE(globalDisplay.IsVsyncEnabled());
+
+ RefPtr<RefreshTimerVsyncDispatcher> vsyncDispatcher =
+ globalDisplay.GetRefreshTimerVsyncDispatcher();
+ ASSERT_TRUE(vsyncDispatcher != nullptr);
+
+ RefPtr<TestVsyncObserver> testVsyncObserver = new TestVsyncObserver();
+ vsyncDispatcher->AddChildRefreshTimer(testVsyncObserver);
+ ASSERT_TRUE(globalDisplay.IsVsyncEnabled());
+
+ testVsyncObserver->WaitForVsyncNotification();
+ ASSERT_TRUE(testVsyncObserver->DidGetVsyncNotification());
+
+ vsyncDispatcher->RemoveChildRefreshTimer(testVsyncObserver);
+ testVsyncObserver->ResetVsyncNotification();
+ testVsyncObserver->WaitForVsyncNotification();
+ ASSERT_FALSE(testVsyncObserver->DidGetVsyncNotification());
+
+ vsyncDispatcher = nullptr;
+ testVsyncObserver = nullptr;
+
+ globalDisplay.DisableVsync();
+ ASSERT_FALSE(globalDisplay.IsVsyncEnabled());
+}
+
+// Test that we can read the vsync rate
+TEST_F(VsyncTester, VsyncSourceHasVsyncRate) {
+ VsyncSource::Display& globalDisplay = mVsyncSource->GetGlobalDisplay();
+ TimeDuration vsyncRate = globalDisplay.GetVsyncRate();
+ ASSERT_NE(vsyncRate, TimeDuration::Forever());
+ ASSERT_GT(vsyncRate.ToMilliseconds(), 0);
+
+ globalDisplay.DisableVsync();
+ ASSERT_FALSE(globalDisplay.IsVsyncEnabled());
+}
diff --git a/gfx/tests/gtest/TextureHelper.h b/gfx/tests/gtest/TextureHelper.h
new file mode 100644
index 0000000000..db0817b3ad
--- /dev/null
+++ b/gfx/tests/gtest/TextureHelper.h
@@ -0,0 +1,161 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <vector>
+
+#include "Types.h"
+#include "gfxImageSurface.h"
+#include "gfxPlatform.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/gfx/Point.h"
+#include "mozilla/layers/BufferTexture.h"
+#include "mozilla/layers/LayersTypes.h"
+#include "mozilla/layers/TextureClient.h"
+#include "mozilla/layers/TextureHost.h"
+#ifdef XP_WIN
+# include "IMFYCbCrImage.h"
+# include "mozilla/gfx/DeviceManagerDx.h"
+# include "mozilla/layers/D3D11YCbCrImage.h"
+# include "mozilla/layers/TextureD3D11.h"
+# include "mozilla/layers/TextureDIB.h"
+#endif
+
+namespace mozilla {
+namespace layers {
+
+using gfx::BackendType;
+using gfx::IntSize;
+using gfx::SurfaceFormat;
+
+/**
+ * Create a YCbCrTextureClient according to the given backend.
+ */
+static already_AddRefed<TextureClient> CreateYCbCrTextureClientWithBackend(
+ LayersBackend aLayersBackend) {
+ TextureData* data = nullptr;
+ IntSize size = IntSize(200, 150);
+ IntSize ySize = IntSize(400, 300);
+
+ RefPtr<gfxImageSurface> ySurface =
+ new gfxImageSurface(ySize, SurfaceFormat::A8);
+ RefPtr<gfxImageSurface> cbSurface =
+ new gfxImageSurface(size, SurfaceFormat::A8);
+ RefPtr<gfxImageSurface> crSurface =
+ new gfxImageSurface(size, SurfaceFormat::A8);
+
+ PlanarYCbCrData clientData;
+ clientData.mYChannel = ySurface->Data();
+ clientData.mCbChannel = cbSurface->Data();
+ clientData.mCrChannel = crSurface->Data();
+ clientData.mYSize = ySurface->GetSize();
+ clientData.mPicSize = ySurface->GetSize();
+ clientData.mCbCrSize = cbSurface->GetSize();
+ clientData.mYStride = ySurface->Stride();
+ clientData.mCbCrStride = cbSurface->Stride();
+ clientData.mStereoMode = StereoMode::MONO;
+ clientData.mYSkip = 0;
+ clientData.mCbSkip = 0;
+ clientData.mCrSkip = 0;
+ clientData.mCrSkip = 0;
+ clientData.mPicX = 0;
+ clientData.mPicX = 0;
+
+ // Create YCbCrTexture for basic backend.
+ if (aLayersBackend == LayersBackend::LAYERS_BASIC) {
+ return TextureClient::CreateForYCbCr(
+ nullptr, clientData.GetPictureRect(), clientData.mYSize,
+ clientData.mYStride, clientData.mCbCrSize, clientData.mCbCrStride,
+ StereoMode::MONO, gfx::ColorDepth::COLOR_8, gfx::YUVColorSpace::BT601,
+ gfx::ColorRange::LIMITED, TextureFlags::DEALLOCATE_CLIENT);
+ }
+
+#ifdef XP_WIN
+ RefPtr<ID3D11Device> device = DeviceManagerDx::Get()->GetImageDevice();
+
+ if (device && aLayersBackend == LayersBackend::LAYERS_D3D11) {
+ DXGIYCbCrTextureAllocationHelper helper(clientData, TextureFlags::DEFAULT,
+ device);
+ RefPtr<TextureClient> texture = helper.Allocate(nullptr);
+ return texture.forget();
+ }
+#endif
+
+ if (data) {
+ return MakeAndAddRef<TextureClient>(data, TextureFlags::DEALLOCATE_CLIENT,
+ nullptr);
+ }
+
+ return nullptr;
+}
+
+/**
+ * Create a TextureClient according to the given backend.
+ */
+static already_AddRefed<TextureClient> CreateTextureClientWithBackend(
+ LayersBackend aLayersBackend) {
+ TextureData* data = nullptr;
+ SurfaceFormat format = gfxPlatform::GetPlatform()->Optimal2DFormatForContent(
+ gfxContentType::COLOR_ALPHA);
+ BackendType moz2DBackend =
+ gfxPlatform::GetPlatform()->GetContentBackendFor(aLayersBackend);
+ TextureAllocationFlags allocFlags = TextureAllocationFlags::ALLOC_DEFAULT;
+ IntSize size = IntSize(400, 300);
+ TextureFlags textureFlags = TextureFlags::DEALLOCATE_CLIENT;
+
+ if (!gfx::Factory::AllowedSurfaceSize(size)) {
+ return nullptr;
+ }
+
+#ifdef XP_WIN
+ if (aLayersBackend == LayersBackend::LAYERS_D3D11 &&
+ (moz2DBackend == BackendType::DIRECT2D ||
+ moz2DBackend == BackendType::DIRECT2D1_1)) {
+ // Create D3D11TextureData.
+ data = D3D11TextureData::Create(size, format, allocFlags);
+ } else if (!data && format == SurfaceFormat::B8G8R8X8 &&
+ moz2DBackend == BackendType::CAIRO) {
+ // Create DIBTextureData.
+ data = DIBTextureData::Create(size, format, nullptr);
+ }
+#endif
+
+ if (!data && aLayersBackend == LayersBackend::LAYERS_BASIC) {
+ // Create BufferTextureData.
+ data = BufferTextureData::Create(size, format, moz2DBackend, aLayersBackend,
+ textureFlags, allocFlags, nullptr);
+ }
+
+ if (data) {
+ return MakeAndAddRef<TextureClient>(data, textureFlags, nullptr);
+ }
+
+ return nullptr;
+}
+
+/**
+ * Create a TextureHost according to the given TextureClient.
+ */
+already_AddRefed<TextureHost> CreateTextureHostWithBackend(
+ TextureClient* aClient, ISurfaceAllocator* aDeallocator,
+ LayersBackend& aLayersBackend) {
+ if (!aClient) {
+ return nullptr;
+ }
+
+ // client serialization
+ SurfaceDescriptor descriptor;
+ ReadLockDescriptor readLock = null_t();
+ RefPtr<TextureHost> textureHost;
+
+ aClient->ToSurfaceDescriptor(descriptor);
+
+ wr::MaybeExternalImageId id = Nothing();
+ return TextureHost::Create(descriptor, readLock, aDeallocator, aLayersBackend,
+ aClient->GetFlags(), id);
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/tests/gtest/gfxSurfaceRefCountTest.cpp b/gfx/tests/gtest/gfxSurfaceRefCountTest.cpp
new file mode 100644
index 0000000000..77ab5aeb91
--- /dev/null
+++ b/gfx/tests/gtest/gfxSurfaceRefCountTest.cpp
@@ -0,0 +1,155 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <stdio.h>
+
+#include "gtest/gtest.h"
+
+#include "gfxASurface.h"
+#include "gfxImageSurface.h"
+
+#include "nsISupportsUtils.h" // for NS_ADDREF
+
+#include "cairo.h"
+
+static int GetASurfaceRefCount(gfxASurface* s) {
+ NS_ADDREF(s);
+ return s->Release();
+}
+
+static int CheckInt(int value, int expected) {
+ if (value != expected) {
+ fprintf(stderr, "Expected %d got %d\n", expected, value);
+ return 1;
+ }
+
+ return 0;
+}
+
+static int CheckPointer(void* value, void* expected) {
+ if (value != expected) {
+ fprintf(stderr, "Expected %p got %p\n", expected, value);
+ return 1;
+ }
+
+ return 0;
+}
+
+static cairo_user_data_key_t destruction_key;
+static void SurfaceDestroyNotifier(void* data) { *(int*)data = 1; }
+
+static int TestNewSurface() {
+ int failures = 0;
+ int destroyed = 0;
+
+ RefPtr<gfxASurface> s =
+ new gfxImageSurface(mozilla::gfx::IntSize(10, 10),
+ mozilla::gfx::SurfaceFormat::A8R8G8B8_UINT32);
+ cairo_surface_t* cs = s->CairoSurface();
+
+ cairo_surface_set_user_data(cs, &destruction_key, &destroyed,
+ SurfaceDestroyNotifier);
+
+ failures += CheckInt(GetASurfaceRefCount(s.get()), 1);
+ failures += CheckInt(cairo_surface_get_reference_count(cs), 1);
+ failures += CheckInt(destroyed, 0);
+
+ cairo_surface_reference(cs);
+
+ failures += CheckInt(GetASurfaceRefCount(s.get()), 2);
+ failures += CheckInt(cairo_surface_get_reference_count(cs), 2);
+ failures += CheckInt(destroyed, 0);
+
+ gfxASurface* savedWrapper = s.get();
+
+ s = nullptr;
+
+ failures += CheckInt(cairo_surface_get_reference_count(cs), 1);
+ failures += CheckInt(destroyed, 0);
+
+ s = gfxASurface::Wrap(cs);
+
+ failures += CheckPointer(s.get(), savedWrapper);
+ failures += CheckInt(GetASurfaceRefCount(s.get()), 2);
+ failures += CheckInt(cairo_surface_get_reference_count(cs), 2);
+ failures += CheckInt(destroyed, 0);
+
+ cairo_surface_destroy(cs);
+
+ failures += CheckInt(GetASurfaceRefCount(s.get()), 1);
+ failures += CheckInt(cairo_surface_get_reference_count(cs), 1);
+ failures += CheckInt(destroyed, 0);
+
+ s = nullptr;
+
+ failures += CheckInt(destroyed, 1);
+
+ return failures;
+}
+
+static int TestExistingSurface() {
+ int failures = 0;
+ int destroyed = 0;
+
+ cairo_surface_t* cs = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 10, 10);
+
+ cairo_surface_set_user_data(cs, &destruction_key, &destroyed,
+ SurfaceDestroyNotifier);
+
+ failures += CheckInt(cairo_surface_get_reference_count(cs), 1);
+ failures += CheckInt(destroyed, 0);
+
+ RefPtr<gfxASurface> s = gfxASurface::Wrap(cs);
+
+ failures += CheckInt(GetASurfaceRefCount(s.get()), 2);
+
+ cairo_surface_reference(cs);
+
+ failures += CheckInt(GetASurfaceRefCount(s.get()), 3);
+ failures += CheckInt(cairo_surface_get_reference_count(cs), 3);
+ failures += CheckInt(destroyed, 0);
+
+ gfxASurface* savedWrapper = s.get();
+
+ s = nullptr;
+
+ failures += CheckInt(cairo_surface_get_reference_count(cs), 2);
+ failures += CheckInt(destroyed, 0);
+
+ s = gfxASurface::Wrap(cs);
+
+ failures += CheckPointer(s.get(), savedWrapper);
+ failures += CheckInt(GetASurfaceRefCount(s.get()), 3);
+ failures += CheckInt(cairo_surface_get_reference_count(cs), 3);
+ failures += CheckInt(destroyed, 0);
+
+ cairo_surface_destroy(cs);
+
+ failures += CheckInt(GetASurfaceRefCount(s.get()), 2);
+ failures += CheckInt(cairo_surface_get_reference_count(cs), 2);
+ failures += CheckInt(destroyed, 0);
+
+ s = nullptr;
+
+ failures += CheckInt(cairo_surface_get_reference_count(cs), 1);
+ failures += CheckInt(destroyed, 0);
+
+ cairo_surface_destroy(cs);
+
+ failures += CheckInt(destroyed, 1);
+
+ return failures;
+}
+
+TEST(Gfx, SurfaceRefCount)
+{
+ int fail;
+
+ fail = TestNewSurface();
+ EXPECT_TRUE(fail == 0) << "TestNewSurface: " << fail << " failures";
+ fail = TestExistingSurface();
+ EXPECT_TRUE(fail == 0) << "TestExistingSurface: " << fail << " failures";
+}
diff --git a/gfx/tests/gtest/moz.build b/gfx/tests/gtest/moz.build
new file mode 100644
index 0000000000..450910d833
--- /dev/null
+++ b/gfx/tests/gtest/moz.build
@@ -0,0 +1,91 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+UNIFIED_SOURCES += [
+ "gfxSurfaceRefCountTest.cpp",
+ "MockWidget.cpp",
+ "PolygonTestUtils.cpp",
+ "TestArena.cpp",
+ "TestArrayView.cpp",
+ "TestBSPTree.cpp",
+ "TestBufferRotation.cpp",
+ "TestColorNames.cpp",
+ "TestConfigManager.cpp",
+ "TestGfxWidgets.cpp",
+ "TestLayers.cpp",
+ "TestMatrix.cpp",
+ "TestMoz2D.cpp",
+ "TestPolygon.cpp",
+ "TestQcms.cpp",
+ "TestRegion.cpp",
+ "TestSkipChars.cpp",
+ "TestSwizzle.cpp",
+ "TestTextures.cpp",
+ "TestTreeTraversal.cpp",
+]
+
+# skip the test on windows10-aarch64 due to perma-crash - bug 1544961
+if not (CONFIG["OS_TARGET"] == "WINNT" and CONFIG["CPU_ARCH"] == "aarch64"):
+ UNIFIED_SOURCES += [
+ "TestVsync.cpp",
+ ]
+
+if CONFIG["OS_TARGET"] != "Android":
+ UNIFIED_SOURCES += [
+ "TestCompositor.cpp",
+ "TestRect.cpp",
+ "TestTextureCompatibility.cpp",
+ ]
+
+UNIFIED_SOURCES += [
+ "/gfx/2d/unittest/%s" % p
+ for p in [
+ "TestBase.cpp",
+ "TestBugs.cpp",
+ "TestCairo.cpp",
+ "TestPoint.cpp",
+ "TestScaling.cpp",
+ ]
+]
+
+# not UNIFIED_SOURCES because layout_common_table_test.cc has classes
+# in an anonymous namespace which result in a GCC error when used in
+# tests (e g. "error: 'ScriptListTableTest_TestSuccess_Test' has a field
+# 'ScriptListTableTest_TestSuccess_Test::<anonymous>' whose type uses
+# the anonymous namespace").
+SOURCES += [
+ "/gfx/ots/tests/%s" % p
+ for p in [
+ "cff_charstring_test.cc",
+ "layout_common_table_test.cc",
+ ]
+]
+
+# ICC profiles used for verifying QCMS transformations. The copyright
+# notice embedded in the profiles should be reviewed to ensure there are
+# no known restrictions on distribution.
+TEST_HARNESS_FILES.gtest += [
+ "../../qcms/profiles/lcms_samsung_syncmaster.icc",
+ "../../qcms/profiles/lcms_thinkpad_w540.icc",
+]
+
+include("/ipc/chromium/chromium-config.mozbuild")
+
+LOCAL_INCLUDES += [
+ "/gfx/2d",
+ "/gfx/2d/unittest",
+ "/gfx/config",
+ "/gfx/layers",
+ "/gfx/ots/src",
+ "/gfx/qcms",
+]
+
+FINAL_LIBRARY = "xul-gtest"
+
+CXXFLAGS += CONFIG["MOZ_CAIRO_CFLAGS"]
+
+if CONFIG["CC_TYPE"] in ("clang", "gcc"):
+ CXXFLAGS += ["-Wno-error=shadow"]