/* 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()); }