// Copyright 2025 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/tile_display_layer_impl.h"

#include <algorithm>

#include "base/check_deref.h"
#include "base/functional/callback_helpers.h"
#include "base/logging.h"
#include "base/test/scoped_feature_list.h"
#include "cc/base/features.h"
#include "cc/debug/debug_colors.h"
#include "cc/layers/append_quads_context.h"
#include "cc/layers/append_quads_data.h"
#include "cc/test/test_layer_tree_host_base.h"
#include "components/viz/client/client_resource_provider.h"
#include "components/viz/common/quads/debug_border_draw_quad.h"
#include "components/viz/common/quads/solid_color_draw_quad.h"
#include "components/viz/common/quads/tile_draw_quad.h"
#include "gpu/command_buffer/client/client_shared_image.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "ui/gfx/geometry/rect.h"

using testing::ElementsAre;

namespace cc {

class TileDisplayLayerImplTest : public TestLayerTreeHostBase {};

TEST_F(TileDisplayLayerImplTest, NoQuadAppendedByDefault) {
  auto layer = std::make_unique<TileDisplayLayerImpl>(
      CHECK_DEREF(host_impl()->active_tree()), /*id=*/42);
  auto* raw_layer = layer.get();
  host_impl()->active_tree()->AddLayer(std::move(layer));
  SetupRootProperties(host_impl()->active_tree()->root_layer());

  auto render_pass = viz::CompositorRenderPass::Create();
  AppendQuadsData data;
  bool has_missing_tiles = raw_layer->HasMissingTiles();
  raw_layer->AppendQuads(AppendQuadsContext{DRAW_MODE_SOFTWARE, {}, false},
                         render_pass.get(), &data);

  EXPECT_EQ(has_missing_tiles, data.num_missing_tiles > 0);

  EXPECT_EQ(render_pass->quad_list.size(), 0u);
}

TEST_F(TileDisplayLayerImplTest,
       AppendQuadsDoesNotAppendQuadsForOccludedTiles) {
  constexpr gfx::Size kLayerBounds(100, 100);
  constexpr gfx::Rect kLayerRect(kLayerBounds);
  constexpr float kOpacity = 1.0;

  auto layer = std::make_unique<TileDisplayLayerImpl>(
      CHECK_DEREF(host_impl()->active_tree()), /*id=*/42);
  auto* raw_layer = layer.get();
  host_impl()->active_tree()->AddLayer(std::move(layer));

  raw_layer->SetBounds(kLayerBounds);
  raw_layer->draw_properties().visible_layer_rect = kLayerRect;
  raw_layer->draw_properties().opacity = kOpacity;

  // Create a tiling with one tile.
  auto& tiling = raw_layer->GetOrCreateTilingFromScaleKey(1.0);
  tiling.SetTileSize(kLayerBounds);
  tiling.SetTilingRect(kLayerRect);

  auto resource_id = host_impl()->resource_provider()->ImportResource(
      viz::TransferableResource::Make(
          gpu::ClientSharedImage::CreateForTesting(),
          viz::TransferableResource::ResourceSource::kTest, gpu::SyncToken()),
      base::DoNothing());
  TileDisplayLayerImpl::TileContents contents =
      TileDisplayLayerTileResource(resource_id, kLayerBounds);
  tiling.SetTileContents(TileIndex{0, 0}, contents, /*update_damage=*/false);

  // Set up occlusion that covers the entire layer. Occlusion is specified in
  // screen space, so we provide an identity transform to make content space
  // the same as screen space.
  gfx::Transform identity_transform;
  SimpleEnclosedRegion screen_occlusion(kLayerRect);
  raw_layer->draw_properties().occlusion_in_content_space =
      Occlusion(identity_transform, screen_occlusion, screen_occlusion);

  SetupRootProperties(host_impl()->active_tree()->root_layer());

  auto render_pass = viz::CompositorRenderPass::Create();
  AppendQuadsData data;
  bool has_missing_tiles = raw_layer->HasMissingTiles();
  raw_layer->AppendQuads(AppendQuadsContext{DRAW_MODE_SOFTWARE, {}, false},
                         render_pass.get(), &data);

  EXPECT_EQ(has_missing_tiles, data.num_missing_tiles > 0);

  EXPECT_EQ(render_pass->quad_list.size(), 0u);
}

TEST_F(TileDisplayLayerImplTest,
       AppendQuadsAppendsClippedQuadsForPartiallyOccludedTiles) {
  const gfx::Rect layer_rect(0, 0, 10, 10);
  const gfx::Rect tile_rect(0, 0, 10, 10);
  const gfx::Rect occluded_rect(0, 0, 5, 10);

  // Setup layer and tiling.
  auto layer = std::make_unique<TileDisplayLayerImpl>(
      CHECK_DEREF(host_impl()->active_tree()), /*id=*/42);
  auto* raw_layer = layer.get();
  host_impl()->active_tree()->AddLayer(std::move(layer));

  raw_layer->SetBounds(layer_rect.size());
  raw_layer->SetRecordedBounds(layer_rect);
  raw_layer->draw_properties().visible_layer_rect = layer_rect;
  raw_layer->draw_properties().occlusion_in_content_space =
      Occlusion(gfx::Transform(), SimpleEnclosedRegion(occluded_rect),
                SimpleEnclosedRegion());

  auto& tiling = raw_layer->GetOrCreateTilingFromScaleKey(1.0);
  tiling.SetTileSize(tile_rect.size());
  tiling.SetTilingRect(tile_rect);

  auto resource_id = host_impl()->resource_provider()->ImportResource(
      viz::TransferableResource::Make(
          gpu::ClientSharedImage::CreateForTesting(),
          viz::TransferableResource::ResourceSource::kTest, gpu::SyncToken()),
      base::DoNothing());
  TileDisplayLayerImpl::TileContents contents =
      TileDisplayLayerTileResource(resource_id, tile_rect.size());
  tiling.SetTileContents(TileIndex{0, 0}, contents, /*update_damage=*/false);

  SetupRootProperties(host_impl()->active_tree()->root_layer());

  // Append quads.
  auto render_pass = viz::CompositorRenderPass::Create();
  AppendQuadsData data;
  bool has_missing_tiles = raw_layer->HasMissingTiles();
  raw_layer->AppendQuads(AppendQuadsContext{DRAW_MODE_SOFTWARE, {}, false},
                         render_pass.get(), &data);

  EXPECT_EQ(has_missing_tiles, data.num_missing_tiles > 0);

  // Verify that one quad is appended and it's clipped.
  ASSERT_EQ(1u, render_pass->quad_list.size());
  const viz::DrawQuad* quad = render_pass->quad_list.front();
  EXPECT_EQ(viz::DrawQuad::Material::kTiledContent, quad->material);

  const auto* tile_quad = viz::TileDrawQuad::MaterialCast(quad);
  EXPECT_EQ(tile_rect, tile_quad->rect);
  const gfx::Rect expected_visible_rect =
      gfx::SubtractRects(tile_rect, occluded_rect);
  EXPECT_EQ(expected_visible_rect, tile_quad->visible_rect);
}

TEST_F(TileDisplayLayerImplTest,
       NonEmptyTilingWithResourceResultsInPictureQuad) {
  constexpr gfx::Size kLayerBounds(1300, 1900);
  constexpr gfx::Rect kLayerRect(kLayerBounds);
  constexpr float kOpacity = 1.0;

  auto layer = std::make_unique<TileDisplayLayerImpl>(
      CHECK_DEREF(host_impl()->active_tree()), /*id=*/42);
  auto* raw_layer = layer.get();
  host_impl()->active_tree()->AddLayer(std::move(layer));

  // For the production code to actually append a quad, the layer must have
  // non-zero size and not be completely transparent.
  raw_layer->SetBounds(kLayerBounds);
  raw_layer->SetRecordedBounds(kLayerRect);
  raw_layer->draw_properties().visible_layer_rect = kLayerRect;
  raw_layer->draw_properties().opacity = kOpacity;

  auto& tiling = raw_layer->GetOrCreateTilingFromScaleKey(1.0);
  tiling.SetTileSize(kLayerBounds);
  tiling.SetTilingRect(kLayerRect);

  auto resource_id = host_impl()->resource_provider()->ImportResource(
      viz::TransferableResource::Make(
          gpu::ClientSharedImage::CreateForTesting(),
          viz::TransferableResource::ResourceSource::kTest, gpu::SyncToken()),
      base::DoNothing());
  TileDisplayLayerImpl::TileContents contents =
      TileDisplayLayerTileResource(resource_id, kLayerBounds);
  tiling.SetTileContents(TileIndex{0, 0}, contents, /*update_damage=*/true);

  SetupRootProperties(host_impl()->active_tree()->root_layer());

  auto render_pass = viz::CompositorRenderPass::Create();
  AppendQuadsData data;
  bool has_missing_tiles = raw_layer->HasMissingTiles();
  raw_layer->AppendQuads(AppendQuadsContext{DRAW_MODE_SOFTWARE, {}, false},
                         render_pass.get(), &data);

  EXPECT_EQ(has_missing_tiles, data.num_missing_tiles > 0);

  EXPECT_EQ(render_pass->quad_list.size(), 1u);
  EXPECT_EQ(render_pass->quad_list.front()->rect, kLayerRect);
  EXPECT_EQ(render_pass->quad_list.front()->visible_rect, kLayerRect);
  EXPECT_EQ(render_pass->quad_list.front()->shared_quad_state->opacity,
            kOpacity);
  EXPECT_EQ(render_pass->quad_list.front()->resource_id, resource_id);
  EXPECT_EQ(render_pass->quad_list.front()->material,
            viz::DrawQuad::Material::kTiledContent);
  EXPECT_EQ(viz::TileDrawQuad::MaterialCast(render_pass->quad_list.front())
                ->force_anti_aliasing_off,
            false);
}

TEST_F(TileDisplayLayerImplTest,
       NonEmptyTilingWithColorResultsInSolidColorQuad) {
  constexpr gfx::Size kLayerBounds(1300, 1900);
  constexpr gfx::Rect kLayerRect(kLayerBounds);
  constexpr float kOpacity = 1.0;
  constexpr SkColor4f kTileColor = SkColors::kRed;

  auto layer = std::make_unique<TileDisplayLayerImpl>(
      CHECK_DEREF(host_impl()->active_tree()), /*id=*/42);
  auto* raw_layer = layer.get();
  host_impl()->active_tree()->AddLayer(std::move(layer));

  // For the production code to actually append a quad, the layer must have
  // non-zero size and not be completely transparent.
  raw_layer->SetBounds(kLayerBounds);
  raw_layer->SetRecordedBounds(kLayerRect);
  raw_layer->draw_properties().visible_layer_rect = kLayerRect;
  raw_layer->draw_properties().opacity = kOpacity;

  auto& tiling = raw_layer->GetOrCreateTilingFromScaleKey(1.0);
  tiling.SetTileSize(kLayerBounds);
  tiling.SetTilingRect(kLayerRect);

  tiling.SetTileContents(TileIndex{0, 0}, kTileColor, /*update_damage=*/true);

  SetupRootProperties(host_impl()->active_tree()->root_layer());

  auto render_pass = viz::CompositorRenderPass::Create();
  AppendQuadsData data;
  bool has_missing_tiles = raw_layer->HasMissingTiles();
  raw_layer->AppendQuads(AppendQuadsContext{DRAW_MODE_SOFTWARE, {}, false},
                         render_pass.get(), &data);

  EXPECT_EQ(has_missing_tiles, data.num_missing_tiles > 0);

  EXPECT_EQ(render_pass->quad_list.size(), 1u);
  EXPECT_EQ(render_pass->quad_list.front()->rect, kLayerRect);
  EXPECT_EQ(render_pass->quad_list.front()->visible_rect, kLayerRect);
  EXPECT_EQ(render_pass->quad_list.front()->shared_quad_state->opacity,
            kOpacity);
  EXPECT_EQ(render_pass->quad_list.front()->material,
            viz::DrawQuad::Material::kSolidColor);
  EXPECT_EQ(
      viz::SolidColorDrawQuad::MaterialCast(render_pass->quad_list.front())
          ->color,
      kTileColor);
  EXPECT_EQ(
      viz::SolidColorDrawQuad::MaterialCast(render_pass->quad_list.front())
          ->force_anti_aliasing_off,
      false);
}

// Verifies that GetContentsResourceId() handles the error case of being called
// when the layer has no tiles, setting the resource ID to invalid in that case.
TEST_F(TileDisplayLayerImplTest, GetContentsResourceIdHandlesLackOfTiles) {
  auto layer = std::make_unique<TileDisplayLayerImpl>(
      CHECK_DEREF(host_impl()->active_tree()), /*id=*/42);
  layer->SetIsBackdropFilterMask(true);

  viz::ResourceId mask_resource_id = viz::ResourceId(42);
  gfx::Size mask_texture_size;
  gfx::SizeF mask_uv_size;
  layer->GetContentsResourceId(&mask_resource_id, &mask_texture_size,
                               &mask_uv_size);
  EXPECT_EQ(mask_resource_id, viz::kInvalidResourceId);
}

// Verifies that GetContentsResourceId() returns the correct resource ID for a
// backdrop filter mask.
TEST_F(TileDisplayLayerImplTest,
       GetContentsResourceIdReturnsResourceForBackdropFilter) {
  constexpr gfx::Size kLayerBounds(1300, 1900);
  constexpr gfx::Rect kLayerRect(kLayerBounds);

  auto layer = std::make_unique<TileDisplayLayerImpl>(
      CHECK_DEREF(host_impl()->active_tree()), /*id=*/42);
  auto* raw_layer = layer.get();
  host_impl()->active_tree()->AddLayer(std::move(layer));

  raw_layer->SetBounds(kLayerBounds);
  raw_layer->SetIsBackdropFilterMask(true);

  auto& tiling = raw_layer->GetOrCreateTilingFromScaleKey(1.0);
  tiling.SetTileSize(kLayerBounds);
  tiling.SetTilingRect(kLayerRect);

  auto resource_id = host_impl()->resource_provider()->ImportResource(
      viz::TransferableResource::Make(
          gpu::ClientSharedImage::CreateForTesting(),
          viz::TransferableResource::ResourceSource::kTest, gpu::SyncToken()),
      base::DoNothing());
  TileDisplayLayerImpl::TileContents contents =
      TileDisplayLayerTileResource(resource_id, kLayerBounds);
  tiling.SetTileContents(TileIndex{0, 0}, contents, /*update_damage=*/true);

  SetupRootProperties(host_impl()->active_tree()->root_layer());

  viz::ResourceId mask_resource_id;
  gfx::Size mask_texture_size;
  gfx::SizeF mask_uv_size;
  raw_layer->GetContentsResourceId(&mask_resource_id, &mask_texture_size,
                                   &mask_uv_size);

  EXPECT_EQ(mask_resource_id, resource_id);
  EXPECT_EQ(mask_texture_size, kLayerBounds);

  // `mask_uv_size` is the ratio between the tile's width/height and that of
  // the resource. Here, the tile and resource have been created with the same
  // size.
  EXPECT_EQ(mask_uv_size, gfx::SizeF(1.0f, 1.0f));
}

// Verifies that GetContentsResourceId() returns the correct mask UV size when
// the tile and resource sizes differ.
TEST_F(TileDisplayLayerImplTest,
       GetContentsResourceIdComputesUVMaskSizeCorrectlyForBackdropFilter) {
  constexpr gfx::Size kLayerBounds(100, 200);
  constexpr gfx::Size kResourceSize(200, 400);
  constexpr gfx::Rect kLayerRect(kLayerBounds);

  auto layer = std::make_unique<TileDisplayLayerImpl>(
      CHECK_DEREF(host_impl()->active_tree()), /*id=*/42);
  auto* raw_layer = layer.get();
  host_impl()->active_tree()->AddLayer(std::move(layer));

  raw_layer->SetBounds(kLayerBounds);
  raw_layer->SetIsBackdropFilterMask(true);

  auto& tiling = raw_layer->GetOrCreateTilingFromScaleKey(1.0);
  tiling.SetTileSize(kLayerBounds);
  tiling.SetTilingRect(kLayerRect);

  auto resource_id = host_impl()->resource_provider()->ImportResource(
      viz::TransferableResource::Make(
          gpu::ClientSharedImage::CreateForTesting(),
          viz::TransferableResource::ResourceSource::kTest, gpu::SyncToken()),
      base::DoNothing());
  TileDisplayLayerImpl::TileContents contents =
      TileDisplayLayerTileResource(resource_id, kResourceSize);
  tiling.SetTileContents(TileIndex{0, 0}, contents, /*update_damage=*/true);

  SetupRootProperties(host_impl()->active_tree()->root_layer());

  viz::ResourceId mask_resource_id;
  gfx::Size mask_texture_size;
  gfx::SizeF mask_uv_size;
  raw_layer->GetContentsResourceId(&mask_resource_id, &mask_texture_size,
                                   &mask_uv_size);

  EXPECT_EQ(mask_resource_id, resource_id);
  EXPECT_EQ(mask_texture_size, kResourceSize);

  // `mask_uv_size` is the ratio between the tile's width/height and that of
  // the resource. Here, the tile has been created to be half the size of the
  // resource in each dimension.
  EXPECT_EQ(mask_uv_size, gfx::SizeF(0.5f, 0.5f));
}

// Verifies that GetContentsResourceId() returns the correct mask UV size when
// the tiling rect and max tile size differ.
TEST_F(
    TileDisplayLayerImplTest,
    GetContentsResourceIdComputesUVMaskSizeCorrectlyWhenTilingRectIsSmallerThanMaxTileSize) {
  constexpr gfx::Size kLayerBounds(100, 200);
  constexpr gfx::Size kTileSize(200, 400);
  constexpr gfx::Size kResourceSize(200, 400);
  constexpr gfx::Rect kLayerRect(kLayerBounds);

  auto layer = std::make_unique<TileDisplayLayerImpl>(
      CHECK_DEREF(host_impl()->active_tree()), /*id=*/42);
  auto* raw_layer = layer.get();
  host_impl()->active_tree()->AddLayer(std::move(layer));

  raw_layer->SetBounds(kLayerBounds);
  raw_layer->SetIsBackdropFilterMask(true);

  auto& tiling = raw_layer->GetOrCreateTilingFromScaleKey(1.0);
  tiling.SetTileSize(kTileSize);
  tiling.SetTilingRect(kLayerRect);

  auto resource_id = host_impl()->resource_provider()->ImportResource(
      viz::TransferableResource::Make(
          gpu::ClientSharedImage::CreateForTesting(),
          viz::TransferableResource::ResourceSource::kTest, gpu::SyncToken()),
      base::DoNothing());
  TileDisplayLayerImpl::TileContents contents =
      TileDisplayLayerTileResource(resource_id, kResourceSize);
  tiling.SetTileContents(TileIndex{0, 0}, contents, /*update_damage=*/true);

  SetupRootProperties(host_impl()->active_tree()->root_layer());

  viz::ResourceId mask_resource_id;
  gfx::Size mask_texture_size;
  gfx::SizeF mask_uv_size;
  raw_layer->GetContentsResourceId(&mask_resource_id, &mask_texture_size,
                                   &mask_uv_size);

  EXPECT_EQ(mask_resource_id, resource_id);
  EXPECT_EQ(mask_texture_size, kResourceSize);

  // `mask_uv_size` is the ratio between the tiling's width/height (i.e., the
  // content size) and that of the resource. Here, the tiling rect is half the
  // size of the resource in each dimension.
  EXPECT_EQ(mask_uv_size, gfx::SizeF(0.5f, 0.5f));
}

// Tests that GetContentsResourceId() returns viz::kInvalidResourceId if the
// layer has more than one tiling, as masks are only supported if they fit on a
// single tile.
TEST_F(TileDisplayLayerImplTest,
       GetContentsResourceIdReturnsInvalidIdForMultipleTilings) {
  auto layer = std::make_unique<TileDisplayLayerImpl>(
      CHECK_DEREF(host_impl()->active_tree()), /*id=*/42);
  auto* raw_layer = layer.get();
  host_impl()->active_tree()->AddLayer(std::move(layer));

  raw_layer->SetIsBackdropFilterMask(true);

  // Create two tilings.
  raw_layer->GetOrCreateTilingFromScaleKey(1.0);
  raw_layer->GetOrCreateTilingFromScaleKey(2.0);

  viz::ResourceId mask_resource_id;
  gfx::Size mask_texture_size;
  gfx::SizeF mask_uv_size;
  raw_layer->GetContentsResourceId(&mask_resource_id, &mask_texture_size,
                                   &mask_uv_size);

  EXPECT_EQ(mask_resource_id, viz::kInvalidResourceId);
}

class TileDisplayLayerImplWithEdgeAADisabledTest
    : public TileDisplayLayerImplTest {
 public:
  LayerTreeSettings CreateSettings() override {
    auto settings = TileDisplayLayerImplTest::CreateSettings();
    settings.enable_edge_anti_aliasing = false;
    return settings;
  }
};

TEST_F(TileDisplayLayerImplWithEdgeAADisabledTest,
       EnableEdgeAntiAliasingIsHonoredForPictureQuads) {
  constexpr gfx::Size kLayerBounds(1300, 1900);
  constexpr gfx::Rect kLayerRect(kLayerBounds);
  constexpr float kOpacity = 1.0;

  auto layer = std::make_unique<TileDisplayLayerImpl>(
      CHECK_DEREF(host_impl()->active_tree()), /*id=*/42);
  auto* raw_layer = layer.get();
  host_impl()->active_tree()->AddLayer(std::move(layer));

  raw_layer->SetBounds(kLayerBounds);
  raw_layer->SetRecordedBounds(kLayerRect);
  raw_layer->draw_properties().visible_layer_rect = kLayerRect;
  raw_layer->draw_properties().opacity = kOpacity;

  auto& tiling = raw_layer->GetOrCreateTilingFromScaleKey(1.0);
  tiling.SetTileSize(kLayerBounds);
  tiling.SetTilingRect(kLayerRect);

  auto resource_id = host_impl()->resource_provider()->ImportResource(
      viz::TransferableResource::Make(
          gpu::ClientSharedImage::CreateForTesting(),
          viz::TransferableResource::ResourceSource::kTest, gpu::SyncToken()),
      base::DoNothing());
  TileDisplayLayerImpl::TileContents contents =
      TileDisplayLayerTileResource(resource_id, kLayerBounds);
  tiling.SetTileContents(TileIndex{0, 0}, contents, /*update_damage=*/true);

  SetupRootProperties(host_impl()->active_tree()->root_layer());

  auto render_pass = viz::CompositorRenderPass::Create();
  AppendQuadsData data;
  bool has_missing_tiles = raw_layer->HasMissingTiles();
  raw_layer->AppendQuads(AppendQuadsContext{DRAW_MODE_SOFTWARE, {}, false},
                         render_pass.get(), &data);

  EXPECT_EQ(has_missing_tiles, data.num_missing_tiles > 0);

  EXPECT_EQ(render_pass->quad_list.size(), 1u);
  EXPECT_EQ(viz::TileDrawQuad::MaterialCast(render_pass->quad_list.front())
                ->force_anti_aliasing_off,
            true);
}

TEST_F(TileDisplayLayerImplWithEdgeAADisabledTest,
       EnableEdgeAntiAliasingIsHonoredForSolidColorQuads) {
  constexpr gfx::Size kLayerBounds(1300, 1900);
  constexpr gfx::Rect kLayerRect(kLayerBounds);
  constexpr float kOpacity = 1.0;
  constexpr SkColor4f kTileColor = SkColors::kRed;

  auto layer = std::make_unique<TileDisplayLayerImpl>(
      CHECK_DEREF(host_impl()->active_tree()), /*id=*/42);
  auto* raw_layer = layer.get();
  host_impl()->active_tree()->AddLayer(std::move(layer));

  raw_layer->SetBounds(kLayerBounds);
  raw_layer->SetRecordedBounds(kLayerRect);
  raw_layer->draw_properties().visible_layer_rect = kLayerRect;
  raw_layer->draw_properties().opacity = kOpacity;

  auto& tiling = raw_layer->GetOrCreateTilingFromScaleKey(1.0);
  tiling.SetTileSize(kLayerBounds);
  tiling.SetTilingRect(kLayerRect);

  tiling.SetTileContents(TileIndex{0, 0}, kTileColor, /*update_damage=*/true);

  SetupRootProperties(host_impl()->active_tree()->root_layer());

  auto render_pass = viz::CompositorRenderPass::Create();
  AppendQuadsData data;
  bool has_missing_tiles = raw_layer->HasMissingTiles();
  raw_layer->AppendQuads(AppendQuadsContext{DRAW_MODE_SOFTWARE, {}, false},
                         render_pass.get(), &data);

  EXPECT_EQ(has_missing_tiles, data.num_missing_tiles > 0);

  EXPECT_EQ(render_pass->quad_list.size(), 1u);
  EXPECT_EQ(
      viz::SolidColorDrawQuad::MaterialCast(render_pass->quad_list.front())
          ->force_anti_aliasing_off,
      true);
}

TEST_F(TileDisplayLayerImplTest, MissingTileResultsInCheckerBoardQuad) {
  constexpr gfx::Size kLayerBounds(1300, 1900);
  constexpr gfx::Rect kLayerRect(kLayerBounds);
  constexpr float kOpacity = 1.0;

  auto layer = std::make_unique<TileDisplayLayerImpl>(
      CHECK_DEREF(host_impl()->active_tree()), /*id=*/42);
  auto* raw_layer = layer.get();
  host_impl()->active_tree()->AddLayer(std::move(layer));

  // For the production code to actually append a quad, the layer must have
  // non-zero size and not be completely transparent.
  raw_layer->SetBounds(kLayerBounds);
  raw_layer->SetRecordedBounds(kLayerRect);
  raw_layer->draw_properties().visible_layer_rect = kLayerRect;
  raw_layer->draw_properties().opacity = kOpacity;

  // Add a tiling, but don't give it any tile contents.
  auto& tiling = raw_layer->GetOrCreateTilingFromScaleKey(1.0);
  tiling.SetTileSize(kLayerBounds);
  tiling.SetTilingRect(kLayerRect);

  SetupRootProperties(host_impl()->active_tree()->root_layer());

  auto render_pass = viz::CompositorRenderPass::Create();
  AppendQuadsData data;
  bool has_missing_tiles = raw_layer->HasMissingTiles();
  raw_layer->AppendQuads(AppendQuadsContext{DRAW_MODE_SOFTWARE, {}, false},
                         render_pass.get(), &data);

  EXPECT_EQ(has_missing_tiles, data.num_missing_tiles > 0);

  // Verify that the layer appended a checkerboard quad for the missing tile.
  // Checkerboard quads are solid-color quads whose color is the safe background
  // opaque color.
  EXPECT_EQ(render_pass->quad_list.size(), 1u);
  EXPECT_EQ(render_pass->quad_list.front()->rect, kLayerRect);
  EXPECT_EQ(render_pass->quad_list.front()->visible_rect, kLayerRect);
  EXPECT_EQ(render_pass->quad_list.front()->shared_quad_state->opacity,
            kOpacity);
  EXPECT_EQ(render_pass->quad_list.front()->material,
            viz::DrawQuad::Material::kSolidColor);
  EXPECT_EQ(
      viz::SolidColorDrawQuad::MaterialCast(render_pass->quad_list.front())
          ->color,
      raw_layer->safe_opaque_background_color());
}

TEST_F(TileDisplayLayerImplTest, OomTileResultsInSolidColorQuad) {
  constexpr gfx::Size kLayerBounds(1300, 1900);
  constexpr gfx::Rect kLayerRect(kLayerBounds);
  constexpr float kOpacity = 1.0;

  auto layer = std::make_unique<TileDisplayLayerImpl>(
      CHECK_DEREF(host_impl()->active_tree()), /*id=*/42);
  auto* raw_layer = layer.get();
  host_impl()->active_tree()->AddLayer(std::move(layer));

  // For the production code to actually append a quad, the layer must have
  // non-zero size and not be completely transparent.
  raw_layer->SetBounds(kLayerBounds);
  raw_layer->SetRecordedBounds(kLayerRect);
  raw_layer->draw_properties().visible_layer_rect = kLayerRect;
  raw_layer->draw_properties().opacity = kOpacity;

  // Add a tiling with an OOM tile.
  auto& tiling = raw_layer->GetOrCreateTilingFromScaleKey(1.0);
  tiling.SetTileSize(kLayerBounds);
  tiling.SetTilingRect(kLayerRect);
  tiling.SetTileContents(
      TileIndex{0, 0},
      TileDisplayLayerNoContents{mojom::MissingTileReason::kOutOfMemory},
      /*update_damage=*/true);

  SetupRootProperties(host_impl()->active_tree()->root_layer());

  auto render_pass = viz::CompositorRenderPass::Create();
  AppendQuadsData data;
  bool has_missing_tiles = raw_layer->HasMissingTiles();
  raw_layer->AppendQuads(AppendQuadsContext{DRAW_MODE_SOFTWARE, {}, false},
                         render_pass.get(), &data);

  EXPECT_EQ(has_missing_tiles, data.num_missing_tiles > 0);

  // Verify that the layer appended a solid color quad for the OOM tile.
  EXPECT_EQ(render_pass->quad_list.size(), 1u);
  EXPECT_EQ(render_pass->quad_list.front()->rect, kLayerRect);
  EXPECT_EQ(render_pass->quad_list.front()->visible_rect, kLayerRect);
  EXPECT_EQ(render_pass->quad_list.front()->shared_quad_state->opacity,
            kOpacity);
  EXPECT_EQ(render_pass->quad_list.front()->material,
            viz::DrawQuad::Material::kSolidColor);
  EXPECT_EQ(
      viz::SolidColorDrawQuad::MaterialCast(render_pass->quad_list.front())
          ->color,
      raw_layer->safe_opaque_background_color());
}

// Verifies that the layer appends quads from the highest-resolution tiling
// when multiple tilings are available.
TEST_F(TileDisplayLayerImplTest, AppendsQuadsFromHighestResolutionTilingByDefault) {
  constexpr gfx::Size kLayerBounds(1300, 1900);
  constexpr gfx::Rect kLayerRect(kLayerBounds);
  constexpr float kOpacity = 1.0;

  auto layer = std::make_unique<TileDisplayLayerImpl>(
      CHECK_DEREF(host_impl()->active_tree()), /*id=*/42);
  auto* raw_layer = layer.get();
  host_impl()->active_tree()->AddLayer(std::move(layer));

  raw_layer->SetBounds(kLayerBounds);
  raw_layer->SetRecordedBounds(kLayerRect);
  raw_layer->draw_properties().visible_layer_rect = kLayerRect;
  raw_layer->draw_properties().opacity = kOpacity;

  // Create two tilings with different scales.
  auto& low_res_tiling = raw_layer->GetOrCreateTilingFromScaleKey(1.0);
  low_res_tiling.SetTileSize(kLayerBounds);
  low_res_tiling.SetTilingRect(kLayerRect);
  auto& high_res_tiling = raw_layer->GetOrCreateTilingFromScaleKey(2.0);
  high_res_tiling.SetTileSize(kLayerBounds);
  high_res_tiling.SetTilingRect(kLayerRect);

  // Set content for the high-res tiling only.
  auto resource_id = host_impl()->resource_provider()->ImportResource(
      viz::TransferableResource::Make(
          gpu::ClientSharedImage::CreateForTesting(),
          viz::TransferableResource::ResourceSource::kTest, gpu::SyncToken()),
      base::DoNothing());
  TileDisplayLayerImpl::TileContents contents =
      TileDisplayLayerTileResource(resource_id, kLayerBounds);
  high_res_tiling.SetTileContents(TileIndex{0, 0}, contents,
                                  /*update_damage=*/true);

  SetupRootProperties(host_impl()->active_tree()->root_layer());

  auto render_pass = viz::CompositorRenderPass::Create();
  AppendQuadsData data;
  bool has_missing_tiles = raw_layer->HasMissingTiles();
  raw_layer->AppendQuads(AppendQuadsContext{DRAW_MODE_SOFTWARE, {}, false},
                         render_pass.get(), &data);

  EXPECT_EQ(has_missing_tiles, data.num_missing_tiles > 0);

  // Verify that the quad is from the high-res tiling.
  EXPECT_EQ(render_pass->quad_list.size(), 1u);
  EXPECT_EQ(render_pass->quad_list.front()->resource_id, resource_id);
}

// Verifies that the layer can be forced to append quads from a
// lower-resolution tiling if the ideal contents scale matches that tiling.
TEST_F(TileDisplayLayerImplTest, AppendsQuadsFromIdealResolutionTiling) {
  constexpr gfx::Size kLayerBounds(1300, 1900);
  constexpr gfx::Rect kLayerRect(kLayerBounds);
  constexpr float kOpacity = 1.0;

  auto layer = std::make_unique<TileDisplayLayerImpl>(
      CHECK_DEREF(host_impl()->active_tree()), /*id=*/42);
  auto* raw_layer = layer.get();
  host_impl()->active_tree()->AddLayer(std::move(layer));

  raw_layer->SetBounds(kLayerBounds);
  raw_layer->SetRecordedBounds(kLayerRect);
  raw_layer->draw_properties().visible_layer_rect = kLayerRect;
  raw_layer->draw_properties().opacity = kOpacity;

  // Create two tilings with different scales.
  auto& low_res_tiling = raw_layer->GetOrCreateTilingFromScaleKey(1.0);
  low_res_tiling.SetTileSize(kLayerBounds);
  low_res_tiling.SetTilingRect(kLayerRect);
  auto& high_res_tiling = raw_layer->GetOrCreateTilingFromScaleKey(2.0);
  high_res_tiling.SetTileSize(kLayerBounds);
  high_res_tiling.SetTilingRect(kLayerRect);

  // Set content for the low-resolution tiling only.
  auto low_res_resource_id = host_impl()->resource_provider()->ImportResource(
      viz::TransferableResource::Make(
          gpu::ClientSharedImage::CreateForTesting(),
          viz::TransferableResource::ResourceSource::kTest, gpu::SyncToken()),
      base::DoNothing());
  TileDisplayLayerImpl::TileContents low_res_contents =
      TileDisplayLayerTileResource(low_res_resource_id, kLayerBounds);
  low_res_tiling.SetTileContents(TileIndex{0, 0}, low_res_contents,
                                 /*update_damage=*/true);

  // With an identity transform, the ideal contents scale is 1.0, so the
  // low-resolution tiling should be chosen.
  SetupRootProperties(host_impl()->active_tree()->root_layer());

  auto render_pass = viz::CompositorRenderPass::Create();
  AppendQuadsData data;
  bool has_missing_tiles = raw_layer->HasMissingTiles();
  raw_layer->AppendQuads(AppendQuadsContext{DRAW_MODE_SOFTWARE, {}, false},
                         render_pass.get(), &data);

  EXPECT_EQ(has_missing_tiles, data.num_missing_tiles > 0);

  // Verify that the quad is from the low-res tiling.
  EXPECT_EQ(render_pass->quad_list.size(), 1u);
  EXPECT_EQ(render_pass->quad_list.front()->resource_id, low_res_resource_id);
}

// TileDisplayLayerImpl is only instantiated when trees-in-viz is enabled, so
// run this test with the feature enabled to mirror production.
class TileDisplayLayerImplWithTreesInVizTest : public TileDisplayLayerImplTest {
 public:
  TileDisplayLayerImplWithTreesInVizTest() {
    scoped_feature_list_.InitAndEnableFeature(features::kTreesInViz);
  }

 private:
  base::test::ScopedFeatureList scoped_feature_list_;
};

// Verifies that an external page scale factor (e.g. a cross-origin iframe
// scaled by its embedder) is applied to the ideal contents scale, so the
// coverage iterator selects the high-resolution tiling even when the layer's
// own geometry transform is identity.
TEST_F(TileDisplayLayerImplWithTreesInVizTest,
       AppendsQuadsFromHighResTilingWhenScaledByExternalPageScaleFactor) {
  constexpr gfx::Size kLayerBounds(1300, 1900);
  constexpr gfx::Rect kLayerRect(kLayerBounds);
  constexpr float kOpacity = 1.0;

  auto layer = std::make_unique<TileDisplayLayerImpl>(
      CHECK_DEREF(host_impl()->active_tree()), /*id=*/42);
  auto* raw_layer = layer.get();
  host_impl()->active_tree()->AddLayer(std::move(layer));

  raw_layer->SetBounds(kLayerBounds);
  raw_layer->SetRecordedBounds(kLayerRect);
  raw_layer->draw_properties().visible_layer_rect = kLayerRect;
  raw_layer->draw_properties().opacity = kOpacity;

  // Create a low-resolution (1.0) and a high-resolution (2.0) tiling, both with
  // content available, so that the drawn tiling is determined by the ideal
  // contents scale rather than by tile availability.
  auto& low_res_tiling = raw_layer->GetOrCreateTilingFromScaleKey(1.0);
  low_res_tiling.SetTileSize(kLayerBounds);
  low_res_tiling.SetTilingRect(kLayerRect);
  auto& high_res_tiling = raw_layer->GetOrCreateTilingFromScaleKey(2.0);
  high_res_tiling.SetTileSize(kLayerBounds);
  high_res_tiling.SetTilingRect(kLayerRect);

  auto low_res_resource_id = host_impl()->resource_provider()->ImportResource(
      viz::TransferableResource::Make(
          gpu::ClientSharedImage::CreateForTesting(),
          viz::TransferableResource::ResourceSource::kTest, gpu::SyncToken()),
      base::DoNothing());
  TileDisplayLayerImpl::TileContents low_res_contents =
      TileDisplayLayerTileResource(low_res_resource_id, kLayerBounds);
  low_res_tiling.SetTileContents(TileIndex{0, 0}, low_res_contents,
                                 /*update_damage=*/true);

  auto high_res_resource_id = host_impl()->resource_provider()->ImportResource(
      viz::TransferableResource::Make(
          gpu::ClientSharedImage::CreateForTesting(),
          viz::TransferableResource::ResourceSource::kTest, gpu::SyncToken()),
      base::DoNothing());
  TileDisplayLayerImpl::TileContents high_res_contents =
      TileDisplayLayerTileResource(high_res_resource_id, kLayerBounds);
  high_res_tiling.SetTileContents(TileIndex{0, 0}, high_res_contents,
                                  /*update_damage=*/true);

  // The layer's own transform is identity, so its geometry ideal contents scale
  // is 1.0. An external page scale factor of 2.0 must raise the ideal contents
  // scale to 2.0.
  host_impl()->active_tree()->SetExternalPageScaleFactor(2.0f);

  SetupRootProperties(host_impl()->active_tree()->root_layer());

  auto render_pass = viz::CompositorRenderPass::Create();
  AppendQuadsData data;
  raw_layer->AppendQuads(AppendQuadsContext{DRAW_MODE_SOFTWARE, {}, false},
                         render_pass.get(), &data);

  // The high-resolution tiling must be chosen because the external page scale
  // factor raised the ideal contents scale to 2.0. Without applying the
  // external page scale factor, the ideal would stay 1.0 and the blurry
  // low-resolution tiling would be drawn instead.
  EXPECT_EQ(render_pass->quad_list.size(), 1u);
  EXPECT_EQ(render_pass->quad_list.front()->resource_id, high_res_resource_id);
}

// Verifies that RemoveTiling correctly removes a tiling.
TEST_F(TileDisplayLayerImplTest, RemoveTilingRemovesTiling) {
  auto layer = std::make_unique<TileDisplayLayerImpl>(
      CHECK_DEREF(host_impl()->active_tree()), /*id=*/42);
  auto* raw_layer = layer.get();
  host_impl()->active_tree()->AddLayer(std::move(layer));

  // Add a tiling.
  raw_layer->GetOrCreateTilingFromScaleKey(1.0);
  ASSERT_NE(raw_layer->GetTilingForTesting(1.0), nullptr);

  // Remove the tiling.
  raw_layer->RemoveTiling(1.0);
  EXPECT_EQ(raw_layer->GetTilingForTesting(1.0), nullptr);
}

// Verifies that removing one of multiple tilings leaves the others intact.
TEST_F(TileDisplayLayerImplTest, RemoveOneOfMultipleTilings) {
  auto layer = std::make_unique<TileDisplayLayerImpl>(
      CHECK_DEREF(host_impl()->active_tree()), /*id=*/42);
  auto* raw_layer = layer.get();
  host_impl()->active_tree()->AddLayer(std::move(layer));

  // Add two tilings.
  raw_layer->GetOrCreateTilingFromScaleKey(1.0);
  raw_layer->GetOrCreateTilingFromScaleKey(2.0);
  ASSERT_NE(raw_layer->GetTilingForTesting(1.0), nullptr);
  ASSERT_NE(raw_layer->GetTilingForTesting(2.0), nullptr);

  // Remove one tiling and verify that that tiling and only that tiling was
  // removed.
  raw_layer->RemoveTiling(1.0);
  EXPECT_EQ(raw_layer->GetTilingForTesting(1.0), nullptr);
  EXPECT_NE(raw_layer->GetTilingForTesting(2.0), nullptr);
}

// Verifies that calling RemoveTiling() for a tiling that doesn't exist doesn't
// crash.
TEST_F(TileDisplayLayerImplTest, RemoveTilingOnNonExistentTilingDoesNotCrash) {
  auto layer = std::make_unique<TileDisplayLayerImpl>(
      CHECK_DEREF(host_impl()->active_tree()), /*id=*/42);
  auto* raw_layer = layer.get();
  host_impl()->active_tree()->AddLayer(std::move(layer));

  ASSERT_EQ(raw_layer->GetTilingForTesting(1.0), nullptr);

  // This should not crash.
  raw_layer->RemoveTiling(1.0);
  EXPECT_EQ(raw_layer->GetTilingForTesting(1.0), nullptr);
}

// Verifies that setting tile contents with `update_damage=true` records the
// correct damage rect on the layer.
TEST_F(TileDisplayLayerImplTest,
       SetTileContentsRecordsDamageWhenUpdateDamageIsTrue) {
  // Configure the layer to have 5 tiles to be able to test damage from
  // individual tile updates.
  constexpr gfx::Size kLayerBounds(100, 100);
  constexpr gfx::Rect kLayerRect(kLayerBounds);
  constexpr gfx::Size kTileSize(20, 20);
  const TileIndex kTileIndex1{1, 2};
  const TileIndex kTileIndex2{3, 0};

  auto layer = std::make_unique<TileDisplayLayerImpl>(
      CHECK_DEREF(host_impl()->active_tree()), /*id=*/42);
  auto* raw_layer = layer.get();
  host_impl()->active_tree()->AddLayer(std::move(layer));

  raw_layer->SetBounds(kLayerBounds);

  auto& tiling = raw_layer->GetOrCreateTilingFromScaleKey(1.0);
  tiling.SetTileSize(kTileSize);
  tiling.SetTilingRect(kLayerRect);

  // When SetTileContents is called with update_damage=true, it calculates the
  // area that needs to be redrawn (the damage). This calculation happens in the
  // tile's coordinate system first. However, the final damage must be recorded
  // on the layer in the layer's coordinate system. TileDisplayLayerImpl uses
  // the inverse of the raster transform to map the tile's damage rectangle
  // back into the layer's coordinate space. Explicitly initialize the raster
  // transform to be the identity transform (it is not explicitly initialized by
  // default).
  tiling.SetRasterTransform(gfx::AxisTransform2d());

  // Set content for a tile and check that the damage rect is updated.
  tiling.SetTileContents(kTileIndex1, SkColors::kRed,
                         /*update_damage=*/true);
  EXPECT_EQ(
      raw_layer->GetDamageRect(),
      tiling.tiling_data()->TileBoundsWithBorder(kTileIndex1.i, kTileIndex1.j));

  // Set content for another tile and check that the damage rect is expanded.
  tiling.SetTileContents(kTileIndex2, SkColors::kBlue,
                         /*update_damage=*/true);
  gfx::Rect expected_damage_rect;
  expected_damage_rect.Union(
      tiling.tiling_data()->TileBoundsWithBorder(kTileIndex1.i, kTileIndex1.j));
  expected_damage_rect.Union(
      tiling.tiling_data()->TileBoundsWithBorder(kTileIndex2.i, kTileIndex2.j));
  EXPECT_EQ(raw_layer->GetDamageRect(), expected_damage_rect);

  // Reset change tracking and check that the damage rect is cleared.
  raw_layer->ResetChangeTracking();
  EXPECT_TRUE(raw_layer->GetDamageRect().IsEmpty());
}

// Verifies that setting tile contents with `update_damage=false` does not
// record damage on the layer.
TEST_F(TileDisplayLayerImplTest,
       SetTileContentsDoesntRecordDamageWhenUpdateDamageIsFalse) {
  // Configure the layer to have 5 tiles to be able to test damage from
  // individual tile updates.
  constexpr gfx::Size kLayerBounds(100, 100);
  constexpr gfx::Rect kLayerRect(kLayerBounds);
  constexpr gfx::Size kTileSize(20, 20);
  const TileIndex kTileIndex{1, 2};

  auto layer = std::make_unique<TileDisplayLayerImpl>(
      CHECK_DEREF(host_impl()->active_tree()), /*id=*/42);
  auto* raw_layer = layer.get();
  host_impl()->active_tree()->AddLayer(std::move(layer));

  raw_layer->SetBounds(kLayerBounds);

  auto& tiling = raw_layer->GetOrCreateTilingFromScaleKey(1.0);
  tiling.SetTileSize(kTileSize);
  tiling.SetTilingRect(kLayerRect);

  tiling.SetTileContents(kTileIndex, SkColors::kRed,
                         /*update_damage=*/false);
  EXPECT_TRUE(raw_layer->GetDamageRect().IsEmpty());
}

// Verifies that when Tiling::SetTileContents is called with NoContents and the
// reason is MissingTileReason::kTileDeleted, the corresponding tile is removed
// from the tiling.
TEST_F(TileDisplayLayerImplTest,
       SetTileContentsWithNoContentsAndTileDeletedReasonRemovesTile) {
  auto layer = std::make_unique<TileDisplayLayerImpl>(
      CHECK_DEREF(host_impl()->active_tree()), /*id=*/42);
  auto* raw_layer = layer.get();
  host_impl()->active_tree()->AddLayer(std::move(layer));

  auto& tiling = raw_layer->GetOrCreateTilingFromScaleKey(1.0);
  const TileIndex kTileIndex{0, 0};

  // Add a tile.
  tiling.SetTileContents(kTileIndex, SkColors::kRed,
                         /*update_damage=*/false);
  ASSERT_NE(tiling.TileAt(kTileIndex), nullptr);

  // Set the tile's contents to NoContents with kTileDeleted as the reason and
  // verify that the tile is deleted.
  tiling.SetTileContents(
      kTileIndex,
      TileDisplayLayerNoContents{mojom::MissingTileReason::kTileDeleted},
      /*update_damage=*/false);
  EXPECT_EQ(tiling.TileAt(kTileIndex), nullptr);
}

// Verifies that when Tiling::SetTileContents is called with NoContents and a
// reason other than MissingTileReason::kTileDeleted, the tile's contents are
// updated to NoContents.
TEST_F(TileDisplayLayerImplTest,
       SetTileContentsWithNoContentsAndOtherReasonUpdatesTile) {
  auto layer = std::make_unique<TileDisplayLayerImpl>(
      CHECK_DEREF(host_impl()->active_tree()), /*id=*/42);
  auto* raw_layer = layer.get();
  host_impl()->active_tree()->AddLayer(std::move(layer));

  auto& tiling = raw_layer->GetOrCreateTilingFromScaleKey(1.0);
  const TileIndex kTileIndex{0, 0};

  // Add a tile.
  tiling.SetTileContents(kTileIndex, SkColors::kRed,
                         /*update_damage=*/false);
  ASSERT_NE(tiling.TileAt(kTileIndex), nullptr);

  // Set the tile's contents to NoContents with a reason other than
  // kTileDeleted.
  tiling.SetTileContents(
      kTileIndex,
      TileDisplayLayerNoContents{mojom::MissingTileReason::kResourceNotReady},
      /*update_damage=*/false);

  // Verify that the tile still exists and its contents are NoContents.
  auto* tile = tiling.TileAt(kTileIndex);
  EXPECT_NE(tile, nullptr);
  EXPECT_TRUE(
      std::holds_alternative<TileDisplayLayerNoContents>(tile->contents()));
}

// Verifies that last_append_quads_scales_ is correctly updated after
// AppendQuads.
TEST_F(TileDisplayLayerImplTest, LastAppendQuadsScalesUpdated) {
  constexpr gfx::Size kLayerBounds(1300, 1900);
  constexpr gfx::Rect kLayerRect(kLayerBounds);

  auto layer = std::make_unique<TileDisplayLayerImpl>(
      CHECK_DEREF(host_impl()->active_tree()), /*id=*/42);
  auto* raw_layer = layer.get();
  host_impl()->active_tree()->AddLayer(std::move(layer));

  raw_layer->SetBounds(kLayerBounds);
  raw_layer->SetRecordedBounds(kLayerRect);
  raw_layer->draw_properties().visible_layer_rect = kLayerRect;
  raw_layer->draw_properties().opacity = 1.0;

  // Create two tilings with different scales.
  auto& low_res_tiling = raw_layer->GetOrCreateTilingFromScaleKey(1.0);
  low_res_tiling.SetTileSize(kLayerBounds);
  low_res_tiling.SetTilingRect(kLayerRect);
  auto& high_res_tiling = raw_layer->GetOrCreateTilingFromScaleKey(2.0);
  high_res_tiling.SetTileSize(kLayerBounds);
  high_res_tiling.SetTilingRect(kLayerRect);

  // Set content for both tilings.
  auto low_res_resource_id = host_impl()->resource_provider()->ImportResource(
      viz::TransferableResource::Make(
          gpu::ClientSharedImage::CreateForTesting(),
          viz::TransferableResource::ResourceSource::kTest, gpu::SyncToken()),
      base::DoNothing());
  TileDisplayLayerImpl::TileContents low_res_contents =
      TileDisplayLayerImpl::TileResource(low_res_resource_id, kLayerBounds);
  low_res_tiling.SetTileContents(TileIndex{0, 0}, low_res_contents,
                                 /*update_damage=*/true);

  auto high_res_resource_id = host_impl()->resource_provider()->ImportResource(
      viz::TransferableResource::Make(
          gpu::ClientSharedImage::CreateForTesting(),
          viz::TransferableResource::ResourceSource::kTest, gpu::SyncToken()),
      base::DoNothing());
  TileDisplayLayerImpl::TileContents high_res_contents =
      TileDisplayLayerTileResource(high_res_resource_id, kLayerBounds);
  high_res_tiling.SetTileContents(TileIndex{0, 0}, high_res_contents,
                                  /*update_damage=*/true);

  SetupRootProperties(host_impl()->active_tree()->root_layer());

  auto render_pass = viz::CompositorRenderPass::Create();
  AppendQuadsData data;
  bool has_missing_tiles = raw_layer->HasMissingTiles();
  raw_layer->AppendQuads(AppendQuadsContext{DRAW_MODE_SOFTWARE, {}, false},
                         render_pass.get(), &data);

  EXPECT_EQ(has_missing_tiles, data.num_missing_tiles > 0);

  // AppendQuads should use the ideal resolution tiling (1.0), so
  // last_append_quads_scales_ should contain 1.0.
  const auto& last_append_scales =
      raw_layer->GetLastAppendQuadsScalesForTesting();
  ASSERT_EQ(last_append_scales.size(), 1u);
  EXPECT_EQ(last_append_scales[0], 1.0f);
}

// Verifies that GetSafeToDeleteTilings returns the correct set of scales for
// tilings that are safe to delete.
TEST_F(TileDisplayLayerImplTest, GetSafeToDeleteTilingsBasic) {
  auto layer = std::make_unique<TileDisplayLayerImpl>(
      CHECK_DEREF(host_impl()->active_tree()), /*id=*/42);
  auto* raw_layer = layer.get();
  host_impl()->active_tree()->AddLayer(std::move(layer));

  // Case 1: Basic scenario - some tilings used, some not.
  raw_layer->GetLastAppendQuadsScalesForTesting() = {2.0f};
  raw_layer->SetProposedTilingScalesForDeletion({1.0f, 2.0f, 3.0f});

  std::vector<float> safe_to_delete = raw_layer->GetSafeToDeleteTilings();
  EXPECT_THAT(safe_to_delete, ElementsAre(1.0f, 3.0f));

  // Case 2: No tilings were used in the last frame.
  raw_layer->GetLastAppendQuadsScalesForTesting().clear();
  raw_layer->SetProposedTilingScalesForDeletion({1.0f, 2.0f, 3.0f});

  safe_to_delete = raw_layer->GetSafeToDeleteTilings();
  EXPECT_THAT(safe_to_delete, ElementsAre(1.0f, 2.0f, 3.0f));

  // Case 3: All proposed tilings were used in the last frame.
  raw_layer->GetLastAppendQuadsScalesForTesting() = {1.0f, 2.0f, 3.0f};
  raw_layer->SetProposedTilingScalesForDeletion({1.0f, 2.0f, 3.0f});

  safe_to_delete = raw_layer->GetSafeToDeleteTilings();
  EXPECT_TRUE(safe_to_delete.empty());

  // Case 4: Proposed tilings include some that don't exist in
  // last_append_quads_scales_.
  raw_layer->GetLastAppendQuadsScalesForTesting() = {2.0f};

  // 4.0 is not in last_append_quads_scales_
  raw_layer->SetProposedTilingScalesForDeletion({1.0f, 4.0f});

  safe_to_delete = raw_layer->GetSafeToDeleteTilings();
  EXPECT_THAT(safe_to_delete, ElementsAre(1.0f, 4.0f));
}

// Verifies that CleanUpTilings returns the correct set of scales for tilings
// that are safe to delete. This is similar to above but more of an integration
// style test since we are using ::AppendQuad() here and not directly using
// GetLastAppendQuadsScalesForTesting().
TEST_F(TileDisplayLayerImplTest, GetSafeToDeleteTilingsIntegration) {
  constexpr gfx::Size kLayerBounds(100, 100);
  constexpr gfx::Rect kLayerRect(kLayerBounds);

  auto layer = std::make_unique<TileDisplayLayerImpl>(
      CHECK_DEREF(host_impl()->active_tree()), /*id=*/42);
  auto* raw_layer = layer.get();
  host_impl()->active_tree()->AddLayer(std::move(layer));

  raw_layer->SetBounds(kLayerBounds);
  raw_layer->SetRecordedBounds(kLayerRect);
  raw_layer->draw_properties().visible_layer_rect = kLayerRect;
  raw_layer->draw_properties().opacity = 1.0;

  // Create three tilings.
  auto& tiling_1 = raw_layer->GetOrCreateTilingFromScaleKey(1.0);
  tiling_1.SetTileSize(kLayerBounds);
  tiling_1.SetTilingRect(kLayerRect);
  tiling_1.SetTileContents(TileIndex{0, 0}, SkColors::kRed, true);

  auto& tiling_2 = raw_layer->GetOrCreateTilingFromScaleKey(2.0);
  tiling_2.SetTileSize(kLayerBounds);
  tiling_2.SetTilingRect(kLayerRect);
  tiling_2.SetTileContents(TileIndex{0, 0}, SkColors::kGreen, true);

  auto& tiling_3 = raw_layer->GetOrCreateTilingFromScaleKey(3.0);
  tiling_3.SetTileSize(kLayerBounds);
  tiling_3.SetTilingRect(kLayerRect);
  tiling_3.SetTileContents(TileIndex{0, 0}, SkColors::kBlue, true);

  SetupRootProperties(host_impl()->active_tree()->root_layer());

  auto render_pass = viz::CompositorRenderPass::Create();
  AppendQuadsData data;
  bool has_missing_tiles = raw_layer->HasMissingTiles();
  raw_layer->AppendQuads(AppendQuadsContext{DRAW_MODE_SOFTWARE, {}, false},
                         render_pass.get(), &data);

  EXPECT_EQ(has_missing_tiles, data.num_missing_tiles > 0);

  // By default, the ideal resolution tiling (1.0) is used.
  raw_layer->SetProposedTilingScalesForDeletion({1.0, 2.0});
  std::vector<float> safe_to_delete = raw_layer->GetSafeToDeleteTilings();
  EXPECT_THAT(safe_to_delete, ElementsAre(2.0f));

  raw_layer->SetProposedTilingScalesForDeletion({1.0, 3.0});
  safe_to_delete = raw_layer->GetSafeToDeleteTilings();
  EXPECT_THAT(safe_to_delete, ElementsAre(3.0f));

  raw_layer->SetProposedTilingScalesForDeletion({3.0});
  safe_to_delete = raw_layer->GetSafeToDeleteTilings();
  EXPECT_THAT(safe_to_delete, ElementsAre(3.0f));

  raw_layer->SetProposedTilingScalesForDeletion({1.0, 2.0, 3.0});
  safe_to_delete = raw_layer->GetSafeToDeleteTilings();
  EXPECT_THAT(safe_to_delete, ElementsAre(2.0f, 3.0f));
}

// Verifies that AppendQuads() appends debug borders for a solid-color tile when
// they are enabled.
TEST_F(TileDisplayLayerImplTest,
       AppendQuadsAppendsDebugBordersForSolidColorTile) {
  // Enable debug borders.
  LayerTreeDebugState debug_state;
  debug_state.show_debug_borders.set(DebugBorderType::LAYER);
  host_impl()->SetDebugState(debug_state);

  // Set up the layer.
  constexpr gfx::Size kLayerBounds(100, 100);
  constexpr gfx::Rect kLayerRect(kLayerBounds);
  auto layer = std::make_unique<TileDisplayLayerImpl>(
      CHECK_DEREF(host_impl()->active_tree()), /*id=*/42);
  auto* raw_layer = layer.get();
  host_impl()->active_tree()->AddLayer(std::move(layer));
  raw_layer->SetBounds(kLayerBounds);
  raw_layer->SetRecordedBounds(kLayerRect);
  raw_layer->draw_properties().visible_layer_rect = kLayerRect;
  raw_layer->draw_properties().opacity = 1.0f;

  // Add a tiling with a solid-color tile.
  auto& tiling = raw_layer->GetOrCreateTilingFromScaleKey(1.0);
  tiling.SetTileSize(kLayerBounds);
  tiling.SetTilingRect(kLayerRect);
  tiling.SetTileContents(TileIndex{0, 0}, SkColors::kRed,
                         /*update_damage=*/true);

  SetupRootProperties(host_impl()->active_tree()->root_layer());

  // Append quads.
  auto render_pass = viz::CompositorRenderPass::Create();
  AppendQuadsData data;
  bool has_missing_tiles = raw_layer->HasMissingTiles();
  raw_layer->AppendQuads(AppendQuadsContext{DRAW_MODE_SOFTWARE, {}, false},
                         render_pass.get(), &data);

  EXPECT_EQ(has_missing_tiles, data.num_missing_tiles > 0);

  // Verify that a layer debug border, a tile debug border, and a content quad
  // were appended.
  ASSERT_EQ(render_pass->quad_list.size(), 3u);
  auto it = render_pass->quad_list.begin();
  EXPECT_EQ((*it)->material, viz::DrawQuad::Material::kDebugBorder);
  EXPECT_EQ(viz::DebugBorderDrawQuad::MaterialCast(*it)->color,
            DebugColors::ContainerLayerBorderColor());
  ++it;
  EXPECT_EQ((*it)->material, viz::DrawQuad::Material::kDebugBorder);
  EXPECT_EQ(viz::DebugBorderDrawQuad::MaterialCast(*it)->color,
            DebugColors::SolidColorTileBorderColor());
  ++it;
  EXPECT_EQ((*it)->material, viz::DrawQuad::Material::kSolidColor);
  EXPECT_EQ(viz::SolidColorDrawQuad::MaterialCast(*it)->color, SkColors::kRed);
}

// Verifies that AppendQuads() appends debug borders for a resource tile when
// debug borders are enabled.
TEST_F(TileDisplayLayerImplTest,
       AppendQuadsAppendsDebugBordersForResourceTile) {
  // Enable debug borders.
  LayerTreeDebugState debug_state;
  debug_state.show_debug_borders.set(DebugBorderType::LAYER);
  host_impl()->SetDebugState(debug_state);

  // Set up the layer.
  constexpr gfx::Size kLayerBounds(100, 100);
  constexpr gfx::Rect kLayerRect(kLayerBounds);
  auto layer = std::make_unique<TileDisplayLayerImpl>(
      CHECK_DEREF(host_impl()->active_tree()), /*id=*/42);
  auto* raw_layer = layer.get();
  host_impl()->active_tree()->AddLayer(std::move(layer));
  raw_layer->SetBounds(kLayerBounds);
  raw_layer->SetRecordedBounds(kLayerRect);
  raw_layer->draw_properties().visible_layer_rect = kLayerRect;
  raw_layer->draw_properties().opacity = 1.0f;

  // Add a tiling with a resource tile.
  auto& tiling = raw_layer->GetOrCreateTilingFromScaleKey(1.0);
  tiling.SetTileSize(kLayerBounds);
  tiling.SetTilingRect(kLayerRect);

  auto resource_id = host_impl()->resource_provider()->ImportResource(
      viz::TransferableResource::Make(
          gpu::ClientSharedImage::CreateForTesting(),
          viz::TransferableResource::ResourceSource::kTest, gpu::SyncToken()),
      base::DoNothing());
  TileDisplayLayerImpl::TileContents contents_resource =
      TileDisplayLayerTileResource(resource_id, kLayerBounds);
  tiling.SetTileContents(TileIndex{0, 0}, contents_resource,
                         /*update_damage=*/true);

  SetupRootProperties(host_impl()->active_tree()->root_layer());

  // Append quads.
  auto render_pass = viz::CompositorRenderPass::Create();
  AppendQuadsData data;
  bool has_missing_tiles = raw_layer->HasMissingTiles();
  raw_layer->AppendQuads(AppendQuadsContext{DRAW_MODE_SOFTWARE, {}, false},
                         render_pass.get(), &data);

  EXPECT_EQ(has_missing_tiles, data.num_missing_tiles > 0);

  // Verify that a layer debug border, a tile debug border, and a content quad
  // were appended.
  ASSERT_EQ(render_pass->quad_list.size(), 3u);
  auto it = render_pass->quad_list.begin();

  EXPECT_EQ((*it)->material, viz::DrawQuad::Material::kDebugBorder);
  EXPECT_EQ(viz::DebugBorderDrawQuad::MaterialCast(*it)->color,
            DebugColors::ContainerLayerBorderColor());
  ++it;
  EXPECT_EQ((*it)->material, viz::DrawQuad::Material::kDebugBorder);
  EXPECT_EQ(viz::DebugBorderDrawQuad::MaterialCast(*it)->color,
            DebugColors::HighResTileBorderColor());
  ++it;
  EXPECT_EQ((*it)->material, viz::DrawQuad::Material::kTiledContent);
  EXPECT_EQ(viz::TileDrawQuad::MaterialCast(*it)->resource_id, resource_id);
}

// Verifies that AppendQuads() appends debug borders for an extra-high-res
// resource tile when debug borders are enabled.
TEST_F(TileDisplayLayerImplTest,
       AppendQuadsAppendsDebugBordersForExtraHighResResourceTile) {
  // Enable debug borders.
  LayerTreeDebugState debug_state;
  debug_state.show_debug_borders.set(DebugBorderType::LAYER);
  host_impl()->SetDebugState(debug_state);

  // Set up the layer.
  constexpr gfx::Size kLayerBounds(100, 100);
  constexpr gfx::Rect kLayerRect(kLayerBounds);
  auto layer = std::make_unique<TileDisplayLayerImpl>(
      CHECK_DEREF(host_impl()->active_tree()), /*id=*/42);
  auto* raw_layer = layer.get();
  host_impl()->active_tree()->AddLayer(std::move(layer));
  raw_layer->SetBounds(kLayerBounds);
  raw_layer->SetRecordedBounds(kLayerRect);
  raw_layer->draw_properties().visible_layer_rect = kLayerRect;
  raw_layer->draw_properties().opacity = 1.0f;

  // Add an above-high-res tiling with a resource tile.
  auto& tiling = raw_layer->GetOrCreateTilingFromScaleKey(2.0);
  tiling.SetTileSize(kLayerBounds);
  tiling.SetTilingRect(kLayerRect);

  auto resource_id = host_impl()->resource_provider()->ImportResource(
      viz::TransferableResource::Make(
          gpu::ClientSharedImage::CreateForTesting(),
          viz::TransferableResource::ResourceSource::kTest, gpu::SyncToken()),
      base::DoNothing());
  TileDisplayLayerImpl::TileContents contents_resource =
      TileDisplayLayerTileResource(resource_id, kLayerBounds);
  tiling.SetTileContents(TileIndex{0, 0}, contents_resource,
                         /*update_damage=*/true);

  SetupRootProperties(host_impl()->active_tree()->root_layer());

  // Append quads.
  auto render_pass = viz::CompositorRenderPass::Create();
  AppendQuadsData data;
  bool has_missing_tiles = raw_layer->HasMissingTiles();
  raw_layer->AppendQuads(AppendQuadsContext{DRAW_MODE_SOFTWARE, {}, false},
                         render_pass.get(), &data);

  EXPECT_EQ(has_missing_tiles, data.num_missing_tiles > 0);

  // Verify that a layer debug border, a tile debug border, and a content quad
  // were appended.
  ASSERT_EQ(render_pass->quad_list.size(), 3u);
  auto it = render_pass->quad_list.begin();

  EXPECT_EQ((*it)->material, viz::DrawQuad::Material::kDebugBorder);
  EXPECT_EQ(viz::DebugBorderDrawQuad::MaterialCast(*it)->color,
            DebugColors::ContainerLayerBorderColor());
  ++it;
  EXPECT_EQ((*it)->material, viz::DrawQuad::Material::kDebugBorder);
  EXPECT_EQ(viz::DebugBorderDrawQuad::MaterialCast(*it)->color,
            DebugColors::AboveHighResTileBorderColor());
  ++it;
  EXPECT_EQ((*it)->material, viz::DrawQuad::Material::kTiledContent);
  EXPECT_EQ(viz::TileDrawQuad::MaterialCast(*it)->resource_id, resource_id);
}

// Verifies that AppendQuads() appends debug borders for an OOM tile when debug
// borders are enabled.
TEST_F(TileDisplayLayerImplTest, AppendQuadsAppendsDebugBordersForOomTile) {
  // Enable debug borders.
  LayerTreeDebugState debug_state;
  debug_state.show_debug_borders.set(DebugBorderType::LAYER);
  host_impl()->SetDebugState(debug_state);

  // Set up the layer.
  constexpr gfx::Size kLayerBounds(100, 100);
  constexpr gfx::Rect kLayerRect(kLayerBounds);
  auto layer = std::make_unique<TileDisplayLayerImpl>(
      CHECK_DEREF(host_impl()->active_tree()), /*id=*/42);
  auto* raw_layer = layer.get();
  host_impl()->active_tree()->AddLayer(std::move(layer));
  raw_layer->SetBounds(kLayerBounds);
  raw_layer->SetRecordedBounds(kLayerRect);
  raw_layer->draw_properties().visible_layer_rect = kLayerRect;
  raw_layer->draw_properties().opacity = 1.0f;

  // Add a tiling with an OOM tile.
  auto& tiling = raw_layer->GetOrCreateTilingFromScaleKey(1.0);
  tiling.SetTileSize(kLayerBounds);
  tiling.SetTilingRect(kLayerRect);
  tiling.SetTileContents(
      TileIndex{0, 0},
      TileDisplayLayerNoContents{mojom::MissingTileReason::kOutOfMemory},
      /*update_damage=*/true);

  SetupRootProperties(host_impl()->active_tree()->root_layer());

  // Append quads.
  auto render_pass = viz::CompositorRenderPass::Create();
  AppendQuadsData data;
  bool has_missing_tiles = raw_layer->HasMissingTiles();
  raw_layer->AppendQuads(AppendQuadsContext{DRAW_MODE_SOFTWARE, {}, false},
                         render_pass.get(), &data);

  EXPECT_EQ(has_missing_tiles, data.num_missing_tiles > 0);

  // Verify that a layer debug border, an OOM tile debug border, and a solid
  // color quad were appended.
  ASSERT_EQ(render_pass->quad_list.size(), 3u);
  auto it = render_pass->quad_list.begin();

  EXPECT_EQ((*it)->material, viz::DrawQuad::Material::kDebugBorder);
  EXPECT_EQ(viz::DebugBorderDrawQuad::MaterialCast(*it)->color,
            DebugColors::ContainerLayerBorderColor());
  ++it;
  EXPECT_EQ((*it)->material, viz::DrawQuad::Material::kDebugBorder);
  EXPECT_EQ(viz::DebugBorderDrawQuad::MaterialCast(*it)->color,
            DebugColors::OOMTileBorderColor());
  ++it;
  EXPECT_EQ((*it)->material, viz::DrawQuad::Material::kSolidColor);
  EXPECT_EQ(viz::SolidColorDrawQuad::MaterialCast(*it)->color,
            DebugColors::DefaultCheckerboardColor());
}

// Verifies that AppendQuads() appends debug borders for a not-ready tile when
// debug borders are enabled.
TEST_F(TileDisplayLayerImplTest,
       AppendQuadsAppendsDebugBordersForNotReadyTile) {
  // Enable debug borders.
  LayerTreeDebugState debug_state;
  debug_state.show_debug_borders.set(DebugBorderType::LAYER);
  host_impl()->SetDebugState(debug_state);

  // Set up the layer.
  constexpr gfx::Size kLayerBounds(100, 100);
  constexpr gfx::Rect kLayerRect(kLayerBounds);
  auto layer = std::make_unique<TileDisplayLayerImpl>(
      CHECK_DEREF(host_impl()->active_tree()), /*id=*/42);
  auto* raw_layer = layer.get();
  host_impl()->active_tree()->AddLayer(std::move(layer));
  raw_layer->SetBounds(kLayerBounds);
  raw_layer->SetRecordedBounds(kLayerRect);
  raw_layer->draw_properties().visible_layer_rect = kLayerRect;
  raw_layer->draw_properties().opacity = 1.0f;

  // Add a tiling with a not-ready tile.
  auto& tiling = raw_layer->GetOrCreateTilingFromScaleKey(1.0);
  tiling.SetTileSize(kLayerBounds);
  tiling.SetTilingRect(kLayerRect);
  tiling.SetTileContents(
      TileIndex{0, 0},
      TileDisplayLayerNoContents{mojom::MissingTileReason::kResourceNotReady},
      /*update_damage=*/true);

  SetupRootProperties(host_impl()->active_tree()->root_layer());

  // Append quads.
  auto render_pass = viz::CompositorRenderPass::Create();
  AppendQuadsData data;
  bool has_missing_tiles = raw_layer->HasMissingTiles();
  raw_layer->AppendQuads(AppendQuadsContext{DRAW_MODE_SOFTWARE, {}, false},
                         render_pass.get(), &data);

  EXPECT_EQ(has_missing_tiles, data.num_missing_tiles > 0);

  // Verify that a layer debug border, a missing tile debug border, and a
  // checkerboarded quad for the missing content were appended.
  ASSERT_EQ(render_pass->quad_list.size(), 3u);
  auto it = render_pass->quad_list.begin();

  EXPECT_EQ((*it)->material, viz::DrawQuad::Material::kDebugBorder);
  EXPECT_EQ(viz::DebugBorderDrawQuad::MaterialCast(*it)->color,
            DebugColors::ContainerLayerBorderColor());
  ++it;
  EXPECT_EQ((*it)->material, viz::DrawQuad::Material::kDebugBorder);
  EXPECT_EQ(viz::DebugBorderDrawQuad::MaterialCast(*it)->color,
            DebugColors::MissingTileBorderColor());
  ++it;
  EXPECT_EQ((*it)->material, viz::DrawQuad::Material::kSolidColor);
  EXPECT_EQ(viz::SolidColorDrawQuad::MaterialCast(*it)->color,
            DebugColors::DefaultCheckerboardColor());
}

TEST_F(TileDisplayLayerImplTest, TileResourceIsOOM) {
  auto layer = std::make_unique<TileDisplayLayerImpl>(
      CHECK_DEREF(host_impl()->active_tree()), /*id=*/42);
  auto* raw_layer = layer.get();
  host_impl()->active_tree()->AddLayer(std::move(layer));

  // Missing tile due to OOM.
  TileDisplayLayerImpl::TileContents oom_contents{
      TileDisplayLayerNoContents(mojom::MissingTileReason::kOutOfMemory)};
  TileDisplayLayerTile oom_tile(*raw_layer, oom_contents);
  EXPECT_TRUE(oom_tile.IsOOM());

  // OOM tiles should be regarded as ready to draw.
  EXPECT_TRUE(oom_tile.IsReadyToDraw());

  // Missing tile due to resource not being ready.
  TileDisplayLayerImpl::TileContents resource_not_ready_contents{
      TileDisplayLayerNoContents(mojom::MissingTileReason::kResourceNotReady)};
  TileDisplayLayerTile not_oom_tile(*raw_layer, resource_not_ready_contents);
  EXPECT_FALSE(not_oom_tile.IsOOM());

  // Non-OOM missing tiles should not be regarded as ready to draw.
  EXPECT_FALSE(not_oom_tile.IsReadyToDraw());

  // Solid color tile.
  TileDisplayLayerImpl::TileContents color_contents(SkColors::kRed);
  TileDisplayLayerTile color_tile(*raw_layer, color_contents);
  EXPECT_FALSE(color_tile.IsOOM());
  EXPECT_TRUE(color_tile.IsReadyToDraw());

  // Resource tile.
  auto resource_id = host_impl()->resource_provider()->ImportResource(
      viz::TransferableResource::Make(
          gpu::ClientSharedImage::CreateForTesting(),
          viz::TransferableResource::ResourceSource::kTest, gpu::SyncToken()),
      base::DoNothing());
  TileDisplayLayerImpl::TileContents resource_contents =
      TileDisplayLayerTileResource(resource_id, gfx::Size(1, 1));
  TileDisplayLayerTile resource_tile(*raw_layer, resource_contents);
  EXPECT_FALSE(resource_tile.IsOOM());
  EXPECT_TRUE(resource_tile.IsReadyToDraw());
}

}  // namespace cc
