| 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 | 
|---|