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