diff options
author | akalin@chromium.org <akalin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-02-10 23:41:40 +0000 |
---|---|---|
committer | akalin@chromium.org <akalin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-02-10 23:41:40 +0000 |
commit | 2b85b67a2938a787ecb3344bb555937e6ef87c9a (patch) | |
tree | 31e8769ffbf4244af44baa91f69a9cc6f7f62df2 /chrome/browser/sync | |
parent | 8fe16d6423f526223016df11b2ae0630672efe8c (diff) | |
download | chromium_src-2b85b67a2938a787ecb3344bb555937e6ef87c9a.zip chromium_src-2b85b67a2938a787ecb3344bb555937e6ef87c9a.tar.gz chromium_src-2b85b67a2938a787ecb3344bb555937e6ef87c9a.tar.bz2 |
[Sync] Add more conversions to Value for sync types
This is a prerequisite for exposing some functions to chrome://sync-internals.
Cleaned up some test code also.
BUG=69500
Committed: http://src.chromium.org/viewvc/chrome?view=rev&revision=74489
Review URL: http://codereview.chromium.org/6476016
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@74521 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/sync')
-rw-r--r-- | chrome/browser/sync/engine/syncapi.cc | 58 | ||||
-rw-r--r-- | chrome/browser/sync/engine/syncapi.h | 19 | ||||
-rw-r--r-- | chrome/browser/sync/engine/syncapi_unittest.cc | 236 | ||||
-rw-r--r-- | chrome/browser/sync/protocol/proto_enum_conversions.cc | 17 | ||||
-rw-r--r-- | chrome/browser/sync/protocol/proto_enum_conversions.h | 3 | ||||
-rw-r--r-- | chrome/browser/sync/protocol/proto_enum_conversions_unittest.cc | 7 | ||||
-rw-r--r-- | chrome/browser/sync/protocol/proto_value_conversions.cc | 104 | ||||
-rw-r--r-- | chrome/browser/sync/protocol/proto_value_conversions.h | 6 | ||||
-rw-r--r-- | chrome/browser/sync/protocol/proto_value_conversions_unittest.cc | 42 | ||||
-rw-r--r-- | chrome/browser/sync/protocol/sync.proto | 2 | ||||
-rw-r--r-- | chrome/browser/sync/sessions/session_state.cc | 92 | ||||
-rw-r--r-- | chrome/browser/sync/sessions/session_state.h | 26 | ||||
-rw-r--r-- | chrome/browser/sync/sessions/session_state_unittest.cc | 191 | ||||
-rw-r--r-- | chrome/browser/sync/syncable/model_type.cc | 13 | ||||
-rw-r--r-- | chrome/browser/sync/syncable/model_type.h | 5 | ||||
-rw-r--r-- | chrome/browser/sync/syncable/model_type_unittest.cc | 33 |
16 files changed, 728 insertions, 126 deletions
diff --git a/chrome/browser/sync/engine/syncapi.cc b/chrome/browser/sync/engine/syncapi.cc index d2b8ecd..69e9e21 100644 --- a/chrome/browser/sync/engine/syncapi.cc +++ b/chrome/browser/sync/engine/syncapi.cc @@ -933,18 +933,70 @@ syncable::BaseTransaction* WriteTransaction::GetWrappedTrans() const { return transaction_; } +SyncManager::ExtraChangeRecordData::~ExtraChangeRecordData() {} + SyncManager::ChangeRecord::ChangeRecord() : id(kInvalidId), action(ACTION_ADD) {} -SyncManager::ChangeRecord::~ChangeRecord() {} +DictionaryValue* SyncManager::ChangeRecord::ToValue( + const BaseTransaction* trans) const { + DictionaryValue* value = new DictionaryValue(); + std::string action_str; + switch (action) { + case ACTION_ADD: + action_str = "Add"; + break; + case ACTION_DELETE: + action_str = "Delete"; + break; + case ACTION_UPDATE: + action_str = "Update"; + break; + default: + NOTREACHED(); + action_str = "Unknown"; + break; + } + value->SetString("action", action_str); + Value* node_value = NULL; + if (action == ACTION_DELETE) { + DictionaryValue* node_dict = new DictionaryValue(); + node_dict->SetString("id", base::Int64ToString(id)); + node_dict->Set("specifics", + browser_sync::EntitySpecificsToValue(specifics)); + if (extra.get()) { + node_dict->Set("extra", extra->ToValue()); + } + node_value = node_dict; + } else { + ReadNode node(trans); + if (node.InitByIdLookup(id)) { + node_value = node.ToValue(); + } + } + if (!node_value) { + NOTREACHED(); + node_value = Value::CreateNullValue(); + } + value->Set("node", node_value); + return value; +} SyncManager::ExtraPasswordChangeRecordData::ExtraPasswordChangeRecordData( const sync_pb::PasswordSpecificsData& data) - : unencrypted_(data) { -} + : unencrypted_(data) {} SyncManager::ExtraPasswordChangeRecordData::~ExtraPasswordChangeRecordData() {} +DictionaryValue* SyncManager::ExtraPasswordChangeRecordData::ToValue() const { + return browser_sync::PasswordSpecificsDataToValue(unencrypted_); +} + +const sync_pb::PasswordSpecificsData& + SyncManager::ExtraPasswordChangeRecordData::unencrypted() const { + return unencrypted_; +} + ////////////////////////////////////////////////////////////////////////// // SyncManager's implementation: SyncManager::SyncInternal class SyncManager::SyncInternal diff --git a/chrome/browser/sync/engine/syncapi.h b/chrome/browser/sync/engine/syncapi.h index 2737b6c..e39a71f 100644 --- a/chrome/browser/sync/engine/syncapi.h +++ b/chrome/browser/sync/engine/syncapi.h @@ -592,8 +592,10 @@ class SyncManager { // wrapper / add a templated method to return unencrypted protobufs. class ExtraChangeRecordData { public: - ExtraChangeRecordData() {} - virtual ~ExtraChangeRecordData() {} + virtual ~ExtraChangeRecordData(); + + // Transfers ownership of the DictionaryValue to the caller. + virtual DictionaryValue* ToValue() const = 0; }; // ChangeRecord indicates a single item that changed as a result of a sync @@ -607,7 +609,9 @@ class SyncManager { ACTION_UPDATE, }; ChangeRecord(); - ~ChangeRecord(); + + // Transfers ownership of the DictionaryValue to the caller. + DictionaryValue* ToValue(const BaseTransaction* trans) const; int64 id; Action action; @@ -622,9 +626,12 @@ class SyncManager { explicit ExtraPasswordChangeRecordData( const sync_pb::PasswordSpecificsData& data); virtual ~ExtraPasswordChangeRecordData(); - const sync_pb::PasswordSpecificsData& unencrypted() { - return unencrypted_; - } + + // Transfers ownership of the DictionaryValue to the caller. + virtual DictionaryValue* ToValue() const; + + const sync_pb::PasswordSpecificsData& unencrypted() const; + private: sync_pb::PasswordSpecificsData unencrypted_; }; diff --git a/chrome/browser/sync/engine/syncapi_unittest.cc b/chrome/browser/sync/engine/syncapi_unittest.cc index f4a05a5..c2e3584 100644 --- a/chrome/browser/sync/engine/syncapi_unittest.cc +++ b/chrome/browser/sync/engine/syncapi_unittest.cc @@ -25,6 +25,7 @@ #include "chrome/browser/sync/syncable/directory_manager.h" #include "chrome/browser/sync/syncable/syncable.h" #include "chrome/test/sync/engine/test_directory_setter_upper.h" +#include "chrome/test/values_test_util.h" #include "jingle/notifier/base/notifier_options.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -34,12 +35,42 @@ using browser_sync::KeyParams; using browser_sync::JsArgList; using browser_sync::MockJsEventHandler; using browser_sync::MockJsEventRouter; +using test::ExpectDictionaryValue; +using test::ExpectStringValue; using testing::_; +using testing::Invoke; using testing::SaveArg; using testing::StrictMock; namespace sync_api { +namespace { + +void ExpectInt64Value(int64 expected_value, + const DictionaryValue& value, const std::string& key) { + std::string int64_str; + EXPECT_TRUE(value.GetString(key, &int64_str)); + int64 val = 0; + EXPECT_TRUE(base::StringToInt64(int64_str, &val)); + EXPECT_EQ(expected_value, val); +} + +// Makes a non-folder child of the root node. Returns the id of the +// newly-created node. +int64 MakeNode(UserShare* share, + syncable::ModelType model_type, + const std::string& client_tag) { + WriteTransaction trans(share); + ReadNode root_node(&trans); + root_node.InitByRootLookup(); + WriteNode node(&trans); + EXPECT_TRUE(node.InitUniqueByCreation(model_type, root_node, client_tag)); + node.SetIsFolder(false); + return node.GetId(); +} + +} // namespace + class SyncApiTest : public testing::Test { public: virtual void SetUp() { @@ -79,16 +110,14 @@ TEST_F(SyncApiTest, SanityCheckTest) { TEST_F(SyncApiTest, BasicTagWrite) { { - WriteTransaction trans(&share_); + ReadTransaction trans(&share_); ReadNode root_node(&trans); root_node.InitByRootLookup(); EXPECT_EQ(root_node.GetFirstChildId(), 0); - - WriteNode wnode(&trans); - EXPECT_TRUE(wnode.InitUniqueByCreation(syncable::BOOKMARKS, - root_node, "testtag")); - wnode.SetIsFolder(false); } + + ignore_result(MakeNode(&share_, syncable::BOOKMARKS, "testtag")); + { ReadTransaction trans(&share_); ReadNode node(&trans); @@ -124,22 +153,12 @@ TEST_F(SyncApiTest, ModelTypesSiloed) { ReadNode root_node(&trans); root_node.InitByRootLookup(); EXPECT_EQ(root_node.GetFirstChildId(), 0); + } - WriteNode bookmarknode(&trans); - EXPECT_TRUE(bookmarknode.InitUniqueByCreation(syncable::BOOKMARKS, - root_node, "collideme")); - bookmarknode.SetIsFolder(false); - - WriteNode prefnode(&trans); - EXPECT_TRUE(prefnode.InitUniqueByCreation(syncable::PREFERENCES, - root_node, "collideme")); - prefnode.SetIsFolder(false); + ignore_result(MakeNode(&share_, syncable::BOOKMARKS, "collideme")); + ignore_result(MakeNode(&share_, syncable::PREFERENCES, "collideme")); + ignore_result(MakeNode(&share_, syncable::AUTOFILL, "collideme")); - WriteNode autofillnode(&trans); - EXPECT_TRUE(autofillnode.InitUniqueByCreation(syncable::AUTOFILL, - root_node, "collideme")); - autofillnode.SetIsFolder(false); - } { ReadTransaction trans(&share_); @@ -286,29 +305,16 @@ TEST_F(SyncApiTest, WriteAndReadPassword) { namespace { -int64 GetInt64Value(const DictionaryValue& value, const std::string& key) { - std::string str; - EXPECT_TRUE(value.GetString(key, &str)); - int64 val = 0; - EXPECT_TRUE(base::StringToInt64(str, &val)); - return val; -} - void CheckNodeValue(const BaseNode& node, const DictionaryValue& value) { - EXPECT_EQ(node.GetId(), GetInt64Value(value, "id")); - EXPECT_EQ(node.GetModificationTime(), - GetInt64Value(value, "modificationTime")); - EXPECT_EQ(node.GetParentId(), GetInt64Value(value, "parentId")); + ExpectInt64Value(node.GetId(), value, "id"); + ExpectInt64Value(node.GetModificationTime(), value, "modificationTime"); + ExpectInt64Value(node.GetParentId(), value, "parentId"); { bool is_folder = false; EXPECT_TRUE(value.GetBoolean("isFolder", &is_folder)); EXPECT_EQ(node.GetIsFolder(), is_folder); } - { - std::string title; - EXPECT_TRUE(value.GetString("title", &title)); - EXPECT_EQ(node.GetTitle(), UTF8ToWide(title)); - } + ExpectStringValue(WideToUTF8(node.GetTitle()), value, "title"); { syncable::ModelType expected_model_type = node.GetModelType(); std::string type_str; @@ -333,10 +339,10 @@ void CheckNodeValue(const BaseNode& node, const DictionaryValue& value) { EXPECT_TRUE(value.Get("specifics", &specifics)); EXPECT_TRUE(Value::Equals(specifics, expected_specifics.get())); } - EXPECT_EQ(node.GetExternalId(), GetInt64Value(value, "externalId")); - EXPECT_EQ(node.GetPredecessorId(), GetInt64Value(value, "predecessorId")); - EXPECT_EQ(node.GetSuccessorId(), GetInt64Value(value, "successorId")); - EXPECT_EQ(node.GetFirstChildId(), GetInt64Value(value, "firstChildId")); + ExpectInt64Value(node.GetExternalId(), value, "externalId"); + ExpectInt64Value(node.GetPredecessorId(), value, "predecessorId"); + ExpectInt64Value(node.GetSuccessorId(), value, "successorId"); + ExpectInt64Value(node.GetFirstChildId(), value, "firstChildId"); EXPECT_EQ(11u, value.size()); } @@ -356,6 +362,139 @@ TEST_F(SyncApiTest, BaseNodeToValue) { namespace { +void ExpectChangeRecordActionValue(SyncManager::ChangeRecord::Action + expected_value, + const DictionaryValue& value, + const std::string& key) { + std::string str_value; + EXPECT_TRUE(value.GetString(key, &str_value)); + switch (expected_value) { + case SyncManager::ChangeRecord::ACTION_ADD: + EXPECT_EQ("Add", str_value); + break; + case SyncManager::ChangeRecord::ACTION_UPDATE: + EXPECT_EQ("Update", str_value); + break; + case SyncManager::ChangeRecord::ACTION_DELETE: + EXPECT_EQ("Delete", str_value); + break; + default: + NOTREACHED(); + break; + } +} + +void CheckNonDeleteChangeRecordValue(const SyncManager::ChangeRecord& record, + const DictionaryValue& value, + BaseTransaction* trans) { + EXPECT_NE(SyncManager::ChangeRecord::ACTION_DELETE, record.action); + ExpectChangeRecordActionValue(record.action, value, "action"); + { + ReadNode node(trans); + EXPECT_TRUE(node.InitByIdLookup(record.id)); + scoped_ptr<DictionaryValue> expected_node_value(node.ToValue()); + ExpectDictionaryValue(*expected_node_value, value, "node"); + } +} + +void CheckDeleteChangeRecordValue(const SyncManager::ChangeRecord& record, + const DictionaryValue& value) { + EXPECT_EQ(SyncManager::ChangeRecord::ACTION_DELETE, record.action); + ExpectChangeRecordActionValue(record.action, value, "action"); + DictionaryValue* node_value = NULL; + EXPECT_TRUE(value.GetDictionary("node", &node_value)); + if (node_value) { + ExpectInt64Value(record.id, *node_value, "id"); + scoped_ptr<DictionaryValue> expected_specifics_value( + browser_sync::EntitySpecificsToValue(record.specifics)); + ExpectDictionaryValue(*expected_specifics_value, + *node_value, "specifics"); + scoped_ptr<DictionaryValue> expected_extra_value; + if (record.extra.get()) { + expected_extra_value.reset(record.extra->ToValue()); + } + Value* extra_value = NULL; + EXPECT_EQ(record.extra.get() != NULL, + node_value->Get("extra", &extra_value)); + EXPECT_TRUE(Value::Equals(extra_value, expected_extra_value.get())); + } +} + +class MockExtraChangeRecordData : public SyncManager::ExtraChangeRecordData { + public: + MOCK_CONST_METHOD0(ToValue, DictionaryValue*()); +}; + +} // namespace + +TEST_F(SyncApiTest, ChangeRecordToValue) { + int64 child_id = MakeNode(&share_, syncable::BOOKMARKS, "testtag"); + sync_pb::EntitySpecifics child_specifics; + { + ReadTransaction trans(&share_); + ReadNode node(&trans); + EXPECT_TRUE(node.InitByIdLookup(child_id)); + child_specifics = node.GetEntry()->Get(syncable::SPECIFICS); + } + + // Add + { + ReadTransaction trans(&share_); + SyncManager::ChangeRecord record; + record.action = SyncManager::ChangeRecord::ACTION_ADD; + record.id = 1; + record.specifics = child_specifics; + record.extra.reset(new StrictMock<MockExtraChangeRecordData>()); + scoped_ptr<DictionaryValue> value(record.ToValue(&trans)); + CheckNonDeleteChangeRecordValue(record, *value, &trans); + } + + // Update + { + ReadTransaction trans(&share_); + SyncManager::ChangeRecord record; + record.action = SyncManager::ChangeRecord::ACTION_UPDATE; + record.id = child_id; + record.specifics = child_specifics; + record.extra.reset(new StrictMock<MockExtraChangeRecordData>()); + scoped_ptr<DictionaryValue> value(record.ToValue(&trans)); + CheckNonDeleteChangeRecordValue(record, *value, &trans); + } + + // Delete (no extra) + { + ReadTransaction trans(&share_); + SyncManager::ChangeRecord record; + record.action = SyncManager::ChangeRecord::ACTION_DELETE; + record.id = child_id + 1; + record.specifics = child_specifics; + scoped_ptr<DictionaryValue> value(record.ToValue(&trans)); + CheckDeleteChangeRecordValue(record, *value); + } + + // Delete (with extra) + { + ReadTransaction trans(&share_); + SyncManager::ChangeRecord record; + record.action = SyncManager::ChangeRecord::ACTION_DELETE; + record.id = child_id + 1; + record.specifics = child_specifics; + + DictionaryValue extra_value; + extra_value.SetString("foo", "bar"); + scoped_ptr<StrictMock<MockExtraChangeRecordData> > extra( + new StrictMock<MockExtraChangeRecordData>()); + EXPECT_CALL(*extra, ToValue()).Times(2).WillRepeatedly( + Invoke(&extra_value, &DictionaryValue::DeepCopy)); + + record.extra.reset(extra.release()); + scoped_ptr<DictionaryValue> value(record.ToValue(&trans)); + CheckDeleteChangeRecordValue(record, *value); + } +} + +namespace { + class TestHttpPostProviderFactory : public HttpPostProviderFactory { public: virtual ~TestHttpPostProviderFactory() {} @@ -506,19 +645,8 @@ void CheckGetNodeByIdReturnArgs(const SyncManager& sync_manager, } TEST_F(SyncManagerTest, ProcessMessageGetNodeById) { - int64 child_id = kInvalidId; - { - WriteTransaction trans(sync_manager_.GetUserShare()); - - ReadNode root_node(&trans); - root_node.InitByRootLookup(); - - WriteNode node(&trans); - EXPECT_TRUE(node.InitUniqueByCreation(syncable::BOOKMARKS, - root_node, "testtag")); - node.SetIsFolder(false); - child_id = node.GetId(); - } + int64 child_id = + MakeNode(sync_manager_.GetUserShare(), syncable::BOOKMARKS, "testtag"); browser_sync::JsBackend* js_backend = sync_manager_.GetJsBackend(); diff --git a/chrome/browser/sync/protocol/proto_enum_conversions.cc b/chrome/browser/sync/protocol/proto_enum_conversions.cc index 7421711..df3d184 100644 --- a/chrome/browser/sync/protocol/proto_enum_conversions.cc +++ b/chrome/browser/sync/protocol/proto_enum_conversions.cc @@ -68,6 +68,23 @@ const char* GetPageTransitionQualifierString( return ""; } +const char* GetUpdatesSourceString( + sync_pb::GetUpdatesCallerInfo::GetUpdatesSource updates_source) { + ASSERT_ENUM_BOUNDS(sync_pb::GetUpdatesCallerInfo, GetUpdatesSource, + UNKNOWN, CLEAR_PRIVATE_DATA); + switch (updates_source) { + ENUM_CASE(sync_pb::GetUpdatesCallerInfo, UNKNOWN); + ENUM_CASE(sync_pb::GetUpdatesCallerInfo, FIRST_UPDATE); + ENUM_CASE(sync_pb::GetUpdatesCallerInfo, LOCAL); + ENUM_CASE(sync_pb::GetUpdatesCallerInfo, NOTIFICATION); + ENUM_CASE(sync_pb::GetUpdatesCallerInfo, PERIODIC); + ENUM_CASE(sync_pb::GetUpdatesCallerInfo, SYNC_CYCLE_CONTINUATION); + ENUM_CASE(sync_pb::GetUpdatesCallerInfo, CLEAR_PRIVATE_DATA); + } + NOTREACHED(); + return ""; +} + #undef ASSERT_ENUM_BOUNDS #undef ENUM_CASE diff --git a/chrome/browser/sync/protocol/proto_enum_conversions.h b/chrome/browser/sync/protocol/proto_enum_conversions.h index 62b6157..fd1142d 100644 --- a/chrome/browser/sync/protocol/proto_enum_conversions.h +++ b/chrome/browser/sync/protocol/proto_enum_conversions.h @@ -28,6 +28,9 @@ const char* GetPageTransitionQualifierString( sync_pb::TabNavigation::PageTransitionQualifier page_transition_qualifier); +const char* GetUpdatesSourceString( + sync_pb::GetUpdatesCallerInfo::GetUpdatesSource updates_source); + } // namespace browser_sync #endif // CHROME_BROWSER_SYNC_PROTOCOL_PROTO_ENUM_CONVERSIONS_H_ diff --git a/chrome/browser/sync/protocol/proto_enum_conversions_unittest.cc b/chrome/browser/sync/protocol/proto_enum_conversions_unittest.cc index 8825458..648cfe6 100644 --- a/chrome/browser/sync/protocol/proto_enum_conversions_unittest.cc +++ b/chrome/browser/sync/protocol/proto_enum_conversions_unittest.cc @@ -51,5 +51,12 @@ TEST_F(ProtoEnumConversionsTest, GetPageTransitionQualifierString) { sync_pb::TabNavigation::PageTransitionQualifier_MAX); } +TEST_F(ProtoEnumConversionsTest, GetUpdatesSourceString) { + TestEnumStringFunction( + GetUpdatesSourceString, + sync_pb::GetUpdatesCallerInfo::GetUpdatesSource_MIN, + sync_pb::GetUpdatesCallerInfo::GetUpdatesSource_MAX); +} + } // namespace } // namespace browser_sync diff --git a/chrome/browser/sync/protocol/proto_value_conversions.cc b/chrome/browser/sync/protocol/proto_value_conversions.cc index 57be094..12ece6e 100644 --- a/chrome/browser/sync/protocol/proto_value_conversions.cc +++ b/chrome/browser/sync/protocol/proto_value_conversions.cc @@ -99,6 +99,67 @@ DictionaryValue* EncryptedDataToValue(const sync_pb::EncryptedData& proto) { return value; } +DictionaryValue* SessionHeaderToValue( + const sync_pb::SessionHeader& proto) { + DictionaryValue* value = new DictionaryValue(); + SET_REP(window, SessionWindowToValue); + return value; +} + +DictionaryValue* SessionTabToValue( + const sync_pb::SessionTab& proto) { + DictionaryValue* value = new DictionaryValue(); + SET_INT32(tab_id); + SET_INT32(window_id); + SET_INT32(tab_visual_index); + SET_INT32(current_navigation_index); + SET_BOOL(pinned); + SET_STR(extension_app_id); + SET_REP(navigation, TabNavigationToValue); + return value; +} + +DictionaryValue* SessionWindowToValue( + const sync_pb::SessionWindow& proto) { + DictionaryValue* value = new DictionaryValue(); + SET_INT32(window_id); + SET_INT32(selected_tab_index); + SET_INT32_REP(tab); + SET_ENUM(browser_type, GetBrowserTypeString); + return value; +} + +DictionaryValue* TabNavigationToValue( + const sync_pb::TabNavigation& proto) { + DictionaryValue* value = new DictionaryValue(); + SET_INT32(index); + SET_STR(virtual_url); + SET_STR(referrer); + SET_STR(title); + SET_STR(state); + SET_ENUM(page_transition, GetPageTransitionString); + SET_ENUM(navigation_qualifier, GetPageTransitionQualifierString); + return value; +} + +DictionaryValue* PasswordSpecificsDataToValue( + const sync_pb::PasswordSpecificsData& proto) { + DictionaryValue* value = new DictionaryValue(); + SET_INT32(scheme); + SET_STR(signon_realm); + SET_STR(origin); + SET_STR(action); + SET_STR(username_element); + SET_STR(username_value); + SET_STR(password_element); + value->SetString("password_value", "<redacted>"); + SET_BOOL(ssl_valid); + SET_BOOL(preferred); + SET_INT64(date_created); + SET_BOOL(blacklisted); + return value; +} + DictionaryValue* AppSpecificsToValue( const sync_pb::AppSpecifics& proto) { DictionaryValue* value = new DictionaryValue(); @@ -200,49 +261,6 @@ DictionaryValue* PreferenceSpecificsToValue( return value; } -DictionaryValue* SessionHeaderToValue( - const sync_pb::SessionHeader& proto) { - DictionaryValue* value = new DictionaryValue(); - SET_REP(window, SessionWindowToValue); - return value; -} - -DictionaryValue* SessionTabToValue( - const sync_pb::SessionTab& proto) { - DictionaryValue* value = new DictionaryValue(); - SET_INT32(tab_id); - SET_INT32(window_id); - SET_INT32(tab_visual_index); - SET_INT32(current_navigation_index); - SET_BOOL(pinned); - SET_STR(extension_app_id); - SET_REP(navigation, TabNavigationToValue); - return value; -} - -DictionaryValue* SessionWindowToValue( - const sync_pb::SessionWindow& proto) { - DictionaryValue* value = new DictionaryValue(); - SET_INT32(window_id); - SET_INT32(selected_tab_index); - SET_INT32_REP(tab); - SET_ENUM(browser_type, GetBrowserTypeString); - return value; -} - -DictionaryValue* TabNavigationToValue( - const sync_pb::TabNavigation& proto) { - DictionaryValue* value = new DictionaryValue(); - SET_INT32(index); - SET_STR(virtual_url); - SET_STR(referrer); - SET_STR(title); - SET_STR(state); - SET_ENUM(page_transition, GetPageTransitionString); - SET_ENUM(navigation_qualifier, GetPageTransitionQualifierString); - return value; -} - DictionaryValue* SessionSpecificsToValue( const sync_pb::SessionSpecifics& proto) { DictionaryValue* value = new DictionaryValue(); diff --git a/chrome/browser/sync/protocol/proto_value_conversions.h b/chrome/browser/sync/protocol/proto_value_conversions.h index c6e4b1d..c2c1ab53 100644 --- a/chrome/browser/sync/protocol/proto_value_conversions.h +++ b/chrome/browser/sync/protocol/proto_value_conversions.h @@ -21,6 +21,7 @@ class EntitySpecifics; class ExtensionSpecifics; class NigoriSpecifics; class PasswordSpecifics; +class PasswordSpecificsData; class PreferenceSpecifics; class SessionHeader; class SessionSpecifics; @@ -63,6 +64,11 @@ DictionaryValue* SessionWindowToValue( DictionaryValue* TabNavigationToValue( const sync_pb::TabNavigation& tab_navigation); +// Sub-protocol of PasswordSpecifics. + +DictionaryValue* PasswordSpecificsDataToValue( + const sync_pb::PasswordSpecificsData& password_specifics_data); + // Main *SpecificsToValue functions. DictionaryValue* AppSpecificsToValue( diff --git a/chrome/browser/sync/protocol/proto_value_conversions_unittest.cc b/chrome/browser/sync/protocol/proto_value_conversions_unittest.cc index 3416282..20e73e4 100644 --- a/chrome/browser/sync/protocol/proto_value_conversions_unittest.cc +++ b/chrome/browser/sync/protocol/proto_value_conversions_unittest.cc @@ -56,6 +56,32 @@ TEST_F(ProtoValueConversionsTest, EncryptedDataToValue) { TestSpecificsToValue(EncryptedDataToValue); } +TEST_F(ProtoValueConversionsTest, SessionHeaderToValue) { + TestSpecificsToValue(SessionHeaderToValue); +} + +TEST_F(ProtoValueConversionsTest, SessionTabToValue) { + TestSpecificsToValue(SessionTabToValue); +} + +TEST_F(ProtoValueConversionsTest, SessionWindowToValue) { + TestSpecificsToValue(SessionWindowToValue); +} + +TEST_F(ProtoValueConversionsTest, TabNavigationToValue) { + TestSpecificsToValue(TabNavigationToValue); +} + +TEST_F(ProtoValueConversionsTest, PasswordSpecificsData) { + sync_pb::PasswordSpecificsData specifics; + specifics.set_password_value("secret"); + scoped_ptr<DictionaryValue> value(PasswordSpecificsDataToValue(specifics)); + EXPECT_FALSE(value->empty()); + std::string password_value; + EXPECT_TRUE(value->GetString("password_value", &password_value)); + EXPECT_EQ("<redacted>", password_value); +} + TEST_F(ProtoValueConversionsTest, AppSpecificsToValue) { TestSpecificsToValue(AppSpecificsToValue); } @@ -92,26 +118,10 @@ TEST_F(ProtoValueConversionsTest, PreferenceSpecificsToValue) { TestSpecificsToValue(PreferenceSpecificsToValue); } -TEST_F(ProtoValueConversionsTest, SessionHeaderToValue) { - TestSpecificsToValue(SessionHeaderToValue); -} - TEST_F(ProtoValueConversionsTest, SessionSpecificsToValue) { TestSpecificsToValue(SessionSpecificsToValue); } -TEST_F(ProtoValueConversionsTest, SessionTabToValue) { - TestSpecificsToValue(SessionTabToValue); -} - -TEST_F(ProtoValueConversionsTest, SessionWindowToValue) { - TestSpecificsToValue(SessionWindowToValue); -} - -TEST_F(ProtoValueConversionsTest, TabNavigationToValue) { - TestSpecificsToValue(TabNavigationToValue); -} - TEST_F(ProtoValueConversionsTest, ThemeSpecificsToValue) { TestSpecificsToValue(ThemeSpecificsToValue); } diff --git a/chrome/browser/sync/protocol/sync.proto b/chrome/browser/sync/protocol/sync.proto index e60d209..6af021b 100644 --- a/chrome/browser/sync/protocol/sync.proto +++ b/chrome/browser/sync/protocol/sync.proto @@ -266,7 +266,7 @@ message GetUpdatesCallerInfo { // continuation of a previous update. CLEAR_PRIVATE_DATA = 6; // Source is a call to remove all private data } - + required GetUpdatesSource source = 1; // True only if notifications were enabled for this GetUpdateMessage. diff --git a/chrome/browser/sync/sessions/session_state.cc b/chrome/browser/sync/sessions/session_state.cc index 3c9887b..e52ebdc 100644 --- a/chrome/browser/sync/sessions/session_state.cc +++ b/chrome/browser/sync/sessions/session_state.cc @@ -7,6 +7,10 @@ #include <set> #include <vector> +#include "base/base64.h" +#include "base/values.h" +#include "chrome/browser/sync/protocol/proto_enum_conversions.h" + using std::set; using std::vector; @@ -37,6 +41,15 @@ TypePayloadMap MakeTypePayloadMapFromRoutingInfo( return types_with_payloads; } +DictionaryValue* TypePayloadMapToValue(const TypePayloadMap& type_payloads) { + DictionaryValue* value = new DictionaryValue(); + for (TypePayloadMap::const_iterator it = type_payloads.begin(); + it != type_payloads.end(); ++it) { + value->SetString(syncable::ModelTypeToString(it->first), it->second); + } + return value; +} + void CoalescePayloads(TypePayloadMap* original, const TypePayloadMap& update) { for (TypePayloadMap::const_iterator i = update.begin(); @@ -63,6 +76,14 @@ SyncSourceInfo::SyncSourceInfo( SyncSourceInfo::~SyncSourceInfo() {} +DictionaryValue* SyncSourceInfo::ToValue() const { + DictionaryValue* value = new DictionaryValue(); + value->SetString("updatesSource", + GetUpdatesSourceString(updates_source)); + value->Set("types", TypePayloadMapToValue(types)); + return value; +} + SyncerStatus::SyncerStatus() : invalid_store(false), syncer_stuck(false), @@ -73,19 +94,62 @@ SyncerStatus::SyncerStatus() num_tombstone_updates_downloaded_total(0) { } +DictionaryValue* SyncerStatus::ToValue() const { + DictionaryValue* value = new DictionaryValue(); + value->SetBoolean("invalidStore", invalid_store); + value->SetBoolean("syncerStuck", syncer_stuck); + value->SetBoolean("syncing", syncing); + value->SetInteger("numSuccessfulCommits", num_successful_commits); + value->SetInteger("numSuccessfulBookmarkCommits", + num_successful_bookmark_commits); + value->SetInteger("numUpdatesDownloadedTotal", + num_updates_downloaded_total); + value->SetInteger("numTombstoneUpdatesDownloadedTotal", + num_tombstone_updates_downloaded_total); + return value; +} + +DictionaryValue* DownloadProgressMarkersToValue( + const std::string + (&download_progress_markers)[syncable::MODEL_TYPE_COUNT]) { + DictionaryValue* value = new DictionaryValue(); + for (int i = syncable::FIRST_REAL_MODEL_TYPE; + i < syncable::MODEL_TYPE_COUNT; ++i) { + // TODO(akalin): Unpack the value into a protobuf. + std::string base64_marker; + bool encoded = + base::Base64Encode(download_progress_markers[i], &base64_marker); + DCHECK(encoded); + value->SetString( + syncable::ModelTypeToString(syncable::ModelTypeFromInt(i)), + base64_marker); + } + return value; +} + ErrorCounters::ErrorCounters() : num_conflicting_commits(0), consecutive_transient_error_commits(0), consecutive_errors(0) { } +DictionaryValue* ErrorCounters::ToValue() const { + DictionaryValue* value = new DictionaryValue(); + value->SetInteger("numConflictingCommits", num_conflicting_commits); + value->SetInteger("consecutiveTransientErrorCommits", + consecutive_transient_error_commits); + value->SetInteger("consecutiveErrors", consecutive_errors); + return value; +} + SyncSessionSnapshot::SyncSessionSnapshot( const SyncerStatus& syncer_status, const ErrorCounters& errors, int64 num_server_changes_remaining, bool is_share_usable, const syncable::ModelTypeBitSet& initial_sync_ended, - std::string download_progress_markers[syncable::MODEL_TYPE_COUNT], + const std::string + (&download_progress_markers)[syncable::MODEL_TYPE_COUNT], bool more_to_sync, bool is_silenced, int64 unsynced_count, @@ -104,7 +168,8 @@ SyncSessionSnapshot::SyncSessionSnapshot( num_conflicting_updates(num_conflicting_updates), did_commit_items(did_commit_items), source(source) { - for (int i = 0; i < syncable::MODEL_TYPE_COUNT; ++i) { + for (int i = syncable::FIRST_REAL_MODEL_TYPE; + i < syncable::MODEL_TYPE_COUNT; ++i) { const_cast<std::string&>(this->download_progress_markers[i]).assign( download_progress_markers[i]); } @@ -112,6 +177,29 @@ SyncSessionSnapshot::SyncSessionSnapshot( SyncSessionSnapshot::~SyncSessionSnapshot() {} +DictionaryValue* SyncSessionSnapshot::ToValue() const { + DictionaryValue* value = new DictionaryValue(); + value->Set("syncerStatus", syncer_status.ToValue()); + value->Set("errors", errors.ToValue()); + // We don't care too much if we lose precision here. + value->SetInteger("numServerChangesRemaining", + static_cast<int>(num_server_changes_remaining)); + value->SetBoolean("isShareUsable", is_share_usable); + value->Set("initialSyncEnded", + syncable::ModelTypeBitSetToValue(initial_sync_ended)); + value->Set("downloadProgressMarkers", + DownloadProgressMarkersToValue(download_progress_markers)); + value->SetBoolean("hasMoreToSync", has_more_to_sync); + value->SetBoolean("isSilenced", is_silenced); + // We don't care too much if we lose precision here, also. + value->SetInteger("unsyncedCount", + static_cast<int>(unsynced_count)); + value->SetInteger("numConflictingUpdates", num_conflicting_updates); + value->SetBoolean("didCommitItems", did_commit_items); + value->Set("source", source.ToValue()); + return value; +} + ConflictProgress::ConflictProgress(bool* dirty_flag) : dirty_(dirty_flag) {} ConflictProgress::~ConflictProgress() { diff --git a/chrome/browser/sync/sessions/session_state.h b/chrome/browser/sync/sessions/session_state.h index d1a6e17..69fbf7a 100644 --- a/chrome/browser/sync/sessions/session_state.h +++ b/chrome/browser/sync/sessions/session_state.h @@ -23,6 +23,8 @@ #include "chrome/browser/sync/syncable/model_type.h" #include "chrome/browser/sync/syncable/syncable.h" +class DictionaryValue; + namespace syncable { class DirectoryManager; } @@ -48,6 +50,9 @@ TypePayloadMap MakeTypePayloadMapFromRoutingInfo( const ModelSafeRoutingInfo& routes, const std::string& payload); +// Caller takes ownership of the returned dictionary. +DictionaryValue* TypePayloadMapToValue(const TypePayloadMap& type_payloads); + // Coalesce |update| into |original|, overwriting only when |update| has // a non-empty payload. void CoalescePayloads(TypePayloadMap* original, const TypePayloadMap& update); @@ -62,6 +67,9 @@ struct SyncSourceInfo { const TypePayloadMap& t); ~SyncSourceInfo(); + // Caller takes ownership of the returned dictionary. + DictionaryValue* ToValue() const; + sync_pb::GetUpdatesCallerInfo::GetUpdatesSource updates_source; TypePayloadMap types; }; @@ -70,6 +78,9 @@ struct SyncSourceInfo { struct SyncerStatus { SyncerStatus(); + // Caller takes ownership of the returned dictionary. + DictionaryValue* ToValue() const; + // True when we get such an INVALID_STORE error from the server. bool invalid_store; // True iff we're stuck. @@ -87,6 +98,10 @@ struct SyncerStatus { // Counters for various errors that can occur repeatedly during a sync session. struct ErrorCounters { ErrorCounters(); + + // Caller takes ownership of the returned dictionary. + DictionaryValue* ToValue() const; + int num_conflicting_commits; // Number of commits hitting transient errors since the last successful @@ -99,6 +114,11 @@ struct ErrorCounters { int consecutive_errors; }; +// Caller takes ownership of the returned dictionary. +DictionaryValue* DownloadProgressMarkersToValue( + const std::string + (&download_progress_markers)[syncable::MODEL_TYPE_COUNT]); + // An immutable snapshot of state from a SyncSession. Convenient to use as // part of notifications as it is inherently thread-safe. struct SyncSessionSnapshot { @@ -108,7 +128,8 @@ struct SyncSessionSnapshot { int64 num_server_changes_remaining, bool is_share_usable, const syncable::ModelTypeBitSet& initial_sync_ended, - std::string download_progress_markers[syncable::MODEL_TYPE_COUNT], + const std::string + (&download_progress_markers)[syncable::MODEL_TYPE_COUNT], bool more_to_sync, bool is_silenced, int64 unsynced_count, @@ -117,6 +138,9 @@ struct SyncSessionSnapshot { const SyncSourceInfo& source); ~SyncSessionSnapshot(); + // Caller takes ownership of the returned dictionary. + DictionaryValue* ToValue() const; + const SyncerStatus syncer_status; const ErrorCounters errors; const int64 num_server_changes_remaining; diff --git a/chrome/browser/sync/sessions/session_state_unittest.cc b/chrome/browser/sync/sessions/session_state_unittest.cc new file mode 100644 index 0000000..3ac832f --- /dev/null +++ b/chrome/browser/sync/sessions/session_state_unittest.cc @@ -0,0 +1,191 @@ +// Copyright (c) 2011 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 "chrome/browser/sync/sessions/session_state.h" + +#include <string> + +#include "base/base64.h" +#include "base/scoped_ptr.h" +#include "base/values.h" +#include "chrome/test/values_test_util.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace browser_sync { +namespace sessions { +namespace { + +using test::ExpectBooleanValue; +using test::ExpectDictionaryValue; +using test::ExpectIntegerValue; +using test::ExpectListValue; +using test::ExpectStringValue; + +class SessionStateTest : public testing::Test {}; + +TEST_F(SessionStateTest, TypePayloadMapToValue) { + TypePayloadMap payloads; + payloads[syncable::BOOKMARKS] = "bookmarkpayload"; + payloads[syncable::APPS] = ""; + + scoped_ptr<DictionaryValue> value(TypePayloadMapToValue(payloads)); + EXPECT_EQ(2u, value->size()); + ExpectStringValue("bookmarkpayload", *value, "Bookmarks"); + ExpectStringValue("", *value, "Apps"); + EXPECT_FALSE(value->HasKey("Preferences")); +} + +TEST_F(SessionStateTest, SyncSourceInfoToValue) { + sync_pb::GetUpdatesCallerInfo::GetUpdatesSource updates_source = + sync_pb::GetUpdatesCallerInfo::PERIODIC; + TypePayloadMap types; + types[syncable::PREFERENCES] = "preferencespayload"; + types[syncable::EXTENSIONS] = ""; + scoped_ptr<DictionaryValue> expected_types_value( + TypePayloadMapToValue(types)); + + SyncSourceInfo source_info(updates_source, types); + + scoped_ptr<DictionaryValue> value(source_info.ToValue()); + EXPECT_EQ(2u, value->size()); + ExpectStringValue("PERIODIC", *value, "updatesSource"); + ExpectDictionaryValue(*expected_types_value, *value, "types"); +} + +TEST_F(SessionStateTest, SyncerStatusToValue) { + SyncerStatus status; + status.invalid_store = true; + status.syncer_stuck = false; + status.syncing = true; + status.num_successful_commits = 5; + status.num_successful_bookmark_commits = 10; + status.num_updates_downloaded_total = 100; + status.num_tombstone_updates_downloaded_total = 200; + + scoped_ptr<DictionaryValue> value(status.ToValue()); + EXPECT_EQ(7u, value->size()); + ExpectBooleanValue(status.invalid_store, *value, "invalidStore"); + ExpectBooleanValue(status.syncer_stuck, *value, "syncerStuck"); + ExpectBooleanValue(status.syncing, *value, "syncing"); + ExpectIntegerValue(status.num_successful_commits, + *value, "numSuccessfulCommits"); + ExpectIntegerValue(status.num_successful_bookmark_commits, + *value, "numSuccessfulBookmarkCommits"); + ExpectIntegerValue(status.num_updates_downloaded_total, + *value, "numUpdatesDownloadedTotal"); + ExpectIntegerValue(status.num_tombstone_updates_downloaded_total, + *value, "numTombstoneUpdatesDownloadedTotal"); +} + +TEST_F(SessionStateTest, ErrorCountersToValue) { + ErrorCounters counters; + counters.num_conflicting_commits = 1; + counters.consecutive_transient_error_commits = 5; + counters.consecutive_errors = 3; + + scoped_ptr<DictionaryValue> value(counters.ToValue()); + EXPECT_EQ(3u, value->size()); + ExpectIntegerValue(counters.num_conflicting_commits, + *value, "numConflictingCommits"); + ExpectIntegerValue(counters.consecutive_transient_error_commits, + *value, "consecutiveTransientErrorCommits"); + ExpectIntegerValue(counters.consecutive_errors, + *value, "consecutiveErrors"); +} + +TEST_F(SessionStateTest, DownloadProgressMarkersToValue) { + std::string download_progress_markers[syncable::MODEL_TYPE_COUNT]; + for (int i = syncable::FIRST_REAL_MODEL_TYPE; + i < syncable::MODEL_TYPE_COUNT; ++i) { + std::string marker(i, i); + download_progress_markers[i] = marker; + } + + scoped_ptr<DictionaryValue> value( + DownloadProgressMarkersToValue(download_progress_markers)); + EXPECT_EQ(syncable::MODEL_TYPE_COUNT - syncable::FIRST_REAL_MODEL_TYPE, + static_cast<int>(value->size())); + for (int i = syncable::FIRST_REAL_MODEL_TYPE; + i < syncable::MODEL_TYPE_COUNT; ++i) { + syncable::ModelType model_type = syncable::ModelTypeFromInt(i); + std::string marker(i, i); + std::string expected_value; + EXPECT_TRUE(base::Base64Encode(marker, &expected_value)); + ExpectStringValue(expected_value, + *value, syncable::ModelTypeToString(model_type)); + } +} + +TEST_F(SessionStateTest, SyncSessionSnapshotToValue) { + SyncerStatus syncer_status; + syncer_status.num_successful_commits = 500; + scoped_ptr<DictionaryValue> expected_syncer_status_value( + syncer_status.ToValue()); + + ErrorCounters errors; + errors.num_conflicting_commits = 250; + scoped_ptr<DictionaryValue> expected_errors_value( + errors.ToValue()); + + const int kNumServerChangesRemaining = 105; + const bool kIsShareUsable = true; + + syncable::ModelTypeBitSet initial_sync_ended; + initial_sync_ended.set(syncable::BOOKMARKS); + initial_sync_ended.set(syncable::PREFERENCES); + scoped_ptr<ListValue> expected_initial_sync_ended_value( + syncable::ModelTypeBitSetToValue(initial_sync_ended)); + + std::string download_progress_markers[syncable::MODEL_TYPE_COUNT]; + download_progress_markers[syncable::BOOKMARKS] = "test"; + download_progress_markers[syncable::APPS] = "apps"; + scoped_ptr<DictionaryValue> expected_download_progress_markers_value( + DownloadProgressMarkersToValue(download_progress_markers)); + + const bool kHasMoreToSync = false; + const bool kIsSilenced = true; + const int kUnsyncedCount = 1053; + const int kNumConflictingUpdates = 1055; + const bool kDidCommitItems = true; + + SyncSourceInfo source; + scoped_ptr<DictionaryValue> expected_source_value(source.ToValue()); + + SyncSessionSnapshot snapshot(syncer_status, + errors, + kNumServerChangesRemaining, + kIsShareUsable, + initial_sync_ended, + download_progress_markers, + kHasMoreToSync, + kIsSilenced, + kUnsyncedCount, + kNumConflictingUpdates, + kDidCommitItems, + source); + scoped_ptr<DictionaryValue> value(snapshot.ToValue()); + EXPECT_EQ(12u, value->size()); + ExpectDictionaryValue(*expected_syncer_status_value, *value, + "syncerStatus"); + ExpectDictionaryValue(*expected_errors_value, *value, "errors"); + ExpectIntegerValue(kNumServerChangesRemaining, *value, + "numServerChangesRemaining"); + ExpectBooleanValue(kIsShareUsable, *value, "isShareUsable"); + ExpectListValue(*expected_initial_sync_ended_value, *value, + "initialSyncEnded"); + ExpectDictionaryValue(*expected_download_progress_markers_value, + *value, "downloadProgressMarkers"); + ExpectBooleanValue(kHasMoreToSync, *value, "hasMoreToSync"); + ExpectBooleanValue(kIsSilenced, *value, "isSilenced"); + ExpectIntegerValue(kUnsyncedCount, *value, "unsyncedCount"); + ExpectIntegerValue(kNumConflictingUpdates, *value, + "numConflictingUpdates"); + ExpectBooleanValue(kDidCommitItems, *value, + "didCommitItems"); + ExpectDictionaryValue(*expected_source_value, *value, "source"); +} + +} // namespace +} // namespace sessions +} // namespace browser_sync diff --git a/chrome/browser/sync/syncable/model_type.cc b/chrome/browser/sync/syncable/model_type.cc index 1aea654..0ff67bd 100644 --- a/chrome/browser/sync/syncable/model_type.cc +++ b/chrome/browser/sync/syncable/model_type.cc @@ -7,6 +7,7 @@ #include <sstream> #include "base/metrics/histogram.h" +#include "base/values.h" #include "chrome/browser/sync/engine/syncproto.h" #include "chrome/browser/sync/protocol/app_specifics.pb.h" #include "chrome/browser/sync/protocol/autofill_specifics.pb.h" @@ -255,6 +256,18 @@ bool ModelTypeBitSetFromString( return iss.eof(); } +ListValue* ModelTypeBitSetToValue(const ModelTypeBitSet& model_types) { + ListValue* value = new ListValue(); + for (int i = FIRST_REAL_MODEL_TYPE; i < MODEL_TYPE_COUNT; ++i) { + if (model_types[i]) { + value->Append( + Value::CreateStringValue( + ModelTypeToString(ModelTypeFromInt(i)))); + } + } + return value; +} + // For now, this just implements UMA_HISTOGRAM_LONG_TIMES. This can be adjusted // if we feel the min, max, or bucket count amount are not appropriate. #define SYNC_FREQ_HISTOGRAM(name, time) UMA_HISTOGRAM_CUSTOM_TIMES( \ diff --git a/chrome/browser/sync/syncable/model_type.h b/chrome/browser/sync/syncable/model_type.h index 308f516..62f1787 100644 --- a/chrome/browser/sync/syncable/model_type.h +++ b/chrome/browser/sync/syncable/model_type.h @@ -17,6 +17,8 @@ #include "base/logging.h" #include "base/time.h" +class ListValue; + namespace sync_pb { class EntitySpecifics; class SyncEntity; @@ -113,6 +115,9 @@ bool ModelTypeBitSetFromString( const std::string& model_type_bitset_string, ModelTypeBitSet* model_types); +// Caller takes ownership of returned list. +ListValue* ModelTypeBitSetToValue(const ModelTypeBitSet& model_types); + // Posts timedeltas to histogram of datatypes. Allows tracking of the frequency // at which datatypes cause syncs. void PostTimeToTypeHistogram(ModelType model_type, base::TimeDelta time); diff --git a/chrome/browser/sync/syncable/model_type_unittest.cc b/chrome/browser/sync/syncable/model_type_unittest.cc new file mode 100644 index 0000000..b15f0a0 --- /dev/null +++ b/chrome/browser/sync/syncable/model_type_unittest.cc @@ -0,0 +1,33 @@ +// Copyright (c) 2011 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 "chrome/browser/sync/syncable/model_type.h" + +#include <string> + +#include "base/scoped_ptr.h" +#include "base/values.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace syncable { +namespace { + +class ModelTypeTest : public testing::Test {}; + +TEST_F(ModelTypeTest, ModelTypeBitSetToValue) { + ModelTypeBitSet model_types; + model_types.set(syncable::BOOKMARKS); + model_types.set(syncable::APPS); + + scoped_ptr<ListValue> value(ModelTypeBitSetToValue(model_types)); + EXPECT_EQ(2u, value->GetSize()); + std::string types[2]; + EXPECT_TRUE(value->GetString(0, &types[0])); + EXPECT_TRUE(value->GetString(1, &types[1])); + EXPECT_EQ("Bookmarks", types[0]); + EXPECT_EQ("Apps", types[1]); +} + +} // namespace +} // namespace syncable |