[go: nahoru, domu]

blob: 418724a2dae2c9f72c088698431ec817d9281b06 [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/feed/core/feed_image_manager.h"
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/files/scoped_temp_dir.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/mock_callback.h"
#include "base/test/scoped_task_environment.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "base/timer/timer.h"
#include "components/image_fetcher/core/image_decoder.h"
#include "components/image_fetcher/core/image_fetcher_impl.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.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/image/image.h"
#include "ui/gfx/image/image_unittest_util.h"
using base::HistogramTester;
using testing::_;
namespace feed {
namespace {
const char kImageURL[] = "http://pie.com/";
const char kImageURL2[] = "http://cake.com/";
const char kImageData[] = "pie image";
const char kImageData2[] = "cake image";
const char kUmaImageLoadSuccessHistogramName[] =
"ContentSuggestions.Feed.Image.FetchResult";
const char kUmaCacheLoadHistogramName[] =
"ContentSuggestions.Feed.Image.LoadFromCacheTime";
const char kUmaNetworkLoadHistogramName[] =
"ContentSuggestions.Feed.Image.LoadFromNetworkTime";
// Keep in sync with DIMENSION_UNKNOWN in third_party/feed/src/main/java/com/
// google/android/libraries/feed/host/imageloader/ImageLoaderApi.java.
const int DIMENSION_UNKNOWN = -1;
class FakeImageDecoder : public image_fetcher::ImageDecoder {
public:
void DecodeImage(
const std::string& image_data,
const gfx::Size& desired_image_frame_size,
const image_fetcher::ImageDecodedCallback& callback) override {
desired_image_frame_size_ = desired_image_frame_size;
gfx::Image image;
if (valid_ && !image_data.empty()) {
ASSERT_EQ(image_data_, image_data);
image = gfx::test::CreateImage();
}
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindRepeating(callback, image));
}
void SetDecodingValid(bool valid) { valid_ = valid; }
void SetExpectedData(std::string data) { image_data_ = data; }
gfx::Size GetDesiredImageFrameSize() { return desired_image_frame_size_; }
private:
bool valid_ = true;
std::string image_data_;
gfx::Size desired_image_frame_size_;
};
} // namespace
class FeedImageManagerTest : public testing::Test {
public:
FeedImageManagerTest() {}
~FeedImageManagerTest() override {
feed_image_manager_.reset();
// We need to run until idle after deleting the database, because
// ProtoDatabase deletes the actual LevelDB asynchronously.
RunUntilIdle();
}
void SetUp() override {
ASSERT_TRUE(database_dir_.CreateUniqueTempDir());
std::unique_ptr<FeedImageDatabase> image_database =
std::make_unique<FeedImageDatabase>(database_dir_.GetPath());
image_database_ = image_database.get();
shared_factory_ =
base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
&test_url_loader_factory_);
auto decoder = std::make_unique<FakeImageDecoder>();
decoder->SetExpectedData(kImageData);
fake_image_decoder_ = decoder.get();
feed_image_manager_ = std::make_unique<FeedImageManager>(
std::make_unique<image_fetcher::ImageFetcherImpl>(std::move(decoder),
shared_factory_),
std::move(image_database));
RunUntilIdle();
ASSERT_TRUE(image_database_->IsInitialized());
}
void RunUntilIdle() { scoped_task_environment_.RunUntilIdle(); }
FeedImageDatabase* image_database() { return image_database_; }
FeedImageManager* feed_image_manager() { return feed_image_manager_.get(); }
base::OneShotTimer& garbage_collection_timer() {
return feed_image_manager_->garbage_collection_timer_;
}
void StartGarbageCollection() {
feed_image_manager_->DoGarbageCollectionIfNeeded();
}
void StopGarbageCollection() { feed_image_manager_->StopGarbageCollection(); }
FakeImageDecoder* fake_image_decoder() { return fake_image_decoder_; }
network::TestURLLoaderFactory* test_url_loader_factory() {
return &test_url_loader_factory_;
}
HistogramTester& histogram() { return histogram_; }
MOCK_METHOD1(OnImageLoaded, void(const std::string&));
private:
network::TestURLLoaderFactory test_url_loader_factory_;
scoped_refptr<network::SharedURLLoaderFactory> shared_factory_;
std::unique_ptr<FeedImageManager> feed_image_manager_;
FeedImageDatabase* image_database_;
base::ScopedTempDir database_dir_;
FakeImageDecoder* fake_image_decoder_;
base::test::ScopedTaskEnvironment scoped_task_environment_;
HistogramTester histogram_;
DISALLOW_COPY_AND_ASSIGN(FeedImageManagerTest);
};
TEST_F(FeedImageManagerTest, FetchEmptyUrlVector) {
base::MockCallback<ImageFetchedCallback> image_callback;
// Make sure an empty image passed to callback.
EXPECT_CALL(
image_callback,
Run(testing::Property(&gfx::Image::IsEmpty, testing::Eq(true)), -1));
feed_image_manager()->FetchImage(std::vector<std::string>(),
DIMENSION_UNKNOWN, DIMENSION_UNKNOWN,
image_callback.Get());
RunUntilIdle();
}
TEST_F(FeedImageManagerTest, FetchImageFromCache) {
// Save the image in the database.
image_database()->SaveImage(kImageURL, kImageData);
RunUntilIdle();
base::MockCallback<ImageFetchedCallback> image_callback;
EXPECT_CALL(
image_callback,
Run(testing::Property(&gfx::Image::IsEmpty, testing::Eq(false)), 0));
feed_image_manager()->FetchImage(std::vector<std::string>({kImageURL}), 100,
200, image_callback.Get());
RunUntilIdle();
ASSERT_EQ(fake_image_decoder()->GetDesiredImageFrameSize().width(), 100);
ASSERT_EQ(fake_image_decoder()->GetDesiredImageFrameSize().height(), 200);
}
TEST_F(FeedImageManagerTest, FetchImagePopulatesCache) {
// Expect the image to be fetched by URL.
{
test_url_loader_factory()->AddResponse(kImageURL, kImageData);
base::MockCallback<ImageFetchedCallback> image_callback;
EXPECT_CALL(
image_callback,
Run(testing::Property(&gfx::Image::IsEmpty, testing::Eq(false)), 0));
feed_image_manager()->FetchImage(std::vector<std::string>({kImageURL}),
DIMENSION_UNKNOWN, DIMENSION_UNKNOWN,
image_callback.Get());
RunUntilIdle();
}
// Make sure the image data is in the database.
{
EXPECT_CALL(*this, OnImageLoaded(kImageData));
image_database()->LoadImage(
kImageURL, base::BindOnce(&FeedImageManagerTest::OnImageLoaded,
base::Unretained(this)));
RunUntilIdle();
}
// Fetch again. The cache should be populated, no network request is needed.
{
test_url_loader_factory()->ClearResponses();
base::MockCallback<ImageFetchedCallback> image_callback;
EXPECT_CALL(
image_callback,
Run(testing::Property(&gfx::Image::IsEmpty, testing::Eq(false)), 0));
feed_image_manager()->FetchImage(std::vector<std::string>({kImageURL}),
DIMENSION_UNKNOWN, DIMENSION_UNKNOWN,
image_callback.Get());
RunUntilIdle();
}
}
TEST_F(FeedImageManagerTest, FetchSecondImageIfFirstFailed) {
// Expect the image to be fetched by URL.
{
test_url_loader_factory()->AddResponse(kImageURL, kImageData,
net::HTTP_NOT_FOUND);
test_url_loader_factory()->AddResponse(kImageURL2, kImageData2);
base::MockCallback<ImageFetchedCallback> image_callback;
EXPECT_CALL(
image_callback,
Run(testing::Property(&gfx::Image::IsEmpty, testing::Eq(false)), 1));
fake_image_decoder()->SetExpectedData(kImageData2);
feed_image_manager()->FetchImage(
std::vector<std::string>({kImageURL, kImageURL2}), DIMENSION_UNKNOWN,
DIMENSION_UNKNOWN, image_callback.Get());
RunUntilIdle();
}
// Make sure the image data is in the database.
{
EXPECT_CALL(*this, OnImageLoaded(kImageData2));
image_database()->LoadImage(
kImageURL2, base::BindOnce(&FeedImageManagerTest::OnImageLoaded,
base::Unretained(this)));
RunUntilIdle();
}
}
TEST_F(FeedImageManagerTest, DecodingErrorWillDeleteCache) {
// Save the image in the database.
image_database()->SaveImage(kImageURL, kImageData);
RunUntilIdle();
{
test_url_loader_factory()->AddResponse(kImageURL, kImageData);
// Set decoding always error.
fake_image_decoder()->SetDecodingValid(false);
base::MockCallback<ImageFetchedCallback> image_callback;
EXPECT_CALL(
image_callback,
Run(testing::Property(&gfx::Image::IsEmpty, testing::Eq(true)), -1));
feed_image_manager()->FetchImage(std::vector<std::string>({kImageURL}),
DIMENSION_UNKNOWN, DIMENSION_UNKNOWN,
image_callback.Get());
RunUntilIdle();
}
// Make sure the image data was deleted from database.
{
EXPECT_CALL(*this, OnImageLoaded(std::string()));
image_database()->LoadImage(
kImageURL, base::BindOnce(&FeedImageManagerTest::OnImageLoaded,
base::Unretained(this)));
RunUntilIdle();
}
}
TEST_F(FeedImageManagerTest, GarbageCollectionRunOnStart) {
// Garbage Collection is scheduled by default.
EXPECT_TRUE(garbage_collection_timer().IsRunning());
StopGarbageCollection();
EXPECT_FALSE(garbage_collection_timer().IsRunning());
// StartGarbageCollection will start the garbage collection, but won't
// schedule the next one.
StartGarbageCollection();
EXPECT_FALSE(garbage_collection_timer().IsRunning());
// After finishing the garbage collection cycle, the next garbage collection
// will be scheduled.
RunUntilIdle();
EXPECT_TRUE(garbage_collection_timer().IsRunning());
}
TEST_F(FeedImageManagerTest, InvalidUrlHistogramFailure) {
base::MockCallback<ImageFetchedCallback> image_callback;
feed_image_manager()->FetchImage(std::vector<std::string>({""}),
DIMENSION_UNKNOWN, DIMENSION_UNKNOWN,
image_callback.Get());
RunUntilIdle();
histogram().ExpectTotalCount(kUmaCacheLoadHistogramName, 0);
histogram().ExpectTotalCount(kUmaNetworkLoadHistogramName, 0);
histogram().ExpectTotalCount(kUmaImageLoadSuccessHistogramName, 1);
histogram().ExpectBucketCount(kUmaImageLoadSuccessHistogramName,
FeedImageFetchResult::kFailure, 1);
}
TEST_F(FeedImageManagerTest, FetchImageFromCachHistogram) {
// Save the image in the database.
image_database()->SaveImage(kImageURL, kImageData);
RunUntilIdle();
base::MockCallback<ImageFetchedCallback> image_callback;
feed_image_manager()->FetchImage(std::vector<std::string>({kImageURL}),
DIMENSION_UNKNOWN, DIMENSION_UNKNOWN,
image_callback.Get());
RunUntilIdle();
histogram().ExpectTotalCount(kUmaCacheLoadHistogramName, 1);
histogram().ExpectTotalCount(kUmaNetworkLoadHistogramName, 0);
histogram().ExpectTotalCount(kUmaImageLoadSuccessHistogramName, 1);
histogram().ExpectBucketCount(kUmaImageLoadSuccessHistogramName,
FeedImageFetchResult::kSuccessCached, 1);
}
TEST_F(FeedImageManagerTest, FetchImageFromNetworkHistogram) {
test_url_loader_factory()->AddResponse(kImageURL, kImageData);
base::MockCallback<ImageFetchedCallback> image_callback;
feed_image_manager()->FetchImage(std::vector<std::string>({kImageURL}),
DIMENSION_UNKNOWN, DIMENSION_UNKNOWN,
image_callback.Get());
RunUntilIdle();
histogram().ExpectTotalCount(kUmaCacheLoadHistogramName, 0);
histogram().ExpectTotalCount(kUmaNetworkLoadHistogramName, 1);
histogram().ExpectTotalCount(kUmaImageLoadSuccessHistogramName, 1);
histogram().ExpectBucketCount(kUmaImageLoadSuccessHistogramName,
FeedImageFetchResult::kSuccessFetched, 1);
}
TEST_F(FeedImageManagerTest, FetchImageFromNetworkEmptyHistogram) {
test_url_loader_factory()->AddResponse(kImageURL, "");
base::MockCallback<ImageFetchedCallback> image_callback;
feed_image_manager()->FetchImage(std::vector<std::string>({kImageURL}),
DIMENSION_UNKNOWN, DIMENSION_UNKNOWN,
image_callback.Get());
RunUntilIdle();
histogram().ExpectTotalCount(kUmaCacheLoadHistogramName, 0);
histogram().ExpectTotalCount(kUmaNetworkLoadHistogramName, 0);
histogram().ExpectTotalCount(kUmaImageLoadSuccessHistogramName, 1);
histogram().ExpectBucketCount(kUmaImageLoadSuccessHistogramName,
FeedImageFetchResult::kFailure, 1);
}
TEST_F(FeedImageManagerTest, NetworkDecodingErrorHistogram) {
test_url_loader_factory()->AddResponse(kImageURL, kImageData);
fake_image_decoder()->SetDecodingValid(false);
base::MockCallback<ImageFetchedCallback> image_callback;
feed_image_manager()->FetchImage(std::vector<std::string>({kImageURL}),
DIMENSION_UNKNOWN, DIMENSION_UNKNOWN,
image_callback.Get());
RunUntilIdle();
histogram().ExpectTotalCount(kUmaCacheLoadHistogramName, 0);
histogram().ExpectTotalCount(kUmaNetworkLoadHistogramName, 0);
histogram().ExpectTotalCount(kUmaImageLoadSuccessHistogramName, 1);
histogram().ExpectBucketCount(kUmaImageLoadSuccessHistogramName,
FeedImageFetchResult::kFailure, 1);
}
} // namespace feed