1 // Copyright 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
7 #include "base/basictypes.h"
8 #include "base/compiler_specific.h"
9 #include "base/file_util.h"
10 #include "base/files/file_path.h"
11 #include "base/files/scoped_temp_dir.h"
12 #include "base/location.h"
13 #include "base/logging.h"
14 #include "base/memory/scoped_ptr.h"
15 #include "base/message_loop.h"
16 #include "base/stl_util.h"
17 #include "base/stringprintf.h"
18 #include "base/synchronization/condition_variable.h"
19 #include "base/test/values_test_util.h"
20 #include "base/threading/platform_thread.h"
21 #include "base/values.h"
22 #include "sync/protocol/bookmark_specifics.pb.h"
23 #include "sync/syncable/directory_backing_store.h"
24 #include "sync/syncable/directory_change_delegate.h"
25 #include "sync/syncable/in_memory_directory_backing_store.h"
26 #include "sync/syncable/metahandle_set.h"
27 #include "sync/syncable/mutable_entry.h"
28 #include "sync/syncable/on_disk_directory_backing_store.h"
29 #include "sync/syncable/syncable_proto_util.h"
30 #include "sync/syncable/syncable_read_transaction.h"
31 #include "sync/syncable/syncable_util.h"
32 #include "sync/syncable/syncable_write_transaction.h"
33 #include "sync/test/engine/test_id_factory.h"
34 #include "sync/test/engine/test_syncable_utils.h"
35 #include "sync/test/fake_encryptor.h"
36 #include "sync/test/null_directory_change_delegate.h"
37 #include "sync/test/null_transaction_observer.h"
38 #include "sync/util/test_unrecoverable_error_handler.h"
39 #include "testing/gtest/include/gtest/gtest.h"
44 using base::ExpectDictBooleanValue
;
45 using base::ExpectDictStringValue
;
47 class SyncableKernelTest
: public testing::Test
{};
49 // TODO(akalin): Add unit tests for EntryKernel::ContainsString().
51 TEST_F(SyncableKernelTest
, ToValue
) {
53 scoped_ptr
<DictionaryValue
> value(kernel
.ToValue(NULL
));
55 // Not much to check without repeating the ToValue() code.
56 EXPECT_TRUE(value
->HasKey("isDirty"));
57 // The extra +2 is for "isDirty" and "serverModelType".
58 EXPECT_EQ(BIT_TEMPS_END
- BEGIN_FIELDS
+ 2,
59 static_cast<int>(value
->size()));
66 void PutDataAsBookmarkFavicon(WriteTransaction
* wtrans
,
69 size_t bytes_length
) {
70 sync_pb::EntitySpecifics specifics
;
71 specifics
.mutable_bookmark()->set_url("http://demo/");
72 specifics
.mutable_bookmark()->set_favicon(bytes
, bytes_length
);
73 e
->Put(SPECIFICS
, specifics
);
76 void ExpectDataFromBookmarkFaviconEquals(BaseTransaction
* trans
,
79 size_t bytes_length
) {
80 ASSERT_TRUE(e
->good());
81 ASSERT_TRUE(e
->Get(SPECIFICS
).has_bookmark());
82 ASSERT_EQ("http://demo/", e
->Get(SPECIFICS
).bookmark().url());
83 ASSERT_EQ(std::string(bytes
, bytes_length
),
84 e
->Get(SPECIFICS
).bookmark().favicon());
88 class SyncableGeneralTest
: public testing::Test
{
90 static const char kIndexTestName
[];
91 virtual void SetUp() {
92 ASSERT_TRUE(temp_dir_
.CreateUniqueTempDir());
93 db_path_
= temp_dir_
.path().Append(
94 FILE_PATH_LITERAL("SyncableTest.sqlite3"));
97 virtual void TearDown() {
100 MessageLoop message_loop_
;
101 base::ScopedTempDir temp_dir_
;
102 NullDirectoryChangeDelegate delegate_
;
103 FakeEncryptor encryptor_
;
104 TestUnrecoverableErrorHandler handler_
;
105 base::FilePath db_path_
;
108 const char SyncableGeneralTest::kIndexTestName
[] = "IndexTest";
110 TEST_F(SyncableGeneralTest
, General
) {
111 Directory
dir(new InMemoryDirectoryBackingStore("SimpleTest"),
117 ASSERT_EQ(OPENED
, dir
.Open(
118 "SimpleTest", &delegate_
, NullTransactionObserver()));
120 int64 root_metahandle
;
122 ReadTransaction
rtrans(FROM_HERE
, &dir
);
123 Entry
e(&rtrans
, GET_BY_ID
, rtrans
.root_id());
124 ASSERT_TRUE(e
.good());
125 root_metahandle
= e
.Get(META_HANDLE
);
128 int64 written_metahandle
;
129 const Id id
= TestIdFactory::FromNumber(99);
130 std::string name
= "Jeff";
131 // Test simple read operations on an empty DB.
133 ReadTransaction
rtrans(FROM_HERE
, &dir
);
134 Entry
e(&rtrans
, GET_BY_ID
, id
);
135 ASSERT_FALSE(e
.good()); // Hasn't been written yet.
137 Directory::ChildHandles child_handles
;
138 dir
.GetChildHandlesById(&rtrans
, rtrans
.root_id(), &child_handles
);
139 EXPECT_TRUE(child_handles
.empty());
141 dir
.GetChildHandlesByHandle(&rtrans
, root_metahandle
, &child_handles
);
142 EXPECT_TRUE(child_handles
.empty());
145 // Test creating a new meta entry.
147 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, &dir
);
148 MutableEntry
me(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), name
);
149 ASSERT_TRUE(me
.good());
151 me
.Put(BASE_VERSION
, 1);
152 written_metahandle
= me
.Get(META_HANDLE
);
155 // Test GetChildHandles* after something is now in the DB.
156 // Also check that GET_BY_ID works.
158 ReadTransaction
rtrans(FROM_HERE
, &dir
);
159 Entry
e(&rtrans
, GET_BY_ID
, id
);
160 ASSERT_TRUE(e
.good());
162 Directory::ChildHandles child_handles
;
163 dir
.GetChildHandlesById(&rtrans
, rtrans
.root_id(), &child_handles
);
164 EXPECT_EQ(1u, child_handles
.size());
166 for (Directory::ChildHandles::iterator i
= child_handles
.begin();
167 i
!= child_handles
.end(); ++i
) {
168 EXPECT_EQ(*i
, written_metahandle
);
171 dir
.GetChildHandlesByHandle(&rtrans
, root_metahandle
, &child_handles
);
172 EXPECT_EQ(1u, child_handles
.size());
174 for (Directory::ChildHandles::iterator i
= child_handles
.begin();
175 i
!= child_handles
.end(); ++i
) {
176 EXPECT_EQ(*i
, written_metahandle
);
180 // Test writing data to an entity. Also check that GET_BY_HANDLE works.
181 static const char s
[] = "Hello World.";
183 WriteTransaction
trans(FROM_HERE
, UNITTEST
, &dir
);
184 MutableEntry
e(&trans
, GET_BY_HANDLE
, written_metahandle
);
185 ASSERT_TRUE(e
.good());
186 PutDataAsBookmarkFavicon(&trans
, &e
, s
, sizeof(s
));
189 // Test reading back the contents that we just wrote.
191 WriteTransaction
trans(FROM_HERE
, UNITTEST
, &dir
);
192 MutableEntry
e(&trans
, GET_BY_HANDLE
, written_metahandle
);
193 ASSERT_TRUE(e
.good());
194 ExpectDataFromBookmarkFaviconEquals(&trans
, &e
, s
, sizeof(s
));
197 // Verify it exists in the folder.
199 ReadTransaction
rtrans(FROM_HERE
, &dir
);
200 EXPECT_EQ(1, CountEntriesWithName(&rtrans
, rtrans
.root_id(), name
));
205 WriteTransaction
trans(FROM_HERE
, UNITTEST
, &dir
);
206 MutableEntry
e(&trans
, GET_BY_HANDLE
, written_metahandle
);
209 EXPECT_EQ(0, CountEntriesWithName(&trans
, trans
.root_id(), name
));
215 TEST_F(SyncableGeneralTest
, ChildrenOps
) {
216 Directory
dir(new InMemoryDirectoryBackingStore("SimpleTest"),
221 ASSERT_EQ(OPENED
, dir
.Open(
222 "SimpleTest", &delegate_
, NullTransactionObserver()));
224 int64 written_metahandle
;
225 const Id id
= TestIdFactory::FromNumber(99);
226 std::string name
= "Jeff";
228 ReadTransaction
rtrans(FROM_HERE
, &dir
);
229 Entry
e(&rtrans
, GET_BY_ID
, id
);
230 ASSERT_FALSE(e
.good()); // Hasn't been written yet.
232 Entry
root(&rtrans
, GET_BY_ID
, rtrans
.root_id());
233 ASSERT_TRUE(root
.good());
234 EXPECT_FALSE(dir
.HasChildren(&rtrans
, rtrans
.root_id()));
235 EXPECT_TRUE(root
.GetFirstChildId().IsRoot());
239 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, &dir
);
240 MutableEntry
me(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), name
);
241 ASSERT_TRUE(me
.good());
243 me
.Put(BASE_VERSION
, 1);
244 written_metahandle
= me
.Get(META_HANDLE
);
247 // Test children ops after something is now in the DB.
249 ReadTransaction
rtrans(FROM_HERE
, &dir
);
250 Entry
e(&rtrans
, GET_BY_ID
, id
);
251 ASSERT_TRUE(e
.good());
253 Entry
child(&rtrans
, GET_BY_HANDLE
, written_metahandle
);
254 ASSERT_TRUE(child
.good());
256 Entry
root(&rtrans
, GET_BY_ID
, rtrans
.root_id());
257 ASSERT_TRUE(root
.good());
258 EXPECT_TRUE(dir
.HasChildren(&rtrans
, rtrans
.root_id()));
259 EXPECT_EQ(e
.Get(ID
), root
.GetFirstChildId());
263 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, &dir
);
264 MutableEntry
me(&wtrans
, GET_BY_HANDLE
, written_metahandle
);
265 ASSERT_TRUE(me
.good());
266 me
.Put(IS_DEL
, true);
269 // Test children ops after the children have been deleted.
271 ReadTransaction
rtrans(FROM_HERE
, &dir
);
272 Entry
e(&rtrans
, GET_BY_ID
, id
);
273 ASSERT_TRUE(e
.good());
275 Entry
root(&rtrans
, GET_BY_ID
, rtrans
.root_id());
276 ASSERT_TRUE(root
.good());
277 EXPECT_FALSE(dir
.HasChildren(&rtrans
, rtrans
.root_id()));
278 EXPECT_TRUE(root
.GetFirstChildId().IsRoot());
284 TEST_F(SyncableGeneralTest
, ClientIndexRebuildsProperly
) {
285 int64 written_metahandle
;
286 TestIdFactory factory
;
287 const Id id
= factory
.NewServerId();
288 std::string name
= "cheesepuffs";
289 std::string tag
= "dietcoke";
291 // Test creating a new meta entry.
293 Directory
dir(new OnDiskDirectoryBackingStore(kIndexTestName
, db_path_
),
298 ASSERT_EQ(OPENED
, dir
.Open(kIndexTestName
, &delegate_
,
299 NullTransactionObserver()));
301 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, &dir
);
302 MutableEntry
me(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), name
);
303 ASSERT_TRUE(me
.good());
305 me
.Put(BASE_VERSION
, 1);
306 me
.Put(UNIQUE_CLIENT_TAG
, tag
);
307 written_metahandle
= me
.Get(META_HANDLE
);
312 // The DB was closed. Now reopen it. This will cause index regeneration.
314 Directory
dir(new OnDiskDirectoryBackingStore(kIndexTestName
, db_path_
),
319 ASSERT_EQ(OPENED
, dir
.Open(kIndexTestName
,
320 &delegate_
, NullTransactionObserver()));
322 ReadTransaction
trans(FROM_HERE
, &dir
);
323 Entry
me(&trans
, GET_BY_CLIENT_TAG
, tag
);
324 ASSERT_TRUE(me
.good());
325 EXPECT_EQ(me
.Get(ID
), id
);
326 EXPECT_EQ(me
.Get(BASE_VERSION
), 1);
327 EXPECT_EQ(me
.Get(UNIQUE_CLIENT_TAG
), tag
);
328 EXPECT_EQ(me
.Get(META_HANDLE
), written_metahandle
);
332 TEST_F(SyncableGeneralTest
, ClientIndexRebuildsDeletedProperly
) {
333 TestIdFactory factory
;
334 const Id id
= factory
.NewServerId();
335 std::string tag
= "dietcoke";
337 // Test creating a deleted, unsynced, server meta entry.
339 Directory
dir(new OnDiskDirectoryBackingStore(kIndexTestName
, db_path_
),
344 ASSERT_EQ(OPENED
, dir
.Open(kIndexTestName
, &delegate_
,
345 NullTransactionObserver()));
347 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, &dir
);
348 MutableEntry
me(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "deleted");
349 ASSERT_TRUE(me
.good());
351 me
.Put(BASE_VERSION
, 1);
352 me
.Put(UNIQUE_CLIENT_TAG
, tag
);
353 me
.Put(IS_DEL
, true);
354 me
.Put(IS_UNSYNCED
, true); // Or it might be purged.
359 // The DB was closed. Now reopen it. This will cause index regeneration.
360 // Should still be present and valid in the client tag index.
362 Directory
dir(new OnDiskDirectoryBackingStore(kIndexTestName
, db_path_
),
367 ASSERT_EQ(OPENED
, dir
.Open(kIndexTestName
, &delegate_
,
368 NullTransactionObserver()));
370 ReadTransaction
trans(FROM_HERE
, &dir
);
371 Entry
me(&trans
, GET_BY_CLIENT_TAG
, tag
);
372 ASSERT_TRUE(me
.good());
373 EXPECT_EQ(me
.Get(ID
), id
);
374 EXPECT_EQ(me
.Get(UNIQUE_CLIENT_TAG
), tag
);
375 EXPECT_TRUE(me
.Get(IS_DEL
));
376 EXPECT_TRUE(me
.Get(IS_UNSYNCED
));
380 TEST_F(SyncableGeneralTest
, ToValue
) {
381 Directory
dir(new InMemoryDirectoryBackingStore("SimpleTest"),
386 ASSERT_EQ(OPENED
, dir
.Open(
387 "SimpleTest", &delegate_
, NullTransactionObserver()));
389 const Id id
= TestIdFactory::FromNumber(99);
391 ReadTransaction
rtrans(FROM_HERE
, &dir
);
392 Entry
e(&rtrans
, GET_BY_ID
, id
);
393 EXPECT_FALSE(e
.good()); // Hasn't been written yet.
395 scoped_ptr
<DictionaryValue
> value(e
.ToValue(NULL
));
396 ExpectDictBooleanValue(false, *value
, "good");
397 EXPECT_EQ(1u, value
->size());
400 // Test creating a new meta entry.
402 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, &dir
);
403 MutableEntry
me(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "new");
404 ASSERT_TRUE(me
.good());
406 me
.Put(BASE_VERSION
, 1);
408 scoped_ptr
<DictionaryValue
> value(me
.ToValue(NULL
));
409 ExpectDictBooleanValue(true, *value
, "good");
410 EXPECT_TRUE(value
->HasKey("kernel"));
411 ExpectDictStringValue("Bookmarks", *value
, "modelType");
412 ExpectDictBooleanValue(true, *value
, "existsOnClientBecauseNameIsNonEmpty");
413 ExpectDictBooleanValue(false, *value
, "isRoot");
419 // Test that the bookmark tag generation algorithm remains unchanged.
420 TEST_F(SyncableGeneralTest
, BookmarkTagTest
) {
421 InMemoryDirectoryBackingStore
* store
= new InMemoryDirectoryBackingStore("x");
423 // The two inputs that form the bookmark tag are the directory's cache_guid
424 // and its next_id value. We don't need to take any action to ensure
425 // consistent next_id values, but we do need to explicitly request that our
426 // InMemoryDirectoryBackingStore always return the same cache_guid.
427 store
->request_consistent_cache_guid();
429 Directory
dir(store
, &handler_
, NULL
, NULL
, NULL
);
430 ASSERT_EQ(OPENED
, dir
.Open("x", &delegate_
, NullTransactionObserver()));
433 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, &dir
);
434 MutableEntry
bm(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), "bm");
435 bm
.Put(IS_UNSYNCED
, true);
437 // If this assertion fails, that might indicate that the algorithm used to
438 // generate bookmark tags has been modified. This could have implications
439 // for bookmark ordering. Please make sure you know what you're doing if
440 // you intend to make such a change.
441 ASSERT_EQ("6wHRAb3kbnXV5GHrejp4/c1y5tw=", bm
.Get(UNIQUE_BOOKMARK_TAG
));
445 // A test fixture for syncable::Directory. Uses an in-memory database to keep
446 // the unit tests fast.
447 class SyncableDirectoryTest
: public testing::Test
{
449 MessageLoop message_loop_
;
450 static const char kName
[];
452 virtual void SetUp() {
453 dir_
.reset(new Directory(new InMemoryDirectoryBackingStore(kName
),
458 ASSERT_TRUE(dir_
.get());
459 ASSERT_EQ(OPENED
, dir_
->Open(kName
, &delegate_
,
460 NullTransactionObserver()));
461 ASSERT_TRUE(dir_
->good());
464 virtual void TearDown() {
470 void GetAllMetaHandles(BaseTransaction
* trans
, MetahandleSet
* result
) {
471 dir_
->GetAllMetaHandles(trans
, result
);
474 bool IsInDirtyMetahandles(int64 metahandle
) {
475 return 1 == dir_
->kernel_
->dirty_metahandles
->count(metahandle
);
478 bool IsInMetahandlesToPurge(int64 metahandle
) {
479 return 1 == dir_
->kernel_
->metahandles_to_purge
->count(metahandle
);
482 void CheckPurgeEntriesWithTypeInSucceeded(ModelTypeSet types_to_purge
,
483 bool before_reload
) {
484 SCOPED_TRACE(testing::Message("Before reload: ") << before_reload
);
486 ReadTransaction
trans(FROM_HERE
, dir_
.get());
487 MetahandleSet all_set
;
488 dir_
->GetAllMetaHandles(&trans
, &all_set
);
489 EXPECT_EQ(4U, all_set
.size());
491 EXPECT_EQ(6U, dir_
->kernel_
->metahandles_to_purge
->size());
492 for (MetahandleSet::iterator iter
= all_set
.begin();
493 iter
!= all_set
.end(); ++iter
) {
494 Entry
e(&trans
, GET_BY_HANDLE
, *iter
);
495 const ModelType local_type
= e
.GetModelType();
496 const ModelType server_type
= e
.GetServerModelType();
498 // Note the dance around incrementing |it|, since we sometimes erase().
499 if ((IsRealDataType(local_type
) &&
500 types_to_purge
.Has(local_type
)) ||
501 (IsRealDataType(server_type
) &&
502 types_to_purge
.Has(server_type
))) {
503 FAIL() << "Illegal type should have been deleted.";
508 for (ModelTypeSet::Iterator it
= types_to_purge
.First();
509 it
.Good(); it
.Inc()) {
510 EXPECT_FALSE(dir_
->InitialSyncEndedForType(it
.Get()));
512 EXPECT_FALSE(types_to_purge
.Has(BOOKMARKS
));
513 EXPECT_TRUE(dir_
->InitialSyncEndedForType(BOOKMARKS
));
516 FakeEncryptor encryptor_
;
517 TestUnrecoverableErrorHandler handler_
;
518 scoped_ptr
<Directory
> dir_
;
519 NullDirectoryChangeDelegate delegate_
;
521 // Creates an empty entry and sets the ID field to a default one.
522 void CreateEntry(const std::string
& entryname
) {
523 CreateEntry(entryname
, TestIdFactory::FromNumber(-99));
526 // Creates an empty entry and sets the ID field to id.
527 void CreateEntry(const std::string
& entryname
, const int id
) {
528 CreateEntry(entryname
, TestIdFactory::FromNumber(id
));
530 void CreateEntry(const std::string
& entryname
, Id id
) {
531 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, dir_
.get());
532 MutableEntry
me(&wtrans
, CREATE
, BOOKMARKS
, wtrans
.root_id(), entryname
);
533 ASSERT_TRUE(me
.good());
535 me
.Put(IS_UNSYNCED
, true);
538 void ValidateEntry(BaseTransaction
* trans
,
541 const std::string
& name
,
543 int64 server_version
,
546 // When a directory is saved then loaded from disk, it will pass through
547 // DropDeletedEntries(). This will remove some entries from the directory.
548 // This function is intended to simulate that process.
550 // WARNING: The directory will be deleted by this operation. You should
551 // not have any pointers to the directory (open transactions included)
552 // when you call this.
553 DirOpenResult
SimulateSaveAndReloadDir();
555 // This function will close and re-open the directory without saving any
556 // pending changes. This is intended to simulate the recovery from a crash
557 // scenario. The same warnings for SimulateSaveAndReloadDir apply here.
558 DirOpenResult
SimulateCrashAndReloadDir();
561 // A helper function for Simulate{Save,Crash}AndReloadDir.
562 DirOpenResult
ReloadDirImpl();
565 TEST_F(SyncableDirectoryTest
, TakeSnapshotGetsMetahandlesToPurge
) {
566 const int metas_to_create
= 50;
567 MetahandleSet expected_purges
;
568 MetahandleSet all_handles
;
570 WriteTransaction
trans(FROM_HERE
, UNITTEST
, dir_
.get());
571 for (int i
= 0; i
< metas_to_create
; i
++) {
572 MutableEntry
e(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "foo");
573 e
.Put(IS_UNSYNCED
, true);
574 sync_pb::EntitySpecifics specs
;
576 AddDefaultFieldValue(BOOKMARKS
, &specs
);
577 expected_purges
.insert(e
.Get(META_HANDLE
));
578 all_handles
.insert(e
.Get(META_HANDLE
));
580 AddDefaultFieldValue(PREFERENCES
, &specs
);
581 all_handles
.insert(e
.Get(META_HANDLE
));
583 e
.Put(SPECIFICS
, specs
);
584 e
.Put(SERVER_SPECIFICS
, specs
);
588 ModelTypeSet
to_purge(BOOKMARKS
);
589 dir_
->PurgeEntriesWithTypeIn(to_purge
, ModelTypeSet());
591 Directory::SaveChangesSnapshot snapshot1
;
592 base::AutoLock
scoped_lock(dir_
->kernel_
->save_changes_mutex
);
593 dir_
->TakeSnapshotForSaveChanges(&snapshot1
);
594 EXPECT_TRUE(expected_purges
== snapshot1
.metahandles_to_purge
);
597 to_purge
.Put(PREFERENCES
);
598 dir_
->PurgeEntriesWithTypeIn(to_purge
, ModelTypeSet());
600 dir_
->HandleSaveChangesFailure(snapshot1
);
602 Directory::SaveChangesSnapshot snapshot2
;
603 dir_
->TakeSnapshotForSaveChanges(&snapshot2
);
604 EXPECT_TRUE(all_handles
== snapshot2
.metahandles_to_purge
);
607 TEST_F(SyncableDirectoryTest
, TakeSnapshotGetsAllDirtyHandlesTest
) {
608 const int metahandles_to_create
= 100;
609 std::vector
<int64
> expected_dirty_metahandles
;
611 WriteTransaction
trans(FROM_HERE
, UNITTEST
, dir_
.get());
612 for (int i
= 0; i
< metahandles_to_create
; i
++) {
613 MutableEntry
e(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "foo");
614 expected_dirty_metahandles
.push_back(e
.Get(META_HANDLE
));
615 e
.Put(IS_UNSYNCED
, true);
618 // Fake SaveChanges() and make sure we got what we expected.
620 Directory::SaveChangesSnapshot snapshot
;
621 base::AutoLock
scoped_lock(dir_
->kernel_
->save_changes_mutex
);
622 dir_
->TakeSnapshotForSaveChanges(&snapshot
);
623 // Make sure there's an entry for each new metahandle. Make sure all
624 // entries are marked dirty.
625 ASSERT_EQ(expected_dirty_metahandles
.size(), snapshot
.dirty_metas
.size());
626 for (EntryKernelSet::const_iterator i
= snapshot
.dirty_metas
.begin();
627 i
!= snapshot
.dirty_metas
.end(); ++i
) {
628 ASSERT_TRUE((*i
)->is_dirty());
630 dir_
->VacuumAfterSaveChanges(snapshot
);
632 // Put a new value with existing transactions as well as adding new ones.
634 WriteTransaction
trans(FROM_HERE
, UNITTEST
, dir_
.get());
635 std::vector
<int64
> new_dirty_metahandles
;
636 for (std::vector
<int64
>::const_iterator i
=
637 expected_dirty_metahandles
.begin();
638 i
!= expected_dirty_metahandles
.end(); ++i
) {
639 // Change existing entries to directories to dirty them.
640 MutableEntry
e1(&trans
, GET_BY_HANDLE
, *i
);
641 e1
.Put(IS_DIR
, true);
642 e1
.Put(IS_UNSYNCED
, true);
644 MutableEntry
e2(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "bar");
645 e2
.Put(IS_UNSYNCED
, true);
646 new_dirty_metahandles
.push_back(e2
.Get(META_HANDLE
));
648 expected_dirty_metahandles
.insert(expected_dirty_metahandles
.end(),
649 new_dirty_metahandles
.begin(), new_dirty_metahandles
.end());
651 // Fake SaveChanges() and make sure we got what we expected.
653 Directory::SaveChangesSnapshot snapshot
;
654 base::AutoLock
scoped_lock(dir_
->kernel_
->save_changes_mutex
);
655 dir_
->TakeSnapshotForSaveChanges(&snapshot
);
656 // Make sure there's an entry for each new metahandle. Make sure all
657 // entries are marked dirty.
658 EXPECT_EQ(expected_dirty_metahandles
.size(), snapshot
.dirty_metas
.size());
659 for (EntryKernelSet::const_iterator i
= snapshot
.dirty_metas
.begin();
660 i
!= snapshot
.dirty_metas
.end(); ++i
) {
661 EXPECT_TRUE((*i
)->is_dirty());
663 dir_
->VacuumAfterSaveChanges(snapshot
);
667 TEST_F(SyncableDirectoryTest
, TakeSnapshotGetsOnlyDirtyHandlesTest
) {
668 const int metahandles_to_create
= 100;
670 // half of 2 * metahandles_to_create
671 const unsigned int number_changed
= 100u;
672 std::vector
<int64
> expected_dirty_metahandles
;
674 WriteTransaction
trans(FROM_HERE
, UNITTEST
, dir_
.get());
675 for (int i
= 0; i
< metahandles_to_create
; i
++) {
676 MutableEntry
e(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "foo");
677 expected_dirty_metahandles
.push_back(e
.Get(META_HANDLE
));
678 e
.Put(IS_UNSYNCED
, true);
682 // Put a new value with existing transactions as well as adding new ones.
684 WriteTransaction
trans(FROM_HERE
, UNITTEST
, dir_
.get());
685 std::vector
<int64
> new_dirty_metahandles
;
686 for (std::vector
<int64
>::const_iterator i
=
687 expected_dirty_metahandles
.begin();
688 i
!= expected_dirty_metahandles
.end(); ++i
) {
689 // Change existing entries to directories to dirty them.
690 MutableEntry
e1(&trans
, GET_BY_HANDLE
, *i
);
691 ASSERT_TRUE(e1
.good());
692 e1
.Put(IS_DIR
, true);
693 e1
.Put(IS_UNSYNCED
, true);
695 MutableEntry
e2(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "bar");
696 e2
.Put(IS_UNSYNCED
, true);
697 new_dirty_metahandles
.push_back(e2
.Get(META_HANDLE
));
699 expected_dirty_metahandles
.insert(expected_dirty_metahandles
.end(),
700 new_dirty_metahandles
.begin(), new_dirty_metahandles
.end());
703 // Don't make any changes whatsoever and ensure nothing comes back.
705 WriteTransaction
trans(FROM_HERE
, UNITTEST
, dir_
.get());
706 for (std::vector
<int64
>::const_iterator i
=
707 expected_dirty_metahandles
.begin();
708 i
!= expected_dirty_metahandles
.end(); ++i
) {
709 MutableEntry
e(&trans
, GET_BY_HANDLE
, *i
);
710 ASSERT_TRUE(e
.good());
711 // We aren't doing anything to dirty these entries.
714 // Fake SaveChanges() and make sure we got what we expected.
716 Directory::SaveChangesSnapshot snapshot
;
717 base::AutoLock
scoped_lock(dir_
->kernel_
->save_changes_mutex
);
718 dir_
->TakeSnapshotForSaveChanges(&snapshot
);
719 // Make sure there are no dirty_metahandles.
720 EXPECT_EQ(0u, snapshot
.dirty_metas
.size());
721 dir_
->VacuumAfterSaveChanges(snapshot
);
724 WriteTransaction
trans(FROM_HERE
, UNITTEST
, dir_
.get());
725 bool should_change
= false;
726 for (std::vector
<int64
>::const_iterator i
=
727 expected_dirty_metahandles
.begin();
728 i
!= expected_dirty_metahandles
.end(); ++i
) {
729 // Maybe change entries by flipping IS_DIR.
730 MutableEntry
e(&trans
, GET_BY_HANDLE
, *i
);
731 ASSERT_TRUE(e
.good());
732 should_change
= !should_change
;
734 bool not_dir
= !e
.Get(IS_DIR
);
735 e
.Put(IS_DIR
, not_dir
);
736 e
.Put(IS_UNSYNCED
, true);
740 // Fake SaveChanges() and make sure we got what we expected.
742 Directory::SaveChangesSnapshot snapshot
;
743 base::AutoLock
scoped_lock(dir_
->kernel_
->save_changes_mutex
);
744 dir_
->TakeSnapshotForSaveChanges(&snapshot
);
745 // Make sure there's an entry for each changed metahandle. Make sure all
746 // entries are marked dirty.
747 EXPECT_EQ(number_changed
, snapshot
.dirty_metas
.size());
748 for (EntryKernelSet::const_iterator i
= snapshot
.dirty_metas
.begin();
749 i
!= snapshot
.dirty_metas
.end(); ++i
) {
750 EXPECT_TRUE((*i
)->is_dirty());
752 dir_
->VacuumAfterSaveChanges(snapshot
);
756 // Test delete journals management.
757 TEST_F(SyncableDirectoryTest
, ManageDeleteJournals
) {
758 sync_pb::EntitySpecifics bookmark_specifics
;
759 AddDefaultFieldValue(BOOKMARKS
, &bookmark_specifics
);
760 bookmark_specifics
.mutable_bookmark()->set_url("url");
762 Id id1
= TestIdFactory::FromNumber(-1);
763 Id id2
= TestIdFactory::FromNumber(-2);
767 // Create two bookmark entries and save in database.
768 CreateEntry("item1", id1
);
769 CreateEntry("item2", id2
);
771 WriteTransaction
trans(FROM_HERE
, UNITTEST
, dir_
.get());
772 MutableEntry
item1(&trans
, GET_BY_ID
, id1
);
773 ASSERT_TRUE(item1
.good());
774 handle1
= item1
.Get(META_HANDLE
);
775 item1
.Put(SPECIFICS
, bookmark_specifics
);
776 item1
.Put(SERVER_SPECIFICS
, bookmark_specifics
);
777 MutableEntry
item2(&trans
, GET_BY_ID
, id2
);
778 ASSERT_TRUE(item2
.good());
779 handle2
= item2
.Get(META_HANDLE
);
780 item2
.Put(SPECIFICS
, bookmark_specifics
);
781 item2
.Put(SERVER_SPECIFICS
, bookmark_specifics
);
783 ASSERT_EQ(OPENED
, SimulateSaveAndReloadDir());
786 { // Test adding and saving delete journals.
787 DeleteJournal
* delete_journal
= dir_
->delete_journal();
789 WriteTransaction
trans(FROM_HERE
, UNITTEST
, dir_
.get());
790 EntryKernelSet journal_entries
;
791 delete_journal
->GetDeleteJournals(&trans
, BOOKMARKS
, &journal_entries
);
792 ASSERT_EQ(0u, journal_entries
.size());
794 // Set SERVER_IS_DEL of the entries to true and they should be added to
796 MutableEntry
item1(&trans
, GET_BY_ID
, id1
);
797 ASSERT_TRUE(item1
.good());
798 item1
.Put(SERVER_IS_DEL
, true);
799 MutableEntry
item2(&trans
, GET_BY_ID
, id2
);
800 ASSERT_TRUE(item2
.good());
801 item2
.Put(SERVER_IS_DEL
, true);
804 EXPECT_TRUE(delete_journal
->delete_journals_
.count(&tmp
));
806 EXPECT_TRUE(delete_journal
->delete_journals_
.count(&tmp
));
809 // Save delete journals in database and verify memory clearing.
810 ASSERT_TRUE(dir_
->SaveChanges());
812 ReadTransaction
trans(FROM_HERE
, dir_
.get());
813 EXPECT_EQ(0u, delete_journal
->GetDeleteJournalSize(&trans
));
815 ASSERT_EQ(OPENED
, SimulateSaveAndReloadDir());
820 // Test reading delete journals from database.
821 WriteTransaction
trans(FROM_HERE
, UNITTEST
, dir_
.get());
822 DeleteJournal
* delete_journal
= dir_
->delete_journal();
823 EntryKernelSet journal_entries
;
824 delete_journal
->GetDeleteJournals(&trans
, BOOKMARKS
, &journal_entries
);
825 ASSERT_EQ(2u, journal_entries
.size());
827 tmp
.put(META_HANDLE
, handle1
);
828 EXPECT_TRUE(journal_entries
.count(&tmp
));
829 tmp
.put(META_HANDLE
, handle2
);
830 EXPECT_TRUE(journal_entries
.count(&tmp
));
833 MetahandleSet to_purge
;
834 to_purge
.insert(handle2
);
835 delete_journal
->PurgeDeleteJournals(&trans
, to_purge
);
837 // Verify that item2 is purged from journals in memory and will be
838 // purged from database.
840 EXPECT_FALSE(delete_journal
->delete_journals_
.count(&tmp
));
841 EXPECT_EQ(1u, delete_journal
->delete_journals_to_purge_
.size());
842 EXPECT_TRUE(delete_journal
->delete_journals_to_purge_
.count(handle2
));
844 ASSERT_EQ(OPENED
, SimulateSaveAndReloadDir());
849 // Verify purged entry is gone in database.
850 WriteTransaction
trans(FROM_HERE
, UNITTEST
, dir_
.get());
851 DeleteJournal
* delete_journal
= dir_
->delete_journal();
852 EntryKernelSet journal_entries
;
853 delete_journal
->GetDeleteJournals(&trans
, BOOKMARKS
, &journal_entries
);
854 ASSERT_EQ(1u, journal_entries
.size());
857 tmp
.put(META_HANDLE
, handle1
);
858 EXPECT_TRUE(journal_entries
.count(&tmp
));
861 MutableEntry
item1(&trans
, GET_BY_ID
, id1
);
862 ASSERT_TRUE(item1
.good());
863 item1
.Put(SERVER_IS_DEL
, false);
864 EXPECT_TRUE(delete_journal
->delete_journals_
.empty());
865 EXPECT_EQ(1u, delete_journal
->delete_journals_to_purge_
.size());
866 EXPECT_TRUE(delete_journal
->delete_journals_to_purge_
.count(handle1
));
868 ASSERT_EQ(OPENED
, SimulateSaveAndReloadDir());
872 // Verify undeleted entry is gone from database.
873 ReadTransaction
trans(FROM_HERE
, dir_
.get());
874 DeleteJournal
* delete_journal
= dir_
->delete_journal();
875 ASSERT_EQ(0u, delete_journal
->GetDeleteJournalSize(&trans
));
879 const char SyncableDirectoryTest::kName
[] = "Foo";
883 TEST_F(SyncableDirectoryTest
, TestBasicLookupNonExistantID
) {
884 ReadTransaction
rtrans(FROM_HERE
, dir_
.get());
885 Entry
e(&rtrans
, GET_BY_ID
, TestIdFactory::FromNumber(-99));
886 ASSERT_FALSE(e
.good());
889 TEST_F(SyncableDirectoryTest
, TestBasicLookupValidID
) {
891 ReadTransaction
rtrans(FROM_HERE
, dir_
.get());
892 Entry
e(&rtrans
, GET_BY_ID
, TestIdFactory::FromNumber(-99));
893 ASSERT_TRUE(e
.good());
896 TEST_F(SyncableDirectoryTest
, TestDelete
) {
897 std::string name
= "peanut butter jelly time";
898 WriteTransaction
trans(FROM_HERE
, UNITTEST
, dir_
.get());
899 MutableEntry
e1(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), name
);
900 ASSERT_TRUE(e1
.good());
901 ASSERT_TRUE(e1
.Put(IS_DEL
, true));
902 MutableEntry
e2(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), name
);
903 ASSERT_TRUE(e2
.good());
904 ASSERT_TRUE(e2
.Put(IS_DEL
, true));
905 MutableEntry
e3(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), name
);
906 ASSERT_TRUE(e3
.good());
907 ASSERT_TRUE(e3
.Put(IS_DEL
, true));
909 ASSERT_TRUE(e1
.Put(IS_DEL
, false));
910 ASSERT_TRUE(e2
.Put(IS_DEL
, false));
911 ASSERT_TRUE(e3
.Put(IS_DEL
, false));
913 ASSERT_TRUE(e1
.Put(IS_DEL
, true));
914 ASSERT_TRUE(e2
.Put(IS_DEL
, true));
915 ASSERT_TRUE(e3
.Put(IS_DEL
, true));
918 TEST_F(SyncableDirectoryTest
, TestGetUnsynced
) {
919 Directory::UnsyncedMetaHandles handles
;
920 int64 handle1
, handle2
;
922 WriteTransaction
trans(FROM_HERE
, UNITTEST
, dir_
.get());
924 dir_
->GetUnsyncedMetaHandles(&trans
, &handles
);
925 ASSERT_TRUE(0 == handles
.size());
927 MutableEntry
e1(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "abba");
928 ASSERT_TRUE(e1
.good());
929 handle1
= e1
.Get(META_HANDLE
);
930 e1
.Put(BASE_VERSION
, 1);
931 e1
.Put(IS_DIR
, true);
932 e1
.Put(ID
, TestIdFactory::FromNumber(101));
934 MutableEntry
e2(&trans
, CREATE
, BOOKMARKS
, e1
.Get(ID
), "bread");
935 ASSERT_TRUE(e2
.good());
936 handle2
= e2
.Get(META_HANDLE
);
937 e2
.Put(BASE_VERSION
, 1);
938 e2
.Put(ID
, TestIdFactory::FromNumber(102));
942 WriteTransaction
trans(FROM_HERE
, UNITTEST
, dir_
.get());
944 dir_
->GetUnsyncedMetaHandles(&trans
, &handles
);
945 ASSERT_TRUE(0 == handles
.size());
947 MutableEntry
e3(&trans
, GET_BY_HANDLE
, handle1
);
948 ASSERT_TRUE(e3
.good());
949 e3
.Put(IS_UNSYNCED
, true);
953 WriteTransaction
trans(FROM_HERE
, UNITTEST
, dir_
.get());
954 dir_
->GetUnsyncedMetaHandles(&trans
, &handles
);
955 ASSERT_TRUE(1 == handles
.size());
956 ASSERT_TRUE(handle1
== handles
[0]);
958 MutableEntry
e4(&trans
, GET_BY_HANDLE
, handle2
);
959 ASSERT_TRUE(e4
.good());
960 e4
.Put(IS_UNSYNCED
, true);
964 WriteTransaction
trans(FROM_HERE
, UNITTEST
, dir_
.get());
965 dir_
->GetUnsyncedMetaHandles(&trans
, &handles
);
966 ASSERT_TRUE(2 == handles
.size());
967 if (handle1
== handles
[0]) {
968 ASSERT_TRUE(handle2
== handles
[1]);
970 ASSERT_TRUE(handle2
== handles
[0]);
971 ASSERT_TRUE(handle1
== handles
[1]);
974 MutableEntry
e5(&trans
, GET_BY_HANDLE
, handle1
);
975 ASSERT_TRUE(e5
.good());
976 ASSERT_TRUE(e5
.Get(IS_UNSYNCED
));
977 ASSERT_TRUE(e5
.Put(IS_UNSYNCED
, false));
978 ASSERT_FALSE(e5
.Get(IS_UNSYNCED
));
982 WriteTransaction
trans(FROM_HERE
, UNITTEST
, dir_
.get());
983 dir_
->GetUnsyncedMetaHandles(&trans
, &handles
);
984 ASSERT_TRUE(1 == handles
.size());
985 ASSERT_TRUE(handle2
== handles
[0]);
989 TEST_F(SyncableDirectoryTest
, TestGetUnappliedUpdates
) {
990 std::vector
<int64
> handles
;
991 int64 handle1
, handle2
;
992 const FullModelTypeSet all_types
= FullModelTypeSet::All();
994 WriteTransaction
trans(FROM_HERE
, UNITTEST
, dir_
.get());
996 dir_
->GetUnappliedUpdateMetaHandles(&trans
, all_types
, &handles
);
997 ASSERT_TRUE(0 == handles
.size());
999 MutableEntry
e1(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "abba");
1000 ASSERT_TRUE(e1
.good());
1001 handle1
= e1
.Get(META_HANDLE
);
1002 e1
.Put(IS_UNAPPLIED_UPDATE
, false);
1003 e1
.Put(BASE_VERSION
, 1);
1004 e1
.Put(ID
, TestIdFactory::FromNumber(101));
1005 e1
.Put(IS_DIR
, true);
1007 MutableEntry
e2(&trans
, CREATE
, BOOKMARKS
, e1
.Get(ID
), "bread");
1008 ASSERT_TRUE(e2
.good());
1009 handle2
= e2
.Get(META_HANDLE
);
1010 e2
.Put(IS_UNAPPLIED_UPDATE
, false);
1011 e2
.Put(BASE_VERSION
, 1);
1012 e2
.Put(ID
, TestIdFactory::FromNumber(102));
1014 dir_
->SaveChanges();
1016 WriteTransaction
trans(FROM_HERE
, UNITTEST
, dir_
.get());
1018 dir_
->GetUnappliedUpdateMetaHandles(&trans
, all_types
, &handles
);
1019 ASSERT_TRUE(0 == handles
.size());
1021 MutableEntry
e3(&trans
, GET_BY_HANDLE
, handle1
);
1022 ASSERT_TRUE(e3
.good());
1023 e3
.Put(IS_UNAPPLIED_UPDATE
, true);
1025 dir_
->SaveChanges();
1027 WriteTransaction
trans(FROM_HERE
, UNITTEST
, dir_
.get());
1028 dir_
->GetUnappliedUpdateMetaHandles(&trans
, all_types
, &handles
);
1029 ASSERT_TRUE(1 == handles
.size());
1030 ASSERT_TRUE(handle1
== handles
[0]);
1032 MutableEntry
e4(&trans
, GET_BY_HANDLE
, handle2
);
1033 ASSERT_TRUE(e4
.good());
1034 e4
.Put(IS_UNAPPLIED_UPDATE
, true);
1036 dir_
->SaveChanges();
1038 WriteTransaction
trans(FROM_HERE
, UNITTEST
, dir_
.get());
1039 dir_
->GetUnappliedUpdateMetaHandles(&trans
, all_types
, &handles
);
1040 ASSERT_TRUE(2 == handles
.size());
1041 if (handle1
== handles
[0]) {
1042 ASSERT_TRUE(handle2
== handles
[1]);
1044 ASSERT_TRUE(handle2
== handles
[0]);
1045 ASSERT_TRUE(handle1
== handles
[1]);
1048 MutableEntry
e5(&trans
, GET_BY_HANDLE
, handle1
);
1049 ASSERT_TRUE(e5
.good());
1050 e5
.Put(IS_UNAPPLIED_UPDATE
, false);
1052 dir_
->SaveChanges();
1054 WriteTransaction
trans(FROM_HERE
, UNITTEST
, dir_
.get());
1055 dir_
->GetUnappliedUpdateMetaHandles(&trans
, all_types
, &handles
);
1056 ASSERT_TRUE(1 == handles
.size());
1057 ASSERT_TRUE(handle2
== handles
[0]);
1062 TEST_F(SyncableDirectoryTest
, DeleteBug_531383
) {
1063 // Try to evoke a check failure...
1064 TestIdFactory id_factory
;
1065 int64 grandchild_handle
;
1067 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, dir_
.get());
1068 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, id_factory
.root(), "Bob");
1069 ASSERT_TRUE(parent
.good());
1070 parent
.Put(IS_DIR
, true);
1071 parent
.Put(ID
, id_factory
.NewServerId());
1072 parent
.Put(BASE_VERSION
, 1);
1073 MutableEntry
child(&wtrans
, CREATE
, BOOKMARKS
, parent
.Get(ID
), "Bob");
1074 ASSERT_TRUE(child
.good());
1075 child
.Put(IS_DIR
, true);
1076 child
.Put(ID
, id_factory
.NewServerId());
1077 child
.Put(BASE_VERSION
, 1);
1078 MutableEntry
grandchild(&wtrans
, CREATE
, BOOKMARKS
, child
.Get(ID
), "Bob");
1079 ASSERT_TRUE(grandchild
.good());
1080 grandchild
.Put(ID
, id_factory
.NewServerId());
1081 grandchild
.Put(BASE_VERSION
, 1);
1082 ASSERT_TRUE(grandchild
.Put(IS_DEL
, true));
1083 MutableEntry
twin(&wtrans
, CREATE
, BOOKMARKS
, child
.Get(ID
), "Bob");
1084 ASSERT_TRUE(twin
.good());
1085 ASSERT_TRUE(twin
.Put(IS_DEL
, true));
1086 ASSERT_TRUE(grandchild
.Put(IS_DEL
, false));
1088 grandchild_handle
= grandchild
.Get(META_HANDLE
);
1090 dir_
->SaveChanges();
1092 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, dir_
.get());
1093 MutableEntry
grandchild(&wtrans
, GET_BY_HANDLE
, grandchild_handle
);
1094 grandchild
.Put(IS_DEL
, true); // Used to CHECK fail here.
1098 static inline bool IsLegalNewParent(const Entry
& a
, const Entry
& b
) {
1099 return IsLegalNewParent(a
.trans(), a
.Get(ID
), b
.Get(ID
));
1102 TEST_F(SyncableDirectoryTest
, TestIsLegalNewParent
) {
1103 TestIdFactory id_factory
;
1104 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, dir_
.get());
1105 Entry
root(&wtrans
, GET_BY_ID
, id_factory
.root());
1106 ASSERT_TRUE(root
.good());
1107 MutableEntry
parent(&wtrans
, CREATE
, BOOKMARKS
, root
.Get(ID
), "Bob");
1108 ASSERT_TRUE(parent
.good());
1109 parent
.Put(IS_DIR
, true);
1110 parent
.Put(ID
, id_factory
.NewServerId());
1111 parent
.Put(BASE_VERSION
, 1);
1112 MutableEntry
child(&wtrans
, CREATE
, BOOKMARKS
, parent
.Get(ID
), "Bob");
1113 ASSERT_TRUE(child
.good());
1114 child
.Put(IS_DIR
, true);
1115 child
.Put(ID
, id_factory
.NewServerId());
1116 child
.Put(BASE_VERSION
, 1);
1117 MutableEntry
grandchild(&wtrans
, CREATE
, BOOKMARKS
, child
.Get(ID
), "Bob");
1118 ASSERT_TRUE(grandchild
.good());
1119 grandchild
.Put(ID
, id_factory
.NewServerId());
1120 grandchild
.Put(BASE_VERSION
, 1);
1122 MutableEntry
parent2(&wtrans
, CREATE
, BOOKMARKS
, root
.Get(ID
), "Pete");
1123 ASSERT_TRUE(parent2
.good());
1124 parent2
.Put(IS_DIR
, true);
1125 parent2
.Put(ID
, id_factory
.NewServerId());
1126 parent2
.Put(BASE_VERSION
, 1);
1127 MutableEntry
child2(&wtrans
, CREATE
, BOOKMARKS
, parent2
.Get(ID
), "Pete");
1128 ASSERT_TRUE(child2
.good());
1129 child2
.Put(IS_DIR
, true);
1130 child2
.Put(ID
, id_factory
.NewServerId());
1131 child2
.Put(BASE_VERSION
, 1);
1132 MutableEntry
grandchild2(&wtrans
, CREATE
, BOOKMARKS
, child2
.Get(ID
), "Pete");
1133 ASSERT_TRUE(grandchild2
.good());
1134 grandchild2
.Put(ID
, id_factory
.NewServerId());
1135 grandchild2
.Put(BASE_VERSION
, 1);
1143 // grandchild grandchild2
1144 ASSERT_TRUE(IsLegalNewParent(child
, root
));
1145 ASSERT_TRUE(IsLegalNewParent(child
, parent
));
1146 ASSERT_FALSE(IsLegalNewParent(child
, child
));
1147 ASSERT_FALSE(IsLegalNewParent(child
, grandchild
));
1148 ASSERT_TRUE(IsLegalNewParent(child
, parent2
));
1149 ASSERT_TRUE(IsLegalNewParent(child
, grandchild2
));
1150 ASSERT_FALSE(IsLegalNewParent(parent
, grandchild
));
1151 ASSERT_FALSE(IsLegalNewParent(root
, grandchild
));
1152 ASSERT_FALSE(IsLegalNewParent(parent
, grandchild
));
1155 TEST_F(SyncableDirectoryTest
, TestEntryIsInFolder
) {
1156 // Create a subdir and an entry.
1158 syncable::Id folder_id
;
1159 syncable::Id entry_id
;
1160 std::string entry_name
= "entry";
1163 WriteTransaction
trans(FROM_HERE
, UNITTEST
, dir_
.get());
1164 MutableEntry
folder(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "folder");
1165 ASSERT_TRUE(folder
.good());
1166 EXPECT_TRUE(folder
.Put(IS_DIR
, true));
1167 EXPECT_TRUE(folder
.Put(IS_UNSYNCED
, true));
1168 folder_id
= folder
.Get(ID
);
1170 MutableEntry
entry(&trans
, CREATE
, BOOKMARKS
, folder
.Get(ID
), entry_name
);
1171 ASSERT_TRUE(entry
.good());
1172 entry_handle
= entry
.Get(META_HANDLE
);
1173 entry
.Put(IS_UNSYNCED
, true);
1174 entry_id
= entry
.Get(ID
);
1177 // Make sure we can find the entry in the folder.
1179 ReadTransaction
trans(FROM_HERE
, dir_
.get());
1180 EXPECT_EQ(0, CountEntriesWithName(&trans
, trans
.root_id(), entry_name
));
1181 EXPECT_EQ(1, CountEntriesWithName(&trans
, folder_id
, entry_name
));
1183 Entry
entry(&trans
, GET_BY_ID
, entry_id
);
1184 ASSERT_TRUE(entry
.good());
1185 EXPECT_EQ(entry_handle
, entry
.Get(META_HANDLE
));
1186 EXPECT_TRUE(entry
.Get(NON_UNIQUE_NAME
) == entry_name
);
1187 EXPECT_TRUE(entry
.Get(PARENT_ID
) == folder_id
);
1191 TEST_F(SyncableDirectoryTest
, TestParentIdIndexUpdate
) {
1192 std::string child_name
= "child";
1194 WriteTransaction
wt(FROM_HERE
, UNITTEST
, dir_
.get());
1195 MutableEntry
parent_folder(&wt
, CREATE
, BOOKMARKS
, wt
.root_id(), "folder1");
1196 parent_folder
.Put(IS_UNSYNCED
, true);
1197 EXPECT_TRUE(parent_folder
.Put(IS_DIR
, true));
1199 MutableEntry
parent_folder2(&wt
, CREATE
, BOOKMARKS
, wt
.root_id(), "folder2");
1200 parent_folder2
.Put(IS_UNSYNCED
, true);
1201 EXPECT_TRUE(parent_folder2
.Put(IS_DIR
, true));
1203 MutableEntry
child(&wt
, CREATE
, BOOKMARKS
, parent_folder
.Get(ID
), child_name
);
1204 EXPECT_TRUE(child
.Put(IS_DIR
, true));
1205 child
.Put(IS_UNSYNCED
, true);
1207 ASSERT_TRUE(child
.good());
1209 EXPECT_EQ(0, CountEntriesWithName(&wt
, wt
.root_id(), child_name
));
1210 EXPECT_EQ(parent_folder
.Get(ID
), child
.Get(PARENT_ID
));
1211 EXPECT_EQ(1, CountEntriesWithName(&wt
, parent_folder
.Get(ID
), child_name
));
1212 EXPECT_EQ(0, CountEntriesWithName(&wt
, parent_folder2
.Get(ID
), child_name
));
1213 child
.Put(PARENT_ID
, parent_folder2
.Get(ID
));
1214 EXPECT_EQ(parent_folder2
.Get(ID
), child
.Get(PARENT_ID
));
1215 EXPECT_EQ(0, CountEntriesWithName(&wt
, parent_folder
.Get(ID
), child_name
));
1216 EXPECT_EQ(1, CountEntriesWithName(&wt
, parent_folder2
.Get(ID
), child_name
));
1219 TEST_F(SyncableDirectoryTest
, TestNoReindexDeletedItems
) {
1220 std::string folder_name
= "folder";
1221 std::string new_name
= "new_name";
1223 WriteTransaction
trans(FROM_HERE
, UNITTEST
, dir_
.get());
1224 MutableEntry
folder(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), folder_name
);
1225 ASSERT_TRUE(folder
.good());
1226 ASSERT_TRUE(folder
.Put(IS_DIR
, true));
1227 ASSERT_TRUE(folder
.Put(IS_DEL
, true));
1229 EXPECT_EQ(0, CountEntriesWithName(&trans
, trans
.root_id(), folder_name
));
1231 MutableEntry
deleted(&trans
, GET_BY_ID
, folder
.Get(ID
));
1232 ASSERT_TRUE(deleted
.good());
1233 ASSERT_TRUE(deleted
.Put(PARENT_ID
, trans
.root_id()));
1234 ASSERT_TRUE(deleted
.Put(NON_UNIQUE_NAME
, new_name
));
1236 EXPECT_EQ(0, CountEntriesWithName(&trans
, trans
.root_id(), folder_name
));
1237 EXPECT_EQ(0, CountEntriesWithName(&trans
, trans
.root_id(), new_name
));
1240 TEST_F(SyncableDirectoryTest
, TestCaseChangeRename
) {
1241 WriteTransaction
trans(FROM_HERE
, UNITTEST
, dir_
.get());
1242 MutableEntry
folder(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "CaseChange");
1243 ASSERT_TRUE(folder
.good());
1244 EXPECT_TRUE(folder
.Put(PARENT_ID
, trans
.root_id()));
1245 EXPECT_TRUE(folder
.Put(NON_UNIQUE_NAME
, "CASECHANGE"));
1246 EXPECT_TRUE(folder
.Put(IS_DEL
, true));
1249 // Create items of each model type, and check that GetModelType and
1250 // GetServerModelType return the right value.
1251 TEST_F(SyncableDirectoryTest
, GetModelType
) {
1252 TestIdFactory id_factory
;
1253 ModelTypeSet protocol_types
= ProtocolTypes();
1254 for (ModelTypeSet::Iterator iter
= protocol_types
.First(); iter
.Good();
1256 ModelType datatype
= iter
.Get();
1257 SCOPED_TRACE(testing::Message("Testing model type ") << datatype
);
1260 case TOP_LEVEL_FOLDER
:
1261 continue; // Datatype isn't a function of Specifics.
1265 sync_pb::EntitySpecifics specifics
;
1266 AddDefaultFieldValue(datatype
, &specifics
);
1268 WriteTransaction
trans(FROM_HERE
, UNITTEST
, dir_
.get());
1270 MutableEntry
folder(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "Folder");
1271 ASSERT_TRUE(folder
.good());
1272 folder
.Put(ID
, id_factory
.NewServerId());
1273 folder
.Put(SPECIFICS
, specifics
);
1274 folder
.Put(BASE_VERSION
, 1);
1275 folder
.Put(IS_DIR
, true);
1276 folder
.Put(IS_DEL
, false);
1277 ASSERT_EQ(datatype
, folder
.GetModelType());
1279 MutableEntry
item(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "Item");
1280 ASSERT_TRUE(item
.good());
1281 item
.Put(ID
, id_factory
.NewServerId());
1282 item
.Put(SPECIFICS
, specifics
);
1283 item
.Put(BASE_VERSION
, 1);
1284 item
.Put(IS_DIR
, false);
1285 item
.Put(IS_DEL
, false);
1286 ASSERT_EQ(datatype
, item
.GetModelType());
1288 // It's critical that deletion records retain their datatype, so that
1289 // they can be dispatched to the appropriate change processor.
1290 MutableEntry
deleted_item(
1291 &trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "Deleted Item");
1292 ASSERT_TRUE(item
.good());
1293 deleted_item
.Put(ID
, id_factory
.NewServerId());
1294 deleted_item
.Put(SPECIFICS
, specifics
);
1295 deleted_item
.Put(BASE_VERSION
, 1);
1296 deleted_item
.Put(IS_DIR
, false);
1297 deleted_item
.Put(IS_DEL
, true);
1298 ASSERT_EQ(datatype
, deleted_item
.GetModelType());
1300 MutableEntry
server_folder(&trans
, CREATE_NEW_UPDATE_ITEM
,
1301 id_factory
.NewServerId());
1302 ASSERT_TRUE(server_folder
.good());
1303 server_folder
.Put(SERVER_SPECIFICS
, specifics
);
1304 server_folder
.Put(BASE_VERSION
, 1);
1305 server_folder
.Put(SERVER_IS_DIR
, true);
1306 server_folder
.Put(SERVER_IS_DEL
, false);
1307 ASSERT_EQ(datatype
, server_folder
.GetServerModelType());
1309 MutableEntry
server_item(&trans
, CREATE_NEW_UPDATE_ITEM
,
1310 id_factory
.NewServerId());
1311 ASSERT_TRUE(server_item
.good());
1312 server_item
.Put(SERVER_SPECIFICS
, specifics
);
1313 server_item
.Put(BASE_VERSION
, 1);
1314 server_item
.Put(SERVER_IS_DIR
, false);
1315 server_item
.Put(SERVER_IS_DEL
, false);
1316 ASSERT_EQ(datatype
, server_item
.GetServerModelType());
1318 sync_pb::SyncEntity folder_entity
;
1319 folder_entity
.set_id_string(SyncableIdToProto(id_factory
.NewServerId()));
1320 folder_entity
.set_deleted(false);
1321 folder_entity
.set_folder(true);
1322 folder_entity
.mutable_specifics()->CopyFrom(specifics
);
1323 ASSERT_EQ(datatype
, GetModelType(folder_entity
));
1325 sync_pb::SyncEntity item_entity
;
1326 item_entity
.set_id_string(SyncableIdToProto(id_factory
.NewServerId()));
1327 item_entity
.set_deleted(false);
1328 item_entity
.set_folder(false);
1329 item_entity
.mutable_specifics()->CopyFrom(specifics
);
1330 ASSERT_EQ(datatype
, GetModelType(item_entity
));
1334 // A test that roughly mimics the directory interaction that occurs when a
1335 // bookmark folder and entry are created then synced for the first time. It is
1336 // a more common variant of the 'DeletedAndUnsyncedChild' scenario tested below.
1337 TEST_F(SyncableDirectoryTest
, ChangeEntryIDAndUpdateChildren_ParentAndChild
) {
1338 TestIdFactory id_factory
;
1343 // Create two client-side items, a parent and child.
1344 WriteTransaction
trans(FROM_HERE
, UNITTEST
, dir_
.get());
1346 MutableEntry
parent(&trans
, CREATE
, BOOKMARKS
, id_factory
.root(), "parent");
1347 parent
.Put(IS_DIR
, true);
1348 parent
.Put(IS_UNSYNCED
, true);
1350 MutableEntry
child(&trans
, CREATE
, BOOKMARKS
, parent
.Get(ID
), "child");
1351 child
.Put(IS_UNSYNCED
, true);
1353 orig_parent_id
= parent
.Get(ID
);
1354 orig_child_id
= child
.Get(ID
);
1358 // Simulate what happens after committing two items. Their IDs will be
1359 // replaced with server IDs. The child is renamed first, then the parent.
1360 WriteTransaction
trans(FROM_HERE
, UNITTEST
, dir_
.get());
1362 MutableEntry
parent(&trans
, GET_BY_ID
, orig_parent_id
);
1363 MutableEntry
child(&trans
, GET_BY_ID
, orig_child_id
);
1365 ChangeEntryIDAndUpdateChildren(&trans
, &child
, id_factory
.NewServerId());
1366 child
.Put(IS_UNSYNCED
, false);
1367 child
.Put(BASE_VERSION
, 1);
1368 child
.Put(SERVER_VERSION
, 1);
1370 ChangeEntryIDAndUpdateChildren(&trans
, &parent
, id_factory
.NewServerId());
1371 parent
.Put(IS_UNSYNCED
, false);
1372 parent
.Put(BASE_VERSION
, 1);
1373 parent
.Put(SERVER_VERSION
, 1);
1376 // Final check for validity.
1377 EXPECT_EQ(OPENED
, SimulateSaveAndReloadDir());
1380 // A test based on the scenario where we create a bookmark folder and entry
1381 // locally, but with a twist. In this case, the bookmark is deleted before we
1382 // are able to sync either it or its parent folder. This scenario used to cause
1383 // directory corruption, see crbug.com/125381.
1384 TEST_F(SyncableDirectoryTest
,
1385 ChangeEntryIDAndUpdateChildren_DeletedAndUnsyncedChild
) {
1386 TestIdFactory id_factory
;
1391 // Create two client-side items, a parent and child.
1392 WriteTransaction
trans(FROM_HERE
, UNITTEST
, dir_
.get());
1394 MutableEntry
parent(&trans
, CREATE
, BOOKMARKS
, id_factory
.root(), "parent");
1395 parent
.Put(IS_DIR
, true);
1396 parent
.Put(IS_UNSYNCED
, true);
1398 MutableEntry
child(&trans
, CREATE
, BOOKMARKS
, parent
.Get(ID
), "child");
1399 child
.Put(IS_UNSYNCED
, true);
1401 orig_parent_id
= parent
.Get(ID
);
1402 orig_child_id
= child
.Get(ID
);
1406 // Delete the child.
1407 WriteTransaction
trans(FROM_HERE
, UNITTEST
, dir_
.get());
1409 MutableEntry
child(&trans
, GET_BY_ID
, orig_child_id
);
1410 child
.Put(IS_DEL
, true);
1414 // Simulate what happens after committing the parent. Its ID will be
1415 // replaced with server a ID.
1416 WriteTransaction
trans(FROM_HERE
, UNITTEST
, dir_
.get());
1418 MutableEntry
parent(&trans
, GET_BY_ID
, orig_parent_id
);
1420 ChangeEntryIDAndUpdateChildren(&trans
, &parent
, id_factory
.NewServerId());
1421 parent
.Put(IS_UNSYNCED
, false);
1422 parent
.Put(BASE_VERSION
, 1);
1423 parent
.Put(SERVER_VERSION
, 1);
1426 // Final check for validity.
1427 EXPECT_EQ(OPENED
, SimulateSaveAndReloadDir());
1430 // Ask the directory to generate a unique ID. Close and re-open the database
1431 // without saving, then ask for another unique ID. Verify IDs are not reused.
1432 // This scenario simulates a crash within the first few seconds of operation.
1433 TEST_F(SyncableDirectoryTest
, LocalIdReuseTest
) {
1434 Id pre_crash_id
= dir_
->NextId();
1435 SimulateCrashAndReloadDir();
1436 Id post_crash_id
= dir_
->NextId();
1437 EXPECT_NE(pre_crash_id
, post_crash_id
);
1440 // Ask the directory to generate a unique ID. Save the directory. Close and
1441 // re-open the database without saving, then ask for another unique ID. Verify
1442 // IDs are not reused. This scenario simulates a steady-state crash.
1443 TEST_F(SyncableDirectoryTest
, LocalIdReuseTestWithSave
) {
1444 Id pre_crash_id
= dir_
->NextId();
1445 dir_
->SaveChanges();
1446 SimulateCrashAndReloadDir();
1447 Id post_crash_id
= dir_
->NextId();
1448 EXPECT_NE(pre_crash_id
, post_crash_id
);
1451 // Ensure that the unsynced, is_del and server unkown entries that may have been
1452 // left in the database by old clients will be deleted when we open the old
1454 TEST_F(SyncableDirectoryTest
, OldClientLeftUnsyncedDeletedLocalItem
) {
1455 // We must create an entry with the offending properties. This is done with
1456 // some abuse of the MutableEntry's API; it doesn't expect us to modify an
1457 // item after it is deleted. If this hack becomes impractical we will need to
1458 // find a new way to simulate this scenario.
1460 TestIdFactory id_factory
;
1462 // Happy-path: These valid entries should not get deleted.
1463 Id server_knows_id
= id_factory
.NewServerId();
1464 Id not_is_del_id
= id_factory
.NewLocalId();
1466 // The ID of the entry which will be unsynced, is_del and !ServerKnows().
1467 Id zombie_id
= id_factory
.NewLocalId();
1469 // We're about to do some bad things. Tell the directory verification
1470 // routines to look the other way.
1471 dir_
->SetInvariantCheckLevel(OFF
);
1474 WriteTransaction
trans(FROM_HERE
, UNITTEST
, dir_
.get());
1476 // Create an uncommitted tombstone entry.
1477 MutableEntry
server_knows(&trans
, CREATE
, BOOKMARKS
, id_factory
.root(),
1479 server_knows
.Put(ID
, server_knows_id
);
1480 server_knows
.Put(IS_UNSYNCED
, true);
1481 server_knows
.Put(IS_DEL
, true);
1482 server_knows
.Put(BASE_VERSION
, 5);
1483 server_knows
.Put(SERVER_VERSION
, 4);
1485 // Create a valid update entry.
1486 MutableEntry
not_is_del(
1487 &trans
, CREATE
, BOOKMARKS
, id_factory
.root(), "not_is_del");
1488 not_is_del
.Put(ID
, not_is_del_id
);
1489 not_is_del
.Put(IS_DEL
, false);
1490 not_is_del
.Put(IS_UNSYNCED
, true);
1492 // Create a tombstone which should never be sent to the server because the
1493 // server never knew about the item's existence.
1495 // New clients should never put entries into this state. We work around
1496 // this by setting IS_DEL before setting IS_UNSYNCED, something which the
1497 // client should never do in practice.
1498 MutableEntry
zombie(&trans
, CREATE
, BOOKMARKS
, id_factory
.root(), "zombie");
1499 zombie
.Put(ID
, zombie_id
);
1500 zombie
.Put(IS_DEL
, true);
1501 zombie
.Put(IS_UNSYNCED
, true);
1504 ASSERT_EQ(OPENED
, SimulateSaveAndReloadDir());
1507 ReadTransaction
trans(FROM_HERE
, dir_
.get());
1509 // The directory loading routines should have cleaned things up, making it
1510 // safe to check invariants once again.
1511 dir_
->FullyCheckTreeInvariants(&trans
);
1513 Entry
server_knows(&trans
, GET_BY_ID
, server_knows_id
);
1514 EXPECT_TRUE(server_knows
.good());
1516 Entry
not_is_del(&trans
, GET_BY_ID
, not_is_del_id
);
1517 EXPECT_TRUE(not_is_del
.good());
1519 Entry
zombie(&trans
, GET_BY_ID
, zombie_id
);
1520 EXPECT_FALSE(zombie
.good());
1524 TEST_F(SyncableDirectoryTest
, PositionWithNullSurvivesSaveAndReload
) {
1525 TestIdFactory id_factory
;
1527 const char null_cstr
[] = "\0null\0test";
1528 std::string
null_str(null_cstr
, arraysize(null_cstr
) - 1);
1529 // Pad up to the minimum length with 0x7f characters, then add a string that
1530 // contains a few NULLs to the end. This is slightly wrong, since the suffix
1531 // part of a UniquePosition shouldn't contain NULLs, but it's good enough for
1533 std::string suffix
=
1534 std::string(UniquePosition::kSuffixLength
- null_str
.length(), '\x7f')
1536 UniquePosition null_pos
= UniquePosition::FromInt64(10, suffix
);
1539 WriteTransaction
trans(FROM_HERE
, UNITTEST
, dir_
.get());
1541 MutableEntry
parent(&trans
, CREATE
, BOOKMARKS
, id_factory
.root(), "parent");
1542 parent
.Put(IS_DIR
, true);
1543 parent
.Put(IS_UNSYNCED
, true);
1545 MutableEntry
child(&trans
, CREATE
, BOOKMARKS
, parent
.Get(ID
), "child");
1546 child
.Put(IS_UNSYNCED
, true);
1547 child
.Put(UNIQUE_POSITION
, null_pos
);
1548 child
.Put(SERVER_UNIQUE_POSITION
, null_pos
);
1550 null_child_id
= child
.Get(ID
);
1553 EXPECT_EQ(OPENED
, SimulateSaveAndReloadDir());
1556 ReadTransaction
trans(FROM_HERE
, dir_
.get());
1558 Entry
null_ordinal_child(&trans
, GET_BY_ID
, null_child_id
);
1560 null_pos
.Equals(null_ordinal_child
.Get(UNIQUE_POSITION
)));
1562 null_pos
.Equals(null_ordinal_child
.Get(SERVER_UNIQUE_POSITION
)));
1566 // An OnDirectoryBackingStore that can be set to always fail SaveChanges.
1567 class TestBackingStore
: public OnDiskDirectoryBackingStore
{
1569 TestBackingStore(const std::string
& dir_name
,
1570 const base::FilePath
& backing_filepath
);
1572 virtual ~TestBackingStore();
1574 virtual bool SaveChanges(const Directory::SaveChangesSnapshot
& snapshot
)
1577 void StartFailingSaveChanges() {
1578 fail_save_changes_
= true;
1582 bool fail_save_changes_
;
1585 TestBackingStore::TestBackingStore(const std::string
& dir_name
,
1586 const base::FilePath
& backing_filepath
)
1587 : OnDiskDirectoryBackingStore(dir_name
, backing_filepath
),
1588 fail_save_changes_(false) {
1591 TestBackingStore::~TestBackingStore() { }
1593 bool TestBackingStore::SaveChanges(
1594 const Directory::SaveChangesSnapshot
& snapshot
){
1595 if (fail_save_changes_
) {
1598 return OnDiskDirectoryBackingStore::SaveChanges(snapshot
);
1602 // A directory whose Save() function can be set to always fail.
1603 class TestDirectory
: public Directory
{
1605 // A factory function used to work around some initialization order issues.
1606 static TestDirectory
* Create(
1607 Encryptor
*encryptor
,
1608 UnrecoverableErrorHandler
*handler
,
1609 const std::string
& dir_name
,
1610 const base::FilePath
& backing_filepath
);
1612 virtual ~TestDirectory();
1614 void StartFailingSaveChanges() {
1615 backing_store_
->StartFailingSaveChanges();
1619 TestDirectory(Encryptor
* encryptor
,
1620 UnrecoverableErrorHandler
* handler
,
1621 TestBackingStore
* backing_store
);
1623 TestBackingStore
* backing_store_
;
1626 TestDirectory
* TestDirectory::Create(
1627 Encryptor
*encryptor
,
1628 UnrecoverableErrorHandler
*handler
,
1629 const std::string
& dir_name
,
1630 const base::FilePath
& backing_filepath
) {
1631 TestBackingStore
* backing_store
=
1632 new TestBackingStore(dir_name
, backing_filepath
);
1633 return new TestDirectory(encryptor
, handler
, backing_store
);
1636 TestDirectory::TestDirectory(Encryptor
* encryptor
,
1637 UnrecoverableErrorHandler
* handler
,
1638 TestBackingStore
* backing_store
)
1639 : Directory(backing_store
, handler
, NULL
, NULL
, NULL
),
1640 backing_store_(backing_store
) {
1643 TestDirectory::~TestDirectory() { }
1645 TEST(OnDiskSyncableDirectory
, FailInitialWrite
) {
1646 FakeEncryptor encryptor
;
1647 TestUnrecoverableErrorHandler handler
;
1648 base::ScopedTempDir temp_dir
;
1649 ASSERT_TRUE(temp_dir
.CreateUniqueTempDir());
1650 base::FilePath file_path
= temp_dir
.path().Append(
1651 FILE_PATH_LITERAL("Test.sqlite3"));
1652 std::string name
= "user@x.com";
1653 NullDirectoryChangeDelegate delegate
;
1655 scoped_ptr
<TestDirectory
> test_dir(
1656 TestDirectory::Create(&encryptor
, &handler
, name
, file_path
));
1658 test_dir
->StartFailingSaveChanges();
1659 ASSERT_EQ(FAILED_INITIAL_WRITE
, test_dir
->Open(name
, &delegate
,
1660 NullTransactionObserver()));
1663 // A variant of SyncableDirectoryTest that uses a real sqlite database.
1664 class OnDiskSyncableDirectoryTest
: public SyncableDirectoryTest
{
1666 // SetUp() is called before each test case is run.
1667 // The sqlite3 DB is deleted before each test is run.
1668 virtual void SetUp() {
1669 ASSERT_TRUE(temp_dir_
.CreateUniqueTempDir());
1670 file_path_
= temp_dir_
.path().Append(
1671 FILE_PATH_LITERAL("Test.sqlite3"));
1672 file_util::Delete(file_path_
, true);
1676 virtual void TearDown() {
1677 // This also closes file handles.
1678 dir_
->SaveChanges();
1680 file_util::Delete(file_path_
, true);
1683 // Creates a new directory. Deletes the old directory, if it exists.
1684 void CreateDirectory() {
1686 TestDirectory::Create(&encryptor_
, &handler_
, kName
, file_path_
);
1687 dir_
.reset(test_directory_
);
1688 ASSERT_TRUE(dir_
.get());
1689 ASSERT_EQ(OPENED
, dir_
->Open(kName
, &delegate_
,
1690 NullTransactionObserver()));
1691 ASSERT_TRUE(dir_
->good());
1694 void SaveAndReloadDir() {
1695 dir_
->SaveChanges();
1699 void StartFailingSaveChanges() {
1700 test_directory_
->StartFailingSaveChanges();
1703 TestDirectory
*test_directory_
; // mirrors scoped_ptr<Directory> dir_
1704 base::ScopedTempDir temp_dir_
;
1705 base::FilePath file_path_
;
1708 TEST_F(OnDiskSyncableDirectoryTest
, TestPurgeEntriesWithTypeIn
) {
1709 sync_pb::EntitySpecifics bookmark_specs
;
1710 sync_pb::EntitySpecifics autofill_specs
;
1711 sync_pb::EntitySpecifics preference_specs
;
1712 AddDefaultFieldValue(BOOKMARKS
, &bookmark_specs
);
1713 AddDefaultFieldValue(PREFERENCES
, &preference_specs
);
1714 AddDefaultFieldValue(AUTOFILL
, &autofill_specs
);
1716 ModelTypeSet
types_to_purge(PREFERENCES
, AUTOFILL
);
1718 TestIdFactory id_factory
;
1719 // Create some items for each type.
1721 WriteTransaction
trans(FROM_HERE
, UNITTEST
, dir_
.get());
1723 // Make it look like these types have completed initial sync.
1724 CreateTypeRoot(&trans
, dir_
.get(), BOOKMARKS
);
1725 CreateTypeRoot(&trans
, dir_
.get(), PREFERENCES
);
1726 CreateTypeRoot(&trans
, dir_
.get(), AUTOFILL
);
1728 // Add more nodes for this type. Technically, they should be placed under
1729 // the proper type root nodes but the assertions in this test won't notice
1730 // if their parent isn't quite right.
1731 MutableEntry
item1(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "Item");
1732 ASSERT_TRUE(item1
.good());
1733 item1
.Put(SERVER_SPECIFICS
, bookmark_specs
);
1734 item1
.Put(IS_UNSYNCED
, true);
1736 MutableEntry
item2(&trans
, CREATE_NEW_UPDATE_ITEM
,
1737 id_factory
.NewServerId());
1738 ASSERT_TRUE(item2
.good());
1739 item2
.Put(SERVER_SPECIFICS
, bookmark_specs
);
1740 item2
.Put(IS_UNAPPLIED_UPDATE
, true);
1742 MutableEntry
item3(&trans
, CREATE
, PREFERENCES
,
1743 trans
.root_id(), "Item");
1744 ASSERT_TRUE(item3
.good());
1745 item3
.Put(SPECIFICS
, preference_specs
);
1746 item3
.Put(SERVER_SPECIFICS
, preference_specs
);
1747 item3
.Put(IS_UNSYNCED
, true);
1749 MutableEntry
item4(&trans
, CREATE_NEW_UPDATE_ITEM
,
1750 id_factory
.NewServerId());
1751 ASSERT_TRUE(item4
.good());
1752 item4
.Put(SERVER_SPECIFICS
, preference_specs
);
1753 item4
.Put(IS_UNAPPLIED_UPDATE
, true);
1755 MutableEntry
item5(&trans
, CREATE
, AUTOFILL
,
1756 trans
.root_id(), "Item");
1757 ASSERT_TRUE(item5
.good());
1758 item5
.Put(SPECIFICS
, autofill_specs
);
1759 item5
.Put(SERVER_SPECIFICS
, autofill_specs
);
1760 item5
.Put(IS_UNSYNCED
, true);
1762 MutableEntry
item6(&trans
, CREATE_NEW_UPDATE_ITEM
,
1763 id_factory
.NewServerId());
1764 ASSERT_TRUE(item6
.good());
1765 item6
.Put(SERVER_SPECIFICS
, autofill_specs
);
1766 item6
.Put(IS_UNAPPLIED_UPDATE
, true);
1769 dir_
->SaveChanges();
1771 ReadTransaction
trans(FROM_HERE
, dir_
.get());
1772 MetahandleSet all_set
;
1773 GetAllMetaHandles(&trans
, &all_set
);
1774 ASSERT_EQ(10U, all_set
.size());
1777 dir_
->PurgeEntriesWithTypeIn(types_to_purge
, ModelTypeSet());
1779 // We first query the in-memory data, and then reload the directory (without
1780 // saving) to verify that disk does not still have the data.
1781 CheckPurgeEntriesWithTypeInSucceeded(types_to_purge
, true);
1783 CheckPurgeEntriesWithTypeInSucceeded(types_to_purge
, false);
1786 TEST_F(OnDiskSyncableDirectoryTest
, TestShareInfo
) {
1787 dir_
->set_store_birthday("Jan 31st");
1788 const char* const bag_of_chips_array
= "\0bag of chips";
1789 const std::string bag_of_chips_string
=
1790 std::string(bag_of_chips_array
, sizeof(bag_of_chips_array
));
1791 dir_
->set_bag_of_chips(bag_of_chips_string
);
1793 ReadTransaction
trans(FROM_HERE
, dir_
.get());
1794 EXPECT_EQ("Jan 31st", dir_
->store_birthday());
1795 EXPECT_EQ(bag_of_chips_string
, dir_
->bag_of_chips());
1797 dir_
->set_store_birthday("April 10th");
1798 const char* const bag_of_chips2_array
= "\0bag of chips2";
1799 const std::string bag_of_chips2_string
=
1800 std::string(bag_of_chips2_array
, sizeof(bag_of_chips2_array
));
1801 dir_
->set_bag_of_chips(bag_of_chips2_string
);
1802 dir_
->SaveChanges();
1804 ReadTransaction
trans(FROM_HERE
, dir_
.get());
1805 EXPECT_EQ("April 10th", dir_
->store_birthday());
1806 EXPECT_EQ(bag_of_chips2_string
, dir_
->bag_of_chips());
1808 const char* const bag_of_chips3_array
= "\0bag of chips3";
1809 const std::string bag_of_chips3_string
=
1810 std::string(bag_of_chips3_array
, sizeof(bag_of_chips3_array
));
1811 dir_
->set_bag_of_chips(bag_of_chips3_string
);
1812 // Restore the directory from disk. Make sure that nothing's changed.
1815 ReadTransaction
trans(FROM_HERE
, dir_
.get());
1816 EXPECT_EQ("April 10th", dir_
->store_birthday());
1817 EXPECT_EQ(bag_of_chips3_string
, dir_
->bag_of_chips());
1821 TEST_F(OnDiskSyncableDirectoryTest
,
1822 TestSimpleFieldsPreservedDuringSaveChanges
) {
1823 Id update_id
= TestIdFactory::FromNumber(1);
1825 EntryKernel create_pre_save
, update_pre_save
;
1826 EntryKernel create_post_save
, update_post_save
;
1827 std::string create_name
= "Create";
1830 WriteTransaction
trans(FROM_HERE
, UNITTEST
, dir_
.get());
1831 MutableEntry
create(
1832 &trans
, CREATE
, BOOKMARKS
, trans
.root_id(), create_name
);
1833 MutableEntry
update(&trans
, CREATE_NEW_UPDATE_ITEM
, update_id
);
1834 create
.Put(IS_UNSYNCED
, true);
1835 update
.Put(IS_UNAPPLIED_UPDATE
, true);
1836 sync_pb::EntitySpecifics specifics
;
1837 specifics
.mutable_bookmark()->set_favicon("PNG");
1838 specifics
.mutable_bookmark()->set_url("http://nowhere");
1839 create
.Put(SPECIFICS
, specifics
);
1840 update
.Put(SPECIFICS
, specifics
);
1841 create_pre_save
= create
.GetKernelCopy();
1842 update_pre_save
= update
.GetKernelCopy();
1843 create_id
= create
.Get(ID
);
1846 dir_
->SaveChanges();
1847 dir_
.reset(new Directory(new OnDiskDirectoryBackingStore(kName
, file_path_
),
1853 ASSERT_TRUE(dir_
.get());
1854 ASSERT_EQ(OPENED
, dir_
->Open(kName
, &delegate_
, NullTransactionObserver()));
1855 ASSERT_TRUE(dir_
->good());
1858 ReadTransaction
trans(FROM_HERE
, dir_
.get());
1859 Entry
create(&trans
, GET_BY_ID
, create_id
);
1860 EXPECT_EQ(1, CountEntriesWithName(&trans
, trans
.root_id(), create_name
));
1861 Entry
update(&trans
, GET_BY_ID
, update_id
);
1862 create_post_save
= create
.GetKernelCopy();
1863 update_post_save
= update
.GetKernelCopy();
1865 int i
= BEGIN_FIELDS
;
1866 for ( ; i
< INT64_FIELDS_END
; ++i
) {
1867 EXPECT_EQ(create_pre_save
.ref((Int64Field
)i
) +
1868 (i
== TRANSACTION_VERSION
? 1 : 0),
1869 create_post_save
.ref((Int64Field
)i
))
1870 << "int64 field #" << i
<< " changed during save/load";
1871 EXPECT_EQ(update_pre_save
.ref((Int64Field
)i
) +
1872 (i
== TRANSACTION_VERSION
? 1 : 0),
1873 update_post_save
.ref((Int64Field
)i
))
1874 << "int64 field #" << i
<< " changed during save/load";
1876 for ( ; i
< TIME_FIELDS_END
; ++i
) {
1877 EXPECT_EQ(create_pre_save
.ref((TimeField
)i
),
1878 create_post_save
.ref((TimeField
)i
))
1879 << "time field #" << i
<< " changed during save/load";
1880 EXPECT_EQ(update_pre_save
.ref((TimeField
)i
),
1881 update_post_save
.ref((TimeField
)i
))
1882 << "time field #" << i
<< " changed during save/load";
1884 for ( ; i
< ID_FIELDS_END
; ++i
) {
1885 EXPECT_EQ(create_pre_save
.ref((IdField
)i
),
1886 create_post_save
.ref((IdField
)i
))
1887 << "id field #" << i
<< " changed during save/load";
1888 EXPECT_EQ(update_pre_save
.ref((IdField
)i
),
1889 update_pre_save
.ref((IdField
)i
))
1890 << "id field #" << i
<< " changed during save/load";
1892 for ( ; i
< BIT_FIELDS_END
; ++i
) {
1893 EXPECT_EQ(create_pre_save
.ref((BitField
)i
),
1894 create_post_save
.ref((BitField
)i
))
1895 << "Bit field #" << i
<< " changed during save/load";
1896 EXPECT_EQ(update_pre_save
.ref((BitField
)i
),
1897 update_post_save
.ref((BitField
)i
))
1898 << "Bit field #" << i
<< " changed during save/load";
1900 for ( ; i
< STRING_FIELDS_END
; ++i
) {
1901 EXPECT_EQ(create_pre_save
.ref((StringField
)i
),
1902 create_post_save
.ref((StringField
)i
))
1903 << "String field #" << i
<< " changed during save/load";
1904 EXPECT_EQ(update_pre_save
.ref((StringField
)i
),
1905 update_post_save
.ref((StringField
)i
))
1906 << "String field #" << i
<< " changed during save/load";
1908 for ( ; i
< PROTO_FIELDS_END
; ++i
) {
1909 EXPECT_EQ(create_pre_save
.ref((ProtoField
)i
).SerializeAsString(),
1910 create_post_save
.ref((ProtoField
)i
).SerializeAsString())
1911 << "Blob field #" << i
<< " changed during save/load";
1912 EXPECT_EQ(update_pre_save
.ref((ProtoField
)i
).SerializeAsString(),
1913 update_post_save
.ref((ProtoField
)i
).SerializeAsString())
1914 << "Blob field #" << i
<< " changed during save/load";
1916 for ( ; i
< UNIQUE_POSITION_FIELDS_END
; ++i
) {
1917 EXPECT_TRUE(create_pre_save
.ref((UniquePositionField
)i
).Equals(
1918 create_post_save
.ref((UniquePositionField
)i
)))
1919 << "Position field #" << i
<< " changed during save/load";
1920 EXPECT_TRUE(update_pre_save
.ref((UniquePositionField
)i
).Equals(
1921 update_post_save
.ref((UniquePositionField
)i
)))
1922 << "Position field #" << i
<< " changed during save/load";
1926 TEST_F(OnDiskSyncableDirectoryTest
, TestSaveChangesFailure
) {
1928 // Set up an item using a regular, saveable directory.
1930 WriteTransaction
trans(FROM_HERE
, UNITTEST
, dir_
.get());
1932 MutableEntry
e1(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "aguilera");
1933 ASSERT_TRUE(e1
.good());
1934 EXPECT_TRUE(e1
.GetKernelCopy().is_dirty());
1935 handle1
= e1
.Get(META_HANDLE
);
1936 e1
.Put(BASE_VERSION
, 1);
1937 e1
.Put(IS_DIR
, true);
1938 e1
.Put(ID
, TestIdFactory::FromNumber(101));
1939 EXPECT_TRUE(e1
.GetKernelCopy().is_dirty());
1940 EXPECT_TRUE(IsInDirtyMetahandles(handle1
));
1942 ASSERT_TRUE(dir_
->SaveChanges());
1944 // Make sure the item is no longer dirty after saving,
1945 // and make a modification.
1947 WriteTransaction
trans(FROM_HERE
, UNITTEST
, dir_
.get());
1949 MutableEntry
aguilera(&trans
, GET_BY_HANDLE
, handle1
);
1950 ASSERT_TRUE(aguilera
.good());
1951 EXPECT_FALSE(aguilera
.GetKernelCopy().is_dirty());
1952 EXPECT_EQ(aguilera
.Get(NON_UNIQUE_NAME
), "aguilera");
1953 aguilera
.Put(NON_UNIQUE_NAME
, "overwritten");
1954 EXPECT_TRUE(aguilera
.GetKernelCopy().is_dirty());
1955 EXPECT_TRUE(IsInDirtyMetahandles(handle1
));
1957 ASSERT_TRUE(dir_
->SaveChanges());
1959 // Now do some operations when SaveChanges() will fail.
1960 StartFailingSaveChanges();
1961 ASSERT_TRUE(dir_
->good());
1965 WriteTransaction
trans(FROM_HERE
, UNITTEST
, dir_
.get());
1967 MutableEntry
aguilera(&trans
, GET_BY_HANDLE
, handle1
);
1968 ASSERT_TRUE(aguilera
.good());
1969 EXPECT_FALSE(aguilera
.GetKernelCopy().is_dirty());
1970 EXPECT_EQ(aguilera
.Get(NON_UNIQUE_NAME
), "overwritten");
1971 EXPECT_FALSE(aguilera
.GetKernelCopy().is_dirty());
1972 EXPECT_FALSE(IsInDirtyMetahandles(handle1
));
1973 aguilera
.Put(NON_UNIQUE_NAME
, "christina");
1974 EXPECT_TRUE(aguilera
.GetKernelCopy().is_dirty());
1975 EXPECT_TRUE(IsInDirtyMetahandles(handle1
));
1978 MutableEntry
kids_on_block(
1979 &trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "kids");
1980 ASSERT_TRUE(kids_on_block
.good());
1981 handle2
= kids_on_block
.Get(META_HANDLE
);
1982 kids_on_block
.Put(BASE_VERSION
, 1);
1983 kids_on_block
.Put(IS_DIR
, true);
1984 kids_on_block
.Put(ID
, TestIdFactory::FromNumber(102));
1985 EXPECT_TRUE(kids_on_block
.GetKernelCopy().is_dirty());
1986 EXPECT_TRUE(IsInDirtyMetahandles(handle2
));
1989 // We are using an unsaveable directory, so this can't succeed. However,
1990 // the HandleSaveChangesFailure code path should have been triggered.
1991 ASSERT_FALSE(dir_
->SaveChanges());
1993 // Make sure things were rolled back and the world is as it was before call.
1995 ReadTransaction
trans(FROM_HERE
, dir_
.get());
1996 Entry
e1(&trans
, GET_BY_HANDLE
, handle1
);
1997 ASSERT_TRUE(e1
.good());
1998 EntryKernel aguilera
= e1
.GetKernelCopy();
1999 Entry
kids(&trans
, GET_BY_HANDLE
, handle2
);
2000 ASSERT_TRUE(kids
.good());
2001 EXPECT_TRUE(kids
.GetKernelCopy().is_dirty());
2002 EXPECT_TRUE(IsInDirtyMetahandles(handle2
));
2003 EXPECT_TRUE(aguilera
.is_dirty());
2004 EXPECT_TRUE(IsInDirtyMetahandles(handle1
));
2008 TEST_F(OnDiskSyncableDirectoryTest
, TestSaveChangesFailureWithPurge
) {
2010 // Set up an item using a regular, saveable directory.
2012 WriteTransaction
trans(FROM_HERE
, UNITTEST
, dir_
.get());
2014 MutableEntry
e1(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), "aguilera");
2015 ASSERT_TRUE(e1
.good());
2016 EXPECT_TRUE(e1
.GetKernelCopy().is_dirty());
2017 handle1
= e1
.Get(META_HANDLE
);
2018 e1
.Put(BASE_VERSION
, 1);
2019 e1
.Put(IS_DIR
, true);
2020 e1
.Put(ID
, TestIdFactory::FromNumber(101));
2021 sync_pb::EntitySpecifics bookmark_specs
;
2022 AddDefaultFieldValue(BOOKMARKS
, &bookmark_specs
);
2023 e1
.Put(SPECIFICS
, bookmark_specs
);
2024 e1
.Put(SERVER_SPECIFICS
, bookmark_specs
);
2025 e1
.Put(ID
, TestIdFactory::FromNumber(101));
2026 EXPECT_TRUE(e1
.GetKernelCopy().is_dirty());
2027 EXPECT_TRUE(IsInDirtyMetahandles(handle1
));
2029 ASSERT_TRUE(dir_
->SaveChanges());
2031 // Now do some operations while SaveChanges() is set to fail.
2032 StartFailingSaveChanges();
2033 ASSERT_TRUE(dir_
->good());
2035 ModelTypeSet
set(BOOKMARKS
);
2036 dir_
->PurgeEntriesWithTypeIn(set
, ModelTypeSet());
2037 EXPECT_TRUE(IsInMetahandlesToPurge(handle1
));
2038 ASSERT_FALSE(dir_
->SaveChanges());
2039 EXPECT_TRUE(IsInMetahandlesToPurge(handle1
));
2044 void SyncableDirectoryTest::ValidateEntry(BaseTransaction
* trans
,
2047 const std::string
& name
,
2049 int64 server_version
,
2051 Entry
e(trans
, GET_BY_ID
, TestIdFactory::FromNumber(id
));
2052 ASSERT_TRUE(e
.good());
2054 ASSERT_TRUE(name
== e
.Get(NON_UNIQUE_NAME
));
2055 ASSERT_TRUE(base_version
== e
.Get(BASE_VERSION
));
2056 ASSERT_TRUE(server_version
== e
.Get(SERVER_VERSION
));
2057 ASSERT_TRUE(is_del
== e
.Get(IS_DEL
));
2060 DirOpenResult
SyncableDirectoryTest::SimulateSaveAndReloadDir() {
2061 if (!dir_
->SaveChanges())
2062 return FAILED_IN_UNITTEST
;
2064 return ReloadDirImpl();
2067 DirOpenResult
SyncableDirectoryTest::SimulateCrashAndReloadDir() {
2068 return ReloadDirImpl();
2071 DirOpenResult
SyncableDirectoryTest::ReloadDirImpl() {
2072 // Do some tricky things to preserve the backing store.
2073 DirectoryBackingStore
* saved_store
= dir_
->store_
.release();
2075 // Close the current directory.
2079 dir_
.reset(new Directory(saved_store
,
2084 DirOpenResult result
= dir_
->OpenImpl(kName
, &delegate_
,
2085 NullTransactionObserver());
2087 // If something went wrong, we need to clear this member. If we don't,
2088 // TearDown() will be guaranteed to crash when it calls SaveChanges().
2089 if (result
!= OPENED
)
2097 class SyncableDirectoryManagement
: public testing::Test
{
2099 virtual void SetUp() {
2100 ASSERT_TRUE(temp_dir_
.CreateUniqueTempDir());
2103 virtual void TearDown() {
2106 MessageLoop message_loop_
;
2107 base::ScopedTempDir temp_dir_
;
2108 FakeEncryptor encryptor_
;
2109 TestUnrecoverableErrorHandler handler_
;
2110 NullDirectoryChangeDelegate delegate_
;
2113 TEST_F(SyncableDirectoryManagement
, TestFileRelease
) {
2114 base::FilePath path
= temp_dir_
.path().Append(
2115 Directory::kSyncDatabaseFilename
);
2117 syncable::Directory
dir(new OnDiskDirectoryBackingStore("ScopeTest", path
),
2122 DirOpenResult result
=
2123 dir
.Open("ScopeTest", &delegate_
, NullTransactionObserver());
2124 ASSERT_EQ(result
, OPENED
);
2127 // Closing the directory should have released the backing database file.
2128 ASSERT_TRUE(file_util::Delete(path
, true));
2131 class StressTransactionsDelegate
: public base::PlatformThread::Delegate
{
2133 StressTransactionsDelegate(Directory
* dir
, int thread_number
)
2135 thread_number_(thread_number
) {}
2138 Directory
* const dir_
;
2139 const int thread_number_
;
2141 // PlatformThread::Delegate methods:
2142 virtual void ThreadMain() OVERRIDE
{
2143 int entry_count
= 0;
2144 std::string path_name
;
2146 for (int i
= 0; i
< 20; ++i
) {
2147 const int rand_action
= rand() % 10;
2148 if (rand_action
< 4 && !path_name
.empty()) {
2149 ReadTransaction
trans(FROM_HERE
, dir_
);
2150 CHECK(1 == CountEntriesWithName(&trans
, trans
.root_id(), path_name
));
2151 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(
2154 std::string unique_name
=
2155 base::StringPrintf("%d.%d", thread_number_
, entry_count
++);
2156 path_name
.assign(unique_name
.begin(), unique_name
.end());
2157 WriteTransaction
trans(FROM_HERE
, UNITTEST
, dir_
);
2158 MutableEntry
e(&trans
, CREATE
, BOOKMARKS
, trans
.root_id(), path_name
);
2160 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(
2162 e
.Put(IS_UNSYNCED
, true);
2163 if (e
.Put(ID
, TestIdFactory::FromNumber(rand())) &&
2164 e
.Get(ID
).ServerKnows() && !e
.Get(ID
).IsRoot()) {
2165 e
.Put(BASE_VERSION
, 1);
2171 DISALLOW_COPY_AND_ASSIGN(StressTransactionsDelegate
);
2174 TEST(SyncableDirectory
, StressTransactions
) {
2175 MessageLoop message_loop
;
2176 base::ScopedTempDir temp_dir
;
2177 ASSERT_TRUE(temp_dir
.CreateUniqueTempDir());
2178 FakeEncryptor encryptor
;
2179 TestUnrecoverableErrorHandler handler
;
2180 NullDirectoryChangeDelegate delegate
;
2181 std::string dirname
= "stress";
2182 Directory
dir(new InMemoryDirectoryBackingStore(dirname
),
2187 dir
.Open(dirname
, &delegate
, NullTransactionObserver());
2189 const int kThreadCount
= 7;
2190 base::PlatformThreadHandle threads
[kThreadCount
];
2191 scoped_ptr
<StressTransactionsDelegate
> thread_delegates
[kThreadCount
];
2193 for (int i
= 0; i
< kThreadCount
; ++i
) {
2194 thread_delegates
[i
].reset(new StressTransactionsDelegate(&dir
, i
));
2195 ASSERT_TRUE(base::PlatformThread::Create(
2196 0, thread_delegates
[i
].get(), &threads
[i
]));
2199 for (int i
= 0; i
< kThreadCount
; ++i
) {
2200 base::PlatformThread::Join(threads
[i
]);
2206 class SyncableClientTagTest
: public SyncableDirectoryTest
{
2208 static const int kBaseVersion
= 1;
2209 const char* test_name_
;
2210 const char* test_tag_
;
2212 SyncableClientTagTest() : test_name_("test_name"), test_tag_("dietcoke") {}
2214 bool CreateWithDefaultTag(Id id
, bool deleted
) {
2215 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, dir_
.get());
2216 MutableEntry
me(&wtrans
, CREATE
, PREFERENCES
,
2217 wtrans
.root_id(), test_name_
);
2220 if (id
.ServerKnows()) {
2221 me
.Put(BASE_VERSION
, kBaseVersion
);
2223 me
.Put(IS_UNSYNCED
, true);
2224 me
.Put(IS_DEL
, deleted
);
2225 me
.Put(IS_DIR
, false);
2226 return me
.Put(UNIQUE_CLIENT_TAG
, test_tag_
);
2229 // Verify an entry exists with the default tag.
2230 void VerifyTag(Id id
, bool deleted
) {
2231 // Should still be present and valid in the client tag index.
2232 ReadTransaction
trans(FROM_HERE
, dir_
.get());
2233 Entry
me(&trans
, GET_BY_CLIENT_TAG
, test_tag_
);
2235 EXPECT_EQ(me
.Get(ID
), id
);
2236 EXPECT_EQ(me
.Get(UNIQUE_CLIENT_TAG
), test_tag_
);
2237 EXPECT_EQ(me
.Get(IS_DEL
), deleted
);
2239 // We only sync deleted items that the server knew about.
2240 if (me
.Get(ID
).ServerKnows() || !me
.Get(IS_DEL
)) {
2241 EXPECT_EQ(me
.Get(IS_UNSYNCED
), true);
2246 TestIdFactory factory_
;
2249 TEST_F(SyncableClientTagTest
, TestClientTagClear
) {
2250 Id server_id
= factory_
.NewServerId();
2251 EXPECT_TRUE(CreateWithDefaultTag(server_id
, false));
2253 WriteTransaction
trans(FROM_HERE
, UNITTEST
, dir_
.get());
2254 MutableEntry
me(&trans
, GET_BY_CLIENT_TAG
, test_tag_
);
2255 EXPECT_TRUE(me
.good());
2256 me
.Put(UNIQUE_CLIENT_TAG
, std::string());
2259 ReadTransaction
trans(FROM_HERE
, dir_
.get());
2260 Entry
by_tag(&trans
, GET_BY_CLIENT_TAG
, test_tag_
);
2261 EXPECT_FALSE(by_tag
.good());
2263 Entry
by_id(&trans
, GET_BY_ID
, server_id
);
2264 EXPECT_TRUE(by_id
.good());
2265 EXPECT_TRUE(by_id
.Get(UNIQUE_CLIENT_TAG
).empty());
2269 TEST_F(SyncableClientTagTest
, TestClientTagIndexServerId
) {
2270 Id server_id
= factory_
.NewServerId();
2271 EXPECT_TRUE(CreateWithDefaultTag(server_id
, false));
2272 VerifyTag(server_id
, false);
2275 TEST_F(SyncableClientTagTest
, TestClientTagIndexClientId
) {
2276 Id client_id
= factory_
.NewLocalId();
2277 EXPECT_TRUE(CreateWithDefaultTag(client_id
, false));
2278 VerifyTag(client_id
, false);
2281 TEST_F(SyncableClientTagTest
, TestDeletedClientTagIndexClientId
) {
2282 Id client_id
= factory_
.NewLocalId();
2283 EXPECT_TRUE(CreateWithDefaultTag(client_id
, true));
2284 VerifyTag(client_id
, true);
2287 TEST_F(SyncableClientTagTest
, TestDeletedClientTagIndexServerId
) {
2288 Id server_id
= factory_
.NewServerId();
2289 EXPECT_TRUE(CreateWithDefaultTag(server_id
, true));
2290 VerifyTag(server_id
, true);
2293 TEST_F(SyncableClientTagTest
, TestClientTagIndexDuplicateServer
) {
2294 EXPECT_TRUE(CreateWithDefaultTag(factory_
.NewServerId(), true));
2295 EXPECT_FALSE(CreateWithDefaultTag(factory_
.NewServerId(), true));
2296 EXPECT_FALSE(CreateWithDefaultTag(factory_
.NewServerId(), false));
2297 EXPECT_FALSE(CreateWithDefaultTag(factory_
.NewLocalId(), false));
2298 EXPECT_FALSE(CreateWithDefaultTag(factory_
.NewLocalId(), true));
2302 } // namespace syncable
2303 } // namespace syncer