910e62b5创建于 1月15日历史提交
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ash/quick_insert/quick_insert_asset_fetcher_impl.h"

#include <string>
#include <utility>
#include <vector>

#include "ash/public/cpp/image_util.h"
#include "ash/public/cpp/test/in_process_data_decoder.h"
#include "ash/quick_insert/quick_insert_asset_fetcher.h"
#include "ash/quick_insert/quick_insert_asset_fetcher_impl_delegate.h"
#include "ash/test/ash_test_util.h"
#include "base/files/file.h"
#include "base/files/file_path.h"
#include "base/functional/bind.h"
#include "base/memory/scoped_refptr.h"
#include "base/notreached.h"
#include "base/test/bind.h"
#include "base/test/task_environment.h"
#include "base/test/test_future.h"
#include "net/http/http_status_code.h"
#include "services/network/public/cpp/resource_request.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "services/network/public/cpp/simple_url_loader.h"
#include "services/network/test/test_url_loader_factory.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/image/image_skia.h"
#include "url/gurl.h"

namespace ash {
namespace {

using ::testing::_;
using ::testing::FieldsAre;
using ::testing::Return;

class MockQuickInsertAssetUrlLoaderFactory
    : public network::SharedURLLoaderFactory {
 public:
  MockQuickInsertAssetUrlLoaderFactory() = default;
  MockQuickInsertAssetUrlLoaderFactory(
      const MockQuickInsertAssetUrlLoaderFactory&) = delete;
  MockQuickInsertAssetUrlLoaderFactory& operator=(
      const MockQuickInsertAssetUrlLoaderFactory&) = delete;

  // network::SharedURLLoaderFactory
  void CreateLoaderAndStart(
      mojo::PendingReceiver<network::mojom::URLLoader> receiver,
      int32_t request_id,
      uint32_t options,
      const network::ResourceRequest& url_request,
      mojo::PendingRemote<network::mojom::URLLoaderClient> client,
      const net::MutableNetworkTrafficAnnotationTag& traffic_annotation)
      override {
    test_url_loader_factory_.CreateLoaderAndStart(
        std::move(receiver), request_id, options, url_request,
        std::move(client), traffic_annotation);
  }

  void Clone(mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver)
      override {
    NOTREACHED();
  }

  std::unique_ptr<network::PendingSharedURLLoaderFactory> Clone() override {
    NOTREACHED();
  }

  void AddResponse(const GURL& url,
                   const std::string& content,
                   net::HttpStatusCode status) {
    test_url_loader_factory_.AddResponse(url.spec(), content, status);
  }

  size_t GetTotalRequests() const {
    return test_url_loader_factory_.total_requests();
  }

 protected:
  ~MockQuickInsertAssetUrlLoaderFactory() override = default;

 private:
  network::TestURLLoaderFactory test_url_loader_factory_;
};

class MockQuickInsertAssetFetcherDelegate
    : public QuickInsertAssetFetcherImplDelegate {
 public:
  MOCK_METHOD(scoped_refptr<network::SharedURLLoaderFactory>,
              GetSharedURLLoaderFactory,
              (),
              (override));
  MOCK_METHOD(void,
              FetchFileThumbnail,
              (const base::FilePath& path,
               const gfx::Size& size,
               FetchFileThumbnailCallback callback),
              (override));
};

class QuickInsertAssetFetcherImplTest : public testing::Test {
 public:
  QuickInsertAssetFetcherImplTest() = default;
  QuickInsertAssetFetcherImplTest(const QuickInsertAssetFetcherImplTest&) =
      delete;
  QuickInsertAssetFetcherImplTest& operator=(
      const QuickInsertAssetFetcherImplTest&) = delete;
  ~QuickInsertAssetFetcherImplTest() override = default;

 private:
  base::test::SingleThreadTaskEnvironment environment_;
  InProcessDataDecoder decoder_;
};

TEST_F(QuickInsertAssetFetcherImplTest,
       FetchGifReturnsEmptyOnFailedNetworkRequest) {
  scoped_refptr<MockQuickInsertAssetUrlLoaderFactory> url_loader_factory =
      base::MakeRefCounted<MockQuickInsertAssetUrlLoaderFactory>();
  const GURL kGifUrl("https://media.tenor.com/invalid-gif.gif");
  url_loader_factory->AddResponse(kGifUrl, "", net::HTTP_NOT_FOUND);
  MockQuickInsertAssetFetcherDelegate mock_delegate;
  EXPECT_CALL(mock_delegate, GetSharedURLLoaderFactory)
      .WillRepeatedly(Return(url_loader_factory));
  QuickInsertAssetFetcherImpl asset_fetcher(&mock_delegate);

  base::test::TestFuture<std::vector<image_util::AnimationFrame>> future;
  std::unique_ptr<network::SimpleURLLoader> loader =
      asset_fetcher.FetchGifFromUrl(kGifUrl, /*rank=*/0, future.GetCallback());

  EXPECT_TRUE(future.Get().empty());
}

TEST_F(QuickInsertAssetFetcherImplTest, FetchesGifPreviewImageFromTenorUrl) {
  scoped_refptr<MockQuickInsertAssetUrlLoaderFactory> url_loader_factory =
      base::MakeRefCounted<MockQuickInsertAssetUrlLoaderFactory>();
  const GURL kGifPreviewImageUrl(
      "https://media.tenor.com/gif-image-preview.png");
  constexpr gfx::Size kGifPreviewImageDimensions(10, 20);
  url_loader_factory->AddResponse(
      kGifPreviewImageUrl,
      CreateEncodedImageForTesting(kGifPreviewImageDimensions), net::HTTP_OK);
  MockQuickInsertAssetFetcherDelegate mock_delegate;
  EXPECT_CALL(mock_delegate, GetSharedURLLoaderFactory)
      .WillRepeatedly(Return(url_loader_factory));
  QuickInsertAssetFetcherImpl asset_fetcher(&mock_delegate);

  base::test::TestFuture<const gfx::ImageSkia&> future;
  std::unique_ptr<network::SimpleURLLoader> loader =
      asset_fetcher.FetchGifPreviewImageFromUrl(kGifPreviewImageUrl,
                                                /*rank=*/0,
                                                future.GetCallback());

  EXPECT_FALSE(future.Get().isNull());
  EXPECT_EQ(future.Get().size(), kGifPreviewImageDimensions);
}

TEST_F(QuickInsertAssetFetcherImplTest,
       DoesNotFetchGifPreviewImageFromNonTenorUrl) {
  scoped_refptr<MockQuickInsertAssetUrlLoaderFactory> url_loader_factory =
      base::MakeRefCounted<MockQuickInsertAssetUrlLoaderFactory>();
  const GURL kNonTenorUrl("https://media.nottenor.com/gif-image-preview.png");
  url_loader_factory->AddResponse(
      kNonTenorUrl, CreateEncodedImageForTesting(gfx::Size(10, 20)),
      net::HTTP_OK);
  MockQuickInsertAssetFetcherDelegate mock_delegate;
  EXPECT_CALL(mock_delegate, GetSharedURLLoaderFactory)
      .WillRepeatedly(Return(url_loader_factory));
  QuickInsertAssetFetcherImpl asset_fetcher(&mock_delegate);

  base::test::TestFuture<const gfx::ImageSkia&> future;
  std::unique_ptr<network::SimpleURLLoader> loader =
      asset_fetcher.FetchGifPreviewImageFromUrl(kNonTenorUrl, /*rank=*/0,
                                                future.GetCallback());

  EXPECT_TRUE(future.Get().isNull());
}

TEST_F(QuickInsertAssetFetcherImplTest, ForwardsToDelegateToFetchThumbnail) {
  MockQuickInsertAssetFetcherDelegate mock_delegate;
  base::test::TestFuture<base::FilePath, gfx::Size,
                         QuickInsertAssetFetcher::FetchFileThumbnailCallback>
      future;
  EXPECT_CALL(mock_delegate, FetchFileThumbnail)
      .WillOnce(
          [&](const base::FilePath& path, const gfx::Size& size,
              QuickInsertAssetFetcher::FetchFileThumbnailCallback callback) {
            future.SetValue(path, size, std::move(callback));
          });
  QuickInsertAssetFetcherImpl asset_fetcher(&mock_delegate);

  const base::FilePath kPath("test/image.png");
  constexpr gfx::Size kThumbnailSize(10, 20);
  base::test::TestFuture<const SkBitmap*, base::File::Error> callback_future;
  asset_fetcher.FetchFileThumbnail(kPath, kThumbnailSize,
                                   callback_future.GetCallback());

  auto [path, size, callback] = future.Take();
  EXPECT_EQ(path, kPath);
  EXPECT_EQ(size, kThumbnailSize);
  EXPECT_FALSE(callback_future.IsReady());
  const SkBitmap* kBitmap = nullptr;
  const base::File::Error kError = base::File::Error::FILE_ERROR_FAILED;
  std::move(callback).Run(kBitmap, kError);
  EXPECT_THAT(callback_future.Take(), FieldsAre(kBitmap, kError));
}

TEST_F(QuickInsertAssetFetcherImplTest, DelaysRequestsWithLargeRank) {
  scoped_refptr<MockQuickInsertAssetUrlLoaderFactory> url_loader_factory =
      base::MakeRefCounted<MockQuickInsertAssetUrlLoaderFactory>();
  const GURL kGifPreviewImageUrl(
      "https://media.tenor.com/gif-image-preview.png");
  constexpr gfx::Size kGifPreviewImageDimensions(10, 20);
  url_loader_factory->AddResponse(
      kGifPreviewImageUrl,
      CreateEncodedImageForTesting(kGifPreviewImageDimensions), net::HTTP_OK);
  MockQuickInsertAssetFetcherDelegate mock_delegate;
  EXPECT_CALL(mock_delegate, GetSharedURLLoaderFactory)
      .WillRepeatedly(Return(url_loader_factory));
  QuickInsertAssetFetcherImpl asset_fetcher(&mock_delegate);

  base::test::TestFuture<const gfx::ImageSkia&> future;
  std::unique_ptr<network::SimpleURLLoader> loader =
      asset_fetcher.FetchGifPreviewImageFromUrl(kGifPreviewImageUrl,
                                                /*rank=*/10,
                                                future.GetCallback());

  EXPECT_EQ(url_loader_factory->GetTotalRequests(), 0u);
  // The request should be processed eventually.
  EXPECT_TRUE(future.Wait());
  EXPECT_EQ(url_loader_factory->GetTotalRequests(), 1u);
}

}  // namespace
}  // namespace ash