// 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 "cc/layers/mirror_layer.h"

#include <memory>
#include <utility>

#include "cc/animation/animation_host.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_delegate.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(),
        const_cast<const FakeLayerTreeHost*>(layer_tree_host_.get())
            ->thread_unsafe_commit_state(),
        host_impl_.pending_tree());
  }

 protected:
  void SetUp() override {
    animation_host_ = AnimationHost::CreateForTesting(ThreadInstance::kMain);
    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;
  }

  FakeLayerTreeHostDelegate 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());
  host_impl_.pending_tree()->AddLayer(std::move(root_impl));
  auto mirrored_impl =
      LayerImpl::Create(host_impl_.pending_tree(), mirrored->id());
  auto* mirrored_impl_ptr = mirrored_impl.get();
  host_impl_.pending_tree()->AddLayer(std::move(mirrored_impl));
  auto mirror_impl =
      MirrorLayerImpl::Create(host_impl_.pending_tree(), mirror->id());
  auto* mirror_impl_ptr = mirror_impl.get();
  host_impl_.pending_tree()->AddLayer(std::move(mirror_impl));

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

  SynchronizeTrees();

  // Verify that property values are pushed to impl layers.
  EXPECT_EQ(mirrored_impl_ptr->id(), mirror_impl_ptr->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_->ClearPendingLayerCommitStates();
  auto commit_state = layer_tree_host_->WillCommit(/*completion_event=*/nullptr,
                                                   /*has_updates=*/true);
  layer_tree_host_->CommitComplete(commit_state->source_frame_number,
                                   {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()
          ->layer_ids_that_should_push_properties.contains(mirrored->id()));
  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()
          ->layer_ids_that_should_push_properties.contains(mirrored->id()));
  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()
                    ->layer_ids_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()
          ->layer_ids_that_should_push_properties.contains(mirrored->id()));
  layer_tree_host_->property_trees()->set_needs_rebuild(false);

  mirrored->SetLayerTreeHost(nullptr);
}

}  // namespace
}  // namespace cc
