Commit 285a1dab authored by pnoland's avatar pnoland Committed by Commit bot

[sync] Add autofill sync metadata to the web db

Two types of sync metadata, once stored in the sync directory, are now being
migrated to the web database. These two types are the per-entry EntityMetadata
and the ModelTypeState, which is globalstate for the autofill model type. This
change adds a migration to add the tables and functions on AutofillTable to
read/write/delete it, with associated tests.

BUG=671832

R=shess@chromium.org,mathp@chromium.org,pavely@chromium.org

Review-Url: https://codereview.chromium.org/2550293002
Cr-Commit-Position: refs/heads/master@{#437722}
parent a0835eec
......@@ -35,6 +35,10 @@
#include "components/autofill/core/common/autofill_util.h"
#include "components/autofill/core/common/form_field_data.h"
#include "components/os_crypt/os_crypt.h"
#include "components/sync/base/model_type.h"
#include "components/sync/model/metadata_batch.h"
#include "components/sync/protocol/entity_metadata.pb.h"
#include "components/sync/protocol/model_type_state.pb.h"
#include "components/webdata/common/web_database.h"
#include "sql/statement.h"
#include "sql/transaction.h"
......@@ -416,7 +420,8 @@ bool AutofillTable::CreateTablesIfNecessary() {
InitProfilePhonesTable() && InitProfileTrashTable() &&
InitMaskedCreditCardsTable() && InitUnmaskedCreditCardsTable() &&
InitServerCardMetadataTable() && InitServerAddressesTable() &&
InitServerAddressMetadataTable());
InitServerAddressMetadataTable() && InitAutofillSyncMetadataTable() &&
InitModelTypeStateTable());
}
bool AutofillTable::IsSyncable() {
......@@ -463,6 +468,9 @@ bool AutofillTable::MigrateToVersion(int version,
case 67:
*update_compatible_version = false;
return MigrateToVersion67AddMaskedCardBillingAddress();
case 70:
*update_compatible_version = false;
return MigrateToVersion70AddSyncMetadata();
}
return true;
}
......@@ -1680,6 +1688,122 @@ bool AutofillTable::IsAutofillGUIDInTrash(const std::string& guid) {
return s.Step();
}
bool AutofillTable::GetAllSyncMetadata(syncer::ModelType model_type,
syncer::MetadataBatch* metadata_batch) {
DCHECK_EQ(model_type, syncer::AUTOFILL)
<< "Only the AUTOFILL model type is supported";
syncer::EntityMetadataMap metadata_records;
if (GetAllSyncEntityMetadata(model_type, &metadata_records)) {
for (const auto& pair : metadata_records) {
// todo(pnoland): add batch transfer of metadata map
metadata_batch->AddMetadata(pair.first, pair.second);
}
} else {
return false;
}
sync_pb::ModelTypeState model_type_state;
if (GetModelTypeState(model_type, &model_type_state)) {
metadata_batch->SetModelTypeState(model_type_state);
} else {
return false;
}
return true;
}
bool AutofillTable::GetAllSyncEntityMetadata(
syncer::ModelType model_type,
syncer::EntityMetadataMap* metadata_records) {
DCHECK_EQ(model_type, syncer::AUTOFILL)
<< "Only the AUTOFILL model type is supported";
sql::Statement s(db_->GetUniqueStatement(
"SELECT storage_key, value FROM autofill_sync_metadata"));
while (s.Step()) {
std::string storage_key = s.ColumnString(0);
std::string serialized_metadata = s.ColumnString(1);
sync_pb::EntityMetadata metadata_record;
if (metadata_record.ParseFromString(serialized_metadata)) {
metadata_records->insert(std::make_pair(storage_key, metadata_record));
} else {
return false;
}
}
return true;
}
bool AutofillTable::UpdateSyncMetadata(
syncer::ModelType model_type,
const std::string& storage_key,
const sync_pb::EntityMetadata& metadata) {
DCHECK_EQ(model_type, syncer::AUTOFILL)
<< "Only the AUTOFILL model type is supported";
sql::Statement s(
db_->GetUniqueStatement("INSERT OR REPLACE INTO autofill_sync_metadata "
"(storage_key, value) VALUES(?, ?)"));
s.BindString(0, storage_key);
s.BindString(1, metadata.SerializeAsString());
return s.Run();
}
bool AutofillTable::ClearSyncMetadata(syncer::ModelType model_type,
const std::string& storage_key) {
DCHECK_EQ(model_type, syncer::AUTOFILL)
<< "Only the AUTOFILL model type is supported";
sql::Statement s(db_->GetUniqueStatement(
"DELETE FROM autofill_sync_metadata WHERE storage_key=?"));
s.BindString(0, storage_key);
return s.Run();
}
bool AutofillTable::GetModelTypeState(syncer::ModelType model_type,
sync_pb::ModelTypeState* state) {
DCHECK_EQ(model_type, syncer::AUTOFILL)
<< "Only the AUTOFILL model type is supported";
sql::Statement s(db_->GetUniqueStatement(
"SELECT value FROM autofill_model_type_state WHERE id=1"));
if (!s.Step()) {
return false;
}
std::string serialized_state = s.ColumnString(0);
return state->ParseFromString(serialized_state);
}
bool AutofillTable::UpdateModelTypeState(
syncer::ModelType model_type,
const sync_pb::ModelTypeState& model_type_state) {
DCHECK_EQ(model_type, syncer::AUTOFILL)
<< "Only the AUTOFILL model type is supported";
// Hardcode the id to force a collision, ensuring that there remains only a
// single entry.
sql::Statement s(db_->GetUniqueStatement(
"INSERT OR REPLACE INTO autofill_model_type_state (id, value) "
"VALUES(1,?)"));
s.BindString(0, model_type_state.SerializeAsString());
return s.Run();
}
bool AutofillTable::ClearModelTypeState(syncer::ModelType model_type) {
DCHECK_EQ(model_type, syncer::AUTOFILL)
<< "Only the AUTOFILL model type is supported";
sql::Statement s(db_->GetUniqueStatement(
"DELETE FROM autofill_model_type_state WHERE id=1"));
return s.Run();
}
bool AutofillTable::InitMainTable() {
if (!db_->DoesTableExist("autofill")) {
if (!db_->Execute("CREATE TABLE autofill ("
......@@ -1879,6 +2003,29 @@ bool AutofillTable::InitServerAddressMetadataTable() {
return true;
}
bool AutofillTable::InitAutofillSyncMetadataTable() {
if (!db_->DoesTableExist("autofill_sync_metadata")) {
if (!db_->Execute("CREATE TABLE autofill_sync_metadata ("
"storage_key VARCHAR PRIMARY KEY NOT NULL,"
"value BLOB)")) {
NOTREACHED();
return false;
}
}
return true;
}
bool AutofillTable::InitModelTypeStateTable() {
if (!db_->DoesTableExist("autofill_model_type_state")) {
if (!db_->Execute("CREATE TABLE autofill_model_type_state (id INTEGER "
"PRIMARY KEY, value BLOB)")) {
NOTREACHED();
return false;
}
}
return true;
}
bool AutofillTable::MigrateToVersion54AddI18nFieldsAndRemoveDeprecatedFields() {
sql::Transaction transaction(db_);
if (!transaction.Begin())
......@@ -2315,4 +2462,15 @@ bool AutofillTable::MigrateToVersion67AddMaskedCardBillingAddress() {
"ALTER TABLE masked_credit_cards ADD COLUMN billing_address_id VARCHAR");
}
} // namespace autofill
bool AutofillTable::MigrateToVersion70AddSyncMetadata() {
if (!db_->Execute("CREATE TABLE autofill_sync_metadata ("
"storage_key VARCHAR PRIMARY KEY NOT NULL,"
"value BLOB)")) {
return false;
}
return db_->Execute(
"CREATE TABLE autofill_model_type_state (id INTEGER PRIMARY KEY, value "
"BLOB)");
}
} // namespace autofill
\ No newline at end of file
......@@ -13,6 +13,8 @@
#include "base/gtest_prod_util.h"
#include "base/macros.h"
#include "base/strings/string16.h"
#include "components/sync/base/model_type.h"
#include "components/sync/model/metadata_batch.h"
#include "components/webdata/common/web_database_table.h"
class WebDatabase;
......@@ -21,6 +23,11 @@ namespace base {
class Time;
}
namespace sync_pb {
class EntityMetadata;
class ModelTypeState;
}
namespace autofill {
class AutofillChange;
......@@ -228,6 +235,18 @@ struct FormFieldData;
// a form.
// use_date The date this address was last used to fill a form,
// in internal t.
// autofill_sync_metadata
// Sync-specific metadata for autofill records.
//
// storage_key A string that uniquely identifies the metadata record
// as well as the corresponding autofill record.
// value The serialized EntityMetadata record.
//
// autofill_model_type_state
// Single row table that contains the sync ModelTypeState
// for the autofill model type.
//
// value The serialized ModelTypeState record.
class AutofillTable : public WebDatabaseTable {
public:
......@@ -403,6 +422,28 @@ class AutofillTable : public WebDatabaseTable {
// Clear all profiles.
bool ClearAutofillProfiles();
// Read all the stored metadata for |model_type| and fill |metadata_batch|
// with it.
bool GetAllSyncMetadata(syncer::ModelType model_type,
syncer::MetadataBatch* metadata_batch);
// Update the metadata row for |model_type|, keyed by |storage_key|, to
// contain the contents of |metadata|.
bool UpdateSyncMetadata(syncer::ModelType model_type,
const std::string& storage_key,
const sync_pb::EntityMetadata& metadata);
// Remove the metadata row of type |model_type| keyed by |storage|key|.
bool ClearSyncMetadata(syncer::ModelType model_type,
const std::string& storage_key);
// Update the stored sync state for the |model_type|.
bool UpdateModelTypeState(syncer::ModelType model_type,
const sync_pb::ModelTypeState& model_type_state);
// Clear the stored sync state for |model_type|.
bool ClearModelTypeState(syncer::ModelType model_type);
// Table migration functions. NB: These do not and should not rely on other
// functions in this class. The implementation of a function such as
// GetCreditCard may change over time, but MigrateToVersionXX should never
......@@ -419,6 +460,7 @@ class AutofillTable : public WebDatabaseTable {
bool MigrateToVersion65AddServerMetadataTables();
bool MigrateToVersion66AddCardBillingAddress();
bool MigrateToVersion67AddMaskedCardBillingAddress();
bool MigrateToVersion70AddSyncMetadata();
// Max data length saved in the table, AKA the maximum length allowed for
// form data.
......@@ -474,6 +516,12 @@ class AutofillTable : public WebDatabaseTable {
std::vector<AutofillChange>* changes,
base::Time time);
bool GetAllSyncEntityMetadata(syncer::ModelType model_type,
syncer::EntityMetadataMap* metadata_records);
bool GetModelTypeState(syncer::ModelType model_type,
sync_pb::ModelTypeState* state);
// Insert a single AutofillEntry into the autofill table.
bool InsertAutofillEntry(const AutofillEntry& entry);
......@@ -496,6 +544,8 @@ class AutofillTable : public WebDatabaseTable {
bool InitServerCardMetadataTable();
bool InitServerAddressesTable();
bool InitServerAddressMetadataTable();
bool InitAutofillSyncMetadataTable();
bool InitModelTypeStateTable();
DISALLOW_COPY_AND_ASSIGN(AutofillTable);
};
......
......@@ -35,6 +35,8 @@
#include "components/autofill/core/common/autofill_util.h"
#include "components/autofill/core/common/form_field_data.h"
#include "components/os_crypt/os_crypt_mocker.h"
#include "components/sync/protocol/entity_metadata.pb.h"
#include "components/sync/protocol/model_type_state.pb.h"
#include "components/webdata/common/web_database.h"
#include "sql/statement.h"
#include "testing/gtest/include/gtest/gtest.h"
......@@ -2016,4 +2018,90 @@ TEST_F(AutofillTableTest, GetFormValuesForElementName_SubstringMatchEnabled) {
}
}
TEST_F(AutofillTableTest, GetAllSyncMetadata) {
sync_pb::EntityMetadata metadata;
std::string storage_key = "storage_key";
std::string storage_key2 = "storage_key2";
metadata.set_sequence_number(1);
EXPECT_TRUE(
table_->UpdateSyncMetadata(syncer::AUTOFILL, storage_key, metadata));
sync_pb::ModelTypeState model_type_state;
model_type_state.set_initial_sync_done(true);
EXPECT_TRUE(table_->UpdateModelTypeState(syncer::AUTOFILL, model_type_state));
metadata.set_sequence_number(2);
EXPECT_TRUE(
table_->UpdateSyncMetadata(syncer::AUTOFILL, storage_key2, metadata));
syncer::MetadataBatch metadata_batch;
EXPECT_TRUE(table_->GetAllSyncMetadata(syncer::AUTOFILL, &metadata_batch));
EXPECT_TRUE(metadata_batch.GetModelTypeState().initial_sync_done());
syncer::EntityMetadataMap metadata_records = metadata_batch.TakeAllMetadata();
EXPECT_EQ(metadata_records.size(), 2u);
EXPECT_EQ(metadata_records[storage_key].sequence_number(), 1);
EXPECT_EQ(metadata_records[storage_key2].sequence_number(), 2);
// Now check that a model type state update replaces the old value
model_type_state.set_initial_sync_done(false);
EXPECT_TRUE(table_->UpdateModelTypeState(syncer::AUTOFILL, model_type_state));
EXPECT_TRUE(table_->GetAllSyncMetadata(syncer::AUTOFILL, &metadata_batch));
EXPECT_FALSE(metadata_batch.GetModelTypeState().initial_sync_done());
}
TEST_F(AutofillTableTest, WriteThenDeleteSyncMetadata) {
sync_pb::EntityMetadata metadata;
syncer::MetadataBatch metadata_batch;
std::string storage_key = "storage_key";
sync_pb::ModelTypeState model_type_state;
model_type_state.set_initial_sync_done(true);
metadata.set_client_tag_hash("client_hash");
// Write the data into the store.
EXPECT_TRUE(
table_->UpdateSyncMetadata(syncer::AUTOFILL, storage_key, metadata));
EXPECT_TRUE(table_->UpdateModelTypeState(syncer::AUTOFILL, model_type_state));
// Delete the data we just wrote.
EXPECT_TRUE(table_->ClearSyncMetadata(syncer::AUTOFILL, storage_key));
// It shouldn't be there any more.
EXPECT_TRUE(table_->GetAllSyncMetadata(syncer::AUTOFILL, &metadata_batch));
syncer::EntityMetadataMap metadata_records = metadata_batch.TakeAllMetadata();
EXPECT_EQ(metadata_records.size(), 0u);
// Now delete the model type state.
EXPECT_TRUE(table_->ClearModelTypeState(syncer::AUTOFILL));
EXPECT_FALSE(table_->GetAllSyncMetadata(syncer::AUTOFILL, &metadata_batch));
}
TEST_F(AutofillTableTest, CorruptSyncMetadata) {
syncer::MetadataBatch metadata_batch;
sync_pb::ModelTypeState state;
std::string storage_key = "storage_key";
sql::Statement s(db_->GetSQLConnection()->GetUniqueStatement(
"INSERT OR REPLACE INTO autofill_sync_metadata "
"(storage_key, value) VALUES(?, ?)"));
s.BindString(0, storage_key);
s.BindString(1, "unparseable");
sql::Statement s2(db_->GetSQLConnection()->GetUniqueStatement(
"INSERT OR REPLACE INTO autofill_model_type_state "
"(rowid, value) VALUES(1, ?)"));
s2.BindString(0, "unparseable");
EXPECT_TRUE(s.Run());
EXPECT_TRUE(s2.Run());
EXPECT_FALSE(table_->GetAllSyncMetadata(syncer::AUTOFILL, &metadata_batch));
}
} // namespace autofill
PRAGMA foreign_keys=OFF;
BEGIN TRANSACTION;
CREATE TABLE meta(key LONGVARCHAR NOT NULL UNIQUE PRIMARY KEY, value LONGVARCHAR);
INSERT INTO "meta" VALUES('mmap_status','-1');
INSERT INTO "meta" VALUES('version','69');
INSERT INTO "meta" VALUES('last_compatible_version','69');
INSERT INTO "meta" VALUES('Builtin Keyword Version','96');
CREATE TABLE token_service (service VARCHAR PRIMARY KEY NOT NULL,encrypted_token BLOB);
CREATE TABLE keywords (id INTEGER PRIMARY KEY,short_name VARCHAR NOT NULL,keyword VARCHAR NOT NULL,favicon_url VARCHAR NOT NULL,url VARCHAR NOT NULL,safe_for_autoreplace INTEGER,originating_url VARCHAR,date_created INTEGER DEFAULT 0,usage_count INTEGER DEFAULT 0,input_encodings VARCHAR,suggest_url VARCHAR,prepopulate_id INTEGER DEFAULT 0,created_by_policy INTEGER DEFAULT 0,instant_url VARCHAR,last_modified INTEGER DEFAULT 0,sync_guid VARCHAR,alternate_urls VARCHAR,search_terms_replacement_key VARCHAR,image_url VARCHAR,search_url_post_params VARCHAR,suggest_url_post_params VARCHAR,instant_url_post_params VARCHAR,image_url_post_params VARCHAR,new_tab_url VARCHAR, last_visited INTEGER DEFAULT 0);
INSERT INTO "keywords" VALUES(2,'Google','google.com','http://www.google.com/favicon.ico','{google:baseURL}search?q={searchTerms}&{google:RLZ}{google:originalQueryForSuggestion}{google:assistedQueryStats}{google:searchFieldtrialParameter}{google:iOSSearchLanguage}{google:searchClient}{google:sourceId}{google:instantExtendedEnabledParameter}{google:contextualSearchVersion}ie={inputEncoding}',1,'',0,0,'UTF-8','{google:baseSuggestURL}search?{google:searchFieldtrialParameter}client={google:suggestClient}&gs_ri={google:suggestRid}&xssi=t&q={searchTerms}&{google:inputType}{google:cursorPosition}{google:currentPageUrl}{google:pageClassification}{google:searchVersion}{google:sessionToken}{google:prefetchQuery}sugkey={google:suggestAPIKeyParameter}',1,0,'{google:baseURL}webhp?sourceid=chrome-instant&{google:RLZ}{google:forceInstantResults}{google:instantExtendedEnabledParameter}ie={inputEncoding}',0,'3967f1bd-7913-440d-aab5-319ae178837a','["{google:baseURL}#q={searchTerms}","{google:baseURL}search#q={searchTerms}","{google:baseURL}webhp#q={searchTerms}","{google:baseURL}s#q={searchTerms}","{google:baseURL}s?q={searchTerms}"]','espv','{google:baseURL}searchbyimage/upload','','','','encoded_image={google:imageThumbnail},image_url={google:imageURL},sbisrc={google:imageSearchSource},original_width={google:imageOriginalWidth},original_height={google:imageOriginalHeight}','{google:baseURL}_/chrome/newtab?{google:RLZ}{google:instantExtendedEnabledParameter}ie={inputEncoding}',0);
INSERT INTO "keywords" VALUES(3,'Bing','bing.com','https://www.bing.com/s/a/bing_p.ico','https://www.bing.com/search?q={searchTerms}&PC=U316&FORM=CHROMN',1,'',0,0,'UTF-8','https://www.bing.com/osjson.aspx?query={searchTerms}&language={language}&PC=U316',3,0,'',0,'edf5b23b-f8c6-4235-80a2-c686b9de3e37','[]','','https://www.bing.com/images/detail/search?iss=sbi&FORM=CHROMI#enterInsights','','','','imgurl={google:imageURL}','https://www.bing.com/chrome/newtab',0);
INSERT INTO "keywords" VALUES(4,'Yahoo!','yahoo.com','https://search.yahoo.com/favicon.ico','https://search.yahoo.com/search?ei={inputEncoding}&fr=crmas&p={searchTerms}',1,'',0,0,'UTF-8','https://search.yahoo.com/sugg/chrome?output=fxjson&appid=crmas&command={searchTerms}',2,0,'',0,'56b59942-da06-4f53-841b-a9ef28f50ac8','[]','','','','','','','',0);
INSERT INTO "keywords" VALUES(5,'AOL','aol.com','http://search.aol.com/favicon.ico','http://search.aol.com/aol/search?q={searchTerms}',1,'',0,0,'UTF-8','http://autocomplete.search.aol.com/autocomplete/get?output=json&it=&q={searchTerms}',35,0,'',0,'1a485f3a-b545-4265-8515-d49914865ebd','[]','','','','','','','',0);
INSERT INTO "keywords" VALUES(6,'Ask','ask.com','http://sp.ask.com/sh/i/a16/favicon/favicon.ico','http://www.ask.com/web?q={searchTerms}',1,'',0,0,'UTF-8','http://ss.ask.com/query?q={searchTerms}&li=ff',4,0,'',0,'6725c539-3c39-4518-b58a-2a0623007920','[]','','','','','','','',0);
CREATE TABLE autofill (name VARCHAR, value VARCHAR, value_lower VARCHAR, date_created INTEGER DEFAULT 0, date_last_used INTEGER DEFAULT 0, count INTEGER DEFAULT 1, PRIMARY KEY (name, value));
CREATE TABLE credit_cards ( guid VARCHAR PRIMARY KEY, name_on_card VARCHAR, expiration_month INTEGER, expiration_year INTEGER, card_number_encrypted BLOB, date_modified INTEGER NOT NULL DEFAULT 0, origin VARCHAR DEFAULT '', use_count INTEGER NOT NULL DEFAULT 0, use_date INTEGER NOT NULL DEFAULT 0, billing_address_id VARCHAR);
CREATE TABLE autofill_profiles ( guid VARCHAR PRIMARY KEY, company_name VARCHAR, street_address VARCHAR, dependent_locality VARCHAR, city VARCHAR, state VARCHAR, zipcode VARCHAR, sorting_code VARCHAR, country_code VARCHAR, date_modified INTEGER NOT NULL DEFAULT 0, origin VARCHAR DEFAULT '', language_code VARCHAR, use_count INTEGER NOT NULL DEFAULT 0, use_date INTEGER NOT NULL DEFAULT 0);
CREATE TABLE autofill_profile_names ( guid VARCHAR, first_name VARCHAR, middle_name VARCHAR, last_name VARCHAR, full_name VARCHAR);
CREATE TABLE autofill_profile_emails ( guid VARCHAR, email VARCHAR);
CREATE TABLE autofill_profile_phones ( guid VARCHAR, number VARCHAR);
CREATE TABLE autofill_profiles_trash ( guid VARCHAR);
CREATE TABLE masked_credit_cards (id VARCHAR,status VARCHAR,name_on_card VARCHAR,type VARCHAR,last_four VARCHAR,exp_month INTEGER DEFAULT 0,exp_year INTEGER DEFAULT 0, billing_address_id VARCHAR);
CREATE TABLE unmasked_credit_cards (id VARCHAR,card_number_encrypted VARCHAR, use_count INTEGER NOT NULL DEFAULT 0, use_date INTEGER NOT NULL DEFAULT 0, unmask_date INTEGER NOT NULL DEFAULT 0);
CREATE TABLE server_card_metadata (id VARCHAR NOT NULL,use_count INTEGER NOT NULL DEFAULT 0, use_date INTEGER NOT NULL DEFAULT 0);
CREATE TABLE server_addresses (id VARCHAR,company_name VARCHAR,street_address VARCHAR,address_1 VARCHAR,address_2 VARCHAR,address_3 VARCHAR,address_4 VARCHAR,postal_code VARCHAR,sorting_code VARCHAR,country_code VARCHAR,language_code VARCHAR, recipient_name VARCHAR, phone_number VARCHAR);
CREATE TABLE server_address_metadata (id VARCHAR NOT NULL,use_count INTEGER NOT NULL DEFAULT 0, use_date INTEGER NOT NULL DEFAULT 0);
CREATE INDEX autofill_name ON autofill (name);
CREATE INDEX autofill_name_value_lower ON autofill (name, value_lower);
COMMIT;
......@@ -55,6 +55,7 @@ bundle_data("unit_tests_bundle_data") {
"//components/test/data/web_database/version_66.sql",
"//components/test/data/web_database/version_67.sql",
"//components/test/data/web_database/version_68.sql",
"//components/test/data/web_database/version_69.sql",
]
outputs = [
"{{bundle_resources_dir}}/" +
......
......@@ -13,13 +13,13 @@
// corresponding changes must happen in the unit tests, and new migration test
// added. See |WebDatabaseMigrationTest::kCurrentTestedVersionNumber|.
// static
const int WebDatabase::kCurrentVersionNumber = 69;
const int WebDatabase::kCurrentVersionNumber = 70;
const int WebDatabase::kDeprecatedVersionNumber = 51;
namespace {
const int kCompatibleVersionNumber = 68;
const int kCompatibleVersionNumber = 70;
// Change the version number and possibly the compatibility version of
// |meta_table_|.
......
......@@ -130,7 +130,7 @@ class WebDatabaseMigrationTest : public testing::Test {
DISALLOW_COPY_AND_ASSIGN(WebDatabaseMigrationTest);
};
const int WebDatabaseMigrationTest::kCurrentTestedVersionNumber = 69;
const int WebDatabaseMigrationTest::kCurrentTestedVersionNumber = 70;
void WebDatabaseMigrationTest::LoadDatabase(
const base::FilePath::StringType& file) {
......@@ -1134,3 +1134,35 @@ TEST_F(WebDatabaseMigrationTest, MigrateVersion68ToCurrent) {
connection.DoesColumnExist("keywords", "last_visited"));
}
}
// Tests addition of sync metadata and model type state tables.
TEST_F(WebDatabaseMigrationTest, MigrateVersion69ToCurrent) {
ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_69.sql")));
// Verify pre-conditions.
{
sql::Connection connection;
ASSERT_TRUE(connection.Open(GetDatabasePath()));
ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));
sql::MetaTable meta_table;
ASSERT_TRUE(meta_table.Init(&connection, 69, 69));
EXPECT_FALSE(connection.DoesTableExist("autofill_sync_metadata"));
EXPECT_FALSE(connection.DoesTableExist("autofill_model_type_state"));
}
DoMigration();
// Verify post-conditions.
{
sql::Connection connection;
ASSERT_TRUE(connection.Open(GetDatabasePath()));
ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));
EXPECT_EQ(kCurrentTestedVersionNumber, VersionFromConnection(&connection));
EXPECT_TRUE(connection.DoesTableExist("autofill_sync_metadata"));
EXPECT_TRUE(connection.DoesTableExist("autofill_model_type_state"));
}
}
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