OLD | NEW |
| (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 | |
OLD | NEW |