diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /gfx/tests/gtest | |
parent | Initial commit. (diff) | |
download | firefox-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')
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"] |