chromium/chrome/browser/thumbnail/cc/thumbnail_cache_unittest.cc

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

#include "chrome/browser/thumbnail/cc/thumbnail_cache.h"

#include "base/memory/weak_ptr.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/task_environment.h"
#include "cc/resources/ui_resource_client.h"
#include "content/public/test/browser_task_environment.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/android/ui_android_export.h"

namespace thumbnail {
namespace {

constexpr int kKiB = 1024;
constexpr int kN32PixelSize = 4;
constexpr int kDefaultCacheSize = 3;
constexpr int kCompressionQueueMaxSize = 2;
constexpr int kWriteQueueMaxSize = 2;

class MockUIResourceProvider : public ui::UIResourceProvider {
 public:
  MOCK_METHOD(cc::UIResourceId,
              CreateUIResource,
              (cc::UIResourceClient*),
              (override));
  MOCK_METHOD(void, DeleteUIResource, (cc::UIResourceId), (override));
  MOCK_METHOD(bool, SupportsETC1NonPowerOfTwo, (), (const, override));

  base::WeakPtr<UIResourceProvider> GetWeakPtr() {
    return weak_factory_.GetWeakPtr();
  }

 private:
  base::WeakPtrFactory<MockUIResourceProvider> weak_factory_{this};
};

}  // namespace

class ThumbnailCacheTest : public ::testing::Test {
 protected:
  void SetUp() override {
    thumbnail_cache_ = std::make_unique<ThumbnailCache>(
        kDefaultCacheSize, kCompressionQueueMaxSize, kWriteQueueMaxSize,
        /*save_jpeg_thumbnails=*/true);
    thumbnail_cache_->SetUIResourceProvider(ui_resource_provider_.GetWeakPtr());

    EXPECT_CALL(ui_resource_provider_, CreateUIResource(::testing::_))
        .WillRepeatedly(::testing::Return(1));
  }

  void TearDown() override {}

  ThumbnailCache& thumbnail_cache() { return *thumbnail_cache_; }
  void RecordCacheMetrics() { thumbnail_cache_->RecordCacheMetrics(); }

  content::BrowserTaskEnvironment task_environment_;

 private:
  MockUIResourceProvider ui_resource_provider_;
  std::unique_ptr<ThumbnailCache> thumbnail_cache_;
};

// TODO(crbug.com/40885026): Tests are being added in the process of refactoring
// and optimizing the ThumbnailCache for modern usage add more tests here.

TEST_F(ThumbnailCacheTest, PruneCache) {
  constexpr int kTabId1 = 1;
  constexpr int kTabId2 = 2;
  constexpr int kDimension = 16;
  SkBitmap bitmap;
  ASSERT_TRUE(bitmap.tryAllocN32Pixels(kDimension * kKiB, kDimension));
  bitmap.setImmutable();

  thumbnail_cache().UpdateVisibleIds(std::vector<TabId>({kTabId1, kTabId2}),
                                     -1);
  EXPECT_TRUE(thumbnail_cache().CheckAndUpdateThumbnailMetaData(
      kTabId1, GURL("https://www.foo.com/"), /*force_update=*/false));
  std::unique_ptr<ThumbnailCaptureTracker, base::OnTaskRunnerDeleter> tracker1(
      new ThumbnailCaptureTracker(base::DoNothing()),
      base::OnTaskRunnerDeleter(
          base::SequencedTaskRunner::GetCurrentDefault()));
  thumbnail_cache().Put(kTabId1, std::move(tracker1), bitmap,
                        /*thumbnail_scale=*/1.0f);

  EXPECT_TRUE(thumbnail_cache().CheckAndUpdateThumbnailMetaData(
      kTabId2, GURL("https://www.bar.com/"), /*force_update=*/false));
  std::unique_ptr<ThumbnailCaptureTracker, base::OnTaskRunnerDeleter> tracker2(
      new ThumbnailCaptureTracker(base::DoNothing()),
      base::OnTaskRunnerDeleter(
          base::SequencedTaskRunner::GetCurrentDefault()));
  thumbnail_cache().Put(kTabId2, std::move(tracker2), bitmap,
                        /*thumbnail_scale=*/1.0f);

  EXPECT_TRUE(thumbnail_cache().Get(kTabId1, false));
  EXPECT_TRUE(thumbnail_cache().Get(kTabId2, false));

  thumbnail_cache().UpdateVisibleIds({kTabId1, kTabId2}, kTabId1);
  EXPECT_TRUE(thumbnail_cache().Get(kTabId1, false));
  EXPECT_TRUE(thumbnail_cache().Get(kTabId2, false));

  thumbnail_cache().UpdateVisibleIds({kTabId1, kTabId2}, -1);
  EXPECT_TRUE(thumbnail_cache().Get(kTabId1, false));
  EXPECT_TRUE(thumbnail_cache().Get(kTabId2, false));

  thumbnail_cache().UpdateVisibleIds({kTabId2}, kTabId1);
  EXPECT_FALSE(thumbnail_cache().Get(kTabId1, false));
  EXPECT_TRUE(thumbnail_cache().Get(kTabId2, false));

  thumbnail_cache().UpdateVisibleIds({kTabId1}, kTabId1);
  // The thumbnail will not be paged in yet although will be scheduled.
  EXPECT_FALSE(thumbnail_cache().Get(kTabId1, false));
  EXPECT_FALSE(thumbnail_cache().Get(kTabId2, false));
}

TEST_F(ThumbnailCacheTest, MetricsEmission) {
  base::HistogramTester histograms;
  histograms.ExpectTotalCount("Android.ThumbnailCache.InMemoryCacheEntries", 0);
  histograms.ExpectTotalCount("Android.ThumbnailCache.InMemoryCacheSize", 0);

  SkBitmap bitmap;
  constexpr int kTabId = 4;
  constexpr int kDimension = 4;
  ASSERT_TRUE(bitmap.tryAllocN32Pixels(kDimension * kKiB, kDimension));
  bitmap.setImmutable();
  thumbnail_cache().UpdateVisibleIds(std::vector<TabId>({kTabId}), -1);
  EXPECT_TRUE(thumbnail_cache().CheckAndUpdateThumbnailMetaData(
      kTabId, GURL("https://www.foo.com/"), /*force_update=*/false));
  std::unique_ptr<ThumbnailCaptureTracker, base::OnTaskRunnerDeleter> tracker(
      new ThumbnailCaptureTracker(base::DoNothing()),
      base::OnTaskRunnerDeleter(
          base::SequencedTaskRunner::GetCurrentDefault()));
  thumbnail_cache().Put(kTabId, std::move(tracker), bitmap,
                        /*thumbnail_scale=*/1.0f);
  RecordCacheMetrics();

  histograms.ExpectTotalCount("Android.ThumbnailCache.InMemoryCacheEntries", 1);
  histograms.ExpectTotalCount("Android.ThumbnailCache.InMemoryCacheSize", 1);
  histograms.ExpectUniqueSample("Android.ThumbnailCache.InMemoryCacheEntries",
                                1, 1);
  histograms.ExpectUniqueSample("Android.ThumbnailCache.InMemoryCacheSize",
                                kDimension * kDimension * kN32PixelSize, 1);
}

TEST_F(ThumbnailCacheTest, InvalidateIfChanged) {
  constexpr int kTabId1 = 1;
  constexpr int kDimension = 16;
  SkBitmap bitmap;
  ASSERT_TRUE(bitmap.tryAllocN32Pixels(kDimension * kKiB, kDimension));
  bitmap.setImmutable();

  thumbnail_cache().UpdateVisibleIds(std::vector<TabId>({kTabId1}), -1);
  GURL url("https://www.foo.com/");
  EXPECT_TRUE(thumbnail_cache().CheckAndUpdateThumbnailMetaData(
      kTabId1, url, /*force_update=*/false));
  std::unique_ptr<ThumbnailCaptureTracker, base::OnTaskRunnerDeleter> tracker1(
      new ThumbnailCaptureTracker(base::DoNothing()),
      base::OnTaskRunnerDeleter(
          base::SequencedTaskRunner::GetCurrentDefault()));
  thumbnail_cache().Put(kTabId1, std::move(tracker1), bitmap,
                        /*thumbnail_scale=*/1.0f);

  EXPECT_TRUE(thumbnail_cache().Get(kTabId1, false));

  thumbnail_cache().InvalidateThumbnailIfChanged(kTabId1, url);
  EXPECT_TRUE(thumbnail_cache().Get(kTabId1, false));

  thumbnail_cache().InvalidateThumbnailIfChanged(kTabId1, GURL());
  EXPECT_TRUE(thumbnail_cache().Get(kTabId1, false));

  thumbnail_cache().InvalidateThumbnailIfChanged(kTabId1,
                                                 GURL("https://www.bar.com/"));
  EXPECT_FALSE(thumbnail_cache().Get(kTabId1, false));
}

}  // namespace thumbnail