- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / chromeos / contacts / contact_database_unittest.cc
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     file_util::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