Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(112)

Side by Side Diff: chrome/browser/sync/syncable/syncable_unittest.cc

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

Powered by Google App Engine
This is Rietveld 408576698