1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "sync/syncable/syncable.h"
9 #include "base/compiler_specific.h"
10 #include "base/file_path.h"
11 #include "base/file_util.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/scoped_temp_dir.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/engine/syncproto.h"
23 #include "sync/util/test_unrecoverable_error_handler.h"
24 #include "sync/syncable/directory_backing_store.h"
25 #include "sync/syncable/directory_change_delegate.h"
26 #include "sync/syncable/on_disk_directory_backing_store.h"
27 #include "sync/test/engine/test_id_factory.h"
28 #include "sync/test/engine/test_syncable_utils.h"
29 #include "sync/test/fake_encryptor.h"
30 #include "sync/test/null_directory_change_delegate.h"
31 #include "sync/test/null_transaction_observer.h"
32 #include "sync/protocol/bookmark_specifics.pb.h"
33 #include "testing/gtest/include/gtest/gtest.h"
35 using base::ExpectDictBooleanValue
;
36 using base::ExpectDictStringValue
;
37 using browser_sync::FakeEncryptor
;
38 using browser_sync::TestIdFactory
;
39 using browser_sync::TestUnrecoverableErrorHandler
;
43 class SyncableKernelTest
: public testing::Test
{};
45 // TODO(akalin): Add unit tests for EntryKernel::ContainsString().
47 TEST_F(SyncableKernelTest
, ToValue
) {
49 scoped_ptr
<DictionaryValue
> value(kernel
.ToValue());
51 // Not much to check without repeating the ToValue() code.
52 EXPECT_TRUE(value
->HasKey("isDirty"));
53 // The extra +2 is for "isDirty" and "serverModelType".
54 EXPECT_EQ(BIT_TEMPS_END
- BEGIN_FIELDS
+ 2,
55 static_cast<int>(value
->size()));
62 void PutDataAsBookmarkFavicon(WriteTransaction
* wtrans
,
65 size_t bytes_length
) {
66 sync_pb::EntitySpecifics specifics
;
67 specifics
.mutable_bookmark()->set_url("http://demo/");
68 specifics
.mutable_bookmark()->set_favicon(bytes
, bytes_length
);
69 e
->Put(SPECIFICS
, specifics
);
72 void ExpectDataFromBookmarkFaviconEquals(BaseTransaction
* trans
,
75 size_t bytes_length
) {
76 ASSERT_TRUE(e
->good());
77 ASSERT_TRUE(e
->Get(SPECIFICS
).has_bookmark());
78 ASSERT_EQ("http://demo/", e
->Get(SPECIFICS
).bookmark().url());
79 ASSERT_EQ(std::string(bytes
, bytes_length
),
80 e
->Get(SPECIFICS
).bookmark().favicon());
84 class SyncableGeneralTest
: public testing::Test
{
86 virtual void SetUp() {
87 ASSERT_TRUE(temp_dir_
.CreateUniqueTempDir());
88 db_path_
= temp_dir_
.path().Append(
89 FILE_PATH_LITERAL("SyncableTest.sqlite3"));
92 virtual void TearDown() {
95 MessageLoop message_loop_
;
96 ScopedTempDir temp_dir_
;
97 NullDirectoryChangeDelegate delegate_
;
98 FakeEncryptor encryptor_
;
99 TestUnrecoverableErrorHandler handler_
;
103 TEST_F(SyncableGeneralTest
, General
) {
104 Directory
dir(&encryptor_
, &handler_
, NULL
);
105 ASSERT_EQ(OPENED
, dir
.OpenInMemoryForTest(
106 "SimpleTest", &delegate_
, NullTransactionObserver()));
108 int64 root_metahandle
;
110 ReadTransaction
rtrans(FROM_HERE
, &dir
);
111 Entry
e(&rtrans
, GET_BY_ID
, rtrans
.root_id());
112 ASSERT_TRUE(e
.good());
113 root_metahandle
= e
.Get(META_HANDLE
);
116 int64 written_metahandle
;
117 const Id id
= TestIdFactory::FromNumber(99);
118 std::string name
= "Jeff";
119 // Test simple read operations on an empty DB.
121 ReadTransaction
rtrans(FROM_HERE
, &dir
);
122 Entry
e(&rtrans
, GET_BY_ID
, id
);
123 ASSERT_FALSE(e
.good()); // Hasn't been written yet.
125 Directory::ChildHandles child_handles
;
126 dir
.GetChildHandlesById(&rtrans
, rtrans
.root_id(), &child_handles
);
127 EXPECT_TRUE(child_handles
.empty());
129 dir
.GetChildHandlesByHandle(&rtrans
, root_metahandle
, &child_handles
);
130 EXPECT_TRUE(child_handles
.empty());
133 // Test creating a new meta entry.
135 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, &dir
);
136 MutableEntry
me(&wtrans
, CREATE
, wtrans
.root_id(), name
);
137 ASSERT_TRUE(me
.good());
139 me
.Put(BASE_VERSION
, 1);
140 written_metahandle
= me
.Get(META_HANDLE
);
143 // Test GetChildHandles* after something is now in the DB.
144 // Also check that GET_BY_ID works.
146 ReadTransaction
rtrans(FROM_HERE
, &dir
);
147 Entry
e(&rtrans
, GET_BY_ID
, id
);
148 ASSERT_TRUE(e
.good());
150 Directory::ChildHandles child_handles
;
151 dir
.GetChildHandlesById(&rtrans
, rtrans
.root_id(), &child_handles
);
152 EXPECT_EQ(1u, child_handles
.size());
154 for (Directory::ChildHandles::iterator i
= child_handles
.begin();
155 i
!= child_handles
.end(); ++i
) {
156 EXPECT_EQ(*i
, written_metahandle
);
159 dir
.GetChildHandlesByHandle(&rtrans
, root_metahandle
, &child_handles
);
160 EXPECT_EQ(1u, child_handles
.size());
162 for (Directory::ChildHandles::iterator i
= child_handles
.begin();
163 i
!= child_handles
.end(); ++i
) {
164 EXPECT_EQ(*i
, written_metahandle
);
168 // Test writing data to an entity. Also check that GET_BY_HANDLE works.
169 static const char s
[] = "Hello World.";
171 WriteTransaction
trans(FROM_HERE
, UNITTEST
, &dir
);
172 MutableEntry
e(&trans
, GET_BY_HANDLE
, written_metahandle
);
173 ASSERT_TRUE(e
.good());
174 PutDataAsBookmarkFavicon(&trans
, &e
, s
, sizeof(s
));
177 // Test reading back the contents that we just wrote.
179 WriteTransaction
trans(FROM_HERE
, UNITTEST
, &dir
);
180 MutableEntry
e(&trans
, GET_BY_HANDLE
, written_metahandle
);
181 ASSERT_TRUE(e
.good());
182 ExpectDataFromBookmarkFaviconEquals(&trans
, &e
, s
, sizeof(s
));
185 // Verify it exists in the folder.
187 ReadTransaction
rtrans(FROM_HERE
, &dir
);
188 EXPECT_EQ(1, CountEntriesWithName(&rtrans
, rtrans
.root_id(), name
));
193 WriteTransaction
trans(FROM_HERE
, UNITTEST
, &dir
);
194 MutableEntry
e(&trans
, GET_BY_HANDLE
, written_metahandle
);
197 EXPECT_EQ(0, CountEntriesWithName(&trans
, trans
.root_id(), name
));
203 TEST_F(SyncableGeneralTest
, ChildrenOps
) {
204 Directory
dir(&encryptor_
, &handler_
, NULL
);
205 ASSERT_EQ(OPENED
, dir
.OpenInMemoryForTest(
206 "SimpleTest", &delegate_
, NullTransactionObserver()));
208 int64 written_metahandle
;
209 const Id id
= TestIdFactory::FromNumber(99);
210 std::string name
= "Jeff";
212 ReadTransaction
rtrans(FROM_HERE
, &dir
);
213 Entry
e(&rtrans
, GET_BY_ID
, id
);
214 ASSERT_FALSE(e
.good()); // Hasn't been written yet.
216 EXPECT_FALSE(dir
.HasChildren(&rtrans
, rtrans
.root_id()));
218 EXPECT_TRUE(dir
.GetFirstChildId(&rtrans
, rtrans
.root_id(), &child_id
));
219 EXPECT_TRUE(child_id
.IsRoot());
223 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, &dir
);
224 MutableEntry
me(&wtrans
, CREATE
, wtrans
.root_id(), name
);
225 ASSERT_TRUE(me
.good());
227 me
.Put(BASE_VERSION
, 1);
228 written_metahandle
= me
.Get(META_HANDLE
);
231 // Test children ops after something is now in the DB.
233 ReadTransaction
rtrans(FROM_HERE
, &dir
);
234 Entry
e(&rtrans
, GET_BY_ID
, id
);
235 ASSERT_TRUE(e
.good());
237 Entry
child(&rtrans
, GET_BY_HANDLE
, written_metahandle
);
238 ASSERT_TRUE(child
.good());
240 EXPECT_TRUE(dir
.HasChildren(&rtrans
, rtrans
.root_id()));
242 EXPECT_TRUE(dir
.GetFirstChildId(&rtrans
, rtrans
.root_id(), &child_id
));
243 EXPECT_EQ(e
.Get(ID
), child_id
);
247 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, &dir
);
248 MutableEntry
me(&wtrans
, GET_BY_HANDLE
, written_metahandle
);
249 ASSERT_TRUE(me
.good());
250 me
.Put(IS_DEL
, true);
253 // Test children ops after the children have been deleted.
255 ReadTransaction
rtrans(FROM_HERE
, &dir
);
256 Entry
e(&rtrans
, GET_BY_ID
, id
);
257 ASSERT_TRUE(e
.good());
259 EXPECT_FALSE(dir
.HasChildren(&rtrans
, rtrans
.root_id()));
261 EXPECT_TRUE(dir
.GetFirstChildId(&rtrans
, rtrans
.root_id(), &child_id
));
262 EXPECT_TRUE(child_id
.IsRoot());
268 TEST_F(SyncableGeneralTest
, ClientIndexRebuildsProperly
) {
269 int64 written_metahandle
;
270 TestIdFactory factory
;
271 const Id id
= factory
.NewServerId();
272 std::string name
= "cheesepuffs";
273 std::string tag
= "dietcoke";
275 // Test creating a new meta entry.
277 Directory
dir(&encryptor_
, &handler_
, NULL
);
278 ASSERT_EQ(OPENED
, dir
.Open(db_path_
, "IndexTest", &delegate_
,
279 NullTransactionObserver()));
281 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, &dir
);
282 MutableEntry
me(&wtrans
, CREATE
, wtrans
.root_id(), name
);
283 ASSERT_TRUE(me
.good());
285 me
.Put(BASE_VERSION
, 1);
286 me
.Put(UNIQUE_CLIENT_TAG
, tag
);
287 written_metahandle
= me
.Get(META_HANDLE
);
292 // The DB was closed. Now reopen it. This will cause index regeneration.
294 Directory
dir(&encryptor_
, &handler_
, NULL
);
295 ASSERT_EQ(OPENED
, dir
.Open(db_path_
, "IndexTest",
296 &delegate_
, NullTransactionObserver()));
298 ReadTransaction
trans(FROM_HERE
, &dir
);
299 Entry
me(&trans
, GET_BY_CLIENT_TAG
, tag
);
300 ASSERT_TRUE(me
.good());
301 EXPECT_EQ(me
.Get(ID
), id
);
302 EXPECT_EQ(me
.Get(BASE_VERSION
), 1);
303 EXPECT_EQ(me
.Get(UNIQUE_CLIENT_TAG
), tag
);
304 EXPECT_EQ(me
.Get(META_HANDLE
), written_metahandle
);
308 TEST_F(SyncableGeneralTest
, ClientIndexRebuildsDeletedProperly
) {
309 TestIdFactory factory
;
310 const Id id
= factory
.NewServerId();
311 std::string tag
= "dietcoke";
313 // Test creating a deleted, unsynced, server meta entry.
315 Directory
dir(&encryptor_
, &handler_
, NULL
);
316 ASSERT_EQ(OPENED
, dir
.Open(db_path_
, "IndexTest", &delegate_
,
317 NullTransactionObserver()));
319 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, &dir
);
320 MutableEntry
me(&wtrans
, CREATE
, wtrans
.root_id(), "deleted");
321 ASSERT_TRUE(me
.good());
323 me
.Put(BASE_VERSION
, 1);
324 me
.Put(UNIQUE_CLIENT_TAG
, tag
);
325 me
.Put(IS_DEL
, true);
326 me
.Put(IS_UNSYNCED
, true); // Or it might be purged.
331 // The DB was closed. Now reopen it. This will cause index regeneration.
332 // Should still be present and valid in the client tag index.
334 Directory
dir(&encryptor_
, &handler_
, NULL
);
335 ASSERT_EQ(OPENED
, dir
.Open(db_path_
, "IndexTest", &delegate_
,
336 NullTransactionObserver()));
338 ReadTransaction
trans(FROM_HERE
, &dir
);
339 Entry
me(&trans
, GET_BY_CLIENT_TAG
, tag
);
340 ASSERT_TRUE(me
.good());
341 EXPECT_EQ(me
.Get(ID
), id
);
342 EXPECT_EQ(me
.Get(UNIQUE_CLIENT_TAG
), tag
);
343 EXPECT_TRUE(me
.Get(IS_DEL
));
344 EXPECT_TRUE(me
.Get(IS_UNSYNCED
));
348 TEST_F(SyncableGeneralTest
, ToValue
) {
349 Directory
dir(&encryptor_
, &handler_
, NULL
);
350 ASSERT_EQ(OPENED
, dir
.OpenInMemoryForTest(
351 "SimpleTest", &delegate_
, NullTransactionObserver()));
353 const Id id
= TestIdFactory::FromNumber(99);
355 ReadTransaction
rtrans(FROM_HERE
, &dir
);
356 Entry
e(&rtrans
, GET_BY_ID
, id
);
357 EXPECT_FALSE(e
.good()); // Hasn't been written yet.
359 scoped_ptr
<DictionaryValue
> value(e
.ToValue());
360 ExpectDictBooleanValue(false, *value
, "good");
361 EXPECT_EQ(1u, value
->size());
364 // Test creating a new meta entry.
366 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, &dir
);
367 MutableEntry
me(&wtrans
, CREATE
, wtrans
.root_id(), "new");
368 ASSERT_TRUE(me
.good());
370 me
.Put(BASE_VERSION
, 1);
372 scoped_ptr
<DictionaryValue
> value(me
.ToValue());
373 ExpectDictBooleanValue(true, *value
, "good");
374 EXPECT_TRUE(value
->HasKey("kernel"));
375 ExpectDictStringValue("Unspecified", *value
, "modelType");
376 ExpectDictBooleanValue(true, *value
, "existsOnClientBecauseNameIsNonEmpty");
377 ExpectDictBooleanValue(false, *value
, "isRoot");
383 // A Directory whose backing store always fails SaveChanges by returning false.
384 class TestUnsaveableDirectory
: public Directory
{
386 TestUnsaveableDirectory() : Directory(&encryptor_
, &handler_
, NULL
) {}
388 class UnsaveableBackingStore
: public OnDiskDirectoryBackingStore
{
390 UnsaveableBackingStore(const std::string
& dir_name
,
391 const FilePath
& backing_filepath
)
392 : OnDiskDirectoryBackingStore(dir_name
, backing_filepath
) { }
393 virtual bool SaveChanges(const Directory::SaveChangesSnapshot
& snapshot
) {
398 DirOpenResult
OpenUnsaveable(
399 const FilePath
& file_path
, const std::string
& name
,
400 DirectoryChangeDelegate
* delegate
,
401 const browser_sync::WeakHandle
<TransactionObserver
>&
402 transaction_observer
) {
403 DirectoryBackingStore
*store
= new UnsaveableBackingStore(name
, file_path
);
404 DirOpenResult result
=
405 OpenImpl(store
, name
, delegate
, transaction_observer
);
406 if (OPENED
!= result
)
412 FakeEncryptor encryptor_
;
413 TestUnrecoverableErrorHandler handler_
;
416 // A test fixture for syncable::Directory. Uses an in-memory database to keep
417 // the unit tests fast.
418 class SyncableDirectoryTest
: public testing::Test
{
420 MessageLoop message_loop_
;
421 static const char kName
[];
423 virtual void SetUp() {
424 dir_
.reset(new Directory(&encryptor_
, &handler_
, NULL
));
425 ASSERT_TRUE(dir_
.get());
426 ASSERT_EQ(OPENED
, dir_
->OpenInMemoryForTest(kName
, &delegate_
,
427 NullTransactionObserver()));
428 ASSERT_TRUE(dir_
->good());
431 virtual void TearDown() {
436 void GetAllMetaHandles(BaseTransaction
* trans
, MetahandleSet
* result
) {
437 dir_
->GetAllMetaHandles(trans
, result
);
440 bool IsInDirtyMetahandles(int64 metahandle
) {
441 return 1 == dir_
->kernel_
->dirty_metahandles
->count(metahandle
);
444 bool IsInMetahandlesToPurge(int64 metahandle
) {
445 return 1 == dir_
->kernel_
->metahandles_to_purge
->count(metahandle
);
448 void CheckPurgeEntriesWithTypeInSucceeded(ModelTypeSet types_to_purge
,
449 bool before_reload
) {
450 SCOPED_TRACE(testing::Message("Before reload: ") << before_reload
);
452 ReadTransaction
trans(FROM_HERE
, dir_
.get());
453 MetahandleSet all_set
;
454 dir_
->GetAllMetaHandles(&trans
, &all_set
);
455 EXPECT_EQ(3U, all_set
.size());
457 EXPECT_EQ(4U, dir_
->kernel_
->metahandles_to_purge
->size());
458 for (MetahandleSet::iterator iter
= all_set
.begin();
459 iter
!= all_set
.end(); ++iter
) {
460 Entry
e(&trans
, GET_BY_HANDLE
, *iter
);
461 const ModelType local_type
= e
.GetModelType();
462 const ModelType server_type
= e
.GetServerModelType();
464 // Note the dance around incrementing |it|, since we sometimes erase().
465 if ((IsRealDataType(local_type
) &&
466 types_to_purge
.Has(local_type
)) ||
467 (IsRealDataType(server_type
) &&
468 types_to_purge
.Has(server_type
))) {
469 FAIL() << "Illegal type should have been deleted.";
474 for (ModelTypeSet::Iterator it
= types_to_purge
.First();
475 it
.Good(); it
.Inc()) {
476 EXPECT_FALSE(dir_
->initial_sync_ended_for_type(it
.Get()));
478 EXPECT_FALSE(types_to_purge
.Has(BOOKMARKS
));
479 EXPECT_TRUE(dir_
->initial_sync_ended_for_type(BOOKMARKS
));
482 FakeEncryptor encryptor_
;
483 TestUnrecoverableErrorHandler handler_
;
484 scoped_ptr
<Directory
> dir_
;
485 NullDirectoryChangeDelegate delegate_
;
487 // Creates an empty entry and sets the ID field to a default one.
488 void CreateEntry(const std::string
& entryname
) {
489 CreateEntry(entryname
, TestIdFactory::FromNumber(-99));
492 // Creates an empty entry and sets the ID field to id.
493 void CreateEntry(const std::string
& entryname
, const int id
) {
494 CreateEntry(entryname
, TestIdFactory::FromNumber(id
));
496 void CreateEntry(const std::string
& entryname
, Id id
) {
497 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, dir_
.get());
498 MutableEntry
me(&wtrans
, CREATE
, wtrans
.root_id(), entryname
);
499 ASSERT_TRUE(me
.good());
501 me
.Put(IS_UNSYNCED
, true);
504 void ValidateEntry(BaseTransaction
* trans
,
507 const std::string
& name
,
509 int64 server_version
,
513 TEST_F(SyncableDirectoryTest
, TakeSnapshotGetsMetahandlesToPurge
) {
514 const int metas_to_create
= 50;
515 MetahandleSet expected_purges
;
516 MetahandleSet all_handles
;
518 WriteTransaction
trans(FROM_HERE
, UNITTEST
, dir_
.get());
519 for (int i
= 0; i
< metas_to_create
; i
++) {
520 MutableEntry
e(&trans
, CREATE
, trans
.root_id(), "foo");
521 e
.Put(IS_UNSYNCED
, true);
522 sync_pb::EntitySpecifics specs
;
524 AddDefaultFieldValue(BOOKMARKS
, &specs
);
525 expected_purges
.insert(e
.Get(META_HANDLE
));
526 all_handles
.insert(e
.Get(META_HANDLE
));
528 AddDefaultFieldValue(PREFERENCES
, &specs
);
529 all_handles
.insert(e
.Get(META_HANDLE
));
531 e
.Put(SPECIFICS
, specs
);
532 e
.Put(SERVER_SPECIFICS
, specs
);
536 syncable::ModelTypeSet
to_purge(BOOKMARKS
);
537 dir_
->PurgeEntriesWithTypeIn(to_purge
);
539 Directory::SaveChangesSnapshot snapshot1
;
540 base::AutoLock
scoped_lock(dir_
->kernel_
->save_changes_mutex
);
541 dir_
->TakeSnapshotForSaveChanges(&snapshot1
);
542 EXPECT_TRUE(expected_purges
== snapshot1
.metahandles_to_purge
);
545 to_purge
.Put(PREFERENCES
);
546 dir_
->PurgeEntriesWithTypeIn(to_purge
);
548 dir_
->HandleSaveChangesFailure(snapshot1
);
550 Directory::SaveChangesSnapshot snapshot2
;
551 dir_
->TakeSnapshotForSaveChanges(&snapshot2
);
552 EXPECT_TRUE(all_handles
== snapshot2
.metahandles_to_purge
);
555 TEST_F(SyncableDirectoryTest
, TakeSnapshotGetsAllDirtyHandlesTest
) {
556 const int metahandles_to_create
= 100;
557 std::vector
<int64
> expected_dirty_metahandles
;
559 WriteTransaction
trans(FROM_HERE
, UNITTEST
, dir_
.get());
560 for (int i
= 0; i
< metahandles_to_create
; i
++) {
561 MutableEntry
e(&trans
, CREATE
, trans
.root_id(), "foo");
562 expected_dirty_metahandles
.push_back(e
.Get(META_HANDLE
));
563 e
.Put(IS_UNSYNCED
, true);
566 // Fake SaveChanges() and make sure we got what we expected.
568 Directory::SaveChangesSnapshot snapshot
;
569 base::AutoLock
scoped_lock(dir_
->kernel_
->save_changes_mutex
);
570 dir_
->TakeSnapshotForSaveChanges(&snapshot
);
571 // Make sure there's an entry for each new metahandle. Make sure all
572 // entries are marked dirty.
573 ASSERT_EQ(expected_dirty_metahandles
.size(), snapshot
.dirty_metas
.size());
574 for (EntryKernelSet::const_iterator i
= snapshot
.dirty_metas
.begin();
575 i
!= snapshot
.dirty_metas
.end(); ++i
) {
576 ASSERT_TRUE(i
->is_dirty());
578 dir_
->VacuumAfterSaveChanges(snapshot
);
580 // Put a new value with existing transactions as well as adding new ones.
582 WriteTransaction
trans(FROM_HERE
, UNITTEST
, dir_
.get());
583 std::vector
<int64
> new_dirty_metahandles
;
584 for (std::vector
<int64
>::const_iterator i
=
585 expected_dirty_metahandles
.begin();
586 i
!= expected_dirty_metahandles
.end(); ++i
) {
587 // Change existing entries to directories to dirty them.
588 MutableEntry
e1(&trans
, GET_BY_HANDLE
, *i
);
589 e1
.Put(IS_DIR
, true);
590 e1
.Put(IS_UNSYNCED
, true);
592 MutableEntry
e2(&trans
, CREATE
, trans
.root_id(), "bar");
593 e2
.Put(IS_UNSYNCED
, true);
594 new_dirty_metahandles
.push_back(e2
.Get(META_HANDLE
));
596 expected_dirty_metahandles
.insert(expected_dirty_metahandles
.end(),
597 new_dirty_metahandles
.begin(), new_dirty_metahandles
.end());
599 // Fake SaveChanges() and make sure we got what we expected.
601 Directory::SaveChangesSnapshot snapshot
;
602 base::AutoLock
scoped_lock(dir_
->kernel_
->save_changes_mutex
);
603 dir_
->TakeSnapshotForSaveChanges(&snapshot
);
604 // Make sure there's an entry for each new metahandle. Make sure all
605 // entries are marked dirty.
606 EXPECT_EQ(expected_dirty_metahandles
.size(), snapshot
.dirty_metas
.size());
607 for (EntryKernelSet::const_iterator i
= snapshot
.dirty_metas
.begin();
608 i
!= snapshot
.dirty_metas
.end(); ++i
) {
609 EXPECT_TRUE(i
->is_dirty());
611 dir_
->VacuumAfterSaveChanges(snapshot
);
615 TEST_F(SyncableDirectoryTest
, TakeSnapshotGetsOnlyDirtyHandlesTest
) {
616 const int metahandles_to_create
= 100;
618 // half of 2 * metahandles_to_create
619 const unsigned int number_changed
= 100u;
620 std::vector
<int64
> expected_dirty_metahandles
;
622 WriteTransaction
trans(FROM_HERE
, UNITTEST
, dir_
.get());
623 for (int i
= 0; i
< metahandles_to_create
; i
++) {
624 MutableEntry
e(&trans
, CREATE
, trans
.root_id(), "foo");
625 expected_dirty_metahandles
.push_back(e
.Get(META_HANDLE
));
626 e
.Put(IS_UNSYNCED
, true);
630 // Put a new value with existing transactions as well as adding new ones.
632 WriteTransaction
trans(FROM_HERE
, UNITTEST
, dir_
.get());
633 std::vector
<int64
> new_dirty_metahandles
;
634 for (std::vector
<int64
>::const_iterator i
=
635 expected_dirty_metahandles
.begin();
636 i
!= expected_dirty_metahandles
.end(); ++i
) {
637 // Change existing entries to directories to dirty them.
638 MutableEntry
e1(&trans
, GET_BY_HANDLE
, *i
);
639 ASSERT_TRUE(e1
.good());
640 e1
.Put(IS_DIR
, true);
641 e1
.Put(IS_UNSYNCED
, true);
643 MutableEntry
e2(&trans
, CREATE
, trans
.root_id(), "bar");
644 e2
.Put(IS_UNSYNCED
, true);
645 new_dirty_metahandles
.push_back(e2
.Get(META_HANDLE
));
647 expected_dirty_metahandles
.insert(expected_dirty_metahandles
.end(),
648 new_dirty_metahandles
.begin(), new_dirty_metahandles
.end());
651 // Don't make any changes whatsoever and ensure nothing comes back.
653 WriteTransaction
trans(FROM_HERE
, UNITTEST
, dir_
.get());
654 for (std::vector
<int64
>::const_iterator i
=
655 expected_dirty_metahandles
.begin();
656 i
!= expected_dirty_metahandles
.end(); ++i
) {
657 MutableEntry
e(&trans
, GET_BY_HANDLE
, *i
);
658 ASSERT_TRUE(e
.good());
659 // We aren't doing anything to dirty these entries.
662 // Fake SaveChanges() and make sure we got what we expected.
664 Directory::SaveChangesSnapshot snapshot
;
665 base::AutoLock
scoped_lock(dir_
->kernel_
->save_changes_mutex
);
666 dir_
->TakeSnapshotForSaveChanges(&snapshot
);
667 // Make sure there are no dirty_metahandles.
668 EXPECT_EQ(0u, snapshot
.dirty_metas
.size());
669 dir_
->VacuumAfterSaveChanges(snapshot
);
672 WriteTransaction
trans(FROM_HERE
, UNITTEST
, dir_
.get());
673 bool should_change
= false;
674 for (std::vector
<int64
>::const_iterator i
=
675 expected_dirty_metahandles
.begin();
676 i
!= expected_dirty_metahandles
.end(); ++i
) {
677 // Maybe change entries by flipping IS_DIR.
678 MutableEntry
e(&trans
, GET_BY_HANDLE
, *i
);
679 ASSERT_TRUE(e
.good());
680 should_change
= !should_change
;
682 bool not_dir
= !e
.Get(IS_DIR
);
683 e
.Put(IS_DIR
, not_dir
);
684 e
.Put(IS_UNSYNCED
, true);
688 // Fake SaveChanges() and make sure we got what we expected.
690 Directory::SaveChangesSnapshot snapshot
;
691 base::AutoLock
scoped_lock(dir_
->kernel_
->save_changes_mutex
);
692 dir_
->TakeSnapshotForSaveChanges(&snapshot
);
693 // Make sure there's an entry for each changed metahandle. Make sure all
694 // entries are marked dirty.
695 EXPECT_EQ(number_changed
, snapshot
.dirty_metas
.size());
696 for (EntryKernelSet::const_iterator i
= snapshot
.dirty_metas
.begin();
697 i
!= snapshot
.dirty_metas
.end(); ++i
) {
698 EXPECT_TRUE(i
->is_dirty());
700 dir_
->VacuumAfterSaveChanges(snapshot
);
704 const char SyncableDirectoryTest::kName
[] = "Foo";
708 TEST_F(SyncableDirectoryTest
, TestBasicLookupNonExistantID
) {
709 ReadTransaction
rtrans(FROM_HERE
, dir_
.get());
710 Entry
e(&rtrans
, GET_BY_ID
, TestIdFactory::FromNumber(-99));
711 ASSERT_FALSE(e
.good());
714 TEST_F(SyncableDirectoryTest
, TestBasicLookupValidID
) {
716 ReadTransaction
rtrans(FROM_HERE
, dir_
.get());
717 Entry
e(&rtrans
, GET_BY_ID
, TestIdFactory::FromNumber(-99));
718 ASSERT_TRUE(e
.good());
721 TEST_F(SyncableDirectoryTest
, TestDelete
) {
722 std::string name
= "peanut butter jelly time";
723 WriteTransaction
trans(FROM_HERE
, UNITTEST
, dir_
.get());
724 MutableEntry
e1(&trans
, CREATE
, trans
.root_id(), name
);
725 ASSERT_TRUE(e1
.good());
726 ASSERT_TRUE(e1
.Put(IS_DEL
, true));
727 MutableEntry
e2(&trans
, CREATE
, trans
.root_id(), name
);
728 ASSERT_TRUE(e2
.good());
729 ASSERT_TRUE(e2
.Put(IS_DEL
, true));
730 MutableEntry
e3(&trans
, CREATE
, trans
.root_id(), name
);
731 ASSERT_TRUE(e3
.good());
732 ASSERT_TRUE(e3
.Put(IS_DEL
, true));
734 ASSERT_TRUE(e1
.Put(IS_DEL
, false));
735 ASSERT_TRUE(e2
.Put(IS_DEL
, false));
736 ASSERT_TRUE(e3
.Put(IS_DEL
, false));
738 ASSERT_TRUE(e1
.Put(IS_DEL
, true));
739 ASSERT_TRUE(e2
.Put(IS_DEL
, true));
740 ASSERT_TRUE(e3
.Put(IS_DEL
, true));
743 TEST_F(SyncableDirectoryTest
, TestGetUnsynced
) {
744 Directory::UnsyncedMetaHandles handles
;
745 int64 handle1
, handle2
;
747 WriteTransaction
trans(FROM_HERE
, UNITTEST
, dir_
.get());
749 dir_
->GetUnsyncedMetaHandles(&trans
, &handles
);
750 ASSERT_TRUE(0 == handles
.size());
752 MutableEntry
e1(&trans
, CREATE
, trans
.root_id(), "abba");
753 ASSERT_TRUE(e1
.good());
754 handle1
= e1
.Get(META_HANDLE
);
755 e1
.Put(BASE_VERSION
, 1);
756 e1
.Put(IS_DIR
, true);
757 e1
.Put(ID
, TestIdFactory::FromNumber(101));
759 MutableEntry
e2(&trans
, CREATE
, e1
.Get(ID
), "bread");
760 ASSERT_TRUE(e2
.good());
761 handle2
= e2
.Get(META_HANDLE
);
762 e2
.Put(BASE_VERSION
, 1);
763 e2
.Put(ID
, TestIdFactory::FromNumber(102));
767 WriteTransaction
trans(FROM_HERE
, UNITTEST
, dir_
.get());
769 dir_
->GetUnsyncedMetaHandles(&trans
, &handles
);
770 ASSERT_TRUE(0 == handles
.size());
772 MutableEntry
e3(&trans
, GET_BY_HANDLE
, handle1
);
773 ASSERT_TRUE(e3
.good());
774 e3
.Put(IS_UNSYNCED
, true);
778 WriteTransaction
trans(FROM_HERE
, UNITTEST
, dir_
.get());
779 dir_
->GetUnsyncedMetaHandles(&trans
, &handles
);
780 ASSERT_TRUE(1 == handles
.size());
781 ASSERT_TRUE(handle1
== handles
[0]);
783 MutableEntry
e4(&trans
, GET_BY_HANDLE
, handle2
);
784 ASSERT_TRUE(e4
.good());
785 e4
.Put(IS_UNSYNCED
, true);
789 WriteTransaction
trans(FROM_HERE
, UNITTEST
, dir_
.get());
790 dir_
->GetUnsyncedMetaHandles(&trans
, &handles
);
791 ASSERT_TRUE(2 == handles
.size());
792 if (handle1
== handles
[0]) {
793 ASSERT_TRUE(handle2
== handles
[1]);
795 ASSERT_TRUE(handle2
== handles
[0]);
796 ASSERT_TRUE(handle1
== handles
[1]);
799 MutableEntry
e5(&trans
, GET_BY_HANDLE
, handle1
);
800 ASSERT_TRUE(e5
.good());
801 ASSERT_TRUE(e5
.Get(IS_UNSYNCED
));
802 ASSERT_TRUE(e5
.Put(IS_UNSYNCED
, false));
803 ASSERT_FALSE(e5
.Get(IS_UNSYNCED
));
807 WriteTransaction
trans(FROM_HERE
, UNITTEST
, dir_
.get());
808 dir_
->GetUnsyncedMetaHandles(&trans
, &handles
);
809 ASSERT_TRUE(1 == handles
.size());
810 ASSERT_TRUE(handle2
== handles
[0]);
814 TEST_F(SyncableDirectoryTest
, TestGetUnappliedUpdates
) {
815 std::vector
<int64
> handles
;
816 int64 handle1
, handle2
;
817 const syncable::FullModelTypeSet all_types
=
818 syncable::FullModelTypeSet::All();
820 WriteTransaction
trans(FROM_HERE
, UNITTEST
, dir_
.get());
822 dir_
->GetUnappliedUpdateMetaHandles(&trans
, all_types
, &handles
);
823 ASSERT_TRUE(0 == handles
.size());
825 MutableEntry
e1(&trans
, CREATE
, trans
.root_id(), "abba");
826 ASSERT_TRUE(e1
.good());
827 handle1
= e1
.Get(META_HANDLE
);
828 e1
.Put(IS_UNAPPLIED_UPDATE
, false);
829 e1
.Put(BASE_VERSION
, 1);
830 e1
.Put(ID
, TestIdFactory::FromNumber(101));
831 e1
.Put(IS_DIR
, true);
833 MutableEntry
e2(&trans
, CREATE
, e1
.Get(ID
), "bread");
834 ASSERT_TRUE(e2
.good());
835 handle2
= e2
.Get(META_HANDLE
);
836 e2
.Put(IS_UNAPPLIED_UPDATE
, false);
837 e2
.Put(BASE_VERSION
, 1);
838 e2
.Put(ID
, TestIdFactory::FromNumber(102));
842 WriteTransaction
trans(FROM_HERE
, UNITTEST
, dir_
.get());
844 dir_
->GetUnappliedUpdateMetaHandles(&trans
, all_types
, &handles
);
845 ASSERT_TRUE(0 == handles
.size());
847 MutableEntry
e3(&trans
, GET_BY_HANDLE
, handle1
);
848 ASSERT_TRUE(e3
.good());
849 e3
.Put(IS_UNAPPLIED_UPDATE
, true);
853 WriteTransaction
trans(FROM_HERE
, UNITTEST
, dir_
.get());
854 dir_
->GetUnappliedUpdateMetaHandles(&trans
, all_types
, &handles
);
855 ASSERT_TRUE(1 == handles
.size());
856 ASSERT_TRUE(handle1
== handles
[0]);
858 MutableEntry
e4(&trans
, GET_BY_HANDLE
, handle2
);
859 ASSERT_TRUE(e4
.good());
860 e4
.Put(IS_UNAPPLIED_UPDATE
, true);
864 WriteTransaction
trans(FROM_HERE
, UNITTEST
, dir_
.get());
865 dir_
->GetUnappliedUpdateMetaHandles(&trans
, all_types
, &handles
);
866 ASSERT_TRUE(2 == handles
.size());
867 if (handle1
== handles
[0]) {
868 ASSERT_TRUE(handle2
== handles
[1]);
870 ASSERT_TRUE(handle2
== handles
[0]);
871 ASSERT_TRUE(handle1
== handles
[1]);
874 MutableEntry
e5(&trans
, GET_BY_HANDLE
, handle1
);
875 ASSERT_TRUE(e5
.good());
876 e5
.Put(IS_UNAPPLIED_UPDATE
, false);
880 WriteTransaction
trans(FROM_HERE
, UNITTEST
, dir_
.get());
881 dir_
->GetUnappliedUpdateMetaHandles(&trans
, all_types
, &handles
);
882 ASSERT_TRUE(1 == handles
.size());
883 ASSERT_TRUE(handle2
== handles
[0]);
888 TEST_F(SyncableDirectoryTest
, DeleteBug_531383
) {
889 // Try to evoke a check failure...
890 TestIdFactory id_factory
;
891 int64 grandchild_handle
;
893 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, dir_
.get());
894 MutableEntry
parent(&wtrans
, CREATE
, id_factory
.root(), "Bob");
895 ASSERT_TRUE(parent
.good());
896 parent
.Put(IS_DIR
, true);
897 parent
.Put(ID
, id_factory
.NewServerId());
898 parent
.Put(BASE_VERSION
, 1);
899 MutableEntry
child(&wtrans
, CREATE
, parent
.Get(ID
), "Bob");
900 ASSERT_TRUE(child
.good());
901 child
.Put(IS_DIR
, true);
902 child
.Put(ID
, id_factory
.NewServerId());
903 child
.Put(BASE_VERSION
, 1);
904 MutableEntry
grandchild(&wtrans
, CREATE
, child
.Get(ID
), "Bob");
905 ASSERT_TRUE(grandchild
.good());
906 grandchild
.Put(ID
, id_factory
.NewServerId());
907 grandchild
.Put(BASE_VERSION
, 1);
908 ASSERT_TRUE(grandchild
.Put(IS_DEL
, true));
909 MutableEntry
twin(&wtrans
, CREATE
, child
.Get(ID
), "Bob");
910 ASSERT_TRUE(twin
.good());
911 ASSERT_TRUE(twin
.Put(IS_DEL
, true));
912 ASSERT_TRUE(grandchild
.Put(IS_DEL
, false));
914 grandchild_handle
= grandchild
.Get(META_HANDLE
);
918 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, dir_
.get());
919 MutableEntry
grandchild(&wtrans
, GET_BY_HANDLE
, grandchild_handle
);
920 grandchild
.Put(IS_DEL
, true); // Used to CHECK fail here.
924 static inline bool IsLegalNewParent(const Entry
& a
, const Entry
& b
) {
925 return IsLegalNewParent(a
.trans(), a
.Get(ID
), b
.Get(ID
));
928 TEST_F(SyncableDirectoryTest
, TestIsLegalNewParent
) {
929 TestIdFactory id_factory
;
930 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, dir_
.get());
931 Entry
root(&wtrans
, GET_BY_ID
, id_factory
.root());
932 ASSERT_TRUE(root
.good());
933 MutableEntry
parent(&wtrans
, CREATE
, root
.Get(ID
), "Bob");
934 ASSERT_TRUE(parent
.good());
935 parent
.Put(IS_DIR
, true);
936 parent
.Put(ID
, id_factory
.NewServerId());
937 parent
.Put(BASE_VERSION
, 1);
938 MutableEntry
child(&wtrans
, CREATE
, parent
.Get(ID
), "Bob");
939 ASSERT_TRUE(child
.good());
940 child
.Put(IS_DIR
, true);
941 child
.Put(ID
, id_factory
.NewServerId());
942 child
.Put(BASE_VERSION
, 1);
943 MutableEntry
grandchild(&wtrans
, CREATE
, child
.Get(ID
), "Bob");
944 ASSERT_TRUE(grandchild
.good());
945 grandchild
.Put(ID
, id_factory
.NewServerId());
946 grandchild
.Put(BASE_VERSION
, 1);
948 MutableEntry
parent2(&wtrans
, CREATE
, root
.Get(ID
), "Pete");
949 ASSERT_TRUE(parent2
.good());
950 parent2
.Put(IS_DIR
, true);
951 parent2
.Put(ID
, id_factory
.NewServerId());
952 parent2
.Put(BASE_VERSION
, 1);
953 MutableEntry
child2(&wtrans
, CREATE
, parent2
.Get(ID
), "Pete");
954 ASSERT_TRUE(child2
.good());
955 child2
.Put(IS_DIR
, true);
956 child2
.Put(ID
, id_factory
.NewServerId());
957 child2
.Put(BASE_VERSION
, 1);
958 MutableEntry
grandchild2(&wtrans
, CREATE
, child2
.Get(ID
), "Pete");
959 ASSERT_TRUE(grandchild2
.good());
960 grandchild2
.Put(ID
, id_factory
.NewServerId());
961 grandchild2
.Put(BASE_VERSION
, 1);
969 // grandchild grandchild2
970 ASSERT_TRUE(IsLegalNewParent(child
, root
));
971 ASSERT_TRUE(IsLegalNewParent(child
, parent
));
972 ASSERT_FALSE(IsLegalNewParent(child
, child
));
973 ASSERT_FALSE(IsLegalNewParent(child
, grandchild
));
974 ASSERT_TRUE(IsLegalNewParent(child
, parent2
));
975 ASSERT_TRUE(IsLegalNewParent(child
, grandchild2
));
976 ASSERT_FALSE(IsLegalNewParent(parent
, grandchild
));
977 ASSERT_FALSE(IsLegalNewParent(root
, grandchild
));
978 ASSERT_FALSE(IsLegalNewParent(parent
, grandchild
));
981 TEST_F(SyncableDirectoryTest
, TestEntryIsInFolder
) {
982 // Create a subdir and an entry.
984 syncable::Id folder_id
;
985 syncable::Id entry_id
;
986 std::string entry_name
= "entry";
989 WriteTransaction
trans(FROM_HERE
, UNITTEST
, dir_
.get());
990 MutableEntry
folder(&trans
, CREATE
, trans
.root_id(), "folder");
991 ASSERT_TRUE(folder
.good());
992 EXPECT_TRUE(folder
.Put(IS_DIR
, true));
993 EXPECT_TRUE(folder
.Put(IS_UNSYNCED
, true));
994 folder_id
= folder
.Get(ID
);
996 MutableEntry
entry(&trans
, CREATE
, folder
.Get(ID
), entry_name
);
997 ASSERT_TRUE(entry
.good());
998 entry_handle
= entry
.Get(META_HANDLE
);
999 entry
.Put(IS_UNSYNCED
, true);
1000 entry_id
= entry
.Get(ID
);
1003 // Make sure we can find the entry in the folder.
1005 ReadTransaction
trans(FROM_HERE
, dir_
.get());
1006 EXPECT_EQ(0, CountEntriesWithName(&trans
, trans
.root_id(), entry_name
));
1007 EXPECT_EQ(1, CountEntriesWithName(&trans
, folder_id
, entry_name
));
1009 Entry
entry(&trans
, GET_BY_ID
, entry_id
);
1010 ASSERT_TRUE(entry
.good());
1011 EXPECT_EQ(entry_handle
, entry
.Get(META_HANDLE
));
1012 EXPECT_TRUE(entry
.Get(NON_UNIQUE_NAME
) == entry_name
);
1013 EXPECT_TRUE(entry
.Get(PARENT_ID
) == folder_id
);
1017 TEST_F(SyncableDirectoryTest
, TestParentIdIndexUpdate
) {
1018 std::string child_name
= "child";
1020 WriteTransaction
wt(FROM_HERE
, UNITTEST
, dir_
.get());
1021 MutableEntry
parent_folder(&wt
, CREATE
, wt
.root_id(), "folder1");
1022 parent_folder
.Put(IS_UNSYNCED
, true);
1023 EXPECT_TRUE(parent_folder
.Put(IS_DIR
, true));
1025 MutableEntry
parent_folder2(&wt
, CREATE
, wt
.root_id(), "folder2");
1026 parent_folder2
.Put(IS_UNSYNCED
, true);
1027 EXPECT_TRUE(parent_folder2
.Put(IS_DIR
, true));
1029 MutableEntry
child(&wt
, CREATE
, parent_folder
.Get(ID
), child_name
);
1030 EXPECT_TRUE(child
.Put(IS_DIR
, true));
1031 child
.Put(IS_UNSYNCED
, true);
1033 ASSERT_TRUE(child
.good());
1035 EXPECT_EQ(0, CountEntriesWithName(&wt
, wt
.root_id(), child_name
));
1036 EXPECT_EQ(parent_folder
.Get(ID
), child
.Get(PARENT_ID
));
1037 EXPECT_EQ(1, CountEntriesWithName(&wt
, parent_folder
.Get(ID
), child_name
));
1038 EXPECT_EQ(0, CountEntriesWithName(&wt
, parent_folder2
.Get(ID
), child_name
));
1039 child
.Put(PARENT_ID
, parent_folder2
.Get(ID
));
1040 EXPECT_EQ(parent_folder2
.Get(ID
), child
.Get(PARENT_ID
));
1041 EXPECT_EQ(0, CountEntriesWithName(&wt
, parent_folder
.Get(ID
), child_name
));
1042 EXPECT_EQ(1, CountEntriesWithName(&wt
, parent_folder2
.Get(ID
), child_name
));
1045 TEST_F(SyncableDirectoryTest
, TestNoReindexDeletedItems
) {
1046 std::string folder_name
= "folder";
1047 std::string new_name
= "new_name";
1049 WriteTransaction
trans(FROM_HERE
, UNITTEST
, dir_
.get());
1050 MutableEntry
folder(&trans
, CREATE
, trans
.root_id(), folder_name
);
1051 ASSERT_TRUE(folder
.good());
1052 ASSERT_TRUE(folder
.Put(IS_DIR
, true));
1053 ASSERT_TRUE(folder
.Put(IS_DEL
, true));
1055 EXPECT_EQ(0, CountEntriesWithName(&trans
, trans
.root_id(), folder_name
));
1057 MutableEntry
deleted(&trans
, GET_BY_ID
, folder
.Get(ID
));
1058 ASSERT_TRUE(deleted
.good());
1059 ASSERT_TRUE(deleted
.Put(PARENT_ID
, trans
.root_id()));
1060 ASSERT_TRUE(deleted
.Put(NON_UNIQUE_NAME
, new_name
));
1062 EXPECT_EQ(0, CountEntriesWithName(&trans
, trans
.root_id(), folder_name
));
1063 EXPECT_EQ(0, CountEntriesWithName(&trans
, trans
.root_id(), new_name
));
1066 TEST_F(SyncableDirectoryTest
, TestCaseChangeRename
) {
1067 WriteTransaction
trans(FROM_HERE
, UNITTEST
, dir_
.get());
1068 MutableEntry
folder(&trans
, CREATE
, trans
.root_id(), "CaseChange");
1069 ASSERT_TRUE(folder
.good());
1070 EXPECT_TRUE(folder
.Put(PARENT_ID
, trans
.root_id()));
1071 EXPECT_TRUE(folder
.Put(NON_UNIQUE_NAME
, "CASECHANGE"));
1072 EXPECT_TRUE(folder
.Put(IS_DEL
, true));
1075 // Create items of each model type, and check that GetModelType and
1076 // GetServerModelType return the right value.
1077 TEST_F(SyncableDirectoryTest
, GetModelType
) {
1078 TestIdFactory id_factory
;
1079 for (int i
= 0; i
< MODEL_TYPE_COUNT
; ++i
) {
1080 ModelType datatype
= ModelTypeFromInt(i
);
1081 SCOPED_TRACE(testing::Message("Testing model type ") << datatype
);
1084 case TOP_LEVEL_FOLDER
:
1085 continue; // Datatype isn't a function of Specifics.
1089 sync_pb::EntitySpecifics specifics
;
1090 AddDefaultFieldValue(datatype
, &specifics
);
1092 WriteTransaction
trans(FROM_HERE
, UNITTEST
, dir_
.get());
1094 MutableEntry
folder(&trans
, CREATE
, trans
.root_id(), "Folder");
1095 ASSERT_TRUE(folder
.good());
1096 folder
.Put(ID
, id_factory
.NewServerId());
1097 folder
.Put(SPECIFICS
, specifics
);
1098 folder
.Put(BASE_VERSION
, 1);
1099 folder
.Put(IS_DIR
, true);
1100 folder
.Put(IS_DEL
, false);
1101 ASSERT_EQ(datatype
, folder
.GetModelType());
1103 MutableEntry
item(&trans
, CREATE
, trans
.root_id(), "Item");
1104 ASSERT_TRUE(item
.good());
1105 item
.Put(ID
, id_factory
.NewServerId());
1106 item
.Put(SPECIFICS
, specifics
);
1107 item
.Put(BASE_VERSION
, 1);
1108 item
.Put(IS_DIR
, false);
1109 item
.Put(IS_DEL
, false);
1110 ASSERT_EQ(datatype
, item
.GetModelType());
1112 // It's critical that deletion records retain their datatype, so that
1113 // they can be dispatched to the appropriate change processor.
1114 MutableEntry
deleted_item(&trans
, CREATE
, trans
.root_id(), "Deleted Item");
1115 ASSERT_TRUE(item
.good());
1116 deleted_item
.Put(ID
, id_factory
.NewServerId());
1117 deleted_item
.Put(SPECIFICS
, specifics
);
1118 deleted_item
.Put(BASE_VERSION
, 1);
1119 deleted_item
.Put(IS_DIR
, false);
1120 deleted_item
.Put(IS_DEL
, true);
1121 ASSERT_EQ(datatype
, deleted_item
.GetModelType());
1123 MutableEntry
server_folder(&trans
, CREATE_NEW_UPDATE_ITEM
,
1124 id_factory
.NewServerId());
1125 ASSERT_TRUE(server_folder
.good());
1126 server_folder
.Put(SERVER_SPECIFICS
, specifics
);
1127 server_folder
.Put(BASE_VERSION
, 1);
1128 server_folder
.Put(SERVER_IS_DIR
, true);
1129 server_folder
.Put(SERVER_IS_DEL
, false);
1130 ASSERT_EQ(datatype
, server_folder
.GetServerModelType());
1132 MutableEntry
server_item(&trans
, CREATE_NEW_UPDATE_ITEM
,
1133 id_factory
.NewServerId());
1134 ASSERT_TRUE(server_item
.good());
1135 server_item
.Put(SERVER_SPECIFICS
, specifics
);
1136 server_item
.Put(BASE_VERSION
, 1);
1137 server_item
.Put(SERVER_IS_DIR
, false);
1138 server_item
.Put(SERVER_IS_DEL
, false);
1139 ASSERT_EQ(datatype
, server_item
.GetServerModelType());
1141 browser_sync::SyncEntity folder_entity
;
1142 folder_entity
.set_id(id_factory
.NewServerId());
1143 folder_entity
.set_deleted(false);
1144 folder_entity
.set_folder(true);
1145 folder_entity
.mutable_specifics()->CopyFrom(specifics
);
1146 ASSERT_EQ(datatype
, folder_entity
.GetModelType());
1148 browser_sync::SyncEntity item_entity
;
1149 item_entity
.set_id(id_factory
.NewServerId());
1150 item_entity
.set_deleted(false);
1151 item_entity
.set_folder(false);
1152 item_entity
.mutable_specifics()->CopyFrom(specifics
);
1153 ASSERT_EQ(datatype
, item_entity
.GetModelType());
1157 // A variant of SyncableDirectoryTest that uses a real sqlite database.
1158 class OnDiskSyncableDirectoryTest
: public SyncableDirectoryTest
{
1160 // SetUp() is called before each test case is run.
1161 // The sqlite3 DB is deleted before each test is run.
1162 virtual void SetUp() {
1163 ASSERT_TRUE(temp_dir_
.CreateUniqueTempDir());
1164 file_path_
= temp_dir_
.path().Append(
1165 FILE_PATH_LITERAL("Test.sqlite3"));
1166 file_util::Delete(file_path_
, true);
1167 dir_
.reset(new Directory(&encryptor_
, &handler_
, NULL
));
1168 ASSERT_TRUE(dir_
.get());
1169 ASSERT_EQ(OPENED
, dir_
->Open(file_path_
, kName
,
1170 &delegate_
, NullTransactionObserver()));
1171 ASSERT_TRUE(dir_
->good());
1174 virtual void TearDown() {
1175 // This also closes file handles.
1176 dir_
->SaveChanges();
1178 file_util::Delete(file_path_
, true);
1182 dir_
.reset(new Directory(&encryptor_
, &handler_
, NULL
));
1183 ASSERT_TRUE(dir_
.get());
1184 ASSERT_EQ(OPENED
, dir_
->Open(file_path_
, kName
,
1185 &delegate_
, NullTransactionObserver()));
1188 void SaveAndReloadDir() {
1189 dir_
->SaveChanges();
1193 void SwapInUnsaveableDirectory() {
1194 dir_
.reset(); // Delete the old directory.
1196 // We first assign the object to a pointer of type TestUnsaveableDirectory
1197 // because the OpenUnsaveable function is not available in the parent class.
1198 scoped_ptr
<TestUnsaveableDirectory
> dir(new TestUnsaveableDirectory());
1199 ASSERT_TRUE(dir
.get());
1200 ASSERT_EQ(OPENED
, dir
->OpenUnsaveable(
1201 file_path_
, kName
, &delegate_
, NullTransactionObserver()));
1203 // Finally, move the unsaveable directory to the dir_ variable.
1207 ScopedTempDir temp_dir_
;
1208 FilePath file_path_
;
1211 TEST_F(OnDiskSyncableDirectoryTest
, TestPurgeEntriesWithTypeIn
) {
1212 sync_pb::EntitySpecifics bookmark_specs
;
1213 sync_pb::EntitySpecifics autofill_specs
;
1214 sync_pb::EntitySpecifics preference_specs
;
1215 AddDefaultFieldValue(BOOKMARKS
, &bookmark_specs
);
1216 AddDefaultFieldValue(PREFERENCES
, &preference_specs
);
1217 AddDefaultFieldValue(AUTOFILL
, &autofill_specs
);
1218 dir_
->set_initial_sync_ended_for_type(BOOKMARKS
, true);
1219 dir_
->set_initial_sync_ended_for_type(PREFERENCES
, true);
1220 dir_
->set_initial_sync_ended_for_type(AUTOFILL
, true);
1222 syncable::ModelTypeSet
types_to_purge(PREFERENCES
, AUTOFILL
);
1224 TestIdFactory id_factory
;
1225 // Create some items for each type.
1227 WriteTransaction
trans(FROM_HERE
, UNITTEST
, dir_
.get());
1228 MutableEntry
item1(&trans
, CREATE
, trans
.root_id(), "Item");
1229 ASSERT_TRUE(item1
.good());
1230 item1
.Put(SPECIFICS
, bookmark_specs
);
1231 item1
.Put(SERVER_SPECIFICS
, bookmark_specs
);
1232 item1
.Put(IS_UNSYNCED
, true);
1234 MutableEntry
item2(&trans
, CREATE_NEW_UPDATE_ITEM
,
1235 id_factory
.NewServerId());
1236 ASSERT_TRUE(item2
.good());
1237 item2
.Put(SERVER_SPECIFICS
, bookmark_specs
);
1238 item2
.Put(IS_UNAPPLIED_UPDATE
, true);
1240 MutableEntry
item3(&trans
, CREATE
, trans
.root_id(), "Item");
1241 ASSERT_TRUE(item3
.good());
1242 item3
.Put(SPECIFICS
, preference_specs
);
1243 item3
.Put(SERVER_SPECIFICS
, preference_specs
);
1244 item3
.Put(IS_UNSYNCED
, true);
1246 MutableEntry
item4(&trans
, CREATE_NEW_UPDATE_ITEM
,
1247 id_factory
.NewServerId());
1248 ASSERT_TRUE(item4
.good());
1249 item4
.Put(SERVER_SPECIFICS
, preference_specs
);
1250 item4
.Put(IS_UNAPPLIED_UPDATE
, true);
1252 MutableEntry
item5(&trans
, CREATE
, trans
.root_id(), "Item");
1253 ASSERT_TRUE(item5
.good());
1254 item5
.Put(SPECIFICS
, autofill_specs
);
1255 item5
.Put(SERVER_SPECIFICS
, autofill_specs
);
1256 item5
.Put(IS_UNSYNCED
, true);
1258 MutableEntry
item6(&trans
, CREATE_NEW_UPDATE_ITEM
,
1259 id_factory
.NewServerId());
1260 ASSERT_TRUE(item6
.good());
1261 item6
.Put(SERVER_SPECIFICS
, autofill_specs
);
1262 item6
.Put(IS_UNAPPLIED_UPDATE
, true);
1265 dir_
->SaveChanges();
1267 ReadTransaction
trans(FROM_HERE
, dir_
.get());
1268 MetahandleSet all_set
;
1269 GetAllMetaHandles(&trans
, &all_set
);
1270 ASSERT_EQ(7U, all_set
.size());
1273 dir_
->PurgeEntriesWithTypeIn(types_to_purge
);
1275 // We first query the in-memory data, and then reload the directory (without
1276 // saving) to verify that disk does not still have the data.
1277 CheckPurgeEntriesWithTypeInSucceeded(types_to_purge
, true);
1279 CheckPurgeEntriesWithTypeInSucceeded(types_to_purge
, false);
1282 TEST_F(OnDiskSyncableDirectoryTest
, TestShareInfo
) {
1283 dir_
->set_initial_sync_ended_for_type(AUTOFILL
, true);
1284 dir_
->set_store_birthday("Jan 31st");
1285 dir_
->SetNotificationState("notification_state");
1287 ReadTransaction
trans(FROM_HERE
, dir_
.get());
1288 EXPECT_TRUE(dir_
->initial_sync_ended_for_type(AUTOFILL
));
1289 EXPECT_FALSE(dir_
->initial_sync_ended_for_type(BOOKMARKS
));
1290 EXPECT_EQ("Jan 31st", dir_
->store_birthday());
1291 EXPECT_EQ("notification_state", dir_
->GetNotificationState());
1293 dir_
->set_store_birthday("April 10th");
1294 dir_
->SetNotificationState("notification_state2");
1295 dir_
->SaveChanges();
1297 ReadTransaction
trans(FROM_HERE
, dir_
.get());
1298 EXPECT_TRUE(dir_
->initial_sync_ended_for_type(AUTOFILL
));
1299 EXPECT_FALSE(dir_
->initial_sync_ended_for_type(BOOKMARKS
));
1300 EXPECT_EQ("April 10th", dir_
->store_birthday());
1301 EXPECT_EQ("notification_state2", dir_
->GetNotificationState());
1303 dir_
->SetNotificationState("notification_state2");
1304 // Restore the directory from disk. Make sure that nothing's changed.
1307 ReadTransaction
trans(FROM_HERE
, dir_
.get());
1308 EXPECT_TRUE(dir_
->initial_sync_ended_for_type(AUTOFILL
));
1309 EXPECT_FALSE(dir_
->initial_sync_ended_for_type(BOOKMARKS
));
1310 EXPECT_EQ("April 10th", dir_
->store_birthday());
1311 EXPECT_EQ("notification_state2", dir_
->GetNotificationState());
1315 TEST_F(OnDiskSyncableDirectoryTest
,
1316 TestSimpleFieldsPreservedDuringSaveChanges
) {
1317 Id update_id
= TestIdFactory::FromNumber(1);
1319 EntryKernel create_pre_save
, update_pre_save
;
1320 EntryKernel create_post_save
, update_post_save
;
1321 std::string create_name
= "Create";
1324 WriteTransaction
trans(FROM_HERE
, UNITTEST
, dir_
.get());
1325 MutableEntry
create(&trans
, CREATE
, trans
.root_id(), create_name
);
1326 MutableEntry
update(&trans
, CREATE_NEW_UPDATE_ITEM
, update_id
);
1327 create
.Put(IS_UNSYNCED
, true);
1328 update
.Put(IS_UNAPPLIED_UPDATE
, true);
1329 sync_pb::EntitySpecifics specifics
;
1330 specifics
.mutable_bookmark()->set_favicon("PNG");
1331 specifics
.mutable_bookmark()->set_url("http://nowhere");
1332 create
.Put(SPECIFICS
, specifics
);
1333 create_pre_save
= create
.GetKernelCopy();
1334 update_pre_save
= update
.GetKernelCopy();
1335 create_id
= create
.Get(ID
);
1338 dir_
->SaveChanges();
1339 dir_
.reset(new Directory(&encryptor_
, &handler_
, NULL
));
1340 ASSERT_TRUE(dir_
.get());
1341 ASSERT_EQ(OPENED
, dir_
->Open(file_path_
, kName
,
1342 &delegate_
, NullTransactionObserver()));
1343 ASSERT_TRUE(dir_
->good());
1346 ReadTransaction
trans(FROM_HERE
, dir_
.get());
1347 Entry
create(&trans
, GET_BY_ID
, create_id
);
1348 EXPECT_EQ(1, CountEntriesWithName(&trans
, trans
.root_id(), create_name
));
1349 Entry
update(&trans
, GET_BY_ID
, update_id
);
1350 create_post_save
= create
.GetKernelCopy();
1351 update_post_save
= update
.GetKernelCopy();
1353 int i
= BEGIN_FIELDS
;
1354 for ( ; i
< INT64_FIELDS_END
; ++i
) {
1355 EXPECT_EQ(create_pre_save
.ref((Int64Field
)i
),
1356 create_post_save
.ref((Int64Field
)i
))
1357 << "int64 field #" << i
<< " changed during save/load";
1358 EXPECT_EQ(update_pre_save
.ref((Int64Field
)i
),
1359 update_post_save
.ref((Int64Field
)i
))
1360 << "int64 field #" << i
<< " changed during save/load";
1362 for ( ; i
< TIME_FIELDS_END
; ++i
) {
1363 EXPECT_EQ(create_pre_save
.ref((TimeField
)i
),
1364 create_post_save
.ref((TimeField
)i
))
1365 << "time field #" << i
<< " changed during save/load";
1366 EXPECT_EQ(update_pre_save
.ref((TimeField
)i
),
1367 update_post_save
.ref((TimeField
)i
))
1368 << "time field #" << i
<< " changed during save/load";
1370 for ( ; i
< ID_FIELDS_END
; ++i
) {
1371 EXPECT_EQ(create_pre_save
.ref((IdField
)i
),
1372 create_post_save
.ref((IdField
)i
))
1373 << "id field #" << i
<< " changed during save/load";
1374 EXPECT_EQ(update_pre_save
.ref((IdField
)i
),
1375 update_pre_save
.ref((IdField
)i
))
1376 << "id field #" << i
<< " changed during save/load";
1378 for ( ; i
< BIT_FIELDS_END
; ++i
) {
1379 EXPECT_EQ(create_pre_save
.ref((BitField
)i
),
1380 create_post_save
.ref((BitField
)i
))
1381 << "Bit field #" << i
<< " changed during save/load";
1382 EXPECT_EQ(update_pre_save
.ref((BitField
)i
),
1383 update_post_save
.ref((BitField
)i
))
1384 << "Bit field #" << i
<< " changed during save/load";
1386 for ( ; i
< STRING_FIELDS_END
; ++i
) {
1387 EXPECT_EQ(create_pre_save
.ref((StringField
)i
),
1388 create_post_save
.ref((StringField
)i
))
1389 << "String field #" << i
<< " changed during save/load";
1390 EXPECT_EQ(update_pre_save
.ref((StringField
)i
),
1391 update_post_save
.ref((StringField
)i
))
1392 << "String field #" << i
<< " changed during save/load";
1394 for ( ; i
< PROTO_FIELDS_END
; ++i
) {
1395 EXPECT_EQ(create_pre_save
.ref((ProtoField
)i
).SerializeAsString(),
1396 create_post_save
.ref((ProtoField
)i
).SerializeAsString())
1397 << "Blob field #" << i
<< " changed during save/load";
1398 EXPECT_EQ(update_pre_save
.ref((ProtoField
)i
).SerializeAsString(),
1399 update_post_save
.ref((ProtoField
)i
).SerializeAsString())
1400 << "Blob field #" << i
<< " changed during save/load";
1404 TEST_F(OnDiskSyncableDirectoryTest
, TestSaveChangesFailure
) {
1406 // Set up an item using a regular, saveable directory.
1408 WriteTransaction
trans(FROM_HERE
, UNITTEST
, dir_
.get());
1410 MutableEntry
e1(&trans
, CREATE
, trans
.root_id(), "aguilera");
1411 ASSERT_TRUE(e1
.good());
1412 EXPECT_TRUE(e1
.GetKernelCopy().is_dirty());
1413 handle1
= e1
.Get(META_HANDLE
);
1414 e1
.Put(BASE_VERSION
, 1);
1415 e1
.Put(IS_DIR
, true);
1416 e1
.Put(ID
, TestIdFactory::FromNumber(101));
1417 EXPECT_TRUE(e1
.GetKernelCopy().is_dirty());
1418 EXPECT_TRUE(IsInDirtyMetahandles(handle1
));
1420 ASSERT_TRUE(dir_
->SaveChanges());
1422 // Make sure the item is no longer dirty after saving,
1423 // and make a modification.
1425 WriteTransaction
trans(FROM_HERE
, UNITTEST
, dir_
.get());
1427 MutableEntry
aguilera(&trans
, GET_BY_HANDLE
, handle1
);
1428 ASSERT_TRUE(aguilera
.good());
1429 EXPECT_FALSE(aguilera
.GetKernelCopy().is_dirty());
1430 EXPECT_EQ(aguilera
.Get(NON_UNIQUE_NAME
), "aguilera");
1431 aguilera
.Put(NON_UNIQUE_NAME
, "overwritten");
1432 EXPECT_TRUE(aguilera
.GetKernelCopy().is_dirty());
1433 EXPECT_TRUE(IsInDirtyMetahandles(handle1
));
1435 ASSERT_TRUE(dir_
->SaveChanges());
1437 // Now do some operations using a directory for which SaveChanges will
1439 SwapInUnsaveableDirectory();
1440 ASSERT_TRUE(dir_
->good());
1444 WriteTransaction
trans(FROM_HERE
, UNITTEST
, dir_
.get());
1446 MutableEntry
aguilera(&trans
, GET_BY_HANDLE
, handle1
);
1447 ASSERT_TRUE(aguilera
.good());
1448 EXPECT_FALSE(aguilera
.GetKernelCopy().is_dirty());
1449 EXPECT_EQ(aguilera
.Get(NON_UNIQUE_NAME
), "overwritten");
1450 EXPECT_FALSE(aguilera
.GetKernelCopy().is_dirty());
1451 EXPECT_FALSE(IsInDirtyMetahandles(handle1
));
1452 aguilera
.Put(NON_UNIQUE_NAME
, "christina");
1453 EXPECT_TRUE(aguilera
.GetKernelCopy().is_dirty());
1454 EXPECT_TRUE(IsInDirtyMetahandles(handle1
));
1457 MutableEntry
kids_on_block(&trans
, CREATE
, trans
.root_id(), "kids");
1458 ASSERT_TRUE(kids_on_block
.good());
1459 handle2
= kids_on_block
.Get(META_HANDLE
);
1460 kids_on_block
.Put(BASE_VERSION
, 1);
1461 kids_on_block
.Put(IS_DIR
, true);
1462 kids_on_block
.Put(ID
, TestIdFactory::FromNumber(102));
1463 EXPECT_TRUE(kids_on_block
.GetKernelCopy().is_dirty());
1464 EXPECT_TRUE(IsInDirtyMetahandles(handle2
));
1467 // We are using an unsaveable directory, so this can't succeed. However,
1468 // the HandleSaveChangesFailure code path should have been triggered.
1469 ASSERT_FALSE(dir_
->SaveChanges());
1471 // Make sure things were rolled back and the world is as it was before call.
1473 ReadTransaction
trans(FROM_HERE
, dir_
.get());
1474 Entry
e1(&trans
, GET_BY_HANDLE
, handle1
);
1475 ASSERT_TRUE(e1
.good());
1476 EntryKernel aguilera
= e1
.GetKernelCopy();
1477 Entry
kids(&trans
, GET_BY_HANDLE
, handle2
);
1478 ASSERT_TRUE(kids
.good());
1479 EXPECT_TRUE(kids
.GetKernelCopy().is_dirty());
1480 EXPECT_TRUE(IsInDirtyMetahandles(handle2
));
1481 EXPECT_TRUE(aguilera
.is_dirty());
1482 EXPECT_TRUE(IsInDirtyMetahandles(handle1
));
1486 TEST_F(OnDiskSyncableDirectoryTest
, TestSaveChangesFailureWithPurge
) {
1488 // Set up an item using a regular, saveable directory.
1490 WriteTransaction
trans(FROM_HERE
, UNITTEST
, dir_
.get());
1492 MutableEntry
e1(&trans
, CREATE
, trans
.root_id(), "aguilera");
1493 ASSERT_TRUE(e1
.good());
1494 EXPECT_TRUE(e1
.GetKernelCopy().is_dirty());
1495 handle1
= e1
.Get(META_HANDLE
);
1496 e1
.Put(BASE_VERSION
, 1);
1497 e1
.Put(IS_DIR
, true);
1498 e1
.Put(ID
, TestIdFactory::FromNumber(101));
1499 sync_pb::EntitySpecifics bookmark_specs
;
1500 AddDefaultFieldValue(BOOKMARKS
, &bookmark_specs
);
1501 e1
.Put(SPECIFICS
, bookmark_specs
);
1502 e1
.Put(SERVER_SPECIFICS
, bookmark_specs
);
1503 e1
.Put(ID
, TestIdFactory::FromNumber(101));
1504 EXPECT_TRUE(e1
.GetKernelCopy().is_dirty());
1505 EXPECT_TRUE(IsInDirtyMetahandles(handle1
));
1507 ASSERT_TRUE(dir_
->SaveChanges());
1509 // Now do some operations using a directory for which SaveChanges will
1511 SwapInUnsaveableDirectory();
1512 ASSERT_TRUE(dir_
->good());
1514 syncable::ModelTypeSet
set(BOOKMARKS
);
1515 dir_
->PurgeEntriesWithTypeIn(set
);
1516 EXPECT_TRUE(IsInMetahandlesToPurge(handle1
));
1517 ASSERT_FALSE(dir_
->SaveChanges());
1518 EXPECT_TRUE(IsInMetahandlesToPurge(handle1
));
1523 void SyncableDirectoryTest::ValidateEntry(BaseTransaction
* trans
,
1526 const std::string
& name
,
1528 int64 server_version
,
1530 Entry
e(trans
, GET_BY_ID
, TestIdFactory::FromNumber(id
));
1531 ASSERT_TRUE(e
.good());
1533 ASSERT_TRUE(name
== e
.Get(NON_UNIQUE_NAME
));
1534 ASSERT_TRUE(base_version
== e
.Get(BASE_VERSION
));
1535 ASSERT_TRUE(server_version
== e
.Get(SERVER_VERSION
));
1536 ASSERT_TRUE(is_del
== e
.Get(IS_DEL
));
1541 class SyncableDirectoryManagement
: public testing::Test
{
1543 virtual void SetUp() {
1544 ASSERT_TRUE(temp_dir_
.CreateUniqueTempDir());
1547 virtual void TearDown() {
1550 MessageLoop message_loop_
;
1551 ScopedTempDir temp_dir_
;
1552 FakeEncryptor encryptor_
;
1553 TestUnrecoverableErrorHandler handler_
;
1554 NullDirectoryChangeDelegate delegate_
;
1557 TEST_F(SyncableDirectoryManagement
, TestFileRelease
) {
1558 FilePath path
= temp_dir_
.path().Append(
1559 Directory::kSyncDatabaseFilename
);
1561 syncable::Directory
dir(&encryptor_
, &handler_
, NULL
);
1562 DirOpenResult result
=
1563 dir
.Open(path
, "ScopeTest", &delegate_
, NullTransactionObserver());
1564 ASSERT_EQ(result
, OPENED
);
1567 // Closing the directory should have released the backing database file.
1568 ASSERT_TRUE(file_util::Delete(path
, true));
1571 class StressTransactionsDelegate
: public base::PlatformThread::Delegate
{
1573 StressTransactionsDelegate(Directory
* dir
, int thread_number
)
1575 thread_number_(thread_number
) {}
1578 Directory
* const dir_
;
1579 const int thread_number_
;
1581 // PlatformThread::Delegate methods:
1582 virtual void ThreadMain() {
1583 int entry_count
= 0;
1584 std::string path_name
;
1586 for (int i
= 0; i
< 20; ++i
) {
1587 const int rand_action
= rand() % 10;
1588 if (rand_action
< 4 && !path_name
.empty()) {
1589 ReadTransaction
trans(FROM_HERE
, dir_
);
1590 CHECK(1 == CountEntriesWithName(&trans
, trans
.root_id(), path_name
));
1591 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(
1594 std::string unique_name
=
1595 base::StringPrintf("%d.%d", thread_number_
, entry_count
++);
1596 path_name
.assign(unique_name
.begin(), unique_name
.end());
1597 WriteTransaction
trans(FROM_HERE
, UNITTEST
, dir_
);
1598 MutableEntry
e(&trans
, CREATE
, trans
.root_id(), path_name
);
1600 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(
1602 e
.Put(IS_UNSYNCED
, true);
1603 if (e
.Put(ID
, TestIdFactory::FromNumber(rand())) &&
1604 e
.Get(ID
).ServerKnows() && !e
.Get(ID
).IsRoot()) {
1605 e
.Put(BASE_VERSION
, 1);
1611 DISALLOW_COPY_AND_ASSIGN(StressTransactionsDelegate
);
1614 TEST(SyncableDirectory
, StressTransactions
) {
1615 MessageLoop message_loop
;
1616 ScopedTempDir temp_dir
;
1617 ASSERT_TRUE(temp_dir
.CreateUniqueTempDir());
1618 FakeEncryptor encryptor
;
1619 TestUnrecoverableErrorHandler handler
;
1620 NullDirectoryChangeDelegate delegate
;
1621 Directory
dir(&encryptor
, &handler
, NULL
);
1622 FilePath path
= temp_dir
.path().Append(Directory::kSyncDatabaseFilename
);
1623 file_util::Delete(path
, true);
1624 std::string dirname
= "stress";
1625 dir
.Open(path
, dirname
, &delegate
, NullTransactionObserver());
1627 const int kThreadCount
= 7;
1628 base::PlatformThreadHandle threads
[kThreadCount
];
1629 scoped_ptr
<StressTransactionsDelegate
> thread_delegates
[kThreadCount
];
1631 for (int i
= 0; i
< kThreadCount
; ++i
) {
1632 thread_delegates
[i
].reset(new StressTransactionsDelegate(&dir
, i
));
1633 ASSERT_TRUE(base::PlatformThread::Create(
1634 0, thread_delegates
[i
].get(), &threads
[i
]));
1637 for (int i
= 0; i
< kThreadCount
; ++i
) {
1638 base::PlatformThread::Join(threads
[i
]);
1642 file_util::Delete(path
, true);
1645 class SyncableClientTagTest
: public SyncableDirectoryTest
{
1647 static const int kBaseVersion
= 1;
1648 const char* test_name_
;
1649 const char* test_tag_
;
1651 SyncableClientTagTest() : test_name_("test_name"), test_tag_("dietcoke") {}
1653 bool CreateWithDefaultTag(Id id
, bool deleted
) {
1654 return CreateWithTag(test_tag_
, id
, deleted
);
1657 // Attempt to create an entry with a default tag.
1658 bool CreateWithTag(const char* tag
, Id id
, bool deleted
) {
1659 WriteTransaction
wtrans(FROM_HERE
, UNITTEST
, dir_
.get());
1660 MutableEntry
me(&wtrans
, CREATE
, wtrans
.root_id(), test_name_
);
1663 if (id
.ServerKnows()) {
1664 me
.Put(BASE_VERSION
, kBaseVersion
);
1666 me
.Put(IS_DEL
, deleted
);
1667 me
.Put(IS_UNSYNCED
, true);
1668 me
.Put(IS_DIR
, false);
1669 return me
.Put(UNIQUE_CLIENT_TAG
, tag
);
1672 // Verify an entry exists with the default tag.
1673 void VerifyTag(Id id
, bool deleted
) {
1674 // Should still be present and valid in the client tag index.
1675 ReadTransaction
trans(FROM_HERE
, dir_
.get());
1676 Entry
me(&trans
, GET_BY_CLIENT_TAG
, test_tag_
);
1678 EXPECT_EQ(me
.Get(ID
), id
);
1679 EXPECT_EQ(me
.Get(UNIQUE_CLIENT_TAG
), test_tag_
);
1680 EXPECT_EQ(me
.Get(IS_DEL
), deleted
);
1681 EXPECT_EQ(me
.Get(IS_UNSYNCED
), true);
1685 TestIdFactory factory_
;
1688 TEST_F(SyncableClientTagTest
, TestClientTagClear
) {
1689 Id server_id
= factory_
.NewServerId();
1690 EXPECT_TRUE(CreateWithDefaultTag(server_id
, false));
1692 WriteTransaction
trans(FROM_HERE
, UNITTEST
, dir_
.get());
1693 MutableEntry
me(&trans
, GET_BY_CLIENT_TAG
, test_tag_
);
1694 EXPECT_TRUE(me
.good());
1695 me
.Put(UNIQUE_CLIENT_TAG
, "");
1698 ReadTransaction
trans(FROM_HERE
, dir_
.get());
1699 Entry
by_tag(&trans
, GET_BY_CLIENT_TAG
, test_tag_
);
1700 EXPECT_FALSE(by_tag
.good());
1702 Entry
by_id(&trans
, GET_BY_ID
, server_id
);
1703 EXPECT_TRUE(by_id
.good());
1704 EXPECT_TRUE(by_id
.Get(UNIQUE_CLIENT_TAG
).empty());
1708 TEST_F(SyncableClientTagTest
, TestClientTagIndexServerId
) {
1709 Id server_id
= factory_
.NewServerId();
1710 EXPECT_TRUE(CreateWithDefaultTag(server_id
, false));
1711 VerifyTag(server_id
, false);
1714 TEST_F(SyncableClientTagTest
, TestClientTagIndexClientId
) {
1715 Id client_id
= factory_
.NewLocalId();
1716 EXPECT_TRUE(CreateWithDefaultTag(client_id
, false));
1717 VerifyTag(client_id
, false);
1720 TEST_F(SyncableClientTagTest
, TestDeletedClientTagIndexClientId
) {
1721 Id client_id
= factory_
.NewLocalId();
1722 EXPECT_TRUE(CreateWithDefaultTag(client_id
, true));
1723 VerifyTag(client_id
, true);
1726 TEST_F(SyncableClientTagTest
, TestDeletedClientTagIndexServerId
) {
1727 Id server_id
= factory_
.NewServerId();
1728 EXPECT_TRUE(CreateWithDefaultTag(server_id
, true));
1729 VerifyTag(server_id
, true);
1732 TEST_F(SyncableClientTagTest
, TestClientTagIndexDuplicateServer
) {
1733 EXPECT_TRUE(CreateWithDefaultTag(factory_
.NewServerId(), true));
1734 EXPECT_FALSE(CreateWithDefaultTag(factory_
.NewServerId(), true));
1735 EXPECT_FALSE(CreateWithDefaultTag(factory_
.NewServerId(), false));
1736 EXPECT_FALSE(CreateWithDefaultTag(factory_
.NewLocalId(), false));
1737 EXPECT_FALSE(CreateWithDefaultTag(factory_
.NewLocalId(), true));
1741 } // namespace syncable