summaryrefslogtreecommitdiffstats
path: root/chrome/browser/sync/syncable
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/browser/sync/syncable')
-rwxr-xr-xchrome/browser/sync/syncable/directory_backing_store.cc309
-rw-r--r--chrome/browser/sync/syncable/directory_backing_store.h38
-rwxr-xr-xchrome/browser/sync/syncable/directory_backing_store_unittest.cc523
-rw-r--r--chrome/browser/sync/syncable/model_type.h16
-rwxr-xr-xchrome/browser/sync/syncable/syncable.cc87
-rwxr-xr-xchrome/browser/sync/syncable/syncable.h62
-rwxr-xr-xchrome/browser/sync/syncable/syncable_columns.h10
-rwxr-xr-xchrome/browser/sync/syncable/syncable_unittest.cc20
8 files changed, 951 insertions, 114 deletions
diff --git a/chrome/browser/sync/syncable/directory_backing_store.cc b/chrome/browser/sync/syncable/directory_backing_store.cc
index 6df61be..4cdf7c2 100755
--- a/chrome/browser/sync/syncable/directory_backing_store.cc
+++ b/chrome/browser/sync/syncable/directory_backing_store.cc
@@ -14,7 +14,9 @@
#include "base/hash_tables.h"
#include "base/logging.h"
+#include "chrome/browser/sync/protocol/bookmark_specifics.pb.h"
#include "chrome/browser/sync/protocol/service_constants.h"
+#include "chrome/browser/sync/protocol/sync.pb.h"
#include "chrome/browser/sync/syncable/syncable-inl.h"
#include "chrome/browser/sync/syncable/syncable_columns.h"
#include "chrome/browser/sync/util/crypto_helpers.h"
@@ -36,13 +38,7 @@ namespace syncable {
static const string::size_type kUpdateStatementBufferSize = 2048;
// Increment this version whenever updating DB tables.
-static const int32 kCurrentDBVersion = 68;
-
-static void RegisterPathNameCollate(sqlite3* dbhandle) {
- const int collate = SQLITE_UTF8;
- CHECK(SQLITE_OK == sqlite3_create_collation(dbhandle, "PATHNAME", collate,
- NULL, &ComparePathNames16));
-}
+extern const int32 kCurrentDBVersion = 69; // Extern only for our unittest.
namespace {
@@ -58,12 +54,13 @@ int ExecQuery(sqlite3* dbhandle, const char* query) {
return result;
}
-} // namespace
-
-static string GenerateCacheGUID() {
+string GenerateCacheGUID() {
return Generate128BitRandomHexString();
}
+} // namespace
+
+
// Iterate over the fields of |entry| and bind each to |statement| for
// updating. Returns the number of args bound.
int BindFields(const EntryKernel& entry, SQLStatement* statement) {
@@ -81,11 +78,10 @@ int BindFields(const EntryKernel& entry, SQLStatement* statement) {
for ( ; i < STRING_FIELDS_END; ++i) {
statement->bind_string(index++, entry.ref(static_cast<StringField>(i)));
}
- for ( ; i < BLOB_FIELDS_END; ++i) {
- uint8* blob = entry.ref(static_cast<BlobField>(i)).empty() ?
- NULL : const_cast<uint8*>(&entry.ref(static_cast<BlobField>(i)).at(0));
- statement->bind_blob(index++, blob,
- entry.ref(static_cast<BlobField>(i)).size());
+ std::string temp;
+ for ( ; i < PROTO_FIELDS_END; ++i) {
+ entry.ref(static_cast<ProtoField>(i)).SerializeToString(&temp);
+ statement->bind_blob(index++, temp.data(), temp.length());
}
return index;
}
@@ -110,12 +106,12 @@ EntryKernel* UnpackEntry(SQLStatement* statement) {
result->put(static_cast<BitField>(i), (0 != statement->column_int(i)));
}
for ( ; i < STRING_FIELDS_END; ++i) {
- result->put(static_cast<StringField>(i),
+ result->put(static_cast<StringField>(i),
statement->column_string(i));
}
- for ( ; i < BLOB_FIELDS_END; ++i) {
- statement->column_blob_as_vector(
- i, &result->mutable_ref(static_cast<BlobField>(i)));
+ for ( ; i < PROTO_FIELDS_END; ++i) {
+ result->mutable_ref(static_cast<ProtoField>(i)).ParseFromArray(
+ statement->column_blob(i), statement->column_bytes(i));
}
ZeroFields(result, i);
} else {
@@ -125,8 +121,11 @@ EntryKernel* UnpackEntry(SQLStatement* statement) {
return result;
}
-static string ComposeCreateTableColumnSpecs(const ColumnSpec* begin,
- const ColumnSpec* end) {
+namespace {
+
+string ComposeCreateTableColumnSpecs() {
+ const ColumnSpec* begin = g_metas_columns;
+ const ColumnSpec* end = g_metas_columns + arraysize(g_metas_columns);
string query;
query.reserve(kUpdateStatementBufferSize);
char separator = '(';
@@ -141,6 +140,18 @@ static string ComposeCreateTableColumnSpecs(const ColumnSpec* begin,
return query;
}
+void AppendColumnList(std::string* output) {
+ const char* joiner = " ";
+ // Be explicit in SELECT order to match up with UnpackEntry.
+ for (int i = BEGIN_FIELDS; i < BEGIN_FIELDS + FIELD_COUNT; ++i) {
+ output->append(joiner);
+ output->append(ColumnName(i));
+ joiner = ", ";
+ }
+}
+
+} // namespace
+
///////////////////////////////////////////////////////////////////////////////
// DirectoryBackingStore implementation.
@@ -149,7 +160,8 @@ DirectoryBackingStore::DirectoryBackingStore(const string& dir_name,
: load_dbhandle_(NULL),
save_dbhandle_(NULL),
dir_name_(dir_name),
- backing_filepath_(backing_filepath) {
+ backing_filepath_(backing_filepath),
+ needs_column_refresh_(false) {
}
DirectoryBackingStore::~DirectoryBackingStore() {
@@ -182,7 +194,6 @@ bool DirectoryBackingStore::OpenAndConfigureHandleHelper(
}
}
sqlite3_busy_timeout(*handle, kDirectoryBackingStoreBusyTimeoutMs);
- RegisterPathNameCollate(*handle);
#if defined(OS_WIN)
// Do not index this file. Scanning can occur every time we close the file,
// which causes long delays in SQLite's file locking.
@@ -200,8 +211,7 @@ bool DirectoryBackingStore::OpenAndConfigureHandleHelper(
DirOpenResult DirectoryBackingStore::Load(MetahandlesIndex* entry_bucket,
ExtendedAttributes* xattrs_bucket,
Directory::KernelLoadInfo* kernel_load_info) {
- DCHECK(load_dbhandle_ == NULL);
- if (!OpenAndConfigureHandleHelper(&load_dbhandle_))
+ if (!BeginLoad())
return FAILED_OPEN_DATABASE;
DirOpenResult result = InitializeTables();
@@ -213,10 +223,18 @@ DirOpenResult DirectoryBackingStore::Load(MetahandlesIndex* entry_bucket,
LoadExtendedAttributes(xattrs_bucket);
LoadInfo(kernel_load_info);
+ EndLoad();
+ return OPENED;
+}
+
+bool DirectoryBackingStore::BeginLoad() {
+ DCHECK(load_dbhandle_ == NULL);
+ return OpenAndConfigureHandleHelper(&load_dbhandle_);
+}
+
+void DirectoryBackingStore::EndLoad() {
sqlite3_close(load_dbhandle_);
load_dbhandle_ = NULL; // No longer used.
-
- return OPENED;
}
bool DirectoryBackingStore::SaveChanges(
@@ -273,25 +291,38 @@ DirOpenResult DirectoryBackingStore::InitializeTables() {
if (SQLITE_OK != transaction.BeginExclusive()) {
return FAILED_DISK_FULL;
}
- int version_on_disk = 0;
+ int version_on_disk = GetVersion();
int last_result = SQLITE_OK;
- if (DoesSqliteTableExist(load_dbhandle_, "share_version")) {
- SQLStatement version_query;
- version_query.prepare(load_dbhandle_, "SELECT data from share_version");
- last_result = version_query.step();
- if (SQLITE_ROW == last_result) {
- version_on_disk = version_query.column_int(0);
- }
- last_result = version_query.reset();
+ // Upgrade from version 67. Version 67 was widely distributed as the original
+ // Bookmark Sync release. Version 68 removed unique naming.
+ if (version_on_disk == 67) {
+ if (MigrateVersion67To68())
+ version_on_disk = 68;
+ }
+ // Version 69 introduced additional datatypes.
+ if (version_on_disk == 68) {
+ if (MigrateVersion68To69())
+ version_on_disk = 69;
}
+
+ // If one of the migrations requested it, drop columns that aren't current.
+ // It's only safe to do this after migrating all the way to the current
+ // version.
+ if (version_on_disk == kCurrentDBVersion && needs_column_refresh_) {
+ if (!RefreshColumns())
+ version_on_disk = 0;
+ }
+
+ // A final, alternative catch-all migration to simply re-sync everything.
if (version_on_disk != kCurrentDBVersion) {
if (version_on_disk > kCurrentDBVersion) {
transaction.Rollback();
return FAILED_NEWER_VERSION;
}
+ // Fallback (re-sync everything) migration path.
LOG(INFO) << "Old/null sync database, version " << version_on_disk;
- // Delete the existing database (if any), and create a freshone.
+ // Delete the existing database (if any), and create a fresh one.
if (SQLITE_OK == last_result) {
DropAllTables();
if (SQLITE_DONE == CreateTables()) {
@@ -323,17 +354,40 @@ DirOpenResult DirectoryBackingStore::InitializeTables() {
return FAILED_DISK_FULL;
}
+bool DirectoryBackingStore::RefreshColumns() {
+ DCHECK(needs_column_refresh_);
+
+ // Create a new table named temp_metas.
+ SafeDropTable("temp_metas");
+ if (CreateMetasTable(true) != SQLITE_DONE)
+ return false;
+
+ // Populate temp_metas from metas.
+ std::string query = "INSERT INTO temp_metas (";
+ AppendColumnList(&query);
+ query.append(") SELECT ");
+ AppendColumnList(&query);
+ query.append(" FROM metas");
+ if (ExecQuery(load_dbhandle_, query.c_str()) != SQLITE_DONE)
+ return false;
+
+ // Drop metas.
+ SafeDropTable("metas");
+
+ // Rename temp_metas -> metas.
+ int result = ExecQuery(load_dbhandle_,
+ "ALTER TABLE temp_metas RENAME TO metas");
+ if (result != SQLITE_DONE)
+ return false;
+ needs_column_refresh_ = false;
+ return true;
+}
+
void DirectoryBackingStore::LoadEntries(MetahandlesIndex* entry_bucket) {
string select;
select.reserve(kUpdateStatementBufferSize);
- select.append("SELECT");
- const char* joiner = " ";
- // Be explicit in SELECT order to match up with UnpackEntry.
- for (int i = BEGIN_FIELDS; i < BEGIN_FIELDS + FIELD_COUNT; ++i) {
- select.append(joiner);
- select.append(ColumnName(i));
- joiner = ", ";
- }
+ select.append("SELECT ");
+ AppendColumnList(&select);
select.append(" FROM metas ");
SQLStatement statement;
statement.prepare(load_dbhandle_, select.c_str());
@@ -402,7 +456,7 @@ bool DirectoryBackingStore::SaveEntryToDB(const EntryKernel& entry) {
values.append("VALUES ");
const char* separator = "( ";
int i = 0;
- for (i = BEGIN_FIELDS; i < BLOB_FIELDS_END; ++i) {
+ for (i = BEGIN_FIELDS; i < PROTO_FIELDS_END; ++i) {
query.append(separator);
values.append(separator);
separator = ", ";
@@ -517,11 +571,162 @@ int DirectoryBackingStore::CreateExtendedAttributeTable() {
void DirectoryBackingStore::DropAllTables() {
SafeDropTable("metas");
+ SafeDropTable("temp_metas");
SafeDropTable("share_info");
SafeDropTable("share_version");
SafeDropTable("extended_attributes");
+ needs_column_refresh_ = false;
+}
+
+bool DirectoryBackingStore::MigrateToSpecifics(
+ const char* old_columns,
+ const char* specifics_column,
+ void (*handler_function)(SQLStatement* old_value_query,
+ int old_value_column,
+ sync_pb::EntitySpecifics* mutable_new_value)) {
+ std::string query_sql = StringPrintf("SELECT metahandle, %s, %s FROM metas",
+ specifics_column, old_columns);
+ std::string update_sql = StringPrintf(
+ "UPDATE metas SET %s = ? WHERE metahandle = ?", specifics_column);
+ SQLStatement query;
+ query.prepare(load_dbhandle_, query_sql.c_str());
+ while (query.step() == SQLITE_ROW) {
+ int64 metahandle = query.column_int64(0);
+ std::string new_value_bytes;
+ query.column_blob_as_string(1, &new_value_bytes);
+ sync_pb::EntitySpecifics new_value;
+ new_value.ParseFromString(new_value_bytes);
+ handler_function(&query, 2, &new_value);
+ new_value.SerializeToString(&new_value_bytes);
+
+ SQLStatement update;
+ update.prepare(load_dbhandle_, update_sql.data(), update_sql.length());
+ update.bind_blob(0, new_value_bytes.data(), new_value_bytes.length());
+ update.bind_int64(1, metahandle);
+ if (update.step() != SQLITE_DONE) {
+ NOTREACHED();
+ return false;
+ }
+ }
+ return true;
+}
+
+bool DirectoryBackingStore::AddColumn(const ColumnSpec* column) {
+ SQLStatement add_column;
+ std::string sql = StringPrintf("ALTER TABLE metas ADD COLUMN %s %s",
+ column->name, column->spec);
+ add_column.prepare(load_dbhandle_, sql.c_str());
+ return add_column.step() == SQLITE_DONE;
+}
+
+bool DirectoryBackingStore::SetVersion(int version) {
+ SQLStatement statement;
+ statement.prepare(load_dbhandle_, "UPDATE share_version SET data = ?");
+ statement.bind_int(0, version);
+ return statement.step() == SQLITE_DONE;
+}
+
+int DirectoryBackingStore::GetVersion() {
+ if (!DoesSqliteTableExist(load_dbhandle_, "share_version"))
+ return 0;
+ SQLStatement version_query;
+ version_query.prepare(load_dbhandle_, "SELECT data from share_version");
+ if (SQLITE_ROW != version_query.step())
+ return 0;
+ int value = version_query.column_int(0);
+ if (version_query.reset() != SQLITE_OK)
+ return 0;
+ return value;
+}
+
+bool DirectoryBackingStore::MigrateVersion67To68() {
+ // This change simply removed three columns:
+ // string NAME
+ // string UNSANITIZED_NAME
+ // string SERVER_NAME
+ // No data migration is necessary, but we should do a column refresh.
+ SetVersion(68);
+ needs_column_refresh_ = true;
+ return true;
+}
+
+namespace {
+
+// Callback passed to MigrateToSpecifics for the v68->v69 migration. See
+// MigrateVersion68To69().
+void EncodeBookmarkURLAndFavicon(SQLStatement* old_value_query,
+ int old_value_column,
+ sync_pb::EntitySpecifics* mutable_new_value) {
+ // Extract data from the column trio we expect.
+ bool old_is_bookmark_object = old_value_query->column_bool(old_value_column);
+ std::string old_url = old_value_query->column_string(old_value_column + 1);
+ std::string old_favicon;
+ old_value_query->column_blob_as_string(old_value_column + 2, &old_favicon);
+ bool old_is_dir = old_value_query->column_bool(old_value_column + 3);
+
+ if (old_is_bookmark_object) {
+ sync_pb::BookmarkSpecifics* bookmark_data =
+ mutable_new_value->MutableExtension(sync_pb::bookmark);
+ if (!old_is_dir) {
+ bookmark_data->set_url(old_url);
+ bookmark_data->set_favicon(old_favicon);
+ }
+ }
}
+} // namespace
+
+bool DirectoryBackingStore::MigrateVersion68To69() {
+ // In Version 68, there were columns on table 'metas':
+ // string BOOKMARK_URL
+ // string SERVER_BOOKMARK_URL
+ // blob BOOKMARK_FAVICON
+ // blob SERVER_BOOKMARK_FAVICON
+ // In version 69, these columns went away in favor of storing
+ // a serialized EntrySpecifics protobuf in the columns:
+ // protobuf blob SPECIFICS
+ // protobuf blob SERVER_SPECIFICS
+ // For bookmarks, EntrySpecifics is extended as per
+ // bookmark_specifics.proto. This migration converts bookmarks from the
+ // former scheme to the latter scheme.
+
+ // First, add the two new columns to the schema.
+ if (!AddColumn(&g_metas_columns[SPECIFICS]))
+ return false;
+ if (!AddColumn(&g_metas_columns[SERVER_SPECIFICS]))
+ return false;
+
+ // Next, fold data from the old columns into the new protobuf columns.
+ if (!MigrateToSpecifics(("is_bookmark_object, bookmark_url, "
+ "bookmark_favicon, is_dir"),
+ "specifics",
+ &EncodeBookmarkURLAndFavicon)) {
+ return false;
+ }
+ if (!MigrateToSpecifics(("server_is_bookmark_object, "
+ "server_bookmark_url, "
+ "server_bookmark_favicon, "
+ "server_is_dir"),
+ "server_specifics",
+ &EncodeBookmarkURLAndFavicon)) {
+ return false;
+ }
+
+ // Lastly, fix up the "Google Chrome" folder, which is of the TOP_LEVEL_FOLDER
+ // ModelType: it shouldn't have BookmarkSpecifics.
+ SQLStatement clear_permanent_items;
+ clear_permanent_items.prepare(load_dbhandle_,
+ "UPDATE metas SET specifics = NULL, server_specifics = NULL WHERE "
+ "singleton_tag IN ('google_chrome')");
+ if (clear_permanent_items.step() != SQLITE_DONE)
+ return false;
+
+ SetVersion(69);
+ needs_column_refresh_ = true; // Trigger deletion of old columns.
+ return true;
+}
+
+
int DirectoryBackingStore::CreateTables() {
LOG(INFO) << "First run, creating tables";
// Create two little tables share_version and share_info
@@ -579,9 +784,7 @@ int DirectoryBackingStore::CreateTables() {
if (result != SQLITE_DONE)
return result;
// Create the big metas table.
- string query = "CREATE TABLE metas " + ComposeCreateTableColumnSpecs
- (g_metas_columns, g_metas_columns + arraysize(g_metas_columns));
- result = ExecQuery(load_dbhandle_, query.c_str());
+ result = CreateMetasTable(false);
if (result != SQLITE_DONE)
return result;
{
@@ -610,4 +813,12 @@ sqlite3* DirectoryBackingStore::LazyGetSaveHandle() {
return save_dbhandle_;
}
+int DirectoryBackingStore::CreateMetasTable(bool is_temporary) {
+ const char* name = is_temporary ? "temp_metas" : "metas";
+ string query = "CREATE TABLE ";
+ query.append(name);
+ query.append(ComposeCreateTableColumnSpecs());
+ return ExecQuery(load_dbhandle_, query.c_str());
+}
+
} // namespace syncable
diff --git a/chrome/browser/sync/syncable/directory_backing_store.h b/chrome/browser/sync/syncable/directory_backing_store.h
index a3fa5093..a4294ae 100644
--- a/chrome/browser/sync/syncable/directory_backing_store.h
+++ b/chrome/browser/sync/syncable/directory_backing_store.h
@@ -11,12 +11,19 @@
#include "base/file_path.h"
#include "chrome/browser/sync/syncable/dir_open_result.h"
#include "chrome/browser/sync/syncable/syncable.h"
+#include "testing/gtest/include/gtest/gtest_prod.h" // For FRIEND_TEST
extern "C" {
struct sqlite3;
struct sqlite3_stmt;
}
+class SQLStatement;
+
+namespace sync_pb {
+class EntitySpecifics;
+}
+
namespace syncable {
struct ColumnSpec;
@@ -68,10 +75,18 @@ class DirectoryBackingStore {
virtual bool SaveChanges(const Directory::SaveChangesSnapshot& snapshot);
private:
+ FRIEND_TEST(DirectoryBackingStoreTest, MigrateVersion67To68);
+ FRIEND_TEST(DirectoryBackingStoreTest, MigrateVersion68To69);
+ FRIEND_TEST(MigrationTest, ToCurrentVersion);
+
// General Directory initialization and load helpers.
DirOpenResult InitializeTables();
// Returns an sqlite return code, usually SQLITE_DONE.
int CreateTables();
+ // Create 'metas' or 'temp_metas' depending on value of is_temporary.
+ // Returns an sqlite return code, SQLITE_DONE on success.
+ int CreateMetasTable(bool is_temporary);
+
int CreateExtendedAttributeTable();
// We don't need to load any synced and applied deleted entries, we can
// in fact just purge them forever on startup.
@@ -98,6 +113,9 @@ class DirectoryBackingStore {
// said handle. Returns true on success, false if the sqlite open operation
// did not succeed.
bool OpenAndConfigureHandleHelper(sqlite3** handle) const;
+ // Initialize and destroy load_dbhandle_. Broken out for testing.
+ bool BeginLoad();
+ void EndLoad();
// Lazy creation of save_dbhandle_ for use by SaveChanges code path.
sqlite3* LazyGetSaveHandle();
@@ -105,6 +123,22 @@ class DirectoryBackingStore {
// Drop all tables in preparation for reinitialization.
void DropAllTables();
+ // Migration utilities.
+ bool AddColumn(const ColumnSpec* column);
+ bool RefreshColumns();
+ bool SetVersion(int version);
+ int GetVersion();
+ bool MigrateToSpecifics(const char* old_columns,
+ const char* specifics_column,
+ void (*handler_function)(
+ SQLStatement* old_value_query,
+ int old_value_column,
+ sync_pb::EntitySpecifics* mutable_new_value));
+
+ // Individual version migrations.
+ bool MigrateVersion67To68();
+ bool MigrateVersion68To69();
+
// The handle to our sqlite on-disk store for initialization and loading, and
// for saving changes periodically via SaveChanges, respectively.
// TODO(timsteele): We should only have one handle here. The reason we need
@@ -117,6 +151,10 @@ class DirectoryBackingStore {
std::string dir_name_;
FilePath backing_filepath_;
+ // Set to true if migration left some old columns around that need to be
+ // discarded.
+ bool needs_column_refresh_;
+
DISALLOW_COPY_AND_ASSIGN(DirectoryBackingStore);
};
diff --git a/chrome/browser/sync/syncable/directory_backing_store_unittest.cc b/chrome/browser/sync/syncable/directory_backing_store_unittest.cc
new file mode 100755
index 0000000..2bdded7
--- /dev/null
+++ b/chrome/browser/sync/syncable/directory_backing_store_unittest.cc
@@ -0,0 +1,523 @@
+// Copyright (c) 2010 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 "testing/gtest/include/gtest/gtest.h"
+
+#include <string>
+
+#include "app/sql/connection.h"
+#include "app/sql/statement.h"
+#include "app/sql/transaction.h"
+#include "base/file_path.h"
+#include "base/scoped_ptr.h"
+#include "base/scoped_temp_dir.h"
+#include "base/stl_util-inl.h"
+#include "chrome/browser/sync/protocol/bookmark_specifics.pb.h"
+#include "chrome/browser/sync/protocol/sync.pb.h"
+#include "chrome/browser/sync/syncable/directory_backing_store.h"
+#include "chrome/browser/sync/syncable/directory_manager.h"
+#include "chrome/browser/sync/syncable/syncable-inl.h"
+#include "chrome/browser/sync/syncable/syncable.h"
+#include "chrome/common/sqlite_utils.h"
+#include "testing/gtest/include/gtest/gtest-param-test.h"
+
+namespace syncable {
+
+extern const int32 kCurrentDBVersion;
+
+class MigrationTest : public testing::TestWithParam<int> {
+ public:
+ virtual void SetUp() {
+ ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+ }
+
+ protected:
+ std::string GetUsername() {
+ return "nick@chromium.org";
+ }
+
+ FilePath GetDatabasePath() {
+ return temp_dir_.path().Append(
+ DirectoryManager::GetSyncDataDatabaseFilename());
+ }
+ void SetUpVersion67Database();
+ void SetUpVersion68Database();
+
+ private:
+ ScopedTempDir temp_dir_;
+};
+
+class DirectoryBackingStoreTest : public MigrationTest {};
+
+void MigrationTest::SetUpVersion67Database() {
+ // This is a version 67 database dump whose contents were backformed from
+ // the contents of the version 68 database dump (the v68 migration was
+ // actually written first).
+ sql::Connection connection;
+ ASSERT_TRUE(connection.Open(GetDatabasePath()));
+ ASSERT_TRUE(connection.BeginTransaction());
+ ASSERT_TRUE(connection.Execute(
+ "CREATE TABLE extended_attributes(metahandle bigint, key varchar(127), "
+ "value blob, PRIMARY KEY(metahandle, key) ON CONFLICT REPLACE);"
+ "CREATE TABLE metas (metahandle bigint primary key ON CONFLICT FAIL,"
+ "base_version bigint default -1,server_version bigint default 0,"
+ "mtime bigint default 0,server_mtime bigint default 0,"
+ "ctime bigint default 0,server_ctime bigint default 0,"
+ "server_position_in_parent bigint default 0,"
+ "local_external_id bigint default 0,id varchar(255) default 'r',"
+ "parent_id varchar(255) default 'r',"
+ "server_parent_id varchar(255) default 'r',"
+ "prev_id varchar(255) default 'r',next_id varchar(255) default 'r',"
+ "is_unsynced bit default 0,is_unapplied_update bit default 0,"
+ "is_del bit default 0,is_dir bit default 0,"
+ "is_bookmark_object bit default 0,server_is_dir bit default 0,"
+ "server_is_del bit default 0,server_is_bookmark_object bit default 0,"
+ "name varchar(255), " /* COLLATE PATHNAME, */
+ "unsanitized_name varchar(255)," /* COLLATE PATHNAME, */
+ "non_unique_name varchar,"
+ "server_name varchar(255)," /* COLLATE PATHNAME */
+ "server_non_unique_name varchar,"
+ "bookmark_url varchar,server_bookmark_url varchar,"
+ "singleton_tag varchar,bookmark_favicon blob,"
+ "server_bookmark_favicon blob);"
+ "INSERT INTO metas VALUES(1,-1,0,129079956640320000,0,"
+ "129079956640320000,0,0,0,'r','r','r','r','r',0,0,0,1,0,0,0,0,NULL,"
+ "NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);"
+ "INSERT INTO metas VALUES(2,669,669,128976886618480000,"
+ "128976886618480000,128976886618480000,128976886618480000,-2097152,"
+ "4,'s_ID_2','s_ID_9','s_ID_9','s_ID_2','s_ID_2',0,0,1,0,1,0,1,1,"
+ "'Deleted Item',NULL,'Deleted Item','Deleted Item','Deleted Item',"
+ "'http://www.google.com/','http://www.google.com/2',NULL,'AASGASGA',"
+ "'ASADGADGADG');"
+ "INSERT INTO metas VALUES(4,681,681,129002163642690000,"
+ "129002163642690000,129002163642690000,129002163642690000,-3145728,"
+ "3,'s_ID_4','s_ID_9','s_ID_9','s_ID_4','s_ID_4',0,0,1,0,1,0,1,1,"
+ "'Welcome to Chromium',NULL,'Welcome to Chromium',"
+ "'Welcome to Chromium','Welcome to Chromium',"
+ "'http://www.google.com/chrome/intl/en/welcome.html',"
+ "'http://www.google.com/chrome/intl/en/welcome.html',NULL,NULL,"
+ "NULL);"
+ "INSERT INTO metas VALUES(5,677,677,129001555500000000,"
+ "129001555500000000,129001555500000000,129001555500000000,1048576,"
+ "7,'s_ID_5','s_ID_9','s_ID_9','s_ID_5','s_ID_5',0,0,1,0,1,0,1,1,"
+ "'Google',NULL,'Google','Google','Google','http://www.google.com/',"
+ "'http://www.google.com/',NULL,'AGASGASG','AGFDGASG');"
+ "INSERT INTO metas VALUES(6,694,694,129053976170000000,"
+ "129053976170000000,129053976170000000,129053976170000000,-4194304,"
+ "6,'s_ID_6','s_ID_9','s_ID_9','r','r',0,0,0,1,1,1,0,1,"
+ "'The Internet',NULL,'The Internet','The Internet',"
+ "'The Internet',NULL,NULL,NULL,NULL,NULL);"
+ "INSERT INTO metas VALUES(7,663,663,128976864758480000,"
+ "128976864758480000,128976864758480000,128976864758480000,"
+ "1048576,0,'s_ID_7','r','r','r','r',0,0,0,1,1,1,0,1,"
+ "'Google Chrome',NULL,'Google Chrome','Google Chrome',"
+ "'Google Chrome',NULL,NULL,'google_chrome',NULL,NULL);"
+ "INSERT INTO metas VALUES(8,664,664,128976864758480000,"
+ "128976864758480000,128976864758480000,128976864758480000,1048576,"
+ "0,'s_ID_8','s_ID_7','s_ID_7','r','r',0,0,0,1,1,1,0,1,'Bookmarks',"
+ "NULL,'Bookmarks','Bookmarks','Bookmarks',NULL,NULL,"
+ "'google_chrome_bookmarks',NULL,NULL);"
+ "INSERT INTO metas VALUES(9,665,665,128976864758480000,"
+ "128976864758480000,128976864758480000,128976864758480000,"
+ "1048576,1,'s_ID_9','s_ID_8','s_ID_8','r','s_ID_10',0,0,0,1,1,1,0,"
+ "1,'Bookmark Bar',NULL,'Bookmark Bar','Bookmark Bar','Bookmark Bar',"
+ "NULL,NULL,'bookmark_bar',NULL,NULL);"
+ "INSERT INTO metas VALUES(10,666,666,128976864758480000,"
+ "128976864758480000,128976864758480000,128976864758480000,2097152,"
+ "2,'s_ID_10','s_ID_8','s_ID_8','s_ID_9','r',0,0,0,1,1,1,0,1,"
+ "'Other Bookmarks',NULL,'Other Bookmarks','Other Bookmarks',"
+ "'Other Bookmarks',NULL,NULL,'other_bookmarks',"
+ "NULL,NULL);"
+ "INSERT INTO metas VALUES(11,683,683,129079956948440000,"
+ "129079956948440000,129079956948440000,129079956948440000,-1048576,"
+ "8,'s_ID_11','s_ID_6','s_ID_6','r','s_ID_13',0,0,0,0,1,0,0,1,"
+ "'Home (The Chromium Projects)',NULL,'Home (The Chromium Projects)',"
+ "'Home (The Chromium Projects)','Home (The Chromium Projects)',"
+ "'http://dev.chromium.org/','http://dev.chromium.org/other',NULL,"
+ "'AGATWA','AFAGVASF');"
+ "INSERT INTO metas VALUES(12,685,685,129079957513650000,"
+ "129079957513650000,129079957513650000,129079957513650000,0,9,"
+ "'s_ID_12','s_ID_6','s_ID_6','s_ID_13','s_ID_14',0,0,0,1,1,1,0,1,"
+ "'Extra Bookmarks',NULL,'Extra Bookmarks','Extra Bookmarks',"
+ "'Extra Bookmarks',NULL,NULL,NULL,NULL,NULL);"
+ "INSERT INTO metas VALUES(13,687,687,129079957985300000,"
+ "129079957985300000,129079957985300000,129079957985300000,-917504,"
+ "10,'s_ID_13','s_ID_6','s_ID_6','s_ID_11','s_ID_12',0,0,0,0,1,0,0,"
+ "1,'ICANN | Internet Corporation for Assigned Names and Numbers',"
+ "'ICANN Internet Corporation for Assigned Names and Numbers',"
+ "'ICANN | Internet Corporation for Assigned Names and Numbers',"
+ "'ICANN | Internet Corporation for Assigned Names and Numbers',"
+ "'ICANN | Internet Corporation for Assigned Names and Numbers',"
+ "'http://www.icann.com/','http://www.icann.com/',NULL,"
+ "'PNGAXF0AAFF','DAAFASF');"
+ "INSERT INTO metas VALUES(14,692,692,129079958383000000,"
+ "129079958383000000,129079958383000000,129079958383000000,1048576,"
+ "11,'s_ID_14','s_ID_6','s_ID_6','s_ID_12','r',0,0,0,0,1,0,0,1,"
+ "'The WebKit Open Source Project',NULL,"
+ "'The WebKit Open Source Project','The WebKit Open Source Project',"
+ "'The WebKit Open Source Project','http://webkit.org/',"
+ "'http://webkit.org/x',NULL,'PNGX','PNG2Y');"
+ "CREATE TABLE share_info (id VARCHAR(128) primary key, "
+ "last_sync_timestamp INT, name VARCHAR(128), "
+ "initial_sync_ended BIT default 0, store_birthday VARCHAR(256), "
+ "db_create_version VARCHAR(128), db_create_time int, "
+ "next_id bigint default -2, cache_guid VARCHAR(32));"
+ "INSERT INTO share_info VALUES('nick@chromium.org',694,"
+ "'nick@chromium.org',1,'c27e9f59-08ca-46f8-b0cc-f16a2ed778bb',"
+ "'Unknown',1263522064,-65542,"
+ "'9010788312004066376x-6609234393368420856x');"
+ "CREATE TABLE share_version (id VARCHAR(128) primary key, data INT);"
+ "INSERT INTO share_version VALUES('nick@chromium.org',68);"));
+ ASSERT_TRUE(connection.CommitTransaction());
+}
+
+void MigrationTest::SetUpVersion68Database() {
+ // This sets up an actual version 68 database dump. The IDs were
+ // canonicalized to be less huge, and the favicons were overwritten
+ // with random junk so that they didn't contain any unprintable
+ // characters. A few server URLs were tweaked so that they'd be
+ // different from the local URLs. Lastly, the custom collation on
+ // the server_non_unique_name column was removed.
+ sql::Connection connection;
+ ASSERT_TRUE(connection.Open(GetDatabasePath()));
+ ASSERT_TRUE(connection.BeginTransaction());
+ ASSERT_TRUE(connection.Execute(
+ "CREATE TABLE extended_attributes(metahandle bigint, key varchar(127), "
+ "value blob, PRIMARY KEY(metahandle, key) ON CONFLICT REPLACE);"
+ "CREATE TABLE metas (metahandle bigint primary key ON CONFLICT FAIL,"
+ "base_version bigint default -1,server_version bigint default 0,"
+ "mtime bigint default 0,server_mtime bigint default 0,"
+ "ctime bigint default 0,server_ctime bigint default 0,"
+ "server_position_in_parent bigint default 0,"
+ "local_external_id bigint default 0,id varchar(255) default 'r',"
+ "parent_id varchar(255) default 'r',"
+ "server_parent_id varchar(255) default 'r',"
+ "prev_id varchar(255) default 'r',next_id varchar(255) default 'r',"
+ "is_unsynced bit default 0,is_unapplied_update bit default 0,"
+ "is_del bit default 0,is_dir bit default 0,"
+ "is_bookmark_object bit default 0,server_is_dir bit default 0,"
+ "server_is_del bit default 0,"
+ "server_is_bookmark_object bit default 0,"
+ "non_unique_name varchar,server_non_unique_name varchar(255),"
+ "bookmark_url varchar,server_bookmark_url varchar,"
+ "singleton_tag varchar,bookmark_favicon blob,"
+ "server_bookmark_favicon blob);"
+ "INSERT INTO metas VALUES(1,-1,0,129079956640320000,0,"
+ "129079956640320000,0,0,0,'r','r','r','r','r',0,0,0,1,0,0,0,0,NULL,"
+ "NULL,NULL,NULL,NULL,NULL,NULL);"
+ "INSERT INTO metas VALUES(2,669,669,128976886618480000,"
+ "128976886618480000,128976886618480000,128976886618480000,-2097152,"
+ "4,'s_ID_2','s_ID_9','s_ID_9','s_ID_2','s_ID_2',0,0,1,0,1,0,1,1,"
+ "'Deleted Item','Deleted Item','http://www.google.com/',"
+ "'http://www.google.com/2',NULL,'AASGASGA','ASADGADGADG');"
+ "INSERT INTO metas VALUES(4,681,681,129002163642690000,"
+ "129002163642690000,129002163642690000,129002163642690000,-3145728,"
+ "3,'s_ID_4','s_ID_9','s_ID_9','s_ID_4','s_ID_4',0,0,1,0,1,0,1,1,"
+ "'Welcome to Chromium','Welcome to Chromium',"
+ "'http://www.google.com/chrome/intl/en/welcome.html',"
+ "'http://www.google.com/chrome/intl/en/welcome.html',NULL,NULL,"
+ "NULL);"
+ "INSERT INTO metas VALUES(5,677,677,129001555500000000,"
+ "129001555500000000,129001555500000000,129001555500000000,1048576,"
+ "7,'s_ID_5','s_ID_9','s_ID_9','s_ID_5','s_ID_5',0,0,1,0,1,0,1,1,"
+ "'Google','Google','http://www.google.com/',"
+ "'http://www.google.com/',NULL,'AGASGASG','AGFDGASG');"
+ "INSERT INTO metas VALUES(6,694,694,129053976170000000,"
+ "129053976170000000,129053976170000000,129053976170000000,-4194304,"
+ "6,'s_ID_6','s_ID_9','s_ID_9','r','r',0,0,0,1,1,1,0,1,"
+ "'The Internet','The Internet',NULL,NULL,NULL,NULL,NULL);"
+ "INSERT INTO metas VALUES(7,663,663,128976864758480000,"
+ "128976864758480000,128976864758480000,128976864758480000,"
+ "1048576,0,'s_ID_7','r','r','r','r',0,0,0,1,1,1,0,1,"
+ "'Google Chrome','Google Chrome',NULL,NULL,'google_chrome',NULL,"
+ "NULL);"
+ "INSERT INTO metas VALUES(8,664,664,128976864758480000,"
+ "128976864758480000,128976864758480000,128976864758480000,1048576,"
+ "0,'s_ID_8','s_ID_7','s_ID_7','r','r',0,0,0,1,1,1,0,1,'Bookmarks',"
+ "'Bookmarks',NULL,NULL,'google_chrome_bookmarks',NULL,NULL);"
+ "INSERT INTO metas VALUES(9,665,665,128976864758480000,"
+ "128976864758480000,128976864758480000,128976864758480000,"
+ "1048576,1,'s_ID_9','s_ID_8','s_ID_8','r','s_ID_10',0,0,0,1,1,1,0,"
+ "1,'Bookmark Bar','Bookmark Bar',NULL,NULL,'bookmark_bar',NULL,"
+ "NULL);"
+ "INSERT INTO metas VALUES(10,666,666,128976864758480000,"
+ "128976864758480000,128976864758480000,128976864758480000,2097152,"
+ "2,'s_ID_10','s_ID_8','s_ID_8','s_ID_9','r',0,0,0,1,1,1,0,1,"
+ "'Other Bookmarks','Other Bookmarks',NULL,NULL,'other_bookmarks',"
+ "NULL,NULL);"
+ "INSERT INTO metas VALUES(11,683,683,129079956948440000,"
+ "129079956948440000,129079956948440000,129079956948440000,-1048576,"
+ "8,'s_ID_11','s_ID_6','s_ID_6','r','s_ID_13',0,0,0,0,1,0,0,1,"
+ "'Home (The Chromium Projects)','Home (The Chromium Projects)',"
+ "'http://dev.chromium.org/','http://dev.chromium.org/other',NULL,"
+ "'AGATWA','AFAGVASF');"
+ "INSERT INTO metas VALUES(12,685,685,129079957513650000,"
+ "129079957513650000,129079957513650000,129079957513650000,0,9,"
+ "'s_ID_12','s_ID_6','s_ID_6','s_ID_13','s_ID_14',0,0,0,1,1,1,0,1,"
+ "'Extra Bookmarks','Extra Bookmarks',NULL,NULL,NULL,NULL,NULL);"
+ "INSERT INTO metas VALUES(13,687,687,129079957985300000,"
+ "129079957985300000,129079957985300000,129079957985300000,-917504,"
+ "10,'s_ID_13','s_ID_6','s_ID_6','s_ID_11','s_ID_12',0,0,0,0,1,0,0,"
+ "1,'ICANN | Internet Corporation for Assigned Names and Numbers',"
+ "'ICANN | Internet Corporation for Assigned Names and Numbers',"
+ "'http://www.icann.com/','http://www.icann.com/',NULL,"
+ "'PNGAXF0AAFF','DAAFASF');"
+ "INSERT INTO metas VALUES(14,692,692,129079958383000000,"
+ "129079958383000000,129079958383000000,129079958383000000,1048576,"
+ "11,'s_ID_14','s_ID_6','s_ID_6','s_ID_12','r',0,0,0,0,1,0,0,1,"
+ "'The WebKit Open Source Project','The WebKit Open Source Project',"
+ "'http://webkit.org/','http://webkit.org/x',NULL,'PNGX','PNG2Y');"
+ "CREATE TABLE share_info (id VARCHAR(128) primary key, "
+ "last_sync_timestamp INT, name VARCHAR(128), "
+ "initial_sync_ended BIT default 0, store_birthday VARCHAR(256), "
+ "db_create_version VARCHAR(128), db_create_time int, "
+ "next_id bigint default -2, cache_guid VARCHAR(32));"
+ "INSERT INTO share_info VALUES('nick@chromium.org',694,"
+ "'nick@chromium.org',1,'c27e9f59-08ca-46f8-b0cc-f16a2ed778bb',"
+ "'Unknown',1263522064,-65542,"
+ "'9010788312004066376x-6609234393368420856x');"
+ "CREATE TABLE share_version (id VARCHAR(128) primary key, data INT);"
+ "INSERT INTO share_version VALUES('nick@chromium.org',68);"));
+ ASSERT_TRUE(connection.CommitTransaction());
+}
+
+TEST_F(DirectoryBackingStoreTest, MigrateVersion67To68) {
+ SetUpVersion67Database();
+
+ {
+ sql::Connection connection;
+ ASSERT_TRUE(connection.Open(GetDatabasePath()));
+
+ // Columns existing befor version 67.
+ ASSERT_TRUE(connection.DoesColumnExist("metas", "name"));
+ ASSERT_TRUE(connection.DoesColumnExist("metas", "unsanitized_name"));
+ ASSERT_TRUE(connection.DoesColumnExist("metas", "server_name"));
+ }
+
+ scoped_ptr<DirectoryBackingStore> dbs(
+ new DirectoryBackingStore(GetUsername(), GetDatabasePath()));
+
+ dbs->BeginLoad();
+ ASSERT_FALSE(dbs->needs_column_refresh_);
+ ASSERT_TRUE(dbs->MigrateVersion67To68());
+ ASSERT_EQ(68, dbs->GetVersion());
+ dbs->EndLoad();
+ ASSERT_TRUE(dbs->needs_column_refresh_);
+}
+
+TEST_F(DirectoryBackingStoreTest, MigrateVersion68To69) {
+ SetUpVersion68Database();
+
+ scoped_ptr<DirectoryBackingStore> dbs(
+ new DirectoryBackingStore(GetUsername(), GetDatabasePath()));
+
+ dbs->BeginLoad();
+ ASSERT_FALSE(dbs->needs_column_refresh_);
+ ASSERT_TRUE(dbs->MigrateVersion68To69());
+ ASSERT_EQ(69, dbs->GetVersion());
+ dbs->EndLoad();
+ ASSERT_TRUE(dbs->needs_column_refresh_);
+
+ sql::Connection connection;
+ ASSERT_TRUE(connection.Open(GetDatabasePath()));
+ ASSERT_TRUE(connection.DoesColumnExist("metas", "specifics"));
+ ASSERT_TRUE(connection.DoesColumnExist("metas", "server_specifics"));
+ sql::Statement s(connection.GetUniqueStatement("SELECT non_unique_name,"
+ "is_del, is_dir, id, specifics, server_specifics FROM metas "
+ "WHERE metahandle = 2"));
+ ASSERT_TRUE(s.Step());
+ ASSERT_EQ("Deleted Item", s.ColumnString(0));
+ ASSERT_TRUE(s.ColumnBool(1));
+ ASSERT_FALSE(s.ColumnBool(2));
+ ASSERT_EQ("s_ID_2", s.ColumnString(3));
+ sync_pb::EntitySpecifics specifics;
+ specifics.ParseFromArray(s.ColumnBlob(4), s.ColumnByteLength(4));
+ ASSERT_TRUE(specifics.HasExtension(sync_pb::bookmark));
+ ASSERT_EQ("http://www.google.com/",
+ specifics.GetExtension(sync_pb::bookmark).url());
+ ASSERT_EQ("AASGASGA", specifics.GetExtension(sync_pb::bookmark).favicon());
+ specifics.ParseFromArray(s.ColumnBlob(5), s.ColumnByteLength(5));
+ ASSERT_TRUE(specifics.HasExtension(sync_pb::bookmark));
+ ASSERT_EQ("http://www.google.com/2",
+ specifics.GetExtension(sync_pb::bookmark).url());
+ ASSERT_EQ("ASADGADGADG", specifics.GetExtension(sync_pb::bookmark).favicon());
+ ASSERT_FALSE(s.Step());
+}
+
+TEST_P(MigrationTest, ToCurrentVersion) {
+ switch (GetParam()) {
+ case 67:
+ SetUpVersion67Database();
+ break;
+ case 68:
+ SetUpVersion68Database();
+ break;
+ default:
+ FAIL() << "Need to supply database dump for version " << GetParam();
+ }
+
+ scoped_ptr<DirectoryBackingStore> dbs(
+ new DirectoryBackingStore(GetUsername(), GetDatabasePath()));
+
+ dbs->BeginLoad();
+ ASSERT_TRUE(OPENED == dbs->InitializeTables());
+ ASSERT_FALSE(dbs->needs_column_refresh_);
+ ASSERT_EQ(kCurrentDBVersion, dbs->GetVersion());
+
+ {
+ sql::Connection connection;
+ ASSERT_TRUE(connection.Open(GetDatabasePath()));
+
+ // Columns deleted in Version 67.
+ ASSERT_FALSE(connection.DoesColumnExist("metas", "name"));
+ ASSERT_FALSE(connection.DoesColumnExist("metas", "unsanitized_name"));
+ ASSERT_FALSE(connection.DoesColumnExist("metas", "server_name"));
+
+ // Columns added in Version 68.
+ ASSERT_TRUE(connection.DoesColumnExist("metas", "specifics"));
+ ASSERT_TRUE(connection.DoesColumnExist("metas", "server_specifics"));
+
+ // Columns deleted in Version 68.
+ ASSERT_FALSE(connection.DoesColumnExist("metas", "is_bookmark_object"));
+ ASSERT_FALSE(connection.DoesColumnExist("metas",
+ "server_is_bookmark_object"));
+ ASSERT_FALSE(connection.DoesColumnExist("metas", "bookmark_favicon"));
+ ASSERT_FALSE(connection.DoesColumnExist("metas", "bookmark_url"));
+ ASSERT_FALSE(connection.DoesColumnExist("metas", "server_bookmark_url"));
+ }
+
+ MetahandlesIndex index;
+ dbs->LoadEntries(&index);
+ dbs->EndLoad();
+
+ MetahandlesIndex::iterator it = index.begin();
+ ASSERT_TRUE(it != index.end());
+ ASSERT_EQ(1, (*it)->ref(META_HANDLE));
+ EXPECT_TRUE((*it)->ref(ID).IsRoot());
+
+ ASSERT_TRUE(++it != index.end()) << "Upgrade destroyed database contents.";
+ ASSERT_EQ(2, (*it)->ref(META_HANDLE));
+ EXPECT_TRUE((*it)->ref(IS_DEL));
+ EXPECT_TRUE((*it)->ref(SERVER_IS_DEL));
+ EXPECT_TRUE((*it)->ref(SPECIFICS).HasExtension(sync_pb::bookmark));
+ EXPECT_TRUE((*it)->ref(SERVER_SPECIFICS).HasExtension(sync_pb::bookmark));
+ EXPECT_EQ("http://www.google.com/",
+ (*it)->ref(SPECIFICS).GetExtension(sync_pb::bookmark).url());
+ EXPECT_EQ("AASGASGA",
+ (*it)->ref(SPECIFICS).GetExtension(sync_pb::bookmark).favicon());
+ EXPECT_EQ("http://www.google.com/2",
+ (*it)->ref(SERVER_SPECIFICS).GetExtension(sync_pb::bookmark).url());
+ EXPECT_EQ("ASADGADGADG",
+ (*it)->ref(SERVER_SPECIFICS).GetExtension(sync_pb::bookmark).favicon());
+ EXPECT_EQ("", (*it)->ref(SINGLETON_TAG));
+ EXPECT_EQ("Deleted Item", (*it)->ref(NON_UNIQUE_NAME));
+ EXPECT_EQ("Deleted Item", (*it)->ref(SERVER_NON_UNIQUE_NAME));
+
+ ASSERT_TRUE(++it != index.end());
+ ASSERT_EQ(4, (*it)->ref(META_HANDLE));
+ EXPECT_TRUE((*it)->ref(IS_DEL));
+ EXPECT_TRUE((*it)->ref(SERVER_IS_DEL));
+
+ ASSERT_TRUE(++it != index.end());
+ ASSERT_EQ(5, (*it)->ref(META_HANDLE));
+ EXPECT_TRUE((*it)->ref(IS_DEL));
+ EXPECT_TRUE((*it)->ref(SERVER_IS_DEL));
+
+ ASSERT_TRUE(++it != index.end());
+ ASSERT_EQ(6, (*it)->ref(META_HANDLE));
+ EXPECT_TRUE((*it)->ref(IS_DIR));
+ EXPECT_TRUE((*it)->ref(SERVER_IS_DIR));
+ EXPECT_FALSE(
+ (*it)->ref(SPECIFICS).GetExtension(sync_pb::bookmark).has_url());
+ EXPECT_FALSE(
+ (*it)->ref(SERVER_SPECIFICS).GetExtension(sync_pb::bookmark).has_url());
+ EXPECT_FALSE(
+ (*it)->ref(SPECIFICS).GetExtension(sync_pb::bookmark).has_favicon());
+ EXPECT_FALSE((*it)->ref(SERVER_SPECIFICS).
+ GetExtension(sync_pb::bookmark).has_favicon());
+
+ ASSERT_TRUE(++it != index.end());
+ ASSERT_EQ(7, (*it)->ref(META_HANDLE));
+ EXPECT_EQ("google_chrome", (*it)->ref(SINGLETON_TAG));
+ EXPECT_FALSE((*it)->ref(SPECIFICS).HasExtension(sync_pb::bookmark));
+ EXPECT_FALSE((*it)->ref(SERVER_SPECIFICS).HasExtension(sync_pb::bookmark));
+
+ ASSERT_TRUE(++it != index.end());
+ ASSERT_EQ(8, (*it)->ref(META_HANDLE));
+ EXPECT_EQ("google_chrome_bookmarks", (*it)->ref(SINGLETON_TAG));
+ EXPECT_TRUE((*it)->ref(SPECIFICS).HasExtension(sync_pb::bookmark));
+ EXPECT_TRUE((*it)->ref(SERVER_SPECIFICS).HasExtension(sync_pb::bookmark));
+
+ ASSERT_TRUE(++it != index.end());
+ ASSERT_EQ(9, (*it)->ref(META_HANDLE));
+ EXPECT_EQ("bookmark_bar", (*it)->ref(SINGLETON_TAG));
+ EXPECT_TRUE((*it)->ref(SPECIFICS).HasExtension(sync_pb::bookmark));
+ EXPECT_TRUE((*it)->ref(SERVER_SPECIFICS).HasExtension(sync_pb::bookmark));
+
+ ASSERT_TRUE(++it != index.end());
+ ASSERT_EQ(10, (*it)->ref(META_HANDLE));
+ EXPECT_FALSE((*it)->ref(IS_DEL));
+ EXPECT_TRUE((*it)->ref(SPECIFICS).HasExtension(sync_pb::bookmark));
+ EXPECT_TRUE((*it)->ref(SERVER_SPECIFICS).HasExtension(sync_pb::bookmark));
+ EXPECT_FALSE((*it)->ref(SPECIFICS).GetExtension(sync_pb::bookmark).has_url());
+ EXPECT_FALSE(
+ (*it)->ref(SPECIFICS).GetExtension(sync_pb::bookmark).has_favicon());
+ EXPECT_FALSE(
+ (*it)->ref(SERVER_SPECIFICS).GetExtension(sync_pb::bookmark).has_url());
+ EXPECT_FALSE((*it)->ref(SERVER_SPECIFICS).
+ GetExtension(sync_pb::bookmark).has_favicon());
+ EXPECT_EQ("other_bookmarks", (*it)->ref(SINGLETON_TAG));
+ EXPECT_EQ("Other Bookmarks", (*it)->ref(NON_UNIQUE_NAME));
+ EXPECT_EQ("Other Bookmarks", (*it)->ref(SERVER_NON_UNIQUE_NAME));
+
+ ASSERT_TRUE(++it != index.end());
+ ASSERT_EQ(11, (*it)->ref(META_HANDLE));
+ EXPECT_FALSE((*it)->ref(IS_DEL));
+ EXPECT_FALSE((*it)->ref(IS_DIR));
+ EXPECT_TRUE((*it)->ref(SPECIFICS).HasExtension(sync_pb::bookmark));
+ EXPECT_TRUE((*it)->ref(SERVER_SPECIFICS).HasExtension(sync_pb::bookmark));
+ EXPECT_EQ("http://dev.chromium.org/",
+ (*it)->ref(SPECIFICS).GetExtension(sync_pb::bookmark).url());
+ EXPECT_EQ("AGATWA",
+ (*it)->ref(SPECIFICS).GetExtension(sync_pb::bookmark).favicon());
+ EXPECT_EQ("http://dev.chromium.org/other",
+ (*it)->ref(SERVER_SPECIFICS).GetExtension(sync_pb::bookmark).url());
+ EXPECT_EQ("AFAGVASF",
+ (*it)->ref(SERVER_SPECIFICS).GetExtension(sync_pb::bookmark).favicon());
+ EXPECT_EQ("", (*it)->ref(SINGLETON_TAG));
+ EXPECT_EQ("Home (The Chromium Projects)", (*it)->ref(NON_UNIQUE_NAME));
+ EXPECT_EQ("Home (The Chromium Projects)", (*it)->ref(SERVER_NON_UNIQUE_NAME));
+
+ ASSERT_TRUE(++it != index.end());
+ ASSERT_EQ(12, (*it)->ref(META_HANDLE));
+ EXPECT_FALSE((*it)->ref(IS_DEL));
+ EXPECT_TRUE((*it)->ref(IS_DIR));
+ EXPECT_EQ("Extra Bookmarks", (*it)->ref(NON_UNIQUE_NAME));
+ EXPECT_EQ("Extra Bookmarks", (*it)->ref(SERVER_NON_UNIQUE_NAME));
+ EXPECT_TRUE((*it)->ref(SPECIFICS).HasExtension(sync_pb::bookmark));
+ EXPECT_TRUE((*it)->ref(SERVER_SPECIFICS).HasExtension(sync_pb::bookmark));
+ EXPECT_FALSE(
+ (*it)->ref(SPECIFICS).GetExtension(sync_pb::bookmark).has_url());
+ EXPECT_FALSE(
+ (*it)->ref(SERVER_SPECIFICS).GetExtension(sync_pb::bookmark).has_url());
+ EXPECT_FALSE(
+ (*it)->ref(SPECIFICS).GetExtension(sync_pb::bookmark).has_favicon());
+ EXPECT_FALSE((*it)->ref(SERVER_SPECIFICS).
+ GetExtension(sync_pb::bookmark).has_favicon());
+
+ ASSERT_TRUE(++it != index.end());
+ ASSERT_EQ(13, (*it)->ref(META_HANDLE));
+
+ ASSERT_TRUE(++it != index.end());
+ ASSERT_EQ(14, (*it)->ref(META_HANDLE));
+
+ ASSERT_TRUE(++it == index.end());
+
+ STLDeleteElements(&index);
+}
+
+INSTANTIATE_TEST_CASE_P(DirectoryBackingStore, MigrationTest,
+ testing::Range(67, kCurrentDBVersion));
+
+} // namespace syncable
diff --git a/chrome/browser/sync/syncable/model_type.h b/chrome/browser/sync/syncable/model_type.h
index 7bd9826..cc00f4c4 100644
--- a/chrome/browser/sync/syncable/model_type.h
+++ b/chrome/browser/sync/syncable/model_type.h
@@ -2,6 +2,10 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+// Enumerate the various item subtypes that are supported by sync.
+// Each sync object is expected to have an immutable object type.
+// An object's type is inferred from the type of data it holds.
+
#ifndef CHROME_BROWSER_SYNC_SYNCABLE_MODEL_TYPE_H_
#define CHROME_BROWSER_SYNC_SYNCABLE_MODEL_TYPE_H_
@@ -10,9 +14,14 @@
namespace syncable {
enum ModelType {
- UNSPECIFIED = 0,
- BOOKMARKS,
- MODEL_TYPE_COUNT
+ UNSPECIFIED, // Object type unknown. Objects may transition through
+ // the unknown state during their initial creation, before
+ // their properties are set. After deletion, object types
+ // are generally preserved.
+ TOP_LEVEL_FOLDER, // A permanent folder whose children may be of mixed
+ // datatypes (e.g. the "Google Chrome" folder).
+ BOOKMARKS, // A bookmark folder or a bookmark URL object.
+ MODEL_TYPE_COUNT,
};
inline ModelType ModelTypeFromInt(int i) {
@@ -24,4 +33,3 @@ inline ModelType ModelTypeFromInt(int i) {
} // namespace syncable
#endif // CHROME_BROWSER_SYNC_SYNCABLE_MODEL_TYPE_H_
-
diff --git a/chrome/browser/sync/syncable/syncable.cc b/chrome/browser/sync/syncable/syncable.cc
index 6816f9c..b8c66b7 100755
--- a/chrome/browser/sync/syncable/syncable.cc
+++ b/chrome/browser/sync/syncable/syncable.cc
@@ -35,6 +35,7 @@
#include "base/time.h"
#include "chrome/browser/sync/engine/syncer.h"
#include "chrome/browser/sync/engine/syncer_util.h"
+#include "chrome/browser/sync/protocol/bookmark_specifics.pb.h"
#include "chrome/browser/sync/protocol/service_constants.h"
#include "chrome/browser/sync/syncable/directory_backing_store.h"
#include "chrome/browser/sync/syncable/directory_manager.h"
@@ -396,8 +397,8 @@ void ZeroFields(EntryKernel* entry, int first_field) {
entry->mutable_ref(static_cast<IdField>(i)).Clear();
for ( ; i < BIT_FIELDS_END; ++i)
entry->put(static_cast<BitField>(i), false);
- if (i < BLOB_FIELDS_END)
- i = BLOB_FIELDS_END;
+ if (i < PROTO_FIELDS_END)
+ i = PROTO_FIELDS_END;
}
void Directory::InsertEntry(EntryKernel* entry) {
@@ -998,6 +999,51 @@ void Entry::DeleteAllExtendedAttributes(WriteTransaction *trans) {
dir()->DeleteAllExtendedAttributes(trans, kernel_->ref(META_HANDLE));
}
+syncable::ModelType Entry::GetServerModelType() const {
+ if (Get(SERVER_SPECIFICS).HasExtension(sync_pb::bookmark))
+ return BOOKMARKS;
+ if (IsRoot())
+ return TOP_LEVEL_FOLDER;
+ // Loose check for server-created top-level folders that aren't
+ // bound to a particular model type.
+ if (!Get(SINGLETON_TAG).empty() && Get(SERVER_IS_DIR))
+ return TOP_LEVEL_FOLDER;
+
+ // Otherwise, we don't have a server type yet. That should only happen
+ // if the item is an uncommitted locally created item.
+ // It's possible we'll need to relax these checks in the future; they're
+ // just here for now as a safety measure.
+ DCHECK(Get(IS_UNSYNCED));
+ DCHECK(Get(SERVER_VERSION) == 0);
+ DCHECK(Get(SERVER_IS_DEL));
+ // Note: can't enforce !Get(ID).ServerKnows() here because that could
+ // actually happen if we hit AttemptReuniteLostCommitResponses.
+ return UNSPECIFIED;
+}
+
+// Note: keep this consistent with GetModelType in syncproto.h!
+syncable::ModelType Entry::GetModelType() const {
+ if (Get(SPECIFICS).HasExtension(sync_pb::bookmark))
+ return BOOKMARKS;
+ if (IsRoot())
+ return TOP_LEVEL_FOLDER;
+ // Loose check for server-created top-level folders that aren't
+ // bound to a particular model type.
+ if (!Get(SINGLETON_TAG).empty() && Get(IS_DIR))
+ return TOP_LEVEL_FOLDER;
+
+ // Otherwise, we don't have a local type yet. That should only happen
+ // if the item doesn't exist locally -- like if it's a new update item.
+ // It's possible we'll need to relax these checks in the future; they're
+ // just here for now as a safety measure.
+ DCHECK(Get(ID).ServerKnows());
+ DCHECK(Get(IS_UNAPPLIED_UPDATE));
+ DCHECK(!Get(IS_UNSYNCED));
+ DCHECK(Get(BASE_VERSION) == CHANGES_VERSION);
+ DCHECK(Get(IS_DEL));
+ return UNSPECIFIED;
+}
+
///////////////////////////////////////////////////////////////////////////
// MutableEntry
@@ -1176,9 +1222,6 @@ void MutableEntry::UnlinkFromOrder() {
}
bool MutableEntry::PutPredecessor(const Id& predecessor_id) {
- // TODO(ncarter): Maybe there should be an independent HAS_POSITION bit?
- if (!Get(IS_BOOKMARK_OBJECT))
- return true;
UnlinkFromOrder();
if (Get(IS_DEL)) {
@@ -1186,6 +1229,15 @@ bool MutableEntry::PutPredecessor(const Id& predecessor_id) {
return true;
}
+ // TODO(ncarter): It should be possible to not maintain position for
+ // non-bookmark items. However, we'd need to robustly handle all possible
+ // permutations of setting IS_DEL and the SPECIFICS to identify the
+ // object type; or else, we'd need to add a ModelType to the
+ // MutableEntry's Create ctor.
+ // if (!ShouldMaintainPosition()) {
+ // return false;
+ // }
+
// This is classic insert-into-doubly-linked-list from CS 101 and your last
// job interview. An "IsRoot" Id signifies the head or tail.
Id successor_id;
@@ -1356,22 +1408,22 @@ inline FastDump& operator<<(FastDump& dump, const DumpColon&) {
std::ostream& operator<<(std::ostream& stream, const syncable::Entry& entry) {
// Using ostreams directly here is dreadfully slow, because a mutex is
// acquired for every <<. Users noticed it spiking CPU.
- using syncable::BitField;
- using syncable::BitTemp;
- using syncable::BlobField;
- using syncable::EntryKernel;
- using syncable::g_metas_columns;
- using syncable::IdField;
- using syncable::Int64Field;
- using syncable::StringField;
using syncable::BEGIN_FIELDS;
using syncable::BIT_FIELDS_END;
using syncable::BIT_TEMPS_BEGIN;
using syncable::BIT_TEMPS_END;
- using syncable::BLOB_FIELDS_END;
- using syncable::INT64_FIELDS_END;
+ using syncable::BitField;
+ using syncable::BitTemp;
+ using syncable::EntryKernel;
using syncable::ID_FIELDS_END;
+ using syncable::INT64_FIELDS_END;
+ using syncable::IdField;
+ using syncable::Int64Field;
+ using syncable::PROTO_FIELDS_END;
+ using syncable::ProtoField;
using syncable::STRING_FIELDS_END;
+ using syncable::StringField;
+ using syncable::g_metas_columns;
int i;
FastDump s(&stream);
@@ -1393,9 +1445,10 @@ std::ostream& operator<<(std::ostream& stream, const syncable::Entry& entry) {
const string& field = kernel->ref(static_cast<StringField>(i));
s << g_metas_columns[i].name << colon << field << separator;
}
- for ( ; i < BLOB_FIELDS_END; ++i) {
+ for ( ; i < PROTO_FIELDS_END; ++i) {
s << g_metas_columns[i].name << colon
- << kernel->ref(static_cast<BlobField>(i)) << separator;
+ << kernel->ref(static_cast<ProtoField>(i)).SerializeAsString()
+ << separator;
}
s << "TempFlags: ";
for ( ; i < BIT_TEMPS_END; ++i) {
diff --git a/chrome/browser/sync/syncable/syncable.h b/chrome/browser/sync/syncable/syncable.h
index 05b3052..0fe199e 100755
--- a/chrome/browser/sync/syncable/syncable.h
+++ b/chrome/browser/sync/syncable/syncable.h
@@ -24,6 +24,7 @@
#include "chrome/browser/sync/syncable/directory_event.h"
#include "chrome/browser/sync/syncable/path_name_cmp.h"
#include "chrome/browser/sync/syncable/syncable_id.h"
+#include "chrome/browser/sync/syncable/model_type.h"
#include "chrome/browser/sync/util/dbgq.h"
#include "chrome/browser/sync/util/event_sys.h"
#include "chrome/browser/sync/util/row_iterator.h"
@@ -116,12 +117,8 @@ enum IsDelField {
enum BitField {
IS_DIR = IS_DEL + 1,
- IS_BOOKMARK_OBJECT,
-
SERVER_IS_DIR,
SERVER_IS_DEL,
- SERVER_IS_BOOKMARK_OBJECT,
-
BIT_FIELDS_END
};
@@ -135,9 +132,6 @@ enum StringField {
NON_UNIQUE_NAME = STRING_FIELDS_BEGIN,
// The server version of |NON_UNIQUE_NAME|.
SERVER_NON_UNIQUE_NAME,
- // For bookmark entries, the URL of the bookmark.
- BOOKMARK_URL,
- SERVER_BOOKMARK_URL,
// A tag string which identifies this node as a particular top-level
// permanent object. The tag can be thought of as a unique key that
@@ -148,27 +142,25 @@ enum StringField {
enum {
STRING_FIELDS_COUNT = STRING_FIELDS_END - STRING_FIELDS_BEGIN,
- BLOB_FIELDS_BEGIN = STRING_FIELDS_END
+ PROTO_FIELDS_BEGIN = STRING_FIELDS_END
};
// From looking at the sqlite3 docs, it's not directly stated, but it
// seems the overhead for storing a NULL blob is very small.
-enum BlobField {
- // For bookmark entries, the favicon data. These will be NULL for
- // non-bookmark items.
- BOOKMARK_FAVICON = BLOB_FIELDS_BEGIN,
- SERVER_BOOKMARK_FAVICON,
- BLOB_FIELDS_END,
+enum ProtoField {
+ SPECIFICS = PROTO_FIELDS_BEGIN,
+ SERVER_SPECIFICS,
+ PROTO_FIELDS_END,
};
enum {
- BLOB_FIELDS_COUNT = BLOB_FIELDS_END - BLOB_FIELDS_BEGIN
+ PROTO_FIELDS_COUNT = PROTO_FIELDS_END - PROTO_FIELDS_BEGIN
};
enum {
- FIELD_COUNT = BLOB_FIELDS_END,
+ FIELD_COUNT = PROTO_FIELDS_END,
// Past this point we have temporaries, stored in memory only.
- BEGIN_TEMPS = BLOB_FIELDS_END,
+ BEGIN_TEMPS = PROTO_FIELDS_END,
BIT_TEMPS_BEGIN = BEGIN_TEMPS,
};
@@ -225,7 +217,7 @@ typedef std::set<std::string> AttributeKeySet;
struct EntryKernel {
private:
std::string string_fields[STRING_FIELDS_COUNT];
- Blob blob_fields[BLOB_FIELDS_COUNT];
+ sync_pb::EntitySpecifics specifics_fields[PROTO_FIELDS_COUNT];
int64 int64_fields[INT64_FIELDS_COUNT];
Id id_fields[ID_FIELDS_COUNT];
std::bitset<BIT_FIELDS_COUNT> bit_fields;
@@ -269,8 +261,8 @@ struct EntryKernel {
inline void put(StringField field, const std::string& value) {
string_fields[field - STRING_FIELDS_BEGIN] = value;
}
- inline void put(BlobField field, const Blob& value) {
- blob_fields[field - BLOB_FIELDS_BEGIN] = value;
+ inline void put(ProtoField field, const sync_pb::EntitySpecifics& value) {
+ specifics_fields[field - PROTO_FIELDS_BEGIN].CopyFrom(value);
}
inline void put(BitTemp field, bool value) {
bit_temps[field - BIT_TEMPS_BEGIN] = value;
@@ -301,8 +293,8 @@ struct EntryKernel {
inline const std::string& ref(StringField field) const {
return string_fields[field - STRING_FIELDS_BEGIN];
}
- inline const Blob& ref(BlobField field) const {
- return blob_fields[field - BLOB_FIELDS_BEGIN];
+ inline const sync_pb::EntitySpecifics& ref(ProtoField field) const {
+ return specifics_fields[field - PROTO_FIELDS_BEGIN];
}
inline bool ref(BitTemp field) const {
return bit_temps[field - BIT_TEMPS_BEGIN];
@@ -312,8 +304,8 @@ struct EntryKernel {
inline std::string& mutable_ref(StringField field) {
return string_fields[field - STRING_FIELDS_BEGIN];
}
- inline Blob& mutable_ref(BlobField field) {
- return blob_fields[field - BLOB_FIELDS_BEGIN];
+ inline sync_pb::EntitySpecifics& mutable_ref(ProtoField field) {
+ return specifics_fields[field - PROTO_FIELDS_BEGIN];
}
inline Id& mutable_ref(IdField field) {
return id_fields[field - ID_FIELDS_BEGIN];
@@ -369,7 +361,7 @@ class Entry {
return kernel_->ref(field);
}
const std::string& Get(StringField field) const;
- inline Blob Get(BlobField field) const {
+ inline const sync_pb::EntitySpecifics& Get(ProtoField field) const {
DCHECK(kernel_);
return kernel_->ref(field);
}
@@ -378,6 +370,15 @@ class Entry {
return kernel_->ref(field);
}
+ ModelType GetServerModelType() const;
+ ModelType GetModelType() const;
+
+ // If this returns false, we shouldn't bother maintaining
+ // a position value (sibling ordering) for this item.
+ bool ShouldMaintainPosition() const {
+ return GetModelType() == BOOKMARKS;
+ }
+
inline bool ExistsOnClientBecauseNameIsNonEmpty() const {
DCHECK(kernel_);
return !kernel_->ref(NON_UNIQUE_NAME).empty();
@@ -444,8 +445,15 @@ class MutableEntry : public Entry {
bool Put(StringField field, const std::string& value);
bool Put(BaseVersion field, int64 value);
- inline bool Put(BlobField field, const Blob& value) {
- return PutField(field, value);
+ inline bool Put(ProtoField field, const sync_pb::EntitySpecifics& value) {
+ DCHECK(kernel_);
+ // TODO(ncarter): This is unfortunately heavyweight. Can we do
+ // better?
+ if (kernel_->ref(field).SerializeAsString() != value.SerializeAsString()) {
+ kernel_->put(field, value);
+ kernel_->mark_dirty();
+ }
+ return true;
}
inline bool Put(BitField field, bool value) {
return PutField(field, value);
diff --git a/chrome/browser/sync/syncable/syncable_columns.h b/chrome/browser/sync/syncable/syncable_columns.h
index 92caf64..56f0212 100755
--- a/chrome/browser/sync/syncable/syncable_columns.h
+++ b/chrome/browser/sync/syncable/syncable_columns.h
@@ -44,21 +44,17 @@ static const ColumnSpec g_metas_columns[] = {
{"is_unapplied_update", "bit default 0"},
{"is_del", "bit default 0"},
{"is_dir", "bit default 0"},
- {"is_bookmark_object", "bit default 0"},
{"server_is_dir", "bit default 0"},
{"server_is_del", "bit default 0"},
- {"server_is_bookmark_object", "bit default 0"},
//////////////////////////////////////
// Strings
{"non_unique_name", "varchar"},
- {"server_non_unique_name", "varchar(255) COLLATE PATHNAME"},
- {"bookmark_url", "varchar"},
- {"server_bookmark_url", "varchar"},
+ {"server_non_unique_name", "varchar(255)"},
{"singleton_tag", "varchar"},
//////////////////////////////////////
// Blobs.
- {"bookmark_favicon", "blob"},
- {"server_bookmark_favicon", "blob"},
+ {"specifics", "blob"},
+ {"server_specifics", "blob"},
};
// At least enforce that there are equal number of column names and fields.
diff --git a/chrome/browser/sync/syncable/syncable_unittest.cc b/chrome/browser/sync/syncable/syncable_unittest.cc
index a03028d..407508a 100755
--- a/chrome/browser/sync/syncable/syncable_unittest.cc
+++ b/chrome/browser/sync/syncable/syncable_unittest.cc
@@ -34,6 +34,7 @@
#include "base/logging.h"
#include "base/platform_thread.h"
#include "base/scoped_ptr.h"
+#include "chrome/browser/sync/protocol/bookmark_specifics.pb.h"
#include "chrome/browser/sync/syncable/directory_backing_store.h"
#include "chrome/browser/sync/syncable/directory_manager.h"
#include "chrome/browser/sync/util/closure.h"
@@ -626,7 +627,6 @@ TEST_F(SyncableDirectoryTest, TestSimpleFieldsPreservedDuringSaveChanges) {
EntryKernel create_pre_save, update_pre_save;
EntryKernel create_post_save, update_post_save;
string create_name = "Create";
- string favicon_bytes = "PNG";
{
WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__);
@@ -634,10 +634,10 @@ TEST_F(SyncableDirectoryTest, TestSimpleFieldsPreservedDuringSaveChanges) {
MutableEntry update(&trans, CREATE_NEW_UPDATE_ITEM, update_id);
create.Put(IS_UNSYNCED, true);
update.Put(IS_UNAPPLIED_UPDATE, true);
- syncable::Blob fav(favicon_bytes.data(),
- favicon_bytes.data() + favicon_bytes.size());
- create.Put(BOOKMARK_FAVICON, fav);
- update.Put(BOOKMARK_FAVICON, fav);
+ sync_pb::EntitySpecifics specifics;
+ specifics.MutableExtension(sync_pb::bookmark)->set_favicon("PNG");
+ specifics.MutableExtension(sync_pb::bookmark)->set_url("http://nowhere");
+ create.Put(SPECIFICS, specifics);
create_pre_save = create.GetKernelCopy();
update_pre_save = update.GetKernelCopy();
create_id = create.Get(ID);
@@ -690,12 +690,12 @@ TEST_F(SyncableDirectoryTest, TestSimpleFieldsPreservedDuringSaveChanges) {
update_post_save.ref((StringField)i))
<< "String field #" << i << " changed during save/load";
}
- for ( ; i < BLOB_FIELDS_END; ++i) {
- EXPECT_EQ(create_pre_save.ref((BlobField)i),
- create_post_save.ref((BlobField)i))
+ for ( ; i < PROTO_FIELDS_END; ++i) {
+ EXPECT_EQ(create_pre_save.ref((ProtoField)i).SerializeAsString(),
+ create_post_save.ref((ProtoField)i).SerializeAsString())
<< "Blob field #" << i << " changed during save/load";
- EXPECT_EQ(update_pre_save.ref((BlobField)i),
- update_post_save.ref((BlobField)i))
+ EXPECT_EQ(update_pre_save.ref((ProtoField)i).SerializeAsString(),
+ update_post_save.ref((ProtoField)i).SerializeAsString())
<< "Blob field #" << i << " changed during save/load";
}
}