OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2009 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 // Mock ServerConnectionManager class for use in client regression tests. |
| 6 // |
| 7 |
| 8 #include "chrome/test/sync/engine/mock_server_connection.h" |
| 9 |
| 10 #include "chrome/browser/sync/engine/syncer_proto_util.h" |
| 11 #include "chrome/browser/sync/util/character_set_converters.h" |
| 12 #include "chrome/test/sync/engine/test_id_factory.h" |
| 13 #include "testing/gtest/include/gtest/gtest.h" |
| 14 |
| 15 using browser_sync::HttpResponse; |
| 16 using browser_sync::ServerConnectionManager; |
| 17 using browser_sync::SyncerProtoUtil; |
| 18 using browser_sync::TestIdFactory; |
| 19 using std::map; |
| 20 using std::string; |
| 21 using sync_pb::ClientToServerMessage; |
| 22 using sync_pb::ClientToServerResponse; |
| 23 using sync_pb::CommitMessage; |
| 24 using sync_pb::CommitResponse; |
| 25 using sync_pb::CommitResponse_EntryResponse; |
| 26 using sync_pb::GetUpdatesMessage; |
| 27 using sync_pb::SyncEntity; |
| 28 using syncable::DirectoryManager; |
| 29 using syncable::WriteTransaction; |
| 30 |
| 31 MockConnectionManager::MockConnectionManager(DirectoryManager* dirmgr, |
| 32 PathString name) |
| 33 : ServerConnectionManager("unused", 0, false, "version", "id"), |
| 34 conflict_all_commits_(false), |
| 35 conflict_n_commits_(0), |
| 36 next_new_id_(10000), |
| 37 store_birthday_("Store BDay!"), |
| 38 store_birthday_sent_(false), |
| 39 client_stuck_(false), |
| 40 commit_time_rename_prepended_string_(""), |
| 41 fail_next_postbuffer_(false), |
| 42 directory_(dirmgr, name), |
| 43 mid_commit_callback_function_(NULL), |
| 44 client_command_(NULL), |
| 45 next_position_in_parent_(2) { |
| 46 server_reachable_ = true; |
| 47 CHECK(directory_.good()); |
| 48 }; |
| 49 |
| 50 MockConnectionManager::~MockConnectionManager() { |
| 51 for (size_t i = 0; i < commit_messages_.size(); i++) |
| 52 delete commit_messages_[i]; |
| 53 } |
| 54 |
| 55 void MockConnectionManager::SetCommitTimeRename(string prepend) { |
| 56 commit_time_rename_prepended_string_ = prepend; |
| 57 } |
| 58 |
| 59 void MockConnectionManager::SetMidCommitCallbackFunction( |
| 60 MockConnectionManager::TestCallbackFunction callback) { |
| 61 mid_commit_callback_function_ = callback; |
| 62 } |
| 63 |
| 64 bool MockConnectionManager::PostBufferToPath(const PostBufferParams* params, |
| 65 const string& path, |
| 66 const string& auth_token) { |
| 67 ClientToServerMessage post; |
| 68 CHECK(post.ParseFromString(params->buffer_in)); |
| 69 client_stuck_ = post.sync_problem_detected(); |
| 70 ClientToServerResponse response; |
| 71 response.Clear(); |
| 72 // If the Directory's locked when we do this, it's a problem as in normal |
| 73 // use this function could take a while to return because it accesses the |
| 74 // network. As we can't test this we do the next best thing and hang here |
| 75 // when there's an issue. |
| 76 { |
| 77 WriteTransaction wt(directory_, syncable::UNITTEST, __FILE__, __LINE__); |
| 78 } |
| 79 if (fail_next_postbuffer_) { |
| 80 fail_next_postbuffer_ = false; |
| 81 return false; |
| 82 } |
| 83 // Default to an ok connection. |
| 84 params->response->server_status = HttpResponse::SERVER_CONNECTION_OK; |
| 85 response.set_store_birthday(store_birthday_); |
| 86 if (post.has_store_birthday() && post.store_birthday() != store_birthday_) { |
| 87 response.set_error_code(ClientToServerResponse::NOT_MY_BIRTHDAY); |
| 88 response.set_error_message("Merry Unbirthday!"); |
| 89 response.SerializeToString(params->buffer_out); |
| 90 store_birthday_sent_ = true; |
| 91 return true; |
| 92 } |
| 93 bool result = true; |
| 94 EXPECT_TRUE(!store_birthday_sent_ || post.has_store_birthday()); |
| 95 store_birthday_sent_ = true; |
| 96 if (post.message_contents() == ClientToServerMessage::COMMIT) { |
| 97 ProcessCommit(&post, &response); |
| 98 } else if (post.message_contents() == ClientToServerMessage::GET_UPDATES) { |
| 99 ProcessGetUpdates(&post, &response); |
| 100 } else { |
| 101 EXPECT_TRUE(false) << "Unknown/unsupported ClientToServerMessage"; |
| 102 return false; |
| 103 } |
| 104 if (client_command_.get()) { |
| 105 response.mutable_client_command()->CopyFrom(*client_command_.get()); |
| 106 } |
| 107 response.SerializeToString(params->buffer_out); |
| 108 if (mid_commit_callback_function_) { |
| 109 if (mid_commit_callback_function_(directory_)) |
| 110 mid_commit_callback_function_ = 0; |
| 111 } |
| 112 return result; |
| 113 } |
| 114 |
| 115 bool MockConnectionManager::IsServerReachable() { |
| 116 return true; |
| 117 } |
| 118 |
| 119 bool MockConnectionManager::IsUserAuthenticated() { |
| 120 return true; |
| 121 } |
| 122 |
| 123 void MockConnectionManager::ResetUpdates() { |
| 124 updates_.Clear(); |
| 125 } |
| 126 |
| 127 namespace { |
| 128 |
| 129 void AddDefaultBookmarkData(SyncEntity* entity, bool is_folder) { |
| 130 sync_pb::SyncEntity_BookmarkData* data = entity->mutable_bookmarkdata(); |
| 131 data->set_bookmark_folder(is_folder); |
| 132 |
| 133 if (!is_folder) { |
| 134 data->set_bookmark_url("http://google.com"); |
| 135 } |
| 136 } |
| 137 |
| 138 } // namespace |
| 139 |
| 140 SyncEntity* MockConnectionManager::AddUpdateDirectory(int id, |
| 141 int parent_id, |
| 142 string name, |
| 143 int64 version, |
| 144 int64 sync_ts) { |
| 145 return AddUpdateDirectory(TestIdFactory::FromNumber(id), |
| 146 TestIdFactory::FromNumber(parent_id), |
| 147 name, |
| 148 version, |
| 149 sync_ts); |
| 150 } |
| 151 |
| 152 sync_pb::ClientCommand* MockConnectionManager::GetNextClientCommand() { |
| 153 if (!client_command_.get()) |
| 154 client_command_.reset(new sync_pb::ClientCommand()); |
| 155 return client_command_.get(); |
| 156 } |
| 157 |
| 158 SyncEntity* MockConnectionManager::AddUpdateBookmark(int id, int parent_id, |
| 159 string name, int64 version, |
| 160 int64 sync_ts) { |
| 161 return AddUpdateBookmark(TestIdFactory::FromNumber(id), |
| 162 TestIdFactory::FromNumber(parent_id), |
| 163 name, |
| 164 version, |
| 165 sync_ts); |
| 166 } |
| 167 |
| 168 SyncEntity* MockConnectionManager::AddUpdateFull(string id, string parent_id, |
| 169 string name, int64 version, |
| 170 int64 sync_ts, bool is_dir) { |
| 171 SyncEntity* ent = updates_.add_entries(); |
| 172 ent->set_id_string(id); |
| 173 ent->set_parent_id_string(parent_id); |
| 174 ent->set_name(name); |
| 175 ent->set_version(version); |
| 176 ent->set_sync_timestamp(sync_ts); |
| 177 ent->set_mtime(sync_ts); |
| 178 ent->set_ctime(1); |
| 179 ent->set_position_in_parent(GeneratePositionInParent()); |
| 180 AddDefaultBookmarkData(ent, is_dir); |
| 181 if (sync_ts > updates_.newest_timestamp()) |
| 182 updates_.set_newest_timestamp(sync_ts); |
| 183 return ent; |
| 184 } |
| 185 |
| 186 SyncEntity* MockConnectionManager::AddUpdateDirectory(string id, |
| 187 string parent_id, |
| 188 string name, |
| 189 int64 version, |
| 190 int64 sync_ts) { |
| 191 return AddUpdateFull(id, parent_id, name, version, sync_ts, true); |
| 192 } |
| 193 |
| 194 SyncEntity* MockConnectionManager::AddUpdateBookmark(string id, |
| 195 string parent_id, |
| 196 string name, int64 version, |
| 197 int64 sync_ts) { |
| 198 return AddUpdateFull(id, parent_id, name, version, sync_ts, false); |
| 199 } |
| 200 |
| 201 void MockConnectionManager::SetLastUpdateDeleted() { |
| 202 GetMutableLastUpdate()->set_deleted(true); |
| 203 } |
| 204 |
| 205 void MockConnectionManager::SetLastUpdateOriginatorFields( |
| 206 const string& client_id, |
| 207 const string& entry_id) { |
| 208 GetMutableLastUpdate()->set_originator_cache_guid(client_id); |
| 209 GetMutableLastUpdate()->set_originator_client_item_id(entry_id); |
| 210 } |
| 211 |
| 212 void MockConnectionManager::SetLastUpdateSingletonTag(const string& tag) { |
| 213 GetMutableLastUpdate()->set_singleton_tag(tag); |
| 214 } |
| 215 |
| 216 void MockConnectionManager::SetLastUpdatePosition(int64 server_position) { |
| 217 GetMutableLastUpdate()->set_position_in_parent(server_position); |
| 218 } |
| 219 |
| 220 void MockConnectionManager::SetNewTimestamp(int64 ts) { |
| 221 updates_.set_new_timestamp(ts); |
| 222 } |
| 223 |
| 224 void MockConnectionManager::SetNewestTimestamp(int64 ts) { |
| 225 updates_.set_newest_timestamp(ts); |
| 226 } |
| 227 |
| 228 void MockConnectionManager::ProcessGetUpdates(ClientToServerMessage* csm, |
| 229 ClientToServerResponse* response) { |
| 230 CHECK(csm->has_get_updates()); |
| 231 ASSERT_EQ(csm->message_contents(), ClientToServerMessage::GET_UPDATES); |
| 232 const GetUpdatesMessage& gu = csm->get_updates(); |
| 233 EXPECT_TRUE(gu.has_from_timestamp()); |
| 234 // TODO(sync): filter results dependant on timestamp? or check limits? |
| 235 response->mutable_get_updates()->CopyFrom(updates_); |
| 236 ResetUpdates(); |
| 237 } |
| 238 |
| 239 bool MockConnectionManager::ShouldConflictThisCommit() { |
| 240 bool conflict = false; |
| 241 if (conflict_all_commits_) { |
| 242 conflict = true; |
| 243 } else if (conflict_n_commits_ > 0) { |
| 244 conflict = true; |
| 245 --conflict_n_commits_; |
| 246 } |
| 247 return conflict; |
| 248 } |
| 249 |
| 250 void MockConnectionManager::ProcessCommit(ClientToServerMessage* csm, |
| 251 ClientToServerResponse* response_buffer) { |
| 252 CHECK(csm->has_commit()); |
| 253 ASSERT_EQ(csm->message_contents(), ClientToServerMessage::COMMIT); |
| 254 map <string, string> changed_ids; |
| 255 const CommitMessage& commit_message = csm->commit(); |
| 256 CommitResponse* commit_response = response_buffer->mutable_commit(); |
| 257 commit_messages_.push_back(new CommitMessage); |
| 258 commit_messages_.back()->CopyFrom(commit_message); |
| 259 map<string, CommitResponse_EntryResponse*> response_map; |
| 260 for (int i = 0; i < commit_message.entries_size() ; i++) { |
| 261 const sync_pb::SyncEntity& entry = commit_message.entries(i); |
| 262 CHECK(entry.has_id_string()); |
| 263 string id = entry.id_string(); |
| 264 ASSERT_LT(entry.name().length(), 256ul) << " name probably too long. True " |
| 265 "server name checking not implemented"; |
| 266 if (entry.version() == 0) { |
| 267 // relys on our new item string id format. (string representation of a |
| 268 // negative number) |
| 269 committed_ids_.push_back(syncable::Id::CreateFromClientString(id)); |
| 270 } else { |
| 271 committed_ids_.push_back(syncable::Id::CreateFromServerId(id)); |
| 272 } |
| 273 if (response_map.end() == response_map.find(id)) |
| 274 response_map[id] = commit_response->add_entryresponse(); |
| 275 CommitResponse_EntryResponse* er = response_map[id]; |
| 276 if (ShouldConflictThisCommit()) { |
| 277 er->set_response_type(CommitResponse::CONFLICT); |
| 278 continue; |
| 279 } |
| 280 er->set_response_type(CommitResponse::SUCCESS); |
| 281 er->set_version(entry.version() + 1); |
| 282 if (!commit_time_rename_prepended_string_.empty()) { |
| 283 // Commit time rename sent down from the server |
| 284 er->set_name(commit_time_rename_prepended_string_ + entry.name()); |
| 285 } |
| 286 string parent_id = entry.parent_id_string(); |
| 287 // Remap id's we've already assigned |
| 288 if (changed_ids.end() != changed_ids.find(parent_id)) { |
| 289 parent_id = changed_ids[parent_id]; |
| 290 er->set_parent_id_string(parent_id); |
| 291 } |
| 292 if (entry.has_version() && 0 != entry.version()) { |
| 293 er->set_id_string(id); // allows verification |
| 294 } else { |
| 295 string new_id = StringPrintf("mock_server:%d", next_new_id_++); |
| 296 changed_ids[id] = new_id; |
| 297 er->set_id_string(new_id); |
| 298 } |
| 299 } |
| 300 } |
| 301 |
| 302 SyncEntity* MockConnectionManager::AddUpdateDirectory( |
| 303 syncable::Id id, syncable::Id parent_id, string name, int64 version, |
| 304 int64 sync_ts) { |
| 305 return AddUpdateDirectory(id.GetServerId(), parent_id.GetServerId(), |
| 306 name, version, sync_ts); |
| 307 } |
| 308 |
| 309 SyncEntity* MockConnectionManager::AddUpdateBookmark( |
| 310 syncable::Id id, syncable::Id parent_id, string name, int64 version, |
| 311 int64 sync_ts) { |
| 312 return AddUpdateBookmark(id.GetServerId(), parent_id.GetServerId(), |
| 313 name, version, sync_ts); |
| 314 } |
| 315 |
| 316 void MockConnectionManager::AddUpdateExtendedAttributes(SyncEntity* ent, |
| 317 PathString* xattr_key, syncable::Blob* xattr_value, int xattr_count) { |
| 318 sync_pb::ExtendedAttributes* mutable_extended_attributes = |
| 319 ent->mutable_extended_attributes(); |
| 320 for (int i = 0; i < xattr_count; i++) { |
| 321 sync_pb::ExtendedAttributes_ExtendedAttribute* extended_attribute = |
| 322 mutable_extended_attributes->add_extendedattribute(); |
| 323 extended_attribute->set_key(static_cast<const string&> |
| 324 (browser_sync::ToUTF8(xattr_key[i]))); |
| 325 SyncerProtoUtil::CopyBlobIntoProtoBytes(xattr_value[i], |
| 326 extended_attribute->mutable_value()); |
| 327 } |
| 328 } |
| 329 |
| 330 SyncEntity* MockConnectionManager::GetMutableLastUpdate() { |
| 331 DCHECK(updates_.entries_size() > 0); |
| 332 return updates_.mutable_entries()->Mutable(updates_.entries_size() - 1); |
| 333 } |
| 334 |
| 335 const CommitMessage& MockConnectionManager::last_sent_commit() const { |
| 336 DCHECK(!commit_messages_.empty()); |
| 337 return *commit_messages_.back(); |
| 338 } |
OLD | NEW |