Roll src/third_party/WebKit 66fcf2e:f05b633 (svn 189594:189607)
[chromium-blink-merge.git] / sync / engine / directory_commit_contribution_unittest.cc
blobb5612b6d66c9c1fb88db5023460d507fa71a17e7
1 // Copyright 2014 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.
5 #include "sync/engine/directory_commit_contribution.h"
7 #include "base/message_loop/message_loop.h"
8 #include "sync/internal_api/public/base/attachment_id_proto.h"
9 #include "sync/sessions/status_controller.h"
10 #include "sync/syncable/entry.h"
11 #include "sync/syncable/mutable_entry.h"
12 #include "sync/syncable/syncable_read_transaction.h"
13 #include "sync/syncable/syncable_write_transaction.h"
14 #include "sync/test/engine/test_directory_setter_upper.h"
15 #include "sync/test/engine/test_id_factory.h"
16 #include "sync/test/engine/test_syncable_utils.h"
17 #include "testing/gtest/include/gtest/gtest.h"
19 namespace syncer {
21 class DirectoryCommitContributionTest : public ::testing::Test {
22 public:
23 void SetUp() override {
24 dir_maker_.SetUp();
26 syncable::WriteTransaction trans(FROM_HERE, syncable::UNITTEST, dir());
27 CreateTypeRoot(&trans, dir(), PREFERENCES);
28 CreateTypeRoot(&trans, dir(), EXTENSIONS);
29 CreateTypeRoot(&trans, dir(), ARTICLES);
30 CreateTypeRoot(&trans, dir(), BOOKMARKS);
33 void TearDown() override { dir_maker_.TearDown(); }
35 protected:
36 int64 CreateUnsyncedItemWithAttachments(
37 syncable::WriteTransaction* trans,
38 ModelType type,
39 const std::string& tag,
40 const sync_pb::AttachmentMetadata& attachment_metadata) {
41 syncable::Entry parent_entry(trans, syncable::GET_TYPE_ROOT, type);
42 syncable::MutableEntry entry(
43 trans,
44 syncable::CREATE,
45 type,
46 parent_entry.GetId(),
47 tag);
48 if (attachment_metadata.record_size() > 0) {
49 entry.PutAttachmentMetadata(attachment_metadata);
51 entry.PutIsUnsynced(true);
52 return entry.GetMetahandle();
55 int64 CreateUnsyncedItem(syncable::WriteTransaction* trans,
56 ModelType type,
57 const std::string& tag) {
58 return CreateUnsyncedItemWithAttachments(
59 trans, type, tag, sync_pb::AttachmentMetadata());
62 int64 CreateSyncedItem(syncable::WriteTransaction* trans,
63 ModelType type,
64 const std::string& tag) {
65 syncable::Entry parent_entry(trans, syncable::GET_TYPE_ROOT, type);
66 syncable::MutableEntry entry(
67 trans,
68 syncable::CREATE,
69 type,
70 parent_entry.GetId(),
71 tag);
73 entry.PutId(syncable::Id::CreateFromServerId(
74 id_factory_.NewServerId().GetServerId()));
75 entry.PutBaseVersion(10);
76 entry.PutServerVersion(10);
77 entry.PutIsUnappliedUpdate(false);
78 entry.PutIsUnsynced(false);
79 entry.PutIsDel(false);
80 entry.PutServerIsDel(false);
82 return entry.GetMetahandle();
85 void CreateSuccessfulCommitResponse(
86 const sync_pb::SyncEntity& entity,
87 sync_pb::CommitResponse::EntryResponse* response) {
88 response->set_response_type(sync_pb::CommitResponse::SUCCESS);
89 response->set_non_unique_name(entity.name());
90 response->set_version(entity.version() + 1);
91 response->set_parent_id_string(entity.parent_id_string());
93 if (entity.id_string()[0] == '-') // Look for the - in 'c-1234' style IDs.
94 response->set_id_string(id_factory_.NewServerId().GetServerId());
95 else
96 response->set_id_string(entity.id_string());
99 syncable::Directory* dir() {
100 return dir_maker_.directory();
103 TestIdFactory id_factory_;
105 // Used in construction of DirectoryTypeDebugInfoEmitters.
106 ObserverList<TypeDebugInfoObserver> type_observers_;
108 private:
109 base::MessageLoop loop_; // Neeed to initialize the directory.
110 TestDirectorySetterUpper dir_maker_;
113 // Verify that the DirectoryCommitContribution contains only entries of its
114 // specified type.
115 TEST_F(DirectoryCommitContributionTest, GatherByTypes) {
116 int64 pref1;
118 syncable::WriteTransaction trans(FROM_HERE, syncable::UNITTEST, dir());
119 pref1 = CreateUnsyncedItem(&trans, PREFERENCES, "pref1");
120 CreateUnsyncedItem(&trans, PREFERENCES, "pref2");
121 CreateUnsyncedItem(&trans, EXTENSIONS, "extension1");
124 DirectoryTypeDebugInfoEmitter emitter(PREFERENCES, &type_observers_);
125 scoped_ptr<DirectoryCommitContribution> cc(
126 DirectoryCommitContribution::Build(dir(), PREFERENCES, 5, &emitter));
127 ASSERT_EQ(2U, cc->GetNumEntries());
129 const std::vector<int64>& metahandles = cc->metahandles_;
130 EXPECT_TRUE(std::find(metahandles.begin(), metahandles.end(), pref1) !=
131 metahandles.end());
132 EXPECT_TRUE(std::find(metahandles.begin(), metahandles.end(), pref1) !=
133 metahandles.end());
135 cc->CleanUp();
138 // Verify that the DirectoryCommitContributionTest builder function
139 // truncates if necessary.
140 TEST_F(DirectoryCommitContributionTest, GatherAndTruncate) {
141 int64 pref1;
142 int64 pref2;
144 syncable::WriteTransaction trans(FROM_HERE, syncable::UNITTEST, dir());
145 pref1 = CreateUnsyncedItem(&trans, PREFERENCES, "pref1");
146 pref2 = CreateUnsyncedItem(&trans, PREFERENCES, "pref2");
147 CreateUnsyncedItem(&trans, EXTENSIONS, "extension1");
150 DirectoryTypeDebugInfoEmitter emitter(PREFERENCES, &type_observers_);
151 scoped_ptr<DirectoryCommitContribution> cc(
152 DirectoryCommitContribution::Build(dir(), PREFERENCES, 1, &emitter));
153 ASSERT_EQ(1U, cc->GetNumEntries());
155 int64 only_metahandle = cc->metahandles_[0];
156 EXPECT_TRUE(only_metahandle == pref1 || only_metahandle == pref2);
158 cc->CleanUp();
161 // Sanity check for building commits from DirectoryCommitContributions.
162 // This test makes two CommitContribution objects of different types and uses
163 // them to initialize a commit message. Then it checks that the contents of the
164 // commit message match those of the directory they came from.
165 TEST_F(DirectoryCommitContributionTest, PrepareCommit) {
167 syncable::WriteTransaction trans(FROM_HERE, syncable::UNITTEST, dir());
168 CreateUnsyncedItem(&trans, PREFERENCES, "pref1");
169 CreateUnsyncedItem(&trans, PREFERENCES, "pref2");
170 CreateUnsyncedItem(&trans, EXTENSIONS, "extension1");
173 DirectoryTypeDebugInfoEmitter emitter1(PREFERENCES, &type_observers_);
174 DirectoryTypeDebugInfoEmitter emitter2(EXTENSIONS, &type_observers_);
175 scoped_ptr<DirectoryCommitContribution> pref_cc(
176 DirectoryCommitContribution::Build(dir(), PREFERENCES, 25, &emitter1));
177 scoped_ptr<DirectoryCommitContribution> ext_cc(
178 DirectoryCommitContribution::Build(dir(), EXTENSIONS, 25, &emitter2));
180 sync_pb::ClientToServerMessage message;
181 pref_cc->AddToCommitMessage(&message);
182 ext_cc->AddToCommitMessage(&message);
184 const sync_pb::CommitMessage& commit_message = message.commit();
186 std::set<syncable::Id> ids_for_commit;
187 ASSERT_EQ(3, commit_message.entries_size());
188 for (int i = 0; i < commit_message.entries_size(); ++i) {
189 const sync_pb::SyncEntity& entity = commit_message.entries(i);
190 // The entities in this test have client-style IDs since they've never been
191 // committed before, so we must use CreateFromClientString to re-create them
192 // from the commit message.
193 ids_for_commit.insert(syncable::Id::CreateFromClientString(
194 entity.id_string()));
197 ASSERT_EQ(3U, ids_for_commit.size());
199 syncable::ReadTransaction trans(FROM_HERE, dir());
200 for (std::set<syncable::Id>::iterator it = ids_for_commit.begin();
201 it != ids_for_commit.end(); ++it) {
202 SCOPED_TRACE(it->value());
203 syncable::Entry entry(&trans, syncable::GET_BY_ID, *it);
204 ASSERT_TRUE(entry.good());
205 EXPECT_TRUE(entry.GetSyncing());
209 pref_cc->CleanUp();
210 ext_cc->CleanUp();
213 // Check that deletion requests include a model type.
214 // This was not always the case, but was implemented to allow us to loosen some
215 // other restrictions in the protocol.
216 TEST_F(DirectoryCommitContributionTest, DeletedItemsWithSpecifics) {
217 int64 pref1;
219 syncable::WriteTransaction trans(FROM_HERE, syncable::UNITTEST, dir());
220 pref1 = CreateSyncedItem(&trans, PREFERENCES, "pref1");
221 syncable::MutableEntry e1(&trans, syncable::GET_BY_HANDLE, pref1);
222 e1.PutIsDel(true);
223 e1.PutIsUnsynced(true);
226 DirectoryTypeDebugInfoEmitter emitter(PREFERENCES, &type_observers_);
227 scoped_ptr<DirectoryCommitContribution> pref_cc(
228 DirectoryCommitContribution::Build(dir(), PREFERENCES, 25, &emitter));
229 ASSERT_TRUE(pref_cc);
231 sync_pb::ClientToServerMessage message;
232 pref_cc->AddToCommitMessage(&message);
234 const sync_pb::CommitMessage& commit_message = message.commit();
235 ASSERT_EQ(1, commit_message.entries_size());
236 EXPECT_TRUE(
237 commit_message.entries(0).specifics().has_preference());
239 pref_cc->CleanUp();
242 // As ususal, bookmarks are special. Bookmark deletion is special.
243 // Deleted bookmarks include a valid "is folder" bit and their full specifics
244 // (especially the meta info, which is what server really wants).
245 TEST_F(DirectoryCommitContributionTest, DeletedBookmarksWithSpecifics) {
246 int64 bm1;
248 syncable::WriteTransaction trans(FROM_HERE, syncable::UNITTEST, dir());
249 bm1 = CreateSyncedItem(&trans, BOOKMARKS, "bm1");
250 syncable::MutableEntry e1(&trans, syncable::GET_BY_HANDLE, bm1);
252 e1.PutIsDir(true);
253 e1.PutServerIsDir(true);
255 sync_pb::EntitySpecifics specifics;
256 sync_pb::BookmarkSpecifics* bm_specifics = specifics.mutable_bookmark();
257 bm_specifics->set_url("http://www.chrome.com");
258 bm_specifics->set_title("Chrome");
259 sync_pb::MetaInfo* meta_info = bm_specifics->add_meta_info();
260 meta_info->set_key("K");
261 meta_info->set_value("V");
262 e1.PutSpecifics(specifics);
264 e1.PutIsDel(true);
265 e1.PutIsUnsynced(true);
268 DirectoryTypeDebugInfoEmitter emitter(BOOKMARKS, &type_observers_);
269 scoped_ptr<DirectoryCommitContribution> bm_cc(
270 DirectoryCommitContribution::Build(dir(), BOOKMARKS, 25, &emitter));
271 ASSERT_TRUE(bm_cc);
273 sync_pb::ClientToServerMessage message;
274 bm_cc->AddToCommitMessage(&message);
276 const sync_pb::CommitMessage& commit_message = message.commit();
277 ASSERT_EQ(1, commit_message.entries_size());
279 const sync_pb::SyncEntity& entity = commit_message.entries(0);
280 EXPECT_TRUE(entity.has_folder());
281 ASSERT_TRUE(entity.specifics().has_bookmark());
282 ASSERT_EQ(1, entity.specifics().bookmark().meta_info_size());
283 EXPECT_EQ("K", entity.specifics().bookmark().meta_info(0).key());
284 EXPECT_EQ("V", entity.specifics().bookmark().meta_info(0).value());
286 bm_cc->CleanUp();
289 // Test that bookmarks support hierarchy.
290 TEST_F(DirectoryCommitContributionTest, HierarchySupport_Bookmark) {
292 // Create a normal-looking bookmark item.
293 int64 bm1;
295 syncable::WriteTransaction trans(FROM_HERE, syncable::UNITTEST, dir());
296 bm1 = CreateSyncedItem(&trans, BOOKMARKS, "bm1");
297 syncable::MutableEntry e(&trans, syncable::GET_BY_HANDLE, bm1);
299 sync_pb::EntitySpecifics specifics;
300 sync_pb::BookmarkSpecifics* bm_specifics = specifics.mutable_bookmark();
301 bm_specifics->set_url("http://www.chrome.com");
302 bm_specifics->set_title("Chrome");
303 e.PutSpecifics(specifics);
305 e.PutIsDel(false);
306 e.PutIsUnsynced(true);
308 EXPECT_TRUE(e.ShouldMaintainHierarchy());
311 DirectoryTypeDebugInfoEmitter emitter(BOOKMARKS, &type_observers_);
312 scoped_ptr<DirectoryCommitContribution> bm_cc(
313 DirectoryCommitContribution::Build(dir(), BOOKMARKS, 25, &emitter));
315 sync_pb::ClientToServerMessage message;
316 bm_cc->AddToCommitMessage(&message);
317 const sync_pb::CommitMessage& commit_message = message.commit();
318 bm_cc->CleanUp();
320 ASSERT_EQ(1, commit_message.entries_size());
321 EXPECT_TRUE(commit_message.entries(0).has_parent_id_string());
322 EXPECT_FALSE(commit_message.entries(0).parent_id_string().empty());
325 // Test that preferences do not support hierarchy.
326 TEST_F(DirectoryCommitContributionTest, HierarchySupport_Preferences) {
327 // Create a normal-looking prefs item.
328 int64 pref1;
330 syncable::WriteTransaction trans(FROM_HERE, syncable::UNITTEST, dir());
331 pref1 = CreateUnsyncedItem(&trans, PREFERENCES, "pref1");
332 syncable::MutableEntry e(&trans, syncable::GET_BY_HANDLE, pref1);
334 EXPECT_FALSE(e.ShouldMaintainHierarchy());
337 DirectoryTypeDebugInfoEmitter emitter(PREFERENCES, &type_observers_);
338 scoped_ptr<DirectoryCommitContribution> pref_cc(
339 DirectoryCommitContribution::Build(dir(), PREFERENCES, 25, &emitter));
341 sync_pb::ClientToServerMessage message;
342 pref_cc->AddToCommitMessage(&message);
343 const sync_pb::CommitMessage& commit_message = message.commit();
344 pref_cc->CleanUp();
346 ASSERT_EQ(1, commit_message.entries_size());
347 EXPECT_FALSE(commit_message.entries(0).has_parent_id_string());
348 EXPECT_TRUE(commit_message.entries(0).parent_id_string().empty());
351 void AddAttachment(sync_pb::AttachmentMetadata* metadata, bool is_on_server) {
352 sync_pb::AttachmentMetadataRecord record;
353 *record.mutable_id() = CreateAttachmentIdProto();
354 record.set_is_on_server(is_on_server);
355 *metadata->add_record() = record;
358 // Creates some unsynced items, pretends to commit them, and hands back a
359 // specially crafted response to the syncer in order to test commit response
360 // processing. The response simulates a succesful commit scenario.
361 TEST_F(DirectoryCommitContributionTest, ProcessCommitResponse) {
362 int64 pref1_handle;
363 int64 pref2_handle;
364 int64 ext1_handle;
366 syncable::WriteTransaction trans(FROM_HERE, syncable::UNITTEST, dir());
367 pref1_handle = CreateUnsyncedItem(&trans, PREFERENCES, "pref1");
368 pref2_handle = CreateUnsyncedItem(&trans, PREFERENCES, "pref2");
369 ext1_handle = CreateUnsyncedItem(&trans, EXTENSIONS, "extension1");
372 DirectoryTypeDebugInfoEmitter emitter1(PREFERENCES, &type_observers_);
373 DirectoryTypeDebugInfoEmitter emitter2(EXTENSIONS, &type_observers_);
374 scoped_ptr<DirectoryCommitContribution> pref_cc(
375 DirectoryCommitContribution::Build(dir(), PREFERENCES, 25, &emitter1));
376 scoped_ptr<DirectoryCommitContribution> ext_cc(
377 DirectoryCommitContribution::Build(dir(), EXTENSIONS, 25, &emitter2));
379 sync_pb::ClientToServerMessage message;
380 pref_cc->AddToCommitMessage(&message);
381 ext_cc->AddToCommitMessage(&message);
383 const sync_pb::CommitMessage& commit_message = message.commit();
384 ASSERT_EQ(3, commit_message.entries_size());
386 sync_pb::ClientToServerResponse response;
387 for (int i = 0; i < commit_message.entries_size(); ++i) {
388 sync_pb::SyncEntity entity = commit_message.entries(i);
389 sync_pb::CommitResponse_EntryResponse* entry_response =
390 response.mutable_commit()->add_entryresponse();
391 CreateSuccessfulCommitResponse(entity, entry_response);
394 sessions::StatusController status;
396 // Process these in reverse order. Just because we can.
397 ext_cc->ProcessCommitResponse(response, &status);
398 pref_cc->ProcessCommitResponse(response, &status);
401 syncable::ReadTransaction trans(FROM_HERE, dir());
402 syncable::Entry p1(&trans, syncable::GET_BY_HANDLE, pref1_handle);
403 EXPECT_TRUE(p1.GetId().ServerKnows());
404 EXPECT_FALSE(p1.GetSyncing());
405 EXPECT_LT(0, p1.GetServerVersion());
407 syncable::Entry p2(&trans, syncable::GET_BY_HANDLE, pref2_handle);
408 EXPECT_TRUE(p2.GetId().ServerKnows());
409 EXPECT_FALSE(p2.GetSyncing());
410 EXPECT_LT(0, p2.GetServerVersion());
412 syncable::Entry e1(&trans, syncable::GET_BY_HANDLE, ext1_handle);
413 EXPECT_TRUE(e1.GetId().ServerKnows());
414 EXPECT_FALSE(e1.GetSyncing());
415 EXPECT_LT(0, e1.GetServerVersion());
418 pref_cc->CleanUp();
419 ext_cc->CleanUp();
422 // Creates some unsynced items with attachments and verifies that only items
423 // where all attachments have been uploaded to the server are eligible to be
424 // committed.
425 TEST_F(DirectoryCommitContributionTest, ProcessCommitResponseWithAttachments) {
426 int64 art1_handle;
427 int64 art2_handle;
428 int64 art3_handle;
430 syncable::WriteTransaction trans(FROM_HERE, syncable::UNITTEST, dir());
432 // art1 has two attachments, both have been uploaded to the server. art1 is
433 // eligible to be committed.
434 sync_pb::AttachmentMetadata art1_attachments;
435 AddAttachment(&art1_attachments, true /* is_on_server */);
436 AddAttachment(&art1_attachments, true /* is_on_server */);
437 art1_handle = CreateUnsyncedItemWithAttachments(
438 &trans, ARTICLES, "art1", art1_attachments);
440 // art2 has two attachments, one of which has been uploaded to the
441 // server. art2 is not eligible to be committed.
442 sync_pb::AttachmentMetadata art2_attachments;
443 AddAttachment(&art2_attachments, false /* is_on_server */);
444 AddAttachment(&art2_attachments, true /* is_on_server */);
445 art2_handle = CreateUnsyncedItemWithAttachments(
446 &trans, ARTICLES, "art2", art2_attachments);
448 // art3 has two attachments, neither of which have been uploaded to the
449 // server. art2 is not eligible to be committed.
450 sync_pb::AttachmentMetadata art3_attachments;
451 AddAttachment(&art3_attachments, false /* is_on_server */);
452 AddAttachment(&art3_attachments, false /* is_on_server */);
453 art3_handle = CreateUnsyncedItemWithAttachments(
454 &trans, ARTICLES, "art3", art3_attachments);
457 DirectoryTypeDebugInfoEmitter emitter(ARTICLES, &type_observers_);
458 scoped_ptr<DirectoryCommitContribution> art_cc(
459 DirectoryCommitContribution::Build(dir(), ARTICLES, 25, &emitter));
461 // Only art1 is ready.
462 EXPECT_EQ(1U, art_cc->GetNumEntries());
464 sync_pb::ClientToServerMessage message;
465 art_cc->AddToCommitMessage(&message);
467 const sync_pb::CommitMessage& commit_message = message.commit();
468 ASSERT_EQ(1, commit_message.entries_size());
470 sync_pb::ClientToServerResponse response;
471 for (int i = 0; i < commit_message.entries_size(); ++i) {
472 sync_pb::SyncEntity entity = commit_message.entries(i);
473 sync_pb::CommitResponse_EntryResponse* entry_response =
474 response.mutable_commit()->add_entryresponse();
475 CreateSuccessfulCommitResponse(entity, entry_response);
478 sessions::StatusController status;
479 art_cc->ProcessCommitResponse(response, &status);
481 syncable::ReadTransaction trans(FROM_HERE, dir());
483 syncable::Entry a1(&trans, syncable::GET_BY_HANDLE, art1_handle);
484 EXPECT_TRUE(a1.GetId().ServerKnows());
485 EXPECT_FALSE(a1.GetSyncing());
486 EXPECT_LT(0, a1.GetServerVersion());
488 syncable::Entry a2(&trans, syncable::GET_BY_HANDLE, art2_handle);
489 EXPECT_FALSE(a2.GetId().ServerKnows());
490 EXPECT_FALSE(a2.GetSyncing());
491 EXPECT_EQ(0, a2.GetServerVersion());
493 syncable::Entry a3(&trans, syncable::GET_BY_HANDLE, art3_handle);
494 EXPECT_FALSE(a3.GetId().ServerKnows());
495 EXPECT_FALSE(a3.GetSyncing());
496 EXPECT_EQ(0, a3.GetServerVersion());
499 art_cc->CleanUp();
502 } // namespace syncer