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