Fix build break
[chromium-blink-merge.git] / sync / syncable / syncable_unittest.cc
blobaffaeb4508410693277d7579991788ca962beb82
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.
5 #include <string>
7 #include "base/basictypes.h"
8 #include "base/compiler_specific.h"
9 #include "base/file_util.h"
10 #include "base/files/file_path.h"
11 #include "base/files/scoped_temp_dir.h"
12 #include "base/location.h"
13 #include "base/logging.h"
14 #include "base/memory/scoped_ptr.h"
15 #include "base/message_loop.h"
16 #include "base/stl_util.h"
17 #include "base/stringprintf.h"
18 #include "base/synchronization/condition_variable.h"
19 #include "base/test/values_test_util.h"
20 #include "base/threading/platform_thread.h"
21 #include "base/values.h"
22 #include "sync/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"
41 namespace syncer {
42 namespace syncable {
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) {
52 EntryKernel kernel;
53 scoped_ptr<DictionaryValue> value(kernel.ToValue(NULL));
54 if (value.get()) {
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();
65 namespace {
66 void PutDataAsBookmarkFavicon(WriteTransaction* wtrans,
67 MutableEntry* e,
68 const char* bytes,
69 size_t bytes_length) {
70 sync_pb::EntitySpecifics specifics;
71 specifics.mutable_bookmark()->set_url("http://demo/");
72 specifics.mutable_bookmark()->set_favicon(bytes, bytes_length);
73 e->Put(SPECIFICS, specifics);
76 void ExpectDataFromBookmarkFaviconEquals(BaseTransaction* trans,
77 Entry* e,
78 const char* bytes,
79 size_t bytes_length) {
80 ASSERT_TRUE(e->good());
81 ASSERT_TRUE(e->Get(SPECIFICS).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());
86 } // namespace
88 class SyncableGeneralTest : public testing::Test {
89 public:
90 static const char kIndexTestName[];
91 virtual void SetUp() {
92 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
93 db_path_ = temp_dir_.path().Append(
94 FILE_PATH_LITERAL("SyncableTest.sqlite3"));
97 virtual void TearDown() {
99 protected:
100 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"),
112 &handler_,
113 NULL,
114 NULL,
115 NULL);
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.Get(META_HANDLE);
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::ChildHandles 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());
150 me.Put(ID, id);
151 me.Put(BASE_VERSION, 1);
152 written_metahandle = me.Get(META_HANDLE);
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::ChildHandles child_handles;
163 dir.GetChildHandlesById(&rtrans, rtrans.root_id(), &child_handles);
164 EXPECT_EQ(1u, child_handles.size());
166 for (Directory::ChildHandles::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::ChildHandles::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));
203 // Now delete it.
205 WriteTransaction trans(FROM_HERE, UNITTEST, &dir);
206 MutableEntry e(&trans, GET_BY_HANDLE, written_metahandle);
207 e.Put(IS_DEL, true);
209 EXPECT_EQ(0, CountEntriesWithName(&trans, trans.root_id(), name));
212 dir.SaveChanges();
215 TEST_F(SyncableGeneralTest, ChildrenOps) {
216 Directory dir(new InMemoryDirectoryBackingStore("SimpleTest"),
217 &handler_,
218 NULL,
219 NULL,
220 NULL);
221 ASSERT_EQ(OPENED, dir.Open(
222 "SimpleTest", &delegate_, NullTransactionObserver()));
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());
242 me.Put(ID, id);
243 me.Put(BASE_VERSION, 1);
244 written_metahandle = me.Get(META_HANDLE);
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.Get(ID), root.GetFirstChildId());
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);
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());
281 dir.SaveChanges();
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_),
294 &handler_,
295 NULL,
296 NULL,
297 NULL);
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());
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);
309 dir.SaveChanges();
312 // The DB was closed. Now reopen it. This will cause index regeneration.
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()));
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);
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_),
340 &handler_,
341 NULL,
342 NULL,
343 NULL);
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());
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.
356 dir.SaveChanges();
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_),
363 &handler_,
364 NULL,
365 NULL,
366 NULL);
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.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));
380 TEST_F(SyncableGeneralTest, ToValue) {
381 Directory dir(new InMemoryDirectoryBackingStore("SimpleTest"),
382 &handler_,
383 NULL,
384 NULL,
385 NULL);
386 ASSERT_EQ(OPENED, dir.Open(
387 "SimpleTest", &delegate_, NullTransactionObserver()));
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<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());
405 me.Put(ID, id);
406 me.Put(BASE_VERSION, 1);
408 scoped_ptr<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");
416 dir.SaveChanges();
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.Put(IS_UNSYNCED, 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.Get(UNIQUE_BOOKMARK_TAG));
445 // A test fixture for syncable::Directory. Uses an in-memory database to keep
446 // the unit tests fast.
447 class SyncableDirectoryTest : public testing::Test {
448 protected:
449 MessageLoop message_loop_;
450 static const char kName[];
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());
464 virtual void TearDown() {
465 if (dir_.get())
466 dir_->SaveChanges();
467 dir_.reset();
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());
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();
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());
534 me.Put(ID, id);
535 me.Put(IS_UNSYNCED, true);
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);
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();
560 private:
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.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));
583 e.Put(SPECIFICS, specs);
584 e.Put(SERVER_SPECIFICS, specs);
588 ModelTypeSet to_purge(BOOKMARKS);
589 dir_->PurgeEntriesWithTypeIn(to_purge, 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);
596 to_purge.Clear();
597 to_purge.Put(PREFERENCES);
598 dir_->PurgeEntriesWithTypeIn(to_purge, 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.Get(META_HANDLE));
615 e.Put(IS_UNSYNCED, 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);
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));
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.Get(META_HANDLE));
678 e.Put(IS_UNSYNCED, true);
681 dir_->SaveChanges();
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());
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));
699 expected_dirty_metahandles.insert(expected_dirty_metahandles.end(),
700 new_dirty_metahandles.begin(), new_dirty_metahandles.end());
702 dir_->SaveChanges();
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;
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);
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);
764 int64 handle1 = 0;
765 int64 handle2 = 0;
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.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);
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
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));
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());
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));
832 // Purge item2.
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.
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));
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());
855 EntryKernel tmp;
856 tmp.put(ID, id1);
857 tmp.put(META_HANDLE, handle1);
858 EXPECT_TRUE(journal_entries.count(&tmp));
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));
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";
881 namespace {
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) {
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());
896 TEST_F(SyncableDirectoryTest, TestDelete) {
897 std::string name = "peanut butter jelly time";
898 WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get());
899 MutableEntry e1(&trans, CREATE, BOOKMARKS, trans.root_id(), name);
900 ASSERT_TRUE(e1.good());
901 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));
909 ASSERT_TRUE(e1.Put(IS_DEL, false));
910 ASSERT_TRUE(e2.Put(IS_DEL, false));
911 ASSERT_TRUE(e3.Put(IS_DEL, false));
913 ASSERT_TRUE(e1.Put(IS_DEL, true));
914 ASSERT_TRUE(e2.Put(IS_DEL, true));
915 ASSERT_TRUE(e3.Put(IS_DEL, true));
918 TEST_F(SyncableDirectoryTest, TestGetUnsynced) {
919 Directory::UnsyncedMetaHandles 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.Get(META_HANDLE);
930 e1.Put(BASE_VERSION, 1);
931 e1.Put(IS_DIR, true);
932 e1.Put(ID, TestIdFactory::FromNumber(101));
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));
940 dir_->SaveChanges();
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.Put(IS_UNSYNCED, true);
951 dir_->SaveChanges();
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.Put(IS_UNSYNCED, true);
962 dir_->SaveChanges();
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]);
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));
980 dir_->SaveChanges();
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.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);
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));
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.Put(IS_UNAPPLIED_UPDATE, 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.Put(IS_UNAPPLIED_UPDATE, 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]);
1043 } else {
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.Put(IS_UNAPPLIED_UPDATE, 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.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));
1088 grandchild_handle = grandchild.Get(META_HANDLE);
1090 dir_->SaveChanges();
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.
1098 static inline bool IsLegalNewParent(const Entry& a, const Entry& b) {
1099 return IsLegalNewParent(a.trans(), a.Get(ID), b.Get(ID));
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.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);
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));
1155 TEST_F(SyncableDirectoryTest, TestEntryIsInFolder) {
1156 // Create a subdir and an entry.
1157 int64 entry_handle;
1158 syncable::Id folder_id;
1159 syncable::Id entry_id;
1160 std::string entry_name = "entry";
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);
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);
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.Get(META_HANDLE));
1186 EXPECT_TRUE(entry.Get(NON_UNIQUE_NAME) == entry_name);
1187 EXPECT_TRUE(entry.Get(PARENT_ID) == 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.Put(IS_UNSYNCED, true);
1197 EXPECT_TRUE(parent_folder.Put(IS_DIR, true));
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));
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);
1207 ASSERT_TRUE(child.good());
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));
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 ASSERT_TRUE(folder.Put(IS_DIR, true));
1227 ASSERT_TRUE(folder.Put(IS_DEL, true));
1229 EXPECT_EQ(0, CountEntriesWithName(&trans, trans.root_id(), folder_name));
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));
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 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));
1249 // Create items of each model type, and check that GetModelType and
1250 // GetServerModelType return the right value.
1251 TEST_F(SyncableDirectoryTest, GetModelType) {
1252 TestIdFactory id_factory;
1253 ModelTypeSet protocol_types = ProtocolTypes();
1254 for (ModelTypeSet::Iterator iter = protocol_types.First(); iter.Good();
1255 iter.Inc()) {
1256 ModelType datatype = iter.Get();
1257 SCOPED_TRACE(testing::Message("Testing model type ") << datatype);
1258 switch (datatype) {
1259 case UNSPECIFIED:
1260 case TOP_LEVEL_FOLDER:
1261 continue; // Datatype isn't a function of Specifics.
1262 default:
1263 break;
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.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());
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());
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());
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());
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());
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;
1339 Id orig_parent_id;
1340 Id orig_child_id;
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.Put(IS_DIR, true);
1348 parent.Put(IS_UNSYNCED, true);
1350 MutableEntry child(&trans, CREATE, BOOKMARKS, parent.Get(ID), "child");
1351 child.Put(IS_UNSYNCED, true);
1353 orig_parent_id = parent.Get(ID);
1354 orig_child_id = child.Get(ID);
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.Put(IS_UNSYNCED, false);
1367 child.Put(BASE_VERSION, 1);
1368 child.Put(SERVER_VERSION, 1);
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);
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;
1387 Id orig_parent_id;
1388 Id orig_child_id;
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.Put(IS_DIR, true);
1396 parent.Put(IS_UNSYNCED, true);
1398 MutableEntry child(&trans, CREATE, BOOKMARKS, parent.Get(ID), "child");
1399 child.Put(IS_UNSYNCED, true);
1401 orig_parent_id = parent.Get(ID);
1402 orig_child_id = child.Get(ID);
1406 // Delete the child.
1407 WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get());
1409 MutableEntry child(&trans, GET_BY_ID, orig_child_id);
1410 child.Put(IS_DEL, 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.Put(IS_UNSYNCED, false);
1422 parent.Put(BASE_VERSION, 1);
1423 parent.Put(SERVER_VERSION, 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
1453 // database.
1454 TEST_F(SyncableDirectoryTest, OldClientLeftUnsyncedDeletedLocalItem) {
1455 // We must create an entry with the offending properties. This is done with
1456 // some abuse of the MutableEntry's API; it doesn't expect us to modify an
1457 // item after it is deleted. If this hack becomes impractical we will need to
1458 // find a new way to simulate this scenario.
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(),
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);
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);
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.Put(ID, zombie_id);
1500 zombie.Put(IS_DEL, true);
1501 zombie.Put(IS_UNSYNCED, 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;
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);
1539 WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get());
1541 MutableEntry parent(&trans, CREATE, BOOKMARKS, id_factory.root(), "parent");
1542 parent.Put(IS_DIR, true);
1543 parent.Put(IS_UNSYNCED, true);
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);
1550 null_child_id = child.Get(ID);
1553 EXPECT_EQ(OPENED, SimulateSaveAndReloadDir());
1556 ReadTransaction trans(FROM_HERE, dir_.get());
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)));
1566 // An OnDirectoryBackingStore that can be set to always fail SaveChanges.
1567 class TestBackingStore : public OnDiskDirectoryBackingStore {
1568 public:
1569 TestBackingStore(const std::string& dir_name,
1570 const base::FilePath& backing_filepath);
1572 virtual ~TestBackingStore();
1574 virtual bool SaveChanges(const Directory::SaveChangesSnapshot& snapshot)
1575 OVERRIDE;
1577 void StartFailingSaveChanges() {
1578 fail_save_changes_ = true;
1581 private:
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_) {
1596 return false;
1597 } else {
1598 return OnDiskDirectoryBackingStore::SaveChanges(snapshot);
1602 // A directory whose Save() function can be set to always fail.
1603 class TestDirectory : public Directory {
1604 public:
1605 // A factory function used to work around some initialization order issues.
1606 static TestDirectory* Create(
1607 Encryptor *encryptor,
1608 UnrecoverableErrorHandler *handler,
1609 const std::string& dir_name,
1610 const base::FilePath& backing_filepath);
1612 virtual ~TestDirectory();
1614 void StartFailingSaveChanges() {
1615 backing_store_->StartFailingSaveChanges();
1618 private:
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 {
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 file_util::Delete(file_path_, true);
1673 CreateDirectory();
1676 virtual void TearDown() {
1677 // This also closes file handles.
1678 dir_->SaveChanges();
1679 dir_.reset();
1680 file_util::Delete(file_path_, true);
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());
1694 void SaveAndReloadDir() {
1695 dir_->SaveChanges();
1696 CreateDirectory();
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.Put(SERVER_SPECIFICS, bookmark_specs);
1734 item1.Put(IS_UNSYNCED, true);
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);
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);
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);
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);
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);
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());
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);
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.
1813 SaveAndReloadDir();
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);
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";
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);
1846 dir_->SaveChanges();
1847 dir_.reset(new Directory(new OnDiskDirectoryBackingStore(kName, file_path_),
1848 &handler_,
1849 NULL,
1850 NULL,
1851 NULL));
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) {
1927 int64 handle1 = 0;
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.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));
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.Get(NON_UNIQUE_NAME), "aguilera");
1953 aguilera.Put(NON_UNIQUE_NAME, "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());
1963 int64 handle2 = 0;
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.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));
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));
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) {
2009 int64 handle1 = 0;
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.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));
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());
2037 EXPECT_TRUE(IsInMetahandlesToPurge(handle1));
2038 ASSERT_FALSE(dir_->SaveChanges());
2039 EXPECT_TRUE(IsInMetahandlesToPurge(handle1));
2042 } // namespace
2044 void SyncableDirectoryTest::ValidateEntry(BaseTransaction* trans,
2045 int64 id,
2046 bool check_name,
2047 const std::string& name,
2048 int64 base_version,
2049 int64 server_version,
2050 bool is_del) {
2051 Entry e(trans, GET_BY_ID, TestIdFactory::FromNumber(id));
2052 ASSERT_TRUE(e.good());
2053 if (check_name)
2054 ASSERT_TRUE(name == e.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));
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.
2076 dir_->Close();
2077 dir_.reset();
2079 dir_.reset(new Directory(saved_store,
2080 &handler_,
2081 NULL,
2082 NULL,
2083 NULL));
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)
2090 dir_.reset();
2092 return result;
2095 namespace {
2097 class SyncableDirectoryManagement : public testing::Test {
2098 public:
2099 virtual void SetUp() {
2100 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
2103 virtual void TearDown() {
2105 protected:
2106 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),
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();
2127 // Closing the directory should have released the backing database file.
2128 ASSERT_TRUE(file_util::Delete(path, true));
2131 class StressTransactionsDelegate : public base::PlatformThread::Delegate {
2132 public:
2133 StressTransactionsDelegate(Directory* dir, int thread_number)
2134 : dir_(dir),
2135 thread_number_(thread_number) {}
2137 private:
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(
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);
2171 DISALLOW_COPY_AND_ASSIGN(StressTransactionsDelegate);
2174 TEST(SyncableDirectory, StressTransactions) {
2175 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());
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]);
2203 dir.Close();
2206 class SyncableClientTagTest : public SyncableDirectoryTest {
2207 public:
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_);
2218 CHECK(me.good());
2219 me.Put(ID, id);
2220 if (id.ServerKnows()) {
2221 me.Put(BASE_VERSION, kBaseVersion);
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_);
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);
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);
2245 protected:
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.Put(UNIQUE_CLIENT_TAG, 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.Get(UNIQUE_CLIENT_TAG).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));
2301 } // namespace
2302 } // namespace syncable
2303 } // namespace syncer