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