OLD | NEW |
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 <string> | 5 #include <string> |
6 | 6 |
7 #include "base/location.h" | 7 #include "base/location.h" |
8 #include "base/memory/scoped_ptr.h" | 8 #include "base/memory/scoped_ptr.h" |
9 #include "base/stringprintf.h" | 9 #include "base/stringprintf.h" |
10 #include "sync/engine/apply_updates_and_resolve_conflicts_command.h" | 10 #include "sync/engine/apply_updates_and_resolve_conflicts_command.h" |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
64 | 64 |
65 protected: | 65 protected: |
66 DISALLOW_COPY_AND_ASSIGN(ApplyUpdatesAndResolveConflictsCommandTest); | 66 DISALLOW_COPY_AND_ASSIGN(ApplyUpdatesAndResolveConflictsCommandTest); |
67 | 67 |
68 ApplyUpdatesAndResolveConflictsCommand apply_updates_command_; | 68 ApplyUpdatesAndResolveConflictsCommand apply_updates_command_; |
69 scoped_ptr<TestEntryFactory> entry_factory_; | 69 scoped_ptr<TestEntryFactory> entry_factory_; |
70 }; | 70 }; |
71 | 71 |
72 TEST_F(ApplyUpdatesAndResolveConflictsCommandTest, Simple) { | 72 TEST_F(ApplyUpdatesAndResolveConflictsCommandTest, Simple) { |
73 string root_server_id = syncable::GetNullId().GetServerId(); | 73 string root_server_id = syncable::GetNullId().GetServerId(); |
74 entry_factory_->CreateUnappliedNewItemWithParent("parent", | 74 entry_factory_->CreateUnappliedNewBookmarkItemWithParent( |
75 DefaultBookmarkSpecifics(), | 75 "parent", DefaultBookmarkSpecifics(), root_server_id); |
76 root_server_id); | 76 entry_factory_->CreateUnappliedNewBookmarkItemWithParent( |
77 entry_factory_->CreateUnappliedNewItemWithParent("child", | 77 "child", DefaultBookmarkSpecifics(), "parent"); |
78 DefaultBookmarkSpecifics(), | |
79 "parent"); | |
80 | 78 |
81 ExpectGroupToChange(apply_updates_command_, GROUP_UI); | 79 ExpectGroupToChange(apply_updates_command_, GROUP_UI); |
82 apply_updates_command_.ExecuteImpl(session()); | 80 apply_updates_command_.ExecuteImpl(session()); |
83 | 81 |
84 const sessions::StatusController& status = session()->status_controller(); | 82 const sessions::StatusController& status = session()->status_controller(); |
85 EXPECT_EQ(0, status.num_encryption_conflicts()) | 83 EXPECT_EQ(0, status.num_encryption_conflicts()) |
86 << "Simple update shouldn't result in conflicts"; | 84 << "Simple update shouldn't result in conflicts"; |
87 EXPECT_EQ(0, status.num_hierarchy_conflicts()) | 85 EXPECT_EQ(0, status.num_hierarchy_conflicts()) |
88 << "Simple update shouldn't result in conflicts"; | 86 << "Simple update shouldn't result in conflicts"; |
89 EXPECT_EQ(2, status.num_updates_applied()) | 87 EXPECT_EQ(2, status.num_updates_applied()) |
90 << "All items should have been successfully applied"; | 88 << "All items should have been successfully applied"; |
91 } | 89 } |
92 | 90 |
93 TEST_F(ApplyUpdatesAndResolveConflictsCommandTest, | 91 TEST_F(ApplyUpdatesAndResolveConflictsCommandTest, |
94 UpdateWithChildrenBeforeParents) { | 92 UpdateWithChildrenBeforeParents) { |
95 // Set a bunch of updates which are difficult to apply in the order | 93 // Set a bunch of updates which are difficult to apply in the order |
96 // they're received due to dependencies on other unseen items. | 94 // they're received due to dependencies on other unseen items. |
97 string root_server_id = syncable::GetNullId().GetServerId(); | 95 string root_server_id = syncable::GetNullId().GetServerId(); |
98 entry_factory_->CreateUnappliedNewItemWithParent( | 96 entry_factory_->CreateUnappliedNewBookmarkItemWithParent( |
99 "a_child_created_first", DefaultBookmarkSpecifics(), "parent"); | 97 "a_child_created_first", DefaultBookmarkSpecifics(), "parent"); |
100 entry_factory_->CreateUnappliedNewItemWithParent( | 98 entry_factory_->CreateUnappliedNewBookmarkItemWithParent( |
101 "x_child_created_first", DefaultBookmarkSpecifics(), "parent"); | 99 "x_child_created_first", DefaultBookmarkSpecifics(), "parent"); |
102 entry_factory_->CreateUnappliedNewItemWithParent( | 100 entry_factory_->CreateUnappliedNewBookmarkItemWithParent( |
103 "parent", DefaultBookmarkSpecifics(), root_server_id); | 101 "parent", DefaultBookmarkSpecifics(), root_server_id); |
104 entry_factory_->CreateUnappliedNewItemWithParent( | 102 entry_factory_->CreateUnappliedNewBookmarkItemWithParent( |
105 "a_child_created_second", DefaultBookmarkSpecifics(), "parent"); | 103 "a_child_created_second", DefaultBookmarkSpecifics(), "parent"); |
106 entry_factory_->CreateUnappliedNewItemWithParent( | 104 entry_factory_->CreateUnappliedNewBookmarkItemWithParent( |
107 "x_child_created_second", DefaultBookmarkSpecifics(), "parent"); | 105 "x_child_created_second", DefaultBookmarkSpecifics(), "parent"); |
108 | 106 |
109 ExpectGroupToChange(apply_updates_command_, GROUP_UI); | 107 ExpectGroupToChange(apply_updates_command_, GROUP_UI); |
110 apply_updates_command_.ExecuteImpl(session()); | 108 apply_updates_command_.ExecuteImpl(session()); |
111 | 109 |
112 const sessions::StatusController& status = session()->status_controller(); | 110 const sessions::StatusController& status = session()->status_controller(); |
113 EXPECT_EQ(5, status.num_updates_applied()) | 111 EXPECT_EQ(5, status.num_updates_applied()) |
114 << "All updates should have been successfully applied"; | 112 << "All updates should have been successfully applied"; |
115 } | 113 } |
116 | 114 |
117 // Runs the ApplyUpdatesAndResolveConflictsCommand on an item that has both | 115 // Runs the ApplyUpdatesAndResolveConflictsCommand on an item that has both |
118 // local and remote modifications (IS_UNSYNCED and IS_UNAPPLIED_UPDATE). We | 116 // local and remote modifications (IS_UNSYNCED and IS_UNAPPLIED_UPDATE). We |
119 // expect the command to detect that this update can't be applied because it is | 117 // expect the command to detect that this update can't be applied because it is |
120 // in a CONFLICT state. | 118 // in a CONFLICT state. |
121 TEST_F(ApplyUpdatesAndResolveConflictsCommandTest, SimpleConflict) { | 119 TEST_F(ApplyUpdatesAndResolveConflictsCommandTest, SimpleConflict) { |
122 entry_factory_->CreateUnappliedAndUnsyncedItem("item", BOOKMARKS); | 120 entry_factory_->CreateUnappliedAndUnsyncedBookmarkItem("item"); |
123 | 121 |
124 ExpectGroupToChange(apply_updates_command_, GROUP_UI); | 122 ExpectGroupToChange(apply_updates_command_, GROUP_UI); |
125 apply_updates_command_.ExecuteImpl(session()); | 123 apply_updates_command_.ExecuteImpl(session()); |
126 | 124 |
127 const sessions::StatusController& status = session()->status_controller(); | 125 const sessions::StatusController& status = session()->status_controller(); |
128 EXPECT_EQ(1, status.num_server_overwrites()) | 126 EXPECT_EQ(1, status.num_server_overwrites()) |
129 << "Unsynced and unapplied item conflict should be resolved"; | 127 << "Unsynced and unapplied item conflict should be resolved"; |
130 EXPECT_EQ(0, status.num_updates_applied()) | 128 EXPECT_EQ(0, status.num_updates_applied()) |
131 << "Update should not be applied; we should override the server."; | 129 << "Update should not be applied; we should override the server."; |
132 } | 130 } |
133 | 131 |
134 // Runs the ApplyUpdatesAndResolveConflictsCommand on an item that has both | 132 // Runs the ApplyUpdatesAndResolveConflictsCommand on an item that has both |
135 // local and remote modifications *and* the remote modification cannot be | 133 // local and remote modifications *and* the remote modification cannot be |
136 // applied without violating the tree constraints. We expect the command to | 134 // applied without violating the tree constraints. We expect the command to |
137 // detect that this update can't be applied and that this situation can't be | 135 // detect that this update can't be applied and that this situation can't be |
138 // resolved with the simple conflict processing logic; it is in a | 136 // resolved with the simple conflict processing logic; it is in a |
139 // CONFLICT_HIERARCHY state. | 137 // CONFLICT_HIERARCHY state. |
140 TEST_F(ApplyUpdatesAndResolveConflictsCommandTest, HierarchyAndSimpleConflict) { | 138 TEST_F(ApplyUpdatesAndResolveConflictsCommandTest, HierarchyAndSimpleConflict) { |
141 // Create a simply-conflicting item. It will start with valid parent ids. | 139 // Create a simply-conflicting item. It will start with valid parent ids. |
142 int64 handle = entry_factory_->CreateUnappliedAndUnsyncedItem( | 140 int64 handle = entry_factory_->CreateUnappliedAndUnsyncedBookmarkItem( |
143 "orphaned_by_server", BOOKMARKS); | 141 "orphaned_by_server"); |
144 { | 142 { |
145 // Manually set the SERVER_PARENT_ID to bad value. | 143 // Manually set the SERVER_PARENT_ID to bad value. |
146 // A bad parent indicates a hierarchy conflict. | 144 // A bad parent indicates a hierarchy conflict. |
147 WriteTransaction trans(FROM_HERE, UNITTEST, directory()); | 145 WriteTransaction trans(FROM_HERE, UNITTEST, directory()); |
148 MutableEntry entry(&trans, syncable::GET_BY_HANDLE, handle); | 146 MutableEntry entry(&trans, syncable::GET_BY_HANDLE, handle); |
149 ASSERT_TRUE(entry.good()); | 147 ASSERT_TRUE(entry.good()); |
150 | 148 |
151 entry.Put(syncable::SERVER_PARENT_ID, | 149 entry.Put(syncable::SERVER_PARENT_ID, |
152 TestIdFactory::MakeServer("bogus_parent")); | 150 TestIdFactory::MakeServer("bogus_parent")); |
153 } | 151 } |
154 | 152 |
155 ExpectGroupToChange(apply_updates_command_, GROUP_UI); | 153 ExpectGroupToChange(apply_updates_command_, GROUP_UI); |
156 apply_updates_command_.ExecuteImpl(session()); | 154 apply_updates_command_.ExecuteImpl(session()); |
157 | 155 |
158 const sessions::StatusController& status = session()->status_controller(); | 156 const sessions::StatusController& status = session()->status_controller(); |
159 EXPECT_EQ(1, status.num_hierarchy_conflicts()); | 157 EXPECT_EQ(1, status.num_hierarchy_conflicts()); |
160 } | 158 } |
161 | 159 |
162 | 160 |
163 // Runs the ApplyUpdatesAndResolveConflictsCommand on an item with remote | 161 // Runs the ApplyUpdatesAndResolveConflictsCommand on an item with remote |
164 // modifications that would create a directory loop if the update were applied. | 162 // modifications that would create a directory loop if the update were applied. |
165 // We expect the command to detect that this update can't be applied because it | 163 // We expect the command to detect that this update can't be applied because it |
166 // is in a CONFLICT_HIERARCHY state. | 164 // is in a CONFLICT_HIERARCHY state. |
167 TEST_F(ApplyUpdatesAndResolveConflictsCommandTest, | 165 TEST_F(ApplyUpdatesAndResolveConflictsCommandTest, |
168 HierarchyConflictDirectoryLoop) { | 166 HierarchyConflictDirectoryLoop) { |
169 // Item 'X' locally has parent of 'root'. Server is updating it to have | 167 // Item 'X' locally has parent of 'root'. Server is updating it to have |
170 // parent of 'Y'. | 168 // parent of 'Y'. |
171 { | 169 { |
172 // Create it as a child of root node. | 170 // Create it as a child of root node. |
173 int64 handle = entry_factory_->CreateSyncedItem("X", BOOKMARKS, true); | 171 int64 handle = entry_factory_->CreateSyncedBookmarkItem("X", true); |
174 | 172 |
175 WriteTransaction trans(FROM_HERE, UNITTEST, directory()); | 173 WriteTransaction trans(FROM_HERE, UNITTEST, directory()); |
176 MutableEntry entry(&trans, syncable::GET_BY_HANDLE, handle); | 174 MutableEntry entry(&trans, syncable::GET_BY_HANDLE, handle); |
177 ASSERT_TRUE(entry.good()); | 175 ASSERT_TRUE(entry.good()); |
178 | 176 |
179 // Re-parent from root to "Y" | 177 // Re-parent from root to "Y" |
180 entry.Put(syncable::SERVER_VERSION, entry_factory_->GetNextRevision()); | 178 entry.Put(syncable::SERVER_VERSION, entry_factory_->GetNextRevision()); |
181 entry.Put(syncable::IS_UNAPPLIED_UPDATE, true); | 179 entry.Put(syncable::IS_UNAPPLIED_UPDATE, true); |
182 entry.Put(syncable::SERVER_PARENT_ID, TestIdFactory::MakeServer("Y")); | 180 entry.Put(syncable::SERVER_PARENT_ID, TestIdFactory::MakeServer("Y")); |
183 } | 181 } |
184 | 182 |
185 // Item 'Y' is child of 'X'. | 183 // Item 'Y' is child of 'X'. |
186 entry_factory_->CreateUnsyncedItem( | 184 entry_factory_->CreateUnsyncedBookmarkItem( |
187 TestIdFactory::MakeServer("Y"), TestIdFactory::MakeServer("X"), "Y", true, | 185 TestIdFactory::MakeServer("Y"), TestIdFactory::MakeServer("X"), "Y", true, |
188 BOOKMARKS, NULL); | 186 NULL); |
189 | 187 |
190 // If the server's update were applied, we would have X be a child of Y, and Y | 188 // If the server's update were applied, we would have X be a child of Y, and Y |
191 // as a child of X. That's a directory loop. The UpdateApplicator should | 189 // as a child of X. That's a directory loop. The UpdateApplicator should |
192 // prevent the update from being applied and note that this is a hierarchy | 190 // prevent the update from being applied and note that this is a hierarchy |
193 // conflict. | 191 // conflict. |
194 | 192 |
195 ExpectGroupToChange(apply_updates_command_, GROUP_UI); | 193 ExpectGroupToChange(apply_updates_command_, GROUP_UI); |
196 apply_updates_command_.ExecuteImpl(session()); | 194 apply_updates_command_.ExecuteImpl(session()); |
197 | 195 |
198 const sessions::StatusController& status = session()->status_controller(); | 196 const sessions::StatusController& status = session()->status_controller(); |
199 | 197 |
200 // This should count as a hierarchy conflict. | 198 // This should count as a hierarchy conflict. |
201 EXPECT_EQ(1, status.num_hierarchy_conflicts()); | 199 EXPECT_EQ(1, status.num_hierarchy_conflicts()); |
202 } | 200 } |
203 | 201 |
204 // Runs the ApplyUpdatesAndResolveConflictsCommand on a directory where the | 202 // Runs the ApplyUpdatesAndResolveConflictsCommand on a directory where the |
205 // server sent us an update to add a child to a locally deleted (and unsynced) | 203 // server sent us an update to add a child to a locally deleted (and unsynced) |
206 // parent. We expect the command to not apply the update and to indicate the | 204 // parent. We expect the command to not apply the update and to indicate the |
207 // update is in a CONFLICT_HIERARCHY state. | 205 // update is in a CONFLICT_HIERARCHY state. |
208 TEST_F(ApplyUpdatesAndResolveConflictsCommandTest, | 206 TEST_F(ApplyUpdatesAndResolveConflictsCommandTest, |
209 HierarchyConflictDeletedParent) { | 207 HierarchyConflictDeletedParent) { |
210 // Create a locally deleted parent item. | 208 // Create a locally deleted parent item. |
211 int64 parent_handle; | 209 int64 parent_handle; |
212 entry_factory_->CreateUnsyncedItem( | 210 entry_factory_->CreateUnsyncedBookmarkItem( |
213 Id::CreateFromServerId("parent"), TestIdFactory::root(), | 211 Id::CreateFromServerId("parent"), TestIdFactory::root(), |
214 "parent", true, BOOKMARKS, &parent_handle); | 212 "parent", true, &parent_handle); |
215 { | 213 { |
216 WriteTransaction trans(FROM_HERE, UNITTEST, directory()); | 214 WriteTransaction trans(FROM_HERE, UNITTEST, directory()); |
217 MutableEntry entry(&trans, syncable::GET_BY_HANDLE, parent_handle); | 215 MutableEntry entry(&trans, syncable::GET_BY_HANDLE, parent_handle); |
218 entry.Put(syncable::IS_DEL, true); | 216 entry.Put(syncable::IS_DEL, true); |
219 } | 217 } |
220 | 218 |
221 // Create an incoming child from the server. | 219 // Create an incoming child from the server. |
222 entry_factory_->CreateUnappliedNewItemWithParent( | 220 entry_factory_->CreateUnappliedNewItemWithParent( |
223 "child", DefaultBookmarkSpecifics(), "parent"); | 221 "child", DefaultBookmarkSpecifics(), "parent"); |
224 | 222 |
(...skipping 10 matching lines...) Expand all Loading... |
235 | 233 |
236 // Runs the ApplyUpdatesAndResolveConflictsCommand on a directory where the | 234 // Runs the ApplyUpdatesAndResolveConflictsCommand on a directory where the |
237 // server is trying to delete a folder that has a recently added (and unsynced) | 235 // server is trying to delete a folder that has a recently added (and unsynced) |
238 // child. We expect the command to not apply the update because it is in a | 236 // child. We expect the command to not apply the update because it is in a |
239 // CONFLICT_HIERARCHY state. | 237 // CONFLICT_HIERARCHY state. |
240 TEST_F(ApplyUpdatesAndResolveConflictsCommandTest, | 238 TEST_F(ApplyUpdatesAndResolveConflictsCommandTest, |
241 HierarchyConflictDeleteNonEmptyDirectory) { | 239 HierarchyConflictDeleteNonEmptyDirectory) { |
242 // Create a server-deleted directory. | 240 // Create a server-deleted directory. |
243 { | 241 { |
244 // Create it as a child of root node. | 242 // Create it as a child of root node. |
245 int64 handle = | 243 int64 handle = entry_factory_->CreateSyncedBookmarkItem("parent", true); |
246 entry_factory_->CreateSyncedItem("parent", BOOKMARKS, true); | |
247 | 244 |
248 WriteTransaction trans(FROM_HERE, UNITTEST, directory()); | 245 WriteTransaction trans(FROM_HERE, UNITTEST, directory()); |
249 MutableEntry entry(&trans, syncable::GET_BY_HANDLE, handle); | 246 MutableEntry entry(&trans, syncable::GET_BY_HANDLE, handle); |
250 ASSERT_TRUE(entry.good()); | 247 ASSERT_TRUE(entry.good()); |
251 | 248 |
252 // Delete it on the server. | 249 // Delete it on the server. |
253 entry.Put(syncable::SERVER_VERSION, entry_factory_->GetNextRevision()); | 250 entry.Put(syncable::SERVER_VERSION, entry_factory_->GetNextRevision()); |
254 entry.Put(syncable::IS_UNAPPLIED_UPDATE, true); | 251 entry.Put(syncable::IS_UNAPPLIED_UPDATE, true); |
255 entry.Put(syncable::SERVER_PARENT_ID, TestIdFactory::root()); | 252 entry.Put(syncable::SERVER_PARENT_ID, TestIdFactory::root()); |
256 entry.Put(syncable::SERVER_IS_DEL, true); | 253 entry.Put(syncable::SERVER_IS_DEL, true); |
257 } | 254 } |
258 | 255 |
259 // Create a local child of the server-deleted directory. | 256 // Create a local child of the server-deleted directory. |
260 entry_factory_->CreateUnsyncedItem( | 257 entry_factory_->CreateUnsyncedBookmarkItem( |
261 TestIdFactory::MakeServer("child"), TestIdFactory::MakeServer("parent"), | 258 TestIdFactory::MakeServer("child"), TestIdFactory::MakeServer("parent"), |
262 "child", false, BOOKMARKS, NULL); | 259 "child", false, NULL); |
263 | 260 |
264 // The server's request to delete the directory must be ignored, otherwise our | 261 // The server's request to delete the directory must be ignored, otherwise our |
265 // unsynced new child would be orphaned. This is a hierarchy conflict. | 262 // unsynced new child would be orphaned. This is a hierarchy conflict. |
266 | 263 |
267 ExpectGroupToChange(apply_updates_command_, GROUP_UI); | 264 ExpectGroupToChange(apply_updates_command_, GROUP_UI); |
268 apply_updates_command_.ExecuteImpl(session()); | 265 apply_updates_command_.ExecuteImpl(session()); |
269 | 266 |
270 const sessions::StatusController& status = session()->status_controller(); | 267 const sessions::StatusController& status = session()->status_controller(); |
271 // This should count as a hierarchy conflict. | 268 // This should count as a hierarchy conflict. |
272 EXPECT_EQ(1, status.num_hierarchy_conflicts()); | 269 EXPECT_EQ(1, status.num_hierarchy_conflicts()); |
(...skipping 137 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
410 | 407 |
411 const sessions::StatusController& status = session()->status_controller(); | 408 const sessions::StatusController& status = session()->status_controller(); |
412 EXPECT_EQ(1, status.num_encryption_conflicts()) | 409 EXPECT_EQ(1, status.num_encryption_conflicts()) |
413 << "The updates that can't be decrypted should be in encryption " | 410 << "The updates that can't be decrypted should be in encryption " |
414 << "conflict"; | 411 << "conflict"; |
415 EXPECT_EQ(1, status.num_updates_applied()) | 412 EXPECT_EQ(1, status.num_updates_applied()) |
416 << "The undecryptable password update shouldn't be applied"; | 413 << "The undecryptable password update shouldn't be applied"; |
417 } | 414 } |
418 | 415 |
419 } // namespace syncer | 416 } // namespace syncer |
OLD | NEW |