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