1 // Copyright (c) 2011 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.
4
5 #include "chrome/browser/sync/syncable/syncable.h"
6
7 #include "build/build_config.h"
8
9 #include <sys/types.h>
10
11 #include <limits>
12 #include <string>
13
14 #if !defined(OS_WIN)
15 #define MAX_PATH PATH_MAX
16 #include <ostream>
17 #include <stdio.h>
18 #include <sys/ipc.h>
19 #include <sys/sem.h>
20 #include <sys/times.h>
21 #endif // !defined(OS_WIN)
22
23 #include "base/file_path.h"
24 #include "base/file_util.h"
25 #include "base/logging.h"
26 #include "base/memory/scoped_ptr.h"
27 #include "base/memory/scoped_temp_dir.h"
28 #include "base/string_util.h"
29 #include "base/threading/platform_thread.h"
30 #include "base/values.h"
31 #include "chrome/browser/sync/engine/syncproto.h"
32 #include "chrome/browser/sync/protocol/bookmark_specifics.pb.h"
33 #include "chrome/browser/sync/syncable/directory_backing_store.h"
34 #include "chrome/browser/sync/syncable/directory_manager.h"
35 #include "chrome/common/deprecated/event_sys-inl.h"
36 #include "chrome/test/sync/engine/test_id_factory.h"
37 #include "chrome/test/sync/engine/test_syncable_utils.h"
38 #include "chrome/test/values_test_util.h"
39 #include "testing/gtest/include/gtest/gtest.h"
40 #include "third_party/sqlite/sqlite3.h"
41
42 using browser_sync::TestIdFactory;
43 using test::ExpectBooleanValue;
44 using test::ExpectStringValue;
45
46 namespace syncable {
47
48 class SyncableKernelTest : public testing::Test {};
49
TEST_F(SyncableKernelTest,ToValue)50 TEST_F(SyncableKernelTest, ToValue) {
51 EntryKernel kernel;
52 scoped_ptr<DictionaryValue> value(kernel.ToValue());
53 if (value.get()) {
54 // Not much to check without repeating the ToValue() code.
55 EXPECT_TRUE(value->HasKey("isDirty"));
56 // The extra +1 is for "isDirty".
57 EXPECT_EQ(BIT_TEMPS_END - BEGIN_FIELDS + 1,
58 static_cast<int>(value->size()));
59 } else {
60 ADD_FAILURE();
61 }
62 }
63
64 namespace {
PutDataAsBookmarkFavicon(WriteTransaction * wtrans,MutableEntry * e,const char * bytes,size_t bytes_length)65 void PutDataAsBookmarkFavicon(WriteTransaction* wtrans,
66 MutableEntry* e,
67 const char* bytes,
68 size_t bytes_length) {
69 sync_pb::EntitySpecifics specifics;
70 specifics.MutableExtension(sync_pb::bookmark)->set_url("http://demo/");
71 specifics.MutableExtension(sync_pb::bookmark)->set_favicon(bytes,
72 bytes_length);
73 e->Put(SPECIFICS, specifics);
74 }
75
ExpectDataFromBookmarkFaviconEquals(BaseTransaction * trans,Entry * e,const char * bytes,size_t bytes_length)76 void ExpectDataFromBookmarkFaviconEquals(BaseTransaction* trans,
77 Entry* e,
78 const char* bytes,
79 size_t bytes_length) {
80 ASSERT_TRUE(e->good());
81 ASSERT_TRUE(e->Get(SPECIFICS).HasExtension(sync_pb::bookmark));
82 ASSERT_EQ("http://demo/",
83 e->Get(SPECIFICS).GetExtension(sync_pb::bookmark).url());
84 ASSERT_EQ(std::string(bytes, bytes_length),
85 e->Get(SPECIFICS).GetExtension(sync_pb::bookmark).favicon());
86 }
87 } // namespace
88
89 class SyncableGeneralTest : public testing::Test {
90 public:
SetUp()91 virtual void SetUp() {
92 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
93 db_path_ = temp_dir_.path().Append(
94 FILE_PATH_LITERAL("SyncableTest.sqlite3"));
95 }
96
TearDown()97 virtual void TearDown() {
98 }
99 protected:
100 ScopedTempDir temp_dir_;
101 FilePath db_path_;
102 };
103
TEST_F(SyncableGeneralTest,General)104 TEST_F(SyncableGeneralTest, General) {
105 Directory dir;
106 dir.Open(db_path_, "SimpleTest");
107
108 int64 written_metahandle;
109 const Id id = TestIdFactory::FromNumber(99);
110 std::string name = "Jeff";
111 // Test simple read operations on an empty DB.
112 {
113 ReadTransaction rtrans(&dir, __FILE__, __LINE__);
114 Entry e(&rtrans, GET_BY_ID, id);
115 ASSERT_FALSE(e.good()); // Hasn't been written yet.
116
117 Directory::ChildHandles child_handles;
118 dir.GetChildHandles(&rtrans, rtrans.root_id(), &child_handles);
119 EXPECT_TRUE(child_handles.empty());
120 }
121
122 // Test creating a new meta entry.
123 {
124 WriteTransaction wtrans(&dir, UNITTEST, __FILE__, __LINE__);
125 MutableEntry me(&wtrans, CREATE, wtrans.root_id(), name);
126 ASSERT_TRUE(me.good());
127 me.Put(ID, id);
128 me.Put(BASE_VERSION, 1);
129 written_metahandle = me.Get(META_HANDLE);
130 }
131
132 // Test GetChildHandles after something is now in the DB.
133 // Also check that GET_BY_ID works.
134 {
135 ReadTransaction rtrans(&dir, __FILE__, __LINE__);
136 Entry e(&rtrans, GET_BY_ID, id);
137 ASSERT_TRUE(e.good());
138
139 Directory::ChildHandles child_handles;
140 dir.GetChildHandles(&rtrans, rtrans.root_id(), &child_handles);
141 EXPECT_EQ(1u, child_handles.size());
142
143 for (Directory::ChildHandles::iterator i = child_handles.begin();
144 i != child_handles.end(); ++i) {
145 EXPECT_EQ(*i, written_metahandle);
146 }
147 }
148
149 // Test writing data to an entity. Also check that GET_BY_HANDLE works.
150 static const char s[] = "Hello World.";
151 {
152 WriteTransaction trans(&dir, UNITTEST, __FILE__, __LINE__);
153 MutableEntry e(&trans, GET_BY_HANDLE, written_metahandle);
154 ASSERT_TRUE(e.good());
155 PutDataAsBookmarkFavicon(&trans, &e, s, sizeof(s));
156 }
157
158 // Test reading back the contents that we just wrote.
159 {
160 WriteTransaction trans(&dir, UNITTEST, __FILE__, __LINE__);
161 MutableEntry e(&trans, GET_BY_HANDLE, written_metahandle);
162 ASSERT_TRUE(e.good());
163 ExpectDataFromBookmarkFaviconEquals(&trans, &e, s, sizeof(s));
164 }
165
166 // Verify it exists in the folder.
167 {
168 ReadTransaction rtrans(&dir, __FILE__, __LINE__);
169 EXPECT_EQ(1, CountEntriesWithName(&rtrans, rtrans.root_id(), name));
170 }
171
172 // Now delete it.
173 {
174 WriteTransaction trans(&dir, UNITTEST, __FILE__, __LINE__);
175 MutableEntry e(&trans, GET_BY_HANDLE, written_metahandle);
176 e.Put(IS_DEL, true);
177
178 EXPECT_EQ(0, CountEntriesWithName(&trans, trans.root_id(), name));
179 }
180
181 dir.SaveChanges();
182 }
183
TEST_F(SyncableGeneralTest,ClientIndexRebuildsProperly)184 TEST_F(SyncableGeneralTest, ClientIndexRebuildsProperly) {
185 int64 written_metahandle;
186 TestIdFactory factory;
187 const Id id = factory.NewServerId();
188 std::string name = "cheesepuffs";
189 std::string tag = "dietcoke";
190
191 // Test creating a new meta entry.
192 {
193 Directory dir;
194 dir.Open(db_path_, "IndexTest");
195 {
196 WriteTransaction wtrans(&dir, UNITTEST, __FILE__, __LINE__);
197 MutableEntry me(&wtrans, CREATE, wtrans.root_id(), name);
198 ASSERT_TRUE(me.good());
199 me.Put(ID, id);
200 me.Put(BASE_VERSION, 1);
201 me.Put(UNIQUE_CLIENT_TAG, tag);
202 written_metahandle = me.Get(META_HANDLE);
203 }
204 dir.SaveChanges();
205 }
206
207 // The DB was closed. Now reopen it. This will cause index regeneration.
208 {
209 Directory dir;
210 dir.Open(db_path_, "IndexTest");
211
212 ReadTransaction trans(&dir, __FILE__, __LINE__);
213 Entry me(&trans, GET_BY_CLIENT_TAG, tag);
214 ASSERT_TRUE(me.good());
215 EXPECT_EQ(me.Get(ID), id);
216 EXPECT_EQ(me.Get(BASE_VERSION), 1);
217 EXPECT_EQ(me.Get(UNIQUE_CLIENT_TAG), tag);
218 EXPECT_EQ(me.Get(META_HANDLE), written_metahandle);
219 }
220 }
221
TEST_F(SyncableGeneralTest,ClientIndexRebuildsDeletedProperly)222 TEST_F(SyncableGeneralTest, ClientIndexRebuildsDeletedProperly) {
223 TestIdFactory factory;
224 const Id id = factory.NewServerId();
225 std::string tag = "dietcoke";
226
227 // Test creating a deleted, unsynced, server meta entry.
228 {
229 Directory dir;
230 dir.Open(db_path_, "IndexTest");
231 {
232 WriteTransaction wtrans(&dir, UNITTEST, __FILE__, __LINE__);
233 MutableEntry me(&wtrans, CREATE, wtrans.root_id(), "deleted");
234 ASSERT_TRUE(me.good());
235 me.Put(ID, id);
236 me.Put(BASE_VERSION, 1);
237 me.Put(UNIQUE_CLIENT_TAG, tag);
238 me.Put(IS_DEL, true);
239 me.Put(IS_UNSYNCED, true); // Or it might be purged.
240 }
241 dir.SaveChanges();
242 }
243
244 // The DB was closed. Now reopen it. This will cause index regeneration.
245 // Should still be present and valid in the client tag index.
246 {
247 Directory dir;
248 dir.Open(db_path_, "IndexTest");
249
250 ReadTransaction trans(&dir, __FILE__, __LINE__);
251 Entry me(&trans, GET_BY_CLIENT_TAG, tag);
252 ASSERT_TRUE(me.good());
253 EXPECT_EQ(me.Get(ID), id);
254 EXPECT_EQ(me.Get(UNIQUE_CLIENT_TAG), tag);
255 EXPECT_TRUE(me.Get(IS_DEL));
256 EXPECT_TRUE(me.Get(IS_UNSYNCED));
257 }
258 }
259
TEST_F(SyncableGeneralTest,ToValue)260 TEST_F(SyncableGeneralTest, ToValue) {
261 Directory dir;
262 dir.Open(db_path_, "SimpleTest");
263
264 const Id id = TestIdFactory::FromNumber(99);
265 {
266 ReadTransaction rtrans(&dir, __FILE__, __LINE__);
267 Entry e(&rtrans, GET_BY_ID, id);
268 EXPECT_FALSE(e.good()); // Hasn't been written yet.
269
270 scoped_ptr<DictionaryValue> value(e.ToValue());
271 ExpectBooleanValue(false, *value, "good");
272 EXPECT_EQ(1u, value->size());
273 }
274
275 // Test creating a new meta entry.
276 {
277 WriteTransaction wtrans(&dir, UNITTEST, __FILE__, __LINE__);
278 MutableEntry me(&wtrans, CREATE, wtrans.root_id(), "new");
279 ASSERT_TRUE(me.good());
280 me.Put(ID, id);
281 me.Put(BASE_VERSION, 1);
282
283 scoped_ptr<DictionaryValue> value(me.ToValue());
284 ExpectBooleanValue(true, *value, "good");
285 EXPECT_TRUE(value->HasKey("kernel"));
286 ExpectStringValue("Unspecified", *value, "serverModelType");
287 ExpectStringValue("Unspecified", *value, "modelType");
288 ExpectBooleanValue(false, *value, "shouldMaintainPosition");
289 ExpectBooleanValue(true, *value, "existsOnClientBecauseNameIsNonEmpty");
290 ExpectBooleanValue(false, *value, "isRoot");
291 }
292
293 dir.SaveChanges();
294 }
295
296 // A Directory whose backing store always fails SaveChanges by returning false.
297 class TestUnsaveableDirectory : public Directory {
298 public:
299 class UnsaveableBackingStore : public DirectoryBackingStore {
300 public:
UnsaveableBackingStore(const std::string & dir_name,const FilePath & backing_filepath)301 UnsaveableBackingStore(const std::string& dir_name,
302 const FilePath& backing_filepath)
303 : DirectoryBackingStore(dir_name, backing_filepath) { }
SaveChanges(const Directory::SaveChangesSnapshot & snapshot)304 virtual bool SaveChanges(const Directory::SaveChangesSnapshot& snapshot) {
305 return false;
306 }
307 };
CreateBackingStore(const std::string & dir_name,const FilePath & backing_filepath)308 virtual DirectoryBackingStore* CreateBackingStore(
309 const std::string& dir_name,
310 const FilePath& backing_filepath) {
311 return new UnsaveableBackingStore(dir_name, backing_filepath);
312 }
313 };
314
315 // Test suite for syncable::Directory.
316 class SyncableDirectoryTest : public testing::Test {
317 protected:
318 static const FilePath::CharType kFilePath[];
319 static const char kName[];
320 static const Id kId;
321
322 // SetUp() is called before each test case is run.
323 // The sqlite3 DB is deleted before each test is run.
SetUp()324 virtual void SetUp() {
325 file_path_ = FilePath(kFilePath);
326 file_util::Delete(file_path_, true);
327 dir_.reset(new Directory());
328 ASSERT_TRUE(dir_.get());
329 ASSERT_TRUE(OPENED == dir_->Open(file_path_, kName));
330 ASSERT_TRUE(dir_->good());
331 }
332
TearDown()333 virtual void TearDown() {
334 // This also closes file handles.
335 dir_->SaveChanges();
336 dir_.reset();
337 file_util::Delete(file_path_, true);
338 }
339
ReloadDir()340 void ReloadDir() {
341 dir_.reset(new Directory());
342 ASSERT_TRUE(dir_.get());
343 ASSERT_TRUE(OPENED == dir_->Open(file_path_, kName));
344 }
345
SaveAndReloadDir()346 void SaveAndReloadDir() {
347 dir_->SaveChanges();
348 ReloadDir();
349 }
350
IsInDirtyMetahandles(int64 metahandle)351 bool IsInDirtyMetahandles(int64 metahandle) {
352 return 1 == dir_->kernel_->dirty_metahandles->count(metahandle);
353 }
354
IsInMetahandlesToPurge(int64 metahandle)355 bool IsInMetahandlesToPurge(int64 metahandle) {
356 return 1 == dir_->kernel_->metahandles_to_purge->count(metahandle);
357 }
358
CheckPurgeEntriesWithTypeInSucceeded(const ModelTypeSet & types_to_purge,bool before_reload)359 void CheckPurgeEntriesWithTypeInSucceeded(const ModelTypeSet& types_to_purge,
360 bool before_reload) {
361 SCOPED_TRACE(testing::Message("Before reload: ") << before_reload);
362 {
363 ReadTransaction trans(dir_.get(), __FILE__, __LINE__);
364 MetahandleSet all_set;
365 dir_->GetAllMetaHandles(&trans, &all_set);
366 EXPECT_EQ(3U, all_set.size());
367 if (before_reload)
368 EXPECT_EQ(4U, dir_->kernel_->metahandles_to_purge->size());
369 for (MetahandleSet::iterator iter = all_set.begin();
370 iter != all_set.end(); ++iter) {
371 Entry e(&trans, GET_BY_HANDLE, *iter);
372 if ((types_to_purge.count(e.GetModelType()) ||
373 types_to_purge.count(e.GetServerModelType()))) {
374 FAIL() << "Illegal type should have been deleted.";
375 }
376 }
377 }
378
379 EXPECT_FALSE(dir_->initial_sync_ended_for_type(PREFERENCES));
380 EXPECT_FALSE(dir_->initial_sync_ended_for_type(AUTOFILL));
381 EXPECT_TRUE(dir_->initial_sync_ended_for_type(BOOKMARKS));
382 }
383
384 scoped_ptr<Directory> dir_;
385 FilePath file_path_;
386
387 // Creates an empty entry and sets the ID field to the default kId.
CreateEntry(const std::string & entryname)388 void CreateEntry(const std::string& entryname) {
389 CreateEntry(entryname, kId);
390 }
391
392 // Creates an empty entry and sets the ID field to id.
CreateEntry(const std::string & entryname,const int id)393 void CreateEntry(const std::string& entryname, const int id) {
394 CreateEntry(entryname, TestIdFactory::FromNumber(id));
395 }
CreateEntry(const std::string & entryname,Id id)396 void CreateEntry(const std::string& entryname, Id id) {
397 WriteTransaction wtrans(dir_.get(), UNITTEST, __FILE__, __LINE__);
398 MutableEntry me(&wtrans, CREATE, wtrans.root_id(), entryname);
399 ASSERT_TRUE(me.good());
400 me.Put(ID, id);
401 me.Put(IS_UNSYNCED, true);
402 }
403
404 void ValidateEntry(BaseTransaction* trans,
405 int64 id,
406 bool check_name,
407 const std::string& name,
408 int64 base_version,
409 int64 server_version,
410 bool is_del);
411 };
412
TEST_F(SyncableDirectoryTest,TakeSnapshotGetsMetahandlesToPurge)413 TEST_F(SyncableDirectoryTest, TakeSnapshotGetsMetahandlesToPurge) {
414 const int metas_to_create = 50;
415 MetahandleSet expected_purges;
416 MetahandleSet all_handles;
417 {
418 WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__);
419 for (int i = 0; i < metas_to_create; i++) {
420 MutableEntry e(&trans, CREATE, trans.root_id(), "foo");
421 e.Put(IS_UNSYNCED, true);
422 sync_pb::EntitySpecifics specs;
423 if (i % 2 == 0) {
424 AddDefaultExtensionValue(BOOKMARKS, &specs);
425 expected_purges.insert(e.Get(META_HANDLE));
426 all_handles.insert(e.Get(META_HANDLE));
427 } else {
428 AddDefaultExtensionValue(PREFERENCES, &specs);
429 all_handles.insert(e.Get(META_HANDLE));
430 }
431 e.Put(SPECIFICS, specs);
432 e.Put(SERVER_SPECIFICS, specs);
433 }
434 }
435
436 ModelTypeSet to_purge;
437 to_purge.insert(BOOKMARKS);
438 dir_->PurgeEntriesWithTypeIn(to_purge);
439
440 Directory::SaveChangesSnapshot snapshot1;
441 base::AutoLock scoped_lock(dir_->kernel_->save_changes_mutex);
442 dir_->TakeSnapshotForSaveChanges(&snapshot1);
443 EXPECT_TRUE(expected_purges == snapshot1.metahandles_to_purge);
444
445 to_purge.clear();
446 to_purge.insert(PREFERENCES);
447 dir_->PurgeEntriesWithTypeIn(to_purge);
448
449 dir_->HandleSaveChangesFailure(snapshot1);
450
451 Directory::SaveChangesSnapshot snapshot2;
452 dir_->TakeSnapshotForSaveChanges(&snapshot2);
453 EXPECT_TRUE(all_handles == snapshot2.metahandles_to_purge);
454 }
455
TEST_F(SyncableDirectoryTest,TakeSnapshotGetsAllDirtyHandlesTest)456 TEST_F(SyncableDirectoryTest, TakeSnapshotGetsAllDirtyHandlesTest) {
457 const int metahandles_to_create = 100;
458 std::vector<int64> expected_dirty_metahandles;
459 {
460 WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__);
461 for (int i = 0; i < metahandles_to_create; i++) {
462 MutableEntry e(&trans, CREATE, trans.root_id(), "foo");
463 expected_dirty_metahandles.push_back(e.Get(META_HANDLE));
464 e.Put(IS_UNSYNCED, true);
465 }
466 }
467 // Fake SaveChanges() and make sure we got what we expected.
468 {
469 Directory::SaveChangesSnapshot snapshot;
470 base::AutoLock scoped_lock(dir_->kernel_->save_changes_mutex);
471 dir_->TakeSnapshotForSaveChanges(&snapshot);
472 // Make sure there's an entry for each new metahandle. Make sure all
473 // entries are marked dirty.
474 ASSERT_EQ(expected_dirty_metahandles.size(), snapshot.dirty_metas.size());
475 for (OriginalEntries::const_iterator i = snapshot.dirty_metas.begin();
476 i != snapshot.dirty_metas.end(); ++i) {
477 ASSERT_TRUE(i->is_dirty());
478 }
479 dir_->VacuumAfterSaveChanges(snapshot);
480 }
481 // Put a new value with existing transactions as well as adding new ones.
482 {
483 WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__);
484 std::vector<int64> new_dirty_metahandles;
485 for (std::vector<int64>::const_iterator i =
486 expected_dirty_metahandles.begin();
487 i != expected_dirty_metahandles.end(); ++i) {
488 // Change existing entries to directories to dirty them.
489 MutableEntry e1(&trans, GET_BY_HANDLE, *i);
490 e1.Put(IS_DIR, true);
491 e1.Put(IS_UNSYNCED, true);
492 // Add new entries
493 MutableEntry e2(&trans, CREATE, trans.root_id(), "bar");
494 e2.Put(IS_UNSYNCED, true);
495 new_dirty_metahandles.push_back(e2.Get(META_HANDLE));
496 }
497 expected_dirty_metahandles.insert(expected_dirty_metahandles.end(),
498 new_dirty_metahandles.begin(), new_dirty_metahandles.end());
499 }
500 // Fake SaveChanges() and make sure we got what we expected.
501 {
502 Directory::SaveChangesSnapshot snapshot;
503 base::AutoLock scoped_lock(dir_->kernel_->save_changes_mutex);
504 dir_->TakeSnapshotForSaveChanges(&snapshot);
505 // Make sure there's an entry for each new metahandle. Make sure all
506 // entries are marked dirty.
507 EXPECT_EQ(expected_dirty_metahandles.size(), snapshot.dirty_metas.size());
508 for (OriginalEntries::const_iterator i = snapshot.dirty_metas.begin();
509 i != snapshot.dirty_metas.end(); ++i) {
510 EXPECT_TRUE(i->is_dirty());
511 }
512 dir_->VacuumAfterSaveChanges(snapshot);
513 }
514 }
515
TEST_F(SyncableDirectoryTest,TestPurgeEntriesWithTypeIn)516 TEST_F(SyncableDirectoryTest, TestPurgeEntriesWithTypeIn) {
517 sync_pb::EntitySpecifics bookmark_specs;
518 sync_pb::EntitySpecifics autofill_specs;
519 sync_pb::EntitySpecifics preference_specs;
520 AddDefaultExtensionValue(BOOKMARKS, &bookmark_specs);
521 AddDefaultExtensionValue(PREFERENCES, &preference_specs);
522 AddDefaultExtensionValue(AUTOFILL, &autofill_specs);
523 dir_->set_initial_sync_ended_for_type(BOOKMARKS, true);
524 dir_->set_initial_sync_ended_for_type(PREFERENCES, true);
525 dir_->set_initial_sync_ended_for_type(AUTOFILL, true);
526
527 std::set<ModelType> types_to_purge;
528 types_to_purge.insert(PREFERENCES);
529 types_to_purge.insert(AUTOFILL);
530
531 TestIdFactory id_factory;
532 // Create some items for each type.
533 {
534 WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__);
535 MutableEntry item1(&trans, CREATE, trans.root_id(), "Item");
536 ASSERT_TRUE(item1.good());
537 item1.Put(SPECIFICS, bookmark_specs);
538 item1.Put(SERVER_SPECIFICS, bookmark_specs);
539 item1.Put(IS_UNSYNCED, true);
540
541 MutableEntry item2(&trans, CREATE_NEW_UPDATE_ITEM,
542 id_factory.NewServerId());
543 ASSERT_TRUE(item2.good());
544 item2.Put(SERVER_SPECIFICS, bookmark_specs);
545 item2.Put(IS_UNAPPLIED_UPDATE, true);
546
547 MutableEntry item3(&trans, CREATE, trans.root_id(), "Item");
548 ASSERT_TRUE(item3.good());
549 item3.Put(SPECIFICS, preference_specs);
550 item3.Put(SERVER_SPECIFICS, preference_specs);
551 item3.Put(IS_UNSYNCED, true);
552
553 MutableEntry item4(&trans, CREATE_NEW_UPDATE_ITEM,
554 id_factory.NewServerId());
555 ASSERT_TRUE(item4.good());
556 item4.Put(SERVER_SPECIFICS, preference_specs);
557 item4.Put(IS_UNAPPLIED_UPDATE, true);
558
559 MutableEntry item5(&trans, CREATE, trans.root_id(), "Item");
560 ASSERT_TRUE(item5.good());
561 item5.Put(SPECIFICS, autofill_specs);
562 item5.Put(SERVER_SPECIFICS, autofill_specs);
563 item5.Put(IS_UNSYNCED, true);
564
565 MutableEntry item6(&trans, CREATE_NEW_UPDATE_ITEM,
566 id_factory.NewServerId());
567 ASSERT_TRUE(item6.good());
568 item6.Put(SERVER_SPECIFICS, autofill_specs);
569 item6.Put(IS_UNAPPLIED_UPDATE, true);
570 }
571
572 dir_->SaveChanges();
573 {
574 ReadTransaction trans(dir_.get(), __FILE__, __LINE__);
575 MetahandleSet all_set;
576 dir_->GetAllMetaHandles(&trans, &all_set);
577 ASSERT_EQ(7U, all_set.size());
578 }
579
580 dir_->PurgeEntriesWithTypeIn(types_to_purge);
581
582 // We first query the in-memory data, and then reload the directory (without
583 // saving) to verify that disk does not still have the data.
584 CheckPurgeEntriesWithTypeInSucceeded(types_to_purge, true);
585 SaveAndReloadDir();
586 CheckPurgeEntriesWithTypeInSucceeded(types_to_purge, false);
587 }
588
TEST_F(SyncableDirectoryTest,TakeSnapshotGetsOnlyDirtyHandlesTest)589 TEST_F(SyncableDirectoryTest, TakeSnapshotGetsOnlyDirtyHandlesTest) {
590 const int metahandles_to_create = 100;
591
592 // half of 2 * metahandles_to_create
593 const unsigned int number_changed = 100u;
594 std::vector<int64> expected_dirty_metahandles;
595 {
596 WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__);
597 for (int i = 0; i < metahandles_to_create; i++) {
598 MutableEntry e(&trans, CREATE, trans.root_id(), "foo");
599 expected_dirty_metahandles.push_back(e.Get(META_HANDLE));
600 e.Put(IS_UNSYNCED, true);
601 }
602 }
603 dir_->SaveChanges();
604 // Put a new value with existing transactions as well as adding new ones.
605 {
606 WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__);
607 std::vector<int64> new_dirty_metahandles;
608 for (std::vector<int64>::const_iterator i =
609 expected_dirty_metahandles.begin();
610 i != expected_dirty_metahandles.end(); ++i) {
611 // Change existing entries to directories to dirty them.
612 MutableEntry e1(&trans, GET_BY_HANDLE, *i);
613 ASSERT_TRUE(e1.good());
614 e1.Put(IS_DIR, true);
615 e1.Put(IS_UNSYNCED, true);
616 // Add new entries
617 MutableEntry e2(&trans, CREATE, trans.root_id(), "bar");
618 e2.Put(IS_UNSYNCED, true);
619 new_dirty_metahandles.push_back(e2.Get(META_HANDLE));
620 }
621 expected_dirty_metahandles.insert(expected_dirty_metahandles.end(),
622 new_dirty_metahandles.begin(), new_dirty_metahandles.end());
623 }
624 dir_->SaveChanges();
625 // Don't make any changes whatsoever and ensure nothing comes back.
626 {
627 WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__);
628 for (std::vector<int64>::const_iterator i =
629 expected_dirty_metahandles.begin();
630 i != expected_dirty_metahandles.end(); ++i) {
631 MutableEntry e(&trans, GET_BY_HANDLE, *i);
632 ASSERT_TRUE(e.good());
633 // We aren't doing anything to dirty these entries.
634 }
635 }
636 // Fake SaveChanges() and make sure we got what we expected.
637 {
638 Directory::SaveChangesSnapshot snapshot;
639 base::AutoLock scoped_lock(dir_->kernel_->save_changes_mutex);
640 dir_->TakeSnapshotForSaveChanges(&snapshot);
641 // Make sure there are no dirty_metahandles.
642 EXPECT_EQ(0u, snapshot.dirty_metas.size());
643 dir_->VacuumAfterSaveChanges(snapshot);
644 }
645 {
646 WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__);
647 bool should_change = false;
648 for (std::vector<int64>::const_iterator i =
649 expected_dirty_metahandles.begin();
650 i != expected_dirty_metahandles.end(); ++i) {
651 // Maybe change entries by flipping IS_DIR.
652 MutableEntry e(&trans, GET_BY_HANDLE, *i);
653 ASSERT_TRUE(e.good());
654 should_change = !should_change;
655 if (should_change) {
656 bool not_dir = !e.Get(IS_DIR);
657 e.Put(IS_DIR, not_dir);
658 e.Put(IS_UNSYNCED, true);
659 }
660 }
661 }
662 // Fake SaveChanges() and make sure we got what we expected.
663 {
664 Directory::SaveChangesSnapshot snapshot;
665 base::AutoLock scoped_lock(dir_->kernel_->save_changes_mutex);
666 dir_->TakeSnapshotForSaveChanges(&snapshot);
667 // Make sure there's an entry for each changed metahandle. Make sure all
668 // entries are marked dirty.
669 EXPECT_EQ(number_changed, snapshot.dirty_metas.size());
670 for (OriginalEntries::const_iterator i = snapshot.dirty_metas.begin();
671 i != snapshot.dirty_metas.end(); ++i) {
672 EXPECT_TRUE(i->is_dirty());
673 }
674 dir_->VacuumAfterSaveChanges(snapshot);
675 }
676 }
677
678 const FilePath::CharType SyncableDirectoryTest::kFilePath[] =
679 FILE_PATH_LITERAL("Test.sqlite3");
680 const char SyncableDirectoryTest::kName[] = "Foo";
681 const Id SyncableDirectoryTest::kId(TestIdFactory::FromNumber(-99));
682
683 namespace {
TEST_F(SyncableDirectoryTest,TestBasicLookupNonExistantID)684 TEST_F(SyncableDirectoryTest, TestBasicLookupNonExistantID) {
685 ReadTransaction rtrans(dir_.get(), __FILE__, __LINE__);
686 Entry e(&rtrans, GET_BY_ID, kId);
687 ASSERT_FALSE(e.good());
688 }
689
TEST_F(SyncableDirectoryTest,TestBasicLookupValidID)690 TEST_F(SyncableDirectoryTest, TestBasicLookupValidID) {
691 CreateEntry("rtc");
692 ReadTransaction rtrans(dir_.get(), __FILE__, __LINE__);
693 Entry e(&rtrans, GET_BY_ID, kId);
694 ASSERT_TRUE(e.good());
695 }
696
TEST_F(SyncableDirectoryTest,TestDelete)697 TEST_F(SyncableDirectoryTest, TestDelete) {
698 std::string name = "peanut butter jelly time";
699 WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__);
700 MutableEntry e1(&trans, CREATE, trans.root_id(), name);
701 ASSERT_TRUE(e1.good());
702 ASSERT_TRUE(e1.Put(IS_DEL, true));
703 MutableEntry e2(&trans, CREATE, trans.root_id(), name);
704 ASSERT_TRUE(e2.good());
705 ASSERT_TRUE(e2.Put(IS_DEL, true));
706 MutableEntry e3(&trans, CREATE, trans.root_id(), name);
707 ASSERT_TRUE(e3.good());
708 ASSERT_TRUE(e3.Put(IS_DEL, true));
709
710 ASSERT_TRUE(e1.Put(IS_DEL, false));
711 ASSERT_TRUE(e2.Put(IS_DEL, false));
712 ASSERT_TRUE(e3.Put(IS_DEL, false));
713
714 ASSERT_TRUE(e1.Put(IS_DEL, true));
715 ASSERT_TRUE(e2.Put(IS_DEL, true));
716 ASSERT_TRUE(e3.Put(IS_DEL, true));
717 }
718
TEST_F(SyncableDirectoryTest,TestGetUnsynced)719 TEST_F(SyncableDirectoryTest, TestGetUnsynced) {
720 Directory::UnsyncedMetaHandles handles;
721 int64 handle1, handle2;
722 {
723 WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__);
724
725 dir_->GetUnsyncedMetaHandles(&trans, &handles);
726 ASSERT_TRUE(0 == handles.size());
727
728 MutableEntry e1(&trans, CREATE, trans.root_id(), "abba");
729 ASSERT_TRUE(e1.good());
730 handle1 = e1.Get(META_HANDLE);
731 e1.Put(BASE_VERSION, 1);
732 e1.Put(IS_DIR, true);
733 e1.Put(ID, TestIdFactory::FromNumber(101));
734
735 MutableEntry e2(&trans, CREATE, e1.Get(ID), "bread");
736 ASSERT_TRUE(e2.good());
737 handle2 = e2.Get(META_HANDLE);
738 e2.Put(BASE_VERSION, 1);
739 e2.Put(ID, TestIdFactory::FromNumber(102));
740 }
741 dir_->SaveChanges();
742 {
743 WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__);
744
745 dir_->GetUnsyncedMetaHandles(&trans, &handles);
746 ASSERT_TRUE(0 == handles.size());
747
748 MutableEntry e3(&trans, GET_BY_HANDLE, handle1);
749 ASSERT_TRUE(e3.good());
750 e3.Put(IS_UNSYNCED, true);
751 }
752 dir_->SaveChanges();
753 {
754 WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__);
755 dir_->GetUnsyncedMetaHandles(&trans, &handles);
756 ASSERT_TRUE(1 == handles.size());
757 ASSERT_TRUE(handle1 == handles[0]);
758
759 MutableEntry e4(&trans, GET_BY_HANDLE, handle2);
760 ASSERT_TRUE(e4.good());
761 e4.Put(IS_UNSYNCED, true);
762 }
763 dir_->SaveChanges();
764 {
765 WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__);
766 dir_->GetUnsyncedMetaHandles(&trans, &handles);
767 ASSERT_TRUE(2 == handles.size());
768 if (handle1 == handles[0]) {
769 ASSERT_TRUE(handle2 == handles[1]);
770 } else {
771 ASSERT_TRUE(handle2 == handles[0]);
772 ASSERT_TRUE(handle1 == handles[1]);
773 }
774
775 MutableEntry e5(&trans, GET_BY_HANDLE, handle1);
776 ASSERT_TRUE(e5.good());
777 ASSERT_TRUE(e5.Get(IS_UNSYNCED));
778 ASSERT_TRUE(e5.Put(IS_UNSYNCED, false));
779 ASSERT_FALSE(e5.Get(IS_UNSYNCED));
780 }
781 dir_->SaveChanges();
782 {
783 WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__);
784 dir_->GetUnsyncedMetaHandles(&trans, &handles);
785 ASSERT_TRUE(1 == handles.size());
786 ASSERT_TRUE(handle2 == handles[0]);
787 }
788 }
789
TEST_F(SyncableDirectoryTest,TestGetUnappliedUpdates)790 TEST_F(SyncableDirectoryTest, TestGetUnappliedUpdates) {
791 Directory::UnappliedUpdateMetaHandles handles;
792 int64 handle1, handle2;
793 {
794 WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__);
795
796 dir_->GetUnappliedUpdateMetaHandles(&trans, &handles);
797 ASSERT_TRUE(0 == handles.size());
798
799 MutableEntry e1(&trans, CREATE, trans.root_id(), "abba");
800 ASSERT_TRUE(e1.good());
801 handle1 = e1.Get(META_HANDLE);
802 e1.Put(IS_UNAPPLIED_UPDATE, false);
803 e1.Put(BASE_VERSION, 1);
804 e1.Put(ID, TestIdFactory::FromNumber(101));
805 e1.Put(IS_DIR, true);
806
807 MutableEntry e2(&trans, CREATE, e1.Get(ID), "bread");
808 ASSERT_TRUE(e2.good());
809 handle2 = e2.Get(META_HANDLE);
810 e2.Put(IS_UNAPPLIED_UPDATE, false);
811 e2.Put(BASE_VERSION, 1);
812 e2.Put(ID, TestIdFactory::FromNumber(102));
813 }
814 dir_->SaveChanges();
815 {
816 WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__);
817
818 dir_->GetUnappliedUpdateMetaHandles(&trans, &handles);
819 ASSERT_TRUE(0 == handles.size());
820
821 MutableEntry e3(&trans, GET_BY_HANDLE, handle1);
822 ASSERT_TRUE(e3.good());
823 e3.Put(IS_UNAPPLIED_UPDATE, true);
824 }
825 dir_->SaveChanges();
826 {
827 WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__);
828 dir_->GetUnappliedUpdateMetaHandles(&trans, &handles);
829 ASSERT_TRUE(1 == handles.size());
830 ASSERT_TRUE(handle1 == handles[0]);
831
832 MutableEntry e4(&trans, GET_BY_HANDLE, handle2);
833 ASSERT_TRUE(e4.good());
834 e4.Put(IS_UNAPPLIED_UPDATE, true);
835 }
836 dir_->SaveChanges();
837 {
838 WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__);
839 dir_->GetUnappliedUpdateMetaHandles(&trans, &handles);
840 ASSERT_TRUE(2 == handles.size());
841 if (handle1 == handles[0]) {
842 ASSERT_TRUE(handle2 == handles[1]);
843 } else {
844 ASSERT_TRUE(handle2 == handles[0]);
845 ASSERT_TRUE(handle1 == handles[1]);
846 }
847
848 MutableEntry e5(&trans, GET_BY_HANDLE, handle1);
849 ASSERT_TRUE(e5.good());
850 e5.Put(IS_UNAPPLIED_UPDATE, false);
851 }
852 dir_->SaveChanges();
853 {
854 WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__);
855 dir_->GetUnappliedUpdateMetaHandles(&trans, &handles);
856 ASSERT_TRUE(1 == handles.size());
857 ASSERT_TRUE(handle2 == handles[0]);
858 }
859 }
860
861
TEST_F(SyncableDirectoryTest,DeleteBug_531383)862 TEST_F(SyncableDirectoryTest, DeleteBug_531383) {
863 // Try to evoke a check failure...
864 TestIdFactory id_factory;
865 int64 grandchild_handle, twin_handle;
866 {
867 WriteTransaction wtrans(dir_.get(), UNITTEST, __FILE__, __LINE__);
868 MutableEntry parent(&wtrans, CREATE, id_factory.root(), "Bob");
869 ASSERT_TRUE(parent.good());
870 parent.Put(IS_DIR, true);
871 parent.Put(ID, id_factory.NewServerId());
872 parent.Put(BASE_VERSION, 1);
873 MutableEntry child(&wtrans, CREATE, parent.Get(ID), "Bob");
874 ASSERT_TRUE(child.good());
875 child.Put(IS_DIR, true);
876 child.Put(ID, id_factory.NewServerId());
877 child.Put(BASE_VERSION, 1);
878 MutableEntry grandchild(&wtrans, CREATE, child.Get(ID), "Bob");
879 ASSERT_TRUE(grandchild.good());
880 grandchild.Put(ID, id_factory.NewServerId());
881 grandchild.Put(BASE_VERSION, 1);
882 ASSERT_TRUE(grandchild.Put(IS_DEL, true));
883 MutableEntry twin(&wtrans, CREATE, child.Get(ID), "Bob");
884 ASSERT_TRUE(twin.good());
885 ASSERT_TRUE(twin.Put(IS_DEL, true));
886 ASSERT_TRUE(grandchild.Put(IS_DEL, false));
887
888 grandchild_handle = grandchild.Get(META_HANDLE);
889 twin_handle = twin.Get(META_HANDLE);
890 }
891 dir_->SaveChanges();
892 {
893 WriteTransaction wtrans(dir_.get(), UNITTEST, __FILE__, __LINE__);
894 MutableEntry grandchild(&wtrans, GET_BY_HANDLE, grandchild_handle);
895 grandchild.Put(IS_DEL, true); // Used to CHECK fail here.
896 }
897 }
898
IsLegalNewParent(const Entry & a,const Entry & b)899 static inline bool IsLegalNewParent(const Entry& a, const Entry& b) {
900 return IsLegalNewParent(a.trans(), a.Get(ID), b.Get(ID));
901 }
902
TEST_F(SyncableDirectoryTest,TestIsLegalNewParent)903 TEST_F(SyncableDirectoryTest, TestIsLegalNewParent) {
904 TestIdFactory id_factory;
905 WriteTransaction wtrans(dir_.get(), UNITTEST, __FILE__, __LINE__);
906 Entry root(&wtrans, GET_BY_ID, id_factory.root());
907 ASSERT_TRUE(root.good());
908 MutableEntry parent(&wtrans, CREATE, root.Get(ID), "Bob");
909 ASSERT_TRUE(parent.good());
910 parent.Put(IS_DIR, true);
911 parent.Put(ID, id_factory.NewServerId());
912 parent.Put(BASE_VERSION, 1);
913 MutableEntry child(&wtrans, CREATE, parent.Get(ID), "Bob");
914 ASSERT_TRUE(child.good());
915 child.Put(IS_DIR, true);
916 child.Put(ID, id_factory.NewServerId());
917 child.Put(BASE_VERSION, 1);
918 MutableEntry grandchild(&wtrans, CREATE, child.Get(ID), "Bob");
919 ASSERT_TRUE(grandchild.good());
920 grandchild.Put(ID, id_factory.NewServerId());
921 grandchild.Put(BASE_VERSION, 1);
922
923 MutableEntry parent2(&wtrans, CREATE, root.Get(ID), "Pete");
924 ASSERT_TRUE(parent2.good());
925 parent2.Put(IS_DIR, true);
926 parent2.Put(ID, id_factory.NewServerId());
927 parent2.Put(BASE_VERSION, 1);
928 MutableEntry child2(&wtrans, CREATE, parent2.Get(ID), "Pete");
929 ASSERT_TRUE(child2.good());
930 child2.Put(IS_DIR, true);
931 child2.Put(ID, id_factory.NewServerId());
932 child2.Put(BASE_VERSION, 1);
933 MutableEntry grandchild2(&wtrans, CREATE, child2.Get(ID), "Pete");
934 ASSERT_TRUE(grandchild2.good());
935 grandchild2.Put(ID, id_factory.NewServerId());
936 grandchild2.Put(BASE_VERSION, 1);
937 // resulting tree
938 // root
939 // / |
940 // parent parent2
941 // | |
942 // child child2
943 // | |
944 // grandchild grandchild2
945 ASSERT_TRUE(IsLegalNewParent(child, root));
946 ASSERT_TRUE(IsLegalNewParent(child, parent));
947 ASSERT_FALSE(IsLegalNewParent(child, child));
948 ASSERT_FALSE(IsLegalNewParent(child, grandchild));
949 ASSERT_TRUE(IsLegalNewParent(child, parent2));
950 ASSERT_TRUE(IsLegalNewParent(child, grandchild2));
951 ASSERT_FALSE(IsLegalNewParent(parent, grandchild));
952 ASSERT_FALSE(IsLegalNewParent(root, grandchild));
953 ASSERT_FALSE(IsLegalNewParent(parent, grandchild));
954 }
955
TEST_F(SyncableDirectoryTest,TestEntryIsInFolder)956 TEST_F(SyncableDirectoryTest, TestEntryIsInFolder) {
957 // Create a subdir and an entry.
958 int64 entry_handle;
959 syncable::Id folder_id;
960 syncable::Id entry_id;
961 std::string entry_name = "entry";
962
963 {
964 WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__);
965 MutableEntry folder(&trans, CREATE, trans.root_id(), "folder");
966 ASSERT_TRUE(folder.good());
967 EXPECT_TRUE(folder.Put(IS_DIR, true));
968 EXPECT_TRUE(folder.Put(IS_UNSYNCED, true));
969 folder_id = folder.Get(ID);
970
971 MutableEntry entry(&trans, CREATE, folder.Get(ID), entry_name);
972 ASSERT_TRUE(entry.good());
973 entry_handle = entry.Get(META_HANDLE);
974 entry.Put(IS_UNSYNCED, true);
975 entry_id = entry.Get(ID);
976 }
977
978 // Make sure we can find the entry in the folder.
979 {
980 ReadTransaction trans(dir_.get(), __FILE__, __LINE__);
981 EXPECT_EQ(0, CountEntriesWithName(&trans, trans.root_id(), entry_name));
982 EXPECT_EQ(1, CountEntriesWithName(&trans, folder_id, entry_name));
983
984 Entry entry(&trans, GET_BY_ID, entry_id);
985 ASSERT_TRUE(entry.good());
986 EXPECT_EQ(entry_handle, entry.Get(META_HANDLE));
987 EXPECT_TRUE(entry.Get(NON_UNIQUE_NAME) == entry_name);
988 EXPECT_TRUE(entry.Get(PARENT_ID) == folder_id);
989 }
990 }
991
TEST_F(SyncableDirectoryTest,TestParentIdIndexUpdate)992 TEST_F(SyncableDirectoryTest, TestParentIdIndexUpdate) {
993 std::string child_name = "child";
994
995 WriteTransaction wt(dir_.get(), UNITTEST, __FILE__, __LINE__);
996 MutableEntry parent_folder(&wt, CREATE, wt.root_id(), "folder1");
997 parent_folder.Put(IS_UNSYNCED, true);
998 EXPECT_TRUE(parent_folder.Put(IS_DIR, true));
999
1000 MutableEntry parent_folder2(&wt, CREATE, wt.root_id(), "folder2");
1001 parent_folder2.Put(IS_UNSYNCED, true);
1002 EXPECT_TRUE(parent_folder2.Put(IS_DIR, true));
1003
1004 MutableEntry child(&wt, CREATE, parent_folder.Get(ID), child_name);
1005 EXPECT_TRUE(child.Put(IS_DIR, true));
1006 child.Put(IS_UNSYNCED, true);
1007
1008 ASSERT_TRUE(child.good());
1009
1010 EXPECT_EQ(0, CountEntriesWithName(&wt, wt.root_id(), child_name));
1011 EXPECT_EQ(parent_folder.Get(ID), child.Get(PARENT_ID));
1012 EXPECT_EQ(1, CountEntriesWithName(&wt, parent_folder.Get(ID), child_name));
1013 EXPECT_EQ(0, CountEntriesWithName(&wt, parent_folder2.Get(ID), child_name));
1014 child.Put(PARENT_ID, parent_folder2.Get(ID));
1015 EXPECT_EQ(parent_folder2.Get(ID), child.Get(PARENT_ID));
1016 EXPECT_EQ(0, CountEntriesWithName(&wt, parent_folder.Get(ID), child_name));
1017 EXPECT_EQ(1, CountEntriesWithName(&wt, parent_folder2.Get(ID), child_name));
1018 }
1019
TEST_F(SyncableDirectoryTest,TestNoReindexDeletedItems)1020 TEST_F(SyncableDirectoryTest, TestNoReindexDeletedItems) {
1021 std::string folder_name = "folder";
1022 std::string new_name = "new_name";
1023
1024 WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__);
1025 MutableEntry folder(&trans, CREATE, trans.root_id(), folder_name);
1026 ASSERT_TRUE(folder.good());
1027 ASSERT_TRUE(folder.Put(IS_DIR, true));
1028 ASSERT_TRUE(folder.Put(IS_DEL, true));
1029
1030 EXPECT_EQ(0, CountEntriesWithName(&trans, trans.root_id(), folder_name));
1031
1032 MutableEntry deleted(&trans, GET_BY_ID, folder.Get(ID));
1033 ASSERT_TRUE(deleted.good());
1034 ASSERT_TRUE(deleted.Put(PARENT_ID, trans.root_id()));
1035 ASSERT_TRUE(deleted.Put(NON_UNIQUE_NAME, new_name));
1036
1037 EXPECT_EQ(0, CountEntriesWithName(&trans, trans.root_id(), folder_name));
1038 EXPECT_EQ(0, CountEntriesWithName(&trans, trans.root_id(), new_name));
1039 }
1040
TEST_F(SyncableDirectoryTest,TestCaseChangeRename)1041 TEST_F(SyncableDirectoryTest, TestCaseChangeRename) {
1042 WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__);
1043 MutableEntry folder(&trans, CREATE, trans.root_id(), "CaseChange");
1044 ASSERT_TRUE(folder.good());
1045 EXPECT_TRUE(folder.Put(PARENT_ID, trans.root_id()));
1046 EXPECT_TRUE(folder.Put(NON_UNIQUE_NAME, "CASECHANGE"));
1047 EXPECT_TRUE(folder.Put(IS_DEL, true));
1048 }
1049
TEST_F(SyncableDirectoryTest,TestShareInfo)1050 TEST_F(SyncableDirectoryTest, TestShareInfo) {
1051 dir_->set_initial_sync_ended_for_type(AUTOFILL, true);
1052 dir_->set_store_birthday("Jan 31st");
1053 dir_->SetNotificationState("notification_state");
1054 {
1055 ReadTransaction trans(dir_.get(), __FILE__, __LINE__);
1056 EXPECT_TRUE(dir_->initial_sync_ended_for_type(AUTOFILL));
1057 EXPECT_FALSE(dir_->initial_sync_ended_for_type(BOOKMARKS));
1058 EXPECT_EQ("Jan 31st", dir_->store_birthday());
1059 EXPECT_EQ("notification_state", dir_->GetAndClearNotificationState());
1060 EXPECT_EQ("", dir_->GetAndClearNotificationState());
1061 }
1062 dir_->set_store_birthday("April 10th");
1063 dir_->SetNotificationState("notification_state2");
1064 dir_->SaveChanges();
1065 {
1066 ReadTransaction trans(dir_.get(), __FILE__, __LINE__);
1067 EXPECT_TRUE(dir_->initial_sync_ended_for_type(AUTOFILL));
1068 EXPECT_FALSE(dir_->initial_sync_ended_for_type(BOOKMARKS));
1069 EXPECT_EQ("April 10th", dir_->store_birthday());
1070 EXPECT_EQ("notification_state2", dir_->GetAndClearNotificationState());
1071 EXPECT_EQ("", dir_->GetAndClearNotificationState());
1072 }
1073 dir_->SetNotificationState("notification_state2");
1074 // Restore the directory from disk. Make sure that nothing's changed.
1075 SaveAndReloadDir();
1076 {
1077 ReadTransaction trans(dir_.get(), __FILE__, __LINE__);
1078 EXPECT_TRUE(dir_->initial_sync_ended_for_type(AUTOFILL));
1079 EXPECT_FALSE(dir_->initial_sync_ended_for_type(BOOKMARKS));
1080 EXPECT_EQ("April 10th", dir_->store_birthday());
1081 EXPECT_EQ("notification_state2", dir_->GetAndClearNotificationState());
1082 EXPECT_EQ("", dir_->GetAndClearNotificationState());
1083 }
1084 }
1085
TEST_F(SyncableDirectoryTest,TestSimpleFieldsPreservedDuringSaveChanges)1086 TEST_F(SyncableDirectoryTest, TestSimpleFieldsPreservedDuringSaveChanges) {
1087 Id update_id = TestIdFactory::FromNumber(1);
1088 Id create_id;
1089 EntryKernel create_pre_save, update_pre_save;
1090 EntryKernel create_post_save, update_post_save;
1091 std::string create_name = "Create";
1092
1093 {
1094 WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__);
1095 MutableEntry create(&trans, CREATE, trans.root_id(), create_name);
1096 MutableEntry update(&trans, CREATE_NEW_UPDATE_ITEM, update_id);
1097 create.Put(IS_UNSYNCED, true);
1098 update.Put(IS_UNAPPLIED_UPDATE, true);
1099 sync_pb::EntitySpecifics specifics;
1100 specifics.MutableExtension(sync_pb::bookmark)->set_favicon("PNG");
1101 specifics.MutableExtension(sync_pb::bookmark)->set_url("http://nowhere");
1102 create.Put(SPECIFICS, specifics);
1103 create_pre_save = create.GetKernelCopy();
1104 update_pre_save = update.GetKernelCopy();
1105 create_id = create.Get(ID);
1106 }
1107
1108 dir_->SaveChanges();
1109 dir_.reset(new Directory());
1110 ASSERT_TRUE(dir_.get());
1111 ASSERT_TRUE(OPENED == dir_->Open(file_path_, kName));
1112 ASSERT_TRUE(dir_->good());
1113
1114 {
1115 ReadTransaction trans(dir_.get(), __FILE__, __LINE__);
1116 Entry create(&trans, GET_BY_ID, create_id);
1117 EXPECT_EQ(1, CountEntriesWithName(&trans, trans.root_id(), create_name));
1118 Entry update(&trans, GET_BY_ID, update_id);
1119 create_post_save = create.GetKernelCopy();
1120 update_post_save = update.GetKernelCopy();
1121 }
1122 int i = BEGIN_FIELDS;
1123 for ( ; i < INT64_FIELDS_END ; ++i) {
1124 EXPECT_EQ(create_pre_save.ref((Int64Field)i),
1125 create_post_save.ref((Int64Field)i))
1126 << "int64 field #" << i << " changed during save/load";
1127 EXPECT_EQ(update_pre_save.ref((Int64Field)i),
1128 update_post_save.ref((Int64Field)i))
1129 << "int64 field #" << i << " changed during save/load";
1130 }
1131 for ( ; i < ID_FIELDS_END ; ++i) {
1132 EXPECT_EQ(create_pre_save.ref((IdField)i),
1133 create_post_save.ref((IdField)i))
1134 << "id field #" << i << " changed during save/load";
1135 EXPECT_EQ(update_pre_save.ref((IdField)i),
1136 update_pre_save.ref((IdField)i))
1137 << "id field #" << i << " changed during save/load";
1138 }
1139 for ( ; i < BIT_FIELDS_END ; ++i) {
1140 EXPECT_EQ(create_pre_save.ref((BitField)i),
1141 create_post_save.ref((BitField)i))
1142 << "Bit field #" << i << " changed during save/load";
1143 EXPECT_EQ(update_pre_save.ref((BitField)i),
1144 update_post_save.ref((BitField)i))
1145 << "Bit field #" << i << " changed during save/load";
1146 }
1147 for ( ; i < STRING_FIELDS_END ; ++i) {
1148 EXPECT_EQ(create_pre_save.ref((StringField)i),
1149 create_post_save.ref((StringField)i))
1150 << "String field #" << i << " changed during save/load";
1151 EXPECT_EQ(update_pre_save.ref((StringField)i),
1152 update_post_save.ref((StringField)i))
1153 << "String field #" << i << " changed during save/load";
1154 }
1155 for ( ; i < PROTO_FIELDS_END; ++i) {
1156 EXPECT_EQ(create_pre_save.ref((ProtoField)i).SerializeAsString(),
1157 create_post_save.ref((ProtoField)i).SerializeAsString())
1158 << "Blob field #" << i << " changed during save/load";
1159 EXPECT_EQ(update_pre_save.ref((ProtoField)i).SerializeAsString(),
1160 update_post_save.ref((ProtoField)i).SerializeAsString())
1161 << "Blob field #" << i << " changed during save/load";
1162 }
1163 }
1164
TEST_F(SyncableDirectoryTest,TestSaveChangesFailure)1165 TEST_F(SyncableDirectoryTest, TestSaveChangesFailure) {
1166 int64 handle1 = 0;
1167 // Set up an item using a regular, saveable directory.
1168 {
1169 WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__);
1170
1171 MutableEntry e1(&trans, CREATE, trans.root_id(), "aguilera");
1172 ASSERT_TRUE(e1.good());
1173 EXPECT_TRUE(e1.GetKernelCopy().is_dirty());
1174 handle1 = e1.Get(META_HANDLE);
1175 e1.Put(BASE_VERSION, 1);
1176 e1.Put(IS_DIR, true);
1177 e1.Put(ID, TestIdFactory::FromNumber(101));
1178 EXPECT_TRUE(e1.GetKernelCopy().is_dirty());
1179 EXPECT_TRUE(IsInDirtyMetahandles(handle1));
1180 }
1181 ASSERT_TRUE(dir_->SaveChanges());
1182
1183 // Make sure the item is no longer dirty after saving,
1184 // and make a modification.
1185 {
1186 WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__);
1187
1188 MutableEntry aguilera(&trans, GET_BY_HANDLE, handle1);
1189 ASSERT_TRUE(aguilera.good());
1190 EXPECT_FALSE(aguilera.GetKernelCopy().is_dirty());
1191 EXPECT_EQ(aguilera.Get(NON_UNIQUE_NAME), "aguilera");
1192 aguilera.Put(NON_UNIQUE_NAME, "overwritten");
1193 EXPECT_TRUE(aguilera.GetKernelCopy().is_dirty());
1194 EXPECT_TRUE(IsInDirtyMetahandles(handle1));
1195 }
1196 ASSERT_TRUE(dir_->SaveChanges());
1197
1198 // Now do some operations using a directory for which SaveChanges will
1199 // always fail.
1200 dir_.reset(new TestUnsaveableDirectory());
1201 ASSERT_TRUE(dir_.get());
1202 ASSERT_TRUE(OPENED == dir_->Open(FilePath(kFilePath), kName));
1203 ASSERT_TRUE(dir_->good());
1204 int64 handle2 = 0;
1205 {
1206 WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__);
1207
1208 MutableEntry aguilera(&trans, GET_BY_HANDLE, handle1);
1209 ASSERT_TRUE(aguilera.good());
1210 EXPECT_FALSE(aguilera.GetKernelCopy().is_dirty());
1211 EXPECT_EQ(aguilera.Get(NON_UNIQUE_NAME), "overwritten");
1212 EXPECT_FALSE(aguilera.GetKernelCopy().is_dirty());
1213 EXPECT_FALSE(IsInDirtyMetahandles(handle1));
1214 aguilera.Put(NON_UNIQUE_NAME, "christina");
1215 EXPECT_TRUE(aguilera.GetKernelCopy().is_dirty());
1216 EXPECT_TRUE(IsInDirtyMetahandles(handle1));
1217
1218 // New item.
1219 MutableEntry kids_on_block(&trans, CREATE, trans.root_id(), "kids");
1220 ASSERT_TRUE(kids_on_block.good());
1221 handle2 = kids_on_block.Get(META_HANDLE);
1222 kids_on_block.Put(BASE_VERSION, 1);
1223 kids_on_block.Put(IS_DIR, true);
1224 kids_on_block.Put(ID, TestIdFactory::FromNumber(102));
1225 EXPECT_TRUE(kids_on_block.GetKernelCopy().is_dirty());
1226 EXPECT_TRUE(IsInDirtyMetahandles(handle2));
1227 }
1228
1229 // We are using an unsaveable directory, so this can't succeed. However,
1230 // the HandleSaveChangesFailure code path should have been triggered.
1231 ASSERT_FALSE(dir_->SaveChanges());
1232
1233 // Make sure things were rolled back and the world is as it was before call.
1234 {
1235 ReadTransaction trans(dir_.get(), __FILE__, __LINE__);
1236 Entry e1(&trans, GET_BY_HANDLE, handle1);
1237 ASSERT_TRUE(e1.good());
1238 EntryKernel aguilera = e1.GetKernelCopy();
1239 Entry kids(&trans, GET_BY_HANDLE, handle2);
1240 ASSERT_TRUE(kids.good());
1241 EXPECT_TRUE(kids.GetKernelCopy().is_dirty());
1242 EXPECT_TRUE(IsInDirtyMetahandles(handle2));
1243 EXPECT_TRUE(aguilera.is_dirty());
1244 EXPECT_TRUE(IsInDirtyMetahandles(handle1));
1245 }
1246 }
1247
TEST_F(SyncableDirectoryTest,TestSaveChangesFailureWithPurge)1248 TEST_F(SyncableDirectoryTest, TestSaveChangesFailureWithPurge) {
1249 int64 handle1 = 0;
1250 // Set up an item using a regular, saveable directory.
1251 {
1252 WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__);
1253
1254 MutableEntry e1(&trans, CREATE, trans.root_id(), "aguilera");
1255 ASSERT_TRUE(e1.good());
1256 EXPECT_TRUE(e1.GetKernelCopy().is_dirty());
1257 handle1 = e1.Get(META_HANDLE);
1258 e1.Put(BASE_VERSION, 1);
1259 e1.Put(IS_DIR, true);
1260 e1.Put(ID, TestIdFactory::FromNumber(101));
1261 sync_pb::EntitySpecifics bookmark_specs;
1262 AddDefaultExtensionValue(BOOKMARKS, &bookmark_specs);
1263 e1.Put(SPECIFICS, bookmark_specs);
1264 e1.Put(SERVER_SPECIFICS, bookmark_specs);
1265 e1.Put(ID, TestIdFactory::FromNumber(101));
1266 EXPECT_TRUE(e1.GetKernelCopy().is_dirty());
1267 EXPECT_TRUE(IsInDirtyMetahandles(handle1));
1268 }
1269 ASSERT_TRUE(dir_->SaveChanges());
1270
1271 // Now do some operations using a directory for which SaveChanges will
1272 // always fail.
1273 dir_.reset(new TestUnsaveableDirectory());
1274 ASSERT_TRUE(dir_.get());
1275 ASSERT_TRUE(OPENED == dir_->Open(FilePath(kFilePath), kName));
1276 ASSERT_TRUE(dir_->good());
1277
1278 ModelTypeSet set;
1279 set.insert(BOOKMARKS);
1280 dir_->PurgeEntriesWithTypeIn(set);
1281 EXPECT_TRUE(IsInMetahandlesToPurge(handle1));
1282 ASSERT_FALSE(dir_->SaveChanges());
1283 EXPECT_TRUE(IsInMetahandlesToPurge(handle1));
1284 }
1285
1286 // Create items of each model type, and check that GetModelType and
1287 // GetServerModelType return the right value.
TEST_F(SyncableDirectoryTest,GetModelType)1288 TEST_F(SyncableDirectoryTest, GetModelType) {
1289 TestIdFactory id_factory;
1290 for (int i = 0; i < MODEL_TYPE_COUNT; ++i) {
1291 ModelType datatype = ModelTypeFromInt(i);
1292 SCOPED_TRACE(testing::Message("Testing model type ") << datatype);
1293 switch (datatype) {
1294 case UNSPECIFIED:
1295 case TOP_LEVEL_FOLDER:
1296 continue; // Datatype isn't a function of Specifics.
1297 default:
1298 break;
1299 }
1300 sync_pb::EntitySpecifics specifics;
1301 AddDefaultExtensionValue(datatype, &specifics);
1302
1303 WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__);
1304
1305 MutableEntry folder(&trans, CREATE, trans.root_id(), "Folder");
1306 ASSERT_TRUE(folder.good());
1307 folder.Put(ID, id_factory.NewServerId());
1308 folder.Put(SPECIFICS, specifics);
1309 folder.Put(BASE_VERSION, 1);
1310 folder.Put(IS_DIR, true);
1311 folder.Put(IS_DEL, false);
1312 ASSERT_EQ(datatype, folder.GetModelType());
1313
1314 MutableEntry item(&trans, CREATE, trans.root_id(), "Item");
1315 ASSERT_TRUE(item.good());
1316 item.Put(ID, id_factory.NewServerId());
1317 item.Put(SPECIFICS, specifics);
1318 item.Put(BASE_VERSION, 1);
1319 item.Put(IS_DIR, false);
1320 item.Put(IS_DEL, false);
1321 ASSERT_EQ(datatype, item.GetModelType());
1322
1323 // It's critical that deletion records retain their datatype, so that
1324 // they can be dispatched to the appropriate change processor.
1325 MutableEntry deleted_item(&trans, CREATE, trans.root_id(), "Deleted Item");
1326 ASSERT_TRUE(item.good());
1327 deleted_item.Put(ID, id_factory.NewServerId());
1328 deleted_item.Put(SPECIFICS, specifics);
1329 deleted_item.Put(BASE_VERSION, 1);
1330 deleted_item.Put(IS_DIR, false);
1331 deleted_item.Put(IS_DEL, true);
1332 ASSERT_EQ(datatype, deleted_item.GetModelType());
1333
1334 MutableEntry server_folder(&trans, CREATE_NEW_UPDATE_ITEM,
1335 id_factory.NewServerId());
1336 ASSERT_TRUE(server_folder.good());
1337 server_folder.Put(SERVER_SPECIFICS, specifics);
1338 server_folder.Put(BASE_VERSION, 1);
1339 server_folder.Put(SERVER_IS_DIR, true);
1340 server_folder.Put(SERVER_IS_DEL, false);
1341 ASSERT_EQ(datatype, server_folder.GetServerModelType());
1342
1343 MutableEntry server_item(&trans, CREATE_NEW_UPDATE_ITEM,
1344 id_factory.NewServerId());
1345 ASSERT_TRUE(server_item.good());
1346 server_item.Put(SERVER_SPECIFICS, specifics);
1347 server_item.Put(BASE_VERSION, 1);
1348 server_item.Put(SERVER_IS_DIR, false);
1349 server_item.Put(SERVER_IS_DEL, false);
1350 ASSERT_EQ(datatype, server_item.GetServerModelType());
1351
1352 browser_sync::SyncEntity folder_entity;
1353 folder_entity.set_id(id_factory.NewServerId());
1354 folder_entity.set_deleted(false);
1355 folder_entity.set_folder(true);
1356 folder_entity.mutable_specifics()->CopyFrom(specifics);
1357 ASSERT_EQ(datatype, folder_entity.GetModelType());
1358
1359 browser_sync::SyncEntity item_entity;
1360 item_entity.set_id(id_factory.NewServerId());
1361 item_entity.set_deleted(false);
1362 item_entity.set_folder(false);
1363 item_entity.mutable_specifics()->CopyFrom(specifics);
1364 ASSERT_EQ(datatype, item_entity.GetModelType());
1365 }
1366 }
1367
1368 } // namespace
1369
ValidateEntry(BaseTransaction * trans,int64 id,bool check_name,const std::string & name,int64 base_version,int64 server_version,bool is_del)1370 void SyncableDirectoryTest::ValidateEntry(BaseTransaction* trans,
1371 int64 id,
1372 bool check_name,
1373 const std::string& name,
1374 int64 base_version,
1375 int64 server_version,
1376 bool is_del) {
1377 Entry e(trans, GET_BY_ID, TestIdFactory::FromNumber(id));
1378 ASSERT_TRUE(e.good());
1379 if (check_name)
1380 ASSERT_TRUE(name == e.Get(NON_UNIQUE_NAME));
1381 ASSERT_TRUE(base_version == e.Get(BASE_VERSION));
1382 ASSERT_TRUE(server_version == e.Get(SERVER_VERSION));
1383 ASSERT_TRUE(is_del == e.Get(IS_DEL));
1384 }
1385
1386 namespace {
1387
TEST(SyncableDirectoryManager,TestFileRelease)1388 TEST(SyncableDirectoryManager, TestFileRelease) {
1389 DirectoryManager dm(FilePath(FILE_PATH_LITERAL(".")));
1390 ASSERT_TRUE(dm.Open("ScopeTest"));
1391 {
1392 ScopedDirLookup(&dm, "ScopeTest");
1393 }
1394 dm.Close("ScopeTest");
1395 ASSERT_TRUE(file_util::Delete(dm.GetSyncDataDatabasePath(), true));
1396 }
1397
1398 class ThreadOpenTestDelegate : public base::PlatformThread::Delegate {
1399 public:
ThreadOpenTestDelegate(DirectoryManager * dm)1400 explicit ThreadOpenTestDelegate(DirectoryManager* dm)
1401 : directory_manager_(dm) {}
1402 DirectoryManager* const directory_manager_;
1403
1404 private:
1405 // PlatformThread::Delegate methods:
ThreadMain()1406 virtual void ThreadMain() {
1407 CHECK(directory_manager_->Open("Open"));
1408 }
1409
1410 DISALLOW_COPY_AND_ASSIGN(ThreadOpenTestDelegate);
1411 };
1412
TEST(SyncableDirectoryManager,ThreadOpenTest)1413 TEST(SyncableDirectoryManager, ThreadOpenTest) {
1414 DirectoryManager dm(FilePath(FILE_PATH_LITERAL(".")));
1415 base::PlatformThreadHandle thread_handle;
1416 ThreadOpenTestDelegate test_delegate(&dm);
1417 ASSERT_TRUE(base::PlatformThread::Create(0, &test_delegate, &thread_handle));
1418 base::PlatformThread::Join(thread_handle);
1419 {
1420 ScopedDirLookup dir(&dm, "Open");
1421 ASSERT_TRUE(dir.good());
1422 }
1423 dm.Close("Open");
1424 ScopedDirLookup dir(&dm, "Open");
1425 ASSERT_FALSE(dir.good());
1426 }
1427
1428 struct Step {
Stepsyncable::__anonb1f1d8ba0311::Step1429 Step() : condvar(&mutex), number(0) {}
1430
1431 base::Lock mutex;
1432 base::ConditionVariable condvar;
1433 int number;
1434 int64 metahandle;
1435 };
1436
1437 class ThreadBugDelegate : public base::PlatformThread::Delegate {
1438 public:
1439 // a role is 0 or 1, meaning this thread does the odd or event steps.
ThreadBugDelegate(int role,Step * step,DirectoryManager * dirman)1440 ThreadBugDelegate(int role, Step* step, DirectoryManager* dirman)
1441 : role_(role), step_(step), directory_manager_(dirman) {}
1442
1443 protected:
1444 const int role_;
1445 Step* const step_;
1446 DirectoryManager* const directory_manager_;
1447
1448 // PlatformThread::Delegate methods:
ThreadMain()1449 virtual void ThreadMain() {
1450 const std::string dirname = "ThreadBug1";
1451 base::AutoLock scoped_lock(step_->mutex);
1452
1453 while (step_->number < 3) {
1454 while (step_->number % 2 != role_) {
1455 step_->condvar.Wait();
1456 }
1457 switch (step_->number) {
1458 case 0:
1459 directory_manager_->Open(dirname);
1460 break;
1461 case 1:
1462 {
1463 directory_manager_->Close(dirname);
1464 directory_manager_->Open(dirname);
1465 ScopedDirLookup dir(directory_manager_, dirname);
1466 CHECK(dir.good());
1467 WriteTransaction trans(dir, UNITTEST, __FILE__, __LINE__);
1468 MutableEntry me(&trans, CREATE, trans.root_id(), "Jeff");
1469 step_->metahandle = me.Get(META_HANDLE);
1470 me.Put(IS_UNSYNCED, true);
1471 }
1472 break;
1473 case 2:
1474 {
1475 ScopedDirLookup dir(directory_manager_, dirname);
1476 CHECK(dir.good());
1477 ReadTransaction trans(dir, __FILE__, __LINE__);
1478 Entry e(&trans, GET_BY_HANDLE, step_->metahandle);
1479 CHECK(e.good()); // Failed due to ThreadBug1
1480 }
1481 directory_manager_->Close(dirname);
1482 break;
1483 }
1484 step_->number += 1;
1485 step_->condvar.Signal();
1486 }
1487 }
1488
1489 DISALLOW_COPY_AND_ASSIGN(ThreadBugDelegate);
1490 };
1491
TEST(SyncableDirectoryManager,ThreadBug1)1492 TEST(SyncableDirectoryManager, ThreadBug1) {
1493 Step step;
1494 step.number = 0;
1495 DirectoryManager dirman(FilePath(FILE_PATH_LITERAL(".")));
1496 ThreadBugDelegate thread_delegate_1(0, &step, &dirman);
1497 ThreadBugDelegate thread_delegate_2(1, &step, &dirman);
1498
1499 base::PlatformThreadHandle thread_handle_1;
1500 base::PlatformThreadHandle thread_handle_2;
1501
1502 ASSERT_TRUE(
1503 base::PlatformThread::Create(0, &thread_delegate_1, &thread_handle_1));
1504 ASSERT_TRUE(
1505 base::PlatformThread::Create(0, &thread_delegate_2, &thread_handle_2));
1506
1507 base::PlatformThread::Join(thread_handle_1);
1508 base::PlatformThread::Join(thread_handle_2);
1509 }
1510
1511
1512 // The in-memory information would get out of sync because a
1513 // directory would be closed and re-opened, and then an old
1514 // Directory::Kernel with stale information would get saved to the db.
1515 class DirectoryKernelStalenessBugDelegate : public ThreadBugDelegate {
1516 public:
DirectoryKernelStalenessBugDelegate(int role,Step * step,DirectoryManager * dirman)1517 DirectoryKernelStalenessBugDelegate(int role, Step* step,
1518 DirectoryManager* dirman)
1519 : ThreadBugDelegate(role, step, dirman) {}
1520
ThreadMain()1521 virtual void ThreadMain() {
1522 const char test_bytes[] = "test data";
1523 const std::string dirname = "DirectoryKernelStalenessBug";
1524 base::AutoLock scoped_lock(step_->mutex);
1525 const Id jeff_id = TestIdFactory::FromNumber(100);
1526
1527 while (step_->number < 4) {
1528 while (step_->number % 2 != role_) {
1529 step_->condvar.Wait();
1530 }
1531 switch (step_->number) {
1532 case 0:
1533 {
1534 // Clean up remnants of earlier test runs.
1535 file_util::Delete(directory_manager_->GetSyncDataDatabasePath(),
1536 true);
1537 // Test.
1538 directory_manager_->Open(dirname);
1539 ScopedDirLookup dir(directory_manager_, dirname);
1540 CHECK(dir.good());
1541 WriteTransaction trans(dir, UNITTEST, __FILE__, __LINE__);
1542 MutableEntry me(&trans, CREATE, trans.root_id(), "Jeff");
1543 me.Put(BASE_VERSION, 1);
1544 me.Put(ID, jeff_id);
1545 PutDataAsBookmarkFavicon(&trans, &me, test_bytes,
1546 sizeof(test_bytes));
1547 }
1548 {
1549 ScopedDirLookup dir(directory_manager_, dirname);
1550 CHECK(dir.good());
1551 dir->SaveChanges();
1552 }
1553 directory_manager_->Close(dirname);
1554 break;
1555 case 1:
1556 {
1557 directory_manager_->Open(dirname);
1558 ScopedDirLookup dir(directory_manager_, dirname);
1559 CHECK(dir.good());
1560 }
1561 break;
1562 case 2:
1563 {
1564 ScopedDirLookup dir(directory_manager_, dirname);
1565 CHECK(dir.good());
1566 }
1567 break;
1568 case 3:
1569 {
1570 ScopedDirLookup dir(directory_manager_, dirname);
1571 CHECK(dir.good());
1572 ReadTransaction trans(dir, __FILE__, __LINE__);
1573 Entry e(&trans, GET_BY_ID, jeff_id);
1574 ExpectDataFromBookmarkFaviconEquals(&trans, &e, test_bytes,
1575 sizeof(test_bytes));
1576 }
1577 // Same result as CloseAllDirectories, but more code coverage.
1578 directory_manager_->Close(dirname);
1579 break;
1580 }
1581 step_->number += 1;
1582 step_->condvar.Signal();
1583 }
1584 }
1585
1586 DISALLOW_COPY_AND_ASSIGN(DirectoryKernelStalenessBugDelegate);
1587 };
1588
TEST(SyncableDirectoryManager,DirectoryKernelStalenessBug)1589 TEST(SyncableDirectoryManager, DirectoryKernelStalenessBug) {
1590 Step step;
1591
1592 DirectoryManager dirman(FilePath(FILE_PATH_LITERAL(".")));
1593 DirectoryKernelStalenessBugDelegate thread_delegate_1(0, &step, &dirman);
1594 DirectoryKernelStalenessBugDelegate thread_delegate_2(1, &step, &dirman);
1595
1596 base::PlatformThreadHandle thread_handle_1;
1597 base::PlatformThreadHandle thread_handle_2;
1598
1599 ASSERT_TRUE(
1600 base::PlatformThread::Create(0, &thread_delegate_1, &thread_handle_1));
1601 ASSERT_TRUE(
1602 base::PlatformThread::Create(0, &thread_delegate_2, &thread_handle_2));
1603
1604 base::PlatformThread::Join(thread_handle_1);
1605 base::PlatformThread::Join(thread_handle_2);
1606 }
1607
1608 class StressTransactionsDelegate : public base::PlatformThread::Delegate {
1609 public:
StressTransactionsDelegate(DirectoryManager * dm,const std::string & dirname,int thread_number)1610 StressTransactionsDelegate(DirectoryManager* dm,
1611 const std::string& dirname,
1612 int thread_number)
1613 : directory_manager_(dm),
1614 dirname_(dirname),
1615 thread_number_(thread_number) {}
1616
1617 private:
1618 DirectoryManager* const directory_manager_;
1619 std::string dirname_;
1620 const int thread_number_;
1621
1622 // PlatformThread::Delegate methods:
ThreadMain()1623 virtual void ThreadMain() {
1624 ScopedDirLookup dir(directory_manager_, dirname_);
1625 CHECK(dir.good());
1626 int entry_count = 0;
1627 std::string path_name;
1628
1629 for (int i = 0; i < 20; ++i) {
1630 const int rand_action = rand() % 10;
1631 if (rand_action < 4 && !path_name.empty()) {
1632 ReadTransaction trans(dir, __FILE__, __LINE__);
1633 CHECK(1 == CountEntriesWithName(&trans, trans.root_id(), path_name));
1634 base::PlatformThread::Sleep(rand() % 10);
1635 } else {
1636 std::string unique_name = StringPrintf("%d.%d", thread_number_,
1637 entry_count++);
1638 path_name.assign(unique_name.begin(), unique_name.end());
1639 WriteTransaction trans(dir, UNITTEST, __FILE__, __LINE__);
1640 MutableEntry e(&trans, CREATE, trans.root_id(), path_name);
1641 CHECK(e.good());
1642 base::PlatformThread::Sleep(rand() % 20);
1643 e.Put(IS_UNSYNCED, true);
1644 if (e.Put(ID, TestIdFactory::FromNumber(rand())) &&
1645 e.Get(ID).ServerKnows() && !e.Get(ID).IsRoot()) {
1646 e.Put(BASE_VERSION, 1);
1647 }
1648 }
1649 }
1650 }
1651
1652 DISALLOW_COPY_AND_ASSIGN(StressTransactionsDelegate);
1653 };
1654
TEST(SyncableDirectory,StressTransactions)1655 TEST(SyncableDirectory, StressTransactions) {
1656 DirectoryManager dirman(FilePath(FILE_PATH_LITERAL(".")));
1657 std::string dirname = "stress";
1658 file_util::Delete(dirman.GetSyncDataDatabasePath(), true);
1659 dirman.Open(dirname);
1660
1661 const int kThreadCount = 7;
1662 base::PlatformThreadHandle threads[kThreadCount];
1663 scoped_ptr<StressTransactionsDelegate> thread_delegates[kThreadCount];
1664
1665 for (int i = 0; i < kThreadCount; ++i) {
1666 thread_delegates[i].reset(
1667 new StressTransactionsDelegate(&dirman, dirname, i));
1668 ASSERT_TRUE(base::PlatformThread::Create(
1669 0, thread_delegates[i].get(), &threads[i]));
1670 }
1671
1672 for (int i = 0; i < kThreadCount; ++i) {
1673 base::PlatformThread::Join(threads[i]);
1674 }
1675
1676 dirman.Close(dirname);
1677 file_util::Delete(dirman.GetSyncDataDatabasePath(), true);
1678 }
1679
1680 class SyncableClientTagTest : public SyncableDirectoryTest {
1681 public:
1682 static const int kBaseVersion = 1;
1683 const char* test_name_;
1684 const char* test_tag_;
1685
SyncableClientTagTest()1686 SyncableClientTagTest() : test_name_("test_name"), test_tag_("dietcoke") {}
1687
CreateWithDefaultTag(Id id,bool deleted)1688 bool CreateWithDefaultTag(Id id, bool deleted) {
1689 return CreateWithTag(test_tag_, id, deleted);
1690 }
1691
1692 // Attempt to create an entry with a default tag.
CreateWithTag(const char * tag,Id id,bool deleted)1693 bool CreateWithTag(const char* tag, Id id, bool deleted) {
1694 WriteTransaction wtrans(dir_.get(), UNITTEST, __FILE__, __LINE__);
1695 MutableEntry me(&wtrans, CREATE, wtrans.root_id(), test_name_);
1696 CHECK(me.good());
1697 me.Put(ID, id);
1698 if (id.ServerKnows()) {
1699 me.Put(BASE_VERSION, kBaseVersion);
1700 }
1701 me.Put(IS_DEL, deleted);
1702 me.Put(IS_UNSYNCED, true);
1703 me.Put(IS_DIR, false);
1704 return me.Put(UNIQUE_CLIENT_TAG, tag);
1705 }
1706
1707 // Verify an entry exists with the default tag.
VerifyTag(Id id,bool deleted)1708 void VerifyTag(Id id, bool deleted) {
1709 // Should still be present and valid in the client tag index.
1710 ReadTransaction trans(dir_.get(), __FILE__, __LINE__);
1711 Entry me(&trans, GET_BY_CLIENT_TAG, test_tag_);
1712 CHECK(me.good());
1713 EXPECT_EQ(me.Get(ID), id);
1714 EXPECT_EQ(me.Get(UNIQUE_CLIENT_TAG), test_tag_);
1715 EXPECT_EQ(me.Get(IS_DEL), deleted);
1716 EXPECT_EQ(me.Get(IS_UNSYNCED), true);
1717 }
1718
1719 protected:
1720 TestIdFactory factory_;
1721 };
1722
TEST_F(SyncableClientTagTest,TestClientTagClear)1723 TEST_F(SyncableClientTagTest, TestClientTagClear) {
1724 Id server_id = factory_.NewServerId();
1725 EXPECT_TRUE(CreateWithDefaultTag(server_id, false));
1726 {
1727 WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__);
1728 MutableEntry me(&trans, GET_BY_CLIENT_TAG, test_tag_);
1729 EXPECT_TRUE(me.good());
1730 me.Put(UNIQUE_CLIENT_TAG, "");
1731 }
1732 {
1733 ReadTransaction trans(dir_.get(), __FILE__, __LINE__);
1734 Entry by_tag(&trans, GET_BY_CLIENT_TAG, test_tag_);
1735 EXPECT_FALSE(by_tag.good());
1736
1737 Entry by_id(&trans, GET_BY_ID, server_id);
1738 EXPECT_TRUE(by_id.good());
1739 EXPECT_TRUE(by_id.Get(UNIQUE_CLIENT_TAG).empty());
1740 }
1741 }
1742
TEST_F(SyncableClientTagTest,TestClientTagIndexServerId)1743 TEST_F(SyncableClientTagTest, TestClientTagIndexServerId) {
1744 Id server_id = factory_.NewServerId();
1745 EXPECT_TRUE(CreateWithDefaultTag(server_id, false));
1746 VerifyTag(server_id, false);
1747 }
1748
TEST_F(SyncableClientTagTest,TestClientTagIndexClientId)1749 TEST_F(SyncableClientTagTest, TestClientTagIndexClientId) {
1750 Id client_id = factory_.NewLocalId();
1751 EXPECT_TRUE(CreateWithDefaultTag(client_id, false));
1752 VerifyTag(client_id, false);
1753 }
1754
TEST_F(SyncableClientTagTest,TestDeletedClientTagIndexClientId)1755 TEST_F(SyncableClientTagTest, TestDeletedClientTagIndexClientId) {
1756 Id client_id = factory_.NewLocalId();
1757 EXPECT_TRUE(CreateWithDefaultTag(client_id, true));
1758 VerifyTag(client_id, true);
1759 }
1760
TEST_F(SyncableClientTagTest,TestDeletedClientTagIndexServerId)1761 TEST_F(SyncableClientTagTest, TestDeletedClientTagIndexServerId) {
1762 Id server_id = factory_.NewServerId();
1763 EXPECT_TRUE(CreateWithDefaultTag(server_id, true));
1764 VerifyTag(server_id, true);
1765 }
1766
TEST_F(SyncableClientTagTest,TestClientTagIndexDuplicateServer)1767 TEST_F(SyncableClientTagTest, TestClientTagIndexDuplicateServer) {
1768 EXPECT_TRUE(CreateWithDefaultTag(factory_.NewServerId(), true));
1769 EXPECT_FALSE(CreateWithDefaultTag(factory_.NewServerId(), true));
1770 EXPECT_FALSE(CreateWithDefaultTag(factory_.NewServerId(), false));
1771 EXPECT_FALSE(CreateWithDefaultTag(factory_.NewLocalId(), false));
1772 EXPECT_FALSE(CreateWithDefaultTag(factory_.NewLocalId(), true));
1773 }
1774
1775 } // namespace
1776 } // namespace syncable
1777