blob: 93587f3e4fdb9e5bdd36f124dbd6fbaa72c16cb8 [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.
#import "ios/chrome/browser/ui/ntp/ntp_tile_saver.h"
#include "base/run_loop.h"
#import "base/strings/sys_string_conversions.h"
#import "base/strings/utf_string_conversions.h"
#include "base/test/scoped_task_environment.h"
#import "components/ntp_tiles/ntp_tile.h"
#import "ios/chrome/browser/ui/favicon/favicon_attributes.h"
#import "ios/chrome/browser/ui/favicon/favicon_attributes_provider.h"
#import "ios/chrome/browser/ui/ntp/ntp_tile.h"
#include "ios/chrome/common/app_group/app_group_constants.h"
#import "ios/chrome/test/block_cleanup_test.h"
#import "net/base/mac/url_conversions.h"
#import "testing/gtest_mac.h"
#import "third_party/ocmock/OCMock/OCMock.h"
#include "ui/base/test/ios/ui_image_test_utils.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
namespace {
class NTPTileSaverControllerTest : public BlockCleanupTest {
protected:
void SetUp() override {
BlockCleanupTest::SetUp();
if ([[NSFileManager defaultManager]
fileExistsAtPath:[TestFaviconDirectory() path]]) {
[[NSFileManager defaultManager] removeItemAtURL:TestFaviconDirectory()
error:nil];
}
CreateMockImage([UIColor blackColor]);
NSUserDefaults* sharedDefaults = app_group::GetGroupUserDefaults();
[sharedDefaults removeObjectForKey:app_group::kSuggestedItems];
}
void TearDown() override {
if ([[NSFileManager defaultManager]
fileExistsAtPath:[TestFaviconDirectory() path]]) {
[[NSFileManager defaultManager] removeItemAtURL:TestFaviconDirectory()
error:nil];
}
NSUserDefaults* sharedDefaults = app_group::GetGroupUserDefaults();
[sharedDefaults removeObjectForKey:app_group::kSuggestedItems];
BlockCleanupTest::TearDown();
}
UIImage* CreateMockImage(UIColor* color) {
mock_image_ = ui::test::uiimage_utils::UIImageWithSizeAndSolidColor(
CGSizeMake(10, 10), color);
return mock_image_;
}
NSURL* TestFaviconDirectory() {
return
[[[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory
inDomains:NSUserDomainMask]
firstObject] URLByAppendingPathComponent:@"testFaviconDirectory"];
}
void SetupMockCallback(id mock,
std::set<GURL> image_urls,
std::set<GURL> fallback_urls) {
OCMStub([[mock ignoringNonObjectArgs]
fetchFaviconAttributesForURL:GURL()
completion:[OCMArg isNotNil]])
.andDo(^(NSInvocation* invocation) {
GURL* urltest;
[invocation getArgument:&urltest atIndex:2];
if (image_urls.find(GURL(*urltest)) != image_urls.end()) {
__unsafe_unretained void (^callback)(id);
[invocation getArgument:&callback atIndex:3];
callback([FaviconAttributes attributesWithImage:mock_image_]);
} else if (fallback_urls.find(GURL(*urltest)) !=
fallback_urls.end()) {
__unsafe_unretained void (^callback)(id);
[invocation getArgument:&callback atIndex:3];
callback([FaviconAttributes
attributesWithMonogram:@"C"
textColor:UIColor.whiteColor
backgroundColor:UIColor.blueColor
defaultBackgroundColor:NO]);
}
});
}
// Checks that |tile| has an image and no fallback data.
// Checks that |tile| title and url match |expected_title| and
// |expected_url|.
// Checks that the file pointed by |tile| match |mock_image_|.
void VerifyWithImage(NTPTile* tile,
NSString* expected_title,
NSURL* expected_url) {
EXPECT_NSNE(tile, nil);
EXPECT_NSEQ(tile.title, expected_title);
EXPECT_NSEQ(tile.URL, expected_url);
EXPECT_NSNE(tile.faviconFileName, nil);
EXPECT_NSEQ(tile.fallbackTextColor, nil);
EXPECT_NSEQ(tile.fallbackBackgroundColor, nil);
EXPECT_TRUE([[NSFileManager defaultManager]
fileExistsAtPath:[[TestFaviconDirectory()
URLByAppendingPathComponent:tile.faviconFileName]
path]]);
UIImage* stored_image = [UIImage
imageWithContentsOfFile:
[[TestFaviconDirectory()
URLByAppendingPathComponent:tile.faviconFileName] path]];
EXPECT_NSEQ(UIImagePNGRepresentation(stored_image),
UIImagePNGRepresentation(mock_image_));
}
// Checks that |tile| has fallback data.
// Checks that |tile| title and url match |expected_title| and
// |expected_url|.
// Checks that the file pointed by |tile| does not exist.
void VerifyWithFallback(NTPTile* tile,
NSString* expected_title,
NSURL* expected_url) {
EXPECT_NSNE(tile, nil);
EXPECT_NSEQ(tile.title, expected_title);
EXPECT_NSEQ(tile.URL, expected_url);
EXPECT_NSNE(tile.faviconFileName, nil);
EXPECT_NSEQ(tile.fallbackTextColor, UIColor.whiteColor);
EXPECT_NSEQ(tile.fallbackBackgroundColor, UIColor.blueColor);
EXPECT_EQ(tile.fallbackIsDefaultColor, NO);
EXPECT_FALSE([[NSFileManager defaultManager]
fileExistsAtPath:[[TestFaviconDirectory()
URLByAppendingPathComponent:tile.faviconFileName]
path]]);
}
// Checks that |tile| has an image and fallback data.
// Checks that |tile| title and url match |expected_title| and
// |expected_url|.
// Checks that the file pointed by |tile| match |mock_image_|.
void VerifyWithFallbackAndImage(NTPTile* tile,
NSString* expected_title,
NSURL* expected_url) {
EXPECT_NSNE(tile, nil);
EXPECT_NSEQ(tile.title, expected_title);
EXPECT_NSEQ(tile.URL, expected_url);
EXPECT_NSNE(tile.faviconFileName, nil);
EXPECT_NSEQ(tile.fallbackTextColor, UIColor.whiteColor);
EXPECT_NSEQ(tile.fallbackBackgroundColor, UIColor.blueColor);
EXPECT_EQ(tile.fallbackIsDefaultColor, NO);
EXPECT_TRUE([[NSFileManager defaultManager]
fileExistsAtPath:[[TestFaviconDirectory()
URLByAppendingPathComponent:tile.faviconFileName]
path]]);
UIImage* stored_image = [UIImage
imageWithContentsOfFile:
[[TestFaviconDirectory()
URLByAppendingPathComponent:tile.faviconFileName] path]];
EXPECT_NSEQ(UIImagePNGRepresentation(stored_image),
UIImagePNGRepresentation(mock_image_));
}
protected:
base::test::ScopedTaskEnvironment scoped_task_evironment_;
UIImage* mock_image_;
};
TEST_F(NTPTileSaverControllerTest, SaveMostVisitedToDisk) {
ntp_tiles::NTPTile image_tile = ntp_tiles::NTPTile();
image_tile.title = base::ASCIIToUTF16("Title");
image_tile.url = GURL("http://image.com");
ntp_tiles::NTPTile fallback_tile = ntp_tiles::NTPTile();
fallback_tile.title = base::ASCIIToUTF16("Title");
fallback_tile.url = GURL("http://fallback.com");
id mock_favicon_fetcher = OCMClassMock([FaviconAttributesProvider class]);
SetupMockCallback(mock_favicon_fetcher, {image_tile.url},
{fallback_tile.url});
ntp_tiles::NTPTilesVector tiles = {
fallback_tile, // NTP tile with fallback data
image_tile, // NTP tile with favicon
};
ntp_tile_saver::SaveMostVisitedToDisk(tiles, mock_favicon_fetcher,
TestFaviconDirectory());
// Wait for all asynchronous tasks to complete.
scoped_task_evironment_.RunUntilIdle();
// Read most visited from disk.
NSDictionary<NSURL*, NTPTile*>* saved_tiles =
ntp_tile_saver::ReadSavedMostVisited();
EXPECT_EQ(saved_tiles.count, 2U);
NSString* fallback_title = base::SysUTF16ToNSString(fallback_tile.title);
NSURL* fallback_url = net::NSURLWithGURL(fallback_tile.url);
NTPTile* fallback_saved_tile = [saved_tiles objectForKey:fallback_url];
VerifyWithFallback(fallback_saved_tile, fallback_title, fallback_url);
NSString* image_title = base::SysUTF16ToNSString(image_tile.title);
NSURL* image_url = net::NSURLWithGURL(image_tile.url);
NTPTile* image_saved_tile = [saved_tiles objectForKey:image_url];
VerifyWithImage(image_saved_tile, image_title, image_url);
}
TEST_F(NTPTileSaverControllerTest, UpdateSingleFaviconFallback) {
// Set up test with 3 saved sites, 2 of which have a favicon.
ntp_tiles::NTPTile image_tile1 = ntp_tiles::NTPTile();
image_tile1.title = base::ASCIIToUTF16("Title1");
image_tile1.url = GURL("http://image1.com");
ntp_tiles::NTPTile image_tile2 = ntp_tiles::NTPTile();
image_tile2.title = base::ASCIIToUTF16("Title2");
image_tile2.url = GURL("http://image2.com");
ntp_tiles::NTPTile fallback_tile = ntp_tiles::NTPTile();
fallback_tile.title = base::ASCIIToUTF16("Title");
fallback_tile.url = GURL("http://fallback.com");
id mock_favicon_fetcher = OCMClassMock([FaviconAttributesProvider class]);
SetupMockCallback(mock_favicon_fetcher, {image_tile1.url, image_tile2.url},
{fallback_tile.url});
ntp_tiles::NTPTilesVector tiles = {image_tile1, fallback_tile, image_tile2};
ntp_tile_saver::SaveMostVisitedToDisk(tiles, mock_favicon_fetcher,
TestFaviconDirectory());
// Wait for all asynchronous tasks to complete.
scoped_task_evironment_.RunUntilIdle();
// Read most visited from disk.
NSDictionary<NSURL*, NTPTile*>* saved_tiles =
ntp_tile_saver::ReadSavedMostVisited();
EXPECT_EQ(saved_tiles.count, 3U);
NSString* image_title1 = base::SysUTF16ToNSString(image_tile1.title);
NSURL* image_url1 = net::NSURLWithGURL(image_tile1.url);
NTPTile* image_saved_tile1 = [saved_tiles objectForKey:image_url1];
VerifyWithImage(image_saved_tile1, image_title1, image_url1);
NSString* image_title2 = base::SysUTF16ToNSString(image_tile2.title);
NSURL* image_url2 = net::NSURLWithGURL(image_tile2.url);
NTPTile* image_saved_tile2 = [saved_tiles objectForKey:image_url2];
VerifyWithImage(image_saved_tile2, image_title2, image_url2);
NSString* fallback_title = base::SysUTF16ToNSString(fallback_tile.title);
NSURL* fallback_url = net::NSURLWithGURL(fallback_tile.url);
NTPTile* fallback_saved_tile = [saved_tiles objectForKey:fallback_url];
VerifyWithFallback(fallback_saved_tile, fallback_title, fallback_url);
// Mock returning a fallback value for the first image tile.
id mock_favicon_fetcher2 = OCMClassMock([FaviconAttributesProvider class]);
SetupMockCallback(mock_favicon_fetcher2, {image_tile2.url},
{image_tile1.url, fallback_tile.url});
ntp_tile_saver::UpdateSingleFavicon(image_tile1.url, mock_favicon_fetcher2,
TestFaviconDirectory());
// Wait for all asynchronous tasks to complete.
scoped_task_evironment_.RunUntilIdle();
// Read most visited from disk.
NSDictionary<NSURL*, NTPTile*>* saved_tiles_after_update =
ntp_tile_saver::ReadSavedMostVisited();
EXPECT_EQ(saved_tiles_after_update.count, 3U);
// Verify that the first image tile now has callback data.
image_saved_tile1 = [saved_tiles_after_update objectForKey:image_url1];
VerifyWithFallback(image_saved_tile1, image_title1, image_url1);
// Verify that the other two tiles did not change.
image_saved_tile2 = [saved_tiles objectForKey:image_url2];
VerifyWithImage(image_saved_tile2, image_title2, image_url2);
fallback_saved_tile = [saved_tiles_after_update objectForKey:fallback_url];
VerifyWithFallback(fallback_saved_tile, fallback_title, fallback_url);
}
// Checks that the image saved for an item is deleted when the item is deleted.
TEST_F(NTPTileSaverControllerTest, DeleteOutdatedImage) {
ntp_tiles::NTPTile image_tile1 = ntp_tiles::NTPTile();
image_tile1.title = base::ASCIIToUTF16("Title");
image_tile1.url = GURL("http://image1.com");
ntp_tiles::NTPTile image_tile2 = ntp_tiles::NTPTile();
image_tile2.title = base::ASCIIToUTF16("Title");
image_tile2.url = GURL("http://image2.com");
id mock_favicon_fetcher = OCMClassMock([FaviconAttributesProvider class]);
SetupMockCallback(mock_favicon_fetcher, {image_tile1.url, image_tile2.url},
{});
ntp_tiles::NTPTilesVector tiles = {
image_tile1,
};
ntp_tile_saver::SaveMostVisitedToDisk(tiles, mock_favicon_fetcher,
TestFaviconDirectory());
// Wait for all asynchronous tasks to complete.
scoped_task_evironment_.RunUntilIdle();
NSDictionary<NSURL*, NTPTile*>* saved_tiles =
ntp_tile_saver::ReadSavedMostVisited();
NSString* image_title1 = base::SysUTF16ToNSString(image_tile1.title);
NSURL* image_url1 = net::NSURLWithGURL(image_tile1.url);
NTPTile* saved_tile1 = [saved_tiles objectForKey:image_url1];
VerifyWithImage(saved_tile1, image_title1, image_url1);
ntp_tiles::NTPTilesVector tiles2 = {
image_tile2,
};
ntp_tile_saver::SaveMostVisitedToDisk(tiles2, mock_favicon_fetcher,
TestFaviconDirectory());
// Wait for all asynchronous tasks to complete.
scoped_task_evironment_.RunUntilIdle();
NSDictionary<NSURL*, NTPTile*>* saved_tiles2 =
ntp_tile_saver::ReadSavedMostVisited();
NSString* image_title2 = base::SysUTF16ToNSString(image_tile2.title);
NSURL* image_url2 = net::NSURLWithGURL(image_tile2.url);
NTPTile* saved_tile2 = [saved_tiles2 objectForKey:image_url2];
VerifyWithImage(saved_tile2, image_title2, image_url2);
EXPECT_FALSE([[NSFileManager defaultManager]
fileExistsAtPath:[[TestFaviconDirectory()
URLByAppendingPathComponent:saved_tile1
.faviconFileName]
path]]);
}
// Checks the different icon transition for an item.
// Checks that when a fallback exists, it persists even if an image is set.
// Checks that if a new icon is received it replaces the old one.
TEST_F(NTPTileSaverControllerTest, UpdateEntry) {
ntp_tiles::NTPTile tile = ntp_tiles::NTPTile();
// Set up a red favicon.
tile.title = base::ASCIIToUTF16("Title");
tile.url = GURL("http://url.com");
NSString* ns_title = base::SysUTF16ToNSString(tile.title);
NSURL* ns_url = net::NSURLWithGURL(tile.url);
UIImage* red_image = CreateMockImage([UIColor redColor]);
id mock_favicon_image_fetcher =
OCMClassMock([FaviconAttributesProvider class]);
SetupMockCallback(mock_favicon_image_fetcher, {tile.url}, {});
id mock_favicon_fallback_fetcher =
OCMClassMock([FaviconAttributesProvider class]);
SetupMockCallback(mock_favicon_fallback_fetcher, {}, {tile.url});
ntp_tiles::NTPTilesVector tiles = {
tile,
};
ntp_tile_saver::SaveMostVisitedToDisk(tiles, mock_favicon_image_fetcher,
TestFaviconDirectory());
// Wait for all asynchronous tasks to complete.
scoped_task_evironment_.RunUntilIdle();
NSDictionary<NSURL*, NTPTile*>* saved =
ntp_tile_saver::ReadSavedMostVisited();
NTPTile* saved_tile = [saved objectForKey:ns_url];
VerifyWithImage(saved_tile, ns_title, ns_url);
// Update image to blue
UIImage* blue_image = CreateMockImage([UIColor blueColor]);
EXPECT_NSNE(UIImagePNGRepresentation(red_image),
UIImagePNGRepresentation(blue_image));
ntp_tile_saver::SaveMostVisitedToDisk(tiles, mock_favicon_image_fetcher,
TestFaviconDirectory());
// Wait for all asynchronous tasks to complete.
scoped_task_evironment_.RunUntilIdle();
saved = ntp_tile_saver::ReadSavedMostVisited();
saved_tile = [saved objectForKey:ns_url];
VerifyWithImage(saved_tile, ns_title, ns_url);
// Update with fallback
ntp_tile_saver::SaveMostVisitedToDisk(tiles, mock_favicon_fallback_fetcher,
TestFaviconDirectory());
// Wait for all asynchronous tasks to complete.
scoped_task_evironment_.RunUntilIdle();
saved = ntp_tile_saver::ReadSavedMostVisited();
saved_tile = [saved objectForKey:ns_url];
VerifyWithFallback(saved_tile, ns_title, ns_url);
// Update image to green
UIImage* green_image = CreateMockImage([UIColor greenColor]);
EXPECT_NSNE(UIImagePNGRepresentation(blue_image),
UIImagePNGRepresentation(green_image));
ntp_tile_saver::SaveMostVisitedToDisk(tiles, mock_favicon_image_fetcher,
TestFaviconDirectory());
// Wait for all asynchronous tasks to complete.
scoped_task_evironment_.RunUntilIdle();
saved = ntp_tile_saver::ReadSavedMostVisited();
saved_tile = [saved objectForKey:ns_url];
// Fallback should still be present.
VerifyWithFallbackAndImage(saved_tile, ns_title, ns_url);
// Remove tile.
ntp_tile_saver::SaveMostVisitedToDisk(ntp_tiles::NTPTilesVector(),
mock_favicon_image_fetcher,
TestFaviconDirectory());
// Wait for all asynchronous tasks to complete.
scoped_task_evironment_.RunUntilIdle();
EXPECT_FALSE([[NSFileManager defaultManager]
fileExistsAtPath:[[TestFaviconDirectory()
URLByAppendingPathComponent:saved_tile
.faviconFileName]
path]]);
}
} // anonymous namespace