Index: sync/syncable/directory_unittest.cc |
diff --git a/sync/syncable/directory_unittest.cc b/sync/syncable/directory_unittest.cc |
index b2bff36aa56131e62f75b098876ee1d6d69fbb92..ef11d3ba6e8da021876e1d95e5399910c89e836c 100644 |
--- a/sync/syncable/directory_unittest.cc |
+++ b/sync/syncable/directory_unittest.cc |
@@ -6,6 +6,11 @@ |
#include "base/strings/stringprintf.h" |
#include "base/test/values_test_util.h" |
+#include "sync/api/attachments/attachment.h" |
+#include "sync/internal_api/public/read_node.h" |
+#include "sync/internal_api/public/read_transaction.h" |
+#include "sync/internal_api/public/write_node.h" |
+#include "sync/internal_api/public/write_transaction.h" |
#include "sync/syncable/syncable_proto_util.h" |
#include "sync/syncable/syncable_util.h" |
#include "sync/syncable/syncable_write_transaction.h" |
@@ -62,24 +67,24 @@ void SyncableDirectoryTest::SetUp() { |
} |
void SyncableDirectoryTest::TearDown() { |
- if (dir_) |
- dir_->SaveChanges(); |
- dir_.reset(); |
+ if (user_share_.directory) |
+ user_share_.directory->SaveChanges(); |
+ user_share_.directory.reset(); |
} |
DirOpenResult SyncableDirectoryTest::ReopenDirectory() { |
// Use a TestDirectoryBackingStore and sql::Connection so we can have test |
// data persist across Directory object lifetimes while getting the |
// performance benefits of not writing to disk. |
- dir_.reset( |
+ user_share_.directory.reset( |
new Directory(new TestDirectoryBackingStore(kDirectoryName, &connection_), |
&handler_, |
NULL, |
NULL, |
NULL)); |
- DirOpenResult open_result = |
- dir_->Open(kDirectoryName, &delegate_, NullTransactionObserver()); |
+ DirOpenResult open_result = user_share_.directory->Open( |
+ kDirectoryName, &delegate_, NullTransactionObserver()); |
return open_result; |
} |
@@ -93,16 +98,30 @@ void SyncableDirectoryTest::CreateEntry(const std::string& entryname, |
const int id) { |
CreateEntry(entryname, TestIdFactory::FromNumber(id)); |
} |
+ |
void SyncableDirectoryTest::CreateEntry(const std::string& entryname, Id id) { |
- WriteTransaction wtrans(FROM_HERE, UNITTEST, dir_.get()); |
+ WriteTransaction wtrans(FROM_HERE, UNITTEST, user_share_.directory.get()); |
MutableEntry me(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(), entryname); |
ASSERT_TRUE(me.good()); |
me.PutId(id); |
me.PutIsUnsynced(true); |
} |
+void SyncableDirectoryTest::CreateEntryWithAttachmentMetadata( |
+ const ModelType& model_type, |
+ const std::string& client_tag, |
+ const sync_pb::AttachmentMetadata& attachment_metadata) { |
+ syncer::WriteTransaction trans(FROM_HERE, user_share()); |
+ syncer::ReadNode root_node(&trans); |
+ root_node.InitByRootLookup(); |
+ syncer::WriteNode node(&trans); |
+ ASSERT_EQ(node.InitUniqueByCreation(model_type, root_node, client_tag), |
+ syncer::WriteNode::INIT_SUCCESS); |
+ node.SetAttachmentMetadata(attachment_metadata); |
+} |
+ |
DirOpenResult SyncableDirectoryTest::SimulateSaveAndReloadDir() { |
- if (!dir_->SaveChanges()) |
+ if (!user_share_.directory->SaveChanges()) |
return FAILED_IN_UNITTEST; |
return ReopenDirectory(); |
@@ -114,7 +133,7 @@ DirOpenResult SyncableDirectoryTest::SimulateCrashAndReloadDir() { |
void SyncableDirectoryTest::GetAllMetaHandles(BaseTransaction* trans, |
MetahandleSet* result) { |
- dir_->GetAllMetaHandles(trans, result); |
+ user_share_.directory->GetAllMetaHandles(trans, result); |
} |
void SyncableDirectoryTest::CheckPurgeEntriesWithTypeInSucceeded( |
@@ -122,12 +141,13 @@ void SyncableDirectoryTest::CheckPurgeEntriesWithTypeInSucceeded( |
bool before_reload) { |
SCOPED_TRACE(testing::Message("Before reload: ") << before_reload); |
{ |
- ReadTransaction trans(FROM_HERE, dir_.get()); |
+ ReadTransaction trans(FROM_HERE, user_share_.directory.get()); |
MetahandleSet all_set; |
- dir_->GetAllMetaHandles(&trans, &all_set); |
+ user_share_.directory->GetAllMetaHandles(&trans, &all_set); |
EXPECT_EQ(4U, all_set.size()); |
if (before_reload) |
- EXPECT_EQ(6U, dir_->kernel_->metahandles_to_purge.size()); |
+ EXPECT_EQ(6U, |
+ user_share_.directory->kernel_->metahandles_to_purge.size()); |
for (MetahandleSet::iterator iter = all_set.begin(); iter != all_set.end(); |
++iter) { |
Entry e(&trans, GET_BY_HANDLE, *iter); |
@@ -144,30 +164,54 @@ void SyncableDirectoryTest::CheckPurgeEntriesWithTypeInSucceeded( |
for (ModelTypeSet::Iterator it = types_to_purge.First(); it.Good(); |
it.Inc()) { |
- EXPECT_FALSE(dir_->InitialSyncEndedForType(it.Get())); |
+ EXPECT_FALSE(user_share_.directory->InitialSyncEndedForType(it.Get())); |
sync_pb::DataTypeProgressMarker progress; |
- dir_->GetDownloadProgress(it.Get(), &progress); |
+ user_share_.directory->GetDownloadProgress(it.Get(), &progress); |
EXPECT_EQ("", progress.token()); |
- ReadTransaction trans(FROM_HERE, dir_.get()); |
+ ReadTransaction trans(FROM_HERE, user_share_.directory.get()); |
sync_pb::DataTypeContext context; |
- dir_->GetDataTypeContext(&trans, it.Get(), &context); |
+ user_share_.directory->GetDataTypeContext(&trans, it.Get(), &context); |
EXPECT_TRUE(context.SerializeAsString().empty()); |
} |
EXPECT_FALSE(types_to_purge.Has(BOOKMARKS)); |
- EXPECT_TRUE(dir_->InitialSyncEndedForType(BOOKMARKS)); |
+ EXPECT_TRUE(user_share_.directory->InitialSyncEndedForType(BOOKMARKS)); |
} |
bool SyncableDirectoryTest::IsInDirtyMetahandles(int64 metahandle) { |
- return 1 == dir_->kernel_->dirty_metahandles.count(metahandle); |
+ return 1 == |
+ user_share_.directory->kernel_->dirty_metahandles.count(metahandle); |
} |
bool SyncableDirectoryTest::IsInMetahandlesToPurge(int64 metahandle) { |
- return 1 == dir_->kernel_->metahandles_to_purge.count(metahandle); |
+ return 1 == |
+ user_share_.directory->kernel_->metahandles_to_purge.count(metahandle); |
+} |
+ |
+BaseNode::InitByLookupResult SyncableDirectoryTest::LookupEntryByClientTag( |
+ const ModelType& model_type, |
+ const std::string& client_tag) { |
+ syncer::ReadTransaction trans(FROM_HERE, user_share()); |
+ syncer::ReadNode node(&trans); |
+ return node.InitByClientTagLookup(model_type, client_tag); |
+} |
+ |
+void SyncableDirectoryTest::ReplaceWithTombstone( |
+ const ModelType& model_type, |
+ const std::string& client_tag) { |
+ syncer::WriteTransaction trans(FROM_HERE, user_share()); |
+ syncer::WriteNode node(&trans); |
+ ASSERT_EQ(node.InitByClientTagLookup(model_type, client_tag), |
+ syncer::WriteNode::INIT_OK); |
+ node.Tombstone(); |
} |
scoped_ptr<Directory>& SyncableDirectoryTest::dir() { |
- return dir_; |
+ return user_share_.directory; |
+} |
+ |
+UserShare* SyncableDirectoryTest::user_share() { |
+ return &user_share_; |
} |
DirectoryChangeDelegate* SyncableDirectoryTest::directory_change_delegate() { |
@@ -1521,6 +1565,58 @@ TEST_F(SyncableDirectoryTest, StressTransactions) { |
} |
} |
+// Verify that Directory keeps track of which attachments are referenced by |
+// which entries. |
+TEST_F(SyncableDirectoryTest, AttachmentLinking) { |
+ // Add an entry with an attachment. |
+ std::string tag1("some tag"); |
+ syncer::AttachmentId attachment_id(syncer::AttachmentId::Create()); |
+ sync_pb::AttachmentMetadata attachment_metadata; |
+ sync_pb::AttachmentMetadataRecord* record = attachment_metadata.add_record(); |
+ *record->mutable_id() = attachment_id.GetProto(); |
+ ASSERT_FALSE(dir()->IsAttachmentLinked(attachment_id)); |
+ CreateEntryWithAttachmentMetadata(PREFERENCES, tag1, attachment_metadata); |
+ |
+ // See that the directory knows it's linked. |
+ ASSERT_TRUE(dir()->IsAttachmentLinked(attachment_id)); |
+ |
+ // Add a second entry referencing the same attachment. |
+ std::string tag2("some other tag"); |
+ CreateEntryWithAttachmentMetadata(PREFERENCES, tag2, attachment_metadata); |
+ |
+ // See that the directory knows it's still linked. |
+ ASSERT_TRUE(dir()->IsAttachmentLinked(attachment_id)); |
+ |
+ // Tombstone the first entry. |
+ ReplaceWithTombstone(syncer::PREFERENCES, tag1); |
+ |
+ // See that the attachment is still considered linked because the entry hasn't |
+ // been purged from the Directory. |
+ ASSERT_TRUE(dir()->IsAttachmentLinked(attachment_id)); |
+ |
+ // Save changes and see that the entry is truly gone. |
+ ASSERT_TRUE(dir()->SaveChanges()); |
+ ASSERT_EQ(LookupEntryByClientTag(PREFERENCES, tag1), |
+ syncer::WriteNode::INIT_FAILED_ENTRY_NOT_GOOD); |
+ |
+ // However, the attachment is still linked. |
+ ASSERT_TRUE(dir()->IsAttachmentLinked(attachment_id)); |
+ |
+ // Save, destroy, and recreate the directory. See that it's still linked. |
+ ASSERT_TRUE(dir()->SaveChanges()); |
+ ASSERT_EQ(syncable::OPENED, ReopenDirectory()); |
+ ASSERT_TRUE(dir()->IsAttachmentLinked(attachment_id)); |
+ |
+ // Tombstone the second entry, save changes, see that it's truly gone. |
+ ReplaceWithTombstone(syncer::PREFERENCES, tag2); |
+ ASSERT_TRUE(dir()->SaveChanges()); |
+ ASSERT_EQ(LookupEntryByClientTag(PREFERENCES, tag2), |
+ syncer::WriteNode::INIT_FAILED_ENTRY_NOT_GOOD); |
+ |
+ // Finally, the attachment is no longer linked. |
+ ASSERT_FALSE(dir()->IsAttachmentLinked(attachment_id)); |
+} |
+ |
} // namespace syncable |
} // namespace syncer |