blob: 82714fd6df99baeb9207a91ab134caff9ee75f46 [file] [log] [blame]
// Copyright 2014 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/search_provider_logos/logo_cache.h"
#include <stddef.h>
#include <stdint.h>
#include <memory>
#include <string>
#include "base/bind.h"
#include "base/callback.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/run_loop.h"
#include "base/time/time.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace search_provider_logos {
LogoMetadata GetExampleMetadata() {
LogoMetadata metadata;
metadata.source_url = GURL("http://google.com/mylogo");
metadata.fingerprint = "LC4JVIZ5HVITQFKH0V70";
EXPECT_TRUE(base::Time::FromString("98-05-05 05:05:06 GMT",
&metadata.expiration_time));
metadata.can_show_after_expiration = true;
metadata.type = LogoType::ANIMATED;
metadata.short_link = GURL("https://g.co/");
metadata.on_click_url = GURL("https://www.google.com/search?q=chicken");
metadata.animated_url = GURL("http://www.google.com/logos/doodle.png");
metadata.alt_text = "A logo about chickens";
metadata.mime_type = "image/jpeg";
metadata.log_url = GURL("https://www.google.com/ddllog?a=b");
metadata.cta_log_url = GURL("https://www.google.com/ddllog?c=d");
metadata.share_button_x = 200;
metadata.share_button_y = 100;
metadata.share_button_opacity = 0.5;
metadata.share_button_icon = "test_img";
metadata.share_button_bg = "#ff22ff";
return metadata;
}
LogoMetadata GetExampleMetadata2() {
LogoMetadata metadata;
metadata.source_url = GURL("https://www.example.com/thebestlogo?size=large");
metadata.fingerprint = "bh4PLHdnEaQAPxNGRyMao1rOmVFTXuOdVhdrMmPV";
EXPECT_TRUE(base::Time::FromString("17-04-04 07:10:58 GMT",
&metadata.expiration_time));
metadata.can_show_after_expiration = false;
metadata.type = LogoType::INTERACTIVE;
metadata.on_click_url = GURL("http://www.example.co.uk/welcome.php#top");
metadata.full_page_url =
GURL("http://www.example.co.uk/welcome.php#fpdoodle");
metadata.alt_text = "This is a logo";
metadata.mime_type = "image/png";
return metadata;
}
base::RefCountedString* CreateExampleImage(size_t num_bytes) {
base::RefCountedString* encoded_image_str = new base::RefCountedString();
std::string& str = encoded_image_str->data();
str.resize(num_bytes);
for (size_t i = 0; i < num_bytes; ++i)
str[i] = static_cast<char>(i);
return encoded_image_str;
}
std::unique_ptr<EncodedLogo> GetExampleLogo() {
auto logo = std::make_unique<EncodedLogo>();
logo->encoded_image = CreateExampleImage(837);
logo->metadata = GetExampleMetadata();
return logo;
}
std::unique_ptr<EncodedLogo> GetExampleLogo2() {
auto logo = std::make_unique<EncodedLogo>();
logo->encoded_image = CreateExampleImage(345);
logo->metadata = GetExampleMetadata2();
return logo;
}
std::unique_ptr<EncodedLogo> GetExampleLogoWithoutImage() {
auto logo = std::make_unique<EncodedLogo>();
logo->encoded_image = nullptr;
logo->metadata = GetExampleMetadata2();
return logo;
}
void ExpectMetadataEqual(const LogoMetadata& expected_metadata,
const LogoMetadata& actual_metadata) {
EXPECT_EQ(expected_metadata.source_url, actual_metadata.source_url);
EXPECT_EQ(expected_metadata.fingerprint, actual_metadata.fingerprint);
EXPECT_EQ(expected_metadata.can_show_after_expiration,
actual_metadata.can_show_after_expiration);
EXPECT_EQ(expected_metadata.expiration_time, actual_metadata.expiration_time);
EXPECT_EQ(expected_metadata.type, actual_metadata.type);
EXPECT_EQ(expected_metadata.on_click_url, actual_metadata.on_click_url);
EXPECT_EQ(expected_metadata.full_page_url, actual_metadata.full_page_url);
EXPECT_EQ(expected_metadata.animated_url, actual_metadata.animated_url);
EXPECT_EQ(expected_metadata.alt_text, actual_metadata.alt_text);
EXPECT_EQ(expected_metadata.mime_type, actual_metadata.mime_type);
EXPECT_EQ(expected_metadata.log_url, actual_metadata.log_url);
EXPECT_EQ(expected_metadata.cta_log_url, actual_metadata.cta_log_url);
EXPECT_EQ(expected_metadata.short_link, actual_metadata.short_link);
EXPECT_EQ(expected_metadata.share_button_x, actual_metadata.share_button_x);
EXPECT_EQ(expected_metadata.share_button_y, actual_metadata.share_button_y);
EXPECT_EQ(expected_metadata.share_button_opacity,
actual_metadata.share_button_opacity);
EXPECT_EQ(expected_metadata.share_button_icon,
actual_metadata.share_button_icon);
EXPECT_EQ(expected_metadata.share_button_bg, actual_metadata.share_button_bg);
}
void ExpectLogosEqual(const EncodedLogo& expected_logo,
const EncodedLogo& actual_logo) {
ASSERT_TRUE(expected_logo.encoded_image.get());
ASSERT_TRUE(actual_logo.encoded_image.get());
EXPECT_TRUE(expected_logo.encoded_image->Equals(actual_logo.encoded_image));
ExpectMetadataEqual(expected_logo.metadata, actual_logo.metadata);
}
// Removes 1 byte from the end of the file at |path|.
void ShortenFile(base::FilePath path) {
base::File file(path, base::File::FLAG_OPEN | base::File::FLAG_WRITE);
int64_t file_length = file.GetLength();
ASSERT_GT(file_length, 0);
file.SetLength(file_length - 1);
}
class LogoCacheTest : public ::testing::Test {
protected:
void SetUp() override {
ASSERT_TRUE(cache_parent_dir_.CreateUniqueTempDir());
InitCache();
}
void InitCache() {
cache_ = std::make_unique<LogoCache>(
cache_parent_dir_.GetPath().Append(FILE_PATH_LITERAL("cache")));
}
void ExpectMetadata(const LogoMetadata* expected_metadata) {
const LogoMetadata* retrieved_metadata = cache_->GetCachedLogoMetadata();
if (expected_metadata) {
ASSERT_TRUE(retrieved_metadata);
ExpectMetadataEqual(*expected_metadata, *retrieved_metadata);
} else {
ASSERT_FALSE(retrieved_metadata);
}
}
void ExpectLogo(const EncodedLogo* expected_logo) {
std::unique_ptr<EncodedLogo> retrieved_logo(cache_->GetCachedLogo());
if (expected_logo) {
ASSERT_TRUE(retrieved_logo.get());
ExpectLogosEqual(*expected_logo, *retrieved_logo);
} else {
ASSERT_FALSE(retrieved_logo);
}
}
void ExpectLogoWithoutImage(const EncodedLogo* expected_logo) {
std::unique_ptr<EncodedLogo> retrieved_logo(cache_->GetCachedLogo());
ASSERT_TRUE(retrieved_logo.get());
ASSERT_FALSE(retrieved_logo->encoded_image.get());
ASSERT_FALSE(expected_logo->encoded_image.get());
ExpectMetadataEqual(expected_logo->metadata, retrieved_logo->metadata);
}
// Deletes the existing LogoCache and creates a new one. This clears any
// logo or metadata cached in memory to simulate restarting Chrome.
void SimulateRestart() {
InitCache();
}
std::unique_ptr<LogoCache> cache_;
base::ScopedTempDir cache_parent_dir_;
};
// Tests -----------------------------------------------------------------------
TEST(LogoCacheSerializationTest, SerializeMetadata) {
LogoMetadata metadata = GetExampleMetadata();
std::string metadata_str;
int logo_num_bytes = 33;
LogoCache::LogoMetadataToString(metadata, logo_num_bytes, &metadata_str);
std::unique_ptr<LogoMetadata> metadata2 =
LogoCache::LogoMetadataFromString(metadata_str, &logo_num_bytes);
ASSERT_TRUE(metadata2);
ExpectMetadataEqual(metadata, *metadata2);
}
TEST(LogoCacheSerializationTest, DeserializeCorruptMetadata) {
int logo_num_bytes = 33;
std::unique_ptr<LogoMetadata> metadata =
LogoCache::LogoMetadataFromString("", &logo_num_bytes);
ASSERT_FALSE(metadata);
LogoMetadata example_metadata = GetExampleMetadata2();
std::string corrupt_str;
LogoCache::LogoMetadataToString(
example_metadata, logo_num_bytes, &corrupt_str);
corrupt_str.append("@");
metadata = LogoCache::LogoMetadataFromString(corrupt_str, &logo_num_bytes);
ASSERT_FALSE(metadata);
}
TEST_F(LogoCacheTest, StoreAndRetrieveMetadata) {
// Expect no metadata at first.
ExpectMetadata(nullptr);
// Set initial metadata.
std::unique_ptr<EncodedLogo> logo = GetExampleLogo();
LogoMetadata& metadata = logo->metadata;
cache_->SetCachedLogo(logo.get());
ExpectMetadata(&metadata);
// Update metadata.
metadata.on_click_url = GURL("http://anotherwebsite.com");
cache_->UpdateCachedLogoMetadata(metadata);
ExpectMetadata(&metadata);
// Read metadata back from disk.
SimulateRestart();
ExpectMetadata(&metadata);
// Ensure metadata is cached in memory.
base::DeleteFile(cache_->GetMetadataPath(), false);
ExpectMetadata(&metadata);
}
TEST_F(LogoCacheTest, StoreAndRetrieveLogo) {
// Expect no metadata at first.
ExpectLogo(nullptr);
// Set initial logo.
std::unique_ptr<EncodedLogo> logo = GetExampleLogo();
cache_->SetCachedLogo(logo.get());
ExpectLogo(logo.get());
// Update logo to null.
cache_->SetCachedLogo(nullptr);
ExpectLogo(nullptr);
// Read logo back from disk.
SimulateRestart();
ExpectLogo(nullptr);
// Update logo.
logo = GetExampleLogo2();
cache_->SetCachedLogo(logo.get());
ExpectLogo(logo.get());
// Read logo back from disk.
SimulateRestart();
ExpectLogo(logo.get());
}
TEST_F(LogoCacheTest, StoreAndRetrieveLogoWithoutImage) {
// Expect no metadata at first.
ExpectLogo(nullptr);
// Set initial logo.
std::unique_ptr<EncodedLogo> logo = GetExampleLogoWithoutImage();
cache_->SetCachedLogo(logo.get());
ExpectLogoWithoutImage(logo.get());
// Update logo to null.
cache_->SetCachedLogo(nullptr);
ExpectLogo(nullptr);
// Read logo back from disk.
SimulateRestart();
ExpectLogo(nullptr);
// Update logo.
logo = GetExampleLogoWithoutImage();
cache_->SetCachedLogo(logo.get());
ExpectLogoWithoutImage(logo.get());
// Read logo back from disk.
SimulateRestart();
ExpectLogoWithoutImage(logo.get());
}
TEST_F(LogoCacheTest, RetrieveCorruptMetadata) {
// Set initial logo.
std::unique_ptr<EncodedLogo> logo = GetExampleLogo2();
cache_->SetCachedLogo(logo.get());
ExpectLogo(logo.get());
// Corrupt metadata and expect null for both logo and metadata.
SimulateRestart();
ShortenFile(cache_->GetMetadataPath());
ExpectMetadata(nullptr);
ExpectLogo(nullptr);
// Ensure corrupt cache files are deleted.
EXPECT_FALSE(base::PathExists(cache_->GetMetadataPath()));
EXPECT_FALSE(base::PathExists(cache_->GetLogoPath()));
}
TEST_F(LogoCacheTest, RetrieveCorruptLogo) {
// Set initial logo.
std::unique_ptr<EncodedLogo> logo = GetExampleLogo();
cache_->SetCachedLogo(logo.get());
ExpectLogo(logo.get());
// Corrupt logo and expect nullptr.
SimulateRestart();
ShortenFile(cache_->GetLogoPath());
ExpectLogo(nullptr);
// Once the logo is noticed to be null, the metadata should also be cleared.
ExpectMetadata(nullptr);
// Ensure corrupt cache files are deleted.
EXPECT_FALSE(base::PathExists(cache_->GetMetadataPath()));
EXPECT_FALSE(base::PathExists(cache_->GetLogoPath()));
}
} // namespace search_provider_logos