Commit 12051b2a authored by Ian Wells's avatar Ian Wells Committed by Commit Bot

Reland "[Offline Pages] Add attribution, snippet and favicon to offline pages database"

The original CL added a column to the "page_thumbnails" table with NOT NULL but with no
default value. The procedure for upgrading to the new schema version involved copying
all rows of that table but did not give a value for the new column, causing a crash
during schema upgrades.

This CL adds a default value for "favicon", eliminates copying all rows as it was
unnecessary anyway, and improves testing by ensuring a row exists in page_thumbnails
before the schema upgrade.

Bug: 841516
Change-Id: I3c9988bb7e23dec1fcc8d1f25db0c9efe8d88517
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1552117Reviewed-by: default avatarCarlos Knippschild <carlosk@chromium.org>
Reviewed-by: default avatarDan H <harringtond@google.com>
Commit-Queue: Ian Wells <iwells@chromium.org>
Cr-Commit-Position: refs/heads/master@{#648750}
parent c2968681
...@@ -39,12 +39,12 @@ ItemActionStatus AddOfflinePageSync(const OfflinePageItem& item, ...@@ -39,12 +39,12 @@ ItemActionStatus AddOfflinePageSync(const OfflinePageItem& item,
sql::Database* db) { sql::Database* db) {
static const char kSql[] = static const char kSql[] =
"INSERT OR IGNORE INTO offlinepages_v1" "INSERT OR IGNORE INTO offlinepages_v1"
" (offline_id,online_url,client_namespace,client_id,file_path," " (offline_id,online_url,client_namespace,client_id,file_path,file_size,"
"file_size,creation_time,last_access_time,access_count," "creation_time,last_access_time,access_count,title,original_url,"
"title,original_url,request_origin,system_download_id," "request_origin,system_download_id,file_missing_time,digest,"
"file_missing_time,digest)" "snippet,attribution)"
" VALUES " " VALUES "
"(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)"; "(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)";
sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql)); sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql));
statement.BindInt64(0, item.offline_id); statement.BindInt64(0, item.offline_id);
...@@ -62,6 +62,8 @@ ItemActionStatus AddOfflinePageSync(const OfflinePageItem& item, ...@@ -62,6 +62,8 @@ ItemActionStatus AddOfflinePageSync(const OfflinePageItem& item,
statement.BindInt64(12, item.system_download_id); statement.BindInt64(12, item.system_download_id);
statement.BindInt64(13, store_utils::ToDatabaseTime(item.file_missing_time)); statement.BindInt64(13, store_utils::ToDatabaseTime(item.file_missing_time));
statement.BindString(14, item.digest); statement.BindString(14, item.digest);
statement.BindString(15, item.snippet);
statement.BindString(16, item.attribution);
if (!statement.Run()) if (!statement.Run())
return ItemActionStatus::STORE_ERROR; return ItemActionStatus::STORE_ERROR;
......
...@@ -37,6 +37,8 @@ const std::string kTestOrigin("abc.xyz"); ...@@ -37,6 +37,8 @@ const std::string kTestOrigin("abc.xyz");
const base::string16 kTestTitle = base::UTF8ToUTF16("a title"); const base::string16 kTestTitle = base::UTF8ToUTF16("a title");
const int64_t kTestDownloadId = 767574LL; const int64_t kTestDownloadId = 767574LL;
const std::string kTestDigest("TesTIngDigEst=="); const std::string kTestDigest("TesTIngDigEst==");
const std::string kTestAttribution = "attribution";
const std::string kTestSnippet = "snippet";
} // namespace } // namespace
...@@ -99,6 +101,8 @@ TEST_F(AddPageTaskTest, AddPageWithAllFieldsSet) { ...@@ -99,6 +101,8 @@ TEST_F(AddPageTaskTest, AddPageWithAllFieldsSet) {
page.system_download_id = kTestDownloadId; page.system_download_id = kTestDownloadId;
page.file_missing_time = base::Time::Now(); page.file_missing_time = base::Time::Now();
page.digest = kTestDigest; page.digest = kTestDigest;
page.attribution = kTestAttribution;
page.snippet = kTestSnippet;
AddPage(page); AddPage(page);
......
...@@ -78,6 +78,12 @@ std::ostream& operator<<(std::ostream& out, const OfflinePageItem& item) { ...@@ -78,6 +78,12 @@ std::ostream& operator<<(std::ostream& out, const OfflinePageItem& item) {
if (!item.digest.empty()) { if (!item.digest.empty()) {
value.SetKey("digest", Value(item.digest)); value.SetKey("digest", Value(item.digest));
} }
if (!item.snippet.empty()) {
value.SetKey("snippet", Value(item.snippet));
}
if (!item.attribution.empty()) {
value.SetKey("attribution", Value(item.attribution));
}
std::string value_string; std::string value_string;
base::JSONWriter::Write(value, &value_string); base::JSONWriter::Write(value, &value_string);
......
...@@ -18,12 +18,13 @@ namespace { ...@@ -18,12 +18,13 @@ namespace {
bool StoreThumbnailSync(const OfflinePageThumbnail& thumbnail, bool StoreThumbnailSync(const OfflinePageThumbnail& thumbnail,
sql::Database* db) { sql::Database* db) {
static const char kSql[] = static const char kSql[] =
"INSERT OR REPLACE INTO page_thumbnails (offline_id, expiration, " "INSERT OR REPLACE INTO page_thumbnails (offline_id,expiration,thumbnail,"
"thumbnail) VALUES (?, ?, ?)"; "favicon) VALUES (?,?,?,?)";
sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql)); sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql));
statement.BindInt64(0, thumbnail.offline_id); statement.BindInt64(0, thumbnail.offline_id);
statement.BindInt64(1, store_utils::ToDatabaseTime(thumbnail.expiration)); statement.BindInt64(1, store_utils::ToDatabaseTime(thumbnail.expiration));
statement.BindString(2, thumbnail.thumbnail); statement.BindString(2, thumbnail.thumbnail);
statement.BindString(3, std::string());
return statement.Run(); return statement.Run();
} }
......
...@@ -92,6 +92,10 @@ struct OfflinePageItem { ...@@ -92,6 +92,10 @@ struct OfflinePageItem {
// page can be trusted. This field will always be an empty string for // page can be trusted. This field will always be an empty string for
// temporary and shared pages. // temporary and shared pages.
std::string digest; std::string digest;
// Snippet from the article.
std::string snippet;
// Text indicating the article's publisher.
std::string attribution;
}; };
// This operator is for testing only, see offline_page_test_utils.cc. // This operator is for testing only, see offline_page_test_utils.cc.
......
...@@ -41,7 +41,7 @@ void ReportStoreEvent(OfflinePagesStoreEvent event) { ...@@ -41,7 +41,7 @@ void ReportStoreEvent(OfflinePagesStoreEvent event) {
} }
bool CreateOfflinePagesTable(sql::Database* db) { bool CreateOfflinePagesTable(sql::Database* db) {
static const char kSql[] = static const char kCreateLatestOfflinePagesTableSql[] =
"CREATE TABLE IF NOT EXISTS " OFFLINE_PAGES_TABLE_NAME "CREATE TABLE IF NOT EXISTS " OFFLINE_PAGES_TABLE_NAME
"(offline_id INTEGER PRIMARY KEY NOT NULL," "(offline_id INTEGER PRIMARY KEY NOT NULL,"
" creation_time INTEGER NOT NULL," " creation_time INTEGER NOT NULL,"
...@@ -60,9 +60,11 @@ bool CreateOfflinePagesTable(sql::Database* db) { ...@@ -60,9 +60,11 @@ bool CreateOfflinePagesTable(sql::Database* db) {
" title VARCHAR NOT NULL DEFAULT ''," " title VARCHAR NOT NULL DEFAULT '',"
" original_url VARCHAR NOT NULL DEFAULT ''," " original_url VARCHAR NOT NULL DEFAULT '',"
" request_origin VARCHAR NOT NULL DEFAULT ''," " request_origin VARCHAR NOT NULL DEFAULT '',"
" digest VARCHAR NOT NULL DEFAULT ''" " digest VARCHAR NOT NULL DEFAULT '',"
" snippet VARCHAR NOT NULL DEFAULT '',"
" attribution VARCHAR NOT NULL DEFAULT ''"
")"; ")";
return db->Execute(kSql); return db->Execute(kCreateLatestOfflinePagesTableSql);
} }
bool UpgradeWithQuery(sql::Database* db, const char* upgrade_sql) { bool UpgradeWithQuery(sql::Database* db, const char* upgrade_sql) {
...@@ -70,7 +72,26 @@ bool UpgradeWithQuery(sql::Database* db, const char* upgrade_sql) { ...@@ -70,7 +72,26 @@ bool UpgradeWithQuery(sql::Database* db, const char* upgrade_sql) {
" RENAME TO temp_" OFFLINE_PAGES_TABLE_NAME)) { " RENAME TO temp_" OFFLINE_PAGES_TABLE_NAME)) {
return false; return false;
} }
if (!CreateOfflinePagesTable(db)) static const char kCreateOfflinePagesTableVersion1Sql[] =
"CREATE TABLE IF NOT EXISTS " OFFLINE_PAGES_TABLE_NAME
"(offline_id INTEGER PRIMARY KEY NOT NULL,"
" creation_time INTEGER NOT NULL,"
" file_size INTEGER NOT NULL,"
" last_access_time INTEGER NOT NULL,"
" access_count INTEGER NOT NULL,"
" system_download_id INTEGER NOT NULL DEFAULT 0,"
" file_missing_time INTEGER NOT NULL DEFAULT 0,"
" upgrade_attempt INTEGER NOT NULL DEFAULT 0,"
" client_namespace VARCHAR NOT NULL,"
" client_id VARCHAR NOT NULL,"
" online_url VARCHAR NOT NULL,"
" file_path VARCHAR NOT NULL,"
" title VARCHAR NOT NULL DEFAULT '',"
" original_url VARCHAR NOT NULL DEFAULT '',"
" request_origin VARCHAR NOT NULL DEFAULT '',"
" digest VARCHAR NOT NULL DEFAULT ''"
")";
if (!db->Execute(kCreateOfflinePagesTableVersion1Sql))
return false; return false;
if (!db->Execute(upgrade_sql)) if (!db->Execute(upgrade_sql))
return false; return false;
...@@ -178,11 +199,14 @@ bool UpgradeFrom61(sql::Database* db) { ...@@ -178,11 +199,14 @@ bool UpgradeFrom61(sql::Database* db) {
} }
bool CreatePageThumbnailsTable(sql::Database* db) { bool CreatePageThumbnailsTable(sql::Database* db) {
// TODO: The next schema change that modifies existing columns on this table
// should also add "DEFAULT x''" to the definition of the "thumbnail" column.
static const char kSql[] = static const char kSql[] =
"CREATE TABLE IF NOT EXISTS page_thumbnails" "CREATE TABLE IF NOT EXISTS page_thumbnails"
" (offline_id INTEGER PRIMARY KEY NOT NULL," " (offline_id INTEGER PRIMARY KEY NOT NULL,"
" expiration INTEGER NOT NULL," " expiration INTEGER NOT NULL,"
" thumbnail BLOB NOT NULL" " thumbnail BLOB NOT NULL,"
" favicon BLOB NOT NULL DEFAULT x''"
")"; ")";
return db->Execute(kSql); return db->Execute(kSql);
} }
...@@ -259,13 +283,42 @@ bool UpgradeFromVersion2ToVersion3(sql::Database* db, ...@@ -259,13 +283,42 @@ bool UpgradeFromVersion2ToVersion3(sql::Database* db,
if (!transaction.Begin()) if (!transaction.Begin())
return false; return false;
if (!CreatePageThumbnailsTable(db)) { static const char kCreatePageThumbnailsSql[] =
"CREATE TABLE IF NOT EXISTS page_thumbnails"
" (offline_id INTEGER PRIMARY KEY NOT NULL,"
"expiration INTEGER NOT NULL,"
"thumbnail BLOB NOT NULL"
")";
if (!db->Execute(kCreatePageThumbnailsSql))
return false; return false;
}
meta_table->SetVersionNumber(3); meta_table->SetVersionNumber(3);
return transaction.Commit(); return transaction.Commit();
} }
bool UpgradeFromVersion3ToVersion4(sql::Database* db,
sql::MetaTable* meta_table) {
sql::Transaction transaction(db);
if (!transaction.Begin())
return false;
const char kSql[] = "ALTER TABLE " OFFLINE_PAGES_TABLE_NAME
" ADD COLUMN snippet VARCHAR NOT NULL DEFAULT ''; "
"ALTER TABLE " OFFLINE_PAGES_TABLE_NAME
" ADD COLUMN attribution VARCHAR NOT NULL DEFAULT '';";
if (!db->Execute(kSql))
return false;
const char kUpgradeThumbnailsTableSql[] =
"ALTER TABLE page_thumbnails"
" ADD COLUMN favicon BLOB NOT NULL DEFAULT x''";
if (!db->Execute(kUpgradeThumbnailsTableSql))
return false;
meta_table->SetVersionNumber(4);
return transaction.Commit();
}
bool CreateSchema(sql::Database* db) { bool CreateSchema(sql::Database* db) {
if (!sql::MetaTable::DoesTableExist(db)) { if (!sql::MetaTable::DoesTableExist(db)) {
// If this looks like a completely empty DB, simply start from scratch. // If this looks like a completely empty DB, simply start from scratch.
...@@ -292,6 +345,10 @@ bool CreateSchema(sql::Database* db) { ...@@ -292,6 +345,10 @@ bool CreateSchema(sql::Database* db) {
if (!UpgradeFromVersion2ToVersion3(db, &meta_table)) if (!UpgradeFromVersion2ToVersion3(db, &meta_table))
return false; return false;
break; break;
case 3:
if (!UpgradeFromVersion3ToVersion4(db, &meta_table))
return false;
break;
case OfflinePageMetadataStore::kCurrentVersion: case OfflinePageMetadataStore::kCurrentVersion:
return true; return true;
default: default:
......
...@@ -35,43 +35,29 @@ typedef StoreUpdateResult<OfflinePageItem> OfflinePagesUpdateResult; ...@@ -35,43 +35,29 @@ typedef StoreUpdateResult<OfflinePageItem> OfflinePagesUpdateResult;
// OfflinePageMetadataStore keeps metadata for the offline pages in an SQLite // OfflinePageMetadataStore keeps metadata for the offline pages in an SQLite
// database. // database.
// //
// This store has a history of schema updates in pretty much every release. // When updating the schema, be sure to do the following:
// Original schema was delivered in M52. Since then, the following changes // * Increment the version number kCurrentVersion (let's call its new value N).
// happened: // * Write a function "UpgradeFromVersion<N-1>ToVersion<N>". This function
// * In M53 expiration_time was added, // should upgrade an existing database of the (previously) latest version and
// * In M54 title was added, // should call meta_table->SetVersionNumber(N). Add a case for version N-1 to
// * In M55 we dropped the following fields (never used): version, status, // the loop in CreateSchema.
// offline_url, user_initiated. // * Update CreateLatestSchema() as necessary: this function creates a new empty
// * In M56 original_url was added. // DB. If there were changes to existing tables, their original "CREATE"
// * In M57 expiration_time was dropped. Existing expired pages would be // queries should be copied into the new "UpgradeFromVersion..." function.
// removed when metadata consistency check happens. // * Update `kCompatibleVersion` when a new schema becomes incompatible with
// * In M58-M60 there were no changes. // old code (for instance, if a column is removed). Change it to the earliest
// * In M61 request_origin was added. // version that is compatible with the new schema; that is very likely to be
// * In M62 system_download_id, file_missing_time, upgrade_attempt and digest // the version that broke compatibility.
// were added to support P2P sharing feature. // * Add a test for upgrading to the latest database version to
// offline_page_metadata_store_unittest.cc. Good luck.
// //
// Here is a procedure to update the schema for this store:
// * Decide how to detect that the store is on a particular version, which
// typically means that a certain field exists or is missing. This happens in
// Upgrade section of |CreateSchema|
// * Work out appropriate change and apply it to all existing upgrade paths. In
// the interest of performing a single update of the store, it upgrades from a
// detected version to the current one. This means that when making a change,
// more than a single query may have to be updated (in case of fields being
// removed or needed to be initialized to a specific, non-default value).
// Such approach is preferred to doing N updates for every changed version on
// a startup after browser update.
// * New upgrade method should specify which version it is upgrading from, e.g.
// |UpgradeFrom54|.
// * Upgrade should use |UpgradeWithQuery| and simply specify SQL command to
// move data from old table (prefixed by temp_) to the new one.
class OfflinePageMetadataStore : public SqlStoreBase { class OfflinePageMetadataStore : public SqlStoreBase {
public: public:
// This is the first version saved in the meta table, which was introduced in // This is the first version saved in the meta table, which was introduced in
// the store in M65. It is set once a legacy upgrade is run successfully for // the store in M65. It is set once a legacy upgrade is run successfully for
// the last time in |UpgradeFromLegacyVersion|. // the last time in |UpgradeFromLegacyVersion|.
static const int kFirstPostLegacyVersion = 1; static const int kFirstPostLegacyVersion = 1;
static const int kCurrentVersion = 3; static const int kCurrentVersion = 4;
static const int kCompatibleVersion = kFirstPostLegacyVersion; static const int kCompatibleVersion = kFirstPostLegacyVersion;
// TODO(fgorski): Move to private and expose ForTest factory. // TODO(fgorski): Move to private and expose ForTest factory.
......
...@@ -51,6 +51,12 @@ const char kTestRequestOrigin[] = "request.origin"; ...@@ -51,6 +51,12 @@ const char kTestRequestOrigin[] = "request.origin";
int64_t kTestSystemDownloadId = 42LL; int64_t kTestSystemDownloadId = 42LL;
const char kTestDigest[] = "test-digest"; const char kTestDigest[] = "test-digest";
const base::Time kThumbnailExpiration = store_utils::FromDatabaseTime(42); const base::Time kThumbnailExpiration = store_utils::FromDatabaseTime(42);
const char kTestSnippet[] = "test snippet";
const char kTestAttribution[] = "test attribution";
OfflinePageThumbnail TestThumbnailVersion3() {
return {1, base::Time(), "abc"};
}
// Build a store with outdated schema to simulate the upgrading process. // Build a store with outdated schema to simulate the upgrading process.
void BuildTestStoreWithSchemaFromM52(const base::FilePath& file) { void BuildTestStoreWithSchemaFromM52(const base::FilePath& file) {
...@@ -470,15 +476,40 @@ void BuildTestStoreWithSchemaVersion2(const base::FilePath& file) { ...@@ -470,15 +476,40 @@ void BuildTestStoreWithSchemaVersion2(const base::FilePath& file) {
sql::Database db; sql::Database db;
ASSERT_TRUE(db.Open(file.Append(FILE_PATH_LITERAL("OfflinePages.db")))); ASSERT_TRUE(db.Open(file.Append(FILE_PATH_LITERAL("OfflinePages.db"))));
sql::MetaTable meta_table; sql::MetaTable meta_table;
ASSERT_TRUE(meta_table.Init(&db, OfflinePageMetadataStore::kCurrentVersion, ASSERT_TRUE(
OfflinePageMetadataStore::kCompatibleVersion)); meta_table.Init(&db, 2, OfflinePageMetadataStore::kCompatibleVersion));
}
bool InsertThumbnailVersion3(sql::Database* db,
const OfflinePageThumbnail& thumbnail) {
static const char kInsertThumbnailSql[] =
"INSERT INTO page_thumbnails"
" (offline_id,expiration,thumbnail) VALUES (?,?,?)";
sql::Statement statement(
db->GetCachedStatement(SQL_FROM_HERE, kInsertThumbnailSql));
statement.BindInt64(0, thumbnail.offline_id);
statement.BindInt64(1, store_utils::ToDatabaseTime(thumbnail.expiration));
statement.BindBlob(2, thumbnail.thumbnail.data(), thumbnail.thumbnail.size());
return statement.Run();
}
void BuildTestStoreWithSchemaVersion3(const base::FilePath& file) {
BuildTestStoreWithSchemaVersion2(file);
sql::Database db;
ASSERT_TRUE(db.Open(file.Append(FILE_PATH_LITERAL("OfflinePages.db"))));
sql::MetaTable meta_table;
ASSERT_TRUE(
meta_table.Init(&db, 3, OfflinePageMetadataStore::kCompatibleVersion));
static const char kSql[] = static const char kSql[] =
"CREATE TABLE page_thumbnails" "CREATE TABLE page_thumbnails"
" (offline_id INTEGER PRIMARY KEY NOT NULL," " (offline_id INTEGER PRIMARY KEY NOT NULL,"
" expiration INTEGER NOT NULL," " expiration INTEGER NOT NULL,"
" thumbnail BLOB NOT NULL" " thumbnail BLOB NOT NULL"
")"; ");";
ASSERT_TRUE(db.Execute(kSql)); ASSERT_TRUE(db.Execute(kSql));
ASSERT_TRUE(InsertThumbnailVersion3(&db, TestThumbnailVersion3()));
} }
// Create an offline page item from a SQL result. Expects complete rows with // Create an offline page item from a SQL result. Expects complete rows with
...@@ -503,6 +534,8 @@ OfflinePageItem MakeOfflinePageItem(sql::Statement* statement) { ...@@ -503,6 +534,8 @@ OfflinePageItem MakeOfflinePageItem(sql::Statement* statement) {
GURL original_url(statement->ColumnString(13)); GURL original_url(statement->ColumnString(13));
std::string request_origin = statement->ColumnString(14); std::string request_origin = statement->ColumnString(14);
std::string digest = statement->ColumnString(15); std::string digest = statement->ColumnString(15);
std::string snippet = statement->ColumnString(16);
std::string attribution = statement->ColumnString(17);
OfflinePageItem item(url, id, client_id, path, file_size, creation_time); OfflinePageItem item(url, id, client_id, path, file_size, creation_time);
item.last_access_time = last_access_time; item.last_access_time = last_access_time;
...@@ -513,6 +546,8 @@ OfflinePageItem MakeOfflinePageItem(sql::Statement* statement) { ...@@ -513,6 +546,8 @@ OfflinePageItem MakeOfflinePageItem(sql::Statement* statement) {
item.system_download_id = system_download_id; item.system_download_id = system_download_id;
item.file_missing_time = file_missing_time; item.file_missing_time = file_missing_time;
item.digest = digest; item.digest = digest;
item.snippet = snippet;
item.attribution = attribution;
return item; return item;
} }
...@@ -578,6 +613,8 @@ class OfflinePageMetadataStoreTest : public testing::Test { ...@@ -578,6 +613,8 @@ class OfflinePageMetadataStoreTest : public testing::Test {
offline_page.original_url_if_different = GURL(kOriginalTestURL); offline_page.original_url_if_different = GURL(kOriginalTestURL);
offline_page.system_download_id = kTestSystemDownloadId; offline_page.system_download_id = kTestSystemDownloadId;
offline_page.digest = kTestDigest; offline_page.digest = kTestDigest;
offline_page.snippet = kTestSnippet;
offline_page.attribution = kTestAttribution;
EXPECT_EQ(ItemActionStatus::SUCCESS, EXPECT_EQ(ItemActionStatus::SUCCESS,
AddOfflinePage(store.get(), offline_page)); AddOfflinePage(store.get(), offline_page));
...@@ -598,11 +635,12 @@ class OfflinePageMetadataStoreTest : public testing::Test { ...@@ -598,11 +635,12 @@ class OfflinePageMetadataStoreTest : public testing::Test {
thumbnail.offline_id = kOfflineId; thumbnail.offline_id = kOfflineId;
thumbnail.expiration = kThumbnailExpiration; thumbnail.expiration = kThumbnailExpiration;
thumbnail.thumbnail = "content"; thumbnail.thumbnail = "content";
std::vector<OfflinePageThumbnail> thumbnails_before = GetThumbnails(store);
AddThumbnail(store, thumbnail); AddThumbnail(store, thumbnail);
std::vector<OfflinePageThumbnail> thumbnails = GetThumbnails(store); std::vector<OfflinePageThumbnail> thumbnails = GetThumbnails(store);
EXPECT_EQ(1UL, thumbnails.size()); EXPECT_EQ(thumbnails_before.size() + 1, thumbnails.size());
EXPECT_EQ(thumbnail, thumbnails[0]); EXPECT_EQ(thumbnail, thumbnails.back());
} }
void VerifyMetaVersions() { void VerifyMetaVersions() {
...@@ -637,11 +675,20 @@ class OfflinePageMetadataStoreTest : public testing::Test { ...@@ -637,11 +675,20 @@ class OfflinePageMetadataStoreTest : public testing::Test {
std::vector<OfflinePageItem> pages = GetOfflinePages(store.get()); std::vector<OfflinePageItem> pages = GetOfflinePages(store.get());
EXPECT_EQ(5U, pages.size()); EXPECT_EQ(5U, pages.size());
// TODO(fgorski): Use persistent namespaces from the client policy CheckThatPageThumbnailCanBeSaved((OfflinePageMetadataStore*)store.get());
// controller once an appropriate method is available. CheckThatOfflinePageCanBeSaved(std::move(store));
std::set<std::string> upgradeable_namespaces{ VerifyMetaVersions();
kAsyncNamespace, kDownloadNamespace, kBrowserActionsNamespace, }
kNTPSuggestionsNamespace};
void LoadAndCheckStoreFromMetaVersion3AndUp() {
auto store = std::make_unique<OfflinePageMetadataStore>(
base::ThreadTaskRunnerHandle::Get(), TempPath());
std::vector<OfflinePageItem> pages = GetOfflinePages(store.get());
EXPECT_EQ(5U, pages.size());
std::vector<OfflinePageThumbnail> thumbnails = GetThumbnails(store.get());
EXPECT_EQ(1U, thumbnails.size());
EXPECT_EQ(TestThumbnailVersion3(), thumbnails.back());
CheckThatPageThumbnailCanBeSaved((OfflinePageMetadataStore*)store.get()); CheckThatPageThumbnailCanBeSaved((OfflinePageMetadataStore*)store.get());
CheckThatOfflinePageCanBeSaved(std::move(store)); CheckThatOfflinePageCanBeSaved(std::move(store));
...@@ -689,9 +736,9 @@ class OfflinePageMetadataStoreTest : public testing::Test { ...@@ -689,9 +736,9 @@ class OfflinePageMetadataStoreTest : public testing::Test {
"file_path," "file_path,"
"file_size,creation_time,last_access_time,access_count," "file_size,creation_time,last_access_time,access_count,"
"title,original_url,request_origin,system_download_id," "title,original_url,request_origin,system_download_id,"
"file_missing_time,digest)" "file_missing_time,digest,snippet,attribution)"
" VALUES " " VALUES "
"(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)"; "(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)";
sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql)); sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql));
statement.BindInt64(0, item.offline_id); statement.BindInt64(0, item.offline_id);
...@@ -711,6 +758,8 @@ class OfflinePageMetadataStoreTest : public testing::Test { ...@@ -711,6 +758,8 @@ class OfflinePageMetadataStoreTest : public testing::Test {
statement.BindInt64(13, statement.BindInt64(13,
store_utils::ToDatabaseTime(item.file_missing_time)); store_utils::ToDatabaseTime(item.file_missing_time));
statement.BindString(14, item.digest); statement.BindString(14, item.digest);
statement.BindString(15, item.snippet);
statement.BindString(16, item.attribution);
if (!statement.Run()) if (!statement.Run())
return ItemActionStatus::STORE_ERROR; return ItemActionStatus::STORE_ERROR;
...@@ -751,12 +800,13 @@ class OfflinePageMetadataStoreTest : public testing::Test { ...@@ -751,12 +800,13 @@ class OfflinePageMetadataStoreTest : public testing::Test {
auto run_callback = base::BindLambdaForTesting([&](sql::Database* db) { auto run_callback = base::BindLambdaForTesting([&](sql::Database* db) {
static const char kSql[] = static const char kSql[] =
"INSERT INTO page_thumbnails" "INSERT INTO page_thumbnails"
" (offline_id, expiration, thumbnail) VALUES (?, ?, ?)"; " (offline_id, expiration, thumbnail, favicon) VALUES (?, ?, ?, ?)";
sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql)); sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql));
statement.BindInt64(0, thumbnail.offline_id); statement.BindInt64(0, thumbnail.offline_id);
statement.BindInt64(1, store_utils::ToDatabaseTime(thumbnail.expiration)); statement.BindInt64(1, store_utils::ToDatabaseTime(thumbnail.expiration));
statement.BindString(2, thumbnail.thumbnail); statement.BindString(2, thumbnail.thumbnail);
statement.BindString(3, std::string());
EXPECT_TRUE(statement.Run()); EXPECT_TRUE(statement.Run());
return thumbnails; return thumbnails;
}); });
...@@ -852,6 +902,11 @@ TEST_F(OfflinePageMetadataStoreTest, LoadStoreWithMetaVersion2) { ...@@ -852,6 +902,11 @@ TEST_F(OfflinePageMetadataStoreTest, LoadStoreWithMetaVersion2) {
LoadAndCheckStoreFromMetaVersion1AndUp(); LoadAndCheckStoreFromMetaVersion1AndUp();
} }
TEST_F(OfflinePageMetadataStoreTest, LoadStoreWithMetaVersion3) {
BuildTestStoreWithSchemaVersion3(TempPath());
LoadAndCheckStoreFromMetaVersion3AndUp();
}
// Adds metadata of an offline page into a store and then opens the store // Adds metadata of an offline page into a store and then opens the store
// again to make sure that stored metadata survives store restarts. // again to make sure that stored metadata survives store restarts.
TEST_F(OfflinePageMetadataStoreTest, AddOfflinePage) { TEST_F(OfflinePageMetadataStoreTest, AddOfflinePage) {
......
...@@ -104,6 +104,8 @@ void PrefetchImporterImpl::ImportArchive(const PrefetchArchiveInfo& archive) { ...@@ -104,6 +104,8 @@ void PrefetchImporterImpl::ImportArchive(const PrefetchArchiveInfo& archive) {
dest_path, archive.file_size, base::Time::Now()); dest_path, archive.file_size, base::Time::Now());
offline_page.original_url_if_different = original_url; offline_page.original_url_if_different = original_url;
offline_page.title = archive.title; offline_page.title = archive.title;
offline_page.snippet = archive.snippet;
offline_page.attribution = archive.attribution;
outstanding_import_offline_ids_.emplace(archive.offline_id); outstanding_import_offline_ids_.emplace(archive.offline_id);
......
...@@ -23,9 +23,18 @@ const int64_t kTestOfflineIDFailedToAdd = 223344; ...@@ -23,9 +23,18 @@ const int64_t kTestOfflineIDFailedToAdd = 223344;
const GURL kTestURL("http://sample.org"); const GURL kTestURL("http://sample.org");
const GURL kTestFinalURL("https://sample.org/foo"); const GURL kTestFinalURL("https://sample.org/foo");
const ClientId kTestClientID("Foo", "C56A4180-65AA-42EC-A945-5FD21DEC0538"); const ClientId kTestClientID("Foo", "C56A4180-65AA-42EC-A945-5FD21DEC0538");
const base::string16 kTestTitle = base::ASCIIToUTF16("Hello"); const base::string16 kTestTitle = base::UTF8ToUTF16("Hello");
const base::FilePath kTestFilePath(FILE_PATH_LITERAL("foo")); const base::FilePath kTestFilePath(FILE_PATH_LITERAL("foo"));
const int64_t kTestFileSize = 88888; const int64_t kTestFileSize = 88888;
GURL TestFaviconURL() {
return GURL("http://sample.org/favicon.png");
}
std::string TestSnippet() {
return "test snippet";
}
std::string TestAttribution() {
return "sample.org";
}
class TestOfflinePageModel : public StubOfflinePageModel { class TestOfflinePageModel : public StubOfflinePageModel {
public: public:
...@@ -77,6 +86,9 @@ class PrefetchImporterImplTest : public testing::Test { ...@@ -77,6 +86,9 @@ class PrefetchImporterImplTest : public testing::Test {
archive.title = kTestTitle; archive.title = kTestTitle;
archive.file_path = file_path; archive.file_path = file_path;
archive.file_size = kTestFileSize; archive.file_size = kTestFileSize;
archive.favicon_url = TestFaviconURL();
archive.snippet = TestSnippet();
archive.attribution = TestAttribution();
importer.ImportArchive(archive); importer.ImportArchive(archive);
task_runner_->RunUntilIdle(); task_runner_->RunUntilIdle();
} }
...@@ -114,6 +126,10 @@ TEST_F(PrefetchImporterImplTest, ImportSuccess) { ...@@ -114,6 +126,10 @@ TEST_F(PrefetchImporterImplTest, ImportSuccess) {
offline_page_model()->last_added_page().original_url_if_different); offline_page_model()->last_added_page().original_url_if_different);
EXPECT_EQ(kTestTitle, offline_page_model()->last_added_page().title); EXPECT_EQ(kTestTitle, offline_page_model()->last_added_page().title);
EXPECT_EQ(kTestFileSize, offline_page_model()->last_added_page().file_size); EXPECT_EQ(kTestFileSize, offline_page_model()->last_added_page().file_size);
EXPECT_EQ(TestSnippet(), offline_page_model()->last_added_page().snippet);
EXPECT_EQ(TestAttribution(),
offline_page_model()->last_added_page().attribution);
} }
TEST_F(PrefetchImporterImplTest, MoveFileError) { TEST_F(PrefetchImporterImplTest, MoveFileError) {
......
...@@ -69,12 +69,6 @@ struct PrefetchItem { ...@@ -69,12 +69,6 @@ struct PrefetchItem {
// The URL to the favicon image of the article's hosting web site. // The URL to the favicon image of the article's hosting web site.
GURL favicon_url; GURL favicon_url;
// A snippet of the article's contents.
std::string snippet;
// The publisher name/web site the article is attributed to.
std::string attribution;
// Number of attempts to request OPS to generate an archive for this item. // Number of attempts to request OPS to generate an archive for this item.
int generate_bundle_attempts = 0; int generate_bundle_attempts = 0;
...@@ -124,6 +118,12 @@ struct PrefetchItem { ...@@ -124,6 +118,12 @@ struct PrefetchItem {
// The title of the page. // The title of the page.
base::string16 title; base::string16 title;
// A snippet of the article's contents.
std::string snippet;
// The publisher name/web site the article is attributed to.
std::string attribution;
// The file path to the archive of the page. // The file path to the archive of the page.
base::FilePath file_path; base::FilePath file_path;
......
...@@ -103,12 +103,6 @@ TEST_F(PrefetchItemTest, OperatorEqualsCopyConstructorAndToString) { ...@@ -103,12 +103,6 @@ TEST_F(PrefetchItemTest, OperatorEqualsCopyConstructorAndToString) {
item1.favicon_url = GURL("http://favicon"); item1.favicon_url = GURL("http://favicon");
CheckFieldAndResetItem(item1, "favicon_url"); CheckFieldAndResetItem(item1, "favicon_url");
item1.snippet = "snippet";
CheckFieldAndResetItem(item1, "snippet");
item1.attribution = "attribution";
CheckFieldAndResetItem(item1, "attribution");
item1.generate_bundle_attempts = 10; item1.generate_bundle_attempts = 10;
CheckFieldAndResetItem(item1, "generate_bundle_attempts"); CheckFieldAndResetItem(item1, "generate_bundle_attempts");
...@@ -145,6 +139,12 @@ TEST_F(PrefetchItemTest, OperatorEqualsCopyConstructorAndToString) { ...@@ -145,6 +139,12 @@ TEST_F(PrefetchItemTest, OperatorEqualsCopyConstructorAndToString) {
item1.file_size = 30; item1.file_size = 30;
CheckFieldAndResetItem(item1, "file_size"); CheckFieldAndResetItem(item1, "file_size");
item1.snippet = "G";
CheckFieldAndResetItem(item1, "snippet");
item1.attribution = "H";
CheckFieldAndResetItem(item1, "attribution");
CheckAllFieldsWereTested(); CheckAllFieldsWereTested();
} }
......
...@@ -142,13 +142,7 @@ RenderPageInfo::RenderPageInfo(const RenderPageInfo& other) = default; ...@@ -142,13 +142,7 @@ RenderPageInfo::RenderPageInfo(const RenderPageInfo& other) = default;
PrefetchURL::PrefetchURL(const std::string& id, PrefetchURL::PrefetchURL(const std::string& id,
const GURL& url, const GURL& url,
const base::string16& title) const base::string16& title)
: id(id), : id(id), url(url), title(title) {}
url(url),
title(title),
thumbnail_url(GURL()),
favicon_url(GURL()),
snippet(std::string()),
attribution(std::string()) {}
PrefetchURL::PrefetchURL(const std::string& id, PrefetchURL::PrefetchURL(const std::string& id,
const GURL& url, const GURL& url,
......
...@@ -290,6 +290,9 @@ struct PrefetchArchiveInfo { ...@@ -290,6 +290,9 @@ struct PrefetchArchiveInfo {
base::string16 title; base::string16 title;
base::FilePath file_path; base::FilePath file_path;
int64_t file_size = 0; int64_t file_size = 0;
GURL favicon_url;
std::string snippet;
std::string attribution;
}; };
// These operators are implemented for testing only, see test_util.cc. // These operators are implemented for testing only, see test_util.cc.
......
...@@ -33,7 +33,7 @@ const char kTestThumbnailURL[] = "http://thumbnail.com/"; ...@@ -33,7 +33,7 @@ const char kTestThumbnailURL[] = "http://thumbnail.com/";
PrefetchURL PrefetchURL1() { PrefetchURL PrefetchURL1() {
return {kClientId1, return {kClientId1,
GURL("https://www.url1.com/"), GURL("https://www.url1.com/"),
base::ASCIIToUTF16("Title 1"), base::UTF8ToUTF16("Title 1"),
GURL("https://www.url1.com/thumbnail.png"), GURL("https://www.url1.com/thumbnail.png"),
GURL("https://www.url1.com/favicon.png"), GURL("https://www.url1.com/favicon.png"),
"snippet 1", "snippet 1",
...@@ -42,7 +42,7 @@ PrefetchURL PrefetchURL1() { ...@@ -42,7 +42,7 @@ PrefetchURL PrefetchURL1() {
PrefetchURL PrefetchURL2() { PrefetchURL PrefetchURL2() {
return {kClientId2, return {kClientId2,
GURL("https://www.url2.com/"), GURL("https://www.url2.com/"),
base::ASCIIToUTF16("Title 2"), base::UTF8ToUTF16("Title 2"),
GURL("https://www.url2.com/thumbnail.png"), GURL("https://www.url2.com/thumbnail.png"),
GURL("https://www.url2.com/favicon.png"), GURL("https://www.url2.com/favicon.png"),
"snippet 2", "snippet 2",
...@@ -51,7 +51,7 @@ PrefetchURL PrefetchURL2() { ...@@ -51,7 +51,7 @@ PrefetchURL PrefetchURL2() {
PrefetchURL PrefetchURL3() { PrefetchURL PrefetchURL3() {
return {kClientId3, return {kClientId3,
GURL("https://www.url3.com/"), GURL("https://www.url3.com/"),
base::ASCIIToUTF16("Title 3"), base::UTF8ToUTF16("Title 3"),
GURL("https://www.url3.com/thumbnail.png"), GURL("https://www.url3.com/thumbnail.png"),
GURL("https://www.url3.com/favicon.png"), GURL("https://www.url3.com/favicon.png"),
"snippet 3", "snippet 3",
......
...@@ -24,10 +24,11 @@ namespace { ...@@ -24,10 +24,11 @@ namespace {
std::unique_ptr<std::vector<PrefetchArchiveInfo>> GetArchivesSync( std::unique_ptr<std::vector<PrefetchArchiveInfo>> GetArchivesSync(
sql::Database* db) { sql::Database* db) {
static const char kSql[] = static const char kSql[] =
"SELECT offline_id, client_namespace, guid, requested_url," "SELECT offline_id,client_namespace,guid,requested_url,"
" final_archived_url, title, file_path, file_size" "final_archived_url,title,file_path,file_size,favicon_url,snippet,"
"attribution"
" FROM prefetch_items" " FROM prefetch_items"
" WHERE state = ?"; " WHERE state=?";
sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql)); sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql));
statement.BindInt(0, static_cast<int>(PrefetchItemState::DOWNLOADED)); statement.BindInt(0, static_cast<int>(PrefetchItemState::DOWNLOADED));
...@@ -45,6 +46,9 @@ std::unique_ptr<std::vector<PrefetchArchiveInfo>> GetArchivesSync( ...@@ -45,6 +46,9 @@ std::unique_ptr<std::vector<PrefetchArchiveInfo>> GetArchivesSync(
archive.file_path = archive.file_path =
store_utils::FromDatabaseFilePath(statement.ColumnString(6)); store_utils::FromDatabaseFilePath(statement.ColumnString(6));
archive.file_size = statement.ColumnInt64(7); archive.file_size = statement.ColumnInt64(7);
archive.favicon_url = GURL(statement.ColumnString(8));
archive.snippet = statement.ColumnString(9);
archive.attribution = statement.ColumnString(10);
if (!archives) if (!archives)
archives = std::make_unique<std::vector<PrefetchArchiveInfo>>(); archives = std::make_unique<std::vector<PrefetchArchiveInfo>>();
archives->push_back(archive); archives->push_back(archive);
...@@ -56,8 +60,8 @@ std::unique_ptr<std::vector<PrefetchArchiveInfo>> GetArchivesSync( ...@@ -56,8 +60,8 @@ std::unique_ptr<std::vector<PrefetchArchiveInfo>> GetArchivesSync(
bool UpdateToImportingStateSync(int64_t offline_id, sql::Database* db) { bool UpdateToImportingStateSync(int64_t offline_id, sql::Database* db) {
static const char kSql[] = static const char kSql[] =
"UPDATE prefetch_items" "UPDATE prefetch_items"
" SET state = ?" " SET state=?"
" WHERE offline_id = ?"; " WHERE offline_id=?";
sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql)); sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql));
statement.BindInt(0, static_cast<int>(PrefetchItemState::IMPORTING)); statement.BindInt(0, static_cast<int>(PrefetchItemState::IMPORTING));
......
...@@ -29,20 +29,62 @@ namespace { ...@@ -29,20 +29,62 @@ namespace {
const int64_t kTestOfflineID = 1111; const int64_t kTestOfflineID = 1111;
const int64_t kTestOfflineID2 = 223344; const int64_t kTestOfflineID2 = 223344;
const int64_t kTestOfflineID3 = 987; const int64_t kTestOfflineID3 = 987;
const GURL kTestURL("http://sample.org"); GURL TestURL() {
const GURL kTestURL2("http://sample.org"); return GURL("http://sample.org");
const GURL kTestFinalURL("https://sample.org/foo"); }
const GURL kTestFinalURL2("https://sample.org/foo"); GURL TestURL2() {
const std::string kTestGUID("C56A4180-65AA-42EC-A945-5FD21DEC0538"); return GURL("http://sample.org");
const ClientId kTestClientID("Foo", kTestGUID); }
const std::string kTestGUID2("784f1b8b-6a32-4535-9751-ade05f947aa9"); GURL TestFinalURL() {
const ClientId kTestClientID2("Foo2", kTestGUID2); return GURL("https://sample.org/foo");
const base::string16 kTestTitle = base::ASCIIToUTF16("Hello"); }
const base::string16 kTestTitle2 = base::ASCIIToUTF16("Hello2"); GURL TestFinalURL2() {
const base::FilePath kTestFilePath(FILE_PATH_LITERAL("foo")); return GURL("https://sample.org/foo");
const base::FilePath kTestFilePath2(FILE_PATH_LITERAL("foo2")); }
std::string TestGUID() {
return "C56A4180-65AA-42EC-A945-5FD21DEC0538";
}
ClientId TestClientID() {
return ClientId("Foo", TestGUID());
}
std::string TestGUID2() {
return "784f1b8b-6a32-4535-9751-ade05f947aa9";
}
ClientId TestClientID2() {
return ClientId("Foo2", TestGUID2());
}
base::string16 TestTitle() {
return base::UTF8ToUTF16("Hello");
}
base::string16 TestTitle2() {
return base::UTF8ToUTF16("Hello2");
}
base::FilePath TestFilePath() {
return base::FilePath(FILE_PATH_LITERAL("foo"));
}
base::FilePath TestFilePath2() {
return base::FilePath(FILE_PATH_LITERAL("foo2"));
}
const int64_t kTestFileSize = 88888; const int64_t kTestFileSize = 88888;
const int64_t kTestFileSize2 = 999; const int64_t kTestFileSize2 = 999;
GURL TestFaviconURL() {
return GURL("http://sample.org/favicon.png");
}
GURL TestFaviconURL2() {
return GURL("http://sample.org/favicon2.png");
}
std::string TestSnippet() {
return "test snippet";
}
std::string TestSnippet2() {
return "test snippet 2";
}
std::string TestAttribution() {
return "test attribution";
}
std::string TestAttribution2() {
return "test attribution 2";
}
class TestPrefetchImporter : public PrefetchImporter { class TestPrefetchImporter : public PrefetchImporter {
public: public:
...@@ -82,29 +124,35 @@ void ImportArchivesTaskTest::SetUp() { ...@@ -82,29 +124,35 @@ void ImportArchivesTaskTest::SetUp() {
PrefetchItem item; PrefetchItem item;
item.offline_id = kTestOfflineID; item.offline_id = kTestOfflineID;
item.state = PrefetchItemState::DOWNLOADED; item.state = PrefetchItemState::DOWNLOADED;
item.url = kTestURL; item.url = TestURL();
item.guid = kTestGUID; item.guid = TestGUID();
item.final_archived_url = kTestFinalURL; item.final_archived_url = TestFinalURL();
item.client_id = kTestClientID; item.client_id = TestClientID();
item.title = kTestTitle; item.title = TestTitle();
item.file_path = kTestFilePath; item.file_path = TestFilePath();
item.file_size = kTestFileSize; item.file_size = kTestFileSize;
item.creation_time = base::Time::Now(); item.creation_time = base::Time::Now();
item.freshness_time = item.creation_time; item.freshness_time = item.creation_time;
item.favicon_url = TestFaviconURL();
item.snippet = TestSnippet();
item.attribution = TestAttribution();
EXPECT_TRUE(store_util()->InsertPrefetchItem(item)); EXPECT_TRUE(store_util()->InsertPrefetchItem(item));
PrefetchItem item2; PrefetchItem item2;
item2.offline_id = kTestOfflineID2; item2.offline_id = kTestOfflineID2;
item2.state = PrefetchItemState::DOWNLOADED; item2.state = PrefetchItemState::DOWNLOADED;
item2.url = kTestURL2; item2.url = TestURL2();
item2.guid = kTestGUID2; item2.guid = TestGUID2();
item2.final_archived_url = kTestFinalURL2; item2.final_archived_url = TestFinalURL2();
item2.client_id = kTestClientID2; item2.client_id = TestClientID2();
item2.title = kTestTitle2; item2.title = TestTitle2();
item2.file_path = kTestFilePath2; item2.file_path = TestFilePath2();
item2.file_size = kTestFileSize2; item2.file_size = kTestFileSize2;
item2.creation_time = base::Time::Now(); item2.creation_time = base::Time::Now();
item2.freshness_time = item.creation_time; item2.freshness_time = item.creation_time;
item2.favicon_url = TestFaviconURL2();
item2.snippet = TestSnippet2();
item2.attribution = TestAttribution2();
EXPECT_TRUE(store_util()->InsertPrefetchItem(item2)); EXPECT_TRUE(store_util()->InsertPrefetchItem(item2));
PrefetchItem item3; PrefetchItem item3;
...@@ -149,20 +197,26 @@ TEST_F(ImportArchivesTaskTest, Importing) { ...@@ -149,20 +197,26 @@ TEST_F(ImportArchivesTaskTest, Importing) {
archive2 = importer()->archives()[0]; archive2 = importer()->archives()[0];
} }
EXPECT_EQ(kTestOfflineID, archive1.offline_id); EXPECT_EQ(kTestOfflineID, archive1.offline_id);
EXPECT_EQ(kTestClientID, archive1.client_id); EXPECT_EQ(TestClientID(), archive1.client_id);
EXPECT_EQ(kTestURL, archive1.url); EXPECT_EQ(TestURL(), archive1.url);
EXPECT_EQ(kTestFinalURL, archive1.final_archived_url); EXPECT_EQ(TestFinalURL(), archive1.final_archived_url);
EXPECT_EQ(kTestTitle, archive1.title); EXPECT_EQ(TestTitle(), archive1.title);
EXPECT_EQ(kTestFilePath, archive1.file_path); EXPECT_EQ(TestFilePath(), archive1.file_path);
EXPECT_EQ(kTestFileSize, archive1.file_size); EXPECT_EQ(kTestFileSize, archive1.file_size);
EXPECT_EQ(TestFaviconURL(), archive1.favicon_url);
EXPECT_EQ(TestSnippet(), archive1.snippet);
EXPECT_EQ(TestAttribution(), archive1.attribution);
EXPECT_EQ(kTestOfflineID2, archive2.offline_id); EXPECT_EQ(kTestOfflineID2, archive2.offline_id);
EXPECT_EQ(kTestClientID2, archive2.client_id); EXPECT_EQ(TestClientID2(), archive2.client_id);
EXPECT_EQ(kTestURL2, archive2.url); EXPECT_EQ(TestURL2(), archive2.url);
EXPECT_EQ(kTestFinalURL2, archive2.final_archived_url); EXPECT_EQ(TestFinalURL2(), archive2.final_archived_url);
EXPECT_EQ(kTestTitle2, archive2.title); EXPECT_EQ(TestTitle2(), archive2.title);
EXPECT_EQ(kTestFilePath2, archive2.file_path); EXPECT_EQ(TestFilePath2(), archive2.file_path);
EXPECT_EQ(kTestFileSize2, archive2.file_size); EXPECT_EQ(kTestFileSize2, archive2.file_size);
EXPECT_EQ(TestFaviconURL2(), archive2.favicon_url);
EXPECT_EQ(TestSnippet2(), archive2.snippet);
EXPECT_EQ(TestAttribution2(), archive2.attribution);
} }
} // namespace offline_pages } // namespace offline_pages
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment