Improves keyboard-only navigation of the Uber Settings page.
[chromium-blink-merge.git] / sync / syncable / syncable_unittest.cc
blobc4f87017cde2fd112ca0c69fe665c710c753ba56
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"
7 #include <string>
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;
41 namespace syncable {
43 class SyncableKernelTest : public testing::Test {};
45 // TODO(akalin): Add unit tests for EntryKernel::ContainsString().
47 TEST_F(SyncableKernelTest, ToValue) {
48 EntryKernel kernel;
49 scoped_ptr<DictionaryValue> value(kernel.ToValue());
50 if (value.get()) {
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()));
56 } else {
57 ADD_FAILURE();
61 namespace {
62 void PutDataAsBookmarkFavicon(WriteTransaction* wtrans,
63 MutableEntry* e,
64 const char* bytes,
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,
73 Entry* e,
74 const char* bytes,
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());
82 } // namespace
84 class SyncableGeneralTest : public testing::Test {
85 public:
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() {
94 protected:
95 MessageLoop message_loop_;
96 ScopedTempDir temp_dir_;
97 NullDirectoryChangeDelegate delegate_;
98 FakeEncryptor encryptor_;
99 TestUnrecoverableErrorHandler handler_;
100 FilePath db_path_;
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());
138 me.Put(ID, id);
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));
191 // Now delete it.
193 WriteTransaction trans(FROM_HERE, UNITTEST, &dir);
194 MutableEntry e(&trans, GET_BY_HANDLE, written_metahandle);
195 e.Put(IS_DEL, true);
197 EXPECT_EQ(0, CountEntriesWithName(&trans, trans.root_id(), name));
200 dir.SaveChanges();
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()));
217 Id child_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());
226 me.Put(ID, id);
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()));
241 Id child_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()));
260 Id child_id;
261 EXPECT_TRUE(dir.GetFirstChildId(&rtrans, rtrans.root_id(), &child_id));
262 EXPECT_TRUE(child_id.IsRoot());
265 dir.SaveChanges();
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());
284 me.Put(ID, id);
285 me.Put(BASE_VERSION, 1);
286 me.Put(UNIQUE_CLIENT_TAG, tag);
287 written_metahandle = me.Get(META_HANDLE);
289 dir.SaveChanges();
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());
322 me.Put(ID, id);
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.
328 dir.SaveChanges();
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());
369 me.Put(ID, id);
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");
380 dir.SaveChanges();
383 // A Directory whose backing store always fails SaveChanges by returning false.
384 class TestUnsaveableDirectory : public Directory {
385 public:
386 TestUnsaveableDirectory() : Directory(&encryptor_, &handler_, NULL) {}
388 class UnsaveableBackingStore : public OnDiskDirectoryBackingStore {
389 public:
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) {
394 return false;
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)
407 Close();
408 return result;
411 private:
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 {
419 protected:
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() {
432 dir_->SaveChanges();
433 dir_.reset();
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());
456 if (before_reload)
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());
500 me.Put(ID, id);
501 me.Put(IS_UNSYNCED, true);
504 void ValidateEntry(BaseTransaction* trans,
505 int64 id,
506 bool check_name,
507 const std::string& name,
508 int64 base_version,
509 int64 server_version,
510 bool is_del);
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;
523 if (i % 2 == 0) {
524 AddDefaultFieldValue(BOOKMARKS, &specs);
525 expected_purges.insert(e.Get(META_HANDLE));
526 all_handles.insert(e.Get(META_HANDLE));
527 } else {
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);
544 to_purge.Clear();
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);
591 // Add new entries
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);
629 dir_->SaveChanges();
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);
642 // Add new entries
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());
650 dir_->SaveChanges();
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;
681 if (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";
706 namespace {
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) {
715 CreateEntry("rtc");
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));
765 dir_->SaveChanges();
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);
776 dir_->SaveChanges();
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);
787 dir_->SaveChanges();
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]);
794 } else {
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));
805 dir_->SaveChanges();
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));
840 dir_->SaveChanges();
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);
851 dir_->SaveChanges();
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);
862 dir_->SaveChanges();
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]);
869 } else {
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);
878 dir_->SaveChanges();
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);
916 dir_->SaveChanges();
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);
962 // resulting tree
963 // root
964 // / |
965 // parent parent2
966 // | |
967 // child child2
968 // | |
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.
983 int64 entry_handle;
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);
1082 switch (datatype) {
1083 case UNSPECIFIED:
1084 case TOP_LEVEL_FOLDER:
1085 continue; // Datatype isn't a function of Specifics.
1086 default:
1087 break;
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 {
1159 protected:
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();
1177 dir_.reset();
1178 file_util::Delete(file_path_, true);
1181 void ReloadDir() {
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();
1190 ReloadDir();
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.
1204 dir_ = dir.Pass();
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);
1278 SaveAndReloadDir();
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.
1305 SaveAndReloadDir();
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);
1318 Id create_id;
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) {
1405 int64 handle1 = 0;
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
1438 // always fail.
1439 SwapInUnsaveableDirectory();
1440 ASSERT_TRUE(dir_->good());
1442 int64 handle2 = 0;
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));
1456 // New item.
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) {
1487 int64 handle1 = 0;
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
1510 // always fail.
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));
1521 } // namespace
1523 void SyncableDirectoryTest::ValidateEntry(BaseTransaction* trans,
1524 int64 id,
1525 bool check_name,
1526 const std::string& name,
1527 int64 base_version,
1528 int64 server_version,
1529 bool is_del) {
1530 Entry e(trans, GET_BY_ID, TestIdFactory::FromNumber(id));
1531 ASSERT_TRUE(e.good());
1532 if (check_name)
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));
1539 namespace {
1541 class SyncableDirectoryManagement : public testing::Test {
1542 public:
1543 virtual void SetUp() {
1544 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
1547 virtual void TearDown() {
1549 protected:
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);
1565 dir.Close();
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 {
1572 public:
1573 StressTransactionsDelegate(Directory* dir, int thread_number)
1574 : dir_(dir),
1575 thread_number_(thread_number) {}
1577 private:
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(
1592 rand() % 10));
1593 } else {
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);
1599 CHECK(e.good());
1600 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(
1601 rand() % 20));
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]);
1641 dir.Close();
1642 file_util::Delete(path, true);
1645 class SyncableClientTagTest : public SyncableDirectoryTest {
1646 public:
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_);
1661 CHECK(me.good());
1662 me.Put(ID, id);
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_);
1677 CHECK(me.good());
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);
1684 protected:
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));
1740 } // namespace
1741 } // namespace syncable