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

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

Issue 2130453004: [Sync] Move //sync to //components/sync. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Rebase. Created 4 years, 4 months 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
« no previous file with comments | « sync/engine/model_type_worker.cc ('k') | sync/engine/net/DEPS » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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.
4
5 #include "sync/engine/model_type_worker.h"
6
7 #include <stddef.h>
8 #include <stdint.h>
9
10 #include <utility>
11
12 #include "base/bind.h"
13 #include "base/memory/ptr_util.h"
14 #include "base/strings/stringprintf.h"
15 #include "sync/engine/commit_contribution.h"
16 #include "sync/internal_api/public/base/model_type.h"
17 #include "sync/internal_api/public/model_type_processor.h"
18 #include "sync/internal_api/public/non_blocking_sync_common.h"
19 #include "sync/protocol/data_type_state.pb.h"
20 #include "sync/protocol/sync.pb.h"
21 #include "sync/sessions/status_controller.h"
22 #include "sync/syncable/syncable_util.h"
23 #include "sync/test/engine/mock_model_type_processor.h"
24 #include "sync/test/engine/mock_nudge_handler.h"
25 #include "sync/test/engine/single_type_mock_server.h"
26 #include "sync/test/fake_encryptor.h"
27 #include "testing/gtest/include/gtest/gtest.h"
28
29 static const syncer::ModelType kModelType = syncer::PREFERENCES;
30
31 // Special constant value taken from cryptographer.cc.
32 const char kNigoriKeyName[] = "nigori-key";
33
34 namespace syncer_v2 {
35
36 using syncer::Cryptographer;
37 using syncer::CommitContribution;
38 using syncer::KeyParams;
39 using syncer::Nigori;
40 using syncer::sessions::StatusController;
41
42 // Tests the ModelTypeWorker.
43 //
44 // This class passes messages between the model thread and sync server.
45 // As such, its code is subject to lots of different race conditions. This
46 // test harness lets us exhaustively test all possible races. We try to
47 // focus on just a few interesting cases.
48 //
49 // Inputs:
50 // - Initial data type state from the model thread.
51 // - Commit requests from the model thread.
52 // - Update responses from the server.
53 // - Commit responses from the server.
54 // - The cryptographer, if encryption is enabled.
55 //
56 // Outputs:
57 // - Commit requests to the server.
58 // - Commit responses to the model thread.
59 // - Update responses to the model thread.
60 // - Nudges to the sync scheduler.
61 //
62 // We use the MockModelTypeProcessor to stub out all communication
63 // with the model thread. That interface is synchronous, which makes it
64 // much easier to test races.
65 //
66 // The interface with the server is built around "pulling" data from this
67 // class, so we don't have to mock out any of it. We wrap it with some
68 // convenience functions so we can emulate server behavior.
69 class ModelTypeWorkerTest : public ::testing::Test {
70 public:
71 ModelTypeWorkerTest();
72 ~ModelTypeWorkerTest() override;
73
74 // One of these Initialize functions should be called at the beginning of
75 // each test.
76
77 // Initializes with no data type state. We will be unable to perform any
78 // significant server action until we receive an update response that
79 // contains the type root node for this type.
80 void FirstInitialize();
81
82 // Initializes with some existing data type state. Allows us to start
83 // committing items right away.
84 void NormalInitialize();
85
86 // Initialize with some saved pending updates from the model thread.
87 void InitializeWithPendingUpdates(
88 const UpdateResponseDataList& initial_pending_updates);
89
90 // Initialize with a custom initial DataTypeState and pending updates.
91 void InitializeWithState(const sync_pb::DataTypeState& state,
92 const UpdateResponseDataList& pending_updates);
93
94 ModelTypeWorker* worker() const { return worker_.get(); }
95
96 // Introduce a new key that the local cryptographer can't decrypt.
97 void NewForeignEncryptionKey();
98
99 // Update the local cryptographer with all relevant keys.
100 void UpdateLocalCryptographer();
101
102 // Use the Nth nigori instance to encrypt incoming updates.
103 // The default value, zero, indicates no encryption.
104 void SetUpdateEncryptionFilter(int n);
105
106 // Modifications on the model thread that get sent to the worker under test.
107 void CommitRequest(const std::string& tag, const std::string& value);
108 void DeleteRequest(const std::string& tag);
109
110 // Pretends to receive update messages from the server.
111 void TriggerTypeRootUpdateFromServer();
112 void TriggerPartialUpdateFromServer(int64_t version_offset,
113 const std::string& tag,
114 const std::string& value);
115 void TriggerUpdateFromServer(int64_t version_offset,
116 const std::string& tag,
117 const std::string& value);
118 void TriggerTombstoneFromServer(int64_t version_offset,
119 const std::string& tag);
120
121 // Delivers specified protos as updates.
122 //
123 // Does not update mock server state. Should be used as a last resort when
124 // writing test cases that require entities that don't fit the normal sync
125 // protocol. Try to use the other, higher level methods if possible.
126 void DeliverRawUpdates(const SyncEntityList& update_list);
127
128 // By default, this harness behaves as if all tasks posted to the model
129 // thread are executed immediately. However, this is not necessarily true.
130 // The model's TaskRunner has a queue, and the tasks we post to it could
131 // linger there for a while. In the meantime, the model thread could
132 // continue posting tasks to the worker based on its stale state.
133 //
134 // If you want to test those race cases, then these functions are for you.
135 void SetModelThreadIsSynchronous(bool is_synchronous);
136 void PumpModelThread();
137
138 // Returns true if the |worker_| is ready to commit something.
139 bool WillCommit();
140
141 // Pretend to successfully commit all outstanding unsynced items.
142 // It is safe to call this only if WillCommit() returns true.
143 void DoSuccessfulCommit();
144
145 // Callback when processor got disconnected with sync.
146 void DisconnectProcessor();
147
148 bool IsProcessorDisconnected();
149 void ResetWorker();
150
151 // Read commit messages the worker_ sent to the emulated server.
152 size_t GetNumCommitMessagesOnServer() const;
153 sync_pb::ClientToServerMessage GetNthCommitMessageOnServer(size_t n) const;
154
155 // Read the latest version of sync entities committed to the emulated server.
156 bool HasCommitEntityOnServer(const std::string& tag) const;
157 sync_pb::SyncEntity GetLatestCommitEntityOnServer(
158 const std::string& tag) const;
159
160 // Read the latest update messages received on the model thread.
161 // Note that if the model thread is in non-blocking mode, this data will not
162 // be updated until the response is actually processed by the model thread.
163 size_t GetNumModelThreadUpdateResponses() const;
164 UpdateResponseDataList GetNthModelThreadUpdateResponse(size_t n) const;
165 sync_pb::DataTypeState GetNthModelThreadUpdateState(size_t n) const;
166
167 // Reads the latest update response datas on the model thread.
168 // Note that if the model thread is in non-blocking mode, this data will not
169 // be updated until the response is actually processed by the model thread.
170 bool HasUpdateResponseOnModelThread(const std::string& tag) const;
171 UpdateResponseData GetUpdateResponseOnModelThread(
172 const std::string& tag) const;
173
174 // Read the latest commit messages received on the model thread.
175 // Note that if the model thread is in non-blocking mode, this data will not
176 // be updated until the response is actually processed by the model thread.
177 size_t GetNumModelThreadCommitResponses() const;
178 CommitResponseDataList GetNthModelThreadCommitResponse(size_t n) const;
179 sync_pb::DataTypeState GetNthModelThreadCommitState(size_t n) const;
180
181 // Reads the latest commit response datas on the model thread.
182 // Note that if the model thread is in non-blocking mode, this data will not
183 // be updated until the response is actually processed by the model thread.
184 bool HasCommitResponseOnModelThread(const std::string& tag) const;
185 CommitResponseData GetCommitResponseOnModelThread(
186 const std::string& tag) const;
187
188 // Returns the number of commit nudges sent to the mock nudge handler.
189 int GetNumCommitNudges() const;
190
191 // Returns the number of initial sync nudges sent to the mock nudge handler.
192 int GetNumInitialDownloadNudges() const;
193
194 // Returns the name of the encryption key in the cryptographer last passed to
195 // the CommitQueue. Returns an empty string if no crypgorapher is
196 // in use. See also: UpdateLocalCryptographer().
197 std::string GetLocalCryptographerKeyName() const;
198
199 // Helpers for building various messages and structures.
200 static std::string GenerateTagHash(const std::string& tag);
201 static sync_pb::EntitySpecifics GenerateSpecifics(const std::string& tag,
202 const std::string& value);
203
204 // Returns a set of KeyParams for the cryptographer. Each input 'n' value
205 // results in a different set of parameters.
206 static KeyParams GetNthKeyParams(int n);
207
208 // Returns the name for the given Nigori.
209 //
210 // Uses some 'white-box' knowledge to mimic the names that a real sync client
211 // would generate. It's probably not necessary to do so, but it can't hurt.
212 static std::string GetNigoriName(const Nigori& nigori);
213
214 // Modifies the input/output parameter |specifics| by encrypting it with
215 // a Nigori intialized with the specified KeyParams.
216 static void EncryptUpdate(const KeyParams& params,
217 sync_pb::EntitySpecifics* specifics);
218
219 private:
220 // An encryptor for our cryptographer.
221 syncer::FakeEncryptor fake_encryptor_;
222
223 // The cryptographer itself. NULL if we're not encrypting the type.
224 std::unique_ptr<Cryptographer> cryptographer_;
225
226 // The number of the most recent foreign encryption key known to our
227 // cryptographer. Note that not all of these will be decryptable.
228 int foreign_encryption_key_index_;
229
230 // The number of the encryption key used to encrypt incoming updates. A zero
231 // value implies no encryption.
232 int update_encryption_filter_index_;
233
234 // The ModelTypeWorker being tested.
235 std::unique_ptr<ModelTypeWorker> worker_;
236
237 // Non-owned, possibly NULL pointer. This object belongs to the
238 // ModelTypeWorker under test.
239 MockModelTypeProcessor* mock_type_processor_;
240
241 // A mock that emulates enough of the sync server that it can be used
242 // a single UpdateHandler and CommitContributor pair. In this test
243 // harness, the |worker_| is both of them.
244 syncer::SingleTypeMockServer mock_server_;
245
246 // A mock to track the number of times the CommitQueue requests to
247 // sync.
248 syncer::MockNudgeHandler mock_nudge_handler_;
249
250 bool is_processor_disconnected_;
251 };
252
253 ModelTypeWorkerTest::ModelTypeWorkerTest()
254 : foreign_encryption_key_index_(0),
255 update_encryption_filter_index_(0),
256 mock_type_processor_(NULL),
257 mock_server_(kModelType),
258 is_processor_disconnected_(false) {}
259
260 ModelTypeWorkerTest::~ModelTypeWorkerTest() {}
261
262 void ModelTypeWorkerTest::FirstInitialize() {
263 sync_pb::DataTypeState initial_state;
264 initial_state.mutable_progress_marker()->set_data_type_id(
265 GetSpecificsFieldNumberFromModelType(kModelType));
266
267 InitializeWithState(initial_state, UpdateResponseDataList());
268 }
269
270 void ModelTypeWorkerTest::NormalInitialize() {
271 InitializeWithPendingUpdates(UpdateResponseDataList());
272 }
273
274 void ModelTypeWorkerTest::InitializeWithPendingUpdates(
275 const UpdateResponseDataList& initial_pending_updates) {
276 sync_pb::DataTypeState initial_state;
277 initial_state.mutable_progress_marker()->set_data_type_id(
278 GetSpecificsFieldNumberFromModelType(kModelType));
279 initial_state.mutable_progress_marker()->set_token(
280 "some_saved_progress_token");
281
282 initial_state.set_initial_sync_done(true);
283
284 InitializeWithState(initial_state, initial_pending_updates);
285
286 mock_nudge_handler_.ClearCounters();
287 }
288
289 void ModelTypeWorkerTest::InitializeWithState(
290 const sync_pb::DataTypeState& state,
291 const UpdateResponseDataList& initial_pending_updates) {
292 DCHECK(!worker_);
293
294 // We don't get to own this object. The |worker_| keeps a unique_ptr to it.
295 mock_type_processor_ = new MockModelTypeProcessor();
296 mock_type_processor_->SetDisconnectCallback(base::Bind(
297 &ModelTypeWorkerTest::DisconnectProcessor, base::Unretained(this)));
298 std::unique_ptr<ModelTypeProcessor> proxy(mock_type_processor_);
299
300 std::unique_ptr<Cryptographer> cryptographer_copy;
301 if (cryptographer_) {
302 cryptographer_copy.reset(new Cryptographer(*cryptographer_));
303 }
304
305 // TODO(maxbogue): crbug.com/529498: Inject pending updates somehow.
306 worker_.reset(new ModelTypeWorker(kModelType, state,
307 std::move(cryptographer_copy),
308 &mock_nudge_handler_, std::move(proxy)));
309 }
310
311 void ModelTypeWorkerTest::NewForeignEncryptionKey() {
312 if (!cryptographer_) {
313 cryptographer_.reset(new Cryptographer(&fake_encryptor_));
314 }
315
316 foreign_encryption_key_index_++;
317
318 sync_pb::NigoriKeyBag bag;
319
320 for (int i = 0; i <= foreign_encryption_key_index_; ++i) {
321 Nigori nigori;
322 KeyParams params = GetNthKeyParams(i);
323 nigori.InitByDerivation(params.hostname, params.username, params.password);
324
325 sync_pb::NigoriKey* key = bag.add_key();
326
327 key->set_name(GetNigoriName(nigori));
328 nigori.ExportKeys(key->mutable_user_key(), key->mutable_encryption_key(),
329 key->mutable_mac_key());
330 }
331
332 // Re-create the last nigori from that loop.
333 Nigori last_nigori;
334 KeyParams params = GetNthKeyParams(foreign_encryption_key_index_);
335 last_nigori.InitByDerivation(params.hostname, params.username,
336 params.password);
337
338 // Serialize and encrypt the bag with the last nigori.
339 std::string serialized_bag;
340 bag.SerializeToString(&serialized_bag);
341
342 sync_pb::EncryptedData encrypted;
343 encrypted.set_key_name(GetNigoriName(last_nigori));
344 last_nigori.Encrypt(serialized_bag, encrypted.mutable_blob());
345
346 // Update the cryptographer with new pending keys.
347 cryptographer_->SetPendingKeys(encrypted);
348
349 // Update the worker with the latest cryptographer.
350 if (worker_) {
351 worker_->UpdateCryptographer(
352 base::WrapUnique(new Cryptographer(*cryptographer_)));
353 }
354 }
355
356 void ModelTypeWorkerTest::UpdateLocalCryptographer() {
357 if (!cryptographer_) {
358 cryptographer_.reset(new Cryptographer(&fake_encryptor_));
359 }
360
361 KeyParams params = GetNthKeyParams(foreign_encryption_key_index_);
362 bool success = cryptographer_->DecryptPendingKeys(params);
363 DCHECK(success);
364
365 // Update the worker with the latest cryptographer.
366 if (worker_) {
367 worker_->UpdateCryptographer(
368 base::WrapUnique(new Cryptographer(*cryptographer_)));
369 }
370 }
371
372 void ModelTypeWorkerTest::SetUpdateEncryptionFilter(int n) {
373 update_encryption_filter_index_ = n;
374 }
375
376 void ModelTypeWorkerTest::CommitRequest(const std::string& name,
377 const std::string& value) {
378 const std::string tag_hash = GenerateTagHash(name);
379 CommitRequestData data = mock_type_processor_->CommitRequest(
380 tag_hash, GenerateSpecifics(name, value));
381 CommitRequestDataList list;
382 list.push_back(data);
383 worker_->EnqueueForCommit(list);
384 }
385
386 void ModelTypeWorkerTest::DeleteRequest(const std::string& tag) {
387 const std::string tag_hash = GenerateTagHash(tag);
388 CommitRequestData data = mock_type_processor_->DeleteRequest(tag_hash);
389 CommitRequestDataList list;
390 list.push_back(data);
391 worker_->EnqueueForCommit(list);
392 }
393
394 void ModelTypeWorkerTest::TriggerTypeRootUpdateFromServer() {
395 sync_pb::SyncEntity entity = mock_server_.TypeRootUpdate();
396 SyncEntityList entity_list;
397 entity_list.push_back(&entity);
398
399 StatusController dummy_status;
400
401 worker_->ProcessGetUpdatesResponse(mock_server_.GetProgress(),
402 mock_server_.GetContext(), entity_list,
403 &dummy_status);
404 worker_->PassiveApplyUpdates(&dummy_status);
405 }
406
407 void ModelTypeWorkerTest::TriggerPartialUpdateFromServer(
408 int64_t version_offset,
409 const std::string& tag,
410 const std::string& value) {
411 sync_pb::SyncEntity entity = mock_server_.UpdateFromServer(
412 version_offset, GenerateTagHash(tag), GenerateSpecifics(tag, value));
413
414 if (update_encryption_filter_index_ != 0) {
415 EncryptUpdate(GetNthKeyParams(update_encryption_filter_index_),
416 entity.mutable_specifics());
417 }
418
419 SyncEntityList entity_list;
420 entity_list.push_back(&entity);
421
422 StatusController dummy_status;
423
424 worker_->ProcessGetUpdatesResponse(mock_server_.GetProgress(),
425 mock_server_.GetContext(), entity_list,
426 &dummy_status);
427 }
428
429 void ModelTypeWorkerTest::TriggerUpdateFromServer(int64_t version_offset,
430 const std::string& tag,
431 const std::string& value) {
432 TriggerPartialUpdateFromServer(version_offset, tag, value);
433 StatusController dummy_status;
434 worker_->ApplyUpdates(&dummy_status);
435 }
436
437 void ModelTypeWorkerTest::DeliverRawUpdates(const SyncEntityList& list) {
438 StatusController dummy_status;
439 worker_->ProcessGetUpdatesResponse(mock_server_.GetProgress(),
440 mock_server_.GetContext(), list,
441 &dummy_status);
442 worker_->ApplyUpdates(&dummy_status);
443 }
444
445 void ModelTypeWorkerTest::TriggerTombstoneFromServer(int64_t version_offset,
446 const std::string& tag) {
447 sync_pb::SyncEntity entity =
448 mock_server_.TombstoneFromServer(version_offset, GenerateTagHash(tag));
449
450 if (update_encryption_filter_index_ != 0) {
451 EncryptUpdate(GetNthKeyParams(update_encryption_filter_index_),
452 entity.mutable_specifics());
453 }
454
455 SyncEntityList entity_list;
456 entity_list.push_back(&entity);
457
458 StatusController dummy_status;
459
460 worker_->ProcessGetUpdatesResponse(mock_server_.GetProgress(),
461 mock_server_.GetContext(), entity_list,
462 &dummy_status);
463 worker_->ApplyUpdates(&dummy_status);
464 }
465
466 void ModelTypeWorkerTest::SetModelThreadIsSynchronous(bool is_synchronous) {
467 mock_type_processor_->SetSynchronousExecution(is_synchronous);
468 }
469
470 void ModelTypeWorkerTest::PumpModelThread() {
471 mock_type_processor_->RunQueuedTasks();
472 }
473
474 bool ModelTypeWorkerTest::WillCommit() {
475 std::unique_ptr<CommitContribution> contribution(
476 worker_->GetContribution(INT_MAX));
477
478 if (contribution) {
479 contribution->CleanUp(); // Gracefully abort the commit.
480 return true;
481 } else {
482 return false;
483 }
484 }
485
486 // Conveniently, this is all one big synchronous operation. The sync thread
487 // remains blocked while the commit is in progress, so we don't need to worry
488 // about other tasks being run between the time when the commit request is
489 // issued and the time when the commit response is received.
490 void ModelTypeWorkerTest::DoSuccessfulCommit() {
491 DCHECK(WillCommit());
492 std::unique_ptr<CommitContribution> contribution(
493 worker_->GetContribution(INT_MAX));
494
495 sync_pb::ClientToServerMessage message;
496 contribution->AddToCommitMessage(&message);
497
498 sync_pb::ClientToServerResponse response =
499 mock_server_.DoSuccessfulCommit(message);
500
501 StatusController dummy_status;
502 contribution->ProcessCommitResponse(response, &dummy_status);
503 contribution->CleanUp();
504 }
505
506 void ModelTypeWorkerTest::DisconnectProcessor() {
507 DCHECK(!is_processor_disconnected_);
508 is_processor_disconnected_ = true;
509 }
510
511 bool ModelTypeWorkerTest::IsProcessorDisconnected() {
512 return is_processor_disconnected_;
513 }
514
515 void ModelTypeWorkerTest::ResetWorker() {
516 worker_.reset();
517 }
518
519 size_t ModelTypeWorkerTest::GetNumCommitMessagesOnServer() const {
520 return mock_server_.GetNumCommitMessages();
521 }
522
523 sync_pb::ClientToServerMessage ModelTypeWorkerTest::GetNthCommitMessageOnServer(
524 size_t n) const {
525 DCHECK_LT(n, GetNumCommitMessagesOnServer());
526 return mock_server_.GetNthCommitMessage(n);
527 }
528
529 bool ModelTypeWorkerTest::HasCommitEntityOnServer(
530 const std::string& tag) const {
531 const std::string tag_hash = GenerateTagHash(tag);
532 return mock_server_.HasCommitEntity(tag_hash);
533 }
534
535 sync_pb::SyncEntity ModelTypeWorkerTest::GetLatestCommitEntityOnServer(
536 const std::string& tag) const {
537 DCHECK(HasCommitEntityOnServer(tag));
538 const std::string tag_hash = GenerateTagHash(tag);
539 return mock_server_.GetLastCommittedEntity(tag_hash);
540 }
541
542 size_t ModelTypeWorkerTest::GetNumModelThreadUpdateResponses() const {
543 return mock_type_processor_->GetNumUpdateResponses();
544 }
545
546 UpdateResponseDataList ModelTypeWorkerTest::GetNthModelThreadUpdateResponse(
547 size_t n) const {
548 DCHECK_LT(n, GetNumModelThreadUpdateResponses());
549 return mock_type_processor_->GetNthUpdateResponse(n);
550 }
551
552 sync_pb::DataTypeState ModelTypeWorkerTest::GetNthModelThreadUpdateState(
553 size_t n) const {
554 DCHECK_LT(n, GetNumModelThreadUpdateResponses());
555 return mock_type_processor_->GetNthTypeStateReceivedInUpdateResponse(n);
556 }
557
558 bool ModelTypeWorkerTest::HasUpdateResponseOnModelThread(
559 const std::string& tag) const {
560 const std::string tag_hash = GenerateTagHash(tag);
561 return mock_type_processor_->HasUpdateResponse(tag_hash);
562 }
563
564 UpdateResponseData ModelTypeWorkerTest::GetUpdateResponseOnModelThread(
565 const std::string& tag) const {
566 const std::string tag_hash = GenerateTagHash(tag);
567 return mock_type_processor_->GetUpdateResponse(tag_hash);
568 }
569
570 size_t ModelTypeWorkerTest::GetNumModelThreadCommitResponses() const {
571 return mock_type_processor_->GetNumCommitResponses();
572 }
573
574 CommitResponseDataList ModelTypeWorkerTest::GetNthModelThreadCommitResponse(
575 size_t n) const {
576 DCHECK_LT(n, GetNumModelThreadCommitResponses());
577 return mock_type_processor_->GetNthCommitResponse(n);
578 }
579
580 sync_pb::DataTypeState ModelTypeWorkerTest::GetNthModelThreadCommitState(
581 size_t n) const {
582 DCHECK_LT(n, GetNumModelThreadCommitResponses());
583 return mock_type_processor_->GetNthTypeStateReceivedInCommitResponse(n);
584 }
585
586 bool ModelTypeWorkerTest::HasCommitResponseOnModelThread(
587 const std::string& tag) const {
588 const std::string tag_hash = GenerateTagHash(tag);
589 return mock_type_processor_->HasCommitResponse(tag_hash);
590 }
591
592 CommitResponseData ModelTypeWorkerTest::GetCommitResponseOnModelThread(
593 const std::string& tag) const {
594 DCHECK(HasCommitResponseOnModelThread(tag));
595 const std::string tag_hash = GenerateTagHash(tag);
596 return mock_type_processor_->GetCommitResponse(tag_hash);
597 }
598
599 int ModelTypeWorkerTest::GetNumCommitNudges() const {
600 return mock_nudge_handler_.GetNumCommitNudges();
601 }
602
603 int ModelTypeWorkerTest::GetNumInitialDownloadNudges() const {
604 return mock_nudge_handler_.GetNumInitialDownloadNudges();
605 }
606
607 std::string ModelTypeWorkerTest::GetLocalCryptographerKeyName() const {
608 if (!cryptographer_) {
609 return std::string();
610 }
611
612 return cryptographer_->GetDefaultNigoriKeyName();
613 }
614
615 // static.
616 std::string ModelTypeWorkerTest::GenerateTagHash(const std::string& tag) {
617 const std::string& client_tag_hash =
618 syncer::syncable::GenerateSyncableHash(kModelType, tag);
619 return client_tag_hash;
620 }
621
622 // static.
623 sync_pb::EntitySpecifics ModelTypeWorkerTest::GenerateSpecifics(
624 const std::string& tag,
625 const std::string& value) {
626 sync_pb::EntitySpecifics specifics;
627 specifics.mutable_preference()->set_name(tag);
628 specifics.mutable_preference()->set_value(value);
629 return specifics;
630 }
631
632 // static.
633 std::string ModelTypeWorkerTest::GetNigoriName(const Nigori& nigori) {
634 std::string name;
635 if (!nigori.Permute(Nigori::Password, kNigoriKeyName, &name)) {
636 NOTREACHED();
637 return std::string();
638 }
639
640 return name;
641 }
642
643 // static.
644 KeyParams ModelTypeWorkerTest::GetNthKeyParams(int n) {
645 KeyParams params;
646 params.hostname = std::string("localhost");
647 params.username = std::string("userX");
648 params.password = base::StringPrintf("pw%02d", n);
649 return params;
650 }
651
652 // static.
653 void ModelTypeWorkerTest::EncryptUpdate(const KeyParams& params,
654 sync_pb::EntitySpecifics* specifics) {
655 Nigori nigori;
656 nigori.InitByDerivation(params.hostname, params.username, params.password);
657
658 sync_pb::EntitySpecifics original_specifics = *specifics;
659 std::string plaintext;
660 original_specifics.SerializeToString(&plaintext);
661
662 std::string encrypted;
663 nigori.Encrypt(plaintext, &encrypted);
664
665 specifics->Clear();
666 AddDefaultFieldValue(kModelType, specifics);
667 specifics->mutable_encrypted()->set_key_name(GetNigoriName(nigori));
668 specifics->mutable_encrypted()->set_blob(encrypted);
669 }
670
671 // Requests a commit and verifies the messages sent to the client and server as
672 // a result.
673 //
674 // This test performs sanity checks on most of the fields in these messages.
675 // For the most part this is checking that the test code behaves as expected
676 // and the |worker_| doesn't mess up its simple task of moving around these
677 // values. It makes sense to have one or two tests that are this thorough, but
678 // we shouldn't be this verbose in all tests.
679 TEST_F(ModelTypeWorkerTest, SimpleCommit) {
680 NormalInitialize();
681
682 EXPECT_FALSE(WillCommit());
683 EXPECT_EQ(0U, GetNumCommitMessagesOnServer());
684 EXPECT_EQ(0U, GetNumModelThreadCommitResponses());
685
686 CommitRequest("tag1", "value1");
687
688 EXPECT_EQ(1, GetNumCommitNudges());
689
690 ASSERT_TRUE(WillCommit());
691 DoSuccessfulCommit();
692
693 const std::string& client_tag_hash = GenerateTagHash("tag1");
694
695 // Exhaustively verify the SyncEntity sent in the commit message.
696 ASSERT_EQ(1U, GetNumCommitMessagesOnServer());
697 EXPECT_EQ(1, GetNthCommitMessageOnServer(0).commit().entries_size());
698 ASSERT_TRUE(HasCommitEntityOnServer("tag1"));
699 const sync_pb::SyncEntity& entity = GetLatestCommitEntityOnServer("tag1");
700 EXPECT_FALSE(entity.id_string().empty());
701 EXPECT_EQ(0, entity.version());
702 EXPECT_NE(0, entity.mtime());
703 EXPECT_NE(0, entity.ctime());
704 EXPECT_FALSE(entity.name().empty());
705 EXPECT_EQ(client_tag_hash, entity.client_defined_unique_tag());
706 EXPECT_EQ("tag1", entity.specifics().preference().name());
707 EXPECT_FALSE(entity.deleted());
708 EXPECT_EQ("value1", entity.specifics().preference().value());
709
710 // Exhaustively verify the commit response returned to the model thread.
711 ASSERT_EQ(1U, GetNumModelThreadCommitResponses());
712 EXPECT_EQ(1U, GetNthModelThreadCommitResponse(0).size());
713 ASSERT_TRUE(HasCommitResponseOnModelThread("tag1"));
714 const CommitResponseData& commit_response =
715 GetCommitResponseOnModelThread("tag1");
716
717 // The ID changes in a commit response to initial commit.
718 EXPECT_FALSE(commit_response.id.empty());
719 EXPECT_NE(entity.id_string(), commit_response.id);
720
721 EXPECT_EQ(client_tag_hash, commit_response.client_tag_hash);
722 EXPECT_LT(0, commit_response.response_version);
723 EXPECT_LT(0, commit_response.sequence_number);
724 EXPECT_FALSE(commit_response.specifics_hash.empty());
725 }
726
727 TEST_F(ModelTypeWorkerTest, SimpleDelete) {
728 NormalInitialize();
729
730 // We can't delete an entity that was never committed.
731 // Step 1 is to create and commit a new entity.
732 CommitRequest("tag1", "value1");
733 EXPECT_EQ(1, GetNumCommitNudges());
734 ASSERT_TRUE(WillCommit());
735 DoSuccessfulCommit();
736
737 ASSERT_TRUE(HasCommitResponseOnModelThread("tag1"));
738 const CommitResponseData& initial_commit_response =
739 GetCommitResponseOnModelThread("tag1");
740 int64_t base_version = initial_commit_response.response_version;
741
742 // Now that we have an entity, we can delete it.
743 DeleteRequest("tag1");
744 ASSERT_TRUE(WillCommit());
745 DoSuccessfulCommit();
746
747 // Verify the SyncEntity sent in the commit message.
748 ASSERT_EQ(2U, GetNumCommitMessagesOnServer());
749 EXPECT_EQ(1, GetNthCommitMessageOnServer(1).commit().entries_size());
750 ASSERT_TRUE(HasCommitEntityOnServer("tag1"));
751 const sync_pb::SyncEntity& entity = GetLatestCommitEntityOnServer("tag1");
752 EXPECT_FALSE(entity.id_string().empty());
753 EXPECT_EQ(GenerateTagHash("tag1"), entity.client_defined_unique_tag());
754 EXPECT_EQ(base_version, entity.version());
755 EXPECT_TRUE(entity.deleted());
756
757 // Deletions should contain enough specifics to identify the type.
758 EXPECT_TRUE(entity.has_specifics());
759 EXPECT_EQ(kModelType, syncer::GetModelTypeFromSpecifics(entity.specifics()));
760
761 // Verify the commit response returned to the model thread.
762 ASSERT_EQ(2U, GetNumModelThreadCommitResponses());
763 EXPECT_EQ(1U, GetNthModelThreadCommitResponse(1).size());
764 ASSERT_TRUE(HasCommitResponseOnModelThread("tag1"));
765 const CommitResponseData& commit_response =
766 GetCommitResponseOnModelThread("tag1");
767
768 EXPECT_EQ(entity.id_string(), commit_response.id);
769 EXPECT_EQ(entity.client_defined_unique_tag(),
770 commit_response.client_tag_hash);
771 EXPECT_EQ(entity.version(), commit_response.response_version);
772 }
773
774 // The server doesn't like it when we try to delete an entity it's never heard
775 // of before. This test helps ensure we avoid that scenario.
776 TEST_F(ModelTypeWorkerTest, NoDeleteUncommitted) {
777 NormalInitialize();
778
779 // Request the commit of a new, never-before-seen item.
780 CommitRequest("tag1", "value1");
781 EXPECT_TRUE(WillCommit());
782 EXPECT_EQ(1, GetNumCommitNudges());
783
784 // Request a deletion of that item before we've had a chance to commit it.
785 DeleteRequest("tag1");
786 EXPECT_FALSE(WillCommit());
787 EXPECT_EQ(2, GetNumCommitNudges());
788 }
789
790 // Verifies the sending of an "initial sync done" signal.
791 TEST_F(ModelTypeWorkerTest, SendInitialSyncDone) {
792 FirstInitialize(); // Initialize with no saved sync state.
793 EXPECT_EQ(0U, GetNumModelThreadUpdateResponses());
794 EXPECT_EQ(1, GetNumInitialDownloadNudges());
795
796 EXPECT_FALSE(worker()->IsInitialSyncEnded());
797
798 // Receive an update response that contains only the type root node.
799 TriggerTypeRootUpdateFromServer();
800
801 // One update triggered by ApplyUpdates, which the worker interprets to mean
802 // "initial sync done". This triggers a model thread update, too.
803 EXPECT_EQ(1U, GetNumModelThreadUpdateResponses());
804
805 // The update contains no entities.
806 EXPECT_EQ(0U, GetNthModelThreadUpdateResponse(0).size());
807
808 const sync_pb::DataTypeState& state = GetNthModelThreadUpdateState(0);
809 EXPECT_FALSE(state.progress_marker().token().empty());
810 EXPECT_TRUE(state.initial_sync_done());
811 EXPECT_TRUE(worker()->IsInitialSyncEnded());
812 }
813
814 // Commit two new entities in two separate commit messages.
815 TEST_F(ModelTypeWorkerTest, TwoNewItemsCommittedSeparately) {
816 NormalInitialize();
817
818 // Commit the first of two entities.
819 CommitRequest("tag1", "value1");
820 EXPECT_EQ(1, GetNumCommitNudges());
821 ASSERT_TRUE(WillCommit());
822 DoSuccessfulCommit();
823 ASSERT_EQ(1U, GetNumCommitMessagesOnServer());
824 EXPECT_EQ(1, GetNthCommitMessageOnServer(0).commit().entries_size());
825 ASSERT_TRUE(HasCommitEntityOnServer("tag1"));
826 const sync_pb::SyncEntity& tag1_entity =
827 GetLatestCommitEntityOnServer("tag1");
828
829 // Commit the second of two entities.
830 CommitRequest("tag2", "value2");
831 EXPECT_EQ(2, GetNumCommitNudges());
832 ASSERT_TRUE(WillCommit());
833 DoSuccessfulCommit();
834 ASSERT_EQ(2U, GetNumCommitMessagesOnServer());
835 EXPECT_EQ(1, GetNthCommitMessageOnServer(1).commit().entries_size());
836 ASSERT_TRUE(HasCommitEntityOnServer("tag2"));
837 const sync_pb::SyncEntity& tag2_entity =
838 GetLatestCommitEntityOnServer("tag2");
839
840 EXPECT_FALSE(WillCommit());
841
842 // The IDs assigned by the |worker_| should be unique.
843 EXPECT_NE(tag1_entity.id_string(), tag2_entity.id_string());
844
845 // Check that the committed specifics values are sane.
846 EXPECT_EQ(tag1_entity.specifics().preference().value(), "value1");
847 EXPECT_EQ(tag2_entity.specifics().preference().value(), "value2");
848
849 // There should have been two separate commit responses sent to the model
850 // thread. They should be uninteresting, so we don't bother inspecting them.
851 EXPECT_EQ(2U, GetNumModelThreadCommitResponses());
852 }
853
854 // Test normal update receipt code path.
855 TEST_F(ModelTypeWorkerTest, ReceiveUpdates) {
856 NormalInitialize();
857
858 const std::string& tag_hash = GenerateTagHash("tag1");
859
860 TriggerUpdateFromServer(10, "tag1", "value1");
861
862 ASSERT_EQ(1U, GetNumModelThreadUpdateResponses());
863 UpdateResponseDataList updates_list = GetNthModelThreadUpdateResponse(0);
864 ASSERT_EQ(1U, updates_list.size());
865
866 ASSERT_TRUE(HasUpdateResponseOnModelThread("tag1"));
867 UpdateResponseData update = GetUpdateResponseOnModelThread("tag1");
868 const EntityData& entity = update.entity.value();
869
870 EXPECT_FALSE(entity.id.empty());
871 EXPECT_EQ(tag_hash, entity.client_tag_hash);
872 EXPECT_LT(0, update.response_version);
873 EXPECT_FALSE(entity.creation_time.is_null());
874 EXPECT_FALSE(entity.modification_time.is_null());
875 EXPECT_FALSE(entity.non_unique_name.empty());
876 EXPECT_FALSE(entity.is_deleted());
877 EXPECT_EQ("tag1", entity.specifics.preference().name());
878 EXPECT_EQ("value1", entity.specifics.preference().value());
879 }
880
881 // Test that an update download coming in multiple parts gets accumulated into
882 // one call to the processor.
883 TEST_F(ModelTypeWorkerTest, ReceiveMultiPartUpdates) {
884 NormalInitialize();
885
886 // A partial update response doesn't pass anything to the processor.
887 TriggerPartialUpdateFromServer(10, "tag1", "value1");
888 ASSERT_EQ(0U, GetNumModelThreadUpdateResponses());
889
890 // Trigger the completion of the update.
891 TriggerUpdateFromServer(10, "tag2", "value2");
892
893 // Processor received exactly one update with entities in the right order.
894 ASSERT_EQ(1U, GetNumModelThreadUpdateResponses());
895 UpdateResponseDataList updates = GetNthModelThreadUpdateResponse(0);
896 ASSERT_EQ(2U, updates.size());
897 EXPECT_EQ(GenerateTagHash("tag1"), updates[0].entity->client_tag_hash);
898 EXPECT_EQ(GenerateTagHash("tag2"), updates[1].entity->client_tag_hash);
899
900 // A subsequent update doesn't pass the same entities again.
901 TriggerUpdateFromServer(10, "tag3", "value3");
902 ASSERT_EQ(2U, GetNumModelThreadUpdateResponses());
903 updates = GetNthModelThreadUpdateResponse(1);
904 ASSERT_EQ(1U, updates.size());
905 EXPECT_EQ(GenerateTagHash("tag3"), updates[0].entity->client_tag_hash);
906 }
907
908 // Test commit of encrypted updates.
909 TEST_F(ModelTypeWorkerTest, EncryptedCommit) {
910 NormalInitialize();
911
912 ASSERT_EQ(0U, GetNumModelThreadUpdateResponses());
913
914 NewForeignEncryptionKey();
915 UpdateLocalCryptographer();
916
917 ASSERT_EQ(1U, GetNumModelThreadUpdateResponses());
918 EXPECT_EQ(GetLocalCryptographerKeyName(),
919 GetNthModelThreadUpdateState(0).encryption_key_name());
920
921 // Normal commit request stuff.
922 CommitRequest("tag1", "value1");
923 DoSuccessfulCommit();
924 ASSERT_EQ(1U, GetNumCommitMessagesOnServer());
925 EXPECT_EQ(1, GetNthCommitMessageOnServer(0).commit().entries_size());
926 ASSERT_TRUE(HasCommitEntityOnServer("tag1"));
927 const sync_pb::SyncEntity& tag1_entity =
928 GetLatestCommitEntityOnServer("tag1");
929
930 EXPECT_TRUE(tag1_entity.specifics().has_encrypted());
931
932 // The title should be overwritten.
933 EXPECT_EQ(tag1_entity.name(), "encrypted");
934
935 // The type should be set, but there should be no non-encrypted contents.
936 EXPECT_TRUE(tag1_entity.specifics().has_preference());
937 EXPECT_FALSE(tag1_entity.specifics().preference().has_name());
938 EXPECT_FALSE(tag1_entity.specifics().preference().has_value());
939 }
940
941 // Test items are not committed when encryption is required but unavailable.
942 TEST_F(ModelTypeWorkerTest, EncryptionBlocksCommits) {
943 NormalInitialize();
944
945 CommitRequest("tag1", "value1");
946 EXPECT_TRUE(WillCommit());
947
948 // We know encryption is in use on this account, but don't have the necessary
949 // encryption keys. The worker should refuse to commit.
950 NewForeignEncryptionKey();
951 EXPECT_FALSE(WillCommit());
952
953 // Once the cryptographer is returned to a normal state, we should be able to
954 // commit again.
955 EXPECT_EQ(1, GetNumCommitNudges());
956 UpdateLocalCryptographer();
957 EXPECT_EQ(2, GetNumCommitNudges());
958 EXPECT_TRUE(WillCommit());
959
960 // Verify the committed entity was properly encrypted.
961 DoSuccessfulCommit();
962 ASSERT_EQ(1U, GetNumCommitMessagesOnServer());
963 EXPECT_EQ(1, GetNthCommitMessageOnServer(0).commit().entries_size());
964 ASSERT_TRUE(HasCommitEntityOnServer("tag1"));
965 const sync_pb::SyncEntity& tag1_entity =
966 GetLatestCommitEntityOnServer("tag1");
967 EXPECT_TRUE(tag1_entity.specifics().has_encrypted());
968 EXPECT_EQ(tag1_entity.name(), "encrypted");
969 EXPECT_TRUE(tag1_entity.specifics().has_preference());
970 EXPECT_FALSE(tag1_entity.specifics().preference().has_name());
971 EXPECT_FALSE(tag1_entity.specifics().preference().has_value());
972 }
973
974 // Test the receipt of decryptable entities.
975 TEST_F(ModelTypeWorkerTest, ReceiveDecryptableEntities) {
976 NormalInitialize();
977
978 // Create a new Nigori and allow the cryptographer to decrypt it.
979 NewForeignEncryptionKey();
980 UpdateLocalCryptographer();
981
982 // First, receive an unencrypted entry.
983 TriggerUpdateFromServer(10, "tag1", "value1");
984
985 // Test some basic properties regarding the update.
986 ASSERT_TRUE(HasUpdateResponseOnModelThread("tag1"));
987 UpdateResponseData update1 = GetUpdateResponseOnModelThread("tag1");
988 EXPECT_EQ("tag1", update1.entity->specifics.preference().name());
989 EXPECT_EQ("value1", update1.entity->specifics.preference().value());
990 EXPECT_TRUE(update1.encryption_key_name.empty());
991
992 // Set received updates to be encrypted using the new nigori.
993 SetUpdateEncryptionFilter(1);
994
995 // This next update will be encrypted.
996 TriggerUpdateFromServer(10, "tag2", "value2");
997
998 // Test its basic features and the value of encryption_key_name.
999 ASSERT_TRUE(HasUpdateResponseOnModelThread("tag2"));
1000 UpdateResponseData update2 = GetUpdateResponseOnModelThread("tag2");
1001 EXPECT_EQ("tag2", update2.entity->specifics.preference().name());
1002 EXPECT_EQ("value2", update2.entity->specifics.preference().value());
1003 EXPECT_FALSE(update2.encryption_key_name.empty());
1004 }
1005
1006 // Test initializing a CommitQueue with a cryptographer at startup.
1007 TEST_F(ModelTypeWorkerTest, InitializeWithCryptographer) {
1008 // Set up some encryption state.
1009 NewForeignEncryptionKey();
1010 UpdateLocalCryptographer();
1011
1012 // Then initialize.
1013 NormalInitialize();
1014
1015 // The worker should tell the model thread about encryption as soon as
1016 // possible, so that it will have the chance to re-encrypt local data if
1017 // necessary.
1018 ASSERT_EQ(1U, GetNumModelThreadUpdateResponses());
1019 EXPECT_EQ(GetLocalCryptographerKeyName(),
1020 GetNthModelThreadUpdateState(0).encryption_key_name());
1021 }
1022
1023 // Receive updates that are initially undecryptable, then ensure they get
1024 // delivered to the model thread when decryption becomes possible.
1025 TEST_F(ModelTypeWorkerTest, ReceiveUndecryptableEntries) {
1026 NormalInitialize();
1027
1028 // Receive a new foreign encryption key that we can't decrypt.
1029 NewForeignEncryptionKey();
1030
1031 // Receive an encrypted with that new key, which we can't access.
1032 SetUpdateEncryptionFilter(1);
1033 TriggerUpdateFromServer(10, "tag1", "value1");
1034
1035 // At this point, the cryptographer does not have access to the key, so the
1036 // updates will be undecryptable. They'll be transfered to the model thread
1037 // for safe-keeping as pending updates.
1038 ASSERT_EQ(1U, GetNumModelThreadUpdateResponses());
1039 UpdateResponseDataList updates_list = GetNthModelThreadUpdateResponse(0);
1040 EXPECT_EQ(0U, updates_list.size());
1041
1042 // The update will be delivered as soon as decryption becomes possible.
1043 UpdateLocalCryptographer();
1044 ASSERT_TRUE(HasUpdateResponseOnModelThread("tag1"));
1045 UpdateResponseData update = GetUpdateResponseOnModelThread("tag1");
1046 EXPECT_EQ("tag1", update.entity->specifics.preference().name());
1047 EXPECT_EQ("value1", update.entity->specifics.preference().value());
1048 EXPECT_FALSE(update.encryption_key_name.empty());
1049 }
1050
1051 // Ensure that even encrypted updates can cause conflicts.
1052 TEST_F(ModelTypeWorkerTest, EncryptedUpdateOverridesPendingCommit) {
1053 NormalInitialize();
1054
1055 // Prepeare to commit an item.
1056 CommitRequest("tag1", "value1");
1057 EXPECT_TRUE(WillCommit());
1058
1059 // Receive an encrypted update for that item.
1060 SetUpdateEncryptionFilter(1);
1061 TriggerUpdateFromServer(10, "tag1", "value1");
1062
1063 // The pending commit state should be cleared.
1064 EXPECT_FALSE(WillCommit());
1065
1066 // The encrypted update will be delivered to the model thread.
1067 ASSERT_EQ(1U, GetNumModelThreadUpdateResponses());
1068 UpdateResponseDataList updates_list = GetNthModelThreadUpdateResponse(0);
1069 EXPECT_EQ(0U, updates_list.size());
1070 }
1071
1072 // Test decryption of pending updates saved across a restart.
1073 TEST_F(ModelTypeWorkerTest, RestorePendingEntries) {
1074 // Create a fake pending update.
1075 EntityData entity;
1076 entity.client_tag_hash = GenerateTagHash("tag1");
1077 entity.id = "SomeID";
1078 entity.creation_time =
1079 base::Time::UnixEpoch() + base::TimeDelta::FromSeconds(10);
1080 entity.modification_time =
1081 base::Time::UnixEpoch() + base::TimeDelta::FromSeconds(11);
1082 entity.non_unique_name = "encrypted";
1083 entity.specifics = GenerateSpecifics("tag1", "value1");
1084 EncryptUpdate(GetNthKeyParams(1), &(entity.specifics));
1085
1086 UpdateResponseData update;
1087 update.entity = entity.PassToPtr();
1088 update.response_version = 100;
1089
1090 // Inject the update during CommitQueue initialization.
1091 UpdateResponseDataList saved_pending_updates;
1092 saved_pending_updates.push_back(update);
1093 InitializeWithPendingUpdates(saved_pending_updates);
1094
1095 // Update will be undecryptable at first.
1096 EXPECT_EQ(0U, GetNumModelThreadUpdateResponses());
1097 ASSERT_FALSE(HasUpdateResponseOnModelThread("tag1"));
1098
1099 // Update the cryptographer so it can decrypt that update.
1100 NewForeignEncryptionKey();
1101 UpdateLocalCryptographer();
1102
1103 // Verify the item gets decrypted and sent back to the model thread.
1104 // TODO(maxbogue): crbug.com/529498: Uncomment when pending updates are
1105 // handled by the worker again.
1106 // ASSERT_TRUE(HasUpdateResponseOnModelThread("tag1"));
1107 }
1108
1109 // Test decryption of pending updates saved across a restart. This test
1110 // differs from the previous one in that the restored updates can be decrypted
1111 // immediately after the CommitQueue is constructed.
1112 TEST_F(ModelTypeWorkerTest, RestoreApplicableEntries) {
1113 // Update the cryptographer so it can decrypt that update.
1114 NewForeignEncryptionKey();
1115 UpdateLocalCryptographer();
1116
1117 // Create a fake pending update.
1118 EntityData entity;
1119 entity.client_tag_hash = GenerateTagHash("tag1");
1120 entity.id = "SomeID";
1121 entity.creation_time =
1122 base::Time::UnixEpoch() + base::TimeDelta::FromSeconds(10);
1123 entity.modification_time =
1124 base::Time::UnixEpoch() + base::TimeDelta::FromSeconds(11);
1125 entity.non_unique_name = "encrypted";
1126
1127 entity.specifics = GenerateSpecifics("tag1", "value1");
1128 EncryptUpdate(GetNthKeyParams(1), &(entity.specifics));
1129
1130 UpdateResponseData update;
1131 update.entity = entity.PassToPtr();
1132 update.response_version = 100;
1133
1134 // Inject the update during CommitQueue initialization.
1135 UpdateResponseDataList saved_pending_updates;
1136 saved_pending_updates.push_back(update);
1137 InitializeWithPendingUpdates(saved_pending_updates);
1138
1139 // Verify the item gets decrypted and sent back to the model thread.
1140 // TODO(maxbogue): crbug.com/529498: Uncomment when pending updates are
1141 // handled by the worker again.
1142 // ASSERT_TRUE(HasUpdateResponseOnModelThread("tag1"));
1143 }
1144
1145 // Test that undecryptable updates provide sufficient reason to not commit.
1146 //
1147 // This should be rare in practice. Usually the cryptographer will be in an
1148 // unusable state when we receive undecryptable updates, and that alone will be
1149 // enough to prevent all commits.
1150 TEST_F(ModelTypeWorkerTest, CommitBlockedByPending) {
1151 NormalInitialize();
1152
1153 // Prepeare to commit an item.
1154 CommitRequest("tag1", "value1");
1155 EXPECT_TRUE(WillCommit());
1156
1157 // Receive an encrypted update for that item.
1158 SetUpdateEncryptionFilter(1);
1159 TriggerUpdateFromServer(10, "tag1", "value1");
1160
1161 // The pending commit state should be cleared.
1162 EXPECT_FALSE(WillCommit());
1163
1164 // The pending update will be delivered to the model thread.
1165 HasUpdateResponseOnModelThread("tag1");
1166
1167 // Pretend the update arrived too late to prevent another commit request.
1168 CommitRequest("tag1", "value2");
1169
1170 EXPECT_FALSE(WillCommit());
1171 }
1172
1173 // Verify that corrupted encrypted updates don't cause crashes.
1174 TEST_F(ModelTypeWorkerTest, ReceiveCorruptEncryption) {
1175 // Initialize the worker with basic encryption state.
1176 NormalInitialize();
1177 NewForeignEncryptionKey();
1178 UpdateLocalCryptographer();
1179
1180 // Manually create an update.
1181 sync_pb::SyncEntity entity;
1182 entity.set_client_defined_unique_tag(GenerateTagHash("tag1"));
1183 entity.set_id_string("SomeID");
1184 entity.set_version(1);
1185 entity.set_ctime(1000);
1186 entity.set_mtime(1001);
1187 entity.set_name("encrypted");
1188 entity.set_deleted(false);
1189
1190 // Encrypt it.
1191 entity.mutable_specifics()->CopyFrom(GenerateSpecifics("tag1", "value1"));
1192 EncryptUpdate(GetNthKeyParams(1), entity.mutable_specifics());
1193
1194 // Replace a few bytes to corrupt it.
1195 entity.mutable_specifics()->mutable_encrypted()->mutable_blob()->replace(
1196 0, 4, "xyz!");
1197
1198 SyncEntityList entity_list;
1199 entity_list.push_back(&entity);
1200
1201 // If a corrupt update could trigger a crash, this is where it would happen.
1202 DeliverRawUpdates(entity_list);
1203
1204 EXPECT_FALSE(HasUpdateResponseOnModelThread("tag1"));
1205
1206 // Deliver a non-corrupt update to see if the everything still works.
1207 SetUpdateEncryptionFilter(1);
1208 TriggerUpdateFromServer(10, "tag1", "value1");
1209 EXPECT_TRUE(HasUpdateResponseOnModelThread("tag1"));
1210 }
1211
1212 // Test that processor has been disconnected from Sync when worker got
1213 // disconnected.
1214 TEST_F(ModelTypeWorkerTest, DisconnectProcessorFromSyncTest) {
1215 // Initialize the worker with basic state.
1216 NormalInitialize();
1217 EXPECT_FALSE(IsProcessorDisconnected());
1218 ResetWorker();
1219 EXPECT_TRUE(IsProcessorDisconnected());
1220 }
1221
1222 } // namespace syncer_v2
OLDNEW
« no previous file with comments | « sync/engine/model_type_worker.cc ('k') | sync/engine/net/DEPS » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698