1 // Copyright 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
7 #include "base/basictypes.h"
8 #include "base/compiler_specific.h"
9 #include "base/file_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"
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
) {
54 scoped_ptr
<DictionaryValue
> value(kernel
.ToValue(NULL
));
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()));
67 void PutDataAsBookmarkFavicon(WriteTransaction
* wtrans
,
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
,
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());
89 class SyncableGeneralTest
: public testing::Test
{
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() {
101 MessageLoop message_loop_
;
102 base::ScopedTempDir temp_dir_
;
103 NullDirectoryChangeDelegate delegate_
;
104 FakeEncryptor encryptor_
;
105 TestUnrecoverableErrorHandler handler_
;
109 const char SyncableGeneralTest::kIndexTestName
[] = "IndexTest";
111 TEST_F(SyncableGeneralTest
, General
) {
112 Directory
dir(new InMemoryDirectoryBackingStore("SimpleTest"),
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());
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
));
206 WriteTransaction
trans(FROM_HERE
, UNITTEST
, &dir
);
207 MutableEntry
e(&trans
, GET_BY_HANDLE
, written_metahandle
);
210 EXPECT_EQ(0, CountEntriesWithName(&trans
, trans
.root_id(), name
));
216 TEST_F(SyncableGeneralTest
, ChildrenOps
) {
217 Directory
dir(new InMemoryDirectoryBackingStore("SimpleTest"),
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()));
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());
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()));
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()));
278 EXPECT_TRUE(dir
.GetFirstChildId(&rtrans
, rtrans
.root_id(), &child_id
));
279 EXPECT_TRUE(child_id
.IsRoot());
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_
),
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());
306 me
.Put(BASE_VERSION
, 1);
307 me
.Put(UNIQUE_CLIENT_TAG
, tag
);
308 written_metahandle
= me
.Get(META_HANDLE
);
313 // The DB was closed. Now reopen it. This will cause index regeneration.
315 Directory
dir(new OnDiskDirectoryBackingStore(kIndexTestName
, db_path_
),
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_
),
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());
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.
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_
),
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"),
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());
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");
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
{
424 MessageLoop message_loop_
;
425 static const char kName
[];
427 virtual void SetUp() {
428 dir_
.reset(new Directory(new InMemoryDirectoryBackingStore(kName
),
433 ASSERT_TRUE(dir_
.get());
434 ASSERT_EQ(OPENED
, dir_
->Open(kName
, &delegate_
,
435 NullTransactionObserver()));
436 ASSERT_TRUE(dir_
->good());
439 virtual void TearDown() {
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());
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());
510 me
.Put(IS_UNSYNCED
, true);
513 void ValidateEntry(BaseTransaction
* trans
,
516 const std::string
& name
,
518 int64 server_version
,
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();
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
;
551 AddDefaultFieldValue(BOOKMARKS
, &specs
);
552 expected_purges
.insert(e
.Get(META_HANDLE
));
553 all_handles
.insert(e
.Get(META_HANDLE
));
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
);
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);
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);
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);
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());
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
;
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);
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
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);
779 EXPECT_TRUE(delete_journal
->delete_journals_
.count(&tmp
));
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());
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
));
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.
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());
832 tmp
.put(META_HANDLE
, handle1
);
833 EXPECT_TRUE(journal_entries
.count(&tmp
));
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";
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
) {
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));
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);
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);
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]);
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
));
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));
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]);
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);
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.
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
);
1233 case TOP_LEVEL_FOLDER
:
1234 continue; // Datatype isn't a function of Specifics.
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
;
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
;
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
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(),
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
;
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
);
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
{
1531 TestBackingStore(const std::string
& dir_name
,
1532 const FilePath
& backing_filepath
);
1534 virtual ~TestBackingStore();
1536 virtual bool SaveChanges(const Directory::SaveChangesSnapshot
& snapshot
)
1539 void StartFailingSaveChanges() {
1540 fail_save_changes_
= true;
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_
) {
1560 return OnDiskDirectoryBackingStore::SaveChanges(snapshot
);
1564 // A directory whose Save() function can be set to always fail.
1565 class TestDirectory
: public Directory
{
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();
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
{
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);
1638 virtual void TearDown() {
1639 // This also closes file handles.
1640 dir_
->SaveChanges();
1642 file_util::Delete(file_path_
, true);
1645 // Creates a new directory. Deletes the old directory, if it exists.
1646 void CreateDirectory() {
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();
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);
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.
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);
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_
),
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
) {
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());
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
));
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
) {
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
));
2005 void SyncableDirectoryTest::ValidateEntry(BaseTransaction
* trans
,
2008 const std::string
& name
,
2010 int64 server_version
,
2012 Entry
e(trans
, GET_BY_ID
, TestIdFactory::FromNumber(id
));
2013 ASSERT_TRUE(e
.good());
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.
2040 dir_
.reset(new Directory(saved_store
,
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
)
2058 class SyncableDirectoryManagement
: public testing::Test
{
2060 virtual void SetUp() {
2061 ASSERT_TRUE(temp_dir_
.CreateUniqueTempDir());
2064 virtual void TearDown() {
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
),
2083 DirOpenResult result
=
2084 dir
.Open("ScopeTest", &delegate_
, NullTransactionObserver());
2085 ASSERT_EQ(result
, OPENED
);
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
{
2094 StressTransactionsDelegate(Directory
* dir
, int thread_number
)
2096 thread_number_(thread_number
) {}
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(
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
);
2121 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(
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
),
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
]);
2167 class SyncableClientTagTest
: public SyncableDirectoryTest
{
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_
);
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_
);
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);
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));
2267 } // namespace syncable
2268 } // namespace syncer