1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 // Keep this file in sync with the .proto files in this directory. 6 7 #include "sync/protocol/proto_value_conversions.h" 8 9 #include "base/memory/scoped_ptr.h" 10 #include "base/strings/string_number_conversions.h" 11 #include "base/time/time.h" 12 #include "base/values.h" 13 #include "sync/internal_api/public/base/model_type.h" 14 #include "sync/protocol/app_notification_specifics.pb.h" 15 #include "sync/protocol/app_setting_specifics.pb.h" 16 #include "sync/protocol/app_specifics.pb.h" 17 #include "sync/protocol/autofill_specifics.pb.h" 18 #include "sync/protocol/bookmark_specifics.pb.h" 19 #include "sync/protocol/device_info_specifics.pb.h" 20 #include "sync/protocol/encryption.pb.h" 21 #include "sync/protocol/experiments_specifics.pb.h" 22 #include "sync/protocol/extension_setting_specifics.pb.h" 23 #include "sync/protocol/extension_specifics.pb.h" 24 #include "sync/protocol/favicon_image_specifics.pb.h" 25 #include "sync/protocol/favicon_tracking_specifics.pb.h" 26 #include "sync/protocol/managed_user_setting_specifics.pb.h" 27 #include "sync/protocol/nigori_specifics.pb.h" 28 #include "sync/protocol/password_specifics.pb.h" 29 #include "sync/protocol/preference_specifics.pb.h" 30 #include "sync/protocol/priority_preference_specifics.pb.h" 31 #include "sync/protocol/search_engine_specifics.pb.h" 32 #include "sync/protocol/session_specifics.pb.h" 33 #include "sync/protocol/sync.pb.h" 34 #include "sync/protocol/theme_specifics.pb.h" 35 #include "sync/protocol/typed_url_specifics.pb.h" 36 #include "testing/gtest/include/gtest/gtest.h" 37 38 namespace syncer { 39 namespace { 40 41 class ProtoValueConversionsTest : public testing::Test { 42 protected: 43 template <class T> 44 void TestSpecificsToValue( 45 base::DictionaryValue* (*specifics_to_value)(const T&)) { 46 const T& specifics(T::default_instance()); 47 scoped_ptr<base::DictionaryValue> value(specifics_to_value(specifics)); 48 // We can't do much but make sure that this doesn't crash. 49 } 50 }; 51 52 TEST_F(ProtoValueConversionsTest, ProtoChangeCheck) { 53 // If this number changes, that means we added or removed a data 54 // type. Don't forget to add a unit test for {New 55 // type}SpecificsToValue below. 56 EXPECT_EQ(30, MODEL_TYPE_COUNT); 57 58 // We'd also like to check if we changed any field in our messages. 59 // However, that's hard to do: sizeof could work, but it's 60 // platform-dependent. default_instance().ByteSize() won't change 61 // for most changes, since most of our fields are optional. So we 62 // just settle for comments in the proto files. 63 } 64 65 TEST_F(ProtoValueConversionsTest, EncryptedDataToValue) { 66 TestSpecificsToValue(EncryptedDataToValue); 67 } 68 69 TEST_F(ProtoValueConversionsTest, SessionHeaderToValue) { 70 TestSpecificsToValue(SessionHeaderToValue); 71 } 72 73 TEST_F(ProtoValueConversionsTest, SessionTabToValue) { 74 TestSpecificsToValue(SessionTabToValue); 75 } 76 77 TEST_F(ProtoValueConversionsTest, SessionWindowToValue) { 78 TestSpecificsToValue(SessionWindowToValue); 79 } 80 81 TEST_F(ProtoValueConversionsTest, TabNavigationToValue) { 82 TestSpecificsToValue(TabNavigationToValue); 83 } 84 85 TEST_F(ProtoValueConversionsTest, PasswordSpecificsData) { 86 sync_pb::PasswordSpecificsData specifics; 87 specifics.set_password_value("secret"); 88 scoped_ptr<base::DictionaryValue> value( 89 PasswordSpecificsDataToValue(specifics)); 90 EXPECT_FALSE(value->empty()); 91 std::string password_value; 92 EXPECT_TRUE(value->GetString("password_value", &password_value)); 93 EXPECT_EQ("<redacted>", password_value); 94 } 95 96 TEST_F(ProtoValueConversionsTest, AppListSpecificsToValue) { 97 TestSpecificsToValue(AppListSpecificsToValue); 98 } 99 100 TEST_F(ProtoValueConversionsTest, AppNotificationToValue) { 101 TestSpecificsToValue(AppNotificationToValue); 102 } 103 104 TEST_F(ProtoValueConversionsTest, AppSettingSpecificsToValue) { 105 sync_pb::AppNotificationSettings specifics; 106 specifics.set_disabled(true); 107 specifics.set_oauth_client_id("some_id_value"); 108 scoped_ptr<base::DictionaryValue> value(AppSettingsToValue(specifics)); 109 EXPECT_FALSE(value->empty()); 110 bool disabled_value = false; 111 std::string oauth_client_id_value; 112 EXPECT_TRUE(value->GetBoolean("disabled", &disabled_value)); 113 EXPECT_EQ(true, disabled_value); 114 EXPECT_TRUE(value->GetString("oauth_client_id", &oauth_client_id_value)); 115 EXPECT_EQ("some_id_value", oauth_client_id_value); 116 } 117 118 TEST_F(ProtoValueConversionsTest, AppSpecificsToValue) { 119 TestSpecificsToValue(AppSpecificsToValue); 120 } 121 122 TEST_F(ProtoValueConversionsTest, AutofillSpecificsToValue) { 123 TestSpecificsToValue(AutofillSpecificsToValue); 124 } 125 126 TEST_F(ProtoValueConversionsTest, AutofillProfileSpecificsToValue) { 127 TestSpecificsToValue(AutofillProfileSpecificsToValue); 128 } 129 130 TEST_F(ProtoValueConversionsTest, BookmarkSpecificsToValue) { 131 TestSpecificsToValue(BookmarkSpecificsToValue); 132 } 133 134 TEST_F(ProtoValueConversionsTest, BookmarkSpecificsData) { 135 const base::Time creation_time(base::Time::Now()); 136 const std::string icon_url = "http://www.google.com/favicon.ico"; 137 sync_pb::BookmarkSpecifics specifics; 138 specifics.set_creation_time_us(creation_time.ToInternalValue()); 139 specifics.set_icon_url(icon_url); 140 sync_pb::MetaInfo* meta_1 = specifics.add_meta_info(); 141 meta_1->set_key("key1"); 142 meta_1->set_value("value1"); 143 sync_pb::MetaInfo* meta_2 = specifics.add_meta_info(); 144 meta_2->set_key("key2"); 145 meta_2->set_value("value2"); 146 147 scoped_ptr<base::DictionaryValue> value(BookmarkSpecificsToValue(specifics)); 148 EXPECT_FALSE(value->empty()); 149 std::string encoded_time; 150 EXPECT_TRUE(value->GetString("creation_time_us", &encoded_time)); 151 EXPECT_EQ(base::Int64ToString(creation_time.ToInternalValue()), encoded_time); 152 std::string encoded_icon_url; 153 EXPECT_TRUE(value->GetString("icon_url", &encoded_icon_url)); 154 EXPECT_EQ(icon_url, encoded_icon_url); 155 base::ListValue* meta_info_list; 156 ASSERT_TRUE(value->GetList("meta_info", &meta_info_list)); 157 EXPECT_EQ(2u, meta_info_list->GetSize()); 158 base::DictionaryValue* meta_info; 159 std::string meta_key; 160 std::string meta_value; 161 ASSERT_TRUE(meta_info_list->GetDictionary(0, &meta_info)); 162 EXPECT_TRUE(meta_info->GetString("key", &meta_key)); 163 EXPECT_TRUE(meta_info->GetString("value", &meta_value)); 164 EXPECT_EQ("key1", meta_key); 165 EXPECT_EQ("value1", meta_value); 166 ASSERT_TRUE(meta_info_list->GetDictionary(1, &meta_info)); 167 EXPECT_TRUE(meta_info->GetString("key", &meta_key)); 168 EXPECT_TRUE(meta_info->GetString("value", &meta_value)); 169 EXPECT_EQ("key2", meta_key); 170 EXPECT_EQ("value2", meta_value); 171 } 172 173 TEST_F(ProtoValueConversionsTest, PriorityPreferenceSpecificsToValue) { 174 TestSpecificsToValue(PriorityPreferenceSpecificsToValue); 175 } 176 177 TEST_F(ProtoValueConversionsTest, DeviceInfoSpecificsToValue) { 178 TestSpecificsToValue(DeviceInfoSpecificsToValue); 179 } 180 181 TEST_F(ProtoValueConversionsTest, ExperimentsSpecificsToValue) { 182 TestSpecificsToValue(ExperimentsSpecificsToValue); 183 } 184 185 TEST_F(ProtoValueConversionsTest, ExtensionSettingSpecificsToValue) { 186 TestSpecificsToValue(ExtensionSettingSpecificsToValue); 187 } 188 189 TEST_F(ProtoValueConversionsTest, ExtensionSpecificsToValue) { 190 TestSpecificsToValue(ExtensionSpecificsToValue); 191 } 192 193 TEST_F(ProtoValueConversionsTest, FaviconImageSpecificsToValue) { 194 TestSpecificsToValue(FaviconImageSpecificsToValue); 195 } 196 197 TEST_F(ProtoValueConversionsTest, FaviconTrackingSpecificsToValue) { 198 TestSpecificsToValue(FaviconTrackingSpecificsToValue); 199 } 200 201 TEST_F(ProtoValueConversionsTest, HistoryDeleteDirectiveSpecificsToValue) { 202 TestSpecificsToValue(HistoryDeleteDirectiveSpecificsToValue); 203 } 204 205 TEST_F(ProtoValueConversionsTest, ManagedUserSettingSpecificsToValue) { 206 TestSpecificsToValue(ManagedUserSettingSpecificsToValue); 207 } 208 209 TEST_F(ProtoValueConversionsTest, ManagedUserSpecificsToValue) { 210 TestSpecificsToValue(ManagedUserSpecificsToValue); 211 } 212 213 TEST_F(ProtoValueConversionsTest, NigoriSpecificsToValue) { 214 TestSpecificsToValue(NigoriSpecificsToValue); 215 } 216 217 TEST_F(ProtoValueConversionsTest, PasswordSpecificsToValue) { 218 TestSpecificsToValue(PasswordSpecificsToValue); 219 } 220 221 TEST_F(ProtoValueConversionsTest, PreferenceSpecificsToValue) { 222 TestSpecificsToValue(PreferenceSpecificsToValue); 223 } 224 225 TEST_F(ProtoValueConversionsTest, SearchEngineSpecificsToValue) { 226 TestSpecificsToValue(SearchEngineSpecificsToValue); 227 } 228 229 TEST_F(ProtoValueConversionsTest, SessionSpecificsToValue) { 230 TestSpecificsToValue(SessionSpecificsToValue); 231 } 232 233 TEST_F(ProtoValueConversionsTest, SyncedNotificationSpecificsToValue) { 234 TestSpecificsToValue(SyncedNotificationSpecificsToValue); 235 } 236 237 TEST_F(ProtoValueConversionsTest, ThemeSpecificsToValue) { 238 TestSpecificsToValue(ThemeSpecificsToValue); 239 } 240 241 TEST_F(ProtoValueConversionsTest, TypedUrlSpecificsToValue) { 242 TestSpecificsToValue(TypedUrlSpecificsToValue); 243 } 244 245 TEST_F(ProtoValueConversionsTest, DictionarySpecificsToValue) { 246 TestSpecificsToValue(DictionarySpecificsToValue); 247 } 248 249 TEST_F(ProtoValueConversionsTest, ArticleSpecificsToValue) { 250 TestSpecificsToValue(ArticleSpecificsToValue); 251 } 252 253 // TODO(akalin): Figure out how to better test EntitySpecificsToValue. 254 255 TEST_F(ProtoValueConversionsTest, EntitySpecificsToValue) { 256 sync_pb::EntitySpecifics specifics; 257 // Touch the extensions to make sure it shows up in the generated 258 // value. 259 #define SET_FIELD(key) (void)specifics.mutable_##key() 260 261 SET_FIELD(app); 262 SET_FIELD(app_list); 263 SET_FIELD(app_notification); 264 SET_FIELD(app_setting); 265 SET_FIELD(article); 266 SET_FIELD(autofill); 267 SET_FIELD(autofill_profile); 268 SET_FIELD(bookmark); 269 SET_FIELD(device_info); 270 SET_FIELD(dictionary); 271 SET_FIELD(experiments); 272 SET_FIELD(extension); 273 SET_FIELD(extension_setting); 274 SET_FIELD(favicon_image); 275 SET_FIELD(favicon_tracking); 276 SET_FIELD(history_delete_directive); 277 SET_FIELD(managed_user_setting); 278 SET_FIELD(managed_user); 279 SET_FIELD(nigori); 280 SET_FIELD(password); 281 SET_FIELD(preference); 282 SET_FIELD(priority_preference); 283 SET_FIELD(search_engine); 284 SET_FIELD(session); 285 SET_FIELD(synced_notification); 286 SET_FIELD(theme); 287 SET_FIELD(typed_url); 288 289 #undef SET_FIELD 290 291 scoped_ptr<base::DictionaryValue> value(EntitySpecificsToValue(specifics)); 292 EXPECT_EQ(MODEL_TYPE_COUNT - FIRST_REAL_MODEL_TYPE - 293 (LAST_PROXY_TYPE - FIRST_PROXY_TYPE + 1), 294 static_cast<int>(value->size())); 295 } 296 297 namespace { 298 // Returns whether the given value has specifics under the entries in the given 299 // path. 300 bool ValueHasSpecifics(const base::DictionaryValue& value, 301 const std::string& path) { 302 const base::ListValue* entities_list = NULL; 303 const base::DictionaryValue* entry_dictionary = NULL; 304 const base::DictionaryValue* specifics_dictionary = NULL; 305 306 if (!value.GetList(path, &entities_list)) 307 return false; 308 309 if (!entities_list->GetDictionary(0, &entry_dictionary)) 310 return false; 311 312 return entry_dictionary->GetDictionary("specifics", 313 &specifics_dictionary); 314 } 315 } // namespace 316 317 // Create a ClientToServerMessage with an EntitySpecifics. Converting it to 318 // a value should respect the |include_specifics| flag. 319 TEST_F(ProtoValueConversionsTest, ClientToServerMessageToValue) { 320 sync_pb::ClientToServerMessage message; 321 sync_pb::CommitMessage* commit_message = message.mutable_commit(); 322 sync_pb::SyncEntity* entity = commit_message->add_entries(); 323 entity->mutable_specifics(); 324 325 scoped_ptr<base::DictionaryValue> value_with_specifics( 326 ClientToServerMessageToValue(message, true /* include_specifics */)); 327 EXPECT_FALSE(value_with_specifics->empty()); 328 EXPECT_TRUE(ValueHasSpecifics(*(value_with_specifics.get()), 329 "commit.entries")); 330 331 scoped_ptr<base::DictionaryValue> value_without_specifics( 332 ClientToServerMessageToValue(message, false /* include_specifics */)); 333 EXPECT_FALSE(value_without_specifics->empty()); 334 EXPECT_FALSE(ValueHasSpecifics(*(value_without_specifics.get()), 335 "commit.entries")); 336 } 337 338 // Create a ClientToServerResponse with an EntitySpecifics. Converting it to 339 // a value should respect the |include_specifics| flag. 340 TEST_F(ProtoValueConversionsTest, ClientToServerResponseToValue) { 341 sync_pb::ClientToServerResponse message; 342 sync_pb::GetUpdatesResponse* response = message.mutable_get_updates(); 343 sync_pb::SyncEntity* entity = response->add_entries(); 344 entity->mutable_specifics(); 345 346 scoped_ptr<base::DictionaryValue> value_with_specifics( 347 ClientToServerResponseToValue(message, true /* include_specifics */)); 348 EXPECT_FALSE(value_with_specifics->empty()); 349 EXPECT_TRUE(ValueHasSpecifics(*(value_with_specifics.get()), 350 "get_updates.entries")); 351 352 scoped_ptr<base::DictionaryValue> value_without_specifics( 353 ClientToServerResponseToValue(message, false /* include_specifics */)); 354 EXPECT_FALSE(value_without_specifics->empty()); 355 EXPECT_FALSE(ValueHasSpecifics(*(value_without_specifics.get()), 356 "get_updates.entries")); 357 } 358 359 } // namespace 360 } // namespace syncer 361