michael@0: /* vim:set ts=2 sw=2 sts=2 et: */ michael@0: /* Any copyright is dedicated to the Public Domain. michael@0: * http://creativecommons.org/publicdomain/zero/1.0/ michael@0: */ michael@0: michael@0: #include "TestLayers.h" michael@0: #include "gtest/gtest.h" michael@0: #include "gmock/gmock.h" michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::gfx; michael@0: using namespace mozilla::layers; michael@0: michael@0: class TestLayerManager: public LayerManager { michael@0: public: michael@0: TestLayerManager() michael@0: : LayerManager() michael@0: {} michael@0: michael@0: virtual bool EndEmptyTransaction(EndTransactionFlags aFlags = END_DEFAULT) { return false; } michael@0: virtual already_AddRefed CreateContainerLayer() { return nullptr; } michael@0: virtual void GetBackendName(nsAString& aName) {} michael@0: virtual LayersBackend GetBackendType() { return LayersBackend::LAYERS_BASIC; } michael@0: virtual void BeginTransaction() {} michael@0: virtual already_AddRefed CreateImageLayer() { return nullptr; } michael@0: virtual void SetRoot(Layer* aLayer) {} michael@0: virtual already_AddRefed CreateColorLayer() { return nullptr; } michael@0: virtual void BeginTransactionWithTarget(gfxContext* aTarget) {} michael@0: virtual already_AddRefed CreateCanvasLayer() { return nullptr; } michael@0: virtual void EndTransaction(DrawThebesLayerCallback aCallback, michael@0: void* aCallbackData, michael@0: EndTransactionFlags aFlags = END_DEFAULT) {} michael@0: virtual int32_t GetMaxTextureSize() const { return 0; } michael@0: virtual already_AddRefed CreateThebesLayer() { return nullptr; } michael@0: }; michael@0: michael@0: class TestContainerLayer: public ContainerLayer { michael@0: public: michael@0: TestContainerLayer(LayerManager* aManager) michael@0: : ContainerLayer(aManager, nullptr) michael@0: {} michael@0: michael@0: virtual const char* Name() const { michael@0: return "TestContainerLayer"; michael@0: } michael@0: michael@0: virtual LayerType GetType() const { michael@0: return TYPE_CONTAINER; michael@0: } michael@0: michael@0: virtual void ComputeEffectiveTransforms(const Matrix4x4& aTransformToSurface) { michael@0: DefaultComputeEffectiveTransforms(aTransformToSurface); michael@0: } michael@0: }; michael@0: michael@0: class TestThebesLayer: public ThebesLayer { michael@0: public: michael@0: TestThebesLayer(LayerManager* aManager) michael@0: : ThebesLayer(aManager, nullptr) michael@0: {} michael@0: michael@0: virtual const char* Name() const { michael@0: return "TestThebesLayer"; michael@0: } michael@0: michael@0: virtual LayerType GetType() const { michael@0: return TYPE_THEBES; michael@0: } michael@0: michael@0: virtual void InvalidateRegion(const nsIntRegion& aRegion) { michael@0: MOZ_CRASH(); michael@0: } michael@0: }; michael@0: michael@0: class TestUserData: public LayerUserData { michael@0: public: michael@0: MOCK_METHOD0(Die, void()); michael@0: virtual ~TestUserData() { Die(); } michael@0: }; michael@0: michael@0: michael@0: TEST(Layers, LayerConstructor) { michael@0: TestContainerLayer layer(nullptr); michael@0: } michael@0: michael@0: TEST(Layers, Defaults) { michael@0: TestContainerLayer layer(nullptr); michael@0: ASSERT_EQ(1.0, layer.GetOpacity()); michael@0: ASSERT_EQ(1.0f, layer.GetPostXScale()); michael@0: ASSERT_EQ(1.0f, layer.GetPostYScale()); michael@0: michael@0: ASSERT_EQ(nullptr, layer.GetNextSibling()); michael@0: ASSERT_EQ(nullptr, layer.GetPrevSibling()); michael@0: ASSERT_EQ(nullptr, layer.GetFirstChild()); michael@0: ASSERT_EQ(nullptr, layer.GetLastChild()); michael@0: } michael@0: michael@0: TEST(Layers, Transform) { michael@0: TestContainerLayer layer(nullptr); michael@0: michael@0: Matrix4x4 identity; michael@0: ASSERT_EQ(true, identity.IsIdentity()); michael@0: michael@0: ASSERT_EQ(identity, layer.GetTransform()); michael@0: } michael@0: michael@0: TEST(Layers, Type) { michael@0: TestContainerLayer layer(nullptr); michael@0: ASSERT_EQ(nullptr, layer.AsThebesLayer()); michael@0: ASSERT_EQ(nullptr, layer.AsRefLayer()); michael@0: ASSERT_EQ(nullptr, layer.AsColorLayer()); michael@0: } michael@0: michael@0: TEST(Layers, UserData) { michael@0: TestContainerLayer* layerPtr = new TestContainerLayer(nullptr); michael@0: TestContainerLayer& layer = *layerPtr; michael@0: michael@0: void* key1 = (void*)1; michael@0: void* key2 = (void*)2; michael@0: void* key3 = (void*)3; michael@0: michael@0: TestUserData* data1 = new TestUserData; michael@0: TestUserData* data2 = new TestUserData; michael@0: TestUserData* data3 = new TestUserData; michael@0: michael@0: ASSERT_EQ(nullptr, layer.GetUserData(key1)); michael@0: ASSERT_EQ(nullptr, layer.GetUserData(key2)); michael@0: ASSERT_EQ(nullptr, layer.GetUserData(key3)); michael@0: michael@0: layer.SetUserData(key1, data1); michael@0: layer.SetUserData(key2, data2); michael@0: layer.SetUserData(key3, data3); michael@0: michael@0: // Also checking that the user data is returned but not free'd michael@0: ASSERT_EQ(data1, layer.RemoveUserData(key1).forget()); michael@0: ASSERT_EQ(data2, layer.RemoveUserData(key2).forget()); michael@0: ASSERT_EQ(data3, layer.RemoveUserData(key3).forget()); michael@0: michael@0: layer.SetUserData(key1, data1); michael@0: layer.SetUserData(key2, data2); michael@0: layer.SetUserData(key3, data3); michael@0: michael@0: // Layer has ownership of data1-3, check that they are destroyed michael@0: EXPECT_CALL(*data1, Die()); michael@0: EXPECT_CALL(*data2, Die()); michael@0: EXPECT_CALL(*data3, Die()); michael@0: delete layerPtr; michael@0: michael@0: } michael@0: michael@0: static michael@0: already_AddRefed CreateLayer(char aLayerType, LayerManager* aManager) { michael@0: nsRefPtr layer = nullptr; michael@0: if (aLayerType == 'c') { michael@0: layer = new TestContainerLayer(aManager); michael@0: } else if (aLayerType == 't') { michael@0: layer = new TestThebesLayer(aManager); michael@0: } michael@0: return layer.forget(); michael@0: } michael@0: michael@0: already_AddRefed CreateLayerTree( michael@0: const char* aLayerTreeDescription, michael@0: nsIntRegion* aVisibleRegions, michael@0: const gfx3DMatrix* aTransforms, michael@0: nsRefPtr& manager, michael@0: nsTArray >& aLayersOut) { michael@0: michael@0: aLayersOut.Clear(); michael@0: michael@0: manager = new TestLayerManager(); michael@0: michael@0: nsRefPtr rootLayer = nullptr; michael@0: nsRefPtr parentContainerLayer = nullptr; michael@0: nsRefPtr lastLayer = nullptr; michael@0: int layerNumber = 0; michael@0: for (size_t i = 0; i < strlen(aLayerTreeDescription); i++) { michael@0: if (aLayerTreeDescription[i] == '(') { michael@0: if (!lastLayer) { michael@0: printf("Syntax error, likely '(' character isn't preceded by a container.\n"); michael@0: MOZ_CRASH(); michael@0: } michael@0: parentContainerLayer = lastLayer->AsContainerLayer(); michael@0: if (!parentContainerLayer) { michael@0: printf("Layer before '(' must be a container.\n"); michael@0: MOZ_CRASH(); michael@0: } michael@0: } else if (aLayerTreeDescription[i] == ')') { michael@0: parentContainerLayer = parentContainerLayer->GetParent(); michael@0: lastLayer = nullptr; michael@0: } else { michael@0: nsRefPtr layer = CreateLayer(aLayerTreeDescription[i], manager.get()); michael@0: if (aVisibleRegions) { michael@0: layer->SetVisibleRegion(aVisibleRegions[layerNumber]); michael@0: } michael@0: if (aTransforms) { michael@0: Matrix4x4 transform; michael@0: ToMatrix4x4(aTransforms[layerNumber], transform); michael@0: layer->SetBaseTransform(transform); michael@0: } michael@0: aLayersOut.AppendElement(layer); michael@0: layerNumber++; michael@0: if (rootLayer && !parentContainerLayer) { michael@0: MOZ_CRASH(); michael@0: } michael@0: if (!rootLayer) { michael@0: rootLayer = layer; michael@0: } michael@0: if (parentContainerLayer) { michael@0: parentContainerLayer->InsertAfter(layer, parentContainerLayer->GetLastChild()); michael@0: layer->SetParent(parentContainerLayer); michael@0: } michael@0: lastLayer = layer; michael@0: } michael@0: } michael@0: if (rootLayer) { michael@0: rootLayer->ComputeEffectiveTransforms(Matrix4x4()); michael@0: } michael@0: return rootLayer.forget(); michael@0: } michael@0: michael@0: TEST(Layers, LayerTree) { michael@0: const char* layerTreeSyntax = "c(c(tt))"; michael@0: nsIntRegion layerVisibleRegion[] = { michael@0: nsIntRegion(nsIntRect(0,0,100,100)), michael@0: nsIntRegion(nsIntRect(0,0,100,100)), michael@0: nsIntRegion(nsIntRect(0,0,100,100)), michael@0: nsIntRegion(nsIntRect(10,10,20,20)), michael@0: }; michael@0: gfx3DMatrix transforms[] = { michael@0: gfx3DMatrix(), michael@0: gfx3DMatrix(), michael@0: gfx3DMatrix(), michael@0: gfx3DMatrix(), michael@0: }; michael@0: nsTArray > layers; michael@0: michael@0: nsRefPtr lm; michael@0: nsRefPtr root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, transforms, lm, layers); michael@0: michael@0: // B2G g++ doesn't like ASSERT_NE with nullptr directly. It thinks it's michael@0: // an int. michael@0: Layer* nullLayer = nullptr; michael@0: ASSERT_NE(nullLayer, layers[0]->AsContainerLayer()); michael@0: ASSERT_NE(nullLayer, layers[1]->AsContainerLayer()); michael@0: ASSERT_NE(nullLayer, layers[2]->AsThebesLayer()); michael@0: ASSERT_NE(nullLayer, layers[3]->AsThebesLayer()); michael@0: } michael@0: michael@0: static void ValidateTreePointers(Layer* aLayer) { michael@0: if (aLayer->GetNextSibling()) { michael@0: ASSERT_EQ(aLayer, aLayer->GetNextSibling()->GetPrevSibling()); michael@0: } else if (aLayer->GetParent()) { michael@0: ASSERT_EQ(aLayer, aLayer->GetParent()->GetLastChild()); michael@0: } michael@0: if (aLayer->GetPrevSibling()) { michael@0: ASSERT_EQ(aLayer, aLayer->GetPrevSibling()->GetNextSibling()); michael@0: } else if (aLayer->GetParent()) { michael@0: ASSERT_EQ(aLayer, aLayer->GetParent()->GetFirstChild()); michael@0: } michael@0: if (aLayer->GetFirstChild()) { michael@0: ASSERT_EQ(aLayer, aLayer->GetFirstChild()->GetParent()); michael@0: } michael@0: if (aLayer->GetLastChild()) { michael@0: ASSERT_EQ(aLayer, aLayer->GetLastChild()->GetParent()); michael@0: } michael@0: } michael@0: michael@0: static void ValidateTreePointers(nsTArray >& aLayers) { michael@0: for (uint32_t i = 0; i < aLayers.Length(); i++) { michael@0: ValidateTreePointers(aLayers[i]); michael@0: } michael@0: } michael@0: michael@0: TEST(Layers, RepositionChild) { michael@0: const char* layerTreeSyntax = "c(ttt)"; michael@0: michael@0: nsTArray > layers; michael@0: nsRefPtr lm; michael@0: nsRefPtr root = CreateLayerTree(layerTreeSyntax, nullptr, nullptr, lm, layers); michael@0: ContainerLayer* parent = root->AsContainerLayer(); michael@0: ValidateTreePointers(layers); michael@0: michael@0: // tree is currently like this (using indexes into layers): michael@0: // 0 michael@0: // 1 2 3 michael@0: ASSERT_EQ(layers[2], layers[1]->GetNextSibling()); michael@0: ASSERT_EQ(layers[3], layers[2]->GetNextSibling()); michael@0: ASSERT_EQ(nullptr, layers[3]->GetNextSibling()); michael@0: michael@0: parent->RepositionChild(layers[1], layers[3]); michael@0: ValidateTreePointers(layers); michael@0: michael@0: // now the tree is like this: michael@0: // 0 michael@0: // 2 3 1 michael@0: ASSERT_EQ(layers[3], layers[2]->GetNextSibling()); michael@0: ASSERT_EQ(layers[1], layers[3]->GetNextSibling()); michael@0: ASSERT_EQ(nullptr, layers[1]->GetNextSibling()); michael@0: michael@0: parent->RepositionChild(layers[3], layers[2]); michael@0: ValidateTreePointers(layers); michael@0: michael@0: // no change michael@0: ASSERT_EQ(layers[3], layers[2]->GetNextSibling()); michael@0: ASSERT_EQ(layers[1], layers[3]->GetNextSibling()); michael@0: ASSERT_EQ(nullptr, layers[1]->GetNextSibling()); michael@0: michael@0: parent->RepositionChild(layers[3], layers[1]); michael@0: ValidateTreePointers(layers); michael@0: michael@0: // 0 michael@0: // 2 1 3 michael@0: ASSERT_EQ(layers[1], layers[2]->GetNextSibling()); michael@0: ASSERT_EQ(layers[3], layers[1]->GetNextSibling()); michael@0: ASSERT_EQ(nullptr, layers[3]->GetNextSibling()); michael@0: michael@0: parent->RepositionChild(layers[3], nullptr); michael@0: ValidateTreePointers(layers); michael@0: michael@0: // 0 michael@0: // 3 2 1 michael@0: ASSERT_EQ(layers[2], layers[3]->GetNextSibling()); michael@0: ASSERT_EQ(layers[1], layers[2]->GetNextSibling()); michael@0: ASSERT_EQ(nullptr, layers[1]->GetNextSibling()); michael@0: }