// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <memory>
#include <utility>

#include "cc/animation/animation_host.h"
#include "cc/layers/mirror_layer.h"
#include "cc/layers/mirror_layer_impl.h"
#include "cc/test/fake_impl_task_runner_provider.h"
#include "cc/test/fake_layer_tree_host.h"
#include "cc/test/fake_layer_tree_host_client.h"
#include "cc/test/fake_layer_tree_host_impl.h"
#include "cc/test/test_task_graph_runner.h"
#include "cc/trees/tree_synchronizer.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace cc {
namespace {

class MirrorLayerTest : public testing::Test {
 public:
  MirrorLayerTest() : host_impl_(&task_runner_provider_, &task_graph_runner_) {}

  // Synchronizes |layer_tree_host_| and |host_impl_| and pushes surface ids.
  void SynchronizeTrees() {
    TreeSynchronizer::PushLayerProperties(
        *layer_tree_host_->GetPendingCommitState(),
        layer_tree_host_->GetThreadUnsafeCommitState(),
        host_impl_.pending_tree());
  }

 protected:
  void SetUp() override {
    animation_host_ = AnimationHost::CreateForTesting(ThreadInstance::MAIN);
    layer_tree_host_ = FakeLayerTreeHost::Create(
        &fake_client_, &task_graph_runner_, animation_host_.get());
    layer_tree_host_->SetViewportRectAndScale(gfx::Rect(10, 10), 1.f,
                                              viz::LocalSurfaceId());
    host_impl_.CreatePendingTree();
  }

  void TearDown() override {
    layer_tree_host_->SetRootLayer(nullptr);
    layer_tree_host_ = nullptr;
  }

  FakeLayerTreeHostClient fake_client_;
  FakeImplTaskRunnerProvider task_runner_provider_;
  TestTaskGraphRunner task_graph_runner_;
  std::unique_ptr<AnimationHost> animation_host_;
  std::unique_ptr<FakeLayerTreeHost> layer_tree_host_;
  FakeLayerTreeHostImpl host_impl_;
};

// This test verifies that MirrorLayer properties are pushed across to
// MirrorLayerImpl.
TEST_F(MirrorLayerTest, PushProperties) {
  auto root = Layer::Create();
  layer_tree_host_->SetRootLayer(root);

  auto mirrored = Layer::Create();
  root->AddChild(mirrored);
  auto mirror = MirrorLayer::Create(mirrored);
  root->AddChild(mirror);

  EXPECT_EQ(1, mirrored->mirror_count());
  EXPECT_EQ(mirrored.get(), mirror->mirrored_layer());

  auto root_impl = LayerImpl::Create(host_impl_.pending_tree(), root->id());
  auto mirrored_impl =
      LayerImpl::Create(host_impl_.pending_tree(), mirrored->id());
  auto mirror_impl =
      MirrorLayerImpl::Create(host_impl_.pending_tree(), mirror->id());

  // Verify that impl layers have default property values.
  EXPECT_EQ(0, mirror_impl->mirrored_layer_id());

  SynchronizeTrees();

  // Verify that property values are pushed to impl layers.
  EXPECT_EQ(mirrored_impl->id(), mirror_impl->mirrored_layer_id());
}

// This test verifies adding/removing mirror layers updates mirror count
// properly and sets appropriate bits on the layer tree host.
TEST_F(MirrorLayerTest, MirrorCount) {
  auto mirrored = Layer::Create();
  mirrored->SetLayerTreeHost(layer_tree_host_.get());

  layer_tree_host_->WillCommit(/*completion_event=*/nullptr,
                               /*has_updates=*/true);
  layer_tree_host_->CommitComplete({base::TimeTicks(), base::TimeTicks::Now()});
  layer_tree_host_->property_trees()->set_needs_rebuild(false);
  EXPECT_EQ(0, mirrored->mirror_count());

  // Creating the first mirror layer should trigger property trees rebuild.
  auto mirror1 = MirrorLayer::Create(mirrored);
  EXPECT_EQ(1, mirrored->mirror_count());
  EXPECT_EQ(mirrored.get(), mirror1->mirrored_layer());
  EXPECT_TRUE(layer_tree_host_->property_trees()->needs_rebuild());
  EXPECT_TRUE(
      const_cast<const FakeLayerTreeHost*>(layer_tree_host_.get())
          ->pending_commit_state()
          ->layers_that_should_push_properties.contains(mirrored.get()));
  layer_tree_host_->property_trees()->set_needs_rebuild(false);

  // Creating a second mirror layer should not trigger property trees rebuild.
  auto mirror2 = MirrorLayer::Create(mirrored);
  EXPECT_EQ(2, mirrored->mirror_count());
  EXPECT_EQ(mirrored.get(), mirror2->mirrored_layer());
  EXPECT_FALSE(layer_tree_host_->property_trees()->needs_rebuild());
  EXPECT_TRUE(
      const_cast<const FakeLayerTreeHost*>(layer_tree_host_.get())
          ->pending_commit_state()
          ->layers_that_should_push_properties.contains(mirrored.get()));
  layer_tree_host_->property_trees()->set_needs_rebuild(false);

  // Destroying one of the mirror layers should not trigger property trees
  // rebuild.
  mirror1->RemoveFromParent();
  mirror1 = nullptr;
  EXPECT_EQ(1, mirrored->mirror_count());
  EXPECT_FALSE(layer_tree_host_->property_trees()->needs_rebuild());
  EXPECT_EQ(1u, const_cast<const FakeLayerTreeHost*>(layer_tree_host_.get())
                    ->pending_commit_state()
                    ->layers_that_should_push_properties.size());
  layer_tree_host_->property_trees()->set_needs_rebuild(false);

  // Destroying the only remaining mirror layer should trigger property trees
  // rebuild.
  mirror2->RemoveFromParent();
  mirror2 = nullptr;
  EXPECT_EQ(0, mirrored->mirror_count());
  EXPECT_TRUE(layer_tree_host_->property_trees()->needs_rebuild());
  EXPECT_TRUE(
      const_cast<const FakeLayerTreeHost*>(layer_tree_host_.get())
          ->pending_commit_state()
          ->layers_that_should_push_properties.contains(mirrored.get()));
  layer_tree_host_->property_trees()->set_needs_rebuild(false);

  mirrored->SetLayerTreeHost(nullptr);
}

}  // namespace
}  // namespace cc