1// Copyright 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <string>
6
7#include "base/basictypes.h"
8#include "base/compiler_specific.h"
9#include "base/files/file_path.h"
10#include "base/files/file_util.h"
11#include "base/files/scoped_temp_dir.h"
12#include "base/location.h"
13#include "base/logging.h"
14#include "base/memory/scoped_ptr.h"
15#include "base/message_loop/message_loop.h"
16#include "base/stl_util.h"
17#include "base/synchronization/condition_variable.h"
18#include "base/test/values_test_util.h"
19#include "base/threading/platform_thread.h"
20#include "base/values.h"
21#include "sync/protocol/bookmark_specifics.pb.h"
22#include "sync/syncable/directory_backing_store.h"
23#include "sync/syncable/directory_change_delegate.h"
24#include "sync/syncable/directory_unittest.h"
25#include "sync/syncable/in_memory_directory_backing_store.h"
26#include "sync/syncable/metahandle_set.h"
27#include "sync/syncable/mutable_entry.h"
28#include "sync/syncable/on_disk_directory_backing_store.h"
29#include "sync/syncable/syncable_proto_util.h"
30#include "sync/syncable/syncable_read_transaction.h"
31#include "sync/syncable/syncable_util.h"
32#include "sync/syncable/syncable_write_transaction.h"
33#include "sync/test/engine/test_id_factory.h"
34#include "sync/test/engine/test_syncable_utils.h"
35#include "sync/test/fake_encryptor.h"
36#include "sync/test/null_directory_change_delegate.h"
37#include "sync/test/null_transaction_observer.h"
38#include "sync/util/test_unrecoverable_error_handler.h"
39#include "testing/gtest/include/gtest/gtest.h"
40
41namespace syncer {
42namespace syncable {
43
44using base::ExpectDictBooleanValue;
45using base::ExpectDictStringValue;
46
47// An OnDiskDirectoryBackingStore that can be set to always fail SaveChanges.
48class TestBackingStore : public OnDiskDirectoryBackingStore {
49 public:
50  TestBackingStore(const std::string& dir_name,
51                   const base::FilePath& backing_filepath);
52
53  virtual ~TestBackingStore();
54
55  virtual bool SaveChanges(const Directory::SaveChangesSnapshot& snapshot)
56      OVERRIDE;
57
58   void StartFailingSaveChanges() {
59     fail_save_changes_ = true;
60   }
61
62 private:
63   bool fail_save_changes_;
64};
65
66TestBackingStore::TestBackingStore(const std::string& dir_name,
67                                   const base::FilePath& backing_filepath)
68  : OnDiskDirectoryBackingStore(dir_name, backing_filepath),
69    fail_save_changes_(false) {
70}
71
72TestBackingStore::~TestBackingStore() { }
73
74bool TestBackingStore::SaveChanges(
75    const Directory::SaveChangesSnapshot& snapshot){
76  if (fail_save_changes_) {
77    return false;
78  } else {
79    return OnDiskDirectoryBackingStore::SaveChanges(snapshot);
80  }
81}
82
83// A directory whose Save() function can be set to always fail.
84class TestDirectory : public Directory {
85 public:
86  // A factory function used to work around some initialization order issues.
87  static TestDirectory* Create(
88      Encryptor *encryptor,
89      UnrecoverableErrorHandler *handler,
90      const std::string& dir_name,
91      const base::FilePath& backing_filepath);
92
93  virtual ~TestDirectory();
94
95  void StartFailingSaveChanges() {
96    backing_store_->StartFailingSaveChanges();
97  }
98
99 private:
100  TestDirectory(Encryptor* encryptor,
101                UnrecoverableErrorHandler* handler,
102                TestBackingStore* backing_store);
103
104  TestBackingStore* backing_store_;
105};
106
107TestDirectory* TestDirectory::Create(
108    Encryptor *encryptor,
109    UnrecoverableErrorHandler *handler,
110    const std::string& dir_name,
111    const base::FilePath& backing_filepath) {
112  TestBackingStore* backing_store =
113      new TestBackingStore(dir_name, backing_filepath);
114  return new TestDirectory(encryptor, handler, backing_store);
115}
116
117TestDirectory::TestDirectory(Encryptor* encryptor,
118                             UnrecoverableErrorHandler* handler,
119                             TestBackingStore* backing_store)
120    : Directory(backing_store, handler, NULL, NULL, NULL),
121      backing_store_(backing_store) {
122}
123
124TestDirectory::~TestDirectory() { }
125
126TEST(OnDiskSyncableDirectory, FailInitialWrite) {
127  base::MessageLoop message_loop;
128  FakeEncryptor encryptor;
129  TestUnrecoverableErrorHandler handler;
130  base::ScopedTempDir temp_dir;
131  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
132  base::FilePath file_path = temp_dir.path().Append(
133      FILE_PATH_LITERAL("Test.sqlite3"));
134  std::string name = "user@x.com";
135  NullDirectoryChangeDelegate delegate;
136
137  scoped_ptr<TestDirectory> test_dir(
138      TestDirectory::Create(&encryptor, &handler, name, file_path));
139
140  test_dir->StartFailingSaveChanges();
141  ASSERT_EQ(FAILED_INITIAL_WRITE, test_dir->Open(name, &delegate,
142                                                 NullTransactionObserver()));
143}
144
145// A variant of SyncableDirectoryTest that uses a real sqlite database.
146class OnDiskSyncableDirectoryTest : public SyncableDirectoryTest {
147 protected:
148  // SetUp() is called before each test case is run.
149  // The sqlite3 DB is deleted before each test is run.
150  virtual void SetUp() {
151    SyncableDirectoryTest::SetUp();
152    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
153    file_path_ = temp_dir_.path().Append(
154        FILE_PATH_LITERAL("Test.sqlite3"));
155    base::DeleteFile(file_path_, true);
156    CreateDirectory();
157  }
158
159  virtual void TearDown() {
160    // This also closes file handles.
161    dir()->SaveChanges();
162    dir().reset();
163    base::DeleteFile(file_path_, true);
164    SyncableDirectoryTest::TearDown();
165  }
166
167  // Creates a new directory.  Deletes the old directory, if it exists.
168  void CreateDirectory() {
169    test_directory_ = TestDirectory::Create(
170        encryptor(), unrecoverable_error_handler(), kDirectoryName, file_path_);
171    dir().reset(test_directory_);
172    ASSERT_TRUE(dir().get());
173    ASSERT_EQ(OPENED,
174              dir()->Open(kDirectoryName,
175                          directory_change_delegate(),
176                          NullTransactionObserver()));
177    ASSERT_TRUE(dir()->good());
178  }
179
180  void SaveAndReloadDir() {
181    dir()->SaveChanges();
182    CreateDirectory();
183  }
184
185  void StartFailingSaveChanges() {
186    test_directory_->StartFailingSaveChanges();
187  }
188
189  TestDirectory *test_directory_;  // mirrors scoped_ptr<Directory> dir_
190  base::ScopedTempDir temp_dir_;
191  base::FilePath file_path_;
192};
193
194sync_pb::DataTypeContext BuildContext(ModelType type) {
195  sync_pb::DataTypeContext context;
196  context.set_context("context");
197  context.set_data_type_id(GetSpecificsFieldNumberFromModelType(type));
198  return context;
199}
200
201TEST_F(OnDiskSyncableDirectoryTest, TestPurgeEntriesWithTypeIn) {
202  sync_pb::EntitySpecifics bookmark_specs;
203  sync_pb::EntitySpecifics autofill_specs;
204  sync_pb::EntitySpecifics preference_specs;
205  AddDefaultFieldValue(BOOKMARKS, &bookmark_specs);
206  AddDefaultFieldValue(PREFERENCES, &preference_specs);
207  AddDefaultFieldValue(AUTOFILL, &autofill_specs);
208
209  ModelTypeSet types_to_purge(PREFERENCES, AUTOFILL);
210
211  dir()->SetDownloadProgress(BOOKMARKS, BuildProgress(BOOKMARKS));
212  dir()->SetDownloadProgress(PREFERENCES, BuildProgress(PREFERENCES));
213  dir()->SetDownloadProgress(AUTOFILL, BuildProgress(AUTOFILL));
214
215  TestIdFactory id_factory;
216  // Create some items for each type.
217  {
218    WriteTransaction trans(FROM_HERE, UNITTEST, dir().get());
219
220    dir()->SetDataTypeContext(&trans, BOOKMARKS, BuildContext(BOOKMARKS));
221    dir()->SetDataTypeContext(&trans, PREFERENCES, BuildContext(PREFERENCES));
222    dir()->SetDataTypeContext(&trans, AUTOFILL, BuildContext(AUTOFILL));
223
224    // Make it look like these types have completed initial sync.
225    CreateTypeRoot(&trans, dir().get(), BOOKMARKS);
226    CreateTypeRoot(&trans, dir().get(), PREFERENCES);
227    CreateTypeRoot(&trans, dir().get(), AUTOFILL);
228
229    // Add more nodes for this type.  Technically, they should be placed under
230    // the proper type root nodes but the assertions in this test won't notice
231    // if their parent isn't quite right.
232    MutableEntry item1(&trans, CREATE, BOOKMARKS, trans.root_id(), "Item");
233    ASSERT_TRUE(item1.good());
234    item1.PutServerSpecifics(bookmark_specs);
235    item1.PutIsUnsynced(true);
236
237    MutableEntry item2(&trans, CREATE_NEW_UPDATE_ITEM,
238                       id_factory.NewServerId());
239    ASSERT_TRUE(item2.good());
240    item2.PutServerSpecifics(bookmark_specs);
241    item2.PutIsUnappliedUpdate(true);
242
243    MutableEntry item3(&trans, CREATE, PREFERENCES,
244                       trans.root_id(), "Item");
245    ASSERT_TRUE(item3.good());
246    item3.PutSpecifics(preference_specs);
247    item3.PutServerSpecifics(preference_specs);
248    item3.PutIsUnsynced(true);
249
250    MutableEntry item4(&trans, CREATE_NEW_UPDATE_ITEM,
251                       id_factory.NewServerId());
252    ASSERT_TRUE(item4.good());
253    item4.PutServerSpecifics(preference_specs);
254    item4.PutIsUnappliedUpdate(true);
255
256    MutableEntry item5(&trans, CREATE, AUTOFILL,
257                       trans.root_id(), "Item");
258    ASSERT_TRUE(item5.good());
259    item5.PutSpecifics(autofill_specs);
260    item5.PutServerSpecifics(autofill_specs);
261    item5.PutIsUnsynced(true);
262
263    MutableEntry item6(&trans, CREATE_NEW_UPDATE_ITEM,
264      id_factory.NewServerId());
265    ASSERT_TRUE(item6.good());
266    item6.PutServerSpecifics(autofill_specs);
267    item6.PutIsUnappliedUpdate(true);
268  }
269
270  dir()->SaveChanges();
271  {
272    ReadTransaction trans(FROM_HERE, dir().get());
273    MetahandleSet all_set;
274    GetAllMetaHandles(&trans, &all_set);
275    ASSERT_EQ(10U, all_set.size());
276  }
277
278  dir()->PurgeEntriesWithTypeIn(types_to_purge, ModelTypeSet(), ModelTypeSet());
279
280  // We first query the in-memory data, and then reload the directory (without
281  // saving) to verify that disk does not still have the data.
282  CheckPurgeEntriesWithTypeInSucceeded(types_to_purge, true);
283  SaveAndReloadDir();
284  CheckPurgeEntriesWithTypeInSucceeded(types_to_purge, false);
285}
286
287TEST_F(OnDiskSyncableDirectoryTest, TestShareInfo) {
288  dir()->set_store_birthday("Jan 31st");
289  const char* const bag_of_chips_array = "\0bag of chips";
290  const std::string bag_of_chips_string =
291      std::string(bag_of_chips_array, sizeof(bag_of_chips_array));
292  dir()->set_bag_of_chips(bag_of_chips_string);
293  {
294    ReadTransaction trans(FROM_HERE, dir().get());
295    EXPECT_EQ("Jan 31st", dir()->store_birthday());
296    EXPECT_EQ(bag_of_chips_string, dir()->bag_of_chips());
297  }
298  dir()->set_store_birthday("April 10th");
299  const char* const bag_of_chips2_array = "\0bag of chips2";
300  const std::string bag_of_chips2_string =
301      std::string(bag_of_chips2_array, sizeof(bag_of_chips2_array));
302  dir()->set_bag_of_chips(bag_of_chips2_string);
303  dir()->SaveChanges();
304  {
305    ReadTransaction trans(FROM_HERE, dir().get());
306    EXPECT_EQ("April 10th", dir()->store_birthday());
307    EXPECT_EQ(bag_of_chips2_string, dir()->bag_of_chips());
308  }
309  const char* const bag_of_chips3_array = "\0bag of chips3";
310  const std::string bag_of_chips3_string =
311      std::string(bag_of_chips3_array, sizeof(bag_of_chips3_array));
312  dir()->set_bag_of_chips(bag_of_chips3_string);
313  // Restore the directory from disk.  Make sure that nothing's changed.
314  SaveAndReloadDir();
315  {
316    ReadTransaction trans(FROM_HERE, dir().get());
317    EXPECT_EQ("April 10th", dir()->store_birthday());
318    EXPECT_EQ(bag_of_chips3_string, dir()->bag_of_chips());
319  }
320}
321
322TEST_F(OnDiskSyncableDirectoryTest,
323       TestSimpleFieldsPreservedDuringSaveChanges) {
324  Id update_id = TestIdFactory::FromNumber(1);
325  Id create_id;
326  EntryKernel create_pre_save, update_pre_save;
327  EntryKernel create_post_save, update_post_save;
328  std::string create_name =  "Create";
329
330  {
331    WriteTransaction trans(FROM_HERE, UNITTEST, dir().get());
332    MutableEntry create(
333        &trans, CREATE, BOOKMARKS, trans.root_id(), create_name);
334    MutableEntry update(&trans, CREATE_NEW_UPDATE_ITEM, update_id);
335    create.PutIsUnsynced(true);
336    update.PutIsUnappliedUpdate(true);
337    sync_pb::EntitySpecifics specifics;
338    specifics.mutable_bookmark()->set_favicon("PNG");
339    specifics.mutable_bookmark()->set_url("http://nowhere");
340    create.PutSpecifics(specifics);
341    update.PutServerSpecifics(specifics);
342    create_pre_save = create.GetKernelCopy();
343    update_pre_save = update.GetKernelCopy();
344    create_id = create.GetId();
345  }
346
347  dir()->SaveChanges();
348  dir().reset(
349      new Directory(new OnDiskDirectoryBackingStore(kDirectoryName, file_path_),
350                    unrecoverable_error_handler(),
351                    NULL,
352                    NULL,
353                    NULL));
354
355  ASSERT_TRUE(dir().get());
356  ASSERT_EQ(OPENED,
357            dir()->Open(kDirectoryName,
358                        directory_change_delegate(),
359                        NullTransactionObserver()));
360  ASSERT_TRUE(dir()->good());
361
362  {
363    ReadTransaction trans(FROM_HERE, dir().get());
364    Entry create(&trans, GET_BY_ID, create_id);
365    EXPECT_EQ(1, CountEntriesWithName(&trans, trans.root_id(), create_name));
366    Entry update(&trans, GET_BY_ID, update_id);
367    create_post_save = create.GetKernelCopy();
368    update_post_save = update.GetKernelCopy();
369  }
370  int i = BEGIN_FIELDS;
371  for ( ; i < INT64_FIELDS_END ; ++i) {
372    EXPECT_EQ(create_pre_save.ref((Int64Field)i) +
373                  (i == TRANSACTION_VERSION ? 1 : 0),
374              create_post_save.ref((Int64Field)i))
375              << "int64 field #" << i << " changed during save/load";
376    EXPECT_EQ(update_pre_save.ref((Int64Field)i),
377              update_post_save.ref((Int64Field)i))
378        << "int64 field #" << i << " changed during save/load";
379  }
380  for ( ; i < TIME_FIELDS_END ; ++i) {
381    EXPECT_EQ(create_pre_save.ref((TimeField)i),
382              create_post_save.ref((TimeField)i))
383              << "time field #" << i << " changed during save/load";
384    EXPECT_EQ(update_pre_save.ref((TimeField)i),
385              update_post_save.ref((TimeField)i))
386              << "time field #" << i << " changed during save/load";
387  }
388  for ( ; i < ID_FIELDS_END ; ++i) {
389    EXPECT_EQ(create_pre_save.ref((IdField)i),
390              create_post_save.ref((IdField)i))
391              << "id field #" << i << " changed during save/load";
392    EXPECT_EQ(update_pre_save.ref((IdField)i),
393              update_pre_save.ref((IdField)i))
394              << "id field #" << i << " changed during save/load";
395  }
396  for ( ; i < BIT_FIELDS_END ; ++i) {
397    EXPECT_EQ(create_pre_save.ref((BitField)i),
398              create_post_save.ref((BitField)i))
399              << "Bit field #" << i << " changed during save/load";
400    EXPECT_EQ(update_pre_save.ref((BitField)i),
401              update_post_save.ref((BitField)i))
402              << "Bit field #" << i << " changed during save/load";
403  }
404  for ( ; i < STRING_FIELDS_END ; ++i) {
405    EXPECT_EQ(create_pre_save.ref((StringField)i),
406              create_post_save.ref((StringField)i))
407              << "String field #" << i << " changed during save/load";
408    EXPECT_EQ(update_pre_save.ref((StringField)i),
409              update_post_save.ref((StringField)i))
410              << "String field #" << i << " changed during save/load";
411  }
412  for ( ; i < PROTO_FIELDS_END; ++i) {
413    EXPECT_EQ(create_pre_save.ref((ProtoField)i).SerializeAsString(),
414              create_post_save.ref((ProtoField)i).SerializeAsString())
415              << "Blob field #" << i << " changed during save/load";
416    EXPECT_EQ(update_pre_save.ref((ProtoField)i).SerializeAsString(),
417              update_post_save.ref((ProtoField)i).SerializeAsString())
418              << "Blob field #" << i << " changed during save/load";
419  }
420  for ( ; i < UNIQUE_POSITION_FIELDS_END; ++i) {
421    EXPECT_TRUE(create_pre_save.ref((UniquePositionField)i).Equals(
422        create_post_save.ref((UniquePositionField)i)))
423        << "Position field #" << i << " changed during save/load";
424    EXPECT_TRUE(update_pre_save.ref((UniquePositionField)i).Equals(
425        update_post_save.ref((UniquePositionField)i)))
426        << "Position field #" << i << " changed during save/load";
427  }
428}
429
430TEST_F(OnDiskSyncableDirectoryTest, TestSaveChangesFailure) {
431  int64 handle1 = 0;
432  // Set up an item using a regular, saveable directory.
433  {
434    WriteTransaction trans(FROM_HERE, UNITTEST, dir().get());
435
436    MutableEntry e1(&trans, CREATE, BOOKMARKS, trans.root_id(), "aguilera");
437    ASSERT_TRUE(e1.good());
438    EXPECT_TRUE(e1.GetKernelCopy().is_dirty());
439    handle1 = e1.GetMetahandle();
440    e1.PutBaseVersion(1);
441    e1.PutIsDir(true);
442    e1.PutId(TestIdFactory::FromNumber(101));
443    EXPECT_TRUE(e1.GetKernelCopy().is_dirty());
444    EXPECT_TRUE(IsInDirtyMetahandles(handle1));
445  }
446  ASSERT_TRUE(dir()->SaveChanges());
447
448  // Make sure the item is no longer dirty after saving,
449  // and make a modification.
450  {
451    WriteTransaction trans(FROM_HERE, UNITTEST, dir().get());
452
453    MutableEntry aguilera(&trans, GET_BY_HANDLE, handle1);
454    ASSERT_TRUE(aguilera.good());
455    EXPECT_FALSE(aguilera.GetKernelCopy().is_dirty());
456    EXPECT_EQ(aguilera.GetNonUniqueName(), "aguilera");
457    aguilera.PutNonUniqueName("overwritten");
458    EXPECT_TRUE(aguilera.GetKernelCopy().is_dirty());
459    EXPECT_TRUE(IsInDirtyMetahandles(handle1));
460  }
461  ASSERT_TRUE(dir()->SaveChanges());
462
463  // Now do some operations when SaveChanges() will fail.
464  StartFailingSaveChanges();
465  ASSERT_TRUE(dir()->good());
466
467  int64 handle2 = 0;
468  {
469    WriteTransaction trans(FROM_HERE, UNITTEST, dir().get());
470
471    MutableEntry aguilera(&trans, GET_BY_HANDLE, handle1);
472    ASSERT_TRUE(aguilera.good());
473    EXPECT_FALSE(aguilera.GetKernelCopy().is_dirty());
474    EXPECT_EQ(aguilera.GetNonUniqueName(), "overwritten");
475    EXPECT_FALSE(aguilera.GetKernelCopy().is_dirty());
476    EXPECT_FALSE(IsInDirtyMetahandles(handle1));
477    aguilera.PutNonUniqueName("christina");
478    EXPECT_TRUE(aguilera.GetKernelCopy().is_dirty());
479    EXPECT_TRUE(IsInDirtyMetahandles(handle1));
480
481    // New item.
482    MutableEntry kids_on_block(
483        &trans, CREATE, BOOKMARKS, trans.root_id(), "kids");
484    ASSERT_TRUE(kids_on_block.good());
485    handle2 = kids_on_block.GetMetahandle();
486    kids_on_block.PutBaseVersion(1);
487    kids_on_block.PutIsDir(true);
488    kids_on_block.PutId(TestIdFactory::FromNumber(102));
489    EXPECT_TRUE(kids_on_block.GetKernelCopy().is_dirty());
490    EXPECT_TRUE(IsInDirtyMetahandles(handle2));
491  }
492
493  // We are using an unsaveable directory, so this can't succeed.  However,
494  // the HandleSaveChangesFailure code path should have been triggered.
495  ASSERT_FALSE(dir()->SaveChanges());
496
497  // Make sure things were rolled back and the world is as it was before call.
498  {
499    ReadTransaction trans(FROM_HERE, dir().get());
500    Entry e1(&trans, GET_BY_HANDLE, handle1);
501    ASSERT_TRUE(e1.good());
502    EntryKernel aguilera = e1.GetKernelCopy();
503    Entry kids(&trans, GET_BY_HANDLE, handle2);
504    ASSERT_TRUE(kids.good());
505    EXPECT_TRUE(kids.GetKernelCopy().is_dirty());
506    EXPECT_TRUE(IsInDirtyMetahandles(handle2));
507    EXPECT_TRUE(aguilera.is_dirty());
508    EXPECT_TRUE(IsInDirtyMetahandles(handle1));
509  }
510}
511
512TEST_F(OnDiskSyncableDirectoryTest, TestSaveChangesFailureWithPurge) {
513  int64 handle1 = 0;
514  // Set up an item and progress marker using a regular, saveable directory.
515  dir()->SetDownloadProgress(BOOKMARKS, BuildProgress(BOOKMARKS));
516  {
517    WriteTransaction trans(FROM_HERE, UNITTEST, dir().get());
518
519    MutableEntry e1(&trans, CREATE, BOOKMARKS, trans.root_id(), "aguilera");
520    ASSERT_TRUE(e1.good());
521    EXPECT_TRUE(e1.GetKernelCopy().is_dirty());
522    handle1 = e1.GetMetahandle();
523    e1.PutBaseVersion(1);
524    e1.PutIsDir(true);
525    e1.PutId(TestIdFactory::FromNumber(101));
526    sync_pb::EntitySpecifics bookmark_specs;
527    AddDefaultFieldValue(BOOKMARKS, &bookmark_specs);
528    e1.PutSpecifics(bookmark_specs);
529    e1.PutServerSpecifics(bookmark_specs);
530    e1.PutId(TestIdFactory::FromNumber(101));
531    EXPECT_TRUE(e1.GetKernelCopy().is_dirty());
532    EXPECT_TRUE(IsInDirtyMetahandles(handle1));
533  }
534  ASSERT_TRUE(dir()->SaveChanges());
535
536  // Now do some operations while SaveChanges() is set to fail.
537  StartFailingSaveChanges();
538  ASSERT_TRUE(dir()->good());
539
540  ModelTypeSet set(BOOKMARKS);
541  dir()->PurgeEntriesWithTypeIn(set, ModelTypeSet(), ModelTypeSet());
542  EXPECT_TRUE(IsInMetahandlesToPurge(handle1));
543  ASSERT_FALSE(dir()->SaveChanges());
544  EXPECT_TRUE(IsInMetahandlesToPurge(handle1));
545}
546
547class SyncableDirectoryManagement : public testing::Test {
548 public:
549  virtual void SetUp() {
550    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
551  }
552
553  virtual void TearDown() {
554  }
555 protected:
556  base::MessageLoop message_loop_;
557  base::ScopedTempDir temp_dir_;
558  FakeEncryptor encryptor_;
559  TestUnrecoverableErrorHandler handler_;
560  NullDirectoryChangeDelegate delegate_;
561};
562
563TEST_F(SyncableDirectoryManagement, TestFileRelease) {
564  base::FilePath path =
565      temp_dir_.path().Append(Directory::kSyncDatabaseFilename);
566
567  Directory dir(new OnDiskDirectoryBackingStore("ScopeTest", path),
568                &handler_,
569                NULL,
570                NULL,
571                NULL);
572  DirOpenResult result =
573      dir.Open("ScopeTest", &delegate_, NullTransactionObserver());
574  ASSERT_EQ(result, OPENED);
575  dir.Close();
576
577  // Closing the directory should have released the backing database file.
578  ASSERT_TRUE(base::DeleteFile(path, true));
579}
580
581class SyncableClientTagTest : public SyncableDirectoryTest {
582 public:
583  static const int kBaseVersion = 1;
584  const char* test_name_;
585  const char* test_tag_;
586
587  SyncableClientTagTest() : test_name_("test_name"), test_tag_("dietcoke") {}
588
589  bool CreateWithDefaultTag(Id id, bool deleted) {
590    WriteTransaction wtrans(FROM_HERE, UNITTEST, dir().get());
591    MutableEntry me(&wtrans, CREATE, PREFERENCES,
592                    wtrans.root_id(), test_name_);
593    CHECK(me.good());
594    me.PutId(id);
595    if (id.ServerKnows()) {
596      me.PutBaseVersion(kBaseVersion);
597    }
598    me.PutIsUnsynced(true);
599    me.PutIsDel(deleted);
600    me.PutIsDir(false);
601    return me.PutUniqueClientTag(test_tag_);
602  }
603
604  // Verify an entry exists with the default tag.
605  void VerifyTag(Id id, bool deleted) {
606    // Should still be present and valid in the client tag index.
607    ReadTransaction trans(FROM_HERE, dir().get());
608    Entry me(&trans, GET_BY_CLIENT_TAG, test_tag_);
609    CHECK(me.good());
610    EXPECT_EQ(me.GetId(), id);
611    EXPECT_EQ(me.GetUniqueClientTag(), test_tag_);
612    EXPECT_EQ(me.GetIsDel(), deleted);
613
614    // We only sync deleted items that the server knew about.
615    if (me.GetId().ServerKnows() || !me.GetIsDel()) {
616      EXPECT_EQ(me.GetIsUnsynced(), true);
617    }
618  }
619
620 protected:
621  TestIdFactory factory_;
622};
623
624TEST_F(SyncableClientTagTest, TestClientTagClear) {
625  Id server_id = factory_.NewServerId();
626  EXPECT_TRUE(CreateWithDefaultTag(server_id, false));
627  {
628    WriteTransaction trans(FROM_HERE, UNITTEST, dir().get());
629    MutableEntry me(&trans, GET_BY_CLIENT_TAG, test_tag_);
630    EXPECT_TRUE(me.good());
631    me.PutUniqueClientTag(std::string());
632  }
633  {
634    ReadTransaction trans(FROM_HERE, dir().get());
635    Entry by_tag(&trans, GET_BY_CLIENT_TAG, test_tag_);
636    EXPECT_FALSE(by_tag.good());
637
638    Entry by_id(&trans, GET_BY_ID, server_id);
639    EXPECT_TRUE(by_id.good());
640    EXPECT_TRUE(by_id.GetUniqueClientTag().empty());
641  }
642}
643
644TEST_F(SyncableClientTagTest, TestClientTagIndexServerId) {
645  Id server_id = factory_.NewServerId();
646  EXPECT_TRUE(CreateWithDefaultTag(server_id, false));
647  VerifyTag(server_id, false);
648}
649
650TEST_F(SyncableClientTagTest, TestClientTagIndexClientId) {
651  Id client_id = factory_.NewLocalId();
652  EXPECT_TRUE(CreateWithDefaultTag(client_id, false));
653  VerifyTag(client_id, false);
654}
655
656TEST_F(SyncableClientTagTest, TestDeletedClientTagIndexClientId) {
657  Id client_id = factory_.NewLocalId();
658  EXPECT_TRUE(CreateWithDefaultTag(client_id, true));
659  VerifyTag(client_id, true);
660}
661
662TEST_F(SyncableClientTagTest, TestDeletedClientTagIndexServerId) {
663  Id server_id = factory_.NewServerId();
664  EXPECT_TRUE(CreateWithDefaultTag(server_id, true));
665  VerifyTag(server_id, true);
666}
667
668TEST_F(SyncableClientTagTest, TestClientTagIndexDuplicateServer) {
669  EXPECT_TRUE(CreateWithDefaultTag(factory_.NewServerId(), true));
670  EXPECT_FALSE(CreateWithDefaultTag(factory_.NewServerId(), true));
671  EXPECT_FALSE(CreateWithDefaultTag(factory_.NewServerId(), false));
672  EXPECT_FALSE(CreateWithDefaultTag(factory_.NewLocalId(), false));
673  EXPECT_FALSE(CreateWithDefaultTag(factory_.NewLocalId(), true));
674}
675
676}  // namespace syncable
677}  // namespace syncer
678