Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(4)

Side by Side Diff: sync/engine/process_commit_response_command_unittest.cc

Issue 11636006: WIP: The Bookmark Position Megapatch (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Various updates, including switch suffix to unique_client_tag style Created 8 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « sync/engine/process_commit_response_command.cc ('k') | sync/engine/process_updates_command.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "sync/engine/process_commit_response_command.h" 5 #include "sync/engine/process_commit_response_command.h"
6 6
7 #include <vector> 7 #include <vector>
8 8
9 #include "base/location.h" 9 #include "base/location.h"
10 #include "base/stringprintf.h" 10 #include "base/stringprintf.h"
11 #include "sync/internal_api/public/test/test_entry_factory.h"
11 #include "sync/protocol/bookmark_specifics.pb.h" 12 #include "sync/protocol/bookmark_specifics.pb.h"
12 #include "sync/protocol/sync.pb.h" 13 #include "sync/protocol/sync.pb.h"
13 #include "sync/sessions/sync_session.h" 14 #include "sync/sessions/sync_session.h"
14 #include "sync/syncable/entry.h" 15 #include "sync/syncable/entry.h"
15 #include "sync/syncable/mutable_entry.h" 16 #include "sync/syncable/mutable_entry.h"
16 #include "sync/syncable/read_transaction.h" 17 #include "sync/syncable/read_transaction.h"
17 #include "sync/syncable/syncable_id.h" 18 #include "sync/syncable/syncable_id.h"
18 #include "sync/syncable/syncable_proto_util.h" 19 #include "sync/syncable/syncable_proto_util.h"
19 #include "sync/syncable/write_transaction.h" 20 #include "sync/syncable/write_transaction.h"
20 #include "sync/test/engine/fake_model_worker.h" 21 #include "sync/test/engine/fake_model_worker.h"
21 #include "sync/test/engine/syncer_command_test.h" 22 #include "sync/test/engine/syncer_command_test.h"
22 #include "sync/test/engine/test_id_factory.h" 23 #include "sync/test/engine/test_id_factory.h"
23 #include "testing/gtest/include/gtest/gtest.h" 24 #include "testing/gtest/include/gtest/gtest.h"
24 25
25 using std::string; 26 using std::string;
26 using sync_pb::ClientToServerMessage; 27 using sync_pb::ClientToServerMessage;
27 using sync_pb::CommitResponse; 28 using sync_pb::CommitResponse;
28 29
29 namespace syncer { 30 namespace syncer {
30 31
31 using sessions::SyncSession; 32 using sessions::SyncSession;
32 using syncable::BASE_VERSION; 33 using syncable::BASE_VERSION;
33 using syncable::Entry; 34 using syncable::Entry;
35 using syncable::ID;
34 using syncable::IS_DIR; 36 using syncable::IS_DIR;
35 using syncable::IS_UNSYNCED; 37 using syncable::IS_UNSYNCED;
36 using syncable::Id; 38 using syncable::Id;
37 using syncable::MutableEntry; 39 using syncable::MutableEntry;
38 using syncable::NON_UNIQUE_NAME; 40 using syncable::NON_UNIQUE_NAME;
41 using syncable::UNIQUE_POSITION;
39 using syncable::UNITTEST; 42 using syncable::UNITTEST;
40 using syncable::WriteTransaction; 43 using syncable::WriteTransaction;
41 44
42 // A test fixture for tests exercising ProcessCommitResponseCommand. 45 // A test fixture for tests exercising ProcessCommitResponseCommand.
43 class ProcessCommitResponseCommandTest : public SyncerCommandTest { 46 class ProcessCommitResponseCommandTest : public SyncerCommandTest {
44 public: 47 public:
45 virtual void SetUp() { 48 virtual void SetUp() {
46 workers()->clear(); 49 workers()->clear();
47 mutable_routing_info()->clear(); 50 mutable_routing_info()->clear();
48 51
49 workers()->push_back( 52 workers()->push_back(
50 make_scoped_refptr(new FakeModelWorker(GROUP_DB))); 53 make_scoped_refptr(new FakeModelWorker(GROUP_DB)));
51 workers()->push_back( 54 workers()->push_back(
52 make_scoped_refptr(new FakeModelWorker(GROUP_UI))); 55 make_scoped_refptr(new FakeModelWorker(GROUP_UI)));
53 (*mutable_routing_info())[BOOKMARKS] = GROUP_UI; 56 (*mutable_routing_info())[BOOKMARKS] = GROUP_UI;
54 (*mutable_routing_info())[PREFERENCES] = GROUP_UI; 57 (*mutable_routing_info())[PREFERENCES] = GROUP_UI;
55 (*mutable_routing_info())[AUTOFILL] = GROUP_DB; 58 (*mutable_routing_info())[AUTOFILL] = GROUP_DB;
56 59
57 SyncerCommandTest::SetUp(); 60 SyncerCommandTest::SetUp();
61
62 test_entry_factory_.reset(new TestEntryFactory(directory()));
58 } 63 }
59 64
60 protected: 65 protected:
61 66
62 ProcessCommitResponseCommandTest() 67 ProcessCommitResponseCommandTest()
63 : next_old_revision_(1), 68 : next_new_revision_(4000),
64 next_new_revision_(4000),
65 next_server_position_(10000) { 69 next_server_position_(10000) {
66 } 70 }
67 71
68 void CheckEntry(Entry* e, const std::string& name, 72 void CheckEntry(Entry* e, const std::string& name,
69 ModelType model_type, const Id& parent_id) { 73 ModelType model_type, const Id& parent_id) {
70 EXPECT_TRUE(e->good()); 74 EXPECT_TRUE(e->good());
71 ASSERT_EQ(name, e->Get(NON_UNIQUE_NAME)); 75 ASSERT_EQ(name, e->Get(NON_UNIQUE_NAME));
72 ASSERT_EQ(model_type, e->GetModelType()); 76 ASSERT_EQ(model_type, e->GetModelType());
73 ASSERT_EQ(parent_id, e->Get(syncable::PARENT_ID)); 77 ASSERT_EQ(parent_id, e->Get(syncable::PARENT_ID));
74 ASSERT_LT(0, e->Get(BASE_VERSION)) 78 ASSERT_LT(0, e->Get(BASE_VERSION))
75 << "Item should have a valid (positive) server base revision"; 79 << "Item should have a valid (positive) server base revision";
76 } 80 }
77 81
78 // Create an unsynced item in the database. If item_id is a local ID, it 82 int CreateUnprocessedBookmarkCommitResult(
79 // will be treated as a create-new. Otherwise, if it's a server ID, we'll
80 // fake the server data so that it looks like it exists on the server.
81 // Returns the methandle of the created item in |metahandle_out| if not NULL.
82 void CreateUnsyncedItem(const Id& item_id,
83 const Id& parent_id,
84 const string& name,
85 bool is_folder,
86 ModelType model_type,
87 int64* metahandle_out) {
88 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
89 Id predecessor_id;
90 ASSERT_TRUE(
91 directory()->GetLastChildIdForTest(&trans, parent_id, &predecessor_id));
92 MutableEntry entry(&trans, syncable::CREATE, parent_id, name);
93 ASSERT_TRUE(entry.good());
94 entry.Put(syncable::ID, item_id);
95 entry.Put(syncable::BASE_VERSION,
96 item_id.ServerKnows() ? next_old_revision_++ : 0);
97 entry.Put(syncable::IS_UNSYNCED, true);
98 entry.Put(syncable::IS_DIR, is_folder);
99 entry.Put(syncable::IS_DEL, false);
100 entry.Put(syncable::PARENT_ID, parent_id);
101 entry.PutPredecessor(predecessor_id);
102 sync_pb::EntitySpecifics default_specifics;
103 AddDefaultFieldValue(model_type, &default_specifics);
104 entry.Put(syncable::SPECIFICS, default_specifics);
105 if (item_id.ServerKnows()) {
106 entry.Put(syncable::SERVER_SPECIFICS, default_specifics);
107 entry.Put(syncable::SERVER_IS_DIR, is_folder);
108 entry.Put(syncable::SERVER_PARENT_ID, parent_id);
109 entry.Put(syncable::SERVER_IS_DEL, false);
110 }
111 if (metahandle_out)
112 *metahandle_out = entry.Get(syncable::META_HANDLE);
113 }
114
115 // Create a new unsynced item in the database, and synthesize a commit
116 // record and a commit response for it in the syncer session. If item_id
117 // is a local ID, the item will be a create operation. Otherwise, it
118 // will be an edit.
119 void CreateUnprocessedCommitResult(
120 const Id& item_id, 83 const Id& item_id,
121 const Id& parent_id, 84 const Id& parent_id,
122 const string& name, 85 const string& name,
123 ModelType model_type, 86 bool is_folder,
124 sessions::OrderedCommitSet *commit_set, 87 sessions::OrderedCommitSet *commit_set,
125 sync_pb::ClientToServerMessage *commit, 88 sync_pb::ClientToServerMessage *commit,
126 sync_pb::ClientToServerResponse *response) { 89 sync_pb::ClientToServerResponse *response) {
127 bool is_folder = true; 90 int64 metahandle;
128 int64 metahandle = 0; 91 test_entry_factory_->CreateUnsyncedBookmarkItem(
129 CreateUnsyncedItem(item_id, parent_id, name, is_folder, model_type, 92 item_id, parent_id, name, is_folder, &metahandle);
130 &metahandle);
131 93
132 // ProcessCommitResponseCommand consumes commit_ids from the session 94 // ProcessCommitResponseCommand consumes commit_ids from the session
133 // state, so we need to update that. O(n^2) because it's a test. 95 // state, so we need to update that. O(n^2) because it's a test.
134 commit_set->AddCommitItem(metahandle, item_id, model_type); 96 commit_set->AddCommitItem(metahandle, item_id, BOOKMARKS);
135 97
136 WriteTransaction trans(FROM_HERE, UNITTEST, directory()); 98 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
137 MutableEntry entry(&trans, syncable::GET_BY_ID, item_id); 99 MutableEntry entry(&trans, syncable::GET_BY_ID, item_id);
138 ASSERT_TRUE(entry.good()); 100 EXPECT_TRUE(entry.good());
139 entry.Put(syncable::SYNCING, true); 101 entry.Put(syncable::SYNCING, true);
140 102
141 // Add to the commit message. 103 // Add to the commit message.
142 commit->set_message_contents(ClientToServerMessage::COMMIT); 104 commit->set_message_contents(ClientToServerMessage::COMMIT);
143 sync_pb::SyncEntity* entity = commit->mutable_commit()->add_entries(); 105 sync_pb::SyncEntity* entity = commit->mutable_commit()->add_entries();
144 entity->set_non_unique_name(name); 106 entity->set_non_unique_name(name);
145 entity->set_folder(is_folder); 107 entity->set_folder(is_folder);
146 entity->set_parent_id_string(SyncableIdToProto(parent_id)); 108 entity->set_parent_id_string(SyncableIdToProto(parent_id));
147 entity->set_version(entry.Get(syncable::BASE_VERSION)); 109 entity->set_version(entry.Get(syncable::BASE_VERSION));
148 entity->mutable_specifics()->CopyFrom(entry.Get(syncable::SPECIFICS)); 110 entity->mutable_specifics()->CopyFrom(entry.Get(syncable::SPECIFICS));
(...skipping 17 matching lines...) Expand all
166 // rewritten, rewrite it in the entry response. This matches 128 // rewritten, rewrite it in the entry response. This matches
167 // the server behavior. 129 // the server behavior.
168 entry_response->set_parent_id_string(entity->parent_id_string()); 130 entry_response->set_parent_id_string(entity->parent_id_string());
169 for (int i = 0; i < commit->commit().entries_size(); ++i) { 131 for (int i = 0; i < commit->commit().entries_size(); ++i) {
170 if (commit->commit().entries(i).id_string() == 132 if (commit->commit().entries(i).id_string() ==
171 entity->parent_id_string()) { 133 entity->parent_id_string()) {
172 entry_response->set_parent_id_string( 134 entry_response->set_parent_id_string(
173 response->commit().entryresponse(i).id_string()); 135 response->commit().entryresponse(i).id_string());
174 } 136 }
175 } 137 }
138
139 return metahandle;
140 }
141
142 // Create a new unsynced item in the database, and synthesize a commit
143 // record and a commit response for it in the syncer session. If item_id
144 // is a local ID, the item will be a create operation. Otherwise, it
145 // will be an edit.
146 int CreateUnprocessedCommitResult(
147 const Id& item_id,
148 const Id& parent_id,
149 const string& name,
150 ModelType model_type,
151 sessions::OrderedCommitSet *commit_set,
152 sync_pb::ClientToServerMessage *commit,
153 sync_pb::ClientToServerResponse *response) {
154 int64 metahandle = 0;
155 test_entry_factory_->CreateUnsyncedItem(item_id, parent_id, name,
156 model_type, &metahandle);
157
158 // ProcessCommitResponseCommand consumes commit_ids from the session
159 // state, so we need to update that. O(n^2) because it's a test.
160 commit_set->AddCommitItem(metahandle, item_id, model_type);
161
162 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
163 MutableEntry entry(&trans, syncable::GET_BY_ID, item_id);
164 EXPECT_TRUE(entry.good());
165 entry.Put(syncable::SYNCING, true);
166
167 // Add to the commit message.
168 commit->set_message_contents(ClientToServerMessage::COMMIT);
169 sync_pb::SyncEntity* entity = commit->mutable_commit()->add_entries();
170 entity->set_non_unique_name(name);
171 entity->set_folder(false);
172 entity->set_parent_id_string(SyncableIdToProto(parent_id));
173 entity->set_version(entry.Get(syncable::BASE_VERSION));
174 entity->mutable_specifics()->CopyFrom(entry.Get(syncable::SPECIFICS));
175 entity->set_id_string(SyncableIdToProto(item_id));
176
177 // Should be a hash, but this is good enough for our purposes.
178 entity->set_client_defined_unique_tag(name);
179
180 // Add to the response message.
181 response->set_error_code(sync_pb::SyncEnums::SUCCESS);
182 sync_pb::CommitResponse_EntryResponse* entry_response =
183 response->mutable_commit()->add_entryresponse();
184 entry_response->set_response_type(CommitResponse::SUCCESS);
185 entry_response->set_name("Garbage.");
186 entry_response->set_non_unique_name(entity->name());
187 if (item_id.ServerKnows())
188 entry_response->set_id_string(entity->id_string());
189 else
190 entry_response->set_id_string(id_factory_.NewServerId().GetServerId());
191 entry_response->set_version(next_new_revision_++);
192 entry_response->set_position_in_parent(next_server_position_++);
193
194 return metahandle;
176 } 195 }
177 196
178 void SetLastErrorCode(sync_pb::CommitResponse::ResponseType error_code, 197 void SetLastErrorCode(sync_pb::CommitResponse::ResponseType error_code,
179 sync_pb::ClientToServerResponse* response) { 198 sync_pb::ClientToServerResponse* response) {
180 sync_pb::CommitResponse_EntryResponse* entry_response = 199 sync_pb::CommitResponse_EntryResponse* entry_response =
181 response->mutable_commit()->mutable_entryresponse( 200 response->mutable_commit()->mutable_entryresponse(
182 response->mutable_commit()->entryresponse_size() - 1); 201 response->mutable_commit()->entryresponse_size() - 1);
183 entry_response->set_response_type(error_code); 202 entry_response->set_response_type(error_code);
184 } 203 }
185 204
186 TestIdFactory id_factory_; 205 TestIdFactory id_factory_;
206 scoped_ptr<TestEntryFactory> test_entry_factory_;
187 private: 207 private:
188 int64 next_old_revision_;
189 int64 next_new_revision_; 208 int64 next_new_revision_;
190 int64 next_server_position_; 209 int64 next_server_position_;
191 DISALLOW_COPY_AND_ASSIGN(ProcessCommitResponseCommandTest); 210 DISALLOW_COPY_AND_ASSIGN(ProcessCommitResponseCommandTest);
192 }; 211 };
193 212
194 TEST_F(ProcessCommitResponseCommandTest, MultipleCommitIdProjections) { 213 TEST_F(ProcessCommitResponseCommandTest, MultipleCommitIdProjections) {
195 sessions::OrderedCommitSet commit_set(session()->routing_info()); 214 sessions::OrderedCommitSet commit_set(session()->routing_info());
196 sync_pb::ClientToServerMessage request; 215 sync_pb::ClientToServerMessage request;
197 sync_pb::ClientToServerResponse response; 216 sync_pb::ClientToServerResponse response;
198 217
199 Id bookmark_folder_id = id_factory_.NewLocalId(); 218 Id bookmark_folder_id = id_factory_.NewLocalId();
200 Id bookmark_id1 = id_factory_.NewLocalId(); 219 int bookmark_folder_handle = CreateUnprocessedBookmarkCommitResult(
201 Id bookmark_id2 = id_factory_.NewLocalId(); 220 bookmark_folder_id, id_factory_.root(), "A bookmark folder", true,
202 Id pref_id1 = id_factory_.NewLocalId(), pref_id2 = id_factory_.NewLocalId(); 221 &commit_set, &request, &response);
203 Id autofill_id1 = id_factory_.NewLocalId(); 222 int bookmark1_handle = CreateUnprocessedBookmarkCommitResult(
204 Id autofill_id2 = id_factory_.NewLocalId(); 223 id_factory_.NewLocalId(), bookmark_folder_id, "bookmark 1", false,
205 CreateUnprocessedCommitResult(bookmark_folder_id, id_factory_.root(), 224 &commit_set, &request, &response);
206 "A bookmark folder", BOOKMARKS, 225 int bookmark2_handle = CreateUnprocessedBookmarkCommitResult(
207 &commit_set, &request, &response); 226 id_factory_.NewLocalId(), bookmark_folder_id, "bookmark 2", false,
208 CreateUnprocessedCommitResult(bookmark_id1, bookmark_folder_id, 227 &commit_set, &request, &response);
209 "bookmark 1", BOOKMARKS, 228 int pref1_handle = CreateUnprocessedCommitResult(
210 &commit_set, &request, &response); 229 id_factory_.NewLocalId(), id_factory_.root(), "Pref 1", PREFERENCES,
211 CreateUnprocessedCommitResult(bookmark_id2, bookmark_folder_id, 230 &commit_set, &request, &response);
212 "bookmark 2", BOOKMARKS, 231 int pref2_handle = CreateUnprocessedCommitResult(
213 &commit_set, &request, &response); 232 id_factory_.NewLocalId(), id_factory_.root(), "Pref 2", PREFERENCES,
214 CreateUnprocessedCommitResult(pref_id1, id_factory_.root(), 233 &commit_set, &request, &response);
215 "Pref 1", PREFERENCES, 234 int autofill1_handle = CreateUnprocessedCommitResult(
216 &commit_set, &request, &response); 235 id_factory_.NewLocalId(), id_factory_.root(), "Autofill 1", AUTOFILL,
217 CreateUnprocessedCommitResult(pref_id2, id_factory_.root(), 236 &commit_set, &request, &response);
218 "Pref 2", PREFERENCES, 237 int autofill2_handle = CreateUnprocessedCommitResult(
219 &commit_set, &request, &response); 238 id_factory_.NewLocalId(), id_factory_.root(), "Autofill 2", AUTOFILL,
220 CreateUnprocessedCommitResult(autofill_id1, id_factory_.root(), 239 &commit_set, &request, &response);
221 "Autofill 1", AUTOFILL,
222 &commit_set, &request, &response);
223 CreateUnprocessedCommitResult(autofill_id2, id_factory_.root(),
224 "Autofill 2", AUTOFILL,
225 &commit_set, &request, &response);
226 240
227 ProcessCommitResponseCommand command(commit_set, request, response); 241 ProcessCommitResponseCommand command(commit_set, request, response);
228 ExpectGroupsToChange(command, GROUP_UI, GROUP_DB); 242 ExpectGroupsToChange(command, GROUP_UI, GROUP_DB);
229 command.ExecuteImpl(session()); 243 command.ExecuteImpl(session());
230 244
231 syncable::ReadTransaction trans(FROM_HERE, directory()); 245 syncable::ReadTransaction trans(FROM_HERE, directory());
232 Id new_fid; 246
233 ASSERT_TRUE(directory()->GetFirstChildId( 247 Entry b_folder(&trans, syncable::GET_BY_HANDLE, bookmark_folder_handle);
234 &trans, id_factory_.root(), &new_fid)); 248 ASSERT_TRUE(b_folder.good());
249
250 Id new_fid = b_folder.Get(syncable::ID);
235 ASSERT_FALSE(new_fid.IsRoot()); 251 ASSERT_FALSE(new_fid.IsRoot());
236 EXPECT_TRUE(new_fid.ServerKnows()); 252 EXPECT_TRUE(new_fid.ServerKnows());
237 EXPECT_FALSE(bookmark_folder_id.ServerKnows()); 253 EXPECT_FALSE(bookmark_folder_id.ServerKnows());
238 EXPECT_FALSE(new_fid == bookmark_folder_id); 254 EXPECT_FALSE(new_fid == bookmark_folder_id);
239 Entry b_folder(&trans, syncable::GET_BY_ID, new_fid); 255
240 ASSERT_TRUE(b_folder.good());
241 ASSERT_EQ("A bookmark folder", b_folder.Get(NON_UNIQUE_NAME)) 256 ASSERT_EQ("A bookmark folder", b_folder.Get(NON_UNIQUE_NAME))
242 << "Name of bookmark folder should not change."; 257 << "Name of bookmark folder should not change.";
243 ASSERT_LT(0, b_folder.Get(BASE_VERSION)) 258 ASSERT_LT(0, b_folder.Get(BASE_VERSION))
244 << "Bookmark folder should have a valid (positive) server base revision"; 259 << "Bookmark folder should have a valid (positive) server base revision";
245 260
246 // Look at the two bookmarks in bookmark_folder. 261 // Look at the two bookmarks in bookmark_folder.
247 Id cid; 262 Entry b1(&trans, syncable::GET_BY_HANDLE, bookmark1_handle);
248 ASSERT_TRUE(directory()->GetFirstChildId(&trans, new_fid, &cid)); 263 Entry b2(&trans, syncable::GET_BY_HANDLE, bookmark2_handle);
249 Entry b1(&trans, syncable::GET_BY_ID, cid);
250 Entry b2(&trans, syncable::GET_BY_ID, b1.Get(syncable::NEXT_ID));
251 CheckEntry(&b1, "bookmark 1", BOOKMARKS, new_fid); 264 CheckEntry(&b1, "bookmark 1", BOOKMARKS, new_fid);
252 CheckEntry(&b2, "bookmark 2", BOOKMARKS, new_fid); 265 CheckEntry(&b2, "bookmark 2", BOOKMARKS, new_fid);
253 ASSERT_TRUE(b2.Get(syncable::NEXT_ID).IsRoot());
254 266
255 // Look at the prefs and autofill items. 267 // Look at the prefs and autofill items.
256 Entry p1(&trans, syncable::GET_BY_ID, b_folder.Get(syncable::NEXT_ID)); 268 Entry p1(&trans, syncable::GET_BY_HANDLE, pref1_handle);
257 Entry p2(&trans, syncable::GET_BY_ID, p1.Get(syncable::NEXT_ID)); 269 Entry p2(&trans, syncable::GET_BY_HANDLE, pref2_handle);
258 CheckEntry(&p1, "Pref 1", PREFERENCES, id_factory_.root()); 270 CheckEntry(&p1, "Pref 1", PREFERENCES, id_factory_.root());
259 CheckEntry(&p2, "Pref 2", PREFERENCES, id_factory_.root()); 271 CheckEntry(&p2, "Pref 2", PREFERENCES, id_factory_.root());
260 272
261 Entry a1(&trans, syncable::GET_BY_ID, p2.Get(syncable::NEXT_ID)); 273 Entry a1(&trans, syncable::GET_BY_HANDLE, autofill1_handle);
262 Entry a2(&trans, syncable::GET_BY_ID, a1.Get(syncable::NEXT_ID)); 274 Entry a2(&trans, syncable::GET_BY_HANDLE, autofill2_handle);
263 CheckEntry(&a1, "Autofill 1", AUTOFILL, id_factory_.root()); 275 CheckEntry(&a1, "Autofill 1", AUTOFILL, id_factory_.root());
264 CheckEntry(&a2, "Autofill 2", AUTOFILL, id_factory_.root()); 276 CheckEntry(&a2, "Autofill 2", AUTOFILL, id_factory_.root());
265 ASSERT_TRUE(a2.Get(syncable::NEXT_ID).IsRoot());
266 } 277 }
267 278
268 // In this test, we test processing a commit response for a commit batch that 279 // In this test, we test processing a commit response for a commit batch that
269 // includes a newly created folder and some (but not all) of its children. 280 // includes a newly created folder and some (but not all) of its children.
270 // In particular, the folder has 50 children, which alternate between being 281 // In particular, the folder has 50 children, which alternate between being
271 // new items and preexisting items. This mixture of new and old is meant to 282 // new items and preexisting items. This mixture of new and old is meant to
272 // be a torture test of the code in ProcessCommitResponseCommand that changes 283 // be a torture test of the code in ProcessCommitResponseCommand that changes
273 // an item's ID from a local ID to a server-generated ID on the first commit. 284 // an item's ID from a local ID to a server-generated ID on the first commit.
274 // We commit only the first 25 children in the sibling order, leaving the 285 // We commit only the first 25 children in the sibling order, leaving the
275 // second 25 children as unsynced items. http://crbug.com/33081 describes 286 // second 25 children as unsynced items. http://crbug.com/33081 describes
276 // how this scenario used to fail, reversing the order for the second half 287 // how this scenario used to fail, reversing the order for the second half
277 // of the children. 288 // of the children.
278 TEST_F(ProcessCommitResponseCommandTest, NewFolderCommitKeepsChildOrder) { 289 TEST_F(ProcessCommitResponseCommandTest, NewFolderCommitKeepsChildOrder) {
279 sessions::OrderedCommitSet commit_set(session()->routing_info()); 290 sessions::OrderedCommitSet commit_set(session()->routing_info());
280 sync_pb::ClientToServerMessage request; 291 sync_pb::ClientToServerMessage request;
281 sync_pb::ClientToServerResponse response; 292 sync_pb::ClientToServerResponse response;
282 293
283 // Create the parent folder, a new item whose ID will change on commit. 294 // Create the parent folder, a new item whose ID will change on commit.
284 Id folder_id = id_factory_.NewLocalId(); 295 Id folder_id = id_factory_.NewLocalId();
285 CreateUnprocessedCommitResult(folder_id, id_factory_.root(), "A", 296 CreateUnprocessedBookmarkCommitResult(folder_id, id_factory_.root(),
286 BOOKMARKS, 297 "A", true,
287 &commit_set, &request, &response); 298 &commit_set, &request, &response);
288 299
289 // Verify that the item is reachable. 300 // Verify that the item is reachable.
290 { 301 {
291 syncable::ReadTransaction trans(FROM_HERE, directory()); 302 syncable::ReadTransaction trans(FROM_HERE, directory());
292 Id child_id; 303 syncable::Entry root(&trans, syncable::GET_BY_ID, id_factory_.root());
293 ASSERT_TRUE(directory()->GetFirstChildId( 304 ASSERT_TRUE(root.good());
294 &trans, id_factory_.root(), &child_id)); 305 ASSERT_EQ(folder_id, root.GetFirstChildId());
295 ASSERT_EQ(folder_id, child_id);
296 } 306 }
297 307
298 // The first 25 children of the parent folder will be part of the commit 308 // The first 25 children of the parent folder will be part of the commit
299 // batch. 309 // batch.
300 int batch_size = 25; 310 int batch_size = 25;
301 int i = 0; 311 int i = 0;
302 for (; i < batch_size; ++i) { 312 for (; i < batch_size; ++i) {
303 // Alternate between new and old child items, just for kicks. 313 // Alternate between new and old child items, just for kicks.
304 Id id = (i % 4 < 2) ? id_factory_.NewLocalId() : id_factory_.NewServerId(); 314 Id id = (i % 4 < 2) ? id_factory_.NewLocalId() : id_factory_.NewServerId();
305 CreateUnprocessedCommitResult( 315 CreateUnprocessedBookmarkCommitResult(
306 id, folder_id, base::StringPrintf("Item %d", i), BOOKMARKS, 316 id, folder_id, base::StringPrintf("Item %d", i), false,
307 &commit_set, &request, &response); 317 &commit_set, &request, &response);
308 } 318 }
309 // The second 25 children will be unsynced items but NOT part of the commit 319 // The second 25 children will be unsynced items but NOT part of the commit
310 // batch. When the ID of the parent folder changes during the commit, 320 // batch. When the ID of the parent folder changes during the commit,
311 // these items PARENT_ID should be updated, and their ordering should be 321 // these items PARENT_ID should be updated, and their ordering should be
312 // preserved. 322 // preserved.
323 std::vector<int64> uncommitted_existing;
324 std::vector<int64> uncommitted_new;
313 for (; i < 2*batch_size; ++i) { 325 for (; i < 2*batch_size; ++i) {
314 // Alternate between new and old child items, just for kicks. 326 // Alternate between new and old child items, just for kicks.
315 Id id = (i % 4 < 2) ? id_factory_.NewLocalId() : id_factory_.NewServerId(); 327 if (i % 4 < 2) {
316 CreateUnsyncedItem(id, folder_id, base::StringPrintf("Item %d", i), 328 int64 metahandle;
317 false, BOOKMARKS, NULL); 329 syncable::Id id = id_factory_.NewLocalId();
330 test_entry_factory_->CreateUnsyncedBookmarkItem(
331 id, folder_id, base::StringPrintf("Item %d", i), false, &metahandle);
332 uncommitted_existing.push_back(metahandle);
333 } else {
334 int64 metahandle;
335 syncable::Id id = id_factory_.NewServerId();
336 test_entry_factory_->CreateUnsyncedBookmarkItem(
337 id, folder_id, base::StringPrintf("Item %d", i), false, &metahandle);
338 uncommitted_new.push_back(metahandle);
339 }
340 }
341
342 // Take a snapshot of current positions under our folder.
343 std::vector<UniquePosition> positions;
344 {
345 syncable::ReadTransaction trans(FROM_HERE, directory());
346 Entry folder(&trans, syncable::GET_BY_ID, folder_id);
347 syncable::Id id_iter = folder.GetFirstChildId();
348 while (!id_iter.IsRoot()) {
349 Entry entry(&trans, syncable::GET_BY_ID, id_iter);
350 positions.push_back(entry.Get(UNIQUE_POSITION));
351 id_iter = entry.GetSuccessorId();
352 }
318 } 353 }
319 354
320 // Process the commit response for the parent folder and the first 355 // Process the commit response for the parent folder and the first
321 // 25 items. This should apply the values indicated by 356 // 25 items. This should apply the values indicated by
322 // each CommitResponse_EntryResponse to the syncable Entries. All new 357 // each CommitResponse_EntryResponse to the syncable Entries. All new
323 // items in the commit batch should have their IDs changed to server IDs. 358 // items in the commit batch should have their IDs changed to server IDs.
324 ProcessCommitResponseCommand command(commit_set, request, response); 359 ProcessCommitResponseCommand command(commit_set, request, response);
325 ExpectGroupToChange(command, GROUP_UI); 360 ExpectGroupToChange(command, GROUP_UI);
326 command.ExecuteImpl(session()); 361 command.ExecuteImpl(session());
327 362
328 syncable::ReadTransaction trans(FROM_HERE, directory()); 363 syncable::ReadTransaction trans(FROM_HERE, directory());
329 // Lookup the parent folder by finding a child of the root. We can't use 364 // Lookup the parent folder by finding a child of the root. We can't use
330 // folder_id here, because it changed during the commit. 365 // folder_id here, because it changed during the commit.
331 Id new_fid; 366 syncable::Entry root(&trans, syncable::GET_BY_ID, id_factory_.root());
332 ASSERT_TRUE(directory()->GetFirstChildId( 367 ASSERT_TRUE(root.good());
333 &trans, id_factory_.root(), &new_fid)); 368 Id new_fid = root.GetFirstChildId();
334 ASSERT_FALSE(new_fid.IsRoot()); 369 ASSERT_FALSE(new_fid.IsRoot());
335 EXPECT_TRUE(new_fid.ServerKnows()); 370 EXPECT_TRUE(new_fid.ServerKnows());
336 EXPECT_FALSE(folder_id.ServerKnows()); 371 EXPECT_FALSE(folder_id.ServerKnows());
337 EXPECT_TRUE(new_fid != folder_id); 372 EXPECT_TRUE(new_fid != folder_id);
338 Entry parent(&trans, syncable::GET_BY_ID, new_fid); 373 Entry parent(&trans, syncable::GET_BY_ID, new_fid);
339 ASSERT_TRUE(parent.good()); 374 ASSERT_TRUE(parent.good());
340 ASSERT_EQ("A", parent.Get(NON_UNIQUE_NAME)) 375 ASSERT_EQ("A", parent.Get(NON_UNIQUE_NAME))
341 << "Name of parent folder should not change."; 376 << "Name of parent folder should not change.";
342 ASSERT_LT(0, parent.Get(BASE_VERSION)) 377 ASSERT_LT(0, parent.Get(BASE_VERSION))
343 << "Parent should have a valid (positive) server base revision"; 378 << "Parent should have a valid (positive) server base revision";
344 379
345 Id cid; 380 //int child_count = 0;
346 ASSERT_TRUE(directory()->GetFirstChildId(&trans, new_fid, &cid)); 381 // Now loop over the of the request, ensuring its contents were committed.
347 int child_count = 0; 382 for (int i = 0; i < response.commit().entryresponse_size(); ++i) {
348 // Now loop over all the children of the parent folder, verifying 383 Id cid = Id::CreateFromServerId(
349 // that they are in their original order by checking to see that their 384 response.commit().entryresponse(i).id_string());
350 // names are still sequential. 385
351 while (!cid.IsRoot()) { 386 SCOPED_TRACE(::testing::Message("Examining item #") << cid);
352 SCOPED_TRACE(::testing::Message("Examining item #") << child_count);
353 Entry c(&trans, syncable::GET_BY_ID, cid); 387 Entry c(&trans, syncable::GET_BY_ID, cid);
354 DCHECK(c.good()); 388 DCHECK(c.good());
355 ASSERT_EQ(base::StringPrintf("Item %d", child_count), 389
356 c.Get(NON_UNIQUE_NAME)); 390 if (cid != new_fid) { // If it's not the parent, it must be a child.
357 ASSERT_EQ(new_fid, c.Get(syncable::PARENT_ID)); 391 ASSERT_EQ(new_fid, c.Get(syncable::PARENT_ID));
358 if (child_count < batch_size) {
359 ASSERT_FALSE(c.Get(IS_UNSYNCED)) << "Item should be committed";
360 ASSERT_TRUE(cid.ServerKnows());
361 ASSERT_LT(0, c.Get(BASE_VERSION));
362 } else {
363 ASSERT_TRUE(c.Get(IS_UNSYNCED)) << "Item should be uncommitted";
364 // We alternated between creates and edits; double check that these items
365 // have been preserved.
366 if (child_count % 4 < 2) {
367 ASSERT_FALSE(cid.ServerKnows());
368 ASSERT_GE(0, c.Get(BASE_VERSION));
369 } else {
370 ASSERT_TRUE(cid.ServerKnows());
371 ASSERT_LT(0, c.Get(BASE_VERSION));
372 }
373 } 392 }
374 cid = c.Get(syncable::NEXT_ID); 393 ASSERT_FALSE(c.Get(IS_UNSYNCED)) << "Item should be committed";
375 child_count++; 394 ASSERT_TRUE(cid.ServerKnows());
395 ASSERT_LT(0, c.Get(BASE_VERSION));
376 } 396 }
377 ASSERT_EQ(batch_size*2, child_count) 397
378 << "Too few or too many children in parent folder after commit."; 398 // Ensure the entries that overflowed the batch size are as we left them.
399 for (std::vector<int64>::iterator i = uncommitted_new.begin();
400 i != uncommitted_new.end(); ++i) {
401 Entry entry(&trans, syncable::GET_BY_HANDLE, *i);
402 ASSERT_TRUE(entry.Get(ID).ServerKnows());
403 ASSERT_LT(0, entry.Get(BASE_VERSION));
404 }
405
406 for (std::vector<int64>::iterator i = uncommitted_existing.begin();
407 i != uncommitted_existing.end(); ++i) {
408 Entry entry(&trans, syncable::GET_BY_HANDLE, *i);
409 ASSERT_FALSE(entry.Get(ID).ServerKnows());
410 ASSERT_GE(0, entry.Get(BASE_VERSION));
411 }
412
413 // Verify positions against the snapshot we took earlier.
414 syncable::Entry new_f(&trans, syncable::GET_BY_ID, new_fid);
415 ASSERT_TRUE(new_f.good());
416 syncable::Id id_iter = new_f.GetFirstChildId();
417 std::vector<UniquePosition>::iterator snapshot_iter = positions.begin();
418 while (!id_iter.IsRoot() && snapshot_iter != positions.end()) {
419 SCOPED_TRACE(::testing::Message("Examining: ") << id_iter);
420 Entry entry(&trans, syncable::GET_BY_ID, id_iter);
421 ASSERT_TRUE(snapshot_iter->Equals(entry.Get(UNIQUE_POSITION)));
422 id_iter = entry.GetSuccessorId();
423 snapshot_iter++;
424 }
425
426 // Assert that we still have the same number of children.
427 EXPECT_TRUE(id_iter.IsRoot() && snapshot_iter == positions.end());
379 } 428 }
380 429
381 // This test fixture runs across a Cartesian product of per-type fail/success 430 // This test fixture runs across a Cartesian product of per-type fail/success
382 // possibilities. 431 // possibilities.
383 enum { 432 enum {
384 TEST_PARAM_BOOKMARK_ENABLE_BIT, 433 TEST_PARAM_BOOKMARK_ENABLE_BIT,
385 TEST_PARAM_AUTOFILL_ENABLE_BIT, 434 TEST_PARAM_AUTOFILL_ENABLE_BIT,
386 TEST_PARAM_BIT_COUNT 435 TEST_PARAM_BIT_COUNT
387 }; 436 };
388 class MixedResult : 437 class MixedResult :
(...skipping 17 matching lines...) Expand all
406 TEST_P(MixedResult, ExtensionActivity) { 455 TEST_P(MixedResult, ExtensionActivity) {
407 sessions::OrderedCommitSet commit_set(session()->routing_info()); 456 sessions::OrderedCommitSet commit_set(session()->routing_info());
408 sync_pb::ClientToServerMessage request; 457 sync_pb::ClientToServerMessage request;
409 sync_pb::ClientToServerResponse response; 458 sync_pb::ClientToServerResponse response;
410 459
411 EXPECT_NE(routing_info().find(BOOKMARKS)->second, 460 EXPECT_NE(routing_info().find(BOOKMARKS)->second,
412 routing_info().find(AUTOFILL)->second) 461 routing_info().find(AUTOFILL)->second)
413 << "To not be lame, this test requires more than one active group."; 462 << "To not be lame, this test requires more than one active group.";
414 463
415 // Bookmark item setup. 464 // Bookmark item setup.
416 CreateUnprocessedCommitResult(id_factory_.NewServerId(), 465 CreateUnprocessedBookmarkCommitResult(
417 id_factory_.root(), "Some bookmark", BOOKMARKS, 466 id_factory_.NewServerId(),
467 id_factory_.root(), "Some bookmark", false,
418 &commit_set, &request, &response); 468 &commit_set, &request, &response);
419 if (ShouldFailBookmarkCommit()) 469 if (ShouldFailBookmarkCommit())
420 SetLastErrorCode(CommitResponse::TRANSIENT_ERROR, &response); 470 SetLastErrorCode(CommitResponse::TRANSIENT_ERROR, &response);
421 // Autofill item setup. 471 // Autofill item setup.
422 CreateUnprocessedCommitResult(id_factory_.NewServerId(), 472 CreateUnprocessedCommitResult(
473 id_factory_.NewServerId(),
423 id_factory_.root(), "Some autofill", AUTOFILL, 474 id_factory_.root(), "Some autofill", AUTOFILL,
424 &commit_set, &request, &response); 475 &commit_set, &request, &response);
425 if (ShouldFailAutofillCommit()) 476 if (ShouldFailAutofillCommit())
426 SetLastErrorCode(CommitResponse::TRANSIENT_ERROR, &response); 477 SetLastErrorCode(CommitResponse::TRANSIENT_ERROR, &response);
427 478
428 // Put some extensions activity in the session. 479 // Put some extensions activity in the session.
429 { 480 {
430 ExtensionsActivityMonitor::Records* records = 481 ExtensionsActivityMonitor::Records* records =
431 session()->mutable_extensions_activity(); 482 session()->mutable_extensions_activity();
432 (*records)["ABC"].extension_id = "ABC"; 483 (*records)["ABC"].extension_id = "ABC";
(...skipping 15 matching lines...) Expand all
448 EXPECT_EQ("xyz", final_monitor_records["xyz"].extension_id); 499 EXPECT_EQ("xyz", final_monitor_records["xyz"].extension_id);
449 EXPECT_EQ(2049U, final_monitor_records["ABC"].bookmark_write_count); 500 EXPECT_EQ(2049U, final_monitor_records["ABC"].bookmark_write_count);
450 EXPECT_EQ(4U, final_monitor_records["xyz"].bookmark_write_count); 501 EXPECT_EQ(4U, final_monitor_records["xyz"].bookmark_write_count);
451 } else { 502 } else {
452 EXPECT_TRUE(final_monitor_records.empty()) 503 EXPECT_TRUE(final_monitor_records.empty())
453 << "Should not restore records after successful bookmark commit."; 504 << "Should not restore records after successful bookmark commit.";
454 } 505 }
455 } 506 }
456 507
457 } // namespace syncer 508 } // namespace syncer
OLDNEW
« no previous file with comments | « sync/engine/process_commit_response_command.cc ('k') | sync/engine/process_updates_command.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698