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