- add sources.
[platform/framework/web/crosswalk.git] / src / sync / syncable / syncable_unittest.cc
1 // Copyright 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 <string>
6
7 #include "base/basictypes.h"
8 #include "base/compiler_specific.h"
9 #include "base/file_util.h"
10 #include "base/files/file_path.h"
11 #include "base/files/scoped_temp_dir.h"
12 #include "base/location.h"
13 #include "base/logging.h"
14 #include "base/memory/scoped_ptr.h"
15 #include "base/message_loop/message_loop.h"
16 #include "base/stl_util.h"
17 #include "base/strings/stringprintf.h"
18 #include "base/synchronization/condition_variable.h"
19 #include "base/test/values_test_util.h"
20 #include "base/threading/platform_thread.h"
21 #include "base/values.h"
22 #include "sync/protocol/bookmark_specifics.pb.h"
23 #include "sync/syncable/directory_backing_store.h"
24 #include "sync/syncable/directory_change_delegate.h"
25 #include "sync/syncable/in_memory_directory_backing_store.h"
26 #include "sync/syncable/metahandle_set.h"
27 #include "sync/syncable/mutable_entry.h"
28 #include "sync/syncable/on_disk_directory_backing_store.h"
29 #include "sync/syncable/syncable_proto_util.h"
30 #include "sync/syncable/syncable_read_transaction.h"
31 #include "sync/syncable/syncable_util.h"
32 #include "sync/syncable/syncable_write_transaction.h"
33 #include "sync/test/engine/test_id_factory.h"
34 #include "sync/test/engine/test_syncable_utils.h"
35 #include "sync/test/fake_encryptor.h"
36 #include "sync/test/null_directory_change_delegate.h"
37 #include "sync/test/null_transaction_observer.h"
38 #include "sync/util/test_unrecoverable_error_handler.h"
39 #include "testing/gtest/include/gtest/gtest.h"
40
41 namespace syncer {
42 namespace syncable {
43
44 using base::ExpectDictBooleanValue;
45 using base::ExpectDictStringValue;
46
47 class SyncableKernelTest : public testing::Test {};
48
49 // TODO(akalin): Add unit tests for EntryKernel::ContainsString().
50
51 TEST_F(SyncableKernelTest, ToValue) {
52   EntryKernel kernel;
53   scoped_ptr<base::DictionaryValue> value(kernel.ToValue(NULL));
54   if (value) {
55     // Not much to check without repeating the ToValue() code.
56     EXPECT_TRUE(value->HasKey("isDirty"));
57     // The extra +2 is for "isDirty" and "serverModelType".
58     EXPECT_EQ(BIT_TEMPS_END - BEGIN_FIELDS + 2,
59               static_cast<int>(value->size()));
60   } else {
61     ADD_FAILURE();
62   }
63 }
64
65 namespace {
66 void PutDataAsBookmarkFavicon(WriteTransaction* wtrans,
67                               MutableEntry* e,
68                               const char* bytes,
69                               size_t bytes_length) {
70   sync_pb::EntitySpecifics specifics;
71   specifics.mutable_bookmark()->set_url("http://demo/");
72   specifics.mutable_bookmark()->set_favicon(bytes, bytes_length);
73   e->PutSpecifics(specifics);
74 }
75
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->GetSpecifics().has_bookmark());
82   ASSERT_EQ("http://demo/", e->GetSpecifics().bookmark().url());
83   ASSERT_EQ(std::string(bytes, bytes_length),
84             e->GetSpecifics().bookmark().favicon());
85 }
86 }  // namespace
87
88 class SyncableGeneralTest : public testing::Test {
89  public:
90   static const char kIndexTestName[];
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
97   virtual void TearDown() {
98   }
99  protected:
100   base::MessageLoop message_loop_;
101   base::ScopedTempDir temp_dir_;
102   NullDirectoryChangeDelegate delegate_;
103   FakeEncryptor encryptor_;
104   TestUnrecoverableErrorHandler handler_;
105   base::FilePath db_path_;
106 };
107
108 const char SyncableGeneralTest::kIndexTestName[] = "IndexTest";
109
110 TEST_F(SyncableGeneralTest, General) {
111   Directory dir(new InMemoryDirectoryBackingStore("SimpleTest"),
112                 &handler_,
113                 NULL,
114                 NULL,
115                 NULL);
116
117   ASSERT_EQ(OPENED, dir.Open(
118           "SimpleTest", &delegate_, NullTransactionObserver()));
119
120   int64 root_metahandle;
121   {
122     ReadTransaction rtrans(FROM_HERE, &dir);
123     Entry e(&rtrans, GET_BY_ID, rtrans.root_id());
124     ASSERT_TRUE(e.good());
125     root_metahandle = e.GetMetahandle();
126   }
127
128   int64 written_metahandle;
129   const Id id = TestIdFactory::FromNumber(99);
130   std::string name = "Jeff";
131   // Test simple read operations on an empty DB.
132   {
133     ReadTransaction rtrans(FROM_HERE, &dir);
134     Entry e(&rtrans, GET_BY_ID, id);
135     ASSERT_FALSE(e.good());  // Hasn't been written yet.
136
137     Directory::Metahandles child_handles;
138     dir.GetChildHandlesById(&rtrans, rtrans.root_id(), &child_handles);
139     EXPECT_TRUE(child_handles.empty());
140
141     dir.GetChildHandlesByHandle(&rtrans, root_metahandle, &child_handles);
142     EXPECT_TRUE(child_handles.empty());
143   }
144
145   // Test creating a new meta entry.
146   {
147     WriteTransaction wtrans(FROM_HERE, UNITTEST, &dir);
148     MutableEntry me(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(), name);
149     ASSERT_TRUE(me.good());
150     me.PutId(id);
151     me.PutBaseVersion(1);
152     written_metahandle = me.GetMetahandle();
153   }
154
155   // Test GetChildHandles* after something is now in the DB.
156   // Also check that GET_BY_ID works.
157   {
158     ReadTransaction rtrans(FROM_HERE, &dir);
159     Entry e(&rtrans, GET_BY_ID, id);
160     ASSERT_TRUE(e.good());
161
162     Directory::Metahandles child_handles;
163     dir.GetChildHandlesById(&rtrans, rtrans.root_id(), &child_handles);
164     EXPECT_EQ(1u, child_handles.size());
165
166     for (Directory::Metahandles::iterator i = child_handles.begin();
167          i != child_handles.end(); ++i) {
168       EXPECT_EQ(*i, written_metahandle);
169     }
170
171     dir.GetChildHandlesByHandle(&rtrans, root_metahandle, &child_handles);
172     EXPECT_EQ(1u, child_handles.size());
173
174     for (Directory::Metahandles::iterator i = child_handles.begin();
175          i != child_handles.end(); ++i) {
176       EXPECT_EQ(*i, written_metahandle);
177     }
178   }
179
180   // Test writing data to an entity. Also check that GET_BY_HANDLE works.
181   static const char s[] = "Hello World.";
182   {
183     WriteTransaction trans(FROM_HERE, UNITTEST, &dir);
184     MutableEntry e(&trans, GET_BY_HANDLE, written_metahandle);
185     ASSERT_TRUE(e.good());
186     PutDataAsBookmarkFavicon(&trans, &e, s, sizeof(s));
187   }
188
189   // Test reading back the contents that we just wrote.
190   {
191     WriteTransaction trans(FROM_HERE, UNITTEST, &dir);
192     MutableEntry e(&trans, GET_BY_HANDLE, written_metahandle);
193     ASSERT_TRUE(e.good());
194     ExpectDataFromBookmarkFaviconEquals(&trans, &e, s, sizeof(s));
195   }
196
197   // Verify it exists in the folder.
198   {
199     ReadTransaction rtrans(FROM_HERE, &dir);
200     EXPECT_EQ(1, CountEntriesWithName(&rtrans, rtrans.root_id(), name));
201   }
202
203   // Now delete it.
204   {
205     WriteTransaction trans(FROM_HERE, UNITTEST, &dir);
206     MutableEntry e(&trans, GET_BY_HANDLE, written_metahandle);
207     e.PutIsDel(true);
208
209     EXPECT_EQ(0, CountEntriesWithName(&trans, trans.root_id(), name));
210   }
211
212   dir.SaveChanges();
213 }
214
215 TEST_F(SyncableGeneralTest, ChildrenOps) {
216   Directory dir(new InMemoryDirectoryBackingStore("SimpleTest"),
217                 &handler_,
218                 NULL,
219                 NULL,
220                 NULL);
221   ASSERT_EQ(OPENED, dir.Open(
222           "SimpleTest", &delegate_, NullTransactionObserver()));
223
224   int64 written_metahandle;
225   const Id id = TestIdFactory::FromNumber(99);
226   std::string name = "Jeff";
227   {
228     ReadTransaction rtrans(FROM_HERE, &dir);
229     Entry e(&rtrans, GET_BY_ID, id);
230     ASSERT_FALSE(e.good());  // Hasn't been written yet.
231
232     Entry root(&rtrans, GET_BY_ID, rtrans.root_id());
233     ASSERT_TRUE(root.good());
234     EXPECT_FALSE(dir.HasChildren(&rtrans, rtrans.root_id()));
235     EXPECT_TRUE(root.GetFirstChildId().IsRoot());
236   }
237
238   {
239     WriteTransaction wtrans(FROM_HERE, UNITTEST, &dir);
240     MutableEntry me(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(), name);
241     ASSERT_TRUE(me.good());
242     me.PutId(id);
243     me.PutBaseVersion(1);
244     written_metahandle = me.GetMetahandle();
245   }
246
247   // Test children ops after something is now in the DB.
248   {
249     ReadTransaction rtrans(FROM_HERE, &dir);
250     Entry e(&rtrans, GET_BY_ID, id);
251     ASSERT_TRUE(e.good());
252
253     Entry child(&rtrans, GET_BY_HANDLE, written_metahandle);
254     ASSERT_TRUE(child.good());
255
256     Entry root(&rtrans, GET_BY_ID, rtrans.root_id());
257     ASSERT_TRUE(root.good());
258     EXPECT_TRUE(dir.HasChildren(&rtrans, rtrans.root_id()));
259     EXPECT_EQ(e.GetId(), root.GetFirstChildId());
260   }
261
262   {
263     WriteTransaction wtrans(FROM_HERE, UNITTEST, &dir);
264     MutableEntry me(&wtrans, GET_BY_HANDLE, written_metahandle);
265     ASSERT_TRUE(me.good());
266     me.PutIsDel(true);
267   }
268
269   // Test children ops after the children have been deleted.
270   {
271     ReadTransaction rtrans(FROM_HERE, &dir);
272     Entry e(&rtrans, GET_BY_ID, id);
273     ASSERT_TRUE(e.good());
274
275     Entry root(&rtrans, GET_BY_ID, rtrans.root_id());
276     ASSERT_TRUE(root.good());
277     EXPECT_FALSE(dir.HasChildren(&rtrans, rtrans.root_id()));
278     EXPECT_TRUE(root.GetFirstChildId().IsRoot());
279   }
280
281   dir.SaveChanges();
282 }
283
284 TEST_F(SyncableGeneralTest, ClientIndexRebuildsProperly) {
285   int64 written_metahandle;
286   TestIdFactory factory;
287   const Id id = factory.NewServerId();
288   std::string name = "cheesepuffs";
289   std::string tag = "dietcoke";
290
291   // Test creating a new meta entry.
292   {
293     Directory dir(new OnDiskDirectoryBackingStore(kIndexTestName, db_path_),
294                   &handler_,
295                   NULL,
296                   NULL,
297                   NULL);
298     ASSERT_EQ(OPENED, dir.Open(kIndexTestName, &delegate_,
299                                NullTransactionObserver()));
300     {
301       WriteTransaction wtrans(FROM_HERE, UNITTEST, &dir);
302       MutableEntry me(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(), name);
303       ASSERT_TRUE(me.good());
304       me.PutId(id);
305       me.PutBaseVersion(1);
306       me.PutUniqueClientTag(tag);
307       written_metahandle = me.GetMetahandle();
308     }
309     dir.SaveChanges();
310   }
311
312   // The DB was closed. Now reopen it. This will cause index regeneration.
313   {
314     Directory dir(new OnDiskDirectoryBackingStore(kIndexTestName, db_path_),
315                   &handler_,
316                   NULL,
317                   NULL,
318                   NULL);
319     ASSERT_EQ(OPENED, dir.Open(kIndexTestName,
320                                &delegate_, NullTransactionObserver()));
321
322     ReadTransaction trans(FROM_HERE, &dir);
323     Entry me(&trans, GET_BY_CLIENT_TAG, tag);
324     ASSERT_TRUE(me.good());
325     EXPECT_EQ(me.GetId(), id);
326     EXPECT_EQ(me.GetBaseVersion(), 1);
327     EXPECT_EQ(me.GetUniqueClientTag(), tag);
328     EXPECT_EQ(me.GetMetahandle(), written_metahandle);
329   }
330 }
331
332 TEST_F(SyncableGeneralTest, ClientIndexRebuildsDeletedProperly) {
333   TestIdFactory factory;
334   const Id id = factory.NewServerId();
335   std::string tag = "dietcoke";
336
337   // Test creating a deleted, unsynced, server meta entry.
338   {
339     Directory dir(new OnDiskDirectoryBackingStore(kIndexTestName, db_path_),
340                   &handler_,
341                   NULL,
342                   NULL,
343                   NULL);
344     ASSERT_EQ(OPENED, dir.Open(kIndexTestName, &delegate_,
345                                 NullTransactionObserver()));
346     {
347       WriteTransaction wtrans(FROM_HERE, UNITTEST, &dir);
348       MutableEntry me(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(), "deleted");
349       ASSERT_TRUE(me.good());
350       me.PutId(id);
351       me.PutBaseVersion(1);
352       me.PutUniqueClientTag(tag);
353       me.PutIsDel(true);
354       me.PutIsUnsynced(true);  // Or it might be purged.
355     }
356     dir.SaveChanges();
357   }
358
359   // The DB was closed. Now reopen it. This will cause index regeneration.
360   // Should still be present and valid in the client tag index.
361   {
362     Directory dir(new OnDiskDirectoryBackingStore(kIndexTestName, db_path_),
363                   &handler_,
364                   NULL,
365                   NULL,
366                   NULL);
367     ASSERT_EQ(OPENED, dir.Open(kIndexTestName, &delegate_,
368                                NullTransactionObserver()));
369
370     ReadTransaction trans(FROM_HERE, &dir);
371     Entry me(&trans, GET_BY_CLIENT_TAG, tag);
372     ASSERT_TRUE(me.good());
373     EXPECT_EQ(me.GetId(), id);
374     EXPECT_EQ(me.GetUniqueClientTag(), tag);
375     EXPECT_TRUE(me.GetIsDel());
376     EXPECT_TRUE(me.GetIsUnsynced());
377   }
378 }
379
380 TEST_F(SyncableGeneralTest, ToValue) {
381   Directory dir(new InMemoryDirectoryBackingStore("SimpleTest"),
382                 &handler_,
383                 NULL,
384                 NULL,
385                 NULL);
386   ASSERT_EQ(OPENED, dir.Open(
387           "SimpleTest", &delegate_, NullTransactionObserver()));
388
389   const Id id = TestIdFactory::FromNumber(99);
390   {
391     ReadTransaction rtrans(FROM_HERE, &dir);
392     Entry e(&rtrans, GET_BY_ID, id);
393     EXPECT_FALSE(e.good());  // Hasn't been written yet.
394
395     scoped_ptr<base::DictionaryValue> value(e.ToValue(NULL));
396     ExpectDictBooleanValue(false, *value, "good");
397     EXPECT_EQ(1u, value->size());
398   }
399
400   // Test creating a new meta entry.
401   {
402     WriteTransaction wtrans(FROM_HERE, UNITTEST, &dir);
403     MutableEntry me(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(), "new");
404     ASSERT_TRUE(me.good());
405     me.PutId(id);
406     me.PutBaseVersion(1);
407
408     scoped_ptr<base::DictionaryValue> value(me.ToValue(NULL));
409     ExpectDictBooleanValue(true, *value, "good");
410     EXPECT_TRUE(value->HasKey("kernel"));
411     ExpectDictStringValue("Bookmarks", *value, "modelType");
412     ExpectDictBooleanValue(true, *value, "existsOnClientBecauseNameIsNonEmpty");
413     ExpectDictBooleanValue(false, *value, "isRoot");
414   }
415
416   dir.SaveChanges();
417 }
418
419 // Test that the bookmark tag generation algorithm remains unchanged.
420 TEST_F(SyncableGeneralTest, BookmarkTagTest) {
421   InMemoryDirectoryBackingStore* store = new InMemoryDirectoryBackingStore("x");
422
423   // The two inputs that form the bookmark tag are the directory's cache_guid
424   // and its next_id value.  We don't need to take any action to ensure
425   // consistent next_id values, but we do need to explicitly request that our
426   // InMemoryDirectoryBackingStore always return the same cache_guid.
427   store->request_consistent_cache_guid();
428
429   Directory dir(store, &handler_, NULL, NULL, NULL);
430   ASSERT_EQ(OPENED, dir.Open("x", &delegate_, NullTransactionObserver()));
431
432   {
433     WriteTransaction wtrans(FROM_HERE, UNITTEST, &dir);
434     MutableEntry bm(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(), "bm");
435     bm.PutIsUnsynced(true);
436
437     // If this assertion fails, that might indicate that the algorithm used to
438     // generate bookmark tags has been modified.  This could have implications
439     // for bookmark ordering.  Please make sure you know what you're doing if
440     // you intend to make such a change.
441     ASSERT_EQ("6wHRAb3kbnXV5GHrejp4/c1y5tw=", bm.GetUniqueBookmarkTag());
442   }
443 }
444
445 // A test fixture for syncable::Directory.  Uses an in-memory database to keep
446 // the unit tests fast.
447 class SyncableDirectoryTest : public testing::Test {
448  protected:
449   base::MessageLoop message_loop_;
450   static const char kName[];
451
452   virtual void SetUp() {
453     dir_.reset(new Directory(new InMemoryDirectoryBackingStore(kName),
454                              &handler_,
455                              NULL,
456                              NULL,
457                              NULL));
458     ASSERT_TRUE(dir_.get());
459     ASSERT_EQ(OPENED, dir_->Open(kName, &delegate_,
460                                  NullTransactionObserver()));
461     ASSERT_TRUE(dir_->good());
462   }
463
464   virtual void TearDown() {
465     if (dir_)
466       dir_->SaveChanges();
467     dir_.reset();
468   }
469
470   void GetAllMetaHandles(BaseTransaction* trans, MetahandleSet* result) {
471     dir_->GetAllMetaHandles(trans, result);
472   }
473
474   bool IsInDirtyMetahandles(int64 metahandle) {
475     return 1 == dir_->kernel_->dirty_metahandles.count(metahandle);
476   }
477
478   bool IsInMetahandlesToPurge(int64 metahandle) {
479     return 1 == dir_->kernel_->metahandles_to_purge.count(metahandle);
480   }
481
482   void CheckPurgeEntriesWithTypeInSucceeded(ModelTypeSet types_to_purge,
483                                             bool before_reload) {
484     SCOPED_TRACE(testing::Message("Before reload: ") << before_reload);
485     {
486       ReadTransaction trans(FROM_HERE, dir_.get());
487       MetahandleSet all_set;
488       dir_->GetAllMetaHandles(&trans, &all_set);
489       EXPECT_EQ(4U, all_set.size());
490       if (before_reload)
491         EXPECT_EQ(6U, dir_->kernel_->metahandles_to_purge.size());
492       for (MetahandleSet::iterator iter = all_set.begin();
493            iter != all_set.end(); ++iter) {
494         Entry e(&trans, GET_BY_HANDLE, *iter);
495         const ModelType local_type = e.GetModelType();
496         const ModelType server_type = e.GetServerModelType();
497
498         // Note the dance around incrementing |it|, since we sometimes erase().
499         if ((IsRealDataType(local_type) &&
500              types_to_purge.Has(local_type)) ||
501             (IsRealDataType(server_type) &&
502              types_to_purge.Has(server_type))) {
503           FAIL() << "Illegal type should have been deleted.";
504         }
505       }
506     }
507
508     for (ModelTypeSet::Iterator it = types_to_purge.First();
509          it.Good(); it.Inc()) {
510       EXPECT_FALSE(dir_->InitialSyncEndedForType(it.Get()));
511     }
512     EXPECT_FALSE(types_to_purge.Has(BOOKMARKS));
513     EXPECT_TRUE(dir_->InitialSyncEndedForType(BOOKMARKS));
514   }
515
516   FakeEncryptor encryptor_;
517   TestUnrecoverableErrorHandler handler_;
518   scoped_ptr<Directory> dir_;
519   NullDirectoryChangeDelegate delegate_;
520
521   // Creates an empty entry and sets the ID field to a default one.
522   void CreateEntry(const std::string& entryname) {
523     CreateEntry(entryname, TestIdFactory::FromNumber(-99));
524   }
525
526   // Creates an empty entry and sets the ID field to id.
527   void CreateEntry(const std::string& entryname, const int id) {
528     CreateEntry(entryname, TestIdFactory::FromNumber(id));
529   }
530   void CreateEntry(const std::string& entryname, Id id) {
531     WriteTransaction wtrans(FROM_HERE, UNITTEST, dir_.get());
532     MutableEntry me(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(), entryname);
533     ASSERT_TRUE(me.good());
534     me.PutId(id);
535     me.PutIsUnsynced(true);
536   }
537
538   void ValidateEntry(BaseTransaction* trans,
539                      int64 id,
540                      bool check_name,
541                      const std::string& name,
542                      int64 base_version,
543                      int64 server_version,
544                      bool is_del);
545
546   // When a directory is saved then loaded from disk, it will pass through
547   // DropDeletedEntries().  This will remove some entries from the directory.
548   // This function is intended to simulate that process.
549   //
550   // WARNING: The directory will be deleted by this operation.  You should
551   // not have any pointers to the directory (open transactions included)
552   // when you call this.
553   DirOpenResult SimulateSaveAndReloadDir();
554
555   // This function will close and re-open the directory without saving any
556   // pending changes.  This is intended to simulate the recovery from a crash
557   // scenario.  The same warnings for SimulateSaveAndReloadDir apply here.
558   DirOpenResult SimulateCrashAndReloadDir();
559
560  private:
561   // A helper function for Simulate{Save,Crash}AndReloadDir.
562   DirOpenResult ReloadDirImpl();
563 };
564
565 TEST_F(SyncableDirectoryTest, TakeSnapshotGetsMetahandlesToPurge) {
566   const int metas_to_create = 50;
567   MetahandleSet expected_purges;
568   MetahandleSet all_handles;
569   {
570     WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get());
571     for (int i = 0; i < metas_to_create; i++) {
572       MutableEntry e(&trans, CREATE, BOOKMARKS, trans.root_id(), "foo");
573       e.PutIsUnsynced(true);
574       sync_pb::EntitySpecifics specs;
575       if (i % 2 == 0) {
576         AddDefaultFieldValue(BOOKMARKS, &specs);
577         expected_purges.insert(e.GetMetahandle());
578         all_handles.insert(e.GetMetahandle());
579       } else {
580         AddDefaultFieldValue(PREFERENCES, &specs);
581         all_handles.insert(e.GetMetahandle());
582       }
583       e.PutSpecifics(specs);
584       e.PutServerSpecifics(specs);
585     }
586   }
587
588   ModelTypeSet to_purge(BOOKMARKS);
589   dir_->PurgeEntriesWithTypeIn(to_purge, ModelTypeSet(), ModelTypeSet());
590
591   Directory::SaveChangesSnapshot snapshot1;
592   base::AutoLock scoped_lock(dir_->kernel_->save_changes_mutex);
593   dir_->TakeSnapshotForSaveChanges(&snapshot1);
594   EXPECT_TRUE(expected_purges == snapshot1.metahandles_to_purge);
595
596   to_purge.Clear();
597   to_purge.Put(PREFERENCES);
598   dir_->PurgeEntriesWithTypeIn(to_purge, ModelTypeSet(), ModelTypeSet());
599
600   dir_->HandleSaveChangesFailure(snapshot1);
601
602   Directory::SaveChangesSnapshot snapshot2;
603   dir_->TakeSnapshotForSaveChanges(&snapshot2);
604   EXPECT_TRUE(all_handles == snapshot2.metahandles_to_purge);
605 }
606
607 TEST_F(SyncableDirectoryTest, TakeSnapshotGetsAllDirtyHandlesTest) {
608   const int metahandles_to_create = 100;
609   std::vector<int64> expected_dirty_metahandles;
610   {
611     WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get());
612     for (int i = 0; i < metahandles_to_create; i++) {
613       MutableEntry e(&trans, CREATE, BOOKMARKS, trans.root_id(), "foo");
614       expected_dirty_metahandles.push_back(e.GetMetahandle());
615       e.PutIsUnsynced(true);
616     }
617   }
618   // Fake SaveChanges() and make sure we got what we expected.
619   {
620     Directory::SaveChangesSnapshot snapshot;
621     base::AutoLock scoped_lock(dir_->kernel_->save_changes_mutex);
622     dir_->TakeSnapshotForSaveChanges(&snapshot);
623     // Make sure there's an entry for each new metahandle.  Make sure all
624     // entries are marked dirty.
625     ASSERT_EQ(expected_dirty_metahandles.size(), snapshot.dirty_metas.size());
626     for (EntryKernelSet::const_iterator i = snapshot.dirty_metas.begin();
627         i != snapshot.dirty_metas.end(); ++i) {
628       ASSERT_TRUE((*i)->is_dirty());
629     }
630     dir_->VacuumAfterSaveChanges(snapshot);
631   }
632   // Put a new value with existing transactions as well as adding new ones.
633   {
634     WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get());
635     std::vector<int64> new_dirty_metahandles;
636     for (std::vector<int64>::const_iterator i =
637         expected_dirty_metahandles.begin();
638         i != expected_dirty_metahandles.end(); ++i) {
639         // Change existing entries to directories to dirty them.
640         MutableEntry e1(&trans, GET_BY_HANDLE, *i);
641         e1.PutIsDir(true);
642         e1.PutIsUnsynced(true);
643         // Add new entries
644         MutableEntry e2(&trans, CREATE, BOOKMARKS, trans.root_id(), "bar");
645         e2.PutIsUnsynced(true);
646         new_dirty_metahandles.push_back(e2.GetMetahandle());
647     }
648     expected_dirty_metahandles.insert(expected_dirty_metahandles.end(),
649         new_dirty_metahandles.begin(), new_dirty_metahandles.end());
650   }
651   // Fake SaveChanges() and make sure we got what we expected.
652   {
653     Directory::SaveChangesSnapshot snapshot;
654     base::AutoLock scoped_lock(dir_->kernel_->save_changes_mutex);
655     dir_->TakeSnapshotForSaveChanges(&snapshot);
656     // Make sure there's an entry for each new metahandle.  Make sure all
657     // entries are marked dirty.
658     EXPECT_EQ(expected_dirty_metahandles.size(), snapshot.dirty_metas.size());
659     for (EntryKernelSet::const_iterator i = snapshot.dirty_metas.begin();
660         i != snapshot.dirty_metas.end(); ++i) {
661       EXPECT_TRUE((*i)->is_dirty());
662     }
663     dir_->VacuumAfterSaveChanges(snapshot);
664   }
665 }
666
667 TEST_F(SyncableDirectoryTest, TakeSnapshotGetsOnlyDirtyHandlesTest) {
668   const int metahandles_to_create = 100;
669
670   // half of 2 * metahandles_to_create
671   const unsigned int number_changed = 100u;
672   std::vector<int64> expected_dirty_metahandles;
673   {
674     WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get());
675     for (int i = 0; i < metahandles_to_create; i++) {
676       MutableEntry e(&trans, CREATE, BOOKMARKS, trans.root_id(), "foo");
677       expected_dirty_metahandles.push_back(e.GetMetahandle());
678       e.PutIsUnsynced(true);
679     }
680   }
681   dir_->SaveChanges();
682   // Put a new value with existing transactions as well as adding new ones.
683   {
684     WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get());
685     std::vector<int64> new_dirty_metahandles;
686     for (std::vector<int64>::const_iterator i =
687         expected_dirty_metahandles.begin();
688         i != expected_dirty_metahandles.end(); ++i) {
689         // Change existing entries to directories to dirty them.
690         MutableEntry e1(&trans, GET_BY_HANDLE, *i);
691         ASSERT_TRUE(e1.good());
692         e1.PutIsDir(true);
693         e1.PutIsUnsynced(true);
694         // Add new entries
695         MutableEntry e2(&trans, CREATE, BOOKMARKS, trans.root_id(), "bar");
696         e2.PutIsUnsynced(true);
697         new_dirty_metahandles.push_back(e2.GetMetahandle());
698     }
699     expected_dirty_metahandles.insert(expected_dirty_metahandles.end(),
700         new_dirty_metahandles.begin(), new_dirty_metahandles.end());
701   }
702   dir_->SaveChanges();
703   // Don't make any changes whatsoever and ensure nothing comes back.
704   {
705     WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get());
706     for (std::vector<int64>::const_iterator i =
707         expected_dirty_metahandles.begin();
708         i != expected_dirty_metahandles.end(); ++i) {
709       MutableEntry e(&trans, GET_BY_HANDLE, *i);
710       ASSERT_TRUE(e.good());
711       // We aren't doing anything to dirty these entries.
712     }
713   }
714   // Fake SaveChanges() and make sure we got what we expected.
715   {
716     Directory::SaveChangesSnapshot snapshot;
717     base::AutoLock scoped_lock(dir_->kernel_->save_changes_mutex);
718     dir_->TakeSnapshotForSaveChanges(&snapshot);
719     // Make sure there are no dirty_metahandles.
720     EXPECT_EQ(0u, snapshot.dirty_metas.size());
721     dir_->VacuumAfterSaveChanges(snapshot);
722   }
723   {
724     WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get());
725     bool should_change = false;
726     for (std::vector<int64>::const_iterator i =
727         expected_dirty_metahandles.begin();
728         i != expected_dirty_metahandles.end(); ++i) {
729         // Maybe change entries by flipping IS_DIR.
730         MutableEntry e(&trans, GET_BY_HANDLE, *i);
731         ASSERT_TRUE(e.good());
732         should_change = !should_change;
733         if (should_change) {
734           bool not_dir = !e.GetIsDir();
735           e.PutIsDir(not_dir);
736           e.PutIsUnsynced(true);
737         }
738     }
739   }
740   // Fake SaveChanges() and make sure we got what we expected.
741   {
742     Directory::SaveChangesSnapshot snapshot;
743     base::AutoLock scoped_lock(dir_->kernel_->save_changes_mutex);
744     dir_->TakeSnapshotForSaveChanges(&snapshot);
745     // Make sure there's an entry for each changed metahandle.  Make sure all
746     // entries are marked dirty.
747     EXPECT_EQ(number_changed, snapshot.dirty_metas.size());
748     for (EntryKernelSet::const_iterator i = snapshot.dirty_metas.begin();
749         i != snapshot.dirty_metas.end(); ++i) {
750       EXPECT_TRUE((*i)->is_dirty());
751     }
752     dir_->VacuumAfterSaveChanges(snapshot);
753   }
754 }
755
756 // Test delete journals management.
757 TEST_F(SyncableDirectoryTest, ManageDeleteJournals) {
758   sync_pb::EntitySpecifics bookmark_specifics;
759   AddDefaultFieldValue(BOOKMARKS, &bookmark_specifics);
760   bookmark_specifics.mutable_bookmark()->set_url("url");
761
762   Id id1 = TestIdFactory::FromNumber(-1);
763   Id id2 = TestIdFactory::FromNumber(-2);
764   int64 handle1 = 0;
765   int64 handle2 = 0;
766   {
767     // Create two bookmark entries and save in database.
768     CreateEntry("item1", id1);
769     CreateEntry("item2", id2);
770     {
771       WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get());
772       MutableEntry item1(&trans, GET_BY_ID, id1);
773       ASSERT_TRUE(item1.good());
774       handle1 = item1.GetMetahandle();
775       item1.PutSpecifics(bookmark_specifics);
776       item1.PutServerSpecifics(bookmark_specifics);
777       MutableEntry item2(&trans, GET_BY_ID, id2);
778       ASSERT_TRUE(item2.good());
779       handle2 = item2.GetMetahandle();
780       item2.PutSpecifics(bookmark_specifics);
781       item2.PutServerSpecifics(bookmark_specifics);
782     }
783     ASSERT_EQ(OPENED, SimulateSaveAndReloadDir());
784   }
785
786   { // Test adding and saving delete journals.
787     DeleteJournal* delete_journal = dir_->delete_journal();
788     {
789       WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get());
790       EntryKernelSet journal_entries;
791       delete_journal->GetDeleteJournals(&trans, BOOKMARKS, &journal_entries);
792       ASSERT_EQ(0u, journal_entries.size());
793
794       // Set SERVER_IS_DEL of the entries to true and they should be added to
795       // delete journals.
796       MutableEntry item1(&trans, GET_BY_ID, id1);
797       ASSERT_TRUE(item1.good());
798       item1.PutServerIsDel(true);
799       MutableEntry item2(&trans, GET_BY_ID, id2);
800       ASSERT_TRUE(item2.good());
801       item2.PutServerIsDel(true);
802       EntryKernel tmp;
803       tmp.put(ID, id1);
804       EXPECT_TRUE(delete_journal->delete_journals_.count(&tmp));
805       tmp.put(ID, id2);
806       EXPECT_TRUE(delete_journal->delete_journals_.count(&tmp));
807     }
808
809     // Save delete journals in database and verify memory clearing.
810     ASSERT_TRUE(dir_->SaveChanges());
811     {
812       ReadTransaction trans(FROM_HERE, dir_.get());
813       EXPECT_EQ(0u, delete_journal->GetDeleteJournalSize(&trans));
814     }
815     ASSERT_EQ(OPENED, SimulateSaveAndReloadDir());
816   }
817
818   {
819     {
820       // Test reading delete journals from database.
821       WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get());
822       DeleteJournal* delete_journal = dir_->delete_journal();
823       EntryKernelSet journal_entries;
824       delete_journal->GetDeleteJournals(&trans, BOOKMARKS, &journal_entries);
825       ASSERT_EQ(2u, journal_entries.size());
826       EntryKernel tmp;
827       tmp.put(META_HANDLE, handle1);
828       EXPECT_TRUE(journal_entries.count(&tmp));
829       tmp.put(META_HANDLE, handle2);
830       EXPECT_TRUE(journal_entries.count(&tmp));
831
832       // Purge item2.
833       MetahandleSet to_purge;
834       to_purge.insert(handle2);
835       delete_journal->PurgeDeleteJournals(&trans, to_purge);
836
837       // Verify that item2 is purged from journals in memory and will be
838       // purged from database.
839       tmp.put(ID, id2);
840       EXPECT_FALSE(delete_journal->delete_journals_.count(&tmp));
841       EXPECT_EQ(1u, delete_journal->delete_journals_to_purge_.size());
842       EXPECT_TRUE(delete_journal->delete_journals_to_purge_.count(handle2));
843     }
844     ASSERT_EQ(OPENED, SimulateSaveAndReloadDir());
845   }
846
847   {
848     {
849       // Verify purged entry is gone in database.
850       WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get());
851       DeleteJournal* delete_journal = dir_->delete_journal();
852       EntryKernelSet journal_entries;
853       delete_journal->GetDeleteJournals(&trans, BOOKMARKS, &journal_entries);
854       ASSERT_EQ(1u, journal_entries.size());
855       EntryKernel tmp;
856       tmp.put(ID, id1);
857       tmp.put(META_HANDLE, handle1);
858       EXPECT_TRUE(journal_entries.count(&tmp));
859
860       // Undelete item1.
861       MutableEntry item1(&trans, GET_BY_ID, id1);
862       ASSERT_TRUE(item1.good());
863       item1.PutServerIsDel(false);
864       EXPECT_TRUE(delete_journal->delete_journals_.empty());
865       EXPECT_EQ(1u, delete_journal->delete_journals_to_purge_.size());
866       EXPECT_TRUE(delete_journal->delete_journals_to_purge_.count(handle1));
867     }
868     ASSERT_EQ(OPENED, SimulateSaveAndReloadDir());
869   }
870
871   {
872     // Verify undeleted entry is gone from database.
873     ReadTransaction trans(FROM_HERE, dir_.get());
874     DeleteJournal* delete_journal = dir_->delete_journal();
875     ASSERT_EQ(0u, delete_journal->GetDeleteJournalSize(&trans));
876   }
877 }
878
879 const char SyncableDirectoryTest::kName[] = "Foo";
880
881 namespace {
882
883 TEST_F(SyncableDirectoryTest, TestBasicLookupNonExistantID) {
884   ReadTransaction rtrans(FROM_HERE, dir_.get());
885   Entry e(&rtrans, GET_BY_ID, TestIdFactory::FromNumber(-99));
886   ASSERT_FALSE(e.good());
887 }
888
889 TEST_F(SyncableDirectoryTest, TestBasicLookupValidID) {
890   CreateEntry("rtc");
891   ReadTransaction rtrans(FROM_HERE, dir_.get());
892   Entry e(&rtrans, GET_BY_ID, TestIdFactory::FromNumber(-99));
893   ASSERT_TRUE(e.good());
894 }
895
896 TEST_F(SyncableDirectoryTest, TestDelete) {
897   std::string name = "peanut butter jelly time";
898   WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get());
899   MutableEntry e1(&trans, CREATE, BOOKMARKS, trans.root_id(), name);
900   ASSERT_TRUE(e1.good());
901   e1.PutIsDel(true);
902   MutableEntry e2(&trans, CREATE, BOOKMARKS, trans.root_id(), name);
903   ASSERT_TRUE(e2.good());
904   e2.PutIsDel(true);
905   MutableEntry e3(&trans, CREATE, BOOKMARKS, trans.root_id(), name);
906   ASSERT_TRUE(e3.good());
907   e3.PutIsDel(true);
908
909   e1.PutIsDel(false);
910   e2.PutIsDel(false);
911   e3.PutIsDel(false);
912
913   e1.PutIsDel(true);
914   e2.PutIsDel(true);
915   e3.PutIsDel(true);
916 }
917
918 TEST_F(SyncableDirectoryTest, TestGetUnsynced) {
919   Directory::Metahandles handles;
920   int64 handle1, handle2;
921   {
922     WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get());
923
924     dir_->GetUnsyncedMetaHandles(&trans, &handles);
925     ASSERT_TRUE(0 == handles.size());
926
927     MutableEntry e1(&trans, CREATE, BOOKMARKS, trans.root_id(), "abba");
928     ASSERT_TRUE(e1.good());
929     handle1 = e1.GetMetahandle();
930     e1.PutBaseVersion(1);
931     e1.PutIsDir(true);
932     e1.PutId(TestIdFactory::FromNumber(101));
933
934     MutableEntry e2(&trans, CREATE, BOOKMARKS, e1.GetId(), "bread");
935     ASSERT_TRUE(e2.good());
936     handle2 = e2.GetMetahandle();
937     e2.PutBaseVersion(1);
938     e2.PutId(TestIdFactory::FromNumber(102));
939   }
940   dir_->SaveChanges();
941   {
942     WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get());
943
944     dir_->GetUnsyncedMetaHandles(&trans, &handles);
945     ASSERT_TRUE(0 == handles.size());
946
947     MutableEntry e3(&trans, GET_BY_HANDLE, handle1);
948     ASSERT_TRUE(e3.good());
949     e3.PutIsUnsynced(true);
950   }
951   dir_->SaveChanges();
952   {
953     WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get());
954     dir_->GetUnsyncedMetaHandles(&trans, &handles);
955     ASSERT_TRUE(1 == handles.size());
956     ASSERT_TRUE(handle1 == handles[0]);
957
958     MutableEntry e4(&trans, GET_BY_HANDLE, handle2);
959     ASSERT_TRUE(e4.good());
960     e4.PutIsUnsynced(true);
961   }
962   dir_->SaveChanges();
963   {
964     WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get());
965     dir_->GetUnsyncedMetaHandles(&trans, &handles);
966     ASSERT_TRUE(2 == handles.size());
967     if (handle1 == handles[0]) {
968       ASSERT_TRUE(handle2 == handles[1]);
969     } else {
970       ASSERT_TRUE(handle2 == handles[0]);
971       ASSERT_TRUE(handle1 == handles[1]);
972     }
973
974     MutableEntry e5(&trans, GET_BY_HANDLE, handle1);
975     ASSERT_TRUE(e5.good());
976     ASSERT_TRUE(e5.GetIsUnsynced());
977     ASSERT_TRUE(e5.PutIsUnsynced(false));
978     ASSERT_FALSE(e5.GetIsUnsynced());
979   }
980   dir_->SaveChanges();
981   {
982     WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get());
983     dir_->GetUnsyncedMetaHandles(&trans, &handles);
984     ASSERT_TRUE(1 == handles.size());
985     ASSERT_TRUE(handle2 == handles[0]);
986   }
987 }
988
989 TEST_F(SyncableDirectoryTest, TestGetUnappliedUpdates) {
990   std::vector<int64> handles;
991   int64 handle1, handle2;
992   const FullModelTypeSet all_types = FullModelTypeSet::All();
993   {
994     WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get());
995
996     dir_->GetUnappliedUpdateMetaHandles(&trans, all_types, &handles);
997     ASSERT_TRUE(0 == handles.size());
998
999     MutableEntry e1(&trans, CREATE, BOOKMARKS, trans.root_id(), "abba");
1000     ASSERT_TRUE(e1.good());
1001     handle1 = e1.GetMetahandle();
1002     e1.PutIsUnappliedUpdate(false);
1003     e1.PutBaseVersion(1);
1004     e1.PutId(TestIdFactory::FromNumber(101));
1005     e1.PutIsDir(true);
1006
1007     MutableEntry e2(&trans, CREATE, BOOKMARKS, e1.GetId(), "bread");
1008     ASSERT_TRUE(e2.good());
1009     handle2 = e2.GetMetahandle();
1010     e2.PutIsUnappliedUpdate(false);
1011     e2.PutBaseVersion(1);
1012     e2.PutId(TestIdFactory::FromNumber(102));
1013   }
1014   dir_->SaveChanges();
1015   {
1016     WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get());
1017
1018     dir_->GetUnappliedUpdateMetaHandles(&trans, all_types, &handles);
1019     ASSERT_TRUE(0 == handles.size());
1020
1021     MutableEntry e3(&trans, GET_BY_HANDLE, handle1);
1022     ASSERT_TRUE(e3.good());
1023     e3.PutIsUnappliedUpdate(true);
1024   }
1025   dir_->SaveChanges();
1026   {
1027     WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get());
1028     dir_->GetUnappliedUpdateMetaHandles(&trans, all_types, &handles);
1029     ASSERT_TRUE(1 == handles.size());
1030     ASSERT_TRUE(handle1 == handles[0]);
1031
1032     MutableEntry e4(&trans, GET_BY_HANDLE, handle2);
1033     ASSERT_TRUE(e4.good());
1034     e4.PutIsUnappliedUpdate(true);
1035   }
1036   dir_->SaveChanges();
1037   {
1038     WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get());
1039     dir_->GetUnappliedUpdateMetaHandles(&trans, all_types, &handles);
1040     ASSERT_TRUE(2 == handles.size());
1041     if (handle1 == handles[0]) {
1042       ASSERT_TRUE(handle2 == handles[1]);
1043     } else {
1044       ASSERT_TRUE(handle2 == handles[0]);
1045       ASSERT_TRUE(handle1 == handles[1]);
1046     }
1047
1048     MutableEntry e5(&trans, GET_BY_HANDLE, handle1);
1049     ASSERT_TRUE(e5.good());
1050     e5.PutIsUnappliedUpdate(false);
1051   }
1052   dir_->SaveChanges();
1053   {
1054     WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get());
1055     dir_->GetUnappliedUpdateMetaHandles(&trans, all_types, &handles);
1056     ASSERT_TRUE(1 == handles.size());
1057     ASSERT_TRUE(handle2 == handles[0]);
1058   }
1059 }
1060
1061
1062 TEST_F(SyncableDirectoryTest, DeleteBug_531383) {
1063   // Try to evoke a check failure...
1064   TestIdFactory id_factory;
1065   int64 grandchild_handle;
1066   {
1067     WriteTransaction wtrans(FROM_HERE, UNITTEST, dir_.get());
1068     MutableEntry parent(&wtrans, CREATE, BOOKMARKS, id_factory.root(), "Bob");
1069     ASSERT_TRUE(parent.good());
1070     parent.PutIsDir(true);
1071     parent.PutId(id_factory.NewServerId());
1072     parent.PutBaseVersion(1);
1073     MutableEntry child(&wtrans, CREATE, BOOKMARKS, parent.GetId(), "Bob");
1074     ASSERT_TRUE(child.good());
1075     child.PutIsDir(true);
1076     child.PutId(id_factory.NewServerId());
1077     child.PutBaseVersion(1);
1078     MutableEntry grandchild(&wtrans, CREATE, BOOKMARKS, child.GetId(), "Bob");
1079     ASSERT_TRUE(grandchild.good());
1080     grandchild.PutId(id_factory.NewServerId());
1081     grandchild.PutBaseVersion(1);
1082     grandchild.PutIsDel(true);
1083     MutableEntry twin(&wtrans, CREATE, BOOKMARKS, child.GetId(), "Bob");
1084     ASSERT_TRUE(twin.good());
1085     twin.PutIsDel(true);
1086     grandchild.PutIsDel(false);
1087
1088     grandchild_handle = grandchild.GetMetahandle();
1089   }
1090   dir_->SaveChanges();
1091   {
1092     WriteTransaction wtrans(FROM_HERE, UNITTEST, dir_.get());
1093     MutableEntry grandchild(&wtrans, GET_BY_HANDLE, grandchild_handle);
1094     grandchild.PutIsDel(true);  // Used to CHECK fail here.
1095   }
1096 }
1097
1098 static inline bool IsLegalNewParent(const Entry& a, const Entry& b) {
1099   return IsLegalNewParent(a.trans(), a.GetId(), b.GetId());
1100 }
1101
1102 TEST_F(SyncableDirectoryTest, TestIsLegalNewParent) {
1103   TestIdFactory id_factory;
1104   WriteTransaction wtrans(FROM_HERE, UNITTEST, dir_.get());
1105   Entry root(&wtrans, GET_BY_ID, id_factory.root());
1106   ASSERT_TRUE(root.good());
1107   MutableEntry parent(&wtrans, CREATE, BOOKMARKS, root.GetId(), "Bob");
1108   ASSERT_TRUE(parent.good());
1109   parent.PutIsDir(true);
1110   parent.PutId(id_factory.NewServerId());
1111   parent.PutBaseVersion(1);
1112   MutableEntry child(&wtrans, CREATE, BOOKMARKS, parent.GetId(), "Bob");
1113   ASSERT_TRUE(child.good());
1114   child.PutIsDir(true);
1115   child.PutId(id_factory.NewServerId());
1116   child.PutBaseVersion(1);
1117   MutableEntry grandchild(&wtrans, CREATE, BOOKMARKS, child.GetId(), "Bob");
1118   ASSERT_TRUE(grandchild.good());
1119   grandchild.PutId(id_factory.NewServerId());
1120   grandchild.PutBaseVersion(1);
1121
1122   MutableEntry parent2(&wtrans, CREATE, BOOKMARKS, root.GetId(), "Pete");
1123   ASSERT_TRUE(parent2.good());
1124   parent2.PutIsDir(true);
1125   parent2.PutId(id_factory.NewServerId());
1126   parent2.PutBaseVersion(1);
1127   MutableEntry child2(&wtrans, CREATE, BOOKMARKS, parent2.GetId(), "Pete");
1128   ASSERT_TRUE(child2.good());
1129   child2.PutIsDir(true);
1130   child2.PutId(id_factory.NewServerId());
1131   child2.PutBaseVersion(1);
1132   MutableEntry grandchild2(&wtrans, CREATE, BOOKMARKS, child2.GetId(), "Pete");
1133   ASSERT_TRUE(grandchild2.good());
1134   grandchild2.PutId(id_factory.NewServerId());
1135   grandchild2.PutBaseVersion(1);
1136   // resulting tree
1137   //           root
1138   //           /  |
1139   //     parent    parent2
1140   //          |    |
1141   //      child    child2
1142   //          |    |
1143   // grandchild    grandchild2
1144   ASSERT_TRUE(IsLegalNewParent(child, root));
1145   ASSERT_TRUE(IsLegalNewParent(child, parent));
1146   ASSERT_FALSE(IsLegalNewParent(child, child));
1147   ASSERT_FALSE(IsLegalNewParent(child, grandchild));
1148   ASSERT_TRUE(IsLegalNewParent(child, parent2));
1149   ASSERT_TRUE(IsLegalNewParent(child, grandchild2));
1150   ASSERT_FALSE(IsLegalNewParent(parent, grandchild));
1151   ASSERT_FALSE(IsLegalNewParent(root, grandchild));
1152   ASSERT_FALSE(IsLegalNewParent(parent, grandchild));
1153 }
1154
1155 TEST_F(SyncableDirectoryTest, TestEntryIsInFolder) {
1156   // Create a subdir and an entry.
1157   int64 entry_handle;
1158   syncable::Id folder_id;
1159   syncable::Id entry_id;
1160   std::string entry_name = "entry";
1161
1162   {
1163     WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get());
1164     MutableEntry folder(&trans, CREATE, BOOKMARKS, trans.root_id(), "folder");
1165     ASSERT_TRUE(folder.good());
1166     folder.PutIsDir(true);
1167     EXPECT_TRUE(folder.PutIsUnsynced(true));
1168     folder_id = folder.GetId();
1169
1170     MutableEntry entry(&trans, CREATE, BOOKMARKS, folder.GetId(), entry_name);
1171     ASSERT_TRUE(entry.good());
1172     entry_handle = entry.GetMetahandle();
1173     entry.PutIsUnsynced(true);
1174     entry_id = entry.GetId();
1175   }
1176
1177   // Make sure we can find the entry in the folder.
1178   {
1179     ReadTransaction trans(FROM_HERE, dir_.get());
1180     EXPECT_EQ(0, CountEntriesWithName(&trans, trans.root_id(), entry_name));
1181     EXPECT_EQ(1, CountEntriesWithName(&trans, folder_id, entry_name));
1182
1183     Entry entry(&trans, GET_BY_ID, entry_id);
1184     ASSERT_TRUE(entry.good());
1185     EXPECT_EQ(entry_handle, entry.GetMetahandle());
1186     EXPECT_TRUE(entry.GetNonUniqueName()== entry_name);
1187     EXPECT_TRUE(entry.GetParentId()== folder_id);
1188   }
1189 }
1190
1191 TEST_F(SyncableDirectoryTest, TestParentIdIndexUpdate) {
1192   std::string child_name = "child";
1193
1194   WriteTransaction wt(FROM_HERE, UNITTEST, dir_.get());
1195   MutableEntry parent_folder(&wt, CREATE, BOOKMARKS, wt.root_id(), "folder1");
1196   parent_folder.PutIsUnsynced(true);
1197   parent_folder.PutIsDir(true);
1198
1199   MutableEntry parent_folder2(&wt, CREATE, BOOKMARKS, wt.root_id(), "folder2");
1200   parent_folder2.PutIsUnsynced(true);
1201   parent_folder2.PutIsDir(true);
1202
1203   MutableEntry child(&wt, CREATE, BOOKMARKS, parent_folder.GetId(), child_name);
1204   child.PutIsDir(true);
1205   child.PutIsUnsynced(true);
1206
1207   ASSERT_TRUE(child.good());
1208
1209   EXPECT_EQ(0, CountEntriesWithName(&wt, wt.root_id(), child_name));
1210   EXPECT_EQ(parent_folder.GetId(), child.GetParentId());
1211   EXPECT_EQ(1, CountEntriesWithName(&wt, parent_folder.GetId(), child_name));
1212   EXPECT_EQ(0, CountEntriesWithName(&wt, parent_folder2.GetId(), child_name));
1213   child.PutParentId(parent_folder2.GetId());
1214   EXPECT_EQ(parent_folder2.GetId(), child.GetParentId());
1215   EXPECT_EQ(0, CountEntriesWithName(&wt, parent_folder.GetId(), child_name));
1216   EXPECT_EQ(1, CountEntriesWithName(&wt, parent_folder2.GetId(), child_name));
1217 }
1218
1219 TEST_F(SyncableDirectoryTest, TestNoReindexDeletedItems) {
1220   std::string folder_name = "folder";
1221   std::string new_name = "new_name";
1222
1223   WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get());
1224   MutableEntry folder(&trans, CREATE, BOOKMARKS, trans.root_id(), folder_name);
1225   ASSERT_TRUE(folder.good());
1226   folder.PutIsDir(true);
1227   folder.PutIsDel(true);
1228
1229   EXPECT_EQ(0, CountEntriesWithName(&trans, trans.root_id(), folder_name));
1230
1231   MutableEntry deleted(&trans, GET_BY_ID, folder.GetId());
1232   ASSERT_TRUE(deleted.good());
1233   deleted.PutParentId(trans.root_id());
1234   deleted.PutNonUniqueName(new_name);
1235
1236   EXPECT_EQ(0, CountEntriesWithName(&trans, trans.root_id(), folder_name));
1237   EXPECT_EQ(0, CountEntriesWithName(&trans, trans.root_id(), new_name));
1238 }
1239
1240 TEST_F(SyncableDirectoryTest, TestCaseChangeRename) {
1241   WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get());
1242   MutableEntry folder(&trans, CREATE, BOOKMARKS, trans.root_id(), "CaseChange");
1243   ASSERT_TRUE(folder.good());
1244   folder.PutParentId(trans.root_id());
1245   folder.PutNonUniqueName("CASECHANGE");
1246   folder.PutIsDel(true);
1247 }
1248
1249 // Create items of each model type, and check that GetModelType and
1250 // GetServerModelType return the right value.
1251 TEST_F(SyncableDirectoryTest, GetModelType) {
1252   TestIdFactory id_factory;
1253   ModelTypeSet protocol_types = ProtocolTypes();
1254   for (ModelTypeSet::Iterator iter = protocol_types.First(); iter.Good();
1255        iter.Inc()) {
1256     ModelType datatype = iter.Get();
1257     SCOPED_TRACE(testing::Message("Testing model type ") << datatype);
1258     switch (datatype) {
1259       case UNSPECIFIED:
1260       case TOP_LEVEL_FOLDER:
1261         continue;  // Datatype isn't a function of Specifics.
1262       default:
1263         break;
1264     }
1265     sync_pb::EntitySpecifics specifics;
1266     AddDefaultFieldValue(datatype, &specifics);
1267
1268     WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get());
1269
1270     MutableEntry folder(&trans, CREATE, BOOKMARKS, trans.root_id(), "Folder");
1271     ASSERT_TRUE(folder.good());
1272     folder.PutId(id_factory.NewServerId());
1273     folder.PutSpecifics(specifics);
1274     folder.PutBaseVersion(1);
1275     folder.PutIsDir(true);
1276     folder.PutIsDel(false);
1277     ASSERT_EQ(datatype, folder.GetModelType());
1278
1279     MutableEntry item(&trans, CREATE, BOOKMARKS, trans.root_id(), "Item");
1280     ASSERT_TRUE(item.good());
1281     item.PutId(id_factory.NewServerId());
1282     item.PutSpecifics(specifics);
1283     item.PutBaseVersion(1);
1284     item.PutIsDir(false);
1285     item.PutIsDel(false);
1286     ASSERT_EQ(datatype, item.GetModelType());
1287
1288     // It's critical that deletion records retain their datatype, so that
1289     // they can be dispatched to the appropriate change processor.
1290     MutableEntry deleted_item(
1291         &trans, CREATE, BOOKMARKS, trans.root_id(), "Deleted Item");
1292     ASSERT_TRUE(item.good());
1293     deleted_item.PutId(id_factory.NewServerId());
1294     deleted_item.PutSpecifics(specifics);
1295     deleted_item.PutBaseVersion(1);
1296     deleted_item.PutIsDir(false);
1297     deleted_item.PutIsDel(true);
1298     ASSERT_EQ(datatype, deleted_item.GetModelType());
1299
1300     MutableEntry server_folder(&trans, CREATE_NEW_UPDATE_ITEM,
1301         id_factory.NewServerId());
1302     ASSERT_TRUE(server_folder.good());
1303     server_folder.PutServerSpecifics(specifics);
1304     server_folder.PutBaseVersion(1);
1305     server_folder.PutServerIsDir(true);
1306     server_folder.PutServerIsDel(false);
1307     ASSERT_EQ(datatype, server_folder.GetServerModelType());
1308
1309     MutableEntry server_item(&trans, CREATE_NEW_UPDATE_ITEM,
1310         id_factory.NewServerId());
1311     ASSERT_TRUE(server_item.good());
1312     server_item.PutServerSpecifics(specifics);
1313     server_item.PutBaseVersion(1);
1314     server_item.PutServerIsDir(false);
1315     server_item.PutServerIsDel(false);
1316     ASSERT_EQ(datatype, server_item.GetServerModelType());
1317
1318     sync_pb::SyncEntity folder_entity;
1319     folder_entity.set_id_string(SyncableIdToProto(id_factory.NewServerId()));
1320     folder_entity.set_deleted(false);
1321     folder_entity.set_folder(true);
1322     folder_entity.mutable_specifics()->CopyFrom(specifics);
1323     ASSERT_EQ(datatype, GetModelType(folder_entity));
1324
1325     sync_pb::SyncEntity item_entity;
1326     item_entity.set_id_string(SyncableIdToProto(id_factory.NewServerId()));
1327     item_entity.set_deleted(false);
1328     item_entity.set_folder(false);
1329     item_entity.mutable_specifics()->CopyFrom(specifics);
1330     ASSERT_EQ(datatype, GetModelType(item_entity));
1331   }
1332 }
1333
1334 // A test that roughly mimics the directory interaction that occurs when a
1335 // bookmark folder and entry are created then synced for the first time.  It is
1336 // a more common variant of the 'DeletedAndUnsyncedChild' scenario tested below.
1337 TEST_F(SyncableDirectoryTest, ChangeEntryIDAndUpdateChildren_ParentAndChild) {
1338   TestIdFactory id_factory;
1339   Id orig_parent_id;
1340   Id orig_child_id;
1341
1342   {
1343     // Create two client-side items, a parent and child.
1344     WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get());
1345
1346     MutableEntry parent(&trans, CREATE, BOOKMARKS, id_factory.root(), "parent");
1347     parent.PutIsDir(true);
1348     parent.PutIsUnsynced(true);
1349
1350     MutableEntry child(&trans, CREATE, BOOKMARKS, parent.GetId(), "child");
1351     child.PutIsUnsynced(true);
1352
1353     orig_parent_id = parent.GetId();
1354     orig_child_id = child.GetId();
1355   }
1356
1357   {
1358     // Simulate what happens after committing two items.  Their IDs will be
1359     // replaced with server IDs.  The child is renamed first, then the parent.
1360     WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get());
1361
1362     MutableEntry parent(&trans, GET_BY_ID, orig_parent_id);
1363     MutableEntry child(&trans, GET_BY_ID, orig_child_id);
1364
1365     ChangeEntryIDAndUpdateChildren(&trans, &child, id_factory.NewServerId());
1366     child.PutIsUnsynced(false);
1367     child.PutBaseVersion(1);
1368     child.PutServerVersion(1);
1369
1370     ChangeEntryIDAndUpdateChildren(&trans, &parent, id_factory.NewServerId());
1371     parent.PutIsUnsynced(false);
1372     parent.PutBaseVersion(1);
1373     parent.PutServerVersion(1);
1374   }
1375
1376   // Final check for validity.
1377   EXPECT_EQ(OPENED, SimulateSaveAndReloadDir());
1378 }
1379
1380 // A test based on the scenario where we create a bookmark folder and entry
1381 // locally, but with a twist.  In this case, the bookmark is deleted before we
1382 // are able to sync either it or its parent folder.  This scenario used to cause
1383 // directory corruption, see crbug.com/125381.
1384 TEST_F(SyncableDirectoryTest,
1385        ChangeEntryIDAndUpdateChildren_DeletedAndUnsyncedChild) {
1386   TestIdFactory id_factory;
1387   Id orig_parent_id;
1388   Id orig_child_id;
1389
1390   {
1391     // Create two client-side items, a parent and child.
1392     WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get());
1393
1394     MutableEntry parent(&trans, CREATE, BOOKMARKS, id_factory.root(), "parent");
1395     parent.PutIsDir(true);
1396     parent.PutIsUnsynced(true);
1397
1398     MutableEntry child(&trans, CREATE, BOOKMARKS, parent.GetId(), "child");
1399     child.PutIsUnsynced(true);
1400
1401     orig_parent_id = parent.GetId();
1402     orig_child_id = child.GetId();
1403   }
1404
1405   {
1406     // Delete the child.
1407     WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get());
1408
1409     MutableEntry child(&trans, GET_BY_ID, orig_child_id);
1410     child.PutIsDel(true);
1411   }
1412
1413   {
1414     // Simulate what happens after committing the parent.  Its ID will be
1415     // replaced with server a ID.
1416     WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get());
1417
1418     MutableEntry parent(&trans, GET_BY_ID, orig_parent_id);
1419
1420     ChangeEntryIDAndUpdateChildren(&trans, &parent, id_factory.NewServerId());
1421     parent.PutIsUnsynced(false);
1422     parent.PutBaseVersion(1);
1423     parent.PutServerVersion(1);
1424   }
1425
1426   // Final check for validity.
1427   EXPECT_EQ(OPENED, SimulateSaveAndReloadDir());
1428 }
1429
1430 // Ask the directory to generate a unique ID.  Close and re-open the database
1431 // without saving, then ask for another unique ID.  Verify IDs are not reused.
1432 // This scenario simulates a crash within the first few seconds of operation.
1433 TEST_F(SyncableDirectoryTest, LocalIdReuseTest) {
1434   Id pre_crash_id = dir_->NextId();
1435   SimulateCrashAndReloadDir();
1436   Id post_crash_id = dir_->NextId();
1437   EXPECT_NE(pre_crash_id, post_crash_id);
1438 }
1439
1440 // Ask the directory to generate a unique ID.  Save the directory.  Close and
1441 // re-open the database without saving, then ask for another unique ID.  Verify
1442 // IDs are not reused.  This scenario simulates a steady-state crash.
1443 TEST_F(SyncableDirectoryTest, LocalIdReuseTestWithSave) {
1444   Id pre_crash_id = dir_->NextId();
1445   dir_->SaveChanges();
1446   SimulateCrashAndReloadDir();
1447   Id post_crash_id = dir_->NextId();
1448   EXPECT_NE(pre_crash_id, post_crash_id);
1449 }
1450
1451 // Ensure that the unsynced, is_del and server unkown entries that may have been
1452 // left in the database by old clients will be deleted when we open the old
1453 // database.
1454 TEST_F(SyncableDirectoryTest, OldClientLeftUnsyncedDeletedLocalItem) {
1455   // We must create an entry with the offending properties.  This is done with
1456   // some abuse of the MutableEntry's API; it doesn't expect us to modify an
1457   // item after it is deleted.  If this hack becomes impractical we will need to
1458   // find a new way to simulate this scenario.
1459
1460   TestIdFactory id_factory;
1461
1462   // Happy-path: These valid entries should not get deleted.
1463   Id server_knows_id = id_factory.NewServerId();
1464   Id not_is_del_id = id_factory.NewLocalId();
1465
1466   // The ID of the entry which will be unsynced, is_del and !ServerKnows().
1467   Id zombie_id = id_factory.NewLocalId();
1468
1469   // We're about to do some bad things.  Tell the directory verification
1470   // routines to look the other way.
1471   dir_->SetInvariantCheckLevel(OFF);
1472
1473   {
1474     WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get());
1475
1476     // Create an uncommitted tombstone entry.
1477     MutableEntry server_knows(&trans, CREATE, BOOKMARKS, id_factory.root(),
1478                               "server_knows");
1479     server_knows.PutId(server_knows_id);
1480     server_knows.PutIsUnsynced(true);
1481     server_knows.PutIsDel(true);
1482     server_knows.PutBaseVersion(5);
1483     server_knows.PutServerVersion(4);
1484
1485     // Create a valid update entry.
1486     MutableEntry not_is_del(
1487         &trans, CREATE, BOOKMARKS, id_factory.root(), "not_is_del");
1488     not_is_del.PutId(not_is_del_id);
1489     not_is_del.PutIsDel(false);
1490     not_is_del.PutIsUnsynced(true);
1491
1492     // Create a tombstone which should never be sent to the server because the
1493     // server never knew about the item's existence.
1494     //
1495     // New clients should never put entries into this state.  We work around
1496     // this by setting IS_DEL before setting IS_UNSYNCED, something which the
1497     // client should never do in practice.
1498     MutableEntry zombie(&trans, CREATE, BOOKMARKS, id_factory.root(), "zombie");
1499     zombie.PutId(zombie_id);
1500     zombie.PutIsDel(true);
1501     zombie.PutIsUnsynced(true);
1502   }
1503
1504   ASSERT_EQ(OPENED, SimulateSaveAndReloadDir());
1505
1506   {
1507     ReadTransaction trans(FROM_HERE, dir_.get());
1508
1509     // The directory loading routines should have cleaned things up, making it
1510     // safe to check invariants once again.
1511     dir_->FullyCheckTreeInvariants(&trans);
1512
1513     Entry server_knows(&trans, GET_BY_ID, server_knows_id);
1514     EXPECT_TRUE(server_knows.good());
1515
1516     Entry not_is_del(&trans, GET_BY_ID, not_is_del_id);
1517     EXPECT_TRUE(not_is_del.good());
1518
1519     Entry zombie(&trans, GET_BY_ID, zombie_id);
1520     EXPECT_FALSE(zombie.good());
1521   }
1522 }
1523
1524 TEST_F(SyncableDirectoryTest, PositionWithNullSurvivesSaveAndReload) {
1525   TestIdFactory id_factory;
1526   Id null_child_id;
1527   const char null_cstr[] = "\0null\0test";
1528   std::string null_str(null_cstr, arraysize(null_cstr) - 1);
1529   // Pad up to the minimum length with 0x7f characters, then add a string that
1530   // contains a few NULLs to the end.  This is slightly wrong, since the suffix
1531   // part of a UniquePosition shouldn't contain NULLs, but it's good enough for
1532   // this test.
1533   std::string suffix =
1534       std::string(UniquePosition::kSuffixLength - null_str.length(), '\x7f')
1535       + null_str;
1536   UniquePosition null_pos = UniquePosition::FromInt64(10, suffix);
1537
1538   {
1539     WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get());
1540
1541     MutableEntry parent(&trans, CREATE, BOOKMARKS, id_factory.root(), "parent");
1542     parent.PutIsDir(true);
1543     parent.PutIsUnsynced(true);
1544
1545     MutableEntry child(&trans, CREATE, BOOKMARKS, parent.GetId(), "child");
1546     child.PutIsUnsynced(true);
1547     child.PutUniquePosition(null_pos);
1548     child.PutServerUniquePosition(null_pos);
1549
1550     null_child_id = child.GetId();
1551   }
1552
1553   EXPECT_EQ(OPENED, SimulateSaveAndReloadDir());
1554
1555   {
1556     ReadTransaction trans(FROM_HERE, dir_.get());
1557
1558     Entry null_ordinal_child(&trans, GET_BY_ID, null_child_id);
1559     EXPECT_TRUE(
1560         null_pos.Equals(null_ordinal_child.GetUniquePosition()));
1561     EXPECT_TRUE(
1562         null_pos.Equals(null_ordinal_child.GetServerUniquePosition()));
1563   }
1564 }
1565
1566 // An OnDirectoryBackingStore that can be set to always fail SaveChanges.
1567 class TestBackingStore : public OnDiskDirectoryBackingStore {
1568  public:
1569   TestBackingStore(const std::string& dir_name,
1570                    const base::FilePath& backing_filepath);
1571
1572   virtual ~TestBackingStore();
1573
1574   virtual bool SaveChanges(const Directory::SaveChangesSnapshot& snapshot)
1575       OVERRIDE;
1576
1577    void StartFailingSaveChanges() {
1578      fail_save_changes_ = true;
1579    }
1580
1581  private:
1582    bool fail_save_changes_;
1583 };
1584
1585 TestBackingStore::TestBackingStore(const std::string& dir_name,
1586                                    const base::FilePath& backing_filepath)
1587   : OnDiskDirectoryBackingStore(dir_name, backing_filepath),
1588     fail_save_changes_(false) {
1589 }
1590
1591 TestBackingStore::~TestBackingStore() { }
1592
1593 bool TestBackingStore::SaveChanges(
1594     const Directory::SaveChangesSnapshot& snapshot){
1595   if (fail_save_changes_) {
1596     return false;
1597   } else {
1598     return OnDiskDirectoryBackingStore::SaveChanges(snapshot);
1599   }
1600 }
1601
1602 // A directory whose Save() function can be set to always fail.
1603 class TestDirectory : public Directory {
1604  public:
1605   // A factory function used to work around some initialization order issues.
1606   static TestDirectory* Create(
1607       Encryptor *encryptor,
1608       UnrecoverableErrorHandler *handler,
1609       const std::string& dir_name,
1610       const base::FilePath& backing_filepath);
1611
1612   virtual ~TestDirectory();
1613
1614   void StartFailingSaveChanges() {
1615     backing_store_->StartFailingSaveChanges();
1616   }
1617
1618  private:
1619   TestDirectory(Encryptor* encryptor,
1620                 UnrecoverableErrorHandler* handler,
1621                 TestBackingStore* backing_store);
1622
1623   TestBackingStore* backing_store_;
1624 };
1625
1626 TestDirectory* TestDirectory::Create(
1627     Encryptor *encryptor,
1628     UnrecoverableErrorHandler *handler,
1629     const std::string& dir_name,
1630     const base::FilePath& backing_filepath) {
1631   TestBackingStore* backing_store =
1632       new TestBackingStore(dir_name, backing_filepath);
1633   return new TestDirectory(encryptor, handler, backing_store);
1634 }
1635
1636 TestDirectory::TestDirectory(Encryptor* encryptor,
1637                              UnrecoverableErrorHandler* handler,
1638                              TestBackingStore* backing_store)
1639     : Directory(backing_store, handler, NULL, NULL, NULL),
1640       backing_store_(backing_store) {
1641 }
1642
1643 TestDirectory::~TestDirectory() { }
1644
1645 TEST(OnDiskSyncableDirectory, FailInitialWrite) {
1646   FakeEncryptor encryptor;
1647   TestUnrecoverableErrorHandler handler;
1648   base::ScopedTempDir temp_dir;
1649   ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
1650   base::FilePath file_path = temp_dir.path().Append(
1651       FILE_PATH_LITERAL("Test.sqlite3"));
1652   std::string name = "user@x.com";
1653   NullDirectoryChangeDelegate delegate;
1654
1655   scoped_ptr<TestDirectory> test_dir(
1656       TestDirectory::Create(&encryptor, &handler, name, file_path));
1657
1658   test_dir->StartFailingSaveChanges();
1659   ASSERT_EQ(FAILED_INITIAL_WRITE, test_dir->Open(name, &delegate,
1660                                                  NullTransactionObserver()));
1661 }
1662
1663 // A variant of SyncableDirectoryTest that uses a real sqlite database.
1664 class OnDiskSyncableDirectoryTest : public SyncableDirectoryTest {
1665  protected:
1666   // SetUp() is called before each test case is run.
1667   // The sqlite3 DB is deleted before each test is run.
1668   virtual void SetUp() {
1669     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
1670     file_path_ = temp_dir_.path().Append(
1671         FILE_PATH_LITERAL("Test.sqlite3"));
1672     base::DeleteFile(file_path_, true);
1673     CreateDirectory();
1674   }
1675
1676   virtual void TearDown() {
1677     // This also closes file handles.
1678     dir_->SaveChanges();
1679     dir_.reset();
1680     base::DeleteFile(file_path_, true);
1681   }
1682
1683   // Creates a new directory.  Deletes the old directory, if it exists.
1684   void CreateDirectory() {
1685     test_directory_ =
1686         TestDirectory::Create(&encryptor_, &handler_, kName, file_path_);
1687     dir_.reset(test_directory_);
1688     ASSERT_TRUE(dir_.get());
1689     ASSERT_EQ(OPENED, dir_->Open(kName, &delegate_,
1690                                  NullTransactionObserver()));
1691     ASSERT_TRUE(dir_->good());
1692   }
1693
1694   void SaveAndReloadDir() {
1695     dir_->SaveChanges();
1696     CreateDirectory();
1697   }
1698
1699   void StartFailingSaveChanges() {
1700     test_directory_->StartFailingSaveChanges();
1701   }
1702
1703   TestDirectory *test_directory_;  // mirrors scoped_ptr<Directory> dir_
1704   base::ScopedTempDir temp_dir_;
1705   base::FilePath file_path_;
1706 };
1707
1708 TEST_F(OnDiskSyncableDirectoryTest, TestPurgeEntriesWithTypeIn) {
1709   sync_pb::EntitySpecifics bookmark_specs;
1710   sync_pb::EntitySpecifics autofill_specs;
1711   sync_pb::EntitySpecifics preference_specs;
1712   AddDefaultFieldValue(BOOKMARKS, &bookmark_specs);
1713   AddDefaultFieldValue(PREFERENCES, &preference_specs);
1714   AddDefaultFieldValue(AUTOFILL, &autofill_specs);
1715
1716   ModelTypeSet types_to_purge(PREFERENCES, AUTOFILL);
1717
1718   TestIdFactory id_factory;
1719   // Create some items for each type.
1720   {
1721     WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get());
1722
1723     // Make it look like these types have completed initial sync.
1724     CreateTypeRoot(&trans, dir_.get(), BOOKMARKS);
1725     CreateTypeRoot(&trans, dir_.get(), PREFERENCES);
1726     CreateTypeRoot(&trans, dir_.get(), AUTOFILL);
1727
1728     // Add more nodes for this type.  Technically, they should be placed under
1729     // the proper type root nodes but the assertions in this test won't notice
1730     // if their parent isn't quite right.
1731     MutableEntry item1(&trans, CREATE, BOOKMARKS, trans.root_id(), "Item");
1732     ASSERT_TRUE(item1.good());
1733     item1.PutServerSpecifics(bookmark_specs);
1734     item1.PutIsUnsynced(true);
1735
1736     MutableEntry item2(&trans, CREATE_NEW_UPDATE_ITEM,
1737                        id_factory.NewServerId());
1738     ASSERT_TRUE(item2.good());
1739     item2.PutServerSpecifics(bookmark_specs);
1740     item2.PutIsUnappliedUpdate(true);
1741
1742     MutableEntry item3(&trans, CREATE, PREFERENCES,
1743                        trans.root_id(), "Item");
1744     ASSERT_TRUE(item3.good());
1745     item3.PutSpecifics(preference_specs);
1746     item3.PutServerSpecifics(preference_specs);
1747     item3.PutIsUnsynced(true);
1748
1749     MutableEntry item4(&trans, CREATE_NEW_UPDATE_ITEM,
1750                        id_factory.NewServerId());
1751     ASSERT_TRUE(item4.good());
1752     item4.PutServerSpecifics(preference_specs);
1753     item4.PutIsUnappliedUpdate(true);
1754
1755     MutableEntry item5(&trans, CREATE, AUTOFILL,
1756                        trans.root_id(), "Item");
1757     ASSERT_TRUE(item5.good());
1758     item5.PutSpecifics(autofill_specs);
1759     item5.PutServerSpecifics(autofill_specs);
1760     item5.PutIsUnsynced(true);
1761
1762     MutableEntry item6(&trans, CREATE_NEW_UPDATE_ITEM,
1763       id_factory.NewServerId());
1764     ASSERT_TRUE(item6.good());
1765     item6.PutServerSpecifics(autofill_specs);
1766     item6.PutIsUnappliedUpdate(true);
1767   }
1768
1769   dir_->SaveChanges();
1770   {
1771     ReadTransaction trans(FROM_HERE, dir_.get());
1772     MetahandleSet all_set;
1773     GetAllMetaHandles(&trans, &all_set);
1774     ASSERT_EQ(10U, all_set.size());
1775   }
1776
1777   dir_->PurgeEntriesWithTypeIn(types_to_purge, ModelTypeSet(), ModelTypeSet());
1778
1779   // We first query the in-memory data, and then reload the directory (without
1780   // saving) to verify that disk does not still have the data.
1781   CheckPurgeEntriesWithTypeInSucceeded(types_to_purge, true);
1782   SaveAndReloadDir();
1783   CheckPurgeEntriesWithTypeInSucceeded(types_to_purge, false);
1784 }
1785
1786 TEST_F(OnDiskSyncableDirectoryTest, TestShareInfo) {
1787   dir_->set_store_birthday("Jan 31st");
1788   const char* const bag_of_chips_array = "\0bag of chips";
1789   const std::string bag_of_chips_string =
1790       std::string(bag_of_chips_array, sizeof(bag_of_chips_array));
1791   dir_->set_bag_of_chips(bag_of_chips_string);
1792   {
1793     ReadTransaction trans(FROM_HERE, dir_.get());
1794     EXPECT_EQ("Jan 31st", dir_->store_birthday());
1795     EXPECT_EQ(bag_of_chips_string, dir_->bag_of_chips());
1796   }
1797   dir_->set_store_birthday("April 10th");
1798   const char* const bag_of_chips2_array = "\0bag of chips2";
1799   const std::string bag_of_chips2_string =
1800       std::string(bag_of_chips2_array, sizeof(bag_of_chips2_array));
1801   dir_->set_bag_of_chips(bag_of_chips2_string);
1802   dir_->SaveChanges();
1803   {
1804     ReadTransaction trans(FROM_HERE, dir_.get());
1805     EXPECT_EQ("April 10th", dir_->store_birthday());
1806     EXPECT_EQ(bag_of_chips2_string, dir_->bag_of_chips());
1807   }
1808   const char* const bag_of_chips3_array = "\0bag of chips3";
1809   const std::string bag_of_chips3_string =
1810       std::string(bag_of_chips3_array, sizeof(bag_of_chips3_array));
1811   dir_->set_bag_of_chips(bag_of_chips3_string);
1812   // Restore the directory from disk.  Make sure that nothing's changed.
1813   SaveAndReloadDir();
1814   {
1815     ReadTransaction trans(FROM_HERE, dir_.get());
1816     EXPECT_EQ("April 10th", dir_->store_birthday());
1817     EXPECT_EQ(bag_of_chips3_string, dir_->bag_of_chips());
1818   }
1819 }
1820
1821 TEST_F(OnDiskSyncableDirectoryTest,
1822        TestSimpleFieldsPreservedDuringSaveChanges) {
1823   Id update_id = TestIdFactory::FromNumber(1);
1824   Id create_id;
1825   EntryKernel create_pre_save, update_pre_save;
1826   EntryKernel create_post_save, update_post_save;
1827   std::string create_name =  "Create";
1828
1829   {
1830     WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get());
1831     MutableEntry create(
1832         &trans, CREATE, BOOKMARKS, trans.root_id(), create_name);
1833     MutableEntry update(&trans, CREATE_NEW_UPDATE_ITEM, update_id);
1834     create.PutIsUnsynced(true);
1835     update.PutIsUnappliedUpdate(true);
1836     sync_pb::EntitySpecifics specifics;
1837     specifics.mutable_bookmark()->set_favicon("PNG");
1838     specifics.mutable_bookmark()->set_url("http://nowhere");
1839     create.PutSpecifics(specifics);
1840     update.PutSpecifics(specifics);
1841     create_pre_save = create.GetKernelCopy();
1842     update_pre_save = update.GetKernelCopy();
1843     create_id = create.GetId();
1844   }
1845
1846   dir_->SaveChanges();
1847   dir_.reset(new Directory(new OnDiskDirectoryBackingStore(kName, file_path_),
1848                            &handler_,
1849                            NULL,
1850                            NULL,
1851                            NULL));
1852
1853   ASSERT_TRUE(dir_.get());
1854   ASSERT_EQ(OPENED, dir_->Open(kName, &delegate_, NullTransactionObserver()));
1855   ASSERT_TRUE(dir_->good());
1856
1857   {
1858     ReadTransaction trans(FROM_HERE, dir_.get());
1859     Entry create(&trans, GET_BY_ID, create_id);
1860     EXPECT_EQ(1, CountEntriesWithName(&trans, trans.root_id(), create_name));
1861     Entry update(&trans, GET_BY_ID, update_id);
1862     create_post_save = create.GetKernelCopy();
1863     update_post_save = update.GetKernelCopy();
1864   }
1865   int i = BEGIN_FIELDS;
1866   for ( ; i < INT64_FIELDS_END ; ++i) {
1867     EXPECT_EQ(create_pre_save.ref((Int64Field)i) +
1868                   (i == TRANSACTION_VERSION ? 1 : 0),
1869               create_post_save.ref((Int64Field)i))
1870               << "int64 field #" << i << " changed during save/load";
1871     EXPECT_EQ(update_pre_save.ref((Int64Field)i) +
1872               (i == TRANSACTION_VERSION ? 1 : 0),
1873               update_post_save.ref((Int64Field)i))
1874               << "int64 field #" << i << " changed during save/load";
1875   }
1876   for ( ; i < TIME_FIELDS_END ; ++i) {
1877     EXPECT_EQ(create_pre_save.ref((TimeField)i),
1878               create_post_save.ref((TimeField)i))
1879               << "time field #" << i << " changed during save/load";
1880     EXPECT_EQ(update_pre_save.ref((TimeField)i),
1881               update_post_save.ref((TimeField)i))
1882               << "time field #" << i << " changed during save/load";
1883   }
1884   for ( ; i < ID_FIELDS_END ; ++i) {
1885     EXPECT_EQ(create_pre_save.ref((IdField)i),
1886               create_post_save.ref((IdField)i))
1887               << "id field #" << i << " changed during save/load";
1888     EXPECT_EQ(update_pre_save.ref((IdField)i),
1889               update_pre_save.ref((IdField)i))
1890               << "id field #" << i << " changed during save/load";
1891   }
1892   for ( ; i < BIT_FIELDS_END ; ++i) {
1893     EXPECT_EQ(create_pre_save.ref((BitField)i),
1894               create_post_save.ref((BitField)i))
1895               << "Bit field #" << i << " changed during save/load";
1896     EXPECT_EQ(update_pre_save.ref((BitField)i),
1897               update_post_save.ref((BitField)i))
1898               << "Bit field #" << i << " changed during save/load";
1899   }
1900   for ( ; i < STRING_FIELDS_END ; ++i) {
1901     EXPECT_EQ(create_pre_save.ref((StringField)i),
1902               create_post_save.ref((StringField)i))
1903               << "String field #" << i << " changed during save/load";
1904     EXPECT_EQ(update_pre_save.ref((StringField)i),
1905               update_post_save.ref((StringField)i))
1906               << "String field #" << i << " changed during save/load";
1907   }
1908   for ( ; i < PROTO_FIELDS_END; ++i) {
1909     EXPECT_EQ(create_pre_save.ref((ProtoField)i).SerializeAsString(),
1910               create_post_save.ref((ProtoField)i).SerializeAsString())
1911               << "Blob field #" << i << " changed during save/load";
1912     EXPECT_EQ(update_pre_save.ref((ProtoField)i).SerializeAsString(),
1913               update_post_save.ref((ProtoField)i).SerializeAsString())
1914               << "Blob field #" << i << " changed during save/load";
1915   }
1916   for ( ; i < UNIQUE_POSITION_FIELDS_END; ++i) {
1917     EXPECT_TRUE(create_pre_save.ref((UniquePositionField)i).Equals(
1918         create_post_save.ref((UniquePositionField)i)))
1919         << "Position field #" << i << " changed during save/load";
1920     EXPECT_TRUE(update_pre_save.ref((UniquePositionField)i).Equals(
1921         update_post_save.ref((UniquePositionField)i)))
1922         << "Position field #" << i << " changed during save/load";
1923   }
1924 }
1925
1926 TEST_F(OnDiskSyncableDirectoryTest, TestSaveChangesFailure) {
1927   int64 handle1 = 0;
1928   // Set up an item using a regular, saveable directory.
1929   {
1930     WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get());
1931
1932     MutableEntry e1(&trans, CREATE, BOOKMARKS, trans.root_id(), "aguilera");
1933     ASSERT_TRUE(e1.good());
1934     EXPECT_TRUE(e1.GetKernelCopy().is_dirty());
1935     handle1 = e1.GetMetahandle();
1936     e1.PutBaseVersion(1);
1937     e1.PutIsDir(true);
1938     e1.PutId(TestIdFactory::FromNumber(101));
1939     EXPECT_TRUE(e1.GetKernelCopy().is_dirty());
1940     EXPECT_TRUE(IsInDirtyMetahandles(handle1));
1941   }
1942   ASSERT_TRUE(dir_->SaveChanges());
1943
1944   // Make sure the item is no longer dirty after saving,
1945   // and make a modification.
1946   {
1947     WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get());
1948
1949     MutableEntry aguilera(&trans, GET_BY_HANDLE, handle1);
1950     ASSERT_TRUE(aguilera.good());
1951     EXPECT_FALSE(aguilera.GetKernelCopy().is_dirty());
1952     EXPECT_EQ(aguilera.GetNonUniqueName(), "aguilera");
1953     aguilera.PutNonUniqueName("overwritten");
1954     EXPECT_TRUE(aguilera.GetKernelCopy().is_dirty());
1955     EXPECT_TRUE(IsInDirtyMetahandles(handle1));
1956   }
1957   ASSERT_TRUE(dir_->SaveChanges());
1958
1959   // Now do some operations when SaveChanges() will fail.
1960   StartFailingSaveChanges();
1961   ASSERT_TRUE(dir_->good());
1962
1963   int64 handle2 = 0;
1964   {
1965     WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get());
1966
1967     MutableEntry aguilera(&trans, GET_BY_HANDLE, handle1);
1968     ASSERT_TRUE(aguilera.good());
1969     EXPECT_FALSE(aguilera.GetKernelCopy().is_dirty());
1970     EXPECT_EQ(aguilera.GetNonUniqueName(), "overwritten");
1971     EXPECT_FALSE(aguilera.GetKernelCopy().is_dirty());
1972     EXPECT_FALSE(IsInDirtyMetahandles(handle1));
1973     aguilera.PutNonUniqueName("christina");
1974     EXPECT_TRUE(aguilera.GetKernelCopy().is_dirty());
1975     EXPECT_TRUE(IsInDirtyMetahandles(handle1));
1976
1977     // New item.
1978     MutableEntry kids_on_block(
1979         &trans, CREATE, BOOKMARKS, trans.root_id(), "kids");
1980     ASSERT_TRUE(kids_on_block.good());
1981     handle2 = kids_on_block.GetMetahandle();
1982     kids_on_block.PutBaseVersion(1);
1983     kids_on_block.PutIsDir(true);
1984     kids_on_block.PutId(TestIdFactory::FromNumber(102));
1985     EXPECT_TRUE(kids_on_block.GetKernelCopy().is_dirty());
1986     EXPECT_TRUE(IsInDirtyMetahandles(handle2));
1987   }
1988
1989   // We are using an unsaveable directory, so this can't succeed.  However,
1990   // the HandleSaveChangesFailure code path should have been triggered.
1991   ASSERT_FALSE(dir_->SaveChanges());
1992
1993   // Make sure things were rolled back and the world is as it was before call.
1994   {
1995      ReadTransaction trans(FROM_HERE, dir_.get());
1996      Entry e1(&trans, GET_BY_HANDLE, handle1);
1997      ASSERT_TRUE(e1.good());
1998      EntryKernel aguilera = e1.GetKernelCopy();
1999      Entry kids(&trans, GET_BY_HANDLE, handle2);
2000      ASSERT_TRUE(kids.good());
2001      EXPECT_TRUE(kids.GetKernelCopy().is_dirty());
2002      EXPECT_TRUE(IsInDirtyMetahandles(handle2));
2003      EXPECT_TRUE(aguilera.is_dirty());
2004      EXPECT_TRUE(IsInDirtyMetahandles(handle1));
2005   }
2006 }
2007
2008 TEST_F(OnDiskSyncableDirectoryTest, TestSaveChangesFailureWithPurge) {
2009   int64 handle1 = 0;
2010   // Set up an item using a regular, saveable directory.
2011   {
2012     WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get());
2013
2014     MutableEntry e1(&trans, CREATE, BOOKMARKS, trans.root_id(), "aguilera");
2015     ASSERT_TRUE(e1.good());
2016     EXPECT_TRUE(e1.GetKernelCopy().is_dirty());
2017     handle1 = e1.GetMetahandle();
2018     e1.PutBaseVersion(1);
2019     e1.PutIsDir(true);
2020     e1.PutId(TestIdFactory::FromNumber(101));
2021     sync_pb::EntitySpecifics bookmark_specs;
2022     AddDefaultFieldValue(BOOKMARKS, &bookmark_specs);
2023     e1.PutSpecifics(bookmark_specs);
2024     e1.PutServerSpecifics(bookmark_specs);
2025     e1.PutId(TestIdFactory::FromNumber(101));
2026     EXPECT_TRUE(e1.GetKernelCopy().is_dirty());
2027     EXPECT_TRUE(IsInDirtyMetahandles(handle1));
2028   }
2029   ASSERT_TRUE(dir_->SaveChanges());
2030
2031   // Now do some operations while SaveChanges() is set to fail.
2032   StartFailingSaveChanges();
2033   ASSERT_TRUE(dir_->good());
2034
2035   ModelTypeSet set(BOOKMARKS);
2036   dir_->PurgeEntriesWithTypeIn(set, ModelTypeSet(), ModelTypeSet());
2037   EXPECT_TRUE(IsInMetahandlesToPurge(handle1));
2038   ASSERT_FALSE(dir_->SaveChanges());
2039   EXPECT_TRUE(IsInMetahandlesToPurge(handle1));
2040 }
2041
2042 }  // namespace
2043
2044 void SyncableDirectoryTest::ValidateEntry(BaseTransaction* trans,
2045                                           int64 id,
2046                                           bool check_name,
2047                                           const std::string& name,
2048                                           int64 base_version,
2049                                           int64 server_version,
2050                                           bool is_del) {
2051   Entry e(trans, GET_BY_ID, TestIdFactory::FromNumber(id));
2052   ASSERT_TRUE(e.good());
2053   if (check_name)
2054     ASSERT_TRUE(name == e.GetNonUniqueName());
2055   ASSERT_TRUE(base_version == e.GetBaseVersion());
2056   ASSERT_TRUE(server_version == e.GetServerVersion());
2057   ASSERT_TRUE(is_del == e.GetIsDel());
2058 }
2059
2060 DirOpenResult SyncableDirectoryTest::SimulateSaveAndReloadDir() {
2061   if (!dir_->SaveChanges())
2062     return FAILED_IN_UNITTEST;
2063
2064   return ReloadDirImpl();
2065 }
2066
2067 DirOpenResult SyncableDirectoryTest::SimulateCrashAndReloadDir() {
2068   return ReloadDirImpl();
2069 }
2070
2071 DirOpenResult SyncableDirectoryTest::ReloadDirImpl() {
2072   // Do some tricky things to preserve the backing store.
2073   DirectoryBackingStore* saved_store = dir_->store_.release();
2074
2075   // Close the current directory.
2076   dir_->Close();
2077   dir_.reset();
2078
2079   dir_.reset(new Directory(saved_store,
2080                            &handler_,
2081                            NULL,
2082                            NULL,
2083                            NULL));
2084   DirOpenResult result = dir_->OpenImpl(kName, &delegate_,
2085                                         NullTransactionObserver());
2086
2087   // If something went wrong, we need to clear this member.  If we don't,
2088   // TearDown() will be guaranteed to crash when it calls SaveChanges().
2089   if (result != OPENED)
2090     dir_.reset();
2091
2092   return result;
2093 }
2094
2095 namespace {
2096
2097 class SyncableDirectoryManagement : public testing::Test {
2098  public:
2099   virtual void SetUp() {
2100     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
2101   }
2102
2103   virtual void TearDown() {
2104   }
2105  protected:
2106   base::MessageLoop message_loop_;
2107   base::ScopedTempDir temp_dir_;
2108   FakeEncryptor encryptor_;
2109   TestUnrecoverableErrorHandler handler_;
2110   NullDirectoryChangeDelegate delegate_;
2111 };
2112
2113 TEST_F(SyncableDirectoryManagement, TestFileRelease) {
2114   base::FilePath path = temp_dir_.path().Append(
2115       Directory::kSyncDatabaseFilename);
2116
2117   syncable::Directory dir(new OnDiskDirectoryBackingStore("ScopeTest", path),
2118                           &handler_,
2119                           NULL,
2120                           NULL,
2121                           NULL);
2122   DirOpenResult result =
2123       dir.Open("ScopeTest", &delegate_, NullTransactionObserver());
2124   ASSERT_EQ(result, OPENED);
2125   dir.Close();
2126
2127   // Closing the directory should have released the backing database file.
2128   ASSERT_TRUE(base::DeleteFile(path, true));
2129 }
2130
2131 class StressTransactionsDelegate : public base::PlatformThread::Delegate {
2132  public:
2133   StressTransactionsDelegate(Directory* dir, int thread_number)
2134       : dir_(dir),
2135         thread_number_(thread_number) {}
2136
2137  private:
2138   Directory* const dir_;
2139   const int thread_number_;
2140
2141   // PlatformThread::Delegate methods:
2142   virtual void ThreadMain() OVERRIDE {
2143     int entry_count = 0;
2144     std::string path_name;
2145
2146     for (int i = 0; i < 20; ++i) {
2147       const int rand_action = rand() % 10;
2148       if (rand_action < 4 && !path_name.empty()) {
2149         ReadTransaction trans(FROM_HERE, dir_);
2150         CHECK(1 == CountEntriesWithName(&trans, trans.root_id(), path_name));
2151         base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(
2152             rand() % 10));
2153       } else {
2154         std::string unique_name =
2155             base::StringPrintf("%d.%d", thread_number_, entry_count++);
2156         path_name.assign(unique_name.begin(), unique_name.end());
2157         WriteTransaction trans(FROM_HERE, UNITTEST, dir_);
2158         MutableEntry e(&trans, CREATE, BOOKMARKS, trans.root_id(), path_name);
2159         CHECK(e.good());
2160         base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(
2161             rand() % 20));
2162         e.PutIsUnsynced(true);
2163         if (e.PutId(TestIdFactory::FromNumber(rand())) &&
2164             e.GetId().ServerKnows() && !e.GetId().IsRoot()) {
2165            e.PutBaseVersion(1);
2166         }
2167       }
2168     }
2169   }
2170
2171   DISALLOW_COPY_AND_ASSIGN(StressTransactionsDelegate);
2172 };
2173
2174 TEST(SyncableDirectory, StressTransactions) {
2175   base::MessageLoop message_loop;
2176   base::ScopedTempDir temp_dir;
2177   ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
2178   FakeEncryptor encryptor;
2179   TestUnrecoverableErrorHandler handler;
2180   NullDirectoryChangeDelegate delegate;
2181   std::string dirname = "stress";
2182   Directory dir(new InMemoryDirectoryBackingStore(dirname),
2183                 &handler,
2184                 NULL,
2185                 NULL,
2186                 NULL);
2187   dir.Open(dirname, &delegate, NullTransactionObserver());
2188
2189   const int kThreadCount = 7;
2190   base::PlatformThreadHandle threads[kThreadCount];
2191   scoped_ptr<StressTransactionsDelegate> thread_delegates[kThreadCount];
2192
2193   for (int i = 0; i < kThreadCount; ++i) {
2194     thread_delegates[i].reset(new StressTransactionsDelegate(&dir, i));
2195     ASSERT_TRUE(base::PlatformThread::Create(
2196         0, thread_delegates[i].get(), &threads[i]));
2197   }
2198
2199   for (int i = 0; i < kThreadCount; ++i) {
2200     base::PlatformThread::Join(threads[i]);
2201   }
2202
2203   dir.Close();
2204 }
2205
2206 class SyncableClientTagTest : public SyncableDirectoryTest {
2207  public:
2208   static const int kBaseVersion = 1;
2209   const char* test_name_;
2210   const char* test_tag_;
2211
2212   SyncableClientTagTest() : test_name_("test_name"), test_tag_("dietcoke") {}
2213
2214   bool CreateWithDefaultTag(Id id, bool deleted) {
2215     WriteTransaction wtrans(FROM_HERE, UNITTEST, dir_.get());
2216     MutableEntry me(&wtrans, CREATE, PREFERENCES,
2217                     wtrans.root_id(), test_name_);
2218     CHECK(me.good());
2219     me.PutId(id);
2220     if (id.ServerKnows()) {
2221       me.PutBaseVersion(kBaseVersion);
2222     }
2223     me.PutIsUnsynced(true);
2224     me.PutIsDel(deleted);
2225     me.PutIsDir(false);
2226     return me.PutUniqueClientTag(test_tag_);
2227   }
2228
2229   // Verify an entry exists with the default tag.
2230   void VerifyTag(Id id, bool deleted) {
2231     // Should still be present and valid in the client tag index.
2232     ReadTransaction trans(FROM_HERE, dir_.get());
2233     Entry me(&trans, GET_BY_CLIENT_TAG, test_tag_);
2234     CHECK(me.good());
2235     EXPECT_EQ(me.GetId(), id);
2236     EXPECT_EQ(me.GetUniqueClientTag(), test_tag_);
2237     EXPECT_EQ(me.GetIsDel(), deleted);
2238
2239     // We only sync deleted items that the server knew about.
2240     if (me.GetId().ServerKnows() || !me.GetIsDel()) {
2241       EXPECT_EQ(me.GetIsUnsynced(), true);
2242     }
2243   }
2244
2245  protected:
2246   TestIdFactory factory_;
2247 };
2248
2249 TEST_F(SyncableClientTagTest, TestClientTagClear) {
2250   Id server_id = factory_.NewServerId();
2251   EXPECT_TRUE(CreateWithDefaultTag(server_id, false));
2252   {
2253     WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get());
2254     MutableEntry me(&trans, GET_BY_CLIENT_TAG, test_tag_);
2255     EXPECT_TRUE(me.good());
2256     me.PutUniqueClientTag(std::string());
2257   }
2258   {
2259     ReadTransaction trans(FROM_HERE, dir_.get());
2260     Entry by_tag(&trans, GET_BY_CLIENT_TAG, test_tag_);
2261     EXPECT_FALSE(by_tag.good());
2262
2263     Entry by_id(&trans, GET_BY_ID, server_id);
2264     EXPECT_TRUE(by_id.good());
2265     EXPECT_TRUE(by_id.GetUniqueClientTag().empty());
2266   }
2267 }
2268
2269 TEST_F(SyncableClientTagTest, TestClientTagIndexServerId) {
2270   Id server_id = factory_.NewServerId();
2271   EXPECT_TRUE(CreateWithDefaultTag(server_id, false));
2272   VerifyTag(server_id, false);
2273 }
2274
2275 TEST_F(SyncableClientTagTest, TestClientTagIndexClientId) {
2276   Id client_id = factory_.NewLocalId();
2277   EXPECT_TRUE(CreateWithDefaultTag(client_id, false));
2278   VerifyTag(client_id, false);
2279 }
2280
2281 TEST_F(SyncableClientTagTest, TestDeletedClientTagIndexClientId) {
2282   Id client_id = factory_.NewLocalId();
2283   EXPECT_TRUE(CreateWithDefaultTag(client_id, true));
2284   VerifyTag(client_id, true);
2285 }
2286
2287 TEST_F(SyncableClientTagTest, TestDeletedClientTagIndexServerId) {
2288   Id server_id = factory_.NewServerId();
2289   EXPECT_TRUE(CreateWithDefaultTag(server_id, true));
2290   VerifyTag(server_id, true);
2291 }
2292
2293 TEST_F(SyncableClientTagTest, TestClientTagIndexDuplicateServer) {
2294   EXPECT_TRUE(CreateWithDefaultTag(factory_.NewServerId(), true));
2295   EXPECT_FALSE(CreateWithDefaultTag(factory_.NewServerId(), true));
2296   EXPECT_FALSE(CreateWithDefaultTag(factory_.NewServerId(), false));
2297   EXPECT_FALSE(CreateWithDefaultTag(factory_.NewLocalId(), false));
2298   EXPECT_FALSE(CreateWithDefaultTag(factory_.NewLocalId(), true));
2299 }
2300
2301 }  // namespace
2302 }  // namespace syncable
2303 }  // namespace syncer