• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2011 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/sync/syncable/syncable.h"
6 
7 #include "build/build_config.h"
8 
9 #include <sys/types.h>
10 
11 #include <limits>
12 #include <string>
13 
14 #if !defined(OS_WIN)
15 #define MAX_PATH PATH_MAX
16 #include <ostream>
17 #include <stdio.h>
18 #include <sys/ipc.h>
19 #include <sys/sem.h>
20 #include <sys/times.h>
21 #endif  // !defined(OS_WIN)
22 
23 #include "base/file_path.h"
24 #include "base/file_util.h"
25 #include "base/logging.h"
26 #include "base/memory/scoped_ptr.h"
27 #include "base/memory/scoped_temp_dir.h"
28 #include "base/string_util.h"
29 #include "base/threading/platform_thread.h"
30 #include "base/values.h"
31 #include "chrome/browser/sync/engine/syncproto.h"
32 #include "chrome/browser/sync/protocol/bookmark_specifics.pb.h"
33 #include "chrome/browser/sync/syncable/directory_backing_store.h"
34 #include "chrome/browser/sync/syncable/directory_manager.h"
35 #include "chrome/common/deprecated/event_sys-inl.h"
36 #include "chrome/test/sync/engine/test_id_factory.h"
37 #include "chrome/test/sync/engine/test_syncable_utils.h"
38 #include "chrome/test/values_test_util.h"
39 #include "testing/gtest/include/gtest/gtest.h"
40 #include "third_party/sqlite/sqlite3.h"
41 
42 using browser_sync::TestIdFactory;
43 using test::ExpectBooleanValue;
44 using test::ExpectStringValue;
45 
46 namespace syncable {
47 
48 class SyncableKernelTest : public testing::Test {};
49 
TEST_F(SyncableKernelTest,ToValue)50 TEST_F(SyncableKernelTest, ToValue) {
51   EntryKernel kernel;
52   scoped_ptr<DictionaryValue> value(kernel.ToValue());
53   if (value.get()) {
54     // Not much to check without repeating the ToValue() code.
55     EXPECT_TRUE(value->HasKey("isDirty"));
56     // The extra +1 is for "isDirty".
57     EXPECT_EQ(BIT_TEMPS_END - BEGIN_FIELDS + 1,
58               static_cast<int>(value->size()));
59   } else {
60     ADD_FAILURE();
61   }
62 }
63 
64 namespace {
PutDataAsBookmarkFavicon(WriteTransaction * wtrans,MutableEntry * e,const char * bytes,size_t bytes_length)65 void PutDataAsBookmarkFavicon(WriteTransaction* wtrans,
66                               MutableEntry* e,
67                               const char* bytes,
68                               size_t bytes_length) {
69   sync_pb::EntitySpecifics specifics;
70   specifics.MutableExtension(sync_pb::bookmark)->set_url("http://demo/");
71   specifics.MutableExtension(sync_pb::bookmark)->set_favicon(bytes,
72       bytes_length);
73   e->Put(SPECIFICS, specifics);
74 }
75 
ExpectDataFromBookmarkFaviconEquals(BaseTransaction * trans,Entry * e,const char * bytes,size_t bytes_length)76 void ExpectDataFromBookmarkFaviconEquals(BaseTransaction* trans,
77                                          Entry* e,
78                                          const char* bytes,
79                                          size_t bytes_length) {
80   ASSERT_TRUE(e->good());
81   ASSERT_TRUE(e->Get(SPECIFICS).HasExtension(sync_pb::bookmark));
82   ASSERT_EQ("http://demo/",
83       e->Get(SPECIFICS).GetExtension(sync_pb::bookmark).url());
84   ASSERT_EQ(std::string(bytes, bytes_length),
85       e->Get(SPECIFICS).GetExtension(sync_pb::bookmark).favicon());
86 }
87 }  // namespace
88 
89 class SyncableGeneralTest : public testing::Test {
90  public:
SetUp()91   virtual void SetUp() {
92     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
93     db_path_ = temp_dir_.path().Append(
94         FILE_PATH_LITERAL("SyncableTest.sqlite3"));
95   }
96 
TearDown()97   virtual void TearDown() {
98   }
99  protected:
100   ScopedTempDir temp_dir_;
101   FilePath db_path_;
102 };
103 
TEST_F(SyncableGeneralTest,General)104 TEST_F(SyncableGeneralTest, General) {
105   Directory dir;
106   dir.Open(db_path_, "SimpleTest");
107 
108   int64 written_metahandle;
109   const Id id = TestIdFactory::FromNumber(99);
110   std::string name = "Jeff";
111   // Test simple read operations on an empty DB.
112   {
113     ReadTransaction rtrans(&dir, __FILE__, __LINE__);
114     Entry e(&rtrans, GET_BY_ID, id);
115     ASSERT_FALSE(e.good());  // Hasn't been written yet.
116 
117     Directory::ChildHandles child_handles;
118     dir.GetChildHandles(&rtrans, rtrans.root_id(), &child_handles);
119     EXPECT_TRUE(child_handles.empty());
120   }
121 
122   // Test creating a new meta entry.
123   {
124     WriteTransaction wtrans(&dir, UNITTEST, __FILE__, __LINE__);
125     MutableEntry me(&wtrans, CREATE, wtrans.root_id(), name);
126     ASSERT_TRUE(me.good());
127     me.Put(ID, id);
128     me.Put(BASE_VERSION, 1);
129     written_metahandle = me.Get(META_HANDLE);
130   }
131 
132   // Test GetChildHandles after something is now in the DB.
133   // Also check that GET_BY_ID works.
134   {
135     ReadTransaction rtrans(&dir, __FILE__, __LINE__);
136     Entry e(&rtrans, GET_BY_ID, id);
137     ASSERT_TRUE(e.good());
138 
139     Directory::ChildHandles child_handles;
140     dir.GetChildHandles(&rtrans, rtrans.root_id(), &child_handles);
141     EXPECT_EQ(1u, child_handles.size());
142 
143     for (Directory::ChildHandles::iterator i = child_handles.begin();
144          i != child_handles.end(); ++i) {
145       EXPECT_EQ(*i, written_metahandle);
146     }
147   }
148 
149   // Test writing data to an entity. Also check that GET_BY_HANDLE works.
150   static const char s[] = "Hello World.";
151   {
152     WriteTransaction trans(&dir, UNITTEST, __FILE__, __LINE__);
153     MutableEntry e(&trans, GET_BY_HANDLE, written_metahandle);
154     ASSERT_TRUE(e.good());
155     PutDataAsBookmarkFavicon(&trans, &e, s, sizeof(s));
156   }
157 
158   // Test reading back the contents that we just wrote.
159   {
160     WriteTransaction trans(&dir, UNITTEST, __FILE__, __LINE__);
161     MutableEntry e(&trans, GET_BY_HANDLE, written_metahandle);
162     ASSERT_TRUE(e.good());
163     ExpectDataFromBookmarkFaviconEquals(&trans, &e, s, sizeof(s));
164   }
165 
166   // Verify it exists in the folder.
167   {
168     ReadTransaction rtrans(&dir, __FILE__, __LINE__);
169     EXPECT_EQ(1, CountEntriesWithName(&rtrans, rtrans.root_id(), name));
170   }
171 
172   // Now delete it.
173   {
174     WriteTransaction trans(&dir, UNITTEST, __FILE__, __LINE__);
175     MutableEntry e(&trans, GET_BY_HANDLE, written_metahandle);
176     e.Put(IS_DEL, true);
177 
178     EXPECT_EQ(0, CountEntriesWithName(&trans, trans.root_id(), name));
179   }
180 
181   dir.SaveChanges();
182 }
183 
TEST_F(SyncableGeneralTest,ClientIndexRebuildsProperly)184 TEST_F(SyncableGeneralTest, ClientIndexRebuildsProperly) {
185   int64 written_metahandle;
186   TestIdFactory factory;
187   const Id id = factory.NewServerId();
188   std::string name = "cheesepuffs";
189   std::string tag = "dietcoke";
190 
191   // Test creating a new meta entry.
192   {
193     Directory dir;
194     dir.Open(db_path_, "IndexTest");
195     {
196       WriteTransaction wtrans(&dir, UNITTEST, __FILE__, __LINE__);
197       MutableEntry me(&wtrans, CREATE, wtrans.root_id(), name);
198       ASSERT_TRUE(me.good());
199       me.Put(ID, id);
200       me.Put(BASE_VERSION, 1);
201       me.Put(UNIQUE_CLIENT_TAG, tag);
202       written_metahandle = me.Get(META_HANDLE);
203     }
204     dir.SaveChanges();
205   }
206 
207   // The DB was closed. Now reopen it. This will cause index regeneration.
208   {
209     Directory dir;
210     dir.Open(db_path_, "IndexTest");
211 
212     ReadTransaction trans(&dir, __FILE__, __LINE__);
213     Entry me(&trans, GET_BY_CLIENT_TAG, tag);
214     ASSERT_TRUE(me.good());
215     EXPECT_EQ(me.Get(ID), id);
216     EXPECT_EQ(me.Get(BASE_VERSION), 1);
217     EXPECT_EQ(me.Get(UNIQUE_CLIENT_TAG), tag);
218     EXPECT_EQ(me.Get(META_HANDLE), written_metahandle);
219   }
220 }
221 
TEST_F(SyncableGeneralTest,ClientIndexRebuildsDeletedProperly)222 TEST_F(SyncableGeneralTest, ClientIndexRebuildsDeletedProperly) {
223   TestIdFactory factory;
224   const Id id = factory.NewServerId();
225   std::string tag = "dietcoke";
226 
227   // Test creating a deleted, unsynced, server meta entry.
228   {
229     Directory dir;
230     dir.Open(db_path_, "IndexTest");
231     {
232       WriteTransaction wtrans(&dir, UNITTEST, __FILE__, __LINE__);
233       MutableEntry me(&wtrans, CREATE, wtrans.root_id(), "deleted");
234       ASSERT_TRUE(me.good());
235       me.Put(ID, id);
236       me.Put(BASE_VERSION, 1);
237       me.Put(UNIQUE_CLIENT_TAG, tag);
238       me.Put(IS_DEL, true);
239       me.Put(IS_UNSYNCED, true);  // Or it might be purged.
240     }
241     dir.SaveChanges();
242   }
243 
244   // The DB was closed. Now reopen it. This will cause index regeneration.
245   // Should still be present and valid in the client tag index.
246   {
247     Directory dir;
248     dir.Open(db_path_, "IndexTest");
249 
250     ReadTransaction trans(&dir, __FILE__, __LINE__);
251     Entry me(&trans, GET_BY_CLIENT_TAG, tag);
252     ASSERT_TRUE(me.good());
253     EXPECT_EQ(me.Get(ID), id);
254     EXPECT_EQ(me.Get(UNIQUE_CLIENT_TAG), tag);
255     EXPECT_TRUE(me.Get(IS_DEL));
256     EXPECT_TRUE(me.Get(IS_UNSYNCED));
257   }
258 }
259 
TEST_F(SyncableGeneralTest,ToValue)260 TEST_F(SyncableGeneralTest, ToValue) {
261   Directory dir;
262   dir.Open(db_path_, "SimpleTest");
263 
264   const Id id = TestIdFactory::FromNumber(99);
265   {
266     ReadTransaction rtrans(&dir, __FILE__, __LINE__);
267     Entry e(&rtrans, GET_BY_ID, id);
268     EXPECT_FALSE(e.good());  // Hasn't been written yet.
269 
270     scoped_ptr<DictionaryValue> value(e.ToValue());
271     ExpectBooleanValue(false, *value, "good");
272     EXPECT_EQ(1u, value->size());
273   }
274 
275   // Test creating a new meta entry.
276   {
277     WriteTransaction wtrans(&dir, UNITTEST, __FILE__, __LINE__);
278     MutableEntry me(&wtrans, CREATE, wtrans.root_id(), "new");
279     ASSERT_TRUE(me.good());
280     me.Put(ID, id);
281     me.Put(BASE_VERSION, 1);
282 
283     scoped_ptr<DictionaryValue> value(me.ToValue());
284     ExpectBooleanValue(true, *value, "good");
285     EXPECT_TRUE(value->HasKey("kernel"));
286     ExpectStringValue("Unspecified", *value, "serverModelType");
287     ExpectStringValue("Unspecified", *value, "modelType");
288     ExpectBooleanValue(false, *value, "shouldMaintainPosition");
289     ExpectBooleanValue(true, *value, "existsOnClientBecauseNameIsNonEmpty");
290     ExpectBooleanValue(false, *value, "isRoot");
291   }
292 
293   dir.SaveChanges();
294 }
295 
296 // A Directory whose backing store always fails SaveChanges by returning false.
297 class TestUnsaveableDirectory : public Directory {
298  public:
299   class UnsaveableBackingStore : public DirectoryBackingStore {
300    public:
UnsaveableBackingStore(const std::string & dir_name,const FilePath & backing_filepath)301      UnsaveableBackingStore(const std::string& dir_name,
302                             const FilePath& backing_filepath)
303          : DirectoryBackingStore(dir_name, backing_filepath) { }
SaveChanges(const Directory::SaveChangesSnapshot & snapshot)304      virtual bool SaveChanges(const Directory::SaveChangesSnapshot& snapshot) {
305        return false;
306      }
307   };
CreateBackingStore(const std::string & dir_name,const FilePath & backing_filepath)308   virtual DirectoryBackingStore* CreateBackingStore(
309       const std::string& dir_name,
310       const FilePath& backing_filepath) {
311     return new UnsaveableBackingStore(dir_name, backing_filepath);
312   }
313 };
314 
315 // Test suite for syncable::Directory.
316 class SyncableDirectoryTest : public testing::Test {
317  protected:
318   static const FilePath::CharType kFilePath[];
319   static const char kName[];
320   static const Id kId;
321 
322   // SetUp() is called before each test case is run.
323   // The sqlite3 DB is deleted before each test is run.
SetUp()324   virtual void SetUp() {
325     file_path_ = FilePath(kFilePath);
326     file_util::Delete(file_path_, true);
327     dir_.reset(new Directory());
328     ASSERT_TRUE(dir_.get());
329     ASSERT_TRUE(OPENED == dir_->Open(file_path_, kName));
330     ASSERT_TRUE(dir_->good());
331   }
332 
TearDown()333   virtual void TearDown() {
334     // This also closes file handles.
335     dir_->SaveChanges();
336     dir_.reset();
337     file_util::Delete(file_path_, true);
338   }
339 
ReloadDir()340   void ReloadDir() {
341     dir_.reset(new Directory());
342     ASSERT_TRUE(dir_.get());
343     ASSERT_TRUE(OPENED == dir_->Open(file_path_, kName));
344   }
345 
SaveAndReloadDir()346   void SaveAndReloadDir() {
347     dir_->SaveChanges();
348     ReloadDir();
349   }
350 
IsInDirtyMetahandles(int64 metahandle)351   bool IsInDirtyMetahandles(int64 metahandle) {
352     return 1 == dir_->kernel_->dirty_metahandles->count(metahandle);
353   }
354 
IsInMetahandlesToPurge(int64 metahandle)355   bool IsInMetahandlesToPurge(int64 metahandle) {
356     return 1 == dir_->kernel_->metahandles_to_purge->count(metahandle);
357   }
358 
CheckPurgeEntriesWithTypeInSucceeded(const ModelTypeSet & types_to_purge,bool before_reload)359   void CheckPurgeEntriesWithTypeInSucceeded(const ModelTypeSet& types_to_purge,
360                                             bool before_reload) {
361     SCOPED_TRACE(testing::Message("Before reload: ") << before_reload);
362     {
363       ReadTransaction trans(dir_.get(), __FILE__, __LINE__);
364       MetahandleSet all_set;
365       dir_->GetAllMetaHandles(&trans, &all_set);
366       EXPECT_EQ(3U, all_set.size());
367       if (before_reload)
368         EXPECT_EQ(4U, dir_->kernel_->metahandles_to_purge->size());
369       for (MetahandleSet::iterator iter = all_set.begin();
370            iter != all_set.end(); ++iter) {
371         Entry e(&trans, GET_BY_HANDLE, *iter);
372         if ((types_to_purge.count(e.GetModelType()) ||
373              types_to_purge.count(e.GetServerModelType()))) {
374           FAIL() << "Illegal type should have been deleted.";
375         }
376       }
377     }
378 
379     EXPECT_FALSE(dir_->initial_sync_ended_for_type(PREFERENCES));
380     EXPECT_FALSE(dir_->initial_sync_ended_for_type(AUTOFILL));
381     EXPECT_TRUE(dir_->initial_sync_ended_for_type(BOOKMARKS));
382   }
383 
384   scoped_ptr<Directory> dir_;
385   FilePath file_path_;
386 
387   // Creates an empty entry and sets the ID field to the default kId.
CreateEntry(const std::string & entryname)388   void CreateEntry(const std::string& entryname) {
389     CreateEntry(entryname, kId);
390   }
391 
392   // Creates an empty entry and sets the ID field to id.
CreateEntry(const std::string & entryname,const int id)393   void CreateEntry(const std::string& entryname, const int id) {
394     CreateEntry(entryname, TestIdFactory::FromNumber(id));
395   }
CreateEntry(const std::string & entryname,Id id)396   void CreateEntry(const std::string& entryname, Id id) {
397     WriteTransaction wtrans(dir_.get(), UNITTEST, __FILE__, __LINE__);
398     MutableEntry me(&wtrans, CREATE, wtrans.root_id(), entryname);
399     ASSERT_TRUE(me.good());
400     me.Put(ID, id);
401     me.Put(IS_UNSYNCED, true);
402   }
403 
404   void ValidateEntry(BaseTransaction* trans,
405                      int64 id,
406                      bool check_name,
407                      const std::string& name,
408                      int64 base_version,
409                      int64 server_version,
410                      bool is_del);
411 };
412 
TEST_F(SyncableDirectoryTest,TakeSnapshotGetsMetahandlesToPurge)413 TEST_F(SyncableDirectoryTest, TakeSnapshotGetsMetahandlesToPurge) {
414   const int metas_to_create = 50;
415   MetahandleSet expected_purges;
416   MetahandleSet all_handles;
417   {
418     WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__);
419     for (int i = 0; i < metas_to_create; i++) {
420       MutableEntry e(&trans, CREATE, trans.root_id(), "foo");
421       e.Put(IS_UNSYNCED, true);
422       sync_pb::EntitySpecifics specs;
423       if (i % 2 == 0) {
424         AddDefaultExtensionValue(BOOKMARKS, &specs);
425         expected_purges.insert(e.Get(META_HANDLE));
426         all_handles.insert(e.Get(META_HANDLE));
427       } else {
428         AddDefaultExtensionValue(PREFERENCES, &specs);
429         all_handles.insert(e.Get(META_HANDLE));
430       }
431       e.Put(SPECIFICS, specs);
432       e.Put(SERVER_SPECIFICS, specs);
433     }
434   }
435 
436   ModelTypeSet to_purge;
437   to_purge.insert(BOOKMARKS);
438   dir_->PurgeEntriesWithTypeIn(to_purge);
439 
440   Directory::SaveChangesSnapshot snapshot1;
441   base::AutoLock scoped_lock(dir_->kernel_->save_changes_mutex);
442   dir_->TakeSnapshotForSaveChanges(&snapshot1);
443   EXPECT_TRUE(expected_purges == snapshot1.metahandles_to_purge);
444 
445   to_purge.clear();
446   to_purge.insert(PREFERENCES);
447   dir_->PurgeEntriesWithTypeIn(to_purge);
448 
449   dir_->HandleSaveChangesFailure(snapshot1);
450 
451   Directory::SaveChangesSnapshot snapshot2;
452   dir_->TakeSnapshotForSaveChanges(&snapshot2);
453   EXPECT_TRUE(all_handles == snapshot2.metahandles_to_purge);
454 }
455 
TEST_F(SyncableDirectoryTest,TakeSnapshotGetsAllDirtyHandlesTest)456 TEST_F(SyncableDirectoryTest, TakeSnapshotGetsAllDirtyHandlesTest) {
457   const int metahandles_to_create = 100;
458   std::vector<int64> expected_dirty_metahandles;
459   {
460     WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__);
461     for (int i = 0; i < metahandles_to_create; i++) {
462       MutableEntry e(&trans, CREATE, trans.root_id(), "foo");
463       expected_dirty_metahandles.push_back(e.Get(META_HANDLE));
464       e.Put(IS_UNSYNCED, true);
465     }
466   }
467   // Fake SaveChanges() and make sure we got what we expected.
468   {
469     Directory::SaveChangesSnapshot snapshot;
470     base::AutoLock scoped_lock(dir_->kernel_->save_changes_mutex);
471     dir_->TakeSnapshotForSaveChanges(&snapshot);
472     // Make sure there's an entry for each new metahandle.  Make sure all
473     // entries are marked dirty.
474     ASSERT_EQ(expected_dirty_metahandles.size(), snapshot.dirty_metas.size());
475     for (OriginalEntries::const_iterator i = snapshot.dirty_metas.begin();
476         i != snapshot.dirty_metas.end(); ++i) {
477       ASSERT_TRUE(i->is_dirty());
478     }
479     dir_->VacuumAfterSaveChanges(snapshot);
480   }
481   // Put a new value with existing transactions as well as adding new ones.
482   {
483     WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__);
484     std::vector<int64> new_dirty_metahandles;
485     for (std::vector<int64>::const_iterator i =
486         expected_dirty_metahandles.begin();
487         i != expected_dirty_metahandles.end(); ++i) {
488         // Change existing entries to directories to dirty them.
489         MutableEntry e1(&trans, GET_BY_HANDLE, *i);
490         e1.Put(IS_DIR, true);
491         e1.Put(IS_UNSYNCED, true);
492         // Add new entries
493         MutableEntry e2(&trans, CREATE, trans.root_id(), "bar");
494         e2.Put(IS_UNSYNCED, true);
495         new_dirty_metahandles.push_back(e2.Get(META_HANDLE));
496     }
497     expected_dirty_metahandles.insert(expected_dirty_metahandles.end(),
498         new_dirty_metahandles.begin(), new_dirty_metahandles.end());
499   }
500   // Fake SaveChanges() and make sure we got what we expected.
501   {
502     Directory::SaveChangesSnapshot snapshot;
503     base::AutoLock scoped_lock(dir_->kernel_->save_changes_mutex);
504     dir_->TakeSnapshotForSaveChanges(&snapshot);
505     // Make sure there's an entry for each new metahandle.  Make sure all
506     // entries are marked dirty.
507     EXPECT_EQ(expected_dirty_metahandles.size(), snapshot.dirty_metas.size());
508     for (OriginalEntries::const_iterator i = snapshot.dirty_metas.begin();
509         i != snapshot.dirty_metas.end(); ++i) {
510       EXPECT_TRUE(i->is_dirty());
511     }
512     dir_->VacuumAfterSaveChanges(snapshot);
513   }
514 }
515 
TEST_F(SyncableDirectoryTest,TestPurgeEntriesWithTypeIn)516 TEST_F(SyncableDirectoryTest, TestPurgeEntriesWithTypeIn) {
517   sync_pb::EntitySpecifics bookmark_specs;
518   sync_pb::EntitySpecifics autofill_specs;
519   sync_pb::EntitySpecifics preference_specs;
520   AddDefaultExtensionValue(BOOKMARKS, &bookmark_specs);
521   AddDefaultExtensionValue(PREFERENCES, &preference_specs);
522   AddDefaultExtensionValue(AUTOFILL, &autofill_specs);
523   dir_->set_initial_sync_ended_for_type(BOOKMARKS, true);
524   dir_->set_initial_sync_ended_for_type(PREFERENCES, true);
525   dir_->set_initial_sync_ended_for_type(AUTOFILL, true);
526 
527   std::set<ModelType> types_to_purge;
528   types_to_purge.insert(PREFERENCES);
529   types_to_purge.insert(AUTOFILL);
530 
531   TestIdFactory id_factory;
532   // Create some items for each type.
533   {
534     WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__);
535     MutableEntry item1(&trans, CREATE, trans.root_id(), "Item");
536     ASSERT_TRUE(item1.good());
537     item1.Put(SPECIFICS, bookmark_specs);
538     item1.Put(SERVER_SPECIFICS, bookmark_specs);
539     item1.Put(IS_UNSYNCED, true);
540 
541     MutableEntry item2(&trans, CREATE_NEW_UPDATE_ITEM,
542                        id_factory.NewServerId());
543     ASSERT_TRUE(item2.good());
544     item2.Put(SERVER_SPECIFICS, bookmark_specs);
545     item2.Put(IS_UNAPPLIED_UPDATE, true);
546 
547     MutableEntry item3(&trans, CREATE, trans.root_id(), "Item");
548     ASSERT_TRUE(item3.good());
549     item3.Put(SPECIFICS, preference_specs);
550     item3.Put(SERVER_SPECIFICS, preference_specs);
551     item3.Put(IS_UNSYNCED, true);
552 
553     MutableEntry item4(&trans, CREATE_NEW_UPDATE_ITEM,
554                        id_factory.NewServerId());
555     ASSERT_TRUE(item4.good());
556     item4.Put(SERVER_SPECIFICS, preference_specs);
557     item4.Put(IS_UNAPPLIED_UPDATE, true);
558 
559     MutableEntry item5(&trans, CREATE, trans.root_id(), "Item");
560     ASSERT_TRUE(item5.good());
561     item5.Put(SPECIFICS, autofill_specs);
562     item5.Put(SERVER_SPECIFICS, autofill_specs);
563     item5.Put(IS_UNSYNCED, true);
564 
565     MutableEntry item6(&trans, CREATE_NEW_UPDATE_ITEM,
566       id_factory.NewServerId());
567     ASSERT_TRUE(item6.good());
568     item6.Put(SERVER_SPECIFICS, autofill_specs);
569     item6.Put(IS_UNAPPLIED_UPDATE, true);
570   }
571 
572   dir_->SaveChanges();
573   {
574     ReadTransaction trans(dir_.get(), __FILE__, __LINE__);
575     MetahandleSet all_set;
576     dir_->GetAllMetaHandles(&trans, &all_set);
577     ASSERT_EQ(7U, all_set.size());
578   }
579 
580   dir_->PurgeEntriesWithTypeIn(types_to_purge);
581 
582   // We first query the in-memory data, and then reload the directory (without
583   // saving) to verify that disk does not still have the data.
584   CheckPurgeEntriesWithTypeInSucceeded(types_to_purge, true);
585   SaveAndReloadDir();
586   CheckPurgeEntriesWithTypeInSucceeded(types_to_purge, false);
587 }
588 
TEST_F(SyncableDirectoryTest,TakeSnapshotGetsOnlyDirtyHandlesTest)589 TEST_F(SyncableDirectoryTest, TakeSnapshotGetsOnlyDirtyHandlesTest) {
590   const int metahandles_to_create = 100;
591 
592   // half of 2 * metahandles_to_create
593   const unsigned int number_changed = 100u;
594   std::vector<int64> expected_dirty_metahandles;
595   {
596     WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__);
597     for (int i = 0; i < metahandles_to_create; i++) {
598       MutableEntry e(&trans, CREATE, trans.root_id(), "foo");
599       expected_dirty_metahandles.push_back(e.Get(META_HANDLE));
600       e.Put(IS_UNSYNCED, true);
601     }
602   }
603   dir_->SaveChanges();
604   // Put a new value with existing transactions as well as adding new ones.
605   {
606     WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__);
607     std::vector<int64> new_dirty_metahandles;
608     for (std::vector<int64>::const_iterator i =
609         expected_dirty_metahandles.begin();
610         i != expected_dirty_metahandles.end(); ++i) {
611         // Change existing entries to directories to dirty them.
612         MutableEntry e1(&trans, GET_BY_HANDLE, *i);
613         ASSERT_TRUE(e1.good());
614         e1.Put(IS_DIR, true);
615         e1.Put(IS_UNSYNCED, true);
616         // Add new entries
617         MutableEntry e2(&trans, CREATE, trans.root_id(), "bar");
618         e2.Put(IS_UNSYNCED, true);
619         new_dirty_metahandles.push_back(e2.Get(META_HANDLE));
620     }
621     expected_dirty_metahandles.insert(expected_dirty_metahandles.end(),
622         new_dirty_metahandles.begin(), new_dirty_metahandles.end());
623   }
624   dir_->SaveChanges();
625   // Don't make any changes whatsoever and ensure nothing comes back.
626   {
627     WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__);
628     for (std::vector<int64>::const_iterator i =
629         expected_dirty_metahandles.begin();
630         i != expected_dirty_metahandles.end(); ++i) {
631       MutableEntry e(&trans, GET_BY_HANDLE, *i);
632       ASSERT_TRUE(e.good());
633       // We aren't doing anything to dirty these entries.
634     }
635   }
636   // Fake SaveChanges() and make sure we got what we expected.
637   {
638     Directory::SaveChangesSnapshot snapshot;
639     base::AutoLock scoped_lock(dir_->kernel_->save_changes_mutex);
640     dir_->TakeSnapshotForSaveChanges(&snapshot);
641     // Make sure there are no dirty_metahandles.
642     EXPECT_EQ(0u, snapshot.dirty_metas.size());
643     dir_->VacuumAfterSaveChanges(snapshot);
644   }
645   {
646     WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__);
647     bool should_change = false;
648     for (std::vector<int64>::const_iterator i =
649         expected_dirty_metahandles.begin();
650         i != expected_dirty_metahandles.end(); ++i) {
651         // Maybe change entries by flipping IS_DIR.
652         MutableEntry e(&trans, GET_BY_HANDLE, *i);
653         ASSERT_TRUE(e.good());
654         should_change = !should_change;
655         if (should_change) {
656           bool not_dir = !e.Get(IS_DIR);
657           e.Put(IS_DIR, not_dir);
658           e.Put(IS_UNSYNCED, true);
659         }
660     }
661   }
662   // Fake SaveChanges() and make sure we got what we expected.
663   {
664     Directory::SaveChangesSnapshot snapshot;
665     base::AutoLock scoped_lock(dir_->kernel_->save_changes_mutex);
666     dir_->TakeSnapshotForSaveChanges(&snapshot);
667     // Make sure there's an entry for each changed metahandle.  Make sure all
668     // entries are marked dirty.
669     EXPECT_EQ(number_changed, snapshot.dirty_metas.size());
670     for (OriginalEntries::const_iterator i = snapshot.dirty_metas.begin();
671         i != snapshot.dirty_metas.end(); ++i) {
672       EXPECT_TRUE(i->is_dirty());
673     }
674     dir_->VacuumAfterSaveChanges(snapshot);
675   }
676 }
677 
678 const FilePath::CharType SyncableDirectoryTest::kFilePath[] =
679     FILE_PATH_LITERAL("Test.sqlite3");
680 const char SyncableDirectoryTest::kName[] = "Foo";
681 const Id SyncableDirectoryTest::kId(TestIdFactory::FromNumber(-99));
682 
683 namespace {
TEST_F(SyncableDirectoryTest,TestBasicLookupNonExistantID)684 TEST_F(SyncableDirectoryTest, TestBasicLookupNonExistantID) {
685   ReadTransaction rtrans(dir_.get(), __FILE__, __LINE__);
686   Entry e(&rtrans, GET_BY_ID, kId);
687   ASSERT_FALSE(e.good());
688 }
689 
TEST_F(SyncableDirectoryTest,TestBasicLookupValidID)690 TEST_F(SyncableDirectoryTest, TestBasicLookupValidID) {
691   CreateEntry("rtc");
692   ReadTransaction rtrans(dir_.get(), __FILE__, __LINE__);
693   Entry e(&rtrans, GET_BY_ID, kId);
694   ASSERT_TRUE(e.good());
695 }
696 
TEST_F(SyncableDirectoryTest,TestDelete)697 TEST_F(SyncableDirectoryTest, TestDelete) {
698   std::string name = "peanut butter jelly time";
699   WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__);
700   MutableEntry e1(&trans, CREATE, trans.root_id(), name);
701   ASSERT_TRUE(e1.good());
702   ASSERT_TRUE(e1.Put(IS_DEL, true));
703   MutableEntry e2(&trans, CREATE, trans.root_id(), name);
704   ASSERT_TRUE(e2.good());
705   ASSERT_TRUE(e2.Put(IS_DEL, true));
706   MutableEntry e3(&trans, CREATE, trans.root_id(), name);
707   ASSERT_TRUE(e3.good());
708   ASSERT_TRUE(e3.Put(IS_DEL, true));
709 
710   ASSERT_TRUE(e1.Put(IS_DEL, false));
711   ASSERT_TRUE(e2.Put(IS_DEL, false));
712   ASSERT_TRUE(e3.Put(IS_DEL, false));
713 
714   ASSERT_TRUE(e1.Put(IS_DEL, true));
715   ASSERT_TRUE(e2.Put(IS_DEL, true));
716   ASSERT_TRUE(e3.Put(IS_DEL, true));
717 }
718 
TEST_F(SyncableDirectoryTest,TestGetUnsynced)719 TEST_F(SyncableDirectoryTest, TestGetUnsynced) {
720   Directory::UnsyncedMetaHandles handles;
721   int64 handle1, handle2;
722   {
723     WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__);
724 
725     dir_->GetUnsyncedMetaHandles(&trans, &handles);
726     ASSERT_TRUE(0 == handles.size());
727 
728     MutableEntry e1(&trans, CREATE, trans.root_id(), "abba");
729     ASSERT_TRUE(e1.good());
730     handle1 = e1.Get(META_HANDLE);
731     e1.Put(BASE_VERSION, 1);
732     e1.Put(IS_DIR, true);
733     e1.Put(ID, TestIdFactory::FromNumber(101));
734 
735     MutableEntry e2(&trans, CREATE, e1.Get(ID), "bread");
736     ASSERT_TRUE(e2.good());
737     handle2 = e2.Get(META_HANDLE);
738     e2.Put(BASE_VERSION, 1);
739     e2.Put(ID, TestIdFactory::FromNumber(102));
740   }
741   dir_->SaveChanges();
742   {
743     WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__);
744 
745     dir_->GetUnsyncedMetaHandles(&trans, &handles);
746     ASSERT_TRUE(0 == handles.size());
747 
748     MutableEntry e3(&trans, GET_BY_HANDLE, handle1);
749     ASSERT_TRUE(e3.good());
750     e3.Put(IS_UNSYNCED, true);
751   }
752   dir_->SaveChanges();
753   {
754     WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__);
755     dir_->GetUnsyncedMetaHandles(&trans, &handles);
756     ASSERT_TRUE(1 == handles.size());
757     ASSERT_TRUE(handle1 == handles[0]);
758 
759     MutableEntry e4(&trans, GET_BY_HANDLE, handle2);
760     ASSERT_TRUE(e4.good());
761     e4.Put(IS_UNSYNCED, true);
762   }
763   dir_->SaveChanges();
764   {
765     WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__);
766     dir_->GetUnsyncedMetaHandles(&trans, &handles);
767     ASSERT_TRUE(2 == handles.size());
768     if (handle1 == handles[0]) {
769       ASSERT_TRUE(handle2 == handles[1]);
770     } else {
771       ASSERT_TRUE(handle2 == handles[0]);
772       ASSERT_TRUE(handle1 == handles[1]);
773     }
774 
775     MutableEntry e5(&trans, GET_BY_HANDLE, handle1);
776     ASSERT_TRUE(e5.good());
777     ASSERT_TRUE(e5.Get(IS_UNSYNCED));
778     ASSERT_TRUE(e5.Put(IS_UNSYNCED, false));
779     ASSERT_FALSE(e5.Get(IS_UNSYNCED));
780   }
781   dir_->SaveChanges();
782   {
783     WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__);
784     dir_->GetUnsyncedMetaHandles(&trans, &handles);
785     ASSERT_TRUE(1 == handles.size());
786     ASSERT_TRUE(handle2 == handles[0]);
787   }
788 }
789 
TEST_F(SyncableDirectoryTest,TestGetUnappliedUpdates)790 TEST_F(SyncableDirectoryTest, TestGetUnappliedUpdates) {
791   Directory::UnappliedUpdateMetaHandles handles;
792   int64 handle1, handle2;
793   {
794     WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__);
795 
796     dir_->GetUnappliedUpdateMetaHandles(&trans, &handles);
797     ASSERT_TRUE(0 == handles.size());
798 
799     MutableEntry e1(&trans, CREATE, trans.root_id(), "abba");
800     ASSERT_TRUE(e1.good());
801     handle1 = e1.Get(META_HANDLE);
802     e1.Put(IS_UNAPPLIED_UPDATE, false);
803     e1.Put(BASE_VERSION, 1);
804     e1.Put(ID, TestIdFactory::FromNumber(101));
805     e1.Put(IS_DIR, true);
806 
807     MutableEntry e2(&trans, CREATE, e1.Get(ID), "bread");
808     ASSERT_TRUE(e2.good());
809     handle2 = e2.Get(META_HANDLE);
810     e2.Put(IS_UNAPPLIED_UPDATE, false);
811     e2.Put(BASE_VERSION, 1);
812     e2.Put(ID, TestIdFactory::FromNumber(102));
813   }
814   dir_->SaveChanges();
815   {
816     WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__);
817 
818     dir_->GetUnappliedUpdateMetaHandles(&trans, &handles);
819     ASSERT_TRUE(0 == handles.size());
820 
821     MutableEntry e3(&trans, GET_BY_HANDLE, handle1);
822     ASSERT_TRUE(e3.good());
823     e3.Put(IS_UNAPPLIED_UPDATE, true);
824   }
825   dir_->SaveChanges();
826   {
827     WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__);
828     dir_->GetUnappliedUpdateMetaHandles(&trans, &handles);
829     ASSERT_TRUE(1 == handles.size());
830     ASSERT_TRUE(handle1 == handles[0]);
831 
832     MutableEntry e4(&trans, GET_BY_HANDLE, handle2);
833     ASSERT_TRUE(e4.good());
834     e4.Put(IS_UNAPPLIED_UPDATE, true);
835   }
836   dir_->SaveChanges();
837   {
838     WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__);
839     dir_->GetUnappliedUpdateMetaHandles(&trans, &handles);
840     ASSERT_TRUE(2 == handles.size());
841     if (handle1 == handles[0]) {
842       ASSERT_TRUE(handle2 == handles[1]);
843     } else {
844       ASSERT_TRUE(handle2 == handles[0]);
845       ASSERT_TRUE(handle1 == handles[1]);
846     }
847 
848     MutableEntry e5(&trans, GET_BY_HANDLE, handle1);
849     ASSERT_TRUE(e5.good());
850     e5.Put(IS_UNAPPLIED_UPDATE, false);
851   }
852   dir_->SaveChanges();
853   {
854     WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__);
855     dir_->GetUnappliedUpdateMetaHandles(&trans, &handles);
856     ASSERT_TRUE(1 == handles.size());
857     ASSERT_TRUE(handle2 == handles[0]);
858   }
859 }
860 
861 
TEST_F(SyncableDirectoryTest,DeleteBug_531383)862 TEST_F(SyncableDirectoryTest, DeleteBug_531383) {
863   // Try to evoke a check failure...
864   TestIdFactory id_factory;
865   int64 grandchild_handle, twin_handle;
866   {
867     WriteTransaction wtrans(dir_.get(), UNITTEST, __FILE__, __LINE__);
868     MutableEntry parent(&wtrans, CREATE, id_factory.root(), "Bob");
869     ASSERT_TRUE(parent.good());
870     parent.Put(IS_DIR, true);
871     parent.Put(ID, id_factory.NewServerId());
872     parent.Put(BASE_VERSION, 1);
873     MutableEntry child(&wtrans, CREATE, parent.Get(ID), "Bob");
874     ASSERT_TRUE(child.good());
875     child.Put(IS_DIR, true);
876     child.Put(ID, id_factory.NewServerId());
877     child.Put(BASE_VERSION, 1);
878     MutableEntry grandchild(&wtrans, CREATE, child.Get(ID), "Bob");
879     ASSERT_TRUE(grandchild.good());
880     grandchild.Put(ID, id_factory.NewServerId());
881     grandchild.Put(BASE_VERSION, 1);
882     ASSERT_TRUE(grandchild.Put(IS_DEL, true));
883     MutableEntry twin(&wtrans, CREATE, child.Get(ID), "Bob");
884     ASSERT_TRUE(twin.good());
885     ASSERT_TRUE(twin.Put(IS_DEL, true));
886     ASSERT_TRUE(grandchild.Put(IS_DEL, false));
887 
888     grandchild_handle = grandchild.Get(META_HANDLE);
889     twin_handle = twin.Get(META_HANDLE);
890   }
891   dir_->SaveChanges();
892   {
893     WriteTransaction wtrans(dir_.get(), UNITTEST, __FILE__, __LINE__);
894     MutableEntry grandchild(&wtrans, GET_BY_HANDLE, grandchild_handle);
895     grandchild.Put(IS_DEL, true);  // Used to CHECK fail here.
896   }
897 }
898 
IsLegalNewParent(const Entry & a,const Entry & b)899 static inline bool IsLegalNewParent(const Entry& a, const Entry& b) {
900   return IsLegalNewParent(a.trans(), a.Get(ID), b.Get(ID));
901 }
902 
TEST_F(SyncableDirectoryTest,TestIsLegalNewParent)903 TEST_F(SyncableDirectoryTest, TestIsLegalNewParent) {
904   TestIdFactory id_factory;
905   WriteTransaction wtrans(dir_.get(), UNITTEST, __FILE__, __LINE__);
906   Entry root(&wtrans, GET_BY_ID, id_factory.root());
907   ASSERT_TRUE(root.good());
908   MutableEntry parent(&wtrans, CREATE, root.Get(ID), "Bob");
909   ASSERT_TRUE(parent.good());
910   parent.Put(IS_DIR, true);
911   parent.Put(ID, id_factory.NewServerId());
912   parent.Put(BASE_VERSION, 1);
913   MutableEntry child(&wtrans, CREATE, parent.Get(ID), "Bob");
914   ASSERT_TRUE(child.good());
915   child.Put(IS_DIR, true);
916   child.Put(ID, id_factory.NewServerId());
917   child.Put(BASE_VERSION, 1);
918   MutableEntry grandchild(&wtrans, CREATE, child.Get(ID), "Bob");
919   ASSERT_TRUE(grandchild.good());
920   grandchild.Put(ID, id_factory.NewServerId());
921   grandchild.Put(BASE_VERSION, 1);
922 
923   MutableEntry parent2(&wtrans, CREATE, root.Get(ID), "Pete");
924   ASSERT_TRUE(parent2.good());
925   parent2.Put(IS_DIR, true);
926   parent2.Put(ID, id_factory.NewServerId());
927   parent2.Put(BASE_VERSION, 1);
928   MutableEntry child2(&wtrans, CREATE, parent2.Get(ID), "Pete");
929   ASSERT_TRUE(child2.good());
930   child2.Put(IS_DIR, true);
931   child2.Put(ID, id_factory.NewServerId());
932   child2.Put(BASE_VERSION, 1);
933   MutableEntry grandchild2(&wtrans, CREATE, child2.Get(ID), "Pete");
934   ASSERT_TRUE(grandchild2.good());
935   grandchild2.Put(ID, id_factory.NewServerId());
936   grandchild2.Put(BASE_VERSION, 1);
937   // resulting tree
938   //           root
939   //           /  |
940   //     parent    parent2
941   //          |    |
942   //      child    child2
943   //          |    |
944   // grandchild    grandchild2
945   ASSERT_TRUE(IsLegalNewParent(child, root));
946   ASSERT_TRUE(IsLegalNewParent(child, parent));
947   ASSERT_FALSE(IsLegalNewParent(child, child));
948   ASSERT_FALSE(IsLegalNewParent(child, grandchild));
949   ASSERT_TRUE(IsLegalNewParent(child, parent2));
950   ASSERT_TRUE(IsLegalNewParent(child, grandchild2));
951   ASSERT_FALSE(IsLegalNewParent(parent, grandchild));
952   ASSERT_FALSE(IsLegalNewParent(root, grandchild));
953   ASSERT_FALSE(IsLegalNewParent(parent, grandchild));
954 }
955 
TEST_F(SyncableDirectoryTest,TestEntryIsInFolder)956 TEST_F(SyncableDirectoryTest, TestEntryIsInFolder) {
957   // Create a subdir and an entry.
958   int64 entry_handle;
959   syncable::Id folder_id;
960   syncable::Id entry_id;
961   std::string entry_name = "entry";
962 
963   {
964     WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__);
965     MutableEntry folder(&trans, CREATE, trans.root_id(), "folder");
966     ASSERT_TRUE(folder.good());
967     EXPECT_TRUE(folder.Put(IS_DIR, true));
968     EXPECT_TRUE(folder.Put(IS_UNSYNCED, true));
969     folder_id = folder.Get(ID);
970 
971     MutableEntry entry(&trans, CREATE, folder.Get(ID), entry_name);
972     ASSERT_TRUE(entry.good());
973     entry_handle = entry.Get(META_HANDLE);
974     entry.Put(IS_UNSYNCED, true);
975     entry_id = entry.Get(ID);
976   }
977 
978   // Make sure we can find the entry in the folder.
979   {
980     ReadTransaction trans(dir_.get(), __FILE__, __LINE__);
981     EXPECT_EQ(0, CountEntriesWithName(&trans, trans.root_id(), entry_name));
982     EXPECT_EQ(1, CountEntriesWithName(&trans, folder_id, entry_name));
983 
984     Entry entry(&trans, GET_BY_ID, entry_id);
985     ASSERT_TRUE(entry.good());
986     EXPECT_EQ(entry_handle, entry.Get(META_HANDLE));
987     EXPECT_TRUE(entry.Get(NON_UNIQUE_NAME) == entry_name);
988     EXPECT_TRUE(entry.Get(PARENT_ID) == folder_id);
989   }
990 }
991 
TEST_F(SyncableDirectoryTest,TestParentIdIndexUpdate)992 TEST_F(SyncableDirectoryTest, TestParentIdIndexUpdate) {
993   std::string child_name = "child";
994 
995   WriteTransaction wt(dir_.get(), UNITTEST, __FILE__, __LINE__);
996   MutableEntry parent_folder(&wt, CREATE, wt.root_id(), "folder1");
997   parent_folder.Put(IS_UNSYNCED, true);
998   EXPECT_TRUE(parent_folder.Put(IS_DIR, true));
999 
1000   MutableEntry parent_folder2(&wt, CREATE, wt.root_id(), "folder2");
1001   parent_folder2.Put(IS_UNSYNCED, true);
1002   EXPECT_TRUE(parent_folder2.Put(IS_DIR, true));
1003 
1004   MutableEntry child(&wt, CREATE, parent_folder.Get(ID), child_name);
1005   EXPECT_TRUE(child.Put(IS_DIR, true));
1006   child.Put(IS_UNSYNCED, true);
1007 
1008   ASSERT_TRUE(child.good());
1009 
1010   EXPECT_EQ(0, CountEntriesWithName(&wt, wt.root_id(), child_name));
1011   EXPECT_EQ(parent_folder.Get(ID), child.Get(PARENT_ID));
1012   EXPECT_EQ(1, CountEntriesWithName(&wt, parent_folder.Get(ID), child_name));
1013   EXPECT_EQ(0, CountEntriesWithName(&wt, parent_folder2.Get(ID), child_name));
1014   child.Put(PARENT_ID, parent_folder2.Get(ID));
1015   EXPECT_EQ(parent_folder2.Get(ID), child.Get(PARENT_ID));
1016   EXPECT_EQ(0, CountEntriesWithName(&wt, parent_folder.Get(ID), child_name));
1017   EXPECT_EQ(1, CountEntriesWithName(&wt, parent_folder2.Get(ID), child_name));
1018 }
1019 
TEST_F(SyncableDirectoryTest,TestNoReindexDeletedItems)1020 TEST_F(SyncableDirectoryTest, TestNoReindexDeletedItems) {
1021   std::string folder_name = "folder";
1022   std::string new_name = "new_name";
1023 
1024   WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__);
1025   MutableEntry folder(&trans, CREATE, trans.root_id(), folder_name);
1026   ASSERT_TRUE(folder.good());
1027   ASSERT_TRUE(folder.Put(IS_DIR, true));
1028   ASSERT_TRUE(folder.Put(IS_DEL, true));
1029 
1030   EXPECT_EQ(0, CountEntriesWithName(&trans, trans.root_id(), folder_name));
1031 
1032   MutableEntry deleted(&trans, GET_BY_ID, folder.Get(ID));
1033   ASSERT_TRUE(deleted.good());
1034   ASSERT_TRUE(deleted.Put(PARENT_ID, trans.root_id()));
1035   ASSERT_TRUE(deleted.Put(NON_UNIQUE_NAME, new_name));
1036 
1037   EXPECT_EQ(0, CountEntriesWithName(&trans, trans.root_id(), folder_name));
1038   EXPECT_EQ(0, CountEntriesWithName(&trans, trans.root_id(), new_name));
1039 }
1040 
TEST_F(SyncableDirectoryTest,TestCaseChangeRename)1041 TEST_F(SyncableDirectoryTest, TestCaseChangeRename) {
1042   WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__);
1043   MutableEntry folder(&trans, CREATE, trans.root_id(), "CaseChange");
1044   ASSERT_TRUE(folder.good());
1045   EXPECT_TRUE(folder.Put(PARENT_ID, trans.root_id()));
1046   EXPECT_TRUE(folder.Put(NON_UNIQUE_NAME, "CASECHANGE"));
1047   EXPECT_TRUE(folder.Put(IS_DEL, true));
1048 }
1049 
TEST_F(SyncableDirectoryTest,TestShareInfo)1050 TEST_F(SyncableDirectoryTest, TestShareInfo) {
1051   dir_->set_initial_sync_ended_for_type(AUTOFILL, true);
1052   dir_->set_store_birthday("Jan 31st");
1053   dir_->SetNotificationState("notification_state");
1054   {
1055     ReadTransaction trans(dir_.get(), __FILE__, __LINE__);
1056     EXPECT_TRUE(dir_->initial_sync_ended_for_type(AUTOFILL));
1057     EXPECT_FALSE(dir_->initial_sync_ended_for_type(BOOKMARKS));
1058     EXPECT_EQ("Jan 31st", dir_->store_birthday());
1059     EXPECT_EQ("notification_state", dir_->GetAndClearNotificationState());
1060     EXPECT_EQ("", dir_->GetAndClearNotificationState());
1061   }
1062   dir_->set_store_birthday("April 10th");
1063   dir_->SetNotificationState("notification_state2");
1064   dir_->SaveChanges();
1065   {
1066     ReadTransaction trans(dir_.get(), __FILE__, __LINE__);
1067     EXPECT_TRUE(dir_->initial_sync_ended_for_type(AUTOFILL));
1068     EXPECT_FALSE(dir_->initial_sync_ended_for_type(BOOKMARKS));
1069     EXPECT_EQ("April 10th", dir_->store_birthday());
1070     EXPECT_EQ("notification_state2", dir_->GetAndClearNotificationState());
1071     EXPECT_EQ("", dir_->GetAndClearNotificationState());
1072   }
1073   dir_->SetNotificationState("notification_state2");
1074   // Restore the directory from disk.  Make sure that nothing's changed.
1075   SaveAndReloadDir();
1076   {
1077     ReadTransaction trans(dir_.get(), __FILE__, __LINE__);
1078     EXPECT_TRUE(dir_->initial_sync_ended_for_type(AUTOFILL));
1079     EXPECT_FALSE(dir_->initial_sync_ended_for_type(BOOKMARKS));
1080     EXPECT_EQ("April 10th", dir_->store_birthday());
1081     EXPECT_EQ("notification_state2", dir_->GetAndClearNotificationState());
1082     EXPECT_EQ("", dir_->GetAndClearNotificationState());
1083   }
1084 }
1085 
TEST_F(SyncableDirectoryTest,TestSimpleFieldsPreservedDuringSaveChanges)1086 TEST_F(SyncableDirectoryTest, TestSimpleFieldsPreservedDuringSaveChanges) {
1087   Id update_id = TestIdFactory::FromNumber(1);
1088   Id create_id;
1089   EntryKernel create_pre_save, update_pre_save;
1090   EntryKernel create_post_save, update_post_save;
1091   std::string create_name =  "Create";
1092 
1093   {
1094     WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__);
1095     MutableEntry create(&trans, CREATE, trans.root_id(), create_name);
1096     MutableEntry update(&trans, CREATE_NEW_UPDATE_ITEM, update_id);
1097     create.Put(IS_UNSYNCED, true);
1098     update.Put(IS_UNAPPLIED_UPDATE, true);
1099     sync_pb::EntitySpecifics specifics;
1100     specifics.MutableExtension(sync_pb::bookmark)->set_favicon("PNG");
1101     specifics.MutableExtension(sync_pb::bookmark)->set_url("http://nowhere");
1102     create.Put(SPECIFICS, specifics);
1103     create_pre_save = create.GetKernelCopy();
1104     update_pre_save = update.GetKernelCopy();
1105     create_id = create.Get(ID);
1106   }
1107 
1108   dir_->SaveChanges();
1109   dir_.reset(new Directory());
1110   ASSERT_TRUE(dir_.get());
1111   ASSERT_TRUE(OPENED == dir_->Open(file_path_, kName));
1112   ASSERT_TRUE(dir_->good());
1113 
1114   {
1115     ReadTransaction trans(dir_.get(), __FILE__, __LINE__);
1116     Entry create(&trans, GET_BY_ID, create_id);
1117     EXPECT_EQ(1, CountEntriesWithName(&trans, trans.root_id(), create_name));
1118     Entry update(&trans, GET_BY_ID, update_id);
1119     create_post_save = create.GetKernelCopy();
1120     update_post_save = update.GetKernelCopy();
1121   }
1122   int i = BEGIN_FIELDS;
1123   for ( ; i < INT64_FIELDS_END ; ++i) {
1124     EXPECT_EQ(create_pre_save.ref((Int64Field)i),
1125               create_post_save.ref((Int64Field)i))
1126               << "int64 field #" << i << " changed during save/load";
1127     EXPECT_EQ(update_pre_save.ref((Int64Field)i),
1128               update_post_save.ref((Int64Field)i))
1129               << "int64 field #" << i << " changed during save/load";
1130   }
1131   for ( ; i < ID_FIELDS_END ; ++i) {
1132     EXPECT_EQ(create_pre_save.ref((IdField)i),
1133               create_post_save.ref((IdField)i))
1134               << "id field #" << i << " changed during save/load";
1135     EXPECT_EQ(update_pre_save.ref((IdField)i),
1136               update_pre_save.ref((IdField)i))
1137               << "id field #" << i << " changed during save/load";
1138   }
1139   for ( ; i < BIT_FIELDS_END ; ++i) {
1140     EXPECT_EQ(create_pre_save.ref((BitField)i),
1141               create_post_save.ref((BitField)i))
1142               << "Bit field #" << i << " changed during save/load";
1143     EXPECT_EQ(update_pre_save.ref((BitField)i),
1144               update_post_save.ref((BitField)i))
1145               << "Bit field #" << i << " changed during save/load";
1146   }
1147   for ( ; i < STRING_FIELDS_END ; ++i) {
1148     EXPECT_EQ(create_pre_save.ref((StringField)i),
1149               create_post_save.ref((StringField)i))
1150               << "String field #" << i << " changed during save/load";
1151     EXPECT_EQ(update_pre_save.ref((StringField)i),
1152               update_post_save.ref((StringField)i))
1153               << "String field #" << i << " changed during save/load";
1154   }
1155   for ( ; i < PROTO_FIELDS_END; ++i) {
1156     EXPECT_EQ(create_pre_save.ref((ProtoField)i).SerializeAsString(),
1157               create_post_save.ref((ProtoField)i).SerializeAsString())
1158               << "Blob field #" << i << " changed during save/load";
1159     EXPECT_EQ(update_pre_save.ref((ProtoField)i).SerializeAsString(),
1160               update_post_save.ref((ProtoField)i).SerializeAsString())
1161               << "Blob field #" << i << " changed during save/load";
1162   }
1163 }
1164 
TEST_F(SyncableDirectoryTest,TestSaveChangesFailure)1165 TEST_F(SyncableDirectoryTest, TestSaveChangesFailure) {
1166   int64 handle1 = 0;
1167   // Set up an item using a regular, saveable directory.
1168   {
1169     WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__);
1170 
1171     MutableEntry e1(&trans, CREATE, trans.root_id(), "aguilera");
1172     ASSERT_TRUE(e1.good());
1173     EXPECT_TRUE(e1.GetKernelCopy().is_dirty());
1174     handle1 = e1.Get(META_HANDLE);
1175     e1.Put(BASE_VERSION, 1);
1176     e1.Put(IS_DIR, true);
1177     e1.Put(ID, TestIdFactory::FromNumber(101));
1178     EXPECT_TRUE(e1.GetKernelCopy().is_dirty());
1179     EXPECT_TRUE(IsInDirtyMetahandles(handle1));
1180   }
1181   ASSERT_TRUE(dir_->SaveChanges());
1182 
1183   // Make sure the item is no longer dirty after saving,
1184   // and make a modification.
1185   {
1186     WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__);
1187 
1188     MutableEntry aguilera(&trans, GET_BY_HANDLE, handle1);
1189     ASSERT_TRUE(aguilera.good());
1190     EXPECT_FALSE(aguilera.GetKernelCopy().is_dirty());
1191     EXPECT_EQ(aguilera.Get(NON_UNIQUE_NAME), "aguilera");
1192     aguilera.Put(NON_UNIQUE_NAME, "overwritten");
1193     EXPECT_TRUE(aguilera.GetKernelCopy().is_dirty());
1194     EXPECT_TRUE(IsInDirtyMetahandles(handle1));
1195   }
1196   ASSERT_TRUE(dir_->SaveChanges());
1197 
1198   // Now do some operations using a directory for which SaveChanges will
1199   // always fail.
1200   dir_.reset(new TestUnsaveableDirectory());
1201   ASSERT_TRUE(dir_.get());
1202   ASSERT_TRUE(OPENED == dir_->Open(FilePath(kFilePath), kName));
1203   ASSERT_TRUE(dir_->good());
1204   int64 handle2 = 0;
1205   {
1206     WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__);
1207 
1208     MutableEntry aguilera(&trans, GET_BY_HANDLE, handle1);
1209     ASSERT_TRUE(aguilera.good());
1210     EXPECT_FALSE(aguilera.GetKernelCopy().is_dirty());
1211     EXPECT_EQ(aguilera.Get(NON_UNIQUE_NAME), "overwritten");
1212     EXPECT_FALSE(aguilera.GetKernelCopy().is_dirty());
1213     EXPECT_FALSE(IsInDirtyMetahandles(handle1));
1214     aguilera.Put(NON_UNIQUE_NAME, "christina");
1215     EXPECT_TRUE(aguilera.GetKernelCopy().is_dirty());
1216     EXPECT_TRUE(IsInDirtyMetahandles(handle1));
1217 
1218     // New item.
1219     MutableEntry kids_on_block(&trans, CREATE, trans.root_id(), "kids");
1220     ASSERT_TRUE(kids_on_block.good());
1221     handle2 = kids_on_block.Get(META_HANDLE);
1222     kids_on_block.Put(BASE_VERSION, 1);
1223     kids_on_block.Put(IS_DIR, true);
1224     kids_on_block.Put(ID, TestIdFactory::FromNumber(102));
1225     EXPECT_TRUE(kids_on_block.GetKernelCopy().is_dirty());
1226     EXPECT_TRUE(IsInDirtyMetahandles(handle2));
1227   }
1228 
1229   // We are using an unsaveable directory, so this can't succeed.  However,
1230   // the HandleSaveChangesFailure code path should have been triggered.
1231   ASSERT_FALSE(dir_->SaveChanges());
1232 
1233   // Make sure things were rolled back and the world is as it was before call.
1234   {
1235      ReadTransaction trans(dir_.get(), __FILE__, __LINE__);
1236      Entry e1(&trans, GET_BY_HANDLE, handle1);
1237      ASSERT_TRUE(e1.good());
1238      EntryKernel aguilera = e1.GetKernelCopy();
1239      Entry kids(&trans, GET_BY_HANDLE, handle2);
1240      ASSERT_TRUE(kids.good());
1241      EXPECT_TRUE(kids.GetKernelCopy().is_dirty());
1242      EXPECT_TRUE(IsInDirtyMetahandles(handle2));
1243      EXPECT_TRUE(aguilera.is_dirty());
1244      EXPECT_TRUE(IsInDirtyMetahandles(handle1));
1245   }
1246 }
1247 
TEST_F(SyncableDirectoryTest,TestSaveChangesFailureWithPurge)1248 TEST_F(SyncableDirectoryTest, TestSaveChangesFailureWithPurge) {
1249   int64 handle1 = 0;
1250   // Set up an item using a regular, saveable directory.
1251   {
1252     WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__);
1253 
1254     MutableEntry e1(&trans, CREATE, trans.root_id(), "aguilera");
1255     ASSERT_TRUE(e1.good());
1256     EXPECT_TRUE(e1.GetKernelCopy().is_dirty());
1257     handle1 = e1.Get(META_HANDLE);
1258     e1.Put(BASE_VERSION, 1);
1259     e1.Put(IS_DIR, true);
1260     e1.Put(ID, TestIdFactory::FromNumber(101));
1261     sync_pb::EntitySpecifics bookmark_specs;
1262     AddDefaultExtensionValue(BOOKMARKS, &bookmark_specs);
1263     e1.Put(SPECIFICS, bookmark_specs);
1264     e1.Put(SERVER_SPECIFICS, bookmark_specs);
1265     e1.Put(ID, TestIdFactory::FromNumber(101));
1266     EXPECT_TRUE(e1.GetKernelCopy().is_dirty());
1267     EXPECT_TRUE(IsInDirtyMetahandles(handle1));
1268   }
1269   ASSERT_TRUE(dir_->SaveChanges());
1270 
1271   // Now do some operations using a directory for which SaveChanges will
1272   // always fail.
1273   dir_.reset(new TestUnsaveableDirectory());
1274   ASSERT_TRUE(dir_.get());
1275   ASSERT_TRUE(OPENED == dir_->Open(FilePath(kFilePath), kName));
1276   ASSERT_TRUE(dir_->good());
1277 
1278   ModelTypeSet set;
1279   set.insert(BOOKMARKS);
1280   dir_->PurgeEntriesWithTypeIn(set);
1281   EXPECT_TRUE(IsInMetahandlesToPurge(handle1));
1282   ASSERT_FALSE(dir_->SaveChanges());
1283   EXPECT_TRUE(IsInMetahandlesToPurge(handle1));
1284 }
1285 
1286 // Create items of each model type, and check that GetModelType and
1287 // GetServerModelType return the right value.
TEST_F(SyncableDirectoryTest,GetModelType)1288 TEST_F(SyncableDirectoryTest, GetModelType) {
1289   TestIdFactory id_factory;
1290   for (int i = 0; i < MODEL_TYPE_COUNT; ++i) {
1291     ModelType datatype = ModelTypeFromInt(i);
1292     SCOPED_TRACE(testing::Message("Testing model type ") << datatype);
1293     switch (datatype) {
1294       case UNSPECIFIED:
1295       case TOP_LEVEL_FOLDER:
1296         continue;  // Datatype isn't a function of Specifics.
1297       default:
1298         break;
1299     }
1300     sync_pb::EntitySpecifics specifics;
1301     AddDefaultExtensionValue(datatype, &specifics);
1302 
1303     WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__);
1304 
1305     MutableEntry folder(&trans, CREATE, trans.root_id(), "Folder");
1306     ASSERT_TRUE(folder.good());
1307     folder.Put(ID, id_factory.NewServerId());
1308     folder.Put(SPECIFICS, specifics);
1309     folder.Put(BASE_VERSION, 1);
1310     folder.Put(IS_DIR, true);
1311     folder.Put(IS_DEL, false);
1312     ASSERT_EQ(datatype, folder.GetModelType());
1313 
1314     MutableEntry item(&trans, CREATE, trans.root_id(), "Item");
1315     ASSERT_TRUE(item.good());
1316     item.Put(ID, id_factory.NewServerId());
1317     item.Put(SPECIFICS, specifics);
1318     item.Put(BASE_VERSION, 1);
1319     item.Put(IS_DIR, false);
1320     item.Put(IS_DEL, false);
1321     ASSERT_EQ(datatype, item.GetModelType());
1322 
1323     // It's critical that deletion records retain their datatype, so that
1324     // they can be dispatched to the appropriate change processor.
1325     MutableEntry deleted_item(&trans, CREATE, trans.root_id(), "Deleted Item");
1326     ASSERT_TRUE(item.good());
1327     deleted_item.Put(ID, id_factory.NewServerId());
1328     deleted_item.Put(SPECIFICS, specifics);
1329     deleted_item.Put(BASE_VERSION, 1);
1330     deleted_item.Put(IS_DIR, false);
1331     deleted_item.Put(IS_DEL, true);
1332     ASSERT_EQ(datatype, deleted_item.GetModelType());
1333 
1334     MutableEntry server_folder(&trans, CREATE_NEW_UPDATE_ITEM,
1335         id_factory.NewServerId());
1336     ASSERT_TRUE(server_folder.good());
1337     server_folder.Put(SERVER_SPECIFICS, specifics);
1338     server_folder.Put(BASE_VERSION, 1);
1339     server_folder.Put(SERVER_IS_DIR, true);
1340     server_folder.Put(SERVER_IS_DEL, false);
1341     ASSERT_EQ(datatype, server_folder.GetServerModelType());
1342 
1343     MutableEntry server_item(&trans, CREATE_NEW_UPDATE_ITEM,
1344         id_factory.NewServerId());
1345     ASSERT_TRUE(server_item.good());
1346     server_item.Put(SERVER_SPECIFICS, specifics);
1347     server_item.Put(BASE_VERSION, 1);
1348     server_item.Put(SERVER_IS_DIR, false);
1349     server_item.Put(SERVER_IS_DEL, false);
1350     ASSERT_EQ(datatype, server_item.GetServerModelType());
1351 
1352     browser_sync::SyncEntity folder_entity;
1353     folder_entity.set_id(id_factory.NewServerId());
1354     folder_entity.set_deleted(false);
1355     folder_entity.set_folder(true);
1356     folder_entity.mutable_specifics()->CopyFrom(specifics);
1357     ASSERT_EQ(datatype, folder_entity.GetModelType());
1358 
1359     browser_sync::SyncEntity item_entity;
1360     item_entity.set_id(id_factory.NewServerId());
1361     item_entity.set_deleted(false);
1362     item_entity.set_folder(false);
1363     item_entity.mutable_specifics()->CopyFrom(specifics);
1364     ASSERT_EQ(datatype, item_entity.GetModelType());
1365   }
1366 }
1367 
1368 }  // namespace
1369 
ValidateEntry(BaseTransaction * trans,int64 id,bool check_name,const std::string & name,int64 base_version,int64 server_version,bool is_del)1370 void SyncableDirectoryTest::ValidateEntry(BaseTransaction* trans,
1371                                           int64 id,
1372                                           bool check_name,
1373                                           const std::string& name,
1374                                           int64 base_version,
1375                                           int64 server_version,
1376                                           bool is_del) {
1377   Entry e(trans, GET_BY_ID, TestIdFactory::FromNumber(id));
1378   ASSERT_TRUE(e.good());
1379   if (check_name)
1380     ASSERT_TRUE(name == e.Get(NON_UNIQUE_NAME));
1381   ASSERT_TRUE(base_version == e.Get(BASE_VERSION));
1382   ASSERT_TRUE(server_version == e.Get(SERVER_VERSION));
1383   ASSERT_TRUE(is_del == e.Get(IS_DEL));
1384 }
1385 
1386 namespace {
1387 
TEST(SyncableDirectoryManager,TestFileRelease)1388 TEST(SyncableDirectoryManager, TestFileRelease) {
1389   DirectoryManager dm(FilePath(FILE_PATH_LITERAL(".")));
1390   ASSERT_TRUE(dm.Open("ScopeTest"));
1391   {
1392     ScopedDirLookup(&dm, "ScopeTest");
1393   }
1394   dm.Close("ScopeTest");
1395   ASSERT_TRUE(file_util::Delete(dm.GetSyncDataDatabasePath(), true));
1396 }
1397 
1398 class ThreadOpenTestDelegate : public base::PlatformThread::Delegate {
1399  public:
ThreadOpenTestDelegate(DirectoryManager * dm)1400   explicit ThreadOpenTestDelegate(DirectoryManager* dm)
1401       : directory_manager_(dm) {}
1402   DirectoryManager* const directory_manager_;
1403 
1404  private:
1405   // PlatformThread::Delegate methods:
ThreadMain()1406   virtual void ThreadMain() {
1407     CHECK(directory_manager_->Open("Open"));
1408   }
1409 
1410   DISALLOW_COPY_AND_ASSIGN(ThreadOpenTestDelegate);
1411 };
1412 
TEST(SyncableDirectoryManager,ThreadOpenTest)1413 TEST(SyncableDirectoryManager, ThreadOpenTest) {
1414   DirectoryManager dm(FilePath(FILE_PATH_LITERAL(".")));
1415   base::PlatformThreadHandle thread_handle;
1416   ThreadOpenTestDelegate test_delegate(&dm);
1417   ASSERT_TRUE(base::PlatformThread::Create(0, &test_delegate, &thread_handle));
1418   base::PlatformThread::Join(thread_handle);
1419   {
1420     ScopedDirLookup dir(&dm, "Open");
1421     ASSERT_TRUE(dir.good());
1422   }
1423   dm.Close("Open");
1424   ScopedDirLookup dir(&dm, "Open");
1425   ASSERT_FALSE(dir.good());
1426 }
1427 
1428 struct Step {
Stepsyncable::__anonb1f1d8ba0311::Step1429   Step() : condvar(&mutex), number(0) {}
1430 
1431   base::Lock mutex;
1432   base::ConditionVariable condvar;
1433   int number;
1434   int64 metahandle;
1435 };
1436 
1437 class ThreadBugDelegate : public base::PlatformThread::Delegate {
1438  public:
1439   // a role is 0 or 1, meaning this thread does the odd or event steps.
ThreadBugDelegate(int role,Step * step,DirectoryManager * dirman)1440   ThreadBugDelegate(int role, Step* step, DirectoryManager* dirman)
1441       : role_(role), step_(step), directory_manager_(dirman) {}
1442 
1443  protected:
1444   const int role_;
1445   Step* const step_;
1446   DirectoryManager* const directory_manager_;
1447 
1448   // PlatformThread::Delegate methods:
ThreadMain()1449   virtual void ThreadMain() {
1450     const std::string dirname = "ThreadBug1";
1451     base::AutoLock scoped_lock(step_->mutex);
1452 
1453     while (step_->number < 3) {
1454       while (step_->number % 2 != role_) {
1455         step_->condvar.Wait();
1456       }
1457       switch (step_->number) {
1458       case 0:
1459         directory_manager_->Open(dirname);
1460         break;
1461       case 1:
1462         {
1463           directory_manager_->Close(dirname);
1464           directory_manager_->Open(dirname);
1465           ScopedDirLookup dir(directory_manager_, dirname);
1466           CHECK(dir.good());
1467           WriteTransaction trans(dir, UNITTEST, __FILE__, __LINE__);
1468           MutableEntry me(&trans, CREATE, trans.root_id(), "Jeff");
1469           step_->metahandle = me.Get(META_HANDLE);
1470           me.Put(IS_UNSYNCED, true);
1471         }
1472         break;
1473       case 2:
1474         {
1475           ScopedDirLookup dir(directory_manager_, dirname);
1476           CHECK(dir.good());
1477           ReadTransaction trans(dir, __FILE__, __LINE__);
1478           Entry e(&trans, GET_BY_HANDLE, step_->metahandle);
1479           CHECK(e.good());  // Failed due to ThreadBug1
1480         }
1481         directory_manager_->Close(dirname);
1482         break;
1483        }
1484        step_->number += 1;
1485        step_->condvar.Signal();
1486     }
1487   }
1488 
1489   DISALLOW_COPY_AND_ASSIGN(ThreadBugDelegate);
1490 };
1491 
TEST(SyncableDirectoryManager,ThreadBug1)1492 TEST(SyncableDirectoryManager, ThreadBug1) {
1493   Step step;
1494   step.number = 0;
1495   DirectoryManager dirman(FilePath(FILE_PATH_LITERAL(".")));
1496   ThreadBugDelegate thread_delegate_1(0, &step, &dirman);
1497   ThreadBugDelegate thread_delegate_2(1, &step, &dirman);
1498 
1499   base::PlatformThreadHandle thread_handle_1;
1500   base::PlatformThreadHandle thread_handle_2;
1501 
1502   ASSERT_TRUE(
1503       base::PlatformThread::Create(0, &thread_delegate_1, &thread_handle_1));
1504   ASSERT_TRUE(
1505       base::PlatformThread::Create(0, &thread_delegate_2, &thread_handle_2));
1506 
1507   base::PlatformThread::Join(thread_handle_1);
1508   base::PlatformThread::Join(thread_handle_2);
1509 }
1510 
1511 
1512 // The in-memory information would get out of sync because a
1513 // directory would be closed and re-opened, and then an old
1514 // Directory::Kernel with stale information would get saved to the db.
1515 class DirectoryKernelStalenessBugDelegate : public ThreadBugDelegate {
1516  public:
DirectoryKernelStalenessBugDelegate(int role,Step * step,DirectoryManager * dirman)1517   DirectoryKernelStalenessBugDelegate(int role, Step* step,
1518                                                DirectoryManager* dirman)
1519     : ThreadBugDelegate(role, step, dirman) {}
1520 
ThreadMain()1521   virtual void ThreadMain() {
1522     const char test_bytes[] = "test data";
1523     const std::string dirname = "DirectoryKernelStalenessBug";
1524     base::AutoLock scoped_lock(step_->mutex);
1525     const Id jeff_id = TestIdFactory::FromNumber(100);
1526 
1527     while (step_->number < 4) {
1528       while (step_->number % 2 != role_) {
1529         step_->condvar.Wait();
1530       }
1531       switch (step_->number) {
1532       case 0:
1533         {
1534           // Clean up remnants of earlier test runs.
1535           file_util::Delete(directory_manager_->GetSyncDataDatabasePath(),
1536                             true);
1537           // Test.
1538           directory_manager_->Open(dirname);
1539           ScopedDirLookup dir(directory_manager_, dirname);
1540           CHECK(dir.good());
1541           WriteTransaction trans(dir, UNITTEST, __FILE__, __LINE__);
1542           MutableEntry me(&trans, CREATE, trans.root_id(), "Jeff");
1543           me.Put(BASE_VERSION, 1);
1544           me.Put(ID, jeff_id);
1545           PutDataAsBookmarkFavicon(&trans, &me, test_bytes,
1546                                      sizeof(test_bytes));
1547         }
1548         {
1549           ScopedDirLookup dir(directory_manager_, dirname);
1550           CHECK(dir.good());
1551           dir->SaveChanges();
1552         }
1553         directory_manager_->Close(dirname);
1554         break;
1555       case 1:
1556         {
1557           directory_manager_->Open(dirname);
1558           ScopedDirLookup dir(directory_manager_, dirname);
1559           CHECK(dir.good());
1560         }
1561         break;
1562       case 2:
1563         {
1564           ScopedDirLookup dir(directory_manager_, dirname);
1565           CHECK(dir.good());
1566         }
1567         break;
1568       case 3:
1569         {
1570           ScopedDirLookup dir(directory_manager_, dirname);
1571           CHECK(dir.good());
1572           ReadTransaction trans(dir, __FILE__, __LINE__);
1573           Entry e(&trans, GET_BY_ID, jeff_id);
1574           ExpectDataFromBookmarkFaviconEquals(&trans, &e, test_bytes,
1575                                                 sizeof(test_bytes));
1576         }
1577         // Same result as CloseAllDirectories, but more code coverage.
1578         directory_manager_->Close(dirname);
1579         break;
1580       }
1581       step_->number += 1;
1582       step_->condvar.Signal();
1583     }
1584   }
1585 
1586   DISALLOW_COPY_AND_ASSIGN(DirectoryKernelStalenessBugDelegate);
1587 };
1588 
TEST(SyncableDirectoryManager,DirectoryKernelStalenessBug)1589 TEST(SyncableDirectoryManager, DirectoryKernelStalenessBug) {
1590   Step step;
1591 
1592   DirectoryManager dirman(FilePath(FILE_PATH_LITERAL(".")));
1593   DirectoryKernelStalenessBugDelegate thread_delegate_1(0, &step, &dirman);
1594   DirectoryKernelStalenessBugDelegate thread_delegate_2(1, &step, &dirman);
1595 
1596   base::PlatformThreadHandle thread_handle_1;
1597   base::PlatformThreadHandle thread_handle_2;
1598 
1599   ASSERT_TRUE(
1600       base::PlatformThread::Create(0, &thread_delegate_1, &thread_handle_1));
1601   ASSERT_TRUE(
1602       base::PlatformThread::Create(0, &thread_delegate_2, &thread_handle_2));
1603 
1604   base::PlatformThread::Join(thread_handle_1);
1605   base::PlatformThread::Join(thread_handle_2);
1606 }
1607 
1608 class StressTransactionsDelegate : public base::PlatformThread::Delegate {
1609  public:
StressTransactionsDelegate(DirectoryManager * dm,const std::string & dirname,int thread_number)1610   StressTransactionsDelegate(DirectoryManager* dm,
1611                              const std::string& dirname,
1612                              int thread_number)
1613       : directory_manager_(dm),
1614         dirname_(dirname),
1615         thread_number_(thread_number) {}
1616 
1617  private:
1618   DirectoryManager* const directory_manager_;
1619   std::string dirname_;
1620   const int thread_number_;
1621 
1622   // PlatformThread::Delegate methods:
ThreadMain()1623   virtual void ThreadMain() {
1624     ScopedDirLookup dir(directory_manager_, dirname_);
1625     CHECK(dir.good());
1626     int entry_count = 0;
1627     std::string path_name;
1628 
1629     for (int i = 0; i < 20; ++i) {
1630       const int rand_action = rand() % 10;
1631       if (rand_action < 4 && !path_name.empty()) {
1632         ReadTransaction trans(dir, __FILE__, __LINE__);
1633         CHECK(1 == CountEntriesWithName(&trans, trans.root_id(), path_name));
1634         base::PlatformThread::Sleep(rand() % 10);
1635       } else {
1636         std::string unique_name = StringPrintf("%d.%d", thread_number_,
1637                                                entry_count++);
1638         path_name.assign(unique_name.begin(), unique_name.end());
1639         WriteTransaction trans(dir, UNITTEST, __FILE__, __LINE__);
1640         MutableEntry e(&trans, CREATE, trans.root_id(), path_name);
1641         CHECK(e.good());
1642         base::PlatformThread::Sleep(rand() % 20);
1643         e.Put(IS_UNSYNCED, true);
1644         if (e.Put(ID, TestIdFactory::FromNumber(rand())) &&
1645             e.Get(ID).ServerKnows() && !e.Get(ID).IsRoot()) {
1646            e.Put(BASE_VERSION, 1);
1647         }
1648       }
1649     }
1650   }
1651 
1652   DISALLOW_COPY_AND_ASSIGN(StressTransactionsDelegate);
1653 };
1654 
TEST(SyncableDirectory,StressTransactions)1655 TEST(SyncableDirectory, StressTransactions) {
1656   DirectoryManager dirman(FilePath(FILE_PATH_LITERAL(".")));
1657   std::string dirname = "stress";
1658   file_util::Delete(dirman.GetSyncDataDatabasePath(), true);
1659   dirman.Open(dirname);
1660 
1661   const int kThreadCount = 7;
1662   base::PlatformThreadHandle threads[kThreadCount];
1663   scoped_ptr<StressTransactionsDelegate> thread_delegates[kThreadCount];
1664 
1665   for (int i = 0; i < kThreadCount; ++i) {
1666     thread_delegates[i].reset(
1667         new StressTransactionsDelegate(&dirman, dirname, i));
1668     ASSERT_TRUE(base::PlatformThread::Create(
1669         0, thread_delegates[i].get(), &threads[i]));
1670   }
1671 
1672   for (int i = 0; i < kThreadCount; ++i) {
1673     base::PlatformThread::Join(threads[i]);
1674   }
1675 
1676   dirman.Close(dirname);
1677   file_util::Delete(dirman.GetSyncDataDatabasePath(), true);
1678 }
1679 
1680 class SyncableClientTagTest : public SyncableDirectoryTest {
1681  public:
1682   static const int kBaseVersion = 1;
1683   const char* test_name_;
1684   const char* test_tag_;
1685 
SyncableClientTagTest()1686   SyncableClientTagTest() : test_name_("test_name"), test_tag_("dietcoke") {}
1687 
CreateWithDefaultTag(Id id,bool deleted)1688   bool CreateWithDefaultTag(Id id, bool deleted) {
1689     return CreateWithTag(test_tag_, id, deleted);
1690   }
1691 
1692   // Attempt to create an entry with a default tag.
CreateWithTag(const char * tag,Id id,bool deleted)1693   bool CreateWithTag(const char* tag, Id id, bool deleted) {
1694     WriteTransaction wtrans(dir_.get(), UNITTEST, __FILE__, __LINE__);
1695     MutableEntry me(&wtrans, CREATE, wtrans.root_id(), test_name_);
1696     CHECK(me.good());
1697     me.Put(ID, id);
1698     if (id.ServerKnows()) {
1699       me.Put(BASE_VERSION, kBaseVersion);
1700     }
1701     me.Put(IS_DEL, deleted);
1702     me.Put(IS_UNSYNCED, true);
1703     me.Put(IS_DIR, false);
1704     return me.Put(UNIQUE_CLIENT_TAG, tag);
1705   }
1706 
1707   // Verify an entry exists with the default tag.
VerifyTag(Id id,bool deleted)1708   void VerifyTag(Id id, bool deleted) {
1709     // Should still be present and valid in the client tag index.
1710     ReadTransaction trans(dir_.get(), __FILE__, __LINE__);
1711     Entry me(&trans, GET_BY_CLIENT_TAG, test_tag_);
1712     CHECK(me.good());
1713     EXPECT_EQ(me.Get(ID), id);
1714     EXPECT_EQ(me.Get(UNIQUE_CLIENT_TAG), test_tag_);
1715     EXPECT_EQ(me.Get(IS_DEL), deleted);
1716     EXPECT_EQ(me.Get(IS_UNSYNCED), true);
1717   }
1718 
1719  protected:
1720   TestIdFactory factory_;
1721 };
1722 
TEST_F(SyncableClientTagTest,TestClientTagClear)1723 TEST_F(SyncableClientTagTest, TestClientTagClear) {
1724   Id server_id = factory_.NewServerId();
1725   EXPECT_TRUE(CreateWithDefaultTag(server_id, false));
1726   {
1727     WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__);
1728     MutableEntry me(&trans, GET_BY_CLIENT_TAG, test_tag_);
1729     EXPECT_TRUE(me.good());
1730     me.Put(UNIQUE_CLIENT_TAG, "");
1731   }
1732   {
1733     ReadTransaction trans(dir_.get(), __FILE__, __LINE__);
1734     Entry by_tag(&trans, GET_BY_CLIENT_TAG, test_tag_);
1735     EXPECT_FALSE(by_tag.good());
1736 
1737     Entry by_id(&trans, GET_BY_ID, server_id);
1738     EXPECT_TRUE(by_id.good());
1739     EXPECT_TRUE(by_id.Get(UNIQUE_CLIENT_TAG).empty());
1740   }
1741 }
1742 
TEST_F(SyncableClientTagTest,TestClientTagIndexServerId)1743 TEST_F(SyncableClientTagTest, TestClientTagIndexServerId) {
1744   Id server_id = factory_.NewServerId();
1745   EXPECT_TRUE(CreateWithDefaultTag(server_id, false));
1746   VerifyTag(server_id, false);
1747 }
1748 
TEST_F(SyncableClientTagTest,TestClientTagIndexClientId)1749 TEST_F(SyncableClientTagTest, TestClientTagIndexClientId) {
1750   Id client_id = factory_.NewLocalId();
1751   EXPECT_TRUE(CreateWithDefaultTag(client_id, false));
1752   VerifyTag(client_id, false);
1753 }
1754 
TEST_F(SyncableClientTagTest,TestDeletedClientTagIndexClientId)1755 TEST_F(SyncableClientTagTest, TestDeletedClientTagIndexClientId) {
1756   Id client_id = factory_.NewLocalId();
1757   EXPECT_TRUE(CreateWithDefaultTag(client_id, true));
1758   VerifyTag(client_id, true);
1759 }
1760 
TEST_F(SyncableClientTagTest,TestDeletedClientTagIndexServerId)1761 TEST_F(SyncableClientTagTest, TestDeletedClientTagIndexServerId) {
1762   Id server_id = factory_.NewServerId();
1763   EXPECT_TRUE(CreateWithDefaultTag(server_id, true));
1764   VerifyTag(server_id, true);
1765 }
1766 
TEST_F(SyncableClientTagTest,TestClientTagIndexDuplicateServer)1767 TEST_F(SyncableClientTagTest, TestClientTagIndexDuplicateServer) {
1768   EXPECT_TRUE(CreateWithDefaultTag(factory_.NewServerId(), true));
1769   EXPECT_FALSE(CreateWithDefaultTag(factory_.NewServerId(), true));
1770   EXPECT_FALSE(CreateWithDefaultTag(factory_.NewServerId(), false));
1771   EXPECT_FALSE(CreateWithDefaultTag(factory_.NewLocalId(), false));
1772   EXPECT_FALSE(CreateWithDefaultTag(factory_.NewLocalId(), true));
1773 }
1774 
1775 }  // namespace
1776 }  // namespace syncable
1777