diff -r 000000000000 -r 6474c204b198 gfx/tests/gtest/TestLayers.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gfx/tests/gtest/TestLayers.cpp Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,326 @@ +/* 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" + +using namespace mozilla; +using namespace mozilla::gfx; +using namespace mozilla::layers; + +class TestLayerManager: public LayerManager { +public: + TestLayerManager() + : LayerManager() + {} + + virtual bool EndEmptyTransaction(EndTransactionFlags aFlags = END_DEFAULT) { return false; } + virtual already_AddRefed CreateContainerLayer() { return nullptr; } + virtual void GetBackendName(nsAString& aName) {} + virtual LayersBackend GetBackendType() { return LayersBackend::LAYERS_BASIC; } + virtual void BeginTransaction() {} + virtual already_AddRefed CreateImageLayer() { return nullptr; } + virtual void SetRoot(Layer* aLayer) {} + virtual already_AddRefed CreateColorLayer() { return nullptr; } + virtual void BeginTransactionWithTarget(gfxContext* aTarget) {} + virtual already_AddRefed CreateCanvasLayer() { return nullptr; } + virtual void EndTransaction(DrawThebesLayerCallback aCallback, + void* aCallbackData, + EndTransactionFlags aFlags = END_DEFAULT) {} + virtual int32_t GetMaxTextureSize() const { return 0; } + virtual already_AddRefed CreateThebesLayer() { return nullptr; } +}; + +class TestContainerLayer: public ContainerLayer { +public: + 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 TestThebesLayer: public ThebesLayer { +public: + TestThebesLayer(LayerManager* aManager) + : ThebesLayer(aManager, nullptr) + {} + + virtual const char* Name() const { + return "TestThebesLayer"; + } + + virtual LayerType GetType() const { + return TYPE_THEBES; + } + + virtual void InvalidateRegion(const nsIntRegion& aRegion) { + MOZ_CRASH(); + } +}; + +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.AsThebesLayer()); + ASSERT_EQ(nullptr, layer.AsRefLayer()); + ASSERT_EQ(nullptr, layer.AsColorLayer()); +} + +TEST(Layers, UserData) { + TestContainerLayer* layerPtr = new TestContainerLayer(nullptr); + TestContainerLayer& layer = *layerPtr; + + void* key1 = (void*)1; + void* key2 = (void*)2; + void* key3 = (void*)3; + + TestUserData* data1 = new TestUserData; + TestUserData* data2 = new TestUserData; + TestUserData* data3 = new TestUserData; + + ASSERT_EQ(nullptr, layer.GetUserData(key1)); + ASSERT_EQ(nullptr, layer.GetUserData(key2)); + ASSERT_EQ(nullptr, layer.GetUserData(key3)); + + layer.SetUserData(key1, data1); + layer.SetUserData(key2, data2); + layer.SetUserData(key3, data3); + + // Also checking that the user data is returned but not free'd + ASSERT_EQ(data1, layer.RemoveUserData(key1).forget()); + ASSERT_EQ(data2, layer.RemoveUserData(key2).forget()); + ASSERT_EQ(data3, layer.RemoveUserData(key3).forget()); + + layer.SetUserData(key1, data1); + layer.SetUserData(key2, data2); + layer.SetUserData(key3, data3); + + // Layer has ownership of data1-3, check that they are destroyed + EXPECT_CALL(*data1, Die()); + EXPECT_CALL(*data2, Die()); + EXPECT_CALL(*data3, Die()); + delete layerPtr; + +} + +static +already_AddRefed CreateLayer(char aLayerType, LayerManager* aManager) { + nsRefPtr layer = nullptr; + if (aLayerType == 'c') { + layer = new TestContainerLayer(aManager); + } else if (aLayerType == 't') { + layer = new TestThebesLayer(aManager); + } + return layer.forget(); +} + +already_AddRefed CreateLayerTree( + const char* aLayerTreeDescription, + nsIntRegion* aVisibleRegions, + const gfx3DMatrix* aTransforms, + nsRefPtr& manager, + nsTArray >& aLayersOut) { + + aLayersOut.Clear(); + + manager = new TestLayerManager(); + + nsRefPtr rootLayer = nullptr; + nsRefPtr parentContainerLayer = nullptr; + nsRefPtr 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 { + nsRefPtr layer = CreateLayer(aLayerTreeDescription[i], manager.get()); + if (aVisibleRegions) { + layer->SetVisibleRegion(aVisibleRegions[layerNumber]); + } + if (aTransforms) { + Matrix4x4 transform; + ToMatrix4x4(aTransforms[layerNumber], transform); + layer->SetBaseTransform(transform); + } + 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()); + } + return rootLayer.forget(); +} + +TEST(Layers, LayerTree) { + const char* layerTreeSyntax = "c(c(tt))"; + nsIntRegion layerVisibleRegion[] = { + nsIntRegion(nsIntRect(0,0,100,100)), + nsIntRegion(nsIntRect(0,0,100,100)), + nsIntRegion(nsIntRect(0,0,100,100)), + nsIntRegion(nsIntRect(10,10,20,20)), + }; + gfx3DMatrix transforms[] = { + gfx3DMatrix(), + gfx3DMatrix(), + gfx3DMatrix(), + gfx3DMatrix(), + }; + nsTArray > layers; + + nsRefPtr lm; + nsRefPtr 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]->AsThebesLayer()); + ASSERT_NE(nullLayer, layers[3]->AsThebesLayer()); +} + +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 >& aLayers) { + for (uint32_t i = 0; i < aLayers.Length(); i++) { + ValidateTreePointers(aLayers[i]); + } +} + +TEST(Layers, RepositionChild) { + const char* layerTreeSyntax = "c(ttt)"; + + nsTArray > layers; + nsRefPtr lm; + nsRefPtr 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()); +}