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.
7 #include "base/basictypes.h"
8 #include "base/compiler_specific.h"
9 #include "base/file_util.h"
10 #include "base/files/file_path.h"
11 #include "base/files/scoped_temp_dir.h"
12 #include "base/location.h"
13 #include "base/logging.h"
14 #include "base/memory/scoped_ptr.h"
15 #include "base/message_loop/message_loop.h"
16 #include "base/stl_util.h"
17 #include "base/strings/stringprintf.h"
18 #include "base/synchronization/condition_variable.h"
19 #include "base/test/values_test_util.h"
20 #include "base/threading/platform_thread.h"
21 #include "base/values.h"
22 #include "sync/protocol/bookmark_specifics.pb.h"
23 #include "sync/syncable/directory_backing_store.h"
24 #include "sync/syncable/directory_change_delegate.h"
25 #include "sync/syncable/in_memory_directory_backing_store.h"
26 #include "sync/syncable/metahandle_set.h"
27 #include "sync/syncable/mutable_entry.h"
28 #include "sync/syncable/on_disk_directory_backing_store.h"
29 #include "sync/syncable/syncable_proto_util.h"
30 #include "sync/syncable/syncable_read_transaction.h"
31 #include "sync/syncable/syncable_util.h"
32 #include "sync/syncable/syncable_write_transaction.h"
33 #include "sync/test/engine/test_id_factory.h"
34 #include "sync/test/engine/test_syncable_utils.h"
35 #include "sync/test/fake_encryptor.h"
36 #include "sync/test/null_directory_change_delegate.h"
37 #include "sync/test/null_transaction_observer.h"
38 #include "sync/util/test_unrecoverable_error_handler.h"
39 #include "testing/gtest/include/gtest/gtest.h"
44 using base::ExpectDictBooleanValue;
45 using base::ExpectDictStringValue;
47 class SyncableKernelTest : public testing::Test {};
49 // TODO(akalin): Add unit tests for EntryKernel::ContainsString().
51 TEST_F(SyncableKernelTest, ToValue) {
53 scoped_ptr<base::DictionaryValue> value(kernel.ToValue(NULL));
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()));
66 void PutDataAsBookmarkFavicon(WriteTransaction* wtrans,
69 size_t bytes_length) {
70 sync_pb::EntitySpecifics specifics;
71 specifics.mutable_bookmark()->set_url("http://demo/");
72 specifics.mutable_bookmark()->set_favicon(bytes, bytes_length);
73 e->PutSpecifics(specifics);
76 void ExpectDataFromBookmarkFaviconEquals(BaseTransaction* trans,
79 size_t bytes_length) {
80 ASSERT_TRUE(e->good());
81 ASSERT_TRUE(e->GetSpecifics().has_bookmark());
82 ASSERT_EQ("http://demo/", e->GetSpecifics().bookmark().url());
83 ASSERT_EQ(std::string(bytes, bytes_length),
84 e->GetSpecifics().bookmark().favicon());
88 class SyncableGeneralTest : public testing::Test {
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"));
97 virtual void TearDown() {
100 base::MessageLoop message_loop_;
101 base::ScopedTempDir temp_dir_;
102 NullDirectoryChangeDelegate delegate_;
103 FakeEncryptor encryptor_;
104 TestUnrecoverableErrorHandler handler_;
105 base::FilePath db_path_;
108 const char SyncableGeneralTest::kIndexTestName[] = "IndexTest";
110 TEST_F(SyncableGeneralTest, General) {
111 Directory dir(new InMemoryDirectoryBackingStore("SimpleTest"),
117 ASSERT_EQ(OPENED, dir.Open(
118 "SimpleTest", &delegate_, NullTransactionObserver()));
120 int64 root_metahandle;
122 ReadTransaction rtrans(FROM_HERE, &dir);
123 Entry e(&rtrans, GET_BY_ID, rtrans.root_id());
124 ASSERT_TRUE(e.good());
125 root_metahandle = e.GetMetahandle();
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.
133 ReadTransaction rtrans(FROM_HERE, &dir);
134 Entry e(&rtrans, GET_BY_ID, id);
135 ASSERT_FALSE(e.good()); // Hasn't been written yet.
137 Directory::Metahandles child_handles;
138 dir.GetChildHandlesById(&rtrans, rtrans.root_id(), &child_handles);
139 EXPECT_TRUE(child_handles.empty());
141 dir.GetChildHandlesByHandle(&rtrans, root_metahandle, &child_handles);
142 EXPECT_TRUE(child_handles.empty());
145 // Test creating a new meta entry.
147 WriteTransaction wtrans(FROM_HERE, UNITTEST, &dir);
148 MutableEntry me(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(), name);
149 ASSERT_TRUE(me.good());
151 me.PutBaseVersion(1);
152 written_metahandle = me.GetMetahandle();
155 // Test GetChildHandles* after something is now in the DB.
156 // Also check that GET_BY_ID works.
158 ReadTransaction rtrans(FROM_HERE, &dir);
159 Entry e(&rtrans, GET_BY_ID, id);
160 ASSERT_TRUE(e.good());
162 Directory::Metahandles child_handles;
163 dir.GetChildHandlesById(&rtrans, rtrans.root_id(), &child_handles);
164 EXPECT_EQ(1u, child_handles.size());
166 for (Directory::Metahandles::iterator i = child_handles.begin();
167 i != child_handles.end(); ++i) {
168 EXPECT_EQ(*i, written_metahandle);
171 dir.GetChildHandlesByHandle(&rtrans, root_metahandle, &child_handles);
172 EXPECT_EQ(1u, child_handles.size());
174 for (Directory::Metahandles::iterator i = child_handles.begin();
175 i != child_handles.end(); ++i) {
176 EXPECT_EQ(*i, written_metahandle);
180 // Test writing data to an entity. Also check that GET_BY_HANDLE works.
181 static const char s[] = "Hello World.";
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));
189 // Test reading back the contents that we just wrote.
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));
197 // Verify it exists in the folder.
199 ReadTransaction rtrans(FROM_HERE, &dir);
200 EXPECT_EQ(1, CountEntriesWithName(&rtrans, rtrans.root_id(), name));
205 WriteTransaction trans(FROM_HERE, UNITTEST, &dir);
206 MutableEntry e(&trans, GET_BY_HANDLE, written_metahandle);
209 EXPECT_EQ(0, CountEntriesWithName(&trans, trans.root_id(), name));
215 TEST_F(SyncableGeneralTest, ChildrenOps) {
216 Directory dir(new InMemoryDirectoryBackingStore("SimpleTest"),
221 ASSERT_EQ(OPENED, dir.Open(
222 "SimpleTest", &delegate_, NullTransactionObserver()));
224 int64 written_metahandle;
225 const Id id = TestIdFactory::FromNumber(99);
226 std::string name = "Jeff";
228 ReadTransaction rtrans(FROM_HERE, &dir);
229 Entry e(&rtrans, GET_BY_ID, id);
230 ASSERT_FALSE(e.good()); // Hasn't been written yet.
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());
239 WriteTransaction wtrans(FROM_HERE, UNITTEST, &dir);
240 MutableEntry me(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(), name);
241 ASSERT_TRUE(me.good());
243 me.PutBaseVersion(1);
244 written_metahandle = me.GetMetahandle();
247 // Test children ops after something is now in the DB.
249 ReadTransaction rtrans(FROM_HERE, &dir);
250 Entry e(&rtrans, GET_BY_ID, id);
251 ASSERT_TRUE(e.good());
253 Entry child(&rtrans, GET_BY_HANDLE, written_metahandle);
254 ASSERT_TRUE(child.good());
256 Entry root(&rtrans, GET_BY_ID, rtrans.root_id());
257 ASSERT_TRUE(root.good());
258 EXPECT_TRUE(dir.HasChildren(&rtrans, rtrans.root_id()));
259 EXPECT_EQ(e.GetId(), root.GetFirstChildId());
263 WriteTransaction wtrans(FROM_HERE, UNITTEST, &dir);
264 MutableEntry me(&wtrans, GET_BY_HANDLE, written_metahandle);
265 ASSERT_TRUE(me.good());
269 // Test children ops after the children have been deleted.
271 ReadTransaction rtrans(FROM_HERE, &dir);
272 Entry e(&rtrans, GET_BY_ID, id);
273 ASSERT_TRUE(e.good());
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());
284 TEST_F(SyncableGeneralTest, ClientIndexRebuildsProperly) {
285 int64 written_metahandle;
286 TestIdFactory factory;
287 const Id id = factory.NewServerId();
288 std::string name = "cheesepuffs";
289 std::string tag = "dietcoke";
291 // Test creating a new meta entry.
293 Directory dir(new OnDiskDirectoryBackingStore(kIndexTestName, db_path_),
298 ASSERT_EQ(OPENED, dir.Open(kIndexTestName, &delegate_,
299 NullTransactionObserver()));
301 WriteTransaction wtrans(FROM_HERE, UNITTEST, &dir);
302 MutableEntry me(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(), name);
303 ASSERT_TRUE(me.good());
305 me.PutBaseVersion(1);
306 me.PutUniqueClientTag(tag);
307 written_metahandle = me.GetMetahandle();
312 // The DB was closed. Now reopen it. This will cause index regeneration.
314 Directory dir(new OnDiskDirectoryBackingStore(kIndexTestName, db_path_),
319 ASSERT_EQ(OPENED, dir.Open(kIndexTestName,
320 &delegate_, NullTransactionObserver()));
322 ReadTransaction trans(FROM_HERE, &dir);
323 Entry me(&trans, GET_BY_CLIENT_TAG, tag);
324 ASSERT_TRUE(me.good());
325 EXPECT_EQ(me.GetId(), id);
326 EXPECT_EQ(me.GetBaseVersion(), 1);
327 EXPECT_EQ(me.GetUniqueClientTag(), tag);
328 EXPECT_EQ(me.GetMetahandle(), written_metahandle);
332 TEST_F(SyncableGeneralTest, ClientIndexRebuildsDeletedProperly) {
333 TestIdFactory factory;
334 const Id id = factory.NewServerId();
335 std::string tag = "dietcoke";
337 // Test creating a deleted, unsynced, server meta entry.
339 Directory dir(new OnDiskDirectoryBackingStore(kIndexTestName, db_path_),
344 ASSERT_EQ(OPENED, dir.Open(kIndexTestName, &delegate_,
345 NullTransactionObserver()));
347 WriteTransaction wtrans(FROM_HERE, UNITTEST, &dir);
348 MutableEntry me(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(), "deleted");
349 ASSERT_TRUE(me.good());
351 me.PutBaseVersion(1);
352 me.PutUniqueClientTag(tag);
354 me.PutIsUnsynced(true); // Or it might be purged.
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.
362 Directory dir(new OnDiskDirectoryBackingStore(kIndexTestName, db_path_),
367 ASSERT_EQ(OPENED, dir.Open(kIndexTestName, &delegate_,
368 NullTransactionObserver()));
370 ReadTransaction trans(FROM_HERE, &dir);
371 Entry me(&trans, GET_BY_CLIENT_TAG, tag);
372 ASSERT_TRUE(me.good());
373 EXPECT_EQ(me.GetId(), id);
374 EXPECT_EQ(me.GetUniqueClientTag(), tag);
375 EXPECT_TRUE(me.GetIsDel());
376 EXPECT_TRUE(me.GetIsUnsynced());
380 TEST_F(SyncableGeneralTest, ToValue) {
381 Directory dir(new InMemoryDirectoryBackingStore("SimpleTest"),
386 ASSERT_EQ(OPENED, dir.Open(
387 "SimpleTest", &delegate_, NullTransactionObserver()));
389 const Id id = TestIdFactory::FromNumber(99);
391 ReadTransaction rtrans(FROM_HERE, &dir);
392 Entry e(&rtrans, GET_BY_ID, id);
393 EXPECT_FALSE(e.good()); // Hasn't been written yet.
395 scoped_ptr<base::DictionaryValue> value(e.ToValue(NULL));
396 ExpectDictBooleanValue(false, *value, "good");
397 EXPECT_EQ(1u, value->size());
400 // Test creating a new meta entry.
402 WriteTransaction wtrans(FROM_HERE, UNITTEST, &dir);
403 MutableEntry me(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(), "new");
404 ASSERT_TRUE(me.good());
406 me.PutBaseVersion(1);
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");
419 // Test that the bookmark tag generation algorithm remains unchanged.
420 TEST_F(SyncableGeneralTest, BookmarkTagTest) {
421 InMemoryDirectoryBackingStore* store = new InMemoryDirectoryBackingStore("x");
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();
429 Directory dir(store, &handler_, NULL, NULL, NULL);
430 ASSERT_EQ(OPENED, dir.Open("x", &delegate_, NullTransactionObserver()));
433 WriteTransaction wtrans(FROM_HERE, UNITTEST, &dir);
434 MutableEntry bm(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(), "bm");
435 bm.PutIsUnsynced(true);
437 // If this assertion fails, that might indicate that the algorithm used to
438 // generate bookmark tags has been modified. This could have implications
439 // for bookmark ordering. Please make sure you know what you're doing if
440 // you intend to make such a change.
441 ASSERT_EQ("6wHRAb3kbnXV5GHrejp4/c1y5tw=", bm.GetUniqueBookmarkTag());
445 // A test fixture for syncable::Directory. Uses an in-memory database to keep
446 // the unit tests fast.
447 class SyncableDirectoryTest : public testing::Test {
449 base::MessageLoop message_loop_;
450 static const char kName[];
452 virtual void SetUp() {
453 dir_.reset(new Directory(new InMemoryDirectoryBackingStore(kName),
458 ASSERT_TRUE(dir_.get());
459 ASSERT_EQ(OPENED, dir_->Open(kName, &delegate_,
460 NullTransactionObserver()));
461 ASSERT_TRUE(dir_->good());
464 virtual void TearDown() {
470 void GetAllMetaHandles(BaseTransaction* trans, MetahandleSet* result) {
471 dir_->GetAllMetaHandles(trans, result);
474 bool IsInDirtyMetahandles(int64 metahandle) {
475 return 1 == dir_->kernel_->dirty_metahandles.count(metahandle);
478 bool IsInMetahandlesToPurge(int64 metahandle) {
479 return 1 == dir_->kernel_->metahandles_to_purge.count(metahandle);
482 void CheckPurgeEntriesWithTypeInSucceeded(ModelTypeSet types_to_purge,
483 bool before_reload) {
484 SCOPED_TRACE(testing::Message("Before reload: ") << before_reload);
486 ReadTransaction trans(FROM_HERE, dir_.get());
487 MetahandleSet all_set;
488 dir_->GetAllMetaHandles(&trans, &all_set);
489 EXPECT_EQ(4U, all_set.size());
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();
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.";
508 for (ModelTypeSet::Iterator it = types_to_purge.First();
509 it.Good(); it.Inc()) {
510 EXPECT_FALSE(dir_->InitialSyncEndedForType(it.Get()));
512 EXPECT_FALSE(types_to_purge.Has(BOOKMARKS));
513 EXPECT_TRUE(dir_->InitialSyncEndedForType(BOOKMARKS));
516 FakeEncryptor encryptor_;
517 TestUnrecoverableErrorHandler handler_;
518 scoped_ptr<Directory> dir_;
519 NullDirectoryChangeDelegate delegate_;
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));
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));
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());
535 me.PutIsUnsynced(true);
538 void ValidateEntry(BaseTransaction* trans,
541 const std::string& name,
543 int64 server_version,
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.
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();
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();
561 // A helper function for Simulate{Save,Crash}AndReloadDir.
562 DirOpenResult ReloadDirImpl();
565 TEST_F(SyncableDirectoryTest, TakeSnapshotGetsMetahandlesToPurge) {
566 const int metas_to_create = 50;
567 MetahandleSet expected_purges;
568 MetahandleSet all_handles;
570 WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get());
571 for (int i = 0; i < metas_to_create; i++) {
572 MutableEntry e(&trans, CREATE, BOOKMARKS, trans.root_id(), "foo");
573 e.PutIsUnsynced(true);
574 sync_pb::EntitySpecifics specs;
576 AddDefaultFieldValue(BOOKMARKS, &specs);
577 expected_purges.insert(e.GetMetahandle());
578 all_handles.insert(e.GetMetahandle());
580 AddDefaultFieldValue(PREFERENCES, &specs);
581 all_handles.insert(e.GetMetahandle());
583 e.PutSpecifics(specs);
584 e.PutServerSpecifics(specs);
588 ModelTypeSet to_purge(BOOKMARKS);
589 dir_->PurgeEntriesWithTypeIn(to_purge, ModelTypeSet(), ModelTypeSet());
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);
597 to_purge.Put(PREFERENCES);
598 dir_->PurgeEntriesWithTypeIn(to_purge, ModelTypeSet(), ModelTypeSet());
600 dir_->HandleSaveChangesFailure(snapshot1);
602 Directory::SaveChangesSnapshot snapshot2;
603 dir_->TakeSnapshotForSaveChanges(&snapshot2);
604 EXPECT_TRUE(all_handles == snapshot2.metahandles_to_purge);
607 TEST_F(SyncableDirectoryTest, TakeSnapshotGetsAllDirtyHandlesTest) {
608 const int metahandles_to_create = 100;
609 std::vector<int64> expected_dirty_metahandles;
611 WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get());
612 for (int i = 0; i < metahandles_to_create; i++) {
613 MutableEntry e(&trans, CREATE, BOOKMARKS, trans.root_id(), "foo");
614 expected_dirty_metahandles.push_back(e.GetMetahandle());
615 e.PutIsUnsynced(true);
618 // Fake SaveChanges() and make sure we got what we expected.
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());
630 dir_->VacuumAfterSaveChanges(snapshot);
632 // Put a new value with existing transactions as well as adding new ones.
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);
642 e1.PutIsUnsynced(true);
644 MutableEntry e2(&trans, CREATE, BOOKMARKS, trans.root_id(), "bar");
645 e2.PutIsUnsynced(true);
646 new_dirty_metahandles.push_back(e2.GetMetahandle());
648 expected_dirty_metahandles.insert(expected_dirty_metahandles.end(),
649 new_dirty_metahandles.begin(), new_dirty_metahandles.end());
651 // Fake SaveChanges() and make sure we got what we expected.
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());
663 dir_->VacuumAfterSaveChanges(snapshot);
667 TEST_F(SyncableDirectoryTest, TakeSnapshotGetsOnlyDirtyHandlesTest) {
668 const int metahandles_to_create = 100;
670 // half of 2 * metahandles_to_create
671 const unsigned int number_changed = 100u;
672 std::vector<int64> expected_dirty_metahandles;
674 WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get());
675 for (int i = 0; i < metahandles_to_create; i++) {
676 MutableEntry e(&trans, CREATE, BOOKMARKS, trans.root_id(), "foo");
677 expected_dirty_metahandles.push_back(e.GetMetahandle());
678 e.PutIsUnsynced(true);
682 // Put a new value with existing transactions as well as adding new ones.
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());
693 e1.PutIsUnsynced(true);
695 MutableEntry e2(&trans, CREATE, BOOKMARKS, trans.root_id(), "bar");
696 e2.PutIsUnsynced(true);
697 new_dirty_metahandles.push_back(e2.GetMetahandle());
699 expected_dirty_metahandles.insert(expected_dirty_metahandles.end(),
700 new_dirty_metahandles.begin(), new_dirty_metahandles.end());
703 // Don't make any changes whatsoever and ensure nothing comes back.
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.
714 // Fake SaveChanges() and make sure we got what we expected.
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);
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;
734 bool not_dir = !e.GetIsDir();
736 e.PutIsUnsynced(true);
740 // Fake SaveChanges() and make sure we got what we expected.
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());
752 dir_->VacuumAfterSaveChanges(snapshot);
756 // Test delete journals management.
757 TEST_F(SyncableDirectoryTest, ManageDeleteJournals) {
758 sync_pb::EntitySpecifics bookmark_specifics;
759 AddDefaultFieldValue(BOOKMARKS, &bookmark_specifics);
760 bookmark_specifics.mutable_bookmark()->set_url("url");
762 Id id1 = TestIdFactory::FromNumber(-1);
763 Id id2 = TestIdFactory::FromNumber(-2);
767 // Create two bookmark entries and save in database.
768 CreateEntry("item1", id1);
769 CreateEntry("item2", id2);
771 WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get());
772 MutableEntry item1(&trans, GET_BY_ID, id1);
773 ASSERT_TRUE(item1.good());
774 handle1 = item1.GetMetahandle();
775 item1.PutSpecifics(bookmark_specifics);
776 item1.PutServerSpecifics(bookmark_specifics);
777 MutableEntry item2(&trans, GET_BY_ID, id2);
778 ASSERT_TRUE(item2.good());
779 handle2 = item2.GetMetahandle();
780 item2.PutSpecifics(bookmark_specifics);
781 item2.PutServerSpecifics(bookmark_specifics);
783 ASSERT_EQ(OPENED, SimulateSaveAndReloadDir());
786 { // Test adding and saving delete journals.
787 DeleteJournal* delete_journal = dir_->delete_journal();
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());
794 // Set SERVER_IS_DEL of the entries to true and they should be added to
796 MutableEntry item1(&trans, GET_BY_ID, id1);
797 ASSERT_TRUE(item1.good());
798 item1.PutServerIsDel(true);
799 MutableEntry item2(&trans, GET_BY_ID, id2);
800 ASSERT_TRUE(item2.good());
801 item2.PutServerIsDel(true);
804 EXPECT_TRUE(delete_journal->delete_journals_.count(&tmp));
806 EXPECT_TRUE(delete_journal->delete_journals_.count(&tmp));
809 // Save delete journals in database and verify memory clearing.
810 ASSERT_TRUE(dir_->SaveChanges());
812 ReadTransaction trans(FROM_HERE, dir_.get());
813 EXPECT_EQ(0u, delete_journal->GetDeleteJournalSize(&trans));
815 ASSERT_EQ(OPENED, SimulateSaveAndReloadDir());
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());
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));
833 MetahandleSet to_purge;
834 to_purge.insert(handle2);
835 delete_journal->PurgeDeleteJournals(&trans, to_purge);
837 // Verify that item2 is purged from journals in memory and will be
838 // purged from database.
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));
844 ASSERT_EQ(OPENED, SimulateSaveAndReloadDir());
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());
857 tmp.put(META_HANDLE, handle1);
858 EXPECT_TRUE(journal_entries.count(&tmp));
861 MutableEntry item1(&trans, GET_BY_ID, id1);
862 ASSERT_TRUE(item1.good());
863 item1.PutServerIsDel(false);
864 EXPECT_TRUE(delete_journal->delete_journals_.empty());
865 EXPECT_EQ(1u, delete_journal->delete_journals_to_purge_.size());
866 EXPECT_TRUE(delete_journal->delete_journals_to_purge_.count(handle1));
868 ASSERT_EQ(OPENED, SimulateSaveAndReloadDir());
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));
879 const char SyncableDirectoryTest::kName[] = "Foo";
883 TEST_F(SyncableDirectoryTest, TestBasicLookupNonExistantID) {
884 ReadTransaction rtrans(FROM_HERE, dir_.get());
885 Entry e(&rtrans, GET_BY_ID, TestIdFactory::FromNumber(-99));
886 ASSERT_FALSE(e.good());
889 TEST_F(SyncableDirectoryTest, TestBasicLookupValidID) {
891 ReadTransaction rtrans(FROM_HERE, dir_.get());
892 Entry e(&rtrans, GET_BY_ID, TestIdFactory::FromNumber(-99));
893 ASSERT_TRUE(e.good());
896 TEST_F(SyncableDirectoryTest, TestDelete) {
897 std::string name = "peanut butter jelly time";
898 WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get());
899 MutableEntry e1(&trans, CREATE, BOOKMARKS, trans.root_id(), name);
900 ASSERT_TRUE(e1.good());
902 MutableEntry e2(&trans, CREATE, BOOKMARKS, trans.root_id(), name);
903 ASSERT_TRUE(e2.good());
905 MutableEntry e3(&trans, CREATE, BOOKMARKS, trans.root_id(), name);
906 ASSERT_TRUE(e3.good());
918 TEST_F(SyncableDirectoryTest, TestGetUnsynced) {
919 Directory::Metahandles handles;
920 int64 handle1, handle2;
922 WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get());
924 dir_->GetUnsyncedMetaHandles(&trans, &handles);
925 ASSERT_TRUE(0 == handles.size());
927 MutableEntry e1(&trans, CREATE, BOOKMARKS, trans.root_id(), "abba");
928 ASSERT_TRUE(e1.good());
929 handle1 = e1.GetMetahandle();
930 e1.PutBaseVersion(1);
932 e1.PutId(TestIdFactory::FromNumber(101));
934 MutableEntry e2(&trans, CREATE, BOOKMARKS, e1.GetId(), "bread");
935 ASSERT_TRUE(e2.good());
936 handle2 = e2.GetMetahandle();
937 e2.PutBaseVersion(1);
938 e2.PutId(TestIdFactory::FromNumber(102));
942 WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get());
944 dir_->GetUnsyncedMetaHandles(&trans, &handles);
945 ASSERT_TRUE(0 == handles.size());
947 MutableEntry e3(&trans, GET_BY_HANDLE, handle1);
948 ASSERT_TRUE(e3.good());
949 e3.PutIsUnsynced(true);
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]);
958 MutableEntry e4(&trans, GET_BY_HANDLE, handle2);
959 ASSERT_TRUE(e4.good());
960 e4.PutIsUnsynced(true);
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]);
970 ASSERT_TRUE(handle2 == handles[0]);
971 ASSERT_TRUE(handle1 == handles[1]);
974 MutableEntry e5(&trans, GET_BY_HANDLE, handle1);
975 ASSERT_TRUE(e5.good());
976 ASSERT_TRUE(e5.GetIsUnsynced());
977 ASSERT_TRUE(e5.PutIsUnsynced(false));
978 ASSERT_FALSE(e5.GetIsUnsynced());
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]);
989 TEST_F(SyncableDirectoryTest, TestGetUnappliedUpdates) {
990 std::vector<int64> handles;
991 int64 handle1, handle2;
992 const FullModelTypeSet all_types = FullModelTypeSet::All();
994 WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get());
996 dir_->GetUnappliedUpdateMetaHandles(&trans, all_types, &handles);
997 ASSERT_TRUE(0 == handles.size());
999 MutableEntry e1(&trans, CREATE, BOOKMARKS, trans.root_id(), "abba");
1000 ASSERT_TRUE(e1.good());
1001 handle1 = e1.GetMetahandle();
1002 e1.PutIsUnappliedUpdate(false);
1003 e1.PutBaseVersion(1);
1004 e1.PutId(TestIdFactory::FromNumber(101));
1007 MutableEntry e2(&trans, CREATE, BOOKMARKS, e1.GetId(), "bread");
1008 ASSERT_TRUE(e2.good());
1009 handle2 = e2.GetMetahandle();
1010 e2.PutIsUnappliedUpdate(false);
1011 e2.PutBaseVersion(1);
1012 e2.PutId(TestIdFactory::FromNumber(102));
1014 dir_->SaveChanges();
1016 WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get());
1018 dir_->GetUnappliedUpdateMetaHandles(&trans, all_types, &handles);
1019 ASSERT_TRUE(0 == handles.size());
1021 MutableEntry e3(&trans, GET_BY_HANDLE, handle1);
1022 ASSERT_TRUE(e3.good());
1023 e3.PutIsUnappliedUpdate(true);
1025 dir_->SaveChanges();
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]);
1032 MutableEntry e4(&trans, GET_BY_HANDLE, handle2);
1033 ASSERT_TRUE(e4.good());
1034 e4.PutIsUnappliedUpdate(true);
1036 dir_->SaveChanges();
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]);
1044 ASSERT_TRUE(handle2 == handles[0]);
1045 ASSERT_TRUE(handle1 == handles[1]);
1048 MutableEntry e5(&trans, GET_BY_HANDLE, handle1);
1049 ASSERT_TRUE(e5.good());
1050 e5.PutIsUnappliedUpdate(false);
1052 dir_->SaveChanges();
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]);
1062 TEST_F(SyncableDirectoryTest, DeleteBug_531383) {
1063 // Try to evoke a check failure...
1064 TestIdFactory id_factory;
1065 int64 grandchild_handle;
1067 WriteTransaction wtrans(FROM_HERE, UNITTEST, dir_.get());
1068 MutableEntry parent(&wtrans, CREATE, BOOKMARKS, id_factory.root(), "Bob");
1069 ASSERT_TRUE(parent.good());
1070 parent.PutIsDir(true);
1071 parent.PutId(id_factory.NewServerId());
1072 parent.PutBaseVersion(1);
1073 MutableEntry child(&wtrans, CREATE, BOOKMARKS, parent.GetId(), "Bob");
1074 ASSERT_TRUE(child.good());
1075 child.PutIsDir(true);
1076 child.PutId(id_factory.NewServerId());
1077 child.PutBaseVersion(1);
1078 MutableEntry grandchild(&wtrans, CREATE, BOOKMARKS, child.GetId(), "Bob");
1079 ASSERT_TRUE(grandchild.good());
1080 grandchild.PutId(id_factory.NewServerId());
1081 grandchild.PutBaseVersion(1);
1082 grandchild.PutIsDel(true);
1083 MutableEntry twin(&wtrans, CREATE, BOOKMARKS, child.GetId(), "Bob");
1084 ASSERT_TRUE(twin.good());
1085 twin.PutIsDel(true);
1086 grandchild.PutIsDel(false);
1088 grandchild_handle = grandchild.GetMetahandle();
1090 dir_->SaveChanges();
1092 WriteTransaction wtrans(FROM_HERE, UNITTEST, dir_.get());
1093 MutableEntry grandchild(&wtrans, GET_BY_HANDLE, grandchild_handle);
1094 grandchild.PutIsDel(true); // Used to CHECK fail here.
1098 static inline bool IsLegalNewParent(const Entry& a, const Entry& b) {
1099 return IsLegalNewParent(a.trans(), a.GetId(), b.GetId());
1102 TEST_F(SyncableDirectoryTest, TestIsLegalNewParent) {
1103 TestIdFactory id_factory;
1104 WriteTransaction wtrans(FROM_HERE, UNITTEST, dir_.get());
1105 Entry root(&wtrans, GET_BY_ID, id_factory.root());
1106 ASSERT_TRUE(root.good());
1107 MutableEntry parent(&wtrans, CREATE, BOOKMARKS, root.GetId(), "Bob");
1108 ASSERT_TRUE(parent.good());
1109 parent.PutIsDir(true);
1110 parent.PutId(id_factory.NewServerId());
1111 parent.PutBaseVersion(1);
1112 MutableEntry child(&wtrans, CREATE, BOOKMARKS, parent.GetId(), "Bob");
1113 ASSERT_TRUE(child.good());
1114 child.PutIsDir(true);
1115 child.PutId(id_factory.NewServerId());
1116 child.PutBaseVersion(1);
1117 MutableEntry grandchild(&wtrans, CREATE, BOOKMARKS, child.GetId(), "Bob");
1118 ASSERT_TRUE(grandchild.good());
1119 grandchild.PutId(id_factory.NewServerId());
1120 grandchild.PutBaseVersion(1);
1122 MutableEntry parent2(&wtrans, CREATE, BOOKMARKS, root.GetId(), "Pete");
1123 ASSERT_TRUE(parent2.good());
1124 parent2.PutIsDir(true);
1125 parent2.PutId(id_factory.NewServerId());
1126 parent2.PutBaseVersion(1);
1127 MutableEntry child2(&wtrans, CREATE, BOOKMARKS, parent2.GetId(), "Pete");
1128 ASSERT_TRUE(child2.good());
1129 child2.PutIsDir(true);
1130 child2.PutId(id_factory.NewServerId());
1131 child2.PutBaseVersion(1);
1132 MutableEntry grandchild2(&wtrans, CREATE, BOOKMARKS, child2.GetId(), "Pete");
1133 ASSERT_TRUE(grandchild2.good());
1134 grandchild2.PutId(id_factory.NewServerId());
1135 grandchild2.PutBaseVersion(1);
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));
1155 TEST_F(SyncableDirectoryTest, TestEntryIsInFolder) {
1156 // Create a subdir and an entry.
1158 syncable::Id folder_id;
1159 syncable::Id entry_id;
1160 std::string entry_name = "entry";
1163 WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get());
1164 MutableEntry folder(&trans, CREATE, BOOKMARKS, trans.root_id(), "folder");
1165 ASSERT_TRUE(folder.good());
1166 folder.PutIsDir(true);
1167 EXPECT_TRUE(folder.PutIsUnsynced(true));
1168 folder_id = folder.GetId();
1170 MutableEntry entry(&trans, CREATE, BOOKMARKS, folder.GetId(), entry_name);
1171 ASSERT_TRUE(entry.good());
1172 entry_handle = entry.GetMetahandle();
1173 entry.PutIsUnsynced(true);
1174 entry_id = entry.GetId();
1177 // Make sure we can find the entry in the folder.
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));
1183 Entry entry(&trans, GET_BY_ID, entry_id);
1184 ASSERT_TRUE(entry.good());
1185 EXPECT_EQ(entry_handle, entry.GetMetahandle());
1186 EXPECT_TRUE(entry.GetNonUniqueName()== entry_name);
1187 EXPECT_TRUE(entry.GetParentId()== folder_id);
1191 TEST_F(SyncableDirectoryTest, TestParentIdIndexUpdate) {
1192 std::string child_name = "child";
1194 WriteTransaction wt(FROM_HERE, UNITTEST, dir_.get());
1195 MutableEntry parent_folder(&wt, CREATE, BOOKMARKS, wt.root_id(), "folder1");
1196 parent_folder.PutIsUnsynced(true);
1197 parent_folder.PutIsDir(true);
1199 MutableEntry parent_folder2(&wt, CREATE, BOOKMARKS, wt.root_id(), "folder2");
1200 parent_folder2.PutIsUnsynced(true);
1201 parent_folder2.PutIsDir(true);
1203 MutableEntry child(&wt, CREATE, BOOKMARKS, parent_folder.GetId(), child_name);
1204 child.PutIsDir(true);
1205 child.PutIsUnsynced(true);
1207 ASSERT_TRUE(child.good());
1209 EXPECT_EQ(0, CountEntriesWithName(&wt, wt.root_id(), child_name));
1210 EXPECT_EQ(parent_folder.GetId(), child.GetParentId());
1211 EXPECT_EQ(1, CountEntriesWithName(&wt, parent_folder.GetId(), child_name));
1212 EXPECT_EQ(0, CountEntriesWithName(&wt, parent_folder2.GetId(), child_name));
1213 child.PutParentId(parent_folder2.GetId());
1214 EXPECT_EQ(parent_folder2.GetId(), child.GetParentId());
1215 EXPECT_EQ(0, CountEntriesWithName(&wt, parent_folder.GetId(), child_name));
1216 EXPECT_EQ(1, CountEntriesWithName(&wt, parent_folder2.GetId(), child_name));
1219 TEST_F(SyncableDirectoryTest, TestNoReindexDeletedItems) {
1220 std::string folder_name = "folder";
1221 std::string new_name = "new_name";
1223 WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get());
1224 MutableEntry folder(&trans, CREATE, BOOKMARKS, trans.root_id(), folder_name);
1225 ASSERT_TRUE(folder.good());
1226 folder.PutIsDir(true);
1227 folder.PutIsDel(true);
1229 EXPECT_EQ(0, CountEntriesWithName(&trans, trans.root_id(), folder_name));
1231 MutableEntry deleted(&trans, GET_BY_ID, folder.GetId());
1232 ASSERT_TRUE(deleted.good());
1233 deleted.PutParentId(trans.root_id());
1234 deleted.PutNonUniqueName(new_name);
1236 EXPECT_EQ(0, CountEntriesWithName(&trans, trans.root_id(), folder_name));
1237 EXPECT_EQ(0, CountEntriesWithName(&trans, trans.root_id(), new_name));
1240 TEST_F(SyncableDirectoryTest, TestCaseChangeRename) {
1241 WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get());
1242 MutableEntry folder(&trans, CREATE, BOOKMARKS, trans.root_id(), "CaseChange");
1243 ASSERT_TRUE(folder.good());
1244 folder.PutParentId(trans.root_id());
1245 folder.PutNonUniqueName("CASECHANGE");
1246 folder.PutIsDel(true);
1249 // Create items of each model type, and check that GetModelType and
1250 // GetServerModelType return the right value.
1251 TEST_F(SyncableDirectoryTest, GetModelType) {
1252 TestIdFactory id_factory;
1253 ModelTypeSet protocol_types = ProtocolTypes();
1254 for (ModelTypeSet::Iterator iter = protocol_types.First(); iter.Good();
1256 ModelType datatype = iter.Get();
1257 SCOPED_TRACE(testing::Message("Testing model type ") << datatype);
1260 case TOP_LEVEL_FOLDER:
1261 continue; // Datatype isn't a function of Specifics.
1265 sync_pb::EntitySpecifics specifics;
1266 AddDefaultFieldValue(datatype, &specifics);
1268 WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get());
1270 MutableEntry folder(&trans, CREATE, BOOKMARKS, trans.root_id(), "Folder");
1271 ASSERT_TRUE(folder.good());
1272 folder.PutId(id_factory.NewServerId());
1273 folder.PutSpecifics(specifics);
1274 folder.PutBaseVersion(1);
1275 folder.PutIsDir(true);
1276 folder.PutIsDel(false);
1277 ASSERT_EQ(datatype, folder.GetModelType());
1279 MutableEntry item(&trans, CREATE, BOOKMARKS, trans.root_id(), "Item");
1280 ASSERT_TRUE(item.good());
1281 item.PutId(id_factory.NewServerId());
1282 item.PutSpecifics(specifics);
1283 item.PutBaseVersion(1);
1284 item.PutIsDir(false);
1285 item.PutIsDel(false);
1286 ASSERT_EQ(datatype, item.GetModelType());
1288 // It's critical that deletion records retain their datatype, so that
1289 // they can be dispatched to the appropriate change processor.
1290 MutableEntry deleted_item(
1291 &trans, CREATE, BOOKMARKS, trans.root_id(), "Deleted Item");
1292 ASSERT_TRUE(item.good());
1293 deleted_item.PutId(id_factory.NewServerId());
1294 deleted_item.PutSpecifics(specifics);
1295 deleted_item.PutBaseVersion(1);
1296 deleted_item.PutIsDir(false);
1297 deleted_item.PutIsDel(true);
1298 ASSERT_EQ(datatype, deleted_item.GetModelType());
1300 MutableEntry server_folder(&trans, CREATE_NEW_UPDATE_ITEM,
1301 id_factory.NewServerId());
1302 ASSERT_TRUE(server_folder.good());
1303 server_folder.PutServerSpecifics(specifics);
1304 server_folder.PutBaseVersion(1);
1305 server_folder.PutServerIsDir(true);
1306 server_folder.PutServerIsDel(false);
1307 ASSERT_EQ(datatype, server_folder.GetServerModelType());
1309 MutableEntry server_item(&trans, CREATE_NEW_UPDATE_ITEM,
1310 id_factory.NewServerId());
1311 ASSERT_TRUE(server_item.good());
1312 server_item.PutServerSpecifics(specifics);
1313 server_item.PutBaseVersion(1);
1314 server_item.PutServerIsDir(false);
1315 server_item.PutServerIsDel(false);
1316 ASSERT_EQ(datatype, server_item.GetServerModelType());
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));
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));
1334 // A test that roughly mimics the directory interaction that occurs when a
1335 // bookmark folder and entry are created then synced for the first time. It is
1336 // a more common variant of the 'DeletedAndUnsyncedChild' scenario tested below.
1337 TEST_F(SyncableDirectoryTest, ChangeEntryIDAndUpdateChildren_ParentAndChild) {
1338 TestIdFactory id_factory;
1343 // Create two client-side items, a parent and child.
1344 WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get());
1346 MutableEntry parent(&trans, CREATE, BOOKMARKS, id_factory.root(), "parent");
1347 parent.PutIsDir(true);
1348 parent.PutIsUnsynced(true);
1350 MutableEntry child(&trans, CREATE, BOOKMARKS, parent.GetId(), "child");
1351 child.PutIsUnsynced(true);
1353 orig_parent_id = parent.GetId();
1354 orig_child_id = child.GetId();
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());
1362 MutableEntry parent(&trans, GET_BY_ID, orig_parent_id);
1363 MutableEntry child(&trans, GET_BY_ID, orig_child_id);
1365 ChangeEntryIDAndUpdateChildren(&trans, &child, id_factory.NewServerId());
1366 child.PutIsUnsynced(false);
1367 child.PutBaseVersion(1);
1368 child.PutServerVersion(1);
1370 ChangeEntryIDAndUpdateChildren(&trans, &parent, id_factory.NewServerId());
1371 parent.PutIsUnsynced(false);
1372 parent.PutBaseVersion(1);
1373 parent.PutServerVersion(1);
1376 // Final check for validity.
1377 EXPECT_EQ(OPENED, SimulateSaveAndReloadDir());
1380 // A test based on the scenario where we create a bookmark folder and entry
1381 // locally, but with a twist. In this case, the bookmark is deleted before we
1382 // are able to sync either it or its parent folder. This scenario used to cause
1383 // directory corruption, see crbug.com/125381.
1384 TEST_F(SyncableDirectoryTest,
1385 ChangeEntryIDAndUpdateChildren_DeletedAndUnsyncedChild) {
1386 TestIdFactory id_factory;
1391 // Create two client-side items, a parent and child.
1392 WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get());
1394 MutableEntry parent(&trans, CREATE, BOOKMARKS, id_factory.root(), "parent");
1395 parent.PutIsDir(true);
1396 parent.PutIsUnsynced(true);
1398 MutableEntry child(&trans, CREATE, BOOKMARKS, parent.GetId(), "child");
1399 child.PutIsUnsynced(true);
1401 orig_parent_id = parent.GetId();
1402 orig_child_id = child.GetId();
1406 // Delete the child.
1407 WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get());
1409 MutableEntry child(&trans, GET_BY_ID, orig_child_id);
1410 child.PutIsDel(true);
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());
1418 MutableEntry parent(&trans, GET_BY_ID, orig_parent_id);
1420 ChangeEntryIDAndUpdateChildren(&trans, &parent, id_factory.NewServerId());
1421 parent.PutIsUnsynced(false);
1422 parent.PutBaseVersion(1);
1423 parent.PutServerVersion(1);
1426 // Final check for validity.
1427 EXPECT_EQ(OPENED, SimulateSaveAndReloadDir());
1430 // Ask the directory to generate a unique ID. Close and re-open the database
1431 // without saving, then ask for another unique ID. Verify IDs are not reused.
1432 // This scenario simulates a crash within the first few seconds of operation.
1433 TEST_F(SyncableDirectoryTest, LocalIdReuseTest) {
1434 Id pre_crash_id = dir_->NextId();
1435 SimulateCrashAndReloadDir();
1436 Id post_crash_id = dir_->NextId();
1437 EXPECT_NE(pre_crash_id, post_crash_id);
1440 // Ask the directory to generate a unique ID. Save the directory. Close and
1441 // re-open the database without saving, then ask for another unique ID. Verify
1442 // IDs are not reused. This scenario simulates a steady-state crash.
1443 TEST_F(SyncableDirectoryTest, LocalIdReuseTestWithSave) {
1444 Id pre_crash_id = dir_->NextId();
1445 dir_->SaveChanges();
1446 SimulateCrashAndReloadDir();
1447 Id post_crash_id = dir_->NextId();
1448 EXPECT_NE(pre_crash_id, post_crash_id);
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
1454 TEST_F(SyncableDirectoryTest, OldClientLeftUnsyncedDeletedLocalItem) {
1455 // We must create an entry with the offending properties. This is done with
1456 // some abuse of the MutableEntry's API; it doesn't expect us to modify an
1457 // item after it is deleted. If this hack becomes impractical we will need to
1458 // find a new way to simulate this scenario.
1460 TestIdFactory id_factory;
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();
1466 // The ID of the entry which will be unsynced, is_del and !ServerKnows().
1467 Id zombie_id = id_factory.NewLocalId();
1469 // We're about to do some bad things. Tell the directory verification
1470 // routines to look the other way.
1471 dir_->SetInvariantCheckLevel(OFF);
1474 WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get());
1476 // Create an uncommitted tombstone entry.
1477 MutableEntry server_knows(&trans, CREATE, BOOKMARKS, id_factory.root(),
1479 server_knows.PutId(server_knows_id);
1480 server_knows.PutIsUnsynced(true);
1481 server_knows.PutIsDel(true);
1482 server_knows.PutBaseVersion(5);
1483 server_knows.PutServerVersion(4);
1485 // Create a valid update entry.
1486 MutableEntry not_is_del(
1487 &trans, CREATE, BOOKMARKS, id_factory.root(), "not_is_del");
1488 not_is_del.PutId(not_is_del_id);
1489 not_is_del.PutIsDel(false);
1490 not_is_del.PutIsUnsynced(true);
1492 // Create a tombstone which should never be sent to the server because the
1493 // server never knew about the item's existence.
1495 // New clients should never put entries into this state. We work around
1496 // this by setting IS_DEL before setting IS_UNSYNCED, something which the
1497 // client should never do in practice.
1498 MutableEntry zombie(&trans, CREATE, BOOKMARKS, id_factory.root(), "zombie");
1499 zombie.PutId(zombie_id);
1500 zombie.PutIsDel(true);
1501 zombie.PutIsUnsynced(true);
1504 ASSERT_EQ(OPENED, SimulateSaveAndReloadDir());
1507 ReadTransaction trans(FROM_HERE, dir_.get());
1509 // The directory loading routines should have cleaned things up, making it
1510 // safe to check invariants once again.
1511 dir_->FullyCheckTreeInvariants(&trans);
1513 Entry server_knows(&trans, GET_BY_ID, server_knows_id);
1514 EXPECT_TRUE(server_knows.good());
1516 Entry not_is_del(&trans, GET_BY_ID, not_is_del_id);
1517 EXPECT_TRUE(not_is_del.good());
1519 Entry zombie(&trans, GET_BY_ID, zombie_id);
1520 EXPECT_FALSE(zombie.good());
1524 TEST_F(SyncableDirectoryTest, PositionWithNullSurvivesSaveAndReload) {
1525 TestIdFactory id_factory;
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
1533 std::string suffix =
1534 std::string(UniquePosition::kSuffixLength - null_str.length(), '\x7f')
1536 UniquePosition null_pos = UniquePosition::FromInt64(10, suffix);
1539 WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get());
1541 MutableEntry parent(&trans, CREATE, BOOKMARKS, id_factory.root(), "parent");
1542 parent.PutIsDir(true);
1543 parent.PutIsUnsynced(true);
1545 MutableEntry child(&trans, CREATE, BOOKMARKS, parent.GetId(), "child");
1546 child.PutIsUnsynced(true);
1547 child.PutUniquePosition(null_pos);
1548 child.PutServerUniquePosition(null_pos);
1550 null_child_id = child.GetId();
1553 EXPECT_EQ(OPENED, SimulateSaveAndReloadDir());
1556 ReadTransaction trans(FROM_HERE, dir_.get());
1558 Entry null_ordinal_child(&trans, GET_BY_ID, null_child_id);
1560 null_pos.Equals(null_ordinal_child.GetUniquePosition()));
1562 null_pos.Equals(null_ordinal_child.GetServerUniquePosition()));
1566 // An OnDirectoryBackingStore that can be set to always fail SaveChanges.
1567 class TestBackingStore : public OnDiskDirectoryBackingStore {
1569 TestBackingStore(const std::string& dir_name,
1570 const base::FilePath& backing_filepath);
1572 virtual ~TestBackingStore();
1574 virtual bool SaveChanges(const Directory::SaveChangesSnapshot& snapshot)
1577 void StartFailingSaveChanges() {
1578 fail_save_changes_ = true;
1582 bool fail_save_changes_;
1585 TestBackingStore::TestBackingStore(const std::string& dir_name,
1586 const base::FilePath& backing_filepath)
1587 : OnDiskDirectoryBackingStore(dir_name, backing_filepath),
1588 fail_save_changes_(false) {
1591 TestBackingStore::~TestBackingStore() { }
1593 bool TestBackingStore::SaveChanges(
1594 const Directory::SaveChangesSnapshot& snapshot){
1595 if (fail_save_changes_) {
1598 return OnDiskDirectoryBackingStore::SaveChanges(snapshot);
1602 // A directory whose Save() function can be set to always fail.
1603 class TestDirectory : public Directory {
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);
1612 virtual ~TestDirectory();
1614 void StartFailingSaveChanges() {
1615 backing_store_->StartFailingSaveChanges();
1619 TestDirectory(Encryptor* encryptor,
1620 UnrecoverableErrorHandler* handler,
1621 TestBackingStore* backing_store);
1623 TestBackingStore* backing_store_;
1626 TestDirectory* TestDirectory::Create(
1627 Encryptor *encryptor,
1628 UnrecoverableErrorHandler *handler,
1629 const std::string& dir_name,
1630 const base::FilePath& backing_filepath) {
1631 TestBackingStore* backing_store =
1632 new TestBackingStore(dir_name, backing_filepath);
1633 return new TestDirectory(encryptor, handler, backing_store);
1636 TestDirectory::TestDirectory(Encryptor* encryptor,
1637 UnrecoverableErrorHandler* handler,
1638 TestBackingStore* backing_store)
1639 : Directory(backing_store, handler, NULL, NULL, NULL),
1640 backing_store_(backing_store) {
1643 TestDirectory::~TestDirectory() { }
1645 TEST(OnDiskSyncableDirectory, FailInitialWrite) {
1646 FakeEncryptor encryptor;
1647 TestUnrecoverableErrorHandler handler;
1648 base::ScopedTempDir temp_dir;
1649 ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
1650 base::FilePath file_path = temp_dir.path().Append(
1651 FILE_PATH_LITERAL("Test.sqlite3"));
1652 std::string name = "user@x.com";
1653 NullDirectoryChangeDelegate delegate;
1655 scoped_ptr<TestDirectory> test_dir(
1656 TestDirectory::Create(&encryptor, &handler, name, file_path));
1658 test_dir->StartFailingSaveChanges();
1659 ASSERT_EQ(FAILED_INITIAL_WRITE, test_dir->Open(name, &delegate,
1660 NullTransactionObserver()));
1663 // A variant of SyncableDirectoryTest that uses a real sqlite database.
1664 class OnDiskSyncableDirectoryTest : public SyncableDirectoryTest {
1666 // SetUp() is called before each test case is run.
1667 // The sqlite3 DB is deleted before each test is run.
1668 virtual void SetUp() {
1669 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
1670 file_path_ = temp_dir_.path().Append(
1671 FILE_PATH_LITERAL("Test.sqlite3"));
1672 base::DeleteFile(file_path_, true);
1676 virtual void TearDown() {
1677 // This also closes file handles.
1678 dir_->SaveChanges();
1680 base::DeleteFile(file_path_, true);
1683 // Creates a new directory. Deletes the old directory, if it exists.
1684 void CreateDirectory() {
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());
1694 void SaveAndReloadDir() {
1695 dir_->SaveChanges();
1699 void StartFailingSaveChanges() {
1700 test_directory_->StartFailingSaveChanges();
1703 TestDirectory *test_directory_; // mirrors scoped_ptr<Directory> dir_
1704 base::ScopedTempDir temp_dir_;
1705 base::FilePath file_path_;
1708 TEST_F(OnDiskSyncableDirectoryTest, TestPurgeEntriesWithTypeIn) {
1709 sync_pb::EntitySpecifics bookmark_specs;
1710 sync_pb::EntitySpecifics autofill_specs;
1711 sync_pb::EntitySpecifics preference_specs;
1712 AddDefaultFieldValue(BOOKMARKS, &bookmark_specs);
1713 AddDefaultFieldValue(PREFERENCES, &preference_specs);
1714 AddDefaultFieldValue(AUTOFILL, &autofill_specs);
1716 ModelTypeSet types_to_purge(PREFERENCES, AUTOFILL);
1718 TestIdFactory id_factory;
1719 // Create some items for each type.
1721 WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get());
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);
1728 // Add more nodes for this type. Technically, they should be placed under
1729 // the proper type root nodes but the assertions in this test won't notice
1730 // if their parent isn't quite right.
1731 MutableEntry item1(&trans, CREATE, BOOKMARKS, trans.root_id(), "Item");
1732 ASSERT_TRUE(item1.good());
1733 item1.PutServerSpecifics(bookmark_specs);
1734 item1.PutIsUnsynced(true);
1736 MutableEntry item2(&trans, CREATE_NEW_UPDATE_ITEM,
1737 id_factory.NewServerId());
1738 ASSERT_TRUE(item2.good());
1739 item2.PutServerSpecifics(bookmark_specs);
1740 item2.PutIsUnappliedUpdate(true);
1742 MutableEntry item3(&trans, CREATE, PREFERENCES,
1743 trans.root_id(), "Item");
1744 ASSERT_TRUE(item3.good());
1745 item3.PutSpecifics(preference_specs);
1746 item3.PutServerSpecifics(preference_specs);
1747 item3.PutIsUnsynced(true);
1749 MutableEntry item4(&trans, CREATE_NEW_UPDATE_ITEM,
1750 id_factory.NewServerId());
1751 ASSERT_TRUE(item4.good());
1752 item4.PutServerSpecifics(preference_specs);
1753 item4.PutIsUnappliedUpdate(true);
1755 MutableEntry item5(&trans, CREATE, AUTOFILL,
1756 trans.root_id(), "Item");
1757 ASSERT_TRUE(item5.good());
1758 item5.PutSpecifics(autofill_specs);
1759 item5.PutServerSpecifics(autofill_specs);
1760 item5.PutIsUnsynced(true);
1762 MutableEntry item6(&trans, CREATE_NEW_UPDATE_ITEM,
1763 id_factory.NewServerId());
1764 ASSERT_TRUE(item6.good());
1765 item6.PutServerSpecifics(autofill_specs);
1766 item6.PutIsUnappliedUpdate(true);
1769 dir_->SaveChanges();
1771 ReadTransaction trans(FROM_HERE, dir_.get());
1772 MetahandleSet all_set;
1773 GetAllMetaHandles(&trans, &all_set);
1774 ASSERT_EQ(10U, all_set.size());
1777 dir_->PurgeEntriesWithTypeIn(types_to_purge, ModelTypeSet(), ModelTypeSet());
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);
1783 CheckPurgeEntriesWithTypeInSucceeded(types_to_purge, false);
1786 TEST_F(OnDiskSyncableDirectoryTest, TestShareInfo) {
1787 dir_->set_store_birthday("Jan 31st");
1788 const char* const bag_of_chips_array = "\0bag of chips";
1789 const std::string bag_of_chips_string =
1790 std::string(bag_of_chips_array, sizeof(bag_of_chips_array));
1791 dir_->set_bag_of_chips(bag_of_chips_string);
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());
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();
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());
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.
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());
1821 TEST_F(OnDiskSyncableDirectoryTest,
1822 TestSimpleFieldsPreservedDuringSaveChanges) {
1823 Id update_id = TestIdFactory::FromNumber(1);
1825 EntryKernel create_pre_save, update_pre_save;
1826 EntryKernel create_post_save, update_post_save;
1827 std::string create_name = "Create";
1830 WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get());
1831 MutableEntry create(
1832 &trans, CREATE, BOOKMARKS, trans.root_id(), create_name);
1833 MutableEntry update(&trans, CREATE_NEW_UPDATE_ITEM, update_id);
1834 create.PutIsUnsynced(true);
1835 update.PutIsUnappliedUpdate(true);
1836 sync_pb::EntitySpecifics specifics;
1837 specifics.mutable_bookmark()->set_favicon("PNG");
1838 specifics.mutable_bookmark()->set_url("http://nowhere");
1839 create.PutSpecifics(specifics);
1840 update.PutSpecifics(specifics);
1841 create_pre_save = create.GetKernelCopy();
1842 update_pre_save = update.GetKernelCopy();
1843 create_id = create.GetId();
1846 dir_->SaveChanges();
1847 dir_.reset(new Directory(new OnDiskDirectoryBackingStore(kName, file_path_),
1853 ASSERT_TRUE(dir_.get());
1854 ASSERT_EQ(OPENED, dir_->Open(kName, &delegate_, NullTransactionObserver()));
1855 ASSERT_TRUE(dir_->good());
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();
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";
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";
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";
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";
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";
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";
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";
1926 TEST_F(OnDiskSyncableDirectoryTest, TestSaveChangesFailure) {
1928 // Set up an item using a regular, saveable directory.
1930 WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get());
1932 MutableEntry e1(&trans, CREATE, BOOKMARKS, trans.root_id(), "aguilera");
1933 ASSERT_TRUE(e1.good());
1934 EXPECT_TRUE(e1.GetKernelCopy().is_dirty());
1935 handle1 = e1.GetMetahandle();
1936 e1.PutBaseVersion(1);
1938 e1.PutId(TestIdFactory::FromNumber(101));
1939 EXPECT_TRUE(e1.GetKernelCopy().is_dirty());
1940 EXPECT_TRUE(IsInDirtyMetahandles(handle1));
1942 ASSERT_TRUE(dir_->SaveChanges());
1944 // Make sure the item is no longer dirty after saving,
1945 // and make a modification.
1947 WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get());
1949 MutableEntry aguilera(&trans, GET_BY_HANDLE, handle1);
1950 ASSERT_TRUE(aguilera.good());
1951 EXPECT_FALSE(aguilera.GetKernelCopy().is_dirty());
1952 EXPECT_EQ(aguilera.GetNonUniqueName(), "aguilera");
1953 aguilera.PutNonUniqueName("overwritten");
1954 EXPECT_TRUE(aguilera.GetKernelCopy().is_dirty());
1955 EXPECT_TRUE(IsInDirtyMetahandles(handle1));
1957 ASSERT_TRUE(dir_->SaveChanges());
1959 // Now do some operations when SaveChanges() will fail.
1960 StartFailingSaveChanges();
1961 ASSERT_TRUE(dir_->good());
1965 WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get());
1967 MutableEntry aguilera(&trans, GET_BY_HANDLE, handle1);
1968 ASSERT_TRUE(aguilera.good());
1969 EXPECT_FALSE(aguilera.GetKernelCopy().is_dirty());
1970 EXPECT_EQ(aguilera.GetNonUniqueName(), "overwritten");
1971 EXPECT_FALSE(aguilera.GetKernelCopy().is_dirty());
1972 EXPECT_FALSE(IsInDirtyMetahandles(handle1));
1973 aguilera.PutNonUniqueName("christina");
1974 EXPECT_TRUE(aguilera.GetKernelCopy().is_dirty());
1975 EXPECT_TRUE(IsInDirtyMetahandles(handle1));
1978 MutableEntry kids_on_block(
1979 &trans, CREATE, BOOKMARKS, trans.root_id(), "kids");
1980 ASSERT_TRUE(kids_on_block.good());
1981 handle2 = kids_on_block.GetMetahandle();
1982 kids_on_block.PutBaseVersion(1);
1983 kids_on_block.PutIsDir(true);
1984 kids_on_block.PutId(TestIdFactory::FromNumber(102));
1985 EXPECT_TRUE(kids_on_block.GetKernelCopy().is_dirty());
1986 EXPECT_TRUE(IsInDirtyMetahandles(handle2));
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());
1993 // Make sure things were rolled back and the world is as it was before call.
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));
2008 TEST_F(OnDiskSyncableDirectoryTest, TestSaveChangesFailureWithPurge) {
2010 // Set up an item using a regular, saveable directory.
2012 WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get());
2014 MutableEntry e1(&trans, CREATE, BOOKMARKS, trans.root_id(), "aguilera");
2015 ASSERT_TRUE(e1.good());
2016 EXPECT_TRUE(e1.GetKernelCopy().is_dirty());
2017 handle1 = e1.GetMetahandle();
2018 e1.PutBaseVersion(1);
2020 e1.PutId(TestIdFactory::FromNumber(101));
2021 sync_pb::EntitySpecifics bookmark_specs;
2022 AddDefaultFieldValue(BOOKMARKS, &bookmark_specs);
2023 e1.PutSpecifics(bookmark_specs);
2024 e1.PutServerSpecifics(bookmark_specs);
2025 e1.PutId(TestIdFactory::FromNumber(101));
2026 EXPECT_TRUE(e1.GetKernelCopy().is_dirty());
2027 EXPECT_TRUE(IsInDirtyMetahandles(handle1));
2029 ASSERT_TRUE(dir_->SaveChanges());
2031 // Now do some operations while SaveChanges() is set to fail.
2032 StartFailingSaveChanges();
2033 ASSERT_TRUE(dir_->good());
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));
2044 void SyncableDirectoryTest::ValidateEntry(BaseTransaction* trans,
2047 const std::string& name,
2049 int64 server_version,
2051 Entry e(trans, GET_BY_ID, TestIdFactory::FromNumber(id));
2052 ASSERT_TRUE(e.good());
2054 ASSERT_TRUE(name == e.GetNonUniqueName());
2055 ASSERT_TRUE(base_version == e.GetBaseVersion());
2056 ASSERT_TRUE(server_version == e.GetServerVersion());
2057 ASSERT_TRUE(is_del == e.GetIsDel());
2060 DirOpenResult SyncableDirectoryTest::SimulateSaveAndReloadDir() {
2061 if (!dir_->SaveChanges())
2062 return FAILED_IN_UNITTEST;
2064 return ReloadDirImpl();
2067 DirOpenResult SyncableDirectoryTest::SimulateCrashAndReloadDir() {
2068 return ReloadDirImpl();
2071 DirOpenResult SyncableDirectoryTest::ReloadDirImpl() {
2072 // Do some tricky things to preserve the backing store.
2073 DirectoryBackingStore* saved_store = dir_->store_.release();
2075 // Close the current directory.
2079 dir_.reset(new Directory(saved_store,
2084 DirOpenResult result = dir_->OpenImpl(kName, &delegate_,
2085 NullTransactionObserver());
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)
2097 class SyncableDirectoryManagement : public testing::Test {
2099 virtual void SetUp() {
2100 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
2103 virtual void TearDown() {
2106 base::MessageLoop message_loop_;
2107 base::ScopedTempDir temp_dir_;
2108 FakeEncryptor encryptor_;
2109 TestUnrecoverableErrorHandler handler_;
2110 NullDirectoryChangeDelegate delegate_;
2113 TEST_F(SyncableDirectoryManagement, TestFileRelease) {
2114 base::FilePath path = temp_dir_.path().Append(
2115 Directory::kSyncDatabaseFilename);
2117 syncable::Directory dir(new OnDiskDirectoryBackingStore("ScopeTest", path),
2122 DirOpenResult result =
2123 dir.Open("ScopeTest", &delegate_, NullTransactionObserver());
2124 ASSERT_EQ(result, OPENED);
2127 // Closing the directory should have released the backing database file.
2128 ASSERT_TRUE(base::DeleteFile(path, true));
2131 class StressTransactionsDelegate : public base::PlatformThread::Delegate {
2133 StressTransactionsDelegate(Directory* dir, int thread_number)
2135 thread_number_(thread_number) {}
2138 Directory* const dir_;
2139 const int thread_number_;
2141 // PlatformThread::Delegate methods:
2142 virtual void ThreadMain() OVERRIDE {
2143 int entry_count = 0;
2144 std::string path_name;
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(
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);
2160 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(
2162 e.PutIsUnsynced(true);
2163 if (e.PutId(TestIdFactory::FromNumber(rand())) &&
2164 e.GetId().ServerKnows() && !e.GetId().IsRoot()) {
2165 e.PutBaseVersion(1);
2171 DISALLOW_COPY_AND_ASSIGN(StressTransactionsDelegate);
2174 TEST(SyncableDirectory, StressTransactions) {
2175 base::MessageLoop message_loop;
2176 base::ScopedTempDir temp_dir;
2177 ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
2178 FakeEncryptor encryptor;
2179 TestUnrecoverableErrorHandler handler;
2180 NullDirectoryChangeDelegate delegate;
2181 std::string dirname = "stress";
2182 Directory dir(new InMemoryDirectoryBackingStore(dirname),
2187 dir.Open(dirname, &delegate, NullTransactionObserver());
2189 const int kThreadCount = 7;
2190 base::PlatformThreadHandle threads[kThreadCount];
2191 scoped_ptr<StressTransactionsDelegate> thread_delegates[kThreadCount];
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]));
2199 for (int i = 0; i < kThreadCount; ++i) {
2200 base::PlatformThread::Join(threads[i]);
2206 class SyncableClientTagTest : public SyncableDirectoryTest {
2208 static const int kBaseVersion = 1;
2209 const char* test_name_;
2210 const char* test_tag_;
2212 SyncableClientTagTest() : test_name_("test_name"), test_tag_("dietcoke") {}
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_);
2220 if (id.ServerKnows()) {
2221 me.PutBaseVersion(kBaseVersion);
2223 me.PutIsUnsynced(true);
2224 me.PutIsDel(deleted);
2226 return me.PutUniqueClientTag(test_tag_);
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_);
2235 EXPECT_EQ(me.GetId(), id);
2236 EXPECT_EQ(me.GetUniqueClientTag(), test_tag_);
2237 EXPECT_EQ(me.GetIsDel(), deleted);
2239 // We only sync deleted items that the server knew about.
2240 if (me.GetId().ServerKnows() || !me.GetIsDel()) {
2241 EXPECT_EQ(me.GetIsUnsynced(), true);
2246 TestIdFactory factory_;
2249 TEST_F(SyncableClientTagTest, TestClientTagClear) {
2250 Id server_id = factory_.NewServerId();
2251 EXPECT_TRUE(CreateWithDefaultTag(server_id, false));
2253 WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get());
2254 MutableEntry me(&trans, GET_BY_CLIENT_TAG, test_tag_);
2255 EXPECT_TRUE(me.good());
2256 me.PutUniqueClientTag(std::string());
2259 ReadTransaction trans(FROM_HERE, dir_.get());
2260 Entry by_tag(&trans, GET_BY_CLIENT_TAG, test_tag_);
2261 EXPECT_FALSE(by_tag.good());
2263 Entry by_id(&trans, GET_BY_ID, server_id);
2264 EXPECT_TRUE(by_id.good());
2265 EXPECT_TRUE(by_id.GetUniqueClientTag().empty());
2269 TEST_F(SyncableClientTagTest, TestClientTagIndexServerId) {
2270 Id server_id = factory_.NewServerId();
2271 EXPECT_TRUE(CreateWithDefaultTag(server_id, false));
2272 VerifyTag(server_id, false);
2275 TEST_F(SyncableClientTagTest, TestClientTagIndexClientId) {
2276 Id client_id = factory_.NewLocalId();
2277 EXPECT_TRUE(CreateWithDefaultTag(client_id, false));
2278 VerifyTag(client_id, false);
2281 TEST_F(SyncableClientTagTest, TestDeletedClientTagIndexClientId) {
2282 Id client_id = factory_.NewLocalId();
2283 EXPECT_TRUE(CreateWithDefaultTag(client_id, true));
2284 VerifyTag(client_id, true);
2287 TEST_F(SyncableClientTagTest, TestDeletedClientTagIndexServerId) {
2288 Id server_id = factory_.NewServerId();
2289 EXPECT_TRUE(CreateWithDefaultTag(server_id, true));
2290 VerifyTag(server_id, true);
2293 TEST_F(SyncableClientTagTest, TestClientTagIndexDuplicateServer) {
2294 EXPECT_TRUE(CreateWithDefaultTag(factory_.NewServerId(), true));
2295 EXPECT_FALSE(CreateWithDefaultTag(factory_.NewServerId(), true));
2296 EXPECT_FALSE(CreateWithDefaultTag(factory_.NewServerId(), false));
2297 EXPECT_FALSE(CreateWithDefaultTag(factory_.NewLocalId(), false));
2298 EXPECT_FALSE(CreateWithDefaultTag(factory_.NewLocalId(), true));
2302 } // namespace syncable
2303 } // namespace syncer