syncable_unittest.cc revision eb525c5499e34cc9c4b825d6d9e75bb07cc06ace
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/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
41namespace syncer {
42namespace syncable {
43
44using base::ExpectDictBooleanValue;
45using base::ExpectDictStringValue;
46
47class SyncableKernelTest : public testing::Test {};
48
49// TODO(akalin): Add unit tests for EntryKernel::ContainsString().
50
51TEST_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
65namespace {
66void 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->Put(SPECIFICS, specifics);
74}
75
76void ExpectDataFromBookmarkFaviconEquals(BaseTransaction* trans,
77                                         Entry* e,
78                                         const char* bytes,
79                                         size_t bytes_length) {
80  ASSERT_TRUE(e->good());
81  ASSERT_TRUE(e->Get(SPECIFICS).has_bookmark());
82  ASSERT_EQ("http://demo/", e->Get(SPECIFICS).bookmark().url());
83  ASSERT_EQ(std::string(bytes, bytes_length),
84            e->Get(SPECIFICS).bookmark().favicon());
85}
86}  // namespace
87
88class 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
108const char SyncableGeneralTest::kIndexTestName[] = "IndexTest";
109
110TEST_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.Get(META_HANDLE);
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.Put(ID, id);
151    me.Put(BASE_VERSION, 1);
152    written_metahandle = me.Get(META_HANDLE);
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.Put(IS_DEL, true);
208
209    EXPECT_EQ(0, CountEntriesWithName(&trans, trans.root_id(), name));
210  }
211
212  dir.SaveChanges();
213}
214
215TEST_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.Put(ID, id);
243    me.Put(BASE_VERSION, 1);
244    written_metahandle = me.Get(META_HANDLE);
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.Get(ID), 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.Put(IS_DEL, 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
284TEST_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.Put(ID, id);
305      me.Put(BASE_VERSION, 1);
306      me.Put(UNIQUE_CLIENT_TAG, tag);
307      written_metahandle = me.Get(META_HANDLE);
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.Get(ID), id);
326    EXPECT_EQ(me.Get(BASE_VERSION), 1);
327    EXPECT_EQ(me.Get(UNIQUE_CLIENT_TAG), tag);
328    EXPECT_EQ(me.Get(META_HANDLE), written_metahandle);
329  }
330}
331
332TEST_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.Put(ID, id);
351      me.Put(BASE_VERSION, 1);
352      me.Put(UNIQUE_CLIENT_TAG, tag);
353      me.Put(IS_DEL, true);
354      me.Put(IS_UNSYNCED, 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.Get(ID), id);
374    EXPECT_EQ(me.Get(UNIQUE_CLIENT_TAG), tag);
375    EXPECT_TRUE(me.Get(IS_DEL));
376    EXPECT_TRUE(me.Get(IS_UNSYNCED));
377  }
378}
379
380TEST_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.Put(ID, id);
406    me.Put(BASE_VERSION, 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.
420TEST_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.Put(IS_UNSYNCED, 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.Get(UNIQUE_BOOKMARK_TAG));
442  }
443}
444
445// A test fixture for syncable::Directory.  Uses an in-memory database to keep
446// the unit tests fast.
447class 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.Put(ID, id);
535    me.Put(IS_UNSYNCED, 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
565TEST_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.Put(IS_UNSYNCED, true);
574      sync_pb::EntitySpecifics specs;
575      if (i % 2 == 0) {
576        AddDefaultFieldValue(BOOKMARKS, &specs);
577        expected_purges.insert(e.Get(META_HANDLE));
578        all_handles.insert(e.Get(META_HANDLE));
579      } else {
580        AddDefaultFieldValue(PREFERENCES, &specs);
581        all_handles.insert(e.Get(META_HANDLE));
582      }
583      e.Put(SPECIFICS, specs);
584      e.Put(SERVER_SPECIFICS, 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
607TEST_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.Get(META_HANDLE));
615      e.Put(IS_UNSYNCED, 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.Put(IS_DIR, true);
642        e1.Put(IS_UNSYNCED, true);
643        // Add new entries
644        MutableEntry e2(&trans, CREATE, BOOKMARKS, trans.root_id(), "bar");
645        e2.Put(IS_UNSYNCED, true);
646        new_dirty_metahandles.push_back(e2.Get(META_HANDLE));
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
667TEST_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.Get(META_HANDLE));
678      e.Put(IS_UNSYNCED, 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.Put(IS_DIR, true);
693        e1.Put(IS_UNSYNCED, true);
694        // Add new entries
695        MutableEntry e2(&trans, CREATE, BOOKMARKS, trans.root_id(), "bar");
696        e2.Put(IS_UNSYNCED, true);
697        new_dirty_metahandles.push_back(e2.Get(META_HANDLE));
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.Get(IS_DIR);
735          e.Put(IS_DIR, not_dir);
736          e.Put(IS_UNSYNCED, 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.
757TEST_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.Get(META_HANDLE);
775      item1.Put(SPECIFICS, bookmark_specifics);
776      item1.Put(SERVER_SPECIFICS, bookmark_specifics);
777      MutableEntry item2(&trans, GET_BY_ID, id2);
778      ASSERT_TRUE(item2.good());
779      handle2 = item2.Get(META_HANDLE);
780      item2.Put(SPECIFICS, bookmark_specifics);
781      item2.Put(SERVER_SPECIFICS, 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.Put(SERVER_IS_DEL, true);
799      MutableEntry item2(&trans, GET_BY_ID, id2);
800      ASSERT_TRUE(item2.good());
801      item2.Put(SERVER_IS_DEL, 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.Put(SERVER_IS_DEL, 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
879const char SyncableDirectoryTest::kName[] = "Foo";
880
881namespace {
882
883TEST_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
889TEST_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
896TEST_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  ASSERT_TRUE(e1.Put(IS_DEL, true));
902  MutableEntry e2(&trans, CREATE, BOOKMARKS, trans.root_id(), name);
903  ASSERT_TRUE(e2.good());
904  ASSERT_TRUE(e2.Put(IS_DEL, true));
905  MutableEntry e3(&trans, CREATE, BOOKMARKS, trans.root_id(), name);
906  ASSERT_TRUE(e3.good());
907  ASSERT_TRUE(e3.Put(IS_DEL, true));
908
909  ASSERT_TRUE(e1.Put(IS_DEL, false));
910  ASSERT_TRUE(e2.Put(IS_DEL, false));
911  ASSERT_TRUE(e3.Put(IS_DEL, false));
912
913  ASSERT_TRUE(e1.Put(IS_DEL, true));
914  ASSERT_TRUE(e2.Put(IS_DEL, true));
915  ASSERT_TRUE(e3.Put(IS_DEL, true));
916}
917
918TEST_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.Get(META_HANDLE);
930    e1.Put(BASE_VERSION, 1);
931    e1.Put(IS_DIR, true);
932    e1.Put(ID, TestIdFactory::FromNumber(101));
933
934    MutableEntry e2(&trans, CREATE, BOOKMARKS, e1.Get(ID), "bread");
935    ASSERT_TRUE(e2.good());
936    handle2 = e2.Get(META_HANDLE);
937    e2.Put(BASE_VERSION, 1);
938    e2.Put(ID, 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.Put(IS_UNSYNCED, 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.Put(IS_UNSYNCED, 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.Get(IS_UNSYNCED));
977    ASSERT_TRUE(e5.Put(IS_UNSYNCED, false));
978    ASSERT_FALSE(e5.Get(IS_UNSYNCED));
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
989TEST_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.Get(META_HANDLE);
1002    e1.Put(IS_UNAPPLIED_UPDATE, false);
1003    e1.Put(BASE_VERSION, 1);
1004    e1.Put(ID, TestIdFactory::FromNumber(101));
1005    e1.Put(IS_DIR, true);
1006
1007    MutableEntry e2(&trans, CREATE, BOOKMARKS, e1.Get(ID), "bread");
1008    ASSERT_TRUE(e2.good());
1009    handle2 = e2.Get(META_HANDLE);
1010    e2.Put(IS_UNAPPLIED_UPDATE, false);
1011    e2.Put(BASE_VERSION, 1);
1012    e2.Put(ID, 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.Put(IS_UNAPPLIED_UPDATE, 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.Put(IS_UNAPPLIED_UPDATE, 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.Put(IS_UNAPPLIED_UPDATE, 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
1062TEST_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.Put(IS_DIR, true);
1071    parent.Put(ID, id_factory.NewServerId());
1072    parent.Put(BASE_VERSION, 1);
1073    MutableEntry child(&wtrans, CREATE, BOOKMARKS, parent.Get(ID), "Bob");
1074    ASSERT_TRUE(child.good());
1075    child.Put(IS_DIR, true);
1076    child.Put(ID, id_factory.NewServerId());
1077    child.Put(BASE_VERSION, 1);
1078    MutableEntry grandchild(&wtrans, CREATE, BOOKMARKS, child.Get(ID), "Bob");
1079    ASSERT_TRUE(grandchild.good());
1080    grandchild.Put(ID, id_factory.NewServerId());
1081    grandchild.Put(BASE_VERSION, 1);
1082    ASSERT_TRUE(grandchild.Put(IS_DEL, true));
1083    MutableEntry twin(&wtrans, CREATE, BOOKMARKS, child.Get(ID), "Bob");
1084    ASSERT_TRUE(twin.good());
1085    ASSERT_TRUE(twin.Put(IS_DEL, true));
1086    ASSERT_TRUE(grandchild.Put(IS_DEL, false));
1087
1088    grandchild_handle = grandchild.Get(META_HANDLE);
1089  }
1090  dir_->SaveChanges();
1091  {
1092    WriteTransaction wtrans(FROM_HERE, UNITTEST, dir_.get());
1093    MutableEntry grandchild(&wtrans, GET_BY_HANDLE, grandchild_handle);
1094    grandchild.Put(IS_DEL, true);  // Used to CHECK fail here.
1095  }
1096}
1097
1098static inline bool IsLegalNewParent(const Entry& a, const Entry& b) {
1099  return IsLegalNewParent(a.trans(), a.Get(ID), b.Get(ID));
1100}
1101
1102TEST_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.Get(ID), "Bob");
1108  ASSERT_TRUE(parent.good());
1109  parent.Put(IS_DIR, true);
1110  parent.Put(ID, id_factory.NewServerId());
1111  parent.Put(BASE_VERSION, 1);
1112  MutableEntry child(&wtrans, CREATE, BOOKMARKS, parent.Get(ID), "Bob");
1113  ASSERT_TRUE(child.good());
1114  child.Put(IS_DIR, true);
1115  child.Put(ID, id_factory.NewServerId());
1116  child.Put(BASE_VERSION, 1);
1117  MutableEntry grandchild(&wtrans, CREATE, BOOKMARKS, child.Get(ID), "Bob");
1118  ASSERT_TRUE(grandchild.good());
1119  grandchild.Put(ID, id_factory.NewServerId());
1120  grandchild.Put(BASE_VERSION, 1);
1121
1122  MutableEntry parent2(&wtrans, CREATE, BOOKMARKS, root.Get(ID), "Pete");
1123  ASSERT_TRUE(parent2.good());
1124  parent2.Put(IS_DIR, true);
1125  parent2.Put(ID, id_factory.NewServerId());
1126  parent2.Put(BASE_VERSION, 1);
1127  MutableEntry child2(&wtrans, CREATE, BOOKMARKS, parent2.Get(ID), "Pete");
1128  ASSERT_TRUE(child2.good());
1129  child2.Put(IS_DIR, true);
1130  child2.Put(ID, id_factory.NewServerId());
1131  child2.Put(BASE_VERSION, 1);
1132  MutableEntry grandchild2(&wtrans, CREATE, BOOKMARKS, child2.Get(ID), "Pete");
1133  ASSERT_TRUE(grandchild2.good());
1134  grandchild2.Put(ID, id_factory.NewServerId());
1135  grandchild2.Put(BASE_VERSION, 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
1155TEST_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    EXPECT_TRUE(folder.Put(IS_DIR, true));
1167    EXPECT_TRUE(folder.Put(IS_UNSYNCED, true));
1168    folder_id = folder.Get(ID);
1169
1170    MutableEntry entry(&trans, CREATE, BOOKMARKS, folder.Get(ID), entry_name);
1171    ASSERT_TRUE(entry.good());
1172    entry_handle = entry.Get(META_HANDLE);
1173    entry.Put(IS_UNSYNCED, true);
1174    entry_id = entry.Get(ID);
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.Get(META_HANDLE));
1186    EXPECT_TRUE(entry.Get(NON_UNIQUE_NAME) == entry_name);
1187    EXPECT_TRUE(entry.Get(PARENT_ID) == folder_id);
1188  }
1189}
1190
1191TEST_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.Put(IS_UNSYNCED, true);
1197  EXPECT_TRUE(parent_folder.Put(IS_DIR, true));
1198
1199  MutableEntry parent_folder2(&wt, CREATE, BOOKMARKS, wt.root_id(), "folder2");
1200  parent_folder2.Put(IS_UNSYNCED, true);
1201  EXPECT_TRUE(parent_folder2.Put(IS_DIR, true));
1202
1203  MutableEntry child(&wt, CREATE, BOOKMARKS, parent_folder.Get(ID), child_name);
1204  EXPECT_TRUE(child.Put(IS_DIR, true));
1205  child.Put(IS_UNSYNCED, true);
1206
1207  ASSERT_TRUE(child.good());
1208
1209  EXPECT_EQ(0, CountEntriesWithName(&wt, wt.root_id(), child_name));
1210  EXPECT_EQ(parent_folder.Get(ID), child.Get(PARENT_ID));
1211  EXPECT_EQ(1, CountEntriesWithName(&wt, parent_folder.Get(ID), child_name));
1212  EXPECT_EQ(0, CountEntriesWithName(&wt, parent_folder2.Get(ID), child_name));
1213  child.Put(PARENT_ID, parent_folder2.Get(ID));
1214  EXPECT_EQ(parent_folder2.Get(ID), child.Get(PARENT_ID));
1215  EXPECT_EQ(0, CountEntriesWithName(&wt, parent_folder.Get(ID), child_name));
1216  EXPECT_EQ(1, CountEntriesWithName(&wt, parent_folder2.Get(ID), child_name));
1217}
1218
1219TEST_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  ASSERT_TRUE(folder.Put(IS_DIR, true));
1227  ASSERT_TRUE(folder.Put(IS_DEL, true));
1228
1229  EXPECT_EQ(0, CountEntriesWithName(&trans, trans.root_id(), folder_name));
1230
1231  MutableEntry deleted(&trans, GET_BY_ID, folder.Get(ID));
1232  ASSERT_TRUE(deleted.good());
1233  ASSERT_TRUE(deleted.Put(PARENT_ID, trans.root_id()));
1234  ASSERT_TRUE(deleted.Put(NON_UNIQUE_NAME, 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
1240TEST_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  EXPECT_TRUE(folder.Put(PARENT_ID, trans.root_id()));
1245  EXPECT_TRUE(folder.Put(NON_UNIQUE_NAME, "CASECHANGE"));
1246  EXPECT_TRUE(folder.Put(IS_DEL, true));
1247}
1248
1249// Create items of each model type, and check that GetModelType and
1250// GetServerModelType return the right value.
1251TEST_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.Put(ID, id_factory.NewServerId());
1273    folder.Put(SPECIFICS, specifics);
1274    folder.Put(BASE_VERSION, 1);
1275    folder.Put(IS_DIR, true);
1276    folder.Put(IS_DEL, 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.Put(ID, id_factory.NewServerId());
1282    item.Put(SPECIFICS, specifics);
1283    item.Put(BASE_VERSION, 1);
1284    item.Put(IS_DIR, false);
1285    item.Put(IS_DEL, 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.Put(ID, id_factory.NewServerId());
1294    deleted_item.Put(SPECIFICS, specifics);
1295    deleted_item.Put(BASE_VERSION, 1);
1296    deleted_item.Put(IS_DIR, false);
1297    deleted_item.Put(IS_DEL, 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.Put(SERVER_SPECIFICS, specifics);
1304    server_folder.Put(BASE_VERSION, 1);
1305    server_folder.Put(SERVER_IS_DIR, true);
1306    server_folder.Put(SERVER_IS_DEL, 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.Put(SERVER_SPECIFICS, specifics);
1313    server_item.Put(BASE_VERSION, 1);
1314    server_item.Put(SERVER_IS_DIR, false);
1315    server_item.Put(SERVER_IS_DEL, 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.
1337TEST_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.Put(IS_DIR, true);
1348    parent.Put(IS_UNSYNCED, true);
1349
1350    MutableEntry child(&trans, CREATE, BOOKMARKS, parent.Get(ID), "child");
1351    child.Put(IS_UNSYNCED, true);
1352
1353    orig_parent_id = parent.Get(ID);
1354    orig_child_id = child.Get(ID);
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.Put(IS_UNSYNCED, false);
1367    child.Put(BASE_VERSION, 1);
1368    child.Put(SERVER_VERSION, 1);
1369
1370    ChangeEntryIDAndUpdateChildren(&trans, &parent, id_factory.NewServerId());
1371    parent.Put(IS_UNSYNCED, false);
1372    parent.Put(BASE_VERSION, 1);
1373    parent.Put(SERVER_VERSION, 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.
1384TEST_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.Put(IS_DIR, true);
1396    parent.Put(IS_UNSYNCED, true);
1397
1398    MutableEntry child(&trans, CREATE, BOOKMARKS, parent.Get(ID), "child");
1399    child.Put(IS_UNSYNCED, true);
1400
1401    orig_parent_id = parent.Get(ID);
1402    orig_child_id = child.Get(ID);
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.Put(IS_DEL, 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.Put(IS_UNSYNCED, false);
1422    parent.Put(BASE_VERSION, 1);
1423    parent.Put(SERVER_VERSION, 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.
1433TEST_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.
1443TEST_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.
1454TEST_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.Put(ID, server_knows_id);
1480    server_knows.Put(IS_UNSYNCED, true);
1481    server_knows.Put(IS_DEL, true);
1482    server_knows.Put(BASE_VERSION, 5);
1483    server_knows.Put(SERVER_VERSION, 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.Put(ID, not_is_del_id);
1489    not_is_del.Put(IS_DEL, false);
1490    not_is_del.Put(IS_UNSYNCED, 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.Put(ID, zombie_id);
1500    zombie.Put(IS_DEL, true);
1501    zombie.Put(IS_UNSYNCED, 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
1524TEST_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.Put(IS_DIR, true);
1543    parent.Put(IS_UNSYNCED, true);
1544
1545    MutableEntry child(&trans, CREATE, BOOKMARKS, parent.Get(ID), "child");
1546    child.Put(IS_UNSYNCED, true);
1547    child.Put(UNIQUE_POSITION, null_pos);
1548    child.Put(SERVER_UNIQUE_POSITION, null_pos);
1549
1550    null_child_id = child.Get(ID);
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.Get(UNIQUE_POSITION)));
1561    EXPECT_TRUE(
1562        null_pos.Equals(null_ordinal_child.Get(SERVER_UNIQUE_POSITION)));
1563  }
1564}
1565
1566// An OnDirectoryBackingStore that can be set to always fail SaveChanges.
1567class 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
1585TestBackingStore::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
1591TestBackingStore::~TestBackingStore() { }
1592
1593bool 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.
1603class 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
1626TestDirectory* 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
1636TestDirectory::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
1643TestDirectory::~TestDirectory() { }
1644
1645TEST(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.
1664class 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::Delete(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::Delete(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
1708TEST_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.Put(SERVER_SPECIFICS, bookmark_specs);
1734    item1.Put(IS_UNSYNCED, true);
1735
1736    MutableEntry item2(&trans, CREATE_NEW_UPDATE_ITEM,
1737                       id_factory.NewServerId());
1738    ASSERT_TRUE(item2.good());
1739    item2.Put(SERVER_SPECIFICS, bookmark_specs);
1740    item2.Put(IS_UNAPPLIED_UPDATE, true);
1741
1742    MutableEntry item3(&trans, CREATE, PREFERENCES,
1743                       trans.root_id(), "Item");
1744    ASSERT_TRUE(item3.good());
1745    item3.Put(SPECIFICS, preference_specs);
1746    item3.Put(SERVER_SPECIFICS, preference_specs);
1747    item3.Put(IS_UNSYNCED, true);
1748
1749    MutableEntry item4(&trans, CREATE_NEW_UPDATE_ITEM,
1750                       id_factory.NewServerId());
1751    ASSERT_TRUE(item4.good());
1752    item4.Put(SERVER_SPECIFICS, preference_specs);
1753    item4.Put(IS_UNAPPLIED_UPDATE, true);
1754
1755    MutableEntry item5(&trans, CREATE, AUTOFILL,
1756                       trans.root_id(), "Item");
1757    ASSERT_TRUE(item5.good());
1758    item5.Put(SPECIFICS, autofill_specs);
1759    item5.Put(SERVER_SPECIFICS, autofill_specs);
1760    item5.Put(IS_UNSYNCED, true);
1761
1762    MutableEntry item6(&trans, CREATE_NEW_UPDATE_ITEM,
1763      id_factory.NewServerId());
1764    ASSERT_TRUE(item6.good());
1765    item6.Put(SERVER_SPECIFICS, autofill_specs);
1766    item6.Put(IS_UNAPPLIED_UPDATE, 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
1786TEST_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
1821TEST_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.Put(IS_UNSYNCED, true);
1835    update.Put(IS_UNAPPLIED_UPDATE, true);
1836    sync_pb::EntitySpecifics specifics;
1837    specifics.mutable_bookmark()->set_favicon("PNG");
1838    specifics.mutable_bookmark()->set_url("http://nowhere");
1839    create.Put(SPECIFICS, specifics);
1840    update.Put(SPECIFICS, specifics);
1841    create_pre_save = create.GetKernelCopy();
1842    update_pre_save = update.GetKernelCopy();
1843    create_id = create.Get(ID);
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
1926TEST_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.Get(META_HANDLE);
1936    e1.Put(BASE_VERSION, 1);
1937    e1.Put(IS_DIR, true);
1938    e1.Put(ID, 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.Get(NON_UNIQUE_NAME), "aguilera");
1953    aguilera.Put(NON_UNIQUE_NAME, "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.Get(NON_UNIQUE_NAME), "overwritten");
1971    EXPECT_FALSE(aguilera.GetKernelCopy().is_dirty());
1972    EXPECT_FALSE(IsInDirtyMetahandles(handle1));
1973    aguilera.Put(NON_UNIQUE_NAME, "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.Get(META_HANDLE);
1982    kids_on_block.Put(BASE_VERSION, 1);
1983    kids_on_block.Put(IS_DIR, true);
1984    kids_on_block.Put(ID, 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
2008TEST_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.Get(META_HANDLE);
2018    e1.Put(BASE_VERSION, 1);
2019    e1.Put(IS_DIR, true);
2020    e1.Put(ID, TestIdFactory::FromNumber(101));
2021    sync_pb::EntitySpecifics bookmark_specs;
2022    AddDefaultFieldValue(BOOKMARKS, &bookmark_specs);
2023    e1.Put(SPECIFICS, bookmark_specs);
2024    e1.Put(SERVER_SPECIFICS, bookmark_specs);
2025    e1.Put(ID, 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
2044void 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.Get(NON_UNIQUE_NAME));
2055  ASSERT_TRUE(base_version == e.Get(BASE_VERSION));
2056  ASSERT_TRUE(server_version == e.Get(SERVER_VERSION));
2057  ASSERT_TRUE(is_del == e.Get(IS_DEL));
2058}
2059
2060DirOpenResult SyncableDirectoryTest::SimulateSaveAndReloadDir() {
2061  if (!dir_->SaveChanges())
2062    return FAILED_IN_UNITTEST;
2063
2064  return ReloadDirImpl();
2065}
2066
2067DirOpenResult SyncableDirectoryTest::SimulateCrashAndReloadDir() {
2068  return ReloadDirImpl();
2069}
2070
2071DirOpenResult 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
2095namespace {
2096
2097class 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
2113TEST_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::Delete(path, true));
2129}
2130
2131class 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.Put(IS_UNSYNCED, true);
2163        if (e.Put(ID, TestIdFactory::FromNumber(rand())) &&
2164            e.Get(ID).ServerKnows() && !e.Get(ID).IsRoot()) {
2165           e.Put(BASE_VERSION, 1);
2166        }
2167      }
2168    }
2169  }
2170
2171  DISALLOW_COPY_AND_ASSIGN(StressTransactionsDelegate);
2172};
2173
2174TEST(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
2206class 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.Put(ID, id);
2220    if (id.ServerKnows()) {
2221      me.Put(BASE_VERSION, kBaseVersion);
2222    }
2223    me.Put(IS_UNSYNCED, true);
2224    me.Put(IS_DEL, deleted);
2225    me.Put(IS_DIR, false);
2226    return me.Put(UNIQUE_CLIENT_TAG, 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.Get(ID), id);
2236    EXPECT_EQ(me.Get(UNIQUE_CLIENT_TAG), test_tag_);
2237    EXPECT_EQ(me.Get(IS_DEL), deleted);
2238
2239    // We only sync deleted items that the server knew about.
2240    if (me.Get(ID).ServerKnows() || !me.Get(IS_DEL)) {
2241      EXPECT_EQ(me.Get(IS_UNSYNCED), true);
2242    }
2243  }
2244
2245 protected:
2246  TestIdFactory factory_;
2247};
2248
2249TEST_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.Put(UNIQUE_CLIENT_TAG, 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.Get(UNIQUE_CLIENT_TAG).empty());
2266  }
2267}
2268
2269TEST_F(SyncableClientTagTest, TestClientTagIndexServerId) {
2270  Id server_id = factory_.NewServerId();
2271  EXPECT_TRUE(CreateWithDefaultTag(server_id, false));
2272  VerifyTag(server_id, false);
2273}
2274
2275TEST_F(SyncableClientTagTest, TestClientTagIndexClientId) {
2276  Id client_id = factory_.NewLocalId();
2277  EXPECT_TRUE(CreateWithDefaultTag(client_id, false));
2278  VerifyTag(client_id, false);
2279}
2280
2281TEST_F(SyncableClientTagTest, TestDeletedClientTagIndexClientId) {
2282  Id client_id = factory_.NewLocalId();
2283  EXPECT_TRUE(CreateWithDefaultTag(client_id, true));
2284  VerifyTag(client_id, true);
2285}
2286
2287TEST_F(SyncableClientTagTest, TestDeletedClientTagIndexServerId) {
2288  Id server_id = factory_.NewServerId();
2289  EXPECT_TRUE(CreateWithDefaultTag(server_id, true));
2290  VerifyTag(server_id, true);
2291}
2292
2293TEST_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
2304