| OLD | NEW |
| (Empty) |
| 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 #include "chrome/browser/chromeos/contacts/contact_database.h" | |
| 6 | |
| 7 #include <string> | |
| 8 | |
| 9 #include "base/bind.h" | |
| 10 #include "base/file_util.h" | |
| 11 #include "base/files/file_enumerator.h" | |
| 12 #include "base/files/file_path.h" | |
| 13 #include "base/files/scoped_temp_dir.h" | |
| 14 #include "base/memory/scoped_ptr.h" | |
| 15 #include "base/memory/scoped_vector.h" | |
| 16 #include "base/message_loop/message_loop.h" | |
| 17 #include "chrome/browser/chromeos/contacts/contact.pb.h" | |
| 18 #include "chrome/browser/chromeos/contacts/contact_test_util.h" | |
| 19 #include "content/public/browser/browser_thread.h" | |
| 20 #include "content/public/test/test_browser_thread.h" | |
| 21 #include "testing/gtest/include/gtest/gtest.h" | |
| 22 #include "ui/gfx/size.h" | |
| 23 | |
| 24 using content::BrowserThread; | |
| 25 | |
| 26 namespace contacts { | |
| 27 namespace test { | |
| 28 | |
| 29 // Name of the directory created within a temporary directory to store the | |
| 30 // contacts database. | |
| 31 const base::FilePath::CharType kDatabaseDirectoryName[] = | |
| 32 FILE_PATH_LITERAL("contacts"); | |
| 33 | |
| 34 class ContactDatabaseTest : public testing::Test { | |
| 35 public: | |
| 36 ContactDatabaseTest() | |
| 37 : ui_thread_(BrowserThread::UI, &message_loop_), | |
| 38 db_(NULL) { | |
| 39 } | |
| 40 | |
| 41 virtual ~ContactDatabaseTest() { | |
| 42 } | |
| 43 | |
| 44 protected: | |
| 45 // testing::Test implementation. | |
| 46 virtual void SetUp() OVERRIDE { | |
| 47 CHECK(temp_dir_.CreateUniqueTempDir()); | |
| 48 CreateDatabase(); | |
| 49 } | |
| 50 | |
| 51 virtual void TearDown() OVERRIDE { | |
| 52 DestroyDatabase(); | |
| 53 } | |
| 54 | |
| 55 protected: | |
| 56 base::FilePath database_path() const { | |
| 57 return temp_dir_.path().Append(kDatabaseDirectoryName); | |
| 58 } | |
| 59 | |
| 60 void CreateDatabase() { | |
| 61 DestroyDatabase(); | |
| 62 db_ = new ContactDatabase; | |
| 63 db_->Init(database_path(), | |
| 64 base::Bind(&ContactDatabaseTest::OnDatabaseInitialized, | |
| 65 base::Unretained(this))); | |
| 66 | |
| 67 // The database will be initialized on the file thread; run the message loop | |
| 68 // until that happens. | |
| 69 message_loop_.Run(); | |
| 70 } | |
| 71 | |
| 72 void DestroyDatabase() { | |
| 73 if (db_) { | |
| 74 db_->DestroyOnUIThread(); | |
| 75 db_ = NULL; | |
| 76 } | |
| 77 } | |
| 78 | |
| 79 // Calls ContactDatabase::SaveContacts() and blocks until the operation is | |
| 80 // complete. | |
| 81 void SaveContacts(scoped_ptr<ContactPointers> contacts_to_save, | |
| 82 scoped_ptr<ContactDatabaseInterface::ContactIds> | |
| 83 contact_ids_to_delete, | |
| 84 scoped_ptr<UpdateMetadata> metadata, | |
| 85 bool is_full_update) { | |
| 86 CHECK(db_); | |
| 87 db_->SaveContacts(contacts_to_save.Pass(), | |
| 88 contact_ids_to_delete.Pass(), | |
| 89 metadata.Pass(), | |
| 90 is_full_update, | |
| 91 base::Bind(&ContactDatabaseTest::OnContactsSaved, | |
| 92 base::Unretained(this))); | |
| 93 message_loop_.Run(); | |
| 94 } | |
| 95 | |
| 96 // Calls ContactDatabase::LoadContacts() and blocks until the operation is | |
| 97 // complete. | |
| 98 void LoadContacts(scoped_ptr<ScopedVector<Contact> >* contacts_out, | |
| 99 scoped_ptr<UpdateMetadata>* metadata_out) { | |
| 100 CHECK(db_); | |
| 101 db_->LoadContacts(base::Bind(&ContactDatabaseTest::OnContactsLoaded, | |
| 102 base::Unretained(this))); | |
| 103 message_loop_.Run(); | |
| 104 contacts_out->swap(loaded_contacts_); | |
| 105 metadata_out->swap(loaded_metadata_); | |
| 106 } | |
| 107 | |
| 108 private: | |
| 109 void OnDatabaseInitialized(bool success) { | |
| 110 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 111 CHECK(success); | |
| 112 // TODO(derat): Move google_apis::test::RunBlockingPoolTask() to a shared | |
| 113 // location and use it for these tests. | |
| 114 message_loop_.Quit(); | |
| 115 } | |
| 116 | |
| 117 void OnContactsSaved(bool success) { | |
| 118 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 119 CHECK(success); | |
| 120 message_loop_.Quit(); | |
| 121 } | |
| 122 | |
| 123 void OnContactsLoaded(bool success, | |
| 124 scoped_ptr<ScopedVector<Contact> > contacts, | |
| 125 scoped_ptr<UpdateMetadata> metadata) { | |
| 126 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 127 CHECK(success); | |
| 128 loaded_contacts_.swap(contacts); | |
| 129 loaded_metadata_.swap(metadata); | |
| 130 message_loop_.Quit(); | |
| 131 } | |
| 132 | |
| 133 base::MessageLoopForUI message_loop_; | |
| 134 content::TestBrowserThread ui_thread_; | |
| 135 | |
| 136 // Temporary directory where the database is saved. | |
| 137 base::ScopedTempDir temp_dir_; | |
| 138 | |
| 139 // This class retains ownership of this object. | |
| 140 ContactDatabase* db_; | |
| 141 | |
| 142 // Contacts and metadata returned by the most-recent | |
| 143 // ContactDatabase::LoadContacts() call. Used to pass returned values from | |
| 144 // OnContactsLoaded() to LoadContacts(). | |
| 145 scoped_ptr<ScopedVector<Contact> > loaded_contacts_; | |
| 146 scoped_ptr<UpdateMetadata> loaded_metadata_; | |
| 147 | |
| 148 DISALLOW_COPY_AND_ASSIGN(ContactDatabaseTest); | |
| 149 }; | |
| 150 | |
| 151 TEST_F(ContactDatabaseTest, SaveAndReload) { | |
| 152 // Save a contact to the database and check that we get the same data back | |
| 153 // when loading it. | |
| 154 const std::string kContactId = "contact_id_1"; | |
| 155 scoped_ptr<Contact> contact(new Contact); | |
| 156 InitContact(kContactId, "1", false, contact.get()); | |
| 157 AddEmailAddress("email_1", Contact_AddressType_Relation_HOME, | |
| 158 "email_label_1", true, contact.get()); | |
| 159 AddEmailAddress("email_2", Contact_AddressType_Relation_WORK, | |
| 160 "", false, contact.get()); | |
| 161 AddPhoneNumber("123-456-7890", Contact_AddressType_Relation_HOME, | |
| 162 "phone_label", true, contact.get()); | |
| 163 AddPostalAddress("postal_1", Contact_AddressType_Relation_HOME, | |
| 164 "postal_label_1", true, contact.get()); | |
| 165 AddPostalAddress("postal_2", Contact_AddressType_Relation_OTHER, | |
| 166 "postal_label_2", false, contact.get()); | |
| 167 AddInstantMessagingAddress("im_1", | |
| 168 Contact_InstantMessagingAddress_Protocol_AIM, | |
| 169 Contact_AddressType_Relation_HOME, | |
| 170 "im_label_1", true, contact.get()); | |
| 171 SetPhoto(gfx::Size(20, 20), contact.get()); | |
| 172 scoped_ptr<ContactPointers> contacts_to_save(new ContactPointers); | |
| 173 contacts_to_save->push_back(contact.get()); | |
| 174 scoped_ptr<ContactDatabaseInterface::ContactIds> contact_ids_to_delete( | |
| 175 new ContactDatabaseInterface::ContactIds); | |
| 176 | |
| 177 const int64 kLastUpdateTime = 1234; | |
| 178 scoped_ptr<UpdateMetadata> metadata_to_save(new UpdateMetadata); | |
| 179 metadata_to_save->set_last_update_start_time(kLastUpdateTime); | |
| 180 | |
| 181 SaveContacts(contacts_to_save.Pass(), | |
| 182 contact_ids_to_delete.Pass(), | |
| 183 metadata_to_save.Pass(), | |
| 184 true); | |
| 185 scoped_ptr<ScopedVector<Contact> > loaded_contacts; | |
| 186 scoped_ptr<UpdateMetadata> loaded_metadata; | |
| 187 LoadContacts(&loaded_contacts, &loaded_metadata); | |
| 188 EXPECT_EQ(VarContactsToString(1, contact.get()), | |
| 189 ContactsToString(*loaded_contacts)); | |
| 190 EXPECT_EQ(kLastUpdateTime, loaded_metadata->last_update_start_time()); | |
| 191 | |
| 192 // Modify the contact, save it, and check that the loaded contact is also | |
| 193 // updated. | |
| 194 InitContact(kContactId, "2", false, contact.get()); | |
| 195 AddEmailAddress("email_3", Contact_AddressType_Relation_OTHER, | |
| 196 "email_label_2", true, contact.get()); | |
| 197 AddPhoneNumber("phone_2", Contact_AddressType_Relation_OTHER, | |
| 198 "phone_label_2", false, contact.get()); | |
| 199 AddPostalAddress("postal_3", Contact_AddressType_Relation_HOME, | |
| 200 "postal_label_3", true, contact.get()); | |
| 201 SetPhoto(gfx::Size(64, 64), contact.get()); | |
| 202 contacts_to_save.reset(new ContactPointers); | |
| 203 contacts_to_save->push_back(contact.get()); | |
| 204 contact_ids_to_delete.reset(new ContactDatabaseInterface::ContactIds); | |
| 205 metadata_to_save.reset(new UpdateMetadata); | |
| 206 const int64 kNewLastUpdateTime = 5678; | |
| 207 metadata_to_save->set_last_update_start_time(kNewLastUpdateTime); | |
| 208 SaveContacts(contacts_to_save.Pass(), | |
| 209 contact_ids_to_delete.Pass(), | |
| 210 metadata_to_save.Pass(), | |
| 211 true); | |
| 212 | |
| 213 LoadContacts(&loaded_contacts, &loaded_metadata); | |
| 214 EXPECT_EQ(VarContactsToString(1, contact.get()), | |
| 215 ContactsToString(*loaded_contacts)); | |
| 216 EXPECT_EQ(kNewLastUpdateTime, loaded_metadata->last_update_start_time()); | |
| 217 } | |
| 218 | |
| 219 TEST_F(ContactDatabaseTest, FullAndIncrementalUpdates) { | |
| 220 // Do a full update that inserts two contacts into the database. | |
| 221 const std::string kContactId1 = "contact_id_1"; | |
| 222 const std::string kSharedEmail = "foo@example.org"; | |
| 223 scoped_ptr<Contact> contact1(new Contact); | |
| 224 InitContact(kContactId1, "1", false, contact1.get()); | |
| 225 AddEmailAddress(kSharedEmail, Contact_AddressType_Relation_HOME, | |
| 226 "", true, contact1.get()); | |
| 227 | |
| 228 const std::string kContactId2 = "contact_id_2"; | |
| 229 scoped_ptr<Contact> contact2(new Contact); | |
| 230 InitContact(kContactId2, "2", false, contact2.get()); | |
| 231 AddEmailAddress(kSharedEmail, Contact_AddressType_Relation_WORK, | |
| 232 "", true, contact2.get()); | |
| 233 | |
| 234 scoped_ptr<ContactPointers> contacts_to_save(new ContactPointers); | |
| 235 contacts_to_save->push_back(contact1.get()); | |
| 236 contacts_to_save->push_back(contact2.get()); | |
| 237 scoped_ptr<ContactDatabaseInterface::ContactIds> contact_ids_to_delete( | |
| 238 new ContactDatabaseInterface::ContactIds); | |
| 239 scoped_ptr<UpdateMetadata> metadata_to_save(new UpdateMetadata); | |
| 240 SaveContacts(contacts_to_save.Pass(), | |
| 241 contact_ids_to_delete.Pass(), | |
| 242 metadata_to_save.Pass(), | |
| 243 true); | |
| 244 | |
| 245 scoped_ptr<ScopedVector<Contact> > loaded_contacts; | |
| 246 scoped_ptr<UpdateMetadata> loaded_metadata; | |
| 247 LoadContacts(&loaded_contacts, &loaded_metadata); | |
| 248 EXPECT_EQ(VarContactsToString(2, contact1.get(), contact2.get()), | |
| 249 ContactsToString(*loaded_contacts)); | |
| 250 | |
| 251 // Do an incremental update including just the second contact. | |
| 252 InitContact(kContactId2, "2b", false, contact2.get()); | |
| 253 AddPostalAddress("postal_1", Contact_AddressType_Relation_HOME, | |
| 254 "", true, contact2.get()); | |
| 255 contacts_to_save.reset(new ContactPointers); | |
| 256 contacts_to_save->push_back(contact2.get()); | |
| 257 contact_ids_to_delete.reset(new ContactDatabaseInterface::ContactIds); | |
| 258 metadata_to_save.reset(new UpdateMetadata); | |
| 259 SaveContacts(contacts_to_save.Pass(), | |
| 260 contact_ids_to_delete.Pass(), | |
| 261 metadata_to_save.Pass(), | |
| 262 false); | |
| 263 LoadContacts(&loaded_contacts, &loaded_metadata); | |
| 264 EXPECT_EQ(VarContactsToString(2, contact1.get(), contact2.get()), | |
| 265 ContactsToString(*loaded_contacts)); | |
| 266 | |
| 267 // Do an empty incremental update and check that the metadata is still | |
| 268 // updated. | |
| 269 contacts_to_save.reset(new ContactPointers); | |
| 270 contact_ids_to_delete.reset(new ContactDatabaseInterface::ContactIds); | |
| 271 metadata_to_save.reset(new UpdateMetadata); | |
| 272 const int64 kLastUpdateTime = 1234; | |
| 273 metadata_to_save->set_last_update_start_time(kLastUpdateTime); | |
| 274 SaveContacts(contacts_to_save.Pass(), | |
| 275 contact_ids_to_delete.Pass(), | |
| 276 metadata_to_save.Pass(), | |
| 277 false); | |
| 278 LoadContacts(&loaded_contacts, &loaded_metadata); | |
| 279 EXPECT_EQ(VarContactsToString(2, contact1.get(), contact2.get()), | |
| 280 ContactsToString(*loaded_contacts)); | |
| 281 EXPECT_EQ(kLastUpdateTime, loaded_metadata->last_update_start_time()); | |
| 282 | |
| 283 // Do a full update including just the first contact. The second contact | |
| 284 // should be removed from the database. | |
| 285 InitContact(kContactId1, "1b", false, contact1.get()); | |
| 286 AddPostalAddress("postal_2", Contact_AddressType_Relation_WORK, | |
| 287 "", true, contact1.get()); | |
| 288 AddPhoneNumber("phone", Contact_AddressType_Relation_HOME, | |
| 289 "", true, contact1.get()); | |
| 290 contacts_to_save.reset(new ContactPointers); | |
| 291 contacts_to_save->push_back(contact1.get()); | |
| 292 contact_ids_to_delete.reset(new ContactDatabaseInterface::ContactIds); | |
| 293 metadata_to_save.reset(new UpdateMetadata); | |
| 294 SaveContacts(contacts_to_save.Pass(), | |
| 295 contact_ids_to_delete.Pass(), | |
| 296 metadata_to_save.Pass(), | |
| 297 true); | |
| 298 LoadContacts(&loaded_contacts, &loaded_metadata); | |
| 299 EXPECT_EQ(VarContactsToString(1, contact1.get()), | |
| 300 ContactsToString(*loaded_contacts)); | |
| 301 | |
| 302 // Do a full update including no contacts. The database should be cleared. | |
| 303 contacts_to_save.reset(new ContactPointers); | |
| 304 contact_ids_to_delete.reset(new ContactDatabaseInterface::ContactIds); | |
| 305 metadata_to_save.reset(new UpdateMetadata); | |
| 306 SaveContacts(contacts_to_save.Pass(), | |
| 307 contact_ids_to_delete.Pass(), | |
| 308 metadata_to_save.Pass(), | |
| 309 true); | |
| 310 LoadContacts(&loaded_contacts, &loaded_metadata); | |
| 311 EXPECT_TRUE(loaded_contacts->empty()); | |
| 312 } | |
| 313 | |
| 314 // Test that we create a new database when we encounter a corrupted one. | |
| 315 TEST_F(ContactDatabaseTest, DeleteWhenCorrupt) { | |
| 316 DestroyDatabase(); | |
| 317 // Overwrite all of the files in the database with a space character. | |
| 318 base::FileEnumerator enumerator( | |
| 319 database_path(), false, base::FileEnumerator::FILES); | |
| 320 for (base::FilePath path = enumerator.Next(); !path.empty(); | |
| 321 path = enumerator.Next()) { | |
| 322 base::WriteFile(path, " ", 1); | |
| 323 } | |
| 324 CreateDatabase(); | |
| 325 | |
| 326 // Make sure that the resulting database is usable. | |
| 327 scoped_ptr<Contact> contact(new Contact); | |
| 328 InitContact("1", "1", false, contact.get()); | |
| 329 scoped_ptr<ContactPointers> contacts_to_save(new ContactPointers); | |
| 330 contacts_to_save->push_back(contact.get()); | |
| 331 scoped_ptr<ContactDatabaseInterface::ContactIds> contact_ids_to_delete( | |
| 332 new ContactDatabaseInterface::ContactIds); | |
| 333 scoped_ptr<UpdateMetadata> metadata_to_save(new UpdateMetadata); | |
| 334 SaveContacts(contacts_to_save.Pass(), | |
| 335 contact_ids_to_delete.Pass(), | |
| 336 metadata_to_save.Pass(), | |
| 337 true); | |
| 338 | |
| 339 scoped_ptr<ScopedVector<Contact> > loaded_contacts; | |
| 340 scoped_ptr<UpdateMetadata> loaded_metadata; | |
| 341 LoadContacts(&loaded_contacts, &loaded_metadata); | |
| 342 EXPECT_EQ(VarContactsToString(1, contact.get()), | |
| 343 ContactsToString(*loaded_contacts)); | |
| 344 } | |
| 345 | |
| 346 TEST_F(ContactDatabaseTest, DeleteRequestedContacts) { | |
| 347 // Insert two contacts into the database with a full update. | |
| 348 const std::string kContactId1 = "contact_id_1"; | |
| 349 scoped_ptr<Contact> contact1(new Contact); | |
| 350 InitContact(kContactId1, "1", false, contact1.get()); | |
| 351 const std::string kContactId2 = "contact_id_2"; | |
| 352 scoped_ptr<Contact> contact2(new Contact); | |
| 353 InitContact(kContactId2, "2", false, contact2.get()); | |
| 354 | |
| 355 scoped_ptr<ContactPointers> contacts_to_save(new ContactPointers); | |
| 356 contacts_to_save->push_back(contact1.get()); | |
| 357 contacts_to_save->push_back(contact2.get()); | |
| 358 scoped_ptr<ContactDatabaseInterface::ContactIds> contact_ids_to_delete( | |
| 359 new ContactDatabaseInterface::ContactIds); | |
| 360 scoped_ptr<UpdateMetadata> metadata_to_save(new UpdateMetadata); | |
| 361 SaveContacts(contacts_to_save.Pass(), | |
| 362 contact_ids_to_delete.Pass(), | |
| 363 metadata_to_save.Pass(), | |
| 364 true); | |
| 365 | |
| 366 // Do an incremental update that inserts a third contact and deletes the first | |
| 367 // contact. | |
| 368 const std::string kContactId3 = "contact_id_3"; | |
| 369 scoped_ptr<Contact> contact3(new Contact); | |
| 370 InitContact(kContactId3, "3", false, contact3.get()); | |
| 371 | |
| 372 contacts_to_save.reset(new ContactPointers); | |
| 373 contacts_to_save->push_back(contact3.get()); | |
| 374 contact_ids_to_delete.reset(new ContactDatabaseInterface::ContactIds); | |
| 375 contact_ids_to_delete->push_back(kContactId1); | |
| 376 metadata_to_save.reset(new UpdateMetadata); | |
| 377 SaveContacts(contacts_to_save.Pass(), | |
| 378 contact_ids_to_delete.Pass(), | |
| 379 metadata_to_save.Pass(), | |
| 380 false); | |
| 381 | |
| 382 // LoadContacts() should return only the second and third contacts. | |
| 383 scoped_ptr<ScopedVector<Contact> > loaded_contacts; | |
| 384 scoped_ptr<UpdateMetadata> loaded_metadata; | |
| 385 LoadContacts(&loaded_contacts, &loaded_metadata); | |
| 386 EXPECT_EQ(VarContactsToString(2, contact2.get(), contact3.get()), | |
| 387 ContactsToString(*loaded_contacts)); | |
| 388 | |
| 389 // Do another incremental update that deletes the second contact. | |
| 390 contacts_to_save.reset(new ContactPointers); | |
| 391 contact_ids_to_delete.reset(new ContactDatabaseInterface::ContactIds); | |
| 392 contact_ids_to_delete->push_back(kContactId2); | |
| 393 metadata_to_save.reset(new UpdateMetadata); | |
| 394 SaveContacts(contacts_to_save.Pass(), | |
| 395 contact_ids_to_delete.Pass(), | |
| 396 metadata_to_save.Pass(), | |
| 397 false); | |
| 398 LoadContacts(&loaded_contacts, &loaded_metadata); | |
| 399 EXPECT_EQ(VarContactsToString(1, contact3.get()), | |
| 400 ContactsToString(*loaded_contacts)); | |
| 401 | |
| 402 // Deleting a contact that isn't present should be a no-op. | |
| 403 contacts_to_save.reset(new ContactPointers); | |
| 404 contact_ids_to_delete.reset(new ContactDatabaseInterface::ContactIds); | |
| 405 contact_ids_to_delete->push_back("bogus_id"); | |
| 406 metadata_to_save.reset(new UpdateMetadata); | |
| 407 SaveContacts(contacts_to_save.Pass(), | |
| 408 contact_ids_to_delete.Pass(), | |
| 409 metadata_to_save.Pass(), | |
| 410 false); | |
| 411 LoadContacts(&loaded_contacts, &loaded_metadata); | |
| 412 EXPECT_EQ(VarContactsToString(1, contact3.get()), | |
| 413 ContactsToString(*loaded_contacts)); | |
| 414 } | |
| 415 | |
| 416 } // namespace test | |
| 417 } // namespace contacts | |
| OLD | NEW |