blob: 48e472277765ac89b6b345a50cb5733428fa840e [file] [log] [blame]
// Copyright 2017 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/ntp_snippets/remote/cached_image_fetcher.h"
#include <memory>
#include "base/bind.h"
#include "base/callback.h"
#include "base/files/file_path.h"
#include "base/files/scoped_temp_dir.h"
#include "base/test/mock_callback.h"
#include "base/test/scoped_task_environment.h"
#include "components/image_fetcher/core/image_decoder.h"
#include "components/image_fetcher/core/image_fetcher.h"
#include "components/image_fetcher/core/image_fetcher_impl.h"
#include "components/ntp_snippets/remote/proto/ntp_snippets.pb.h"
#include "components/ntp_snippets/remote/remote_suggestions_database.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/testing_pref_service.h"
#include "net/url_request/test_url_fetcher_factory.h"
#include "net/url_request/url_request_test_util.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 testing::_;
using testing::Eq;
using testing::Property;
namespace ntp_snippets {
namespace {
const char kImageData[] = "data";
const char kImageURL[] = "http://image.test/test.png";
const char kSnippetID[] = "http://localhost";
// Always decodes a valid image for all non-empty input.
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 {
gfx::Image image;
if (!image_data.empty()) {
image = gfx::test::CreateImage();
}
callback.Run(image);
}
};
} // namespace
class CachedImageFetcherTest : public testing::Test {
public:
CachedImageFetcherTest() : fake_url_fetcher_factory_(nullptr) {
EXPECT_TRUE(database_dir_.CreateUniqueTempDir());
RequestThrottler::RegisterProfilePrefs(pref_service_.registry());
database_ =
std::make_unique<RemoteSuggestionsDatabase>(database_dir_.GetPath());
request_context_getter_ = scoped_refptr<net::TestURLRequestContextGetter>(
new net::TestURLRequestContextGetter(
scoped_task_environment_.GetMainThreadTaskRunner()));
auto decoder = std::make_unique<FakeImageDecoder>();
fake_image_decoder_ = decoder.get();
cached_image_fetcher_ = std::make_unique<ntp_snippets::CachedImageFetcher>(
std::make_unique<image_fetcher::ImageFetcherImpl>(
std::move(decoder), request_context_getter_.get()),
&pref_service_, database_.get());
RunUntilIdle();
EXPECT_TRUE(database_->IsInitialized());
}
~CachedImageFetcherTest() override {
cached_image_fetcher_.reset();
database_.reset();
// We need to run until idle after deleting the database, because
// ProtoDatabaseImpl deletes the actual LevelDB asynchronously.
RunUntilIdle();
}
void FetchImage(ImageFetchedCallback callback) {
ContentSuggestion::ID content_suggestion_id(
Category::FromKnownCategory(KnownCategories::ARTICLES), kSnippetID);
cached_image_fetcher_->FetchSuggestionImage(
content_suggestion_id, GURL(kImageURL), std::move(callback));
}
void RunUntilIdle() { scoped_task_environment_.RunUntilIdle(); }
RemoteSuggestionsDatabase* database() { return database_.get(); }
FakeImageDecoder* fake_image_decoder() { return fake_image_decoder_; }
net::FakeURLFetcherFactory* fake_url_fetcher_factory() {
return &fake_url_fetcher_factory_;
}
private:
std::unique_ptr<CachedImageFetcher> cached_image_fetcher_;
std::unique_ptr<RemoteSuggestionsDatabase> database_;
base::ScopedTempDir database_dir_;
FakeImageDecoder* fake_image_decoder_;
net::FakeURLFetcherFactory fake_url_fetcher_factory_;
TestingPrefServiceSimple pref_service_;
scoped_refptr<net::TestURLRequestContextGetter> request_context_getter_;
base::test::ScopedTaskEnvironment scoped_task_environment_;
DISALLOW_COPY_AND_ASSIGN(CachedImageFetcherTest);
};
TEST_F(CachedImageFetcherTest, FetchImageFromCache) {
// Save the image in the database.
database()->SaveImage(kSnippetID, kImageData);
RunUntilIdle();
// Do not provide any URL responses and expect that the image is fetched (from
// cache).
base::MockCallback<ImageFetchedCallback> mock_image_fetched_callback;
EXPECT_CALL(mock_image_fetched_callback,
Run(Property(&gfx::Image::IsEmpty, Eq(false))));
FetchImage(mock_image_fetched_callback.Get());
RunUntilIdle();
}
TEST_F(CachedImageFetcherTest, FetchImageNotInCache) {
// Expect the image to be fetched by URL.
fake_url_fetcher_factory()->SetFakeResponse(GURL(kImageURL), kImageData,
net::HTTP_OK,
net::URLRequestStatus::SUCCESS);
base::MockCallback<ImageFetchedCallback> mock_image_fetched_callback;
EXPECT_CALL(mock_image_fetched_callback,
Run(Property(&gfx::Image::IsEmpty, Eq(false))));
FetchImage(mock_image_fetched_callback.Get());
RunUntilIdle();
}
TEST_F(CachedImageFetcherTest, FetchNonExistingImage) {
const std::string kErrorResponse = "error-response";
fake_url_fetcher_factory()->SetFakeResponse(GURL(kImageURL), kErrorResponse,
net::HTTP_NOT_FOUND,
net::URLRequestStatus::FAILED);
// Expect an empty image is fetched if the URL cannot be requested.
const std::string kEmptyImageData;
base::MockCallback<ImageFetchedCallback> mock_image_fetched_callback;
EXPECT_CALL(mock_image_fetched_callback,
Run(Property(&gfx::Image::IsEmpty, Eq(true))));
FetchImage(mock_image_fetched_callback.Get());
RunUntilIdle();
}
} // namespace ntp_snippets