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

Side by Side Diff: sync/internal_api/shared_model_type_processor_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/internal_api/shared_model_type_processor.cc ('k') | sync/internal_api/sync_db_util.cc » ('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/internal_api/public/shared_model_type_processor.h"
6
7 #include <stddef.h>
8 #include <stdint.h>
9
10 #include <map>
11 #include <vector>
12
13 #include "base/bind.h"
14 #include "base/callback.h"
15 #include "base/memory/ptr_util.h"
16 #include "base/message_loop/message_loop.h"
17 #include "base/run_loop.h"
18 #include "sync/api/fake_model_type_service.h"
19 #include "sync/engine/commit_queue.h"
20 #include "sync/internal_api/public/activation_context.h"
21 #include "sync/internal_api/public/base/model_type.h"
22 #include "sync/internal_api/public/data_batch_impl.h"
23 #include "sync/internal_api/public/non_blocking_sync_common.h"
24 #include "sync/internal_api/public/simple_metadata_change_list.h"
25 #include "sync/internal_api/public/test/data_type_error_handler_mock.h"
26 #include "sync/protocol/data_type_state.pb.h"
27 #include "sync/protocol/sync.pb.h"
28 #include "sync/syncable/syncable_util.h"
29 #include "sync/test/engine/mock_model_type_worker.h"
30 #include "sync/util/time.h"
31 #include "testing/gtest/include/gtest/gtest.h"
32
33 namespace syncer_v2 {
34
35 namespace {
36
37 const std::string kTag1 = "tag1";
38 const std::string kTag2 = "tag2";
39 const std::string kTag3 = "tag3";
40 const std::string kValue1 = "value1";
41 const std::string kValue2 = "value2";
42 const std::string kValue3 = "value3";
43
44 std::string GenerateTagHash(const std::string& tag) {
45 return syncer::syncable::GenerateSyncableHash(syncer::PREFERENCES, tag);
46 }
47
48 sync_pb::EntitySpecifics GenerateSpecifics(const std::string& tag,
49 const std::string& value) {
50 sync_pb::EntitySpecifics specifics;
51 specifics.mutable_preference()->set_name(tag);
52 specifics.mutable_preference()->set_value(value);
53 return specifics;
54 }
55
56 std::unique_ptr<EntityData> GenerateEntityData(const std::string& tag,
57 const std::string& value) {
58 std::unique_ptr<EntityData> entity_data = base::WrapUnique(new EntityData());
59 entity_data->client_tag_hash = GenerateTagHash(tag);
60 entity_data->specifics = GenerateSpecifics(tag, value);
61 entity_data->non_unique_name = tag;
62 return entity_data;
63 }
64
65 // It is intentionally very difficult to copy an EntityData, as in normal code
66 // we never want to. However, since we store the data as an EntityData for the
67 // test code here, this function is needed to manually copy it.
68 std::unique_ptr<EntityData> CopyEntityData(const EntityData& old_data) {
69 std::unique_ptr<EntityData> new_data(new EntityData());
70 new_data->id = old_data.id;
71 new_data->client_tag_hash = old_data.client_tag_hash;
72 new_data->non_unique_name = old_data.non_unique_name;
73 new_data->specifics = old_data.specifics;
74 new_data->creation_time = old_data.creation_time;
75 new_data->modification_time = old_data.modification_time;
76 return new_data;
77 }
78
79 syncer::SyncError CreateSyncError(syncer::SyncError::ErrorType error_type) {
80 return syncer::SyncError(FROM_HERE, error_type, "TestError",
81 syncer::PREFERENCES);
82 }
83
84 // A basic in-memory storage mechanism for data and metadata. This makes it
85 // easier to test more complex behaviors involving when entities are written,
86 // committed, etc. Having a separate class helps keep the main one cleaner.
87 class SimpleStore {
88 public:
89 void PutData(const std::string& tag, const EntityData& data) {
90 data_change_count_++;
91 data_store_[tag] = CopyEntityData(data);
92 }
93
94 void PutMetadata(const std::string& tag,
95 const sync_pb::EntityMetadata& metadata) {
96 metadata_change_count_++;
97 metadata_store_[tag] = metadata;
98 }
99
100 void RemoveData(const std::string& tag) {
101 data_change_count_++;
102 data_store_.erase(tag);
103 }
104
105 void RemoveMetadata(const std::string& tag) {
106 metadata_change_count_++;
107 metadata_store_.erase(tag);
108 }
109
110 bool HasData(const std::string& tag) const {
111 return data_store_.find(tag) != data_store_.end();
112 }
113
114 bool HasMetadata(const std::string& tag) const {
115 return metadata_store_.find(tag) != metadata_store_.end();
116 }
117
118 const std::map<std::string, std::unique_ptr<EntityData>>& GetAllData() const {
119 return data_store_;
120 }
121
122 const EntityData& GetData(const std::string& tag) const {
123 return *data_store_.find(tag)->second;
124 }
125
126 const std::string& GetValue(const std::string& tag) const {
127 return GetData(tag).specifics.preference().value();
128 }
129
130 const sync_pb::EntityMetadata& GetMetadata(const std::string& tag) const {
131 return metadata_store_.find(tag)->second;
132 }
133
134 size_t DataCount() const { return data_store_.size(); }
135 size_t MetadataCount() const { return metadata_store_.size(); }
136
137 size_t DataChangeCount() const { return data_change_count_; }
138 size_t MetadataChangeCount() const { return metadata_change_count_; }
139
140 const sync_pb::DataTypeState& data_type_state() const {
141 return data_type_state_;
142 }
143
144 void set_data_type_state(const sync_pb::DataTypeState& data_type_state) {
145 data_type_state_ = data_type_state;
146 }
147
148 std::unique_ptr<MetadataBatch> CreateMetadataBatch() const {
149 std::unique_ptr<MetadataBatch> metadata_batch(new MetadataBatch());
150 metadata_batch->SetDataTypeState(data_type_state_);
151 for (auto it = metadata_store_.begin(); it != metadata_store_.end(); it++) {
152 metadata_batch->AddMetadata(it->first, it->second);
153 }
154 return metadata_batch;
155 }
156
157 void Reset() {
158 data_change_count_ = 0;
159 metadata_change_count_ = 0;
160 data_store_.clear();
161 metadata_store_.clear();
162 data_type_state_.Clear();
163 }
164
165 private:
166 size_t data_change_count_ = 0;
167 size_t metadata_change_count_ = 0;
168 std::map<std::string, std::unique_ptr<EntityData>> data_store_;
169 std::map<std::string, sync_pb::EntityMetadata> metadata_store_;
170 sync_pb::DataTypeState data_type_state_;
171 };
172
173 } // namespace
174
175 // Tests the various functionality of SharedModelTypeProcessor.
176 //
177 // The processor sits between the service (implemented by this test class) and
178 // the worker, which is represented by a MockModelTypeWorker. This test suite
179 // exercises the initialization flows (whether initial sync is done, performing
180 // the initial merge, etc) as well as normal functionality:
181 //
182 // - Initialization before the initial sync and merge correctly performs a merge
183 // and initializes the metadata in storage.
184 // - Initialization after the initial sync correctly loads metadata and queues
185 // any pending commits.
186 // - Put and Delete calls from the service result in the correct metadata in
187 // storage and the correct commit requests on the worker side.
188 // - Updates and commit responses from the worker correctly affect data and
189 // metadata in storage on the service side.
190 class SharedModelTypeProcessorTest : public ::testing::Test,
191 public FakeModelTypeService {
192 public:
193 SharedModelTypeProcessorTest()
194 : FakeModelTypeService(
195 base::Bind(&SharedModelTypeProcessor::CreateAsChangeProcessor)) {}
196
197 ~SharedModelTypeProcessorTest() override { CheckPostConditions(); }
198
199 void InitializeToMetadataLoaded() {
200 CreateChangeProcessor();
201 sync_pb::DataTypeState data_type_state(db_.data_type_state());
202 data_type_state.set_initial_sync_done(true);
203 db_.set_data_type_state(data_type_state);
204 OnMetadataLoaded();
205 }
206
207 // Initialize to a "ready-to-commit" state.
208 void InitializeToReadyState() {
209 InitializeToMetadataLoaded();
210 if (!data_callback_.is_null()) {
211 OnPendingCommitDataLoaded();
212 }
213 OnSyncStarting();
214 }
215
216 void OnMetadataLoaded() {
217 type_processor()->OnMetadataLoaded(syncer::SyncError(),
218 db_.CreateMetadataBatch());
219 }
220
221 void OnPendingCommitDataLoaded() {
222 DCHECK(!data_callback_.is_null());
223 data_callback_.Run();
224 data_callback_.Reset();
225 }
226
227 void OnSyncStarting() {
228 type_processor()->OnSyncStarting(
229 &error_handler_,
230 base::Bind(&SharedModelTypeProcessorTest::OnReadyToConnect,
231 base::Unretained(this)));
232 }
233
234 void DisconnectSync() {
235 type_processor()->DisconnectSync();
236 worker_ = nullptr;
237 }
238
239 // Local data modification. Emulates signals from the model thread.
240 void WriteItem(const std::string& tag, const std::string& value) {
241 WriteItem(tag, GenerateEntityData(tag, value));
242 }
243
244 // Overloaded form to allow passing of custom entity data.
245 void WriteItem(const std::string& tag,
246 std::unique_ptr<EntityData> entity_data) {
247 db_.PutData(tag, *entity_data);
248 if (type_processor()) {
249 std::unique_ptr<MetadataChangeList> change_list(
250 new SimpleMetadataChangeList());
251 type_processor()->Put(tag, std::move(entity_data), change_list.get());
252 ApplyMetadataChangeList(std::move(change_list));
253 }
254 }
255
256 // Writes data for |tag| and simulates a commit response for it.
257 void WriteItemAndAck(const std::string& tag, const std::string& value) {
258 WriteItem(tag, value);
259 worker()->ExpectPendingCommits({tag});
260 worker()->AckOnePendingCommit();
261 EXPECT_EQ(0U, worker()->GetNumPendingCommits());
262 }
263
264 void DeleteItem(const std::string& tag) {
265 db_.RemoveData(tag);
266 if (type_processor()) {
267 std::unique_ptr<MetadataChangeList> change_list(
268 new SimpleMetadataChangeList());
269 type_processor()->Delete(tag, change_list.get());
270 ApplyMetadataChangeList(std::move(change_list));
271 }
272 }
273
274 void ResetState() {
275 clear_change_processor();
276 db_.Reset();
277 worker_ = nullptr;
278 CheckPostConditions();
279 }
280
281 // Wipes existing DB and simulates a pending update of a server-known item.
282 void ResetStateWriteItem(const std::string& tag, const std::string& value) {
283 ResetState();
284 InitializeToReadyState();
285 EXPECT_EQ(0U, ProcessorEntityCount());
286 WriteItemAndAck(tag, "acked-value");
287 WriteItem(tag, value);
288 EXPECT_EQ(1U, ProcessorEntityCount());
289 clear_change_processor();
290 worker_ = nullptr;
291 }
292
293 // Wipes existing DB and simulates a pending deletion of a server-known item.
294 void ResetStateDeleteItem(const std::string& tag, const std::string& value) {
295 ResetState();
296 InitializeToReadyState();
297 EXPECT_EQ(0U, ProcessorEntityCount());
298 WriteItemAndAck(tag, value);
299 EXPECT_EQ(1U, ProcessorEntityCount());
300 DeleteItem(tag);
301 EXPECT_EQ(1U, ProcessorEntityCount());
302 clear_change_processor();
303 worker_ = nullptr;
304 }
305
306 // Simulates an initial GetUpdates response from the worker with |updates|.
307 void OnInitialSyncDone(UpdateResponseDataList updates) {
308 sync_pb::DataTypeState data_type_state(db_.data_type_state());
309 data_type_state.set_initial_sync_done(true);
310 type_processor()->OnUpdateReceived(data_type_state, updates);
311 }
312
313 // Overloaded form with no updates.
314 void OnInitialSyncDone() { OnInitialSyncDone(UpdateResponseDataList()); }
315
316 // Overloaded form that constructs an update for a single entity.
317 void OnInitialSyncDone(const std::string& tag, const std::string& value) {
318 UpdateResponseDataList updates;
319 UpdateResponseData update;
320 update.entity = GenerateEntityData(tag, value)->PassToPtr();
321 updates.push_back(update);
322 OnInitialSyncDone(updates);
323 }
324
325 // Return the number of entities the processor has metadata for.
326 size_t ProcessorEntityCount() const {
327 DCHECK(type_processor());
328 return type_processor()->entities_.size();
329 }
330
331 // Store a resolution for the next call to ResolveConflict. Note that if this
332 // is a USE_NEW resolution, the data will only exist for one resolve call.
333 void SetConflictResolution(ConflictResolution resolution) {
334 conflict_resolution_.reset(new ConflictResolution(std::move(resolution)));
335 }
336
337 // Sets the error that the next fallible call to the service will generate.
338 void SetServiceError(syncer::SyncError::ErrorType error_type) {
339 DCHECK(!service_error_.IsSet());
340 service_error_ = CreateSyncError(error_type);
341 }
342
343 // Sets the error type that OnReadyToConnect (our StartCallback) expects to
344 // receive.
345 void ExpectStartError(syncer::SyncError::ErrorType error_type) {
346 DCHECK(expected_start_error_ == syncer::SyncError::UNSET);
347 expected_start_error_ = error_type;
348 }
349
350 const SimpleStore& db() const { return db_; }
351
352 MockModelTypeWorker* worker() { return worker_; }
353
354 SharedModelTypeProcessor* type_processor() const {
355 return static_cast<SharedModelTypeProcessor*>(change_processor());
356 }
357
358 syncer::DataTypeErrorHandlerMock* error_handler() { return &error_handler_; }
359
360 private:
361 void CheckPostConditions() {
362 DCHECK(data_callback_.is_null());
363 DCHECK(!service_error_.IsSet());
364 DCHECK_EQ(syncer::SyncError::UNSET, expected_start_error_);
365 }
366
367 void OnReadyToConnect(syncer::SyncError error,
368 std::unique_ptr<ActivationContext> context) {
369 if (expected_start_error_ != syncer::SyncError::UNSET) {
370 EXPECT_TRUE(error.IsSet());
371 EXPECT_EQ(expected_start_error_, error.error_type());
372 EXPECT_EQ(nullptr, context);
373 expected_start_error_ = syncer::SyncError::UNSET;
374 return;
375 }
376
377 std::unique_ptr<MockModelTypeWorker> worker(
378 new MockModelTypeWorker(context->data_type_state, type_processor()));
379 // Keep an unsafe pointer to the commit queue the processor will use.
380 worker_ = worker.get();
381 // The context contains a proxy to the processor, but this call is
382 // side-stepping that completely and connecting directly to the real
383 // processor, since these tests are single-threaded and don't need proxies.
384 type_processor()->ConnectSync(std::move(worker));
385 }
386
387 // FakeModelTypeService overrides.
388
389 std::string GetClientTag(const EntityData& entity_data) override {
390 // The tag is the preference name - see GenerateSpecifics.
391 return entity_data.specifics.preference().name();
392 }
393
394 std::unique_ptr<MetadataChangeList> CreateMetadataChangeList() override {
395 return std::unique_ptr<MetadataChangeList>(new SimpleMetadataChangeList());
396 }
397
398 syncer::SyncError MergeSyncData(
399 std::unique_ptr<MetadataChangeList> metadata_changes,
400 EntityDataMap data_map) override {
401 if (service_error_.IsSet()) {
402 syncer::SyncError error = service_error_;
403 service_error_ = syncer::SyncError();
404 return error;
405 }
406 // Commit any local entities that aren't being overwritten by the server.
407 const auto& local_data = db_.GetAllData();
408 for (auto it = local_data.begin(); it != local_data.end(); it++) {
409 if (data_map.find(it->first) == data_map.end()) {
410 type_processor()->Put(it->first, CopyEntityData(*it->second),
411 metadata_changes.get());
412 }
413 }
414 // Store any new remote entities.
415 for (auto it = data_map.begin(); it != data_map.end(); it++) {
416 db_.PutData(it->first, it->second.value());
417 }
418 ApplyMetadataChangeList(std::move(metadata_changes));
419 return syncer::SyncError();
420 }
421
422 syncer::SyncError ApplySyncChanges(
423 std::unique_ptr<MetadataChangeList> metadata_changes,
424 EntityChangeList entity_changes) override {
425 if (service_error_.IsSet()) {
426 syncer::SyncError error = service_error_;
427 service_error_ = syncer::SyncError();
428 return error;
429 }
430 for (const EntityChange& change : entity_changes) {
431 switch (change.type()) {
432 case EntityChange::ACTION_ADD:
433 EXPECT_FALSE(db_.HasData(change.client_tag()));
434 db_.PutData(change.client_tag(), change.data());
435 break;
436 case EntityChange::ACTION_UPDATE:
437 EXPECT_TRUE(db_.HasData(change.client_tag()));
438 db_.PutData(change.client_tag(), change.data());
439 break;
440 case EntityChange::ACTION_DELETE:
441 EXPECT_TRUE(db_.HasData(change.client_tag()));
442 db_.RemoveData(change.client_tag());
443 break;
444 }
445 }
446 ApplyMetadataChangeList(std::move(metadata_changes));
447 return syncer::SyncError();
448 }
449
450 void ApplyMetadataChangeList(
451 std::unique_ptr<MetadataChangeList> change_list) {
452 DCHECK(change_list);
453 SimpleMetadataChangeList* changes =
454 static_cast<SimpleMetadataChangeList*>(change_list.get());
455 const auto& metadata_changes = changes->GetMetadataChanges();
456 for (auto it = metadata_changes.begin(); it != metadata_changes.end();
457 it++) {
458 switch (it->second.type) {
459 case SimpleMetadataChangeList::UPDATE:
460 db_.PutMetadata(it->first, it->second.metadata);
461 break;
462 case SimpleMetadataChangeList::CLEAR:
463 EXPECT_TRUE(db_.HasMetadata(it->first));
464 db_.RemoveMetadata(it->first);
465 break;
466 }
467 }
468 if (changes->HasDataTypeStateChange()) {
469 const SimpleMetadataChangeList::DataTypeStateChange& state_change =
470 changes->GetDataTypeStateChange();
471 switch (state_change.type) {
472 case SimpleMetadataChangeList::UPDATE:
473 db_.set_data_type_state(state_change.state);
474 break;
475 case SimpleMetadataChangeList::CLEAR:
476 db_.set_data_type_state(sync_pb::DataTypeState());
477 break;
478 }
479 }
480 }
481
482 void GetData(ClientTagList tags, DataCallback callback) override {
483 if (service_error_.IsSet()) {
484 data_callback_ = base::Bind(callback, service_error_, nullptr);
485 service_error_ = syncer::SyncError();
486 return;
487 }
488 std::unique_ptr<DataBatchImpl> batch(new DataBatchImpl());
489 for (const std::string& tag : tags) {
490 DCHECK(db_.HasData(tag)) << "No data for " << tag;
491 batch->Put(tag, CopyEntityData(db_.GetData(tag)));
492 }
493 data_callback_ =
494 base::Bind(callback, syncer::SyncError(), base::Passed(&batch));
495 }
496
497 ConflictResolution ResolveConflict(
498 const EntityData& local_data,
499 const EntityData& remote_data) const override {
500 DCHECK(conflict_resolution_);
501 return std::move(*conflict_resolution_);
502 }
503
504 std::unique_ptr<ConflictResolution> conflict_resolution_;
505
506 // This sets ThreadTaskRunnerHandle on the current thread, which the type
507 // processor will pick up as the sync task runner.
508 base::MessageLoop sync_loop_;
509
510 // The current mock queue, which is owned by |type_processor()|.
511 MockModelTypeWorker* worker_;
512
513 // Stores the data callback between GetData() and OnPendingCommitDataLoaded().
514 base::Closure data_callback_;
515
516 // Contains all of the data and metadata state for these tests.
517 SimpleStore db_;
518
519 // The processor's error handler.
520 syncer::DataTypeErrorHandlerMock error_handler_;
521
522 // The error to produce on the next service call.
523 syncer::SyncError service_error_;
524
525 // The error to expect in OnReadyToConnect().
526 syncer::SyncError::ErrorType expected_start_error_ = syncer::SyncError::UNSET;
527 };
528
529 // Test that an initial sync handles local and remote items properly.
530 TEST_F(SharedModelTypeProcessorTest, InitialSync) {
531 CreateChangeProcessor();
532 OnMetadataLoaded();
533 OnSyncStarting();
534
535 // Local write before initial sync.
536 WriteItem(kTag1, kValue1);
537
538 // Has data, but no metadata, entity in the processor, or commit request.
539 EXPECT_EQ(1U, db().DataCount());
540 EXPECT_EQ(0U, db().MetadataCount());
541 EXPECT_EQ(0U, ProcessorEntityCount());
542 EXPECT_EQ(0U, worker()->GetNumPendingCommits());
543
544 // Initial sync with one server item.
545 OnInitialSyncDone(kTag2, kValue2);
546
547 // Now have data and metadata for both items, as well as a commit request for
548 // the local item.
549 EXPECT_EQ(2U, db().DataCount());
550 EXPECT_EQ(2U, db().MetadataCount());
551 EXPECT_EQ(2U, ProcessorEntityCount());
552 EXPECT_EQ(1, db().GetMetadata(kTag1).sequence_number());
553 EXPECT_EQ(0, db().GetMetadata(kTag2).sequence_number());
554 worker()->ExpectPendingCommits({kTag1});
555 }
556
557 // Test that an error during the merge is propagated to the error handler.
558 TEST_F(SharedModelTypeProcessorTest, InitialSyncError) {
559 CreateChangeProcessor();
560 OnMetadataLoaded();
561 OnSyncStarting();
562
563 SetServiceError(syncer::SyncError::DATATYPE_ERROR);
564 error_handler()->ExpectError(syncer::SyncError::DATATYPE_ERROR);
565 OnInitialSyncDone();
566 }
567
568 // Test that errors before it's called are passed to |start_callback| correctly.
569 TEST_F(SharedModelTypeProcessorTest, StartErrors) {
570 CreateChangeProcessor();
571 type_processor()->OnMetadataLoaded(
572 CreateSyncError(syncer::SyncError::DATATYPE_ERROR), nullptr);
573 ExpectStartError(syncer::SyncError::DATATYPE_ERROR);
574 OnSyncStarting();
575
576 // Test OnSyncStarting happening first.
577 ResetState();
578 CreateChangeProcessor();
579 OnSyncStarting();
580 ExpectStartError(syncer::SyncError::DATATYPE_ERROR);
581 type_processor()->OnMetadataLoaded(
582 CreateSyncError(syncer::SyncError::DATATYPE_ERROR), nullptr);
583
584 // Test an error loading pending data.
585 ResetStateWriteItem(kTag1, kValue1);
586 SetServiceError(syncer::SyncError::DATATYPE_ERROR);
587 InitializeToMetadataLoaded();
588 OnPendingCommitDataLoaded();
589 ExpectStartError(syncer::SyncError::DATATYPE_ERROR);
590 OnSyncStarting();
591 }
592
593 // This test covers race conditions during loading pending data. All cases
594 // start with no processor and one acked (committed to the server) item with a
595 // pending commit. There are three different events that can occur in any order
596 // once metadata is loaded:
597 //
598 // - Pending commit data is loaded.
599 // - Sync gets connected.
600 // - Optionally, a put or delete happens to the item.
601 //
602 // This results in 2 + 12 = 14 orderings of the events.
603 TEST_F(SharedModelTypeProcessorTest, LoadPendingCommit) {
604 // Data, connect.
605 ResetStateWriteItem(kTag1, kValue1);
606 InitializeToMetadataLoaded();
607 OnPendingCommitDataLoaded();
608 OnSyncStarting();
609 EXPECT_EQ(1U, worker()->GetNumPendingCommits());
610 worker()->ExpectNthPendingCommit(0, kTag1, kValue1);
611
612 // Connect, data.
613 ResetStateWriteItem(kTag1, kValue1);
614 InitializeToMetadataLoaded();
615 OnSyncStarting();
616 EXPECT_EQ(nullptr, worker());
617 OnPendingCommitDataLoaded();
618 EXPECT_EQ(1U, worker()->GetNumPendingCommits());
619 worker()->ExpectNthPendingCommit(0, kTag1, kValue1);
620
621 // Data, connect, put.
622 ResetStateWriteItem(kTag1, kValue1);
623 InitializeToMetadataLoaded();
624 OnPendingCommitDataLoaded();
625 OnSyncStarting();
626 WriteItem(kTag1, kValue2);
627 EXPECT_EQ(2U, worker()->GetNumPendingCommits());
628 worker()->ExpectNthPendingCommit(0, kTag1, kValue1);
629 worker()->ExpectNthPendingCommit(1, kTag1, kValue2);
630
631 // Data, put, connect.
632 ResetStateWriteItem(kTag1, kValue1);
633 InitializeToMetadataLoaded();
634 OnPendingCommitDataLoaded();
635 WriteItem(kTag1, kValue2);
636 OnSyncStarting();
637 EXPECT_EQ(1U, worker()->GetNumPendingCommits());
638 worker()->ExpectNthPendingCommit(0, kTag1, kValue2);
639
640 // Connect, data, put.
641 ResetStateWriteItem(kTag1, kValue1);
642 InitializeToMetadataLoaded();
643 OnSyncStarting();
644 OnPendingCommitDataLoaded();
645 WriteItem(kTag1, kValue2);
646 EXPECT_EQ(2U, worker()->GetNumPendingCommits());
647 worker()->ExpectNthPendingCommit(0, kTag1, kValue1);
648 worker()->ExpectNthPendingCommit(1, kTag1, kValue2);
649
650 // Connect, put, data.
651 ResetStateWriteItem(kTag1, kValue1);
652 InitializeToMetadataLoaded();
653 OnSyncStarting();
654 WriteItem(kTag1, kValue2);
655 EXPECT_EQ(nullptr, worker());
656 OnPendingCommitDataLoaded();
657 EXPECT_EQ(1U, worker()->GetNumPendingCommits());
658 worker()->ExpectNthPendingCommit(0, kTag1, kValue2);
659
660 // Put, data, connect.
661 ResetStateWriteItem(kTag1, kValue1);
662 InitializeToMetadataLoaded();
663 WriteItem(kTag1, kValue2);
664 OnPendingCommitDataLoaded();
665 OnSyncStarting();
666 EXPECT_EQ(1U, worker()->GetNumPendingCommits());
667 worker()->ExpectNthPendingCommit(0, kTag1, kValue2);
668
669 // Put, connect, data.
670 ResetStateWriteItem(kTag1, kValue1);
671 InitializeToMetadataLoaded();
672 WriteItem(kTag1, kValue2);
673 OnSyncStarting();
674 EXPECT_EQ(nullptr, worker());
675 OnPendingCommitDataLoaded();
676 EXPECT_EQ(1U, worker()->GetNumPendingCommits());
677 worker()->ExpectNthPendingCommit(0, kTag1, kValue2);
678
679 // Data, connect, delete.
680 ResetStateWriteItem(kTag1, kValue1);
681 InitializeToMetadataLoaded();
682 OnPendingCommitDataLoaded();
683 OnSyncStarting();
684 DeleteItem(kTag1);
685 EXPECT_EQ(2U, worker()->GetNumPendingCommits());
686 worker()->ExpectNthPendingCommit(0, kTag1, kValue1);
687 worker()->ExpectNthPendingCommit(1, kTag1, "");
688
689 // Data, delete, connect.
690 ResetStateWriteItem(kTag1, kValue1);
691 InitializeToMetadataLoaded();
692 OnPendingCommitDataLoaded();
693 DeleteItem(kTag1);
694 OnSyncStarting();
695 EXPECT_EQ(1U, worker()->GetNumPendingCommits());
696 worker()->ExpectNthPendingCommit(0, kTag1, "");
697
698 // Connect, data, delete.
699 ResetStateWriteItem(kTag1, kValue1);
700 InitializeToMetadataLoaded();
701 OnSyncStarting();
702 OnPendingCommitDataLoaded();
703 DeleteItem(kTag1);
704 EXPECT_EQ(2U, worker()->GetNumPendingCommits());
705 worker()->ExpectNthPendingCommit(0, kTag1, kValue1);
706 worker()->ExpectNthPendingCommit(1, kTag1, "");
707
708 // Connect, delete, data.
709 ResetStateWriteItem(kTag1, kValue1);
710 InitializeToMetadataLoaded();
711 OnSyncStarting();
712 DeleteItem(kTag1);
713 EXPECT_EQ(nullptr, worker());
714 OnPendingCommitDataLoaded();
715 EXPECT_EQ(1U, worker()->GetNumPendingCommits());
716 worker()->ExpectNthPendingCommit(0, kTag1, "");
717
718 // Delete, data, connect.
719 ResetStateWriteItem(kTag1, kValue1);
720 InitializeToMetadataLoaded();
721 DeleteItem(kTag1);
722 OnPendingCommitDataLoaded();
723 OnSyncStarting();
724 EXPECT_EQ(1U, worker()->GetNumPendingCommits());
725 worker()->ExpectNthPendingCommit(0, kTag1, "");
726
727 // Delete, connect, data.
728 ResetStateWriteItem(kTag1, kValue1);
729 InitializeToMetadataLoaded();
730 DeleteItem(kTag1);
731 OnSyncStarting();
732 EXPECT_EQ(nullptr, worker());
733 OnPendingCommitDataLoaded();
734 EXPECT_EQ(1U, worker()->GetNumPendingCommits());
735 worker()->ExpectNthPendingCommit(0, kTag1, "");
736 }
737
738 // This test covers race conditions during loading a pending delete. All cases
739 // start with no processor and one item with a pending delete. There are two
740 // different events that can occur in any order once metadata is loaded, since
741 // for a deletion there is no data to load:
742 //
743 // - Sync gets connected.
744 // - Optionally, a put or delete happens to the item (repeated deletes should be
745 // handled properly).
746 //
747 // This results in 1 + 4 = 5 orderings of the events.
748 TEST_F(SharedModelTypeProcessorTest, LoadPendingDelete) {
749 // Connect.
750 ResetStateDeleteItem(kTag1, kValue1);
751 InitializeToMetadataLoaded();
752 OnSyncStarting();
753 EXPECT_EQ(1U, worker()->GetNumPendingCommits());
754 worker()->ExpectNthPendingCommit(0, kTag1, "");
755
756 // Connect, put.
757 ResetStateDeleteItem(kTag1, kValue1);
758 InitializeToMetadataLoaded();
759 OnSyncStarting();
760 EXPECT_EQ(1U, worker()->GetNumPendingCommits());
761 WriteItem(kTag1, kValue2);
762 EXPECT_EQ(2U, worker()->GetNumPendingCommits());
763 worker()->ExpectNthPendingCommit(0, kTag1, "");
764 worker()->ExpectNthPendingCommit(1, kTag1, kValue2);
765
766 // Put, connect.
767 ResetStateDeleteItem(kTag1, kValue1);
768 InitializeToMetadataLoaded();
769 WriteItem(kTag1, kValue2);
770 OnSyncStarting();
771 EXPECT_EQ(1U, worker()->GetNumPendingCommits());
772 worker()->ExpectNthPendingCommit(0, kTag1, kValue2);
773
774 // Connect, delete.
775 ResetStateDeleteItem(kTag1, kValue1);
776 InitializeToMetadataLoaded();
777 OnSyncStarting();
778 EXPECT_EQ(1U, worker()->GetNumPendingCommits());
779 DeleteItem(kTag1);
780 EXPECT_EQ(2U, worker()->GetNumPendingCommits());
781 worker()->ExpectNthPendingCommit(0, kTag1, "");
782 worker()->ExpectNthPendingCommit(1, kTag1, "");
783
784 // Delete, connect.
785 ResetStateDeleteItem(kTag1, kValue1);
786 InitializeToMetadataLoaded();
787 DeleteItem(kTag1);
788 OnSyncStarting();
789 EXPECT_EQ(1U, worker()->GetNumPendingCommits());
790 worker()->ExpectNthPendingCommit(0, kTag1, "");
791 }
792
793 // Test that loading a committed item does not queue another commit.
794 TEST_F(SharedModelTypeProcessorTest, LoadCommited) {
795 InitializeToReadyState();
796 WriteItemAndAck(kTag1, kValue1);
797 clear_change_processor();
798
799 // Test that a new processor loads the metadata without committing.
800 InitializeToReadyState();
801 EXPECT_EQ(1U, ProcessorEntityCount());
802 EXPECT_EQ(0U, worker()->GetNumPendingCommits());
803 }
804
805 // Creates a new item locally.
806 // Thoroughly tests the data generated by a local item creation.
807 TEST_F(SharedModelTypeProcessorTest, LocalCreateItem) {
808 InitializeToReadyState();
809 EXPECT_EQ(0U, worker()->GetNumPendingCommits());
810
811 WriteItem(kTag1, kValue1);
812
813 // Verify the commit request this operation has triggered.
814 worker()->ExpectPendingCommits({kTag1});
815 const CommitRequestData& tag1_request_data =
816 worker()->GetLatestPendingCommitForTag(kTag1);
817 const EntityData& tag1_data = tag1_request_data.entity.value();
818
819 EXPECT_EQ(kUncommittedVersion, tag1_request_data.base_version);
820 EXPECT_TRUE(tag1_data.id.empty());
821 EXPECT_FALSE(tag1_data.creation_time.is_null());
822 EXPECT_FALSE(tag1_data.modification_time.is_null());
823 EXPECT_EQ(kTag1, tag1_data.non_unique_name);
824 EXPECT_FALSE(tag1_data.is_deleted());
825 EXPECT_EQ(kTag1, tag1_data.specifics.preference().name());
826 EXPECT_EQ(kValue1, tag1_data.specifics.preference().value());
827
828 EXPECT_EQ(1U, db().MetadataCount());
829 const sync_pb::EntityMetadata metadata = db().GetMetadata(kTag1);
830 EXPECT_TRUE(metadata.has_client_tag_hash());
831 EXPECT_FALSE(metadata.has_server_id());
832 EXPECT_FALSE(metadata.is_deleted());
833 EXPECT_EQ(1, metadata.sequence_number());
834 EXPECT_EQ(0, metadata.acked_sequence_number());
835 EXPECT_EQ(kUncommittedVersion, metadata.server_version());
836 EXPECT_TRUE(metadata.has_creation_time());
837 EXPECT_TRUE(metadata.has_modification_time());
838 EXPECT_TRUE(metadata.has_specifics_hash());
839
840 worker()->AckOnePendingCommit();
841 EXPECT_EQ(1U, db().MetadataCount());
842 const sync_pb::EntityMetadata acked_metadata = db().GetMetadata(kTag1);
843 EXPECT_TRUE(acked_metadata.has_server_id());
844 EXPECT_EQ(1, acked_metadata.sequence_number());
845 EXPECT_EQ(1, acked_metadata.acked_sequence_number());
846 EXPECT_EQ(1, acked_metadata.server_version());
847 }
848
849 // Test that an error applying metadata changes from a commit response is
850 // propagated to the error handler.
851 TEST_F(SharedModelTypeProcessorTest, ErrorApplyingAck) {
852 InitializeToReadyState();
853 WriteItem(kTag1, kValue1);
854 SetServiceError(syncer::SyncError::DATATYPE_ERROR);
855 error_handler()->ExpectError(syncer::SyncError::DATATYPE_ERROR);
856 worker()->AckOnePendingCommit();
857 }
858
859 // The purpose of this test case is to test setting |client_tag_hash| and |id|
860 // on the EntityData object as we pass it into the Put method of the processor.
861 TEST_F(SharedModelTypeProcessorTest, LocalUpdateItemWithOverrides) {
862 const std::string kId1 = "cid1";
863 const std::string kId2 = "cid2";
864 const std::string kName1 = "name1";
865 const std::string kName2 = "name2";
866 const std::string kTag3Hash = GenerateTagHash(kTag3);
867
868 InitializeToReadyState();
869 EXPECT_EQ(0U, worker()->GetNumPendingCommits());
870
871 std::unique_ptr<EntityData> entity_data = base::WrapUnique(new EntityData());
872 entity_data->specifics.mutable_preference()->set_name(kName1);
873 entity_data->specifics.mutable_preference()->set_value(kValue1);
874
875 entity_data->non_unique_name = kName1;
876 entity_data->client_tag_hash = kTag3Hash;
877 entity_data->id = kId1;
878 WriteItem(kTag1, std::move(entity_data));
879
880 EXPECT_EQ(1U, worker()->GetNumPendingCommits());
881 ASSERT_FALSE(worker()->HasPendingCommitForTag(kTag3));
882 ASSERT_TRUE(worker()->HasPendingCommitForTag(kTag1));
883 EXPECT_EQ(1U, db().MetadataCount());
884 const EntityData& out_entity1 =
885 worker()->GetLatestPendingCommitForTag(kTag1).entity.value();
886 const sync_pb::EntityMetadata metadata_v1 = db().GetMetadata(kTag1);
887
888 EXPECT_EQ(kId1, out_entity1.id);
889 EXPECT_NE(kTag3Hash, out_entity1.client_tag_hash);
890 EXPECT_EQ(kValue1, out_entity1.specifics.preference().value());
891 EXPECT_EQ(kId1, metadata_v1.server_id());
892 EXPECT_EQ(metadata_v1.client_tag_hash(), out_entity1.client_tag_hash);
893
894 entity_data.reset(new EntityData());
895 entity_data->specifics.mutable_preference()->set_name(kName2);
896 entity_data->specifics.mutable_preference()->set_value(kValue2);
897 entity_data->non_unique_name = kName2;
898 entity_data->client_tag_hash = kTag3Hash;
899 // Make sure ID isn't overwritten either.
900 entity_data->id = kId2;
901 WriteItem(kTag1, std::move(entity_data));
902
903 EXPECT_EQ(2U, worker()->GetNumPendingCommits());
904 ASSERT_FALSE(worker()->HasPendingCommitForTag(kTag3));
905 ASSERT_TRUE(worker()->HasPendingCommitForTag(kTag1));
906 EXPECT_EQ(1U, db().MetadataCount());
907 const EntityData& out_entity2 =
908 worker()->GetLatestPendingCommitForTag(kTag1).entity.value();
909 const sync_pb::EntityMetadata metadata_v2 = db().GetMetadata(kTag1);
910
911 EXPECT_EQ(kValue2, out_entity2.specifics.preference().value());
912 // Should still see old cid1 value, override is not respected on update.
913 EXPECT_EQ(kId1, out_entity2.id);
914 EXPECT_EQ(kId1, metadata_v2.server_id());
915 EXPECT_EQ(metadata_v2.client_tag_hash(), out_entity2.client_tag_hash);
916
917 // Specifics have changed so the hashes should not match.
918 EXPECT_NE(metadata_v1.specifics_hash(), metadata_v2.specifics_hash());
919 }
920
921 // Creates a new local item then modifies it.
922 // Thoroughly tests data generated by modification of server-unknown item.
923 TEST_F(SharedModelTypeProcessorTest, LocalUpdateItem) {
924 InitializeToReadyState();
925
926 WriteItem(kTag1, kValue1);
927 EXPECT_EQ(1U, db().MetadataCount());
928 worker()->ExpectPendingCommits({kTag1});
929
930 const CommitRequestData& request_data_v1 =
931 worker()->GetLatestPendingCommitForTag(kTag1);
932 const EntityData& data_v1 = request_data_v1.entity.value();
933 const sync_pb::EntityMetadata metadata_v1 = db().GetMetadata(kTag1);
934
935 WriteItem(kTag1, kValue2);
936 EXPECT_EQ(1U, db().MetadataCount());
937 worker()->ExpectPendingCommits({kTag1, kTag1});
938
939 const CommitRequestData& request_data_v2 =
940 worker()->GetLatestPendingCommitForTag(kTag1);
941 const EntityData& data_v2 = request_data_v2.entity.value();
942 const sync_pb::EntityMetadata metadata_v2 = db().GetMetadata(kTag1);
943
944 // Test some of the relations between old and new commit requests.
945 EXPECT_GT(request_data_v2.sequence_number, request_data_v1.sequence_number);
946 EXPECT_EQ(data_v1.specifics.preference().value(), kValue1);
947
948 // Perform a thorough examination of the update-generated request.
949 EXPECT_EQ(kUncommittedVersion, request_data_v2.base_version);
950 EXPECT_TRUE(data_v2.id.empty());
951 EXPECT_FALSE(data_v2.creation_time.is_null());
952 EXPECT_FALSE(data_v2.modification_time.is_null());
953 EXPECT_EQ(kTag1, data_v2.non_unique_name);
954 EXPECT_FALSE(data_v2.is_deleted());
955 EXPECT_EQ(kTag1, data_v2.specifics.preference().name());
956 EXPECT_EQ(kValue2, data_v2.specifics.preference().value());
957
958 EXPECT_FALSE(metadata_v1.has_server_id());
959 EXPECT_FALSE(metadata_v1.is_deleted());
960 EXPECT_EQ(1, metadata_v1.sequence_number());
961 EXPECT_EQ(0, metadata_v1.acked_sequence_number());
962 EXPECT_EQ(kUncommittedVersion, metadata_v1.server_version());
963
964 EXPECT_FALSE(metadata_v2.has_server_id());
965 EXPECT_FALSE(metadata_v2.is_deleted());
966 EXPECT_EQ(2, metadata_v2.sequence_number());
967 EXPECT_EQ(0, metadata_v2.acked_sequence_number());
968 EXPECT_EQ(kUncommittedVersion, metadata_v2.server_version());
969
970 EXPECT_EQ(metadata_v1.client_tag_hash(), metadata_v2.client_tag_hash());
971 EXPECT_NE(metadata_v1.specifics_hash(), metadata_v2.specifics_hash());
972 }
973
974 // Tests that a local update that doesn't change specifics doesn't generate a
975 // commit request.
976 TEST_F(SharedModelTypeProcessorTest, LocalUpdateItemRedundant) {
977 InitializeToReadyState();
978 WriteItem(kTag1, kValue1);
979 EXPECT_EQ(1U, db().MetadataCount());
980 worker()->ExpectPendingCommits({kTag1});
981
982 WriteItem(kTag1, kValue1);
983 worker()->ExpectPendingCommits({kTag1});
984 }
985
986 // Thoroughly tests the data generated by a server item creation.
987 TEST_F(SharedModelTypeProcessorTest, ServerCreateItem) {
988 InitializeToReadyState();
989 worker()->UpdateFromServer(kTag1, kValue1);
990 EXPECT_EQ(1U, db().DataCount());
991 EXPECT_EQ(1U, db().MetadataCount());
992 EXPECT_EQ(1U, ProcessorEntityCount());
993 EXPECT_EQ(0U, worker()->GetNumPendingCommits());
994
995 const EntityData& data = db().GetData(kTag1);
996 EXPECT_FALSE(data.id.empty());
997 EXPECT_EQ(kTag1, data.specifics.preference().name());
998 EXPECT_EQ(kValue1, data.specifics.preference().value());
999 EXPECT_FALSE(data.creation_time.is_null());
1000 EXPECT_FALSE(data.modification_time.is_null());
1001 EXPECT_EQ(kTag1, data.non_unique_name);
1002 EXPECT_FALSE(data.is_deleted());
1003
1004 const sync_pb::EntityMetadata metadata = db().GetMetadata(kTag1);
1005 EXPECT_TRUE(metadata.has_client_tag_hash());
1006 EXPECT_TRUE(metadata.has_server_id());
1007 EXPECT_FALSE(metadata.is_deleted());
1008 EXPECT_EQ(0, metadata.sequence_number());
1009 EXPECT_EQ(0, metadata.acked_sequence_number());
1010 EXPECT_EQ(1, metadata.server_version());
1011 EXPECT_TRUE(metadata.has_creation_time());
1012 EXPECT_TRUE(metadata.has_modification_time());
1013 EXPECT_TRUE(metadata.has_specifics_hash());
1014 }
1015
1016 // Test that an error applying changes from a server update is
1017 // propagated to the error handler.
1018 TEST_F(SharedModelTypeProcessorTest, ErrorApplyingUpdate) {
1019 InitializeToReadyState();
1020 SetServiceError(syncer::SyncError::DATATYPE_ERROR);
1021 error_handler()->ExpectError(syncer::SyncError::DATATYPE_ERROR);
1022 worker()->UpdateFromServer(kTag1, kValue1);
1023 }
1024
1025 // Thoroughly tests the data generated by a server item creation.
1026 TEST_F(SharedModelTypeProcessorTest, ServerUpdateItem) {
1027 InitializeToReadyState();
1028
1029 // Local add writes data and metadata; ack writes metadata again.
1030 WriteItemAndAck(kTag1, kValue1);
1031 EXPECT_EQ(1U, db().DataChangeCount());
1032 EXPECT_EQ(2U, db().MetadataChangeCount());
1033
1034 // Redundant update from server doesn't write data but updates metadata.
1035 worker()->UpdateFromServer(kTag1, kValue1);
1036 EXPECT_EQ(1U, db().DataChangeCount());
1037 EXPECT_EQ(3U, db().MetadataChangeCount());
1038
1039 // A reflection (update already received) is ignored completely.
1040 worker()->UpdateFromServer(kTag1, kValue1, 0 /* version_offset */);
1041 EXPECT_EQ(1U, db().DataChangeCount());
1042 EXPECT_EQ(3U, db().MetadataChangeCount());
1043 }
1044
1045 // Tests locally deleting an acknowledged item.
1046 TEST_F(SharedModelTypeProcessorTest, LocalDeleteItem) {
1047 InitializeToReadyState();
1048 WriteItemAndAck(kTag1, kValue1);
1049 EXPECT_EQ(0U, worker()->GetNumPendingCommits());
1050
1051 const sync_pb::EntityMetadata metadata_v1 = db().GetMetadata(kTag1);
1052 EXPECT_FALSE(metadata_v1.is_deleted());
1053 EXPECT_EQ(1, metadata_v1.sequence_number());
1054 EXPECT_EQ(1, metadata_v1.acked_sequence_number());
1055 EXPECT_EQ(1, metadata_v1.server_version());
1056
1057 DeleteItem(kTag1);
1058 EXPECT_EQ(0U, db().DataCount());
1059 // Metadata is not removed until the commit response comes back.
1060 EXPECT_EQ(1U, db().MetadataCount());
1061 EXPECT_EQ(1U, ProcessorEntityCount());
1062 worker()->ExpectPendingCommits({kTag1});
1063
1064 const sync_pb::EntityMetadata metadata_v2 = db().GetMetadata(kTag1);
1065 EXPECT_TRUE(metadata_v2.is_deleted());
1066 EXPECT_EQ(2, metadata_v2.sequence_number());
1067 EXPECT_EQ(1, metadata_v2.acked_sequence_number());
1068 EXPECT_EQ(1, metadata_v2.server_version());
1069
1070 // Ack the delete and check that the metadata is cleared.
1071 worker()->AckOnePendingCommit();
1072 EXPECT_EQ(0U, db().MetadataCount());
1073 EXPECT_EQ(0U, ProcessorEntityCount());
1074 }
1075
1076 // Tests creating and deleting an item locally before receiving a commit
1077 // response, then getting the commit responses.
1078 TEST_F(SharedModelTypeProcessorTest, LocalDeleteItemInterleaved) {
1079 InitializeToReadyState();
1080 WriteItem(kTag1, kValue1);
1081 worker()->ExpectPendingCommits({kTag1});
1082 const CommitRequestData& data_v1 =
1083 worker()->GetLatestPendingCommitForTag(kTag1);
1084
1085 const sync_pb::EntityMetadata metadata_v1 = db().GetMetadata(kTag1);
1086 EXPECT_FALSE(metadata_v1.is_deleted());
1087 EXPECT_EQ(1, metadata_v1.sequence_number());
1088 EXPECT_EQ(0, metadata_v1.acked_sequence_number());
1089 EXPECT_EQ(kUncommittedVersion, metadata_v1.server_version());
1090
1091 DeleteItem(kTag1);
1092 EXPECT_EQ(0U, db().DataCount());
1093 EXPECT_EQ(1U, db().MetadataCount());
1094 EXPECT_EQ(1U, ProcessorEntityCount());
1095 worker()->ExpectPendingCommits({kTag1, kTag1});
1096
1097 const CommitRequestData& data_v2 =
1098 worker()->GetLatestPendingCommitForTag(kTag1);
1099 EXPECT_GT(data_v2.sequence_number, data_v1.sequence_number);
1100 EXPECT_TRUE(data_v2.entity->id.empty());
1101 EXPECT_EQ(kUncommittedVersion, data_v2.base_version);
1102 EXPECT_TRUE(data_v2.entity->is_deleted());
1103
1104 const sync_pb::EntityMetadata metadata_v2 = db().GetMetadata(kTag1);
1105 EXPECT_TRUE(metadata_v2.is_deleted());
1106 EXPECT_EQ(2, metadata_v2.sequence_number());
1107 EXPECT_EQ(0, metadata_v2.acked_sequence_number());
1108 EXPECT_EQ(kUncommittedVersion, metadata_v2.server_version());
1109
1110 // A response for the first commit doesn't change much.
1111 worker()->AckOnePendingCommit();
1112 EXPECT_EQ(0U, db().DataCount());
1113 EXPECT_EQ(1U, db().MetadataCount());
1114 EXPECT_EQ(1U, ProcessorEntityCount());
1115
1116 const sync_pb::EntityMetadata metadata_v3 = db().GetMetadata(kTag1);
1117 EXPECT_TRUE(metadata_v3.is_deleted());
1118 EXPECT_EQ(2, metadata_v3.sequence_number());
1119 EXPECT_EQ(1, metadata_v3.acked_sequence_number());
1120 EXPECT_EQ(1, metadata_v3.server_version());
1121
1122 worker()->AckOnePendingCommit();
1123 // The delete was acked so the metadata should now be cleared.
1124 EXPECT_EQ(0U, db().MetadataCount());
1125 EXPECT_EQ(0U, ProcessorEntityCount());
1126 }
1127
1128 TEST_F(SharedModelTypeProcessorTest, ServerDeleteItem) {
1129 InitializeToReadyState();
1130 WriteItemAndAck(kTag1, kValue1);
1131 EXPECT_EQ(1U, ProcessorEntityCount());
1132 EXPECT_EQ(1U, db().MetadataCount());
1133 EXPECT_EQ(1U, db().DataCount());
1134 EXPECT_EQ(0U, worker()->GetNumPendingCommits());
1135
1136 worker()->TombstoneFromServer(kTag1);
1137 // Delete from server should clear the data and all the metadata.
1138 EXPECT_EQ(0U, db().DataCount());
1139 EXPECT_EQ(0U, db().MetadataCount());
1140 EXPECT_EQ(0U, ProcessorEntityCount());
1141 EXPECT_EQ(0U, worker()->GetNumPendingCommits());
1142 }
1143
1144 // Deletes an item we've never seen before.
1145 // Should have no effect and not crash.
1146 TEST_F(SharedModelTypeProcessorTest, LocalDeleteUnknown) {
1147 InitializeToReadyState();
1148 DeleteItem(kTag1);
1149 EXPECT_EQ(0U, db().DataCount());
1150 EXPECT_EQ(0U, db().MetadataCount());
1151 EXPECT_EQ(0U, ProcessorEntityCount());
1152 EXPECT_EQ(0U, worker()->GetNumPendingCommits());
1153 }
1154
1155 // Deletes an item we've never seen before.
1156 // Should have no effect and not crash.
1157 TEST_F(SharedModelTypeProcessorTest, ServerDeleteUnknown) {
1158 InitializeToReadyState();
1159 worker()->TombstoneFromServer(kTag1);
1160 EXPECT_EQ(0U, db().DataCount());
1161 EXPECT_EQ(0U, db().MetadataCount());
1162 EXPECT_EQ(0U, ProcessorEntityCount());
1163 EXPECT_EQ(0U, worker()->GetNumPendingCommits());
1164 }
1165
1166 // Creates two different sync items.
1167 // Verifies that the second has no effect on the first.
1168 TEST_F(SharedModelTypeProcessorTest, TwoIndependentItems) {
1169 InitializeToReadyState();
1170 EXPECT_EQ(0U, worker()->GetNumPendingCommits());
1171
1172 WriteItem(kTag1, kValue1);
1173 EXPECT_EQ(1U, db().DataCount());
1174 EXPECT_EQ(1U, db().MetadataCount());
1175 const sync_pb::EntityMetadata metadata1 = db().GetMetadata(kTag1);
1176
1177 // There should be one commit request for this item only.
1178 worker()->ExpectPendingCommits({kTag1});
1179
1180 WriteItem(kTag2, kValue2);
1181 EXPECT_EQ(2U, db().DataCount());
1182 EXPECT_EQ(2U, db().MetadataCount());
1183 const sync_pb::EntityMetadata metadata2 = db().GetMetadata(kTag2);
1184
1185 // The second write should trigger another single-item commit request.
1186 worker()->ExpectPendingCommits({kTag1, kTag2});
1187
1188 EXPECT_FALSE(metadata1.is_deleted());
1189 EXPECT_EQ(1, metadata1.sequence_number());
1190 EXPECT_EQ(0, metadata1.acked_sequence_number());
1191 EXPECT_EQ(kUncommittedVersion, metadata1.server_version());
1192
1193 EXPECT_FALSE(metadata2.is_deleted());
1194 EXPECT_EQ(1, metadata2.sequence_number());
1195 EXPECT_EQ(0, metadata2.acked_sequence_number());
1196 EXPECT_EQ(kUncommittedVersion, metadata2.server_version());
1197 }
1198
1199 TEST_F(SharedModelTypeProcessorTest, ConflictResolutionChangesMatch) {
1200 InitializeToReadyState();
1201 WriteItem(kTag1, kValue1);
1202 EXPECT_EQ(1U, db().DataChangeCount());
1203 EXPECT_EQ(kValue1, db().GetValue(kTag1));
1204 EXPECT_EQ(1U, db().MetadataChangeCount());
1205 EXPECT_EQ(kUncommittedVersion, db().GetMetadata(kTag1).server_version());
1206 worker()->ExpectPendingCommits({kTag1});
1207 worker()->ExpectNthPendingCommit(0, kTag1, kValue1);
1208
1209 // Changes match doesn't call ResolveConflict.
1210 worker()->UpdateFromServer(kTag1, kValue1);
1211
1212 // Updated metadata but not data; no new commit request.
1213 EXPECT_EQ(1U, db().DataChangeCount());
1214 EXPECT_EQ(1, db().GetMetadata(kTag1).server_version());
1215 worker()->ExpectPendingCommits({kTag1});
1216 }
1217
1218 TEST_F(SharedModelTypeProcessorTest, ConflictResolutionUseLocal) {
1219 InitializeToReadyState();
1220 WriteItem(kTag1, kValue1);
1221 SetConflictResolution(ConflictResolution::UseLocal());
1222
1223 worker()->UpdateFromServer(kTag1, kValue2);
1224
1225 // Updated metadata but not data; new commit request.
1226 EXPECT_EQ(1U, db().DataChangeCount());
1227 EXPECT_EQ(2U, db().MetadataChangeCount());
1228 EXPECT_EQ(1, db().GetMetadata(kTag1).server_version());
1229 worker()->ExpectPendingCommits({kTag1, kTag1});
1230 worker()->ExpectNthPendingCommit(1, kTag1, kValue1);
1231 }
1232
1233 TEST_F(SharedModelTypeProcessorTest, ConflictResolutionUseRemote) {
1234 InitializeToReadyState();
1235 WriteItem(kTag1, kValue1);
1236 SetConflictResolution(ConflictResolution::UseRemote());
1237 worker()->UpdateFromServer(kTag1, kValue2);
1238
1239 // Updated client data and metadata; no new commit request.
1240 EXPECT_EQ(2U, db().DataChangeCount());
1241 EXPECT_EQ(kValue2, db().GetValue(kTag1));
1242 EXPECT_EQ(2U, db().MetadataChangeCount());
1243 EXPECT_EQ(1, db().GetMetadata(kTag1).server_version());
1244 worker()->ExpectPendingCommits({kTag1});
1245 }
1246
1247 TEST_F(SharedModelTypeProcessorTest, ConflictResolutionUseNew) {
1248 InitializeToReadyState();
1249 WriteItem(kTag1, kValue1);
1250 SetConflictResolution(
1251 ConflictResolution::UseNew(GenerateEntityData(kTag1, kValue3)));
1252
1253 worker()->UpdateFromServer(kTag1, kValue2);
1254 EXPECT_EQ(2U, db().DataChangeCount());
1255 EXPECT_EQ(kValue3, db().GetValue(kTag1));
1256 EXPECT_EQ(2U, db().MetadataChangeCount());
1257 EXPECT_EQ(1, db().GetMetadata(kTag1).server_version());
1258 worker()->ExpectPendingCommits({kTag1, kTag1});
1259 worker()->ExpectNthPendingCommit(1, kTag1, kValue3);
1260 }
1261
1262 // Test proper handling of disconnect and reconnect.
1263 //
1264 // Creates items in various states of commit and verifies they re-attempt to
1265 // commit on reconnect.
1266 TEST_F(SharedModelTypeProcessorTest, Disconnect) {
1267 InitializeToReadyState();
1268
1269 // The first item is fully committed.
1270 WriteItemAndAck(kTag1, kValue1);
1271
1272 // The second item has a commit request in progress.
1273 WriteItem(kTag2, kValue2);
1274 EXPECT_TRUE(worker()->HasPendingCommitForTag(kTag2));
1275
1276 DisconnectSync();
1277
1278 // The third item is added after stopping.
1279 WriteItem(kTag3, kValue3);
1280
1281 // Reconnect.
1282 OnSyncStarting();
1283
1284 EXPECT_EQ(1U, worker()->GetNumPendingCommits());
1285 EXPECT_EQ(2U, worker()->GetNthPendingCommit(0).size());
1286
1287 // The first item was already in sync.
1288 EXPECT_FALSE(worker()->HasPendingCommitForTag(kTag1));
1289
1290 // The second item's commit was interrupted and should be retried.
1291 EXPECT_TRUE(worker()->HasPendingCommitForTag(kTag2));
1292
1293 // The third item's commit was not started until the reconnect.
1294 EXPECT_TRUE(worker()->HasPendingCommitForTag(kTag3));
1295 }
1296
1297 // Test proper handling of disable and re-enable.
1298 //
1299 // Creates items in various states of commit and verifies they re-attempt to
1300 // commit on re-enable.
1301 TEST_F(SharedModelTypeProcessorTest, Disable) {
1302 InitializeToReadyState();
1303
1304 // The first item is fully committed.
1305 WriteItemAndAck(kTag1, kValue1);
1306
1307 // The second item has a commit request in progress.
1308 WriteItem(kTag2, kValue2);
1309 EXPECT_TRUE(worker()->HasPendingCommitForTag(kTag2));
1310
1311 DisableSync();
1312
1313 // The third item is added after disable.
1314 WriteItem(kTag3, kValue3);
1315
1316 // Now we re-enable.
1317 CreateChangeProcessor();
1318 OnMetadataLoaded();
1319 OnSyncStarting();
1320 OnInitialSyncDone();
1321
1322 // Once we're ready to commit, all three local items should consider
1323 // themselves uncommitted and pending for commit.
1324 worker()->ExpectPendingCommits({kTag1, kTag2, kTag3});
1325 }
1326
1327 // Test re-encrypt everything when desired encryption key changes.
1328 TEST_F(SharedModelTypeProcessorTest, ReEncryptCommitsWithNewKey) {
1329 InitializeToReadyState();
1330
1331 // Commit an item.
1332 WriteItemAndAck(kTag1, kValue1);
1333 // Create another item and don't wait for its commit response.
1334 WriteItem(kTag2, kValue2);
1335 worker()->ExpectPendingCommits({kTag2});
1336 EXPECT_EQ(1U, db().GetMetadata(kTag1).sequence_number());
1337 EXPECT_EQ(1U, db().GetMetadata(kTag2).sequence_number());
1338
1339 // Receive notice that the account's desired encryption key has changed.
1340 worker()->UpdateWithEncryptionKey("k1");
1341 // Tag 2 is recommitted immediately because the data was in memory.
1342 ASSERT_EQ(2U, worker()->GetNumPendingCommits());
1343 worker()->ExpectNthPendingCommit(1, kTag2, kValue2);
1344 // Sequence numbers in the store are updated.
1345 EXPECT_EQ(2U, db().GetMetadata(kTag1).sequence_number());
1346 EXPECT_EQ(2U, db().GetMetadata(kTag2).sequence_number());
1347
1348 // Tag 1 needs to go to the store to load its data before recommitting.
1349 OnPendingCommitDataLoaded();
1350 ASSERT_EQ(3U, worker()->GetNumPendingCommits());
1351 worker()->ExpectNthPendingCommit(2, kTag1, kValue1);
1352 }
1353
1354 // Test that an error loading pending commit data for re-encryption is
1355 // propagated to the error handler.
1356 TEST_F(SharedModelTypeProcessorTest, ReEncryptErrorLoadingData) {
1357 InitializeToReadyState();
1358 WriteItemAndAck(kTag1, kValue1);
1359 SetServiceError(syncer::SyncError::DATATYPE_ERROR);
1360 worker()->UpdateWithEncryptionKey("k1");
1361 error_handler()->ExpectError(syncer::SyncError::DATATYPE_ERROR);
1362 OnPendingCommitDataLoaded();
1363 }
1364
1365 // Test receipt of updates with new and old keys.
1366 TEST_F(SharedModelTypeProcessorTest, ReEncryptUpdatesWithNewKey) {
1367 InitializeToReadyState();
1368
1369 // Receive an unencrypted update.
1370 worker()->UpdateFromServer(kTag1, kValue1);
1371 ASSERT_EQ(0U, worker()->GetNumPendingCommits());
1372
1373 UpdateResponseDataList update;
1374 // Receive an entity with old encryption as part of the update.
1375 update.push_back(worker()->GenerateUpdateData(kTag2, kValue2, 1, "k1"));
1376 // Receive an entity with up-to-date encryption as part of the update.
1377 update.push_back(worker()->GenerateUpdateData(kTag3, kValue3, 1, "k2"));
1378 // Set desired encryption key to k2 to force updates to some items.
1379 worker()->UpdateWithEncryptionKey("k2", update);
1380
1381 // kTag2 needed to be re-encrypted and had data so it was queued immediately.
1382 worker()->ExpectPendingCommits({kTag2});
1383 OnPendingCommitDataLoaded();
1384 // kTag1 needed data so once that's loaded, it is also queued.
1385 worker()->ExpectPendingCommits({kTag2, kTag1});
1386
1387 // Receive a separate update that was encrypted with key k1.
1388 worker()->UpdateFromServer("enc_k1", kValue1, 1, "k1");
1389 // Receipt of updates encrypted with old key also forces a re-encrypt commit.
1390 worker()->ExpectPendingCommits({kTag2, kTag1, "enc_k1"});
1391
1392 // Receive an update that was encrypted with key k2.
1393 worker()->UpdateFromServer("enc_k2", kValue1, 1, "k2");
1394 // That was the correct key, so no re-encryption is required.
1395 worker()->ExpectPendingCommits({kTag2, kTag1, "enc_k1"});
1396 }
1397
1398 // Test that re-encrypting enqueues the right data for USE_LOCAL conflicts.
1399 TEST_F(SharedModelTypeProcessorTest, ReEncryptConflictResolutionUseLocal) {
1400 InitializeToReadyState();
1401 worker()->UpdateWithEncryptionKey("k1");
1402 WriteItem(kTag1, kValue1);
1403 worker()->ExpectPendingCommits({kTag1});
1404
1405 SetConflictResolution(ConflictResolution::UseLocal());
1406 // Unencrypted update needs to be re-commited with key k1.
1407 worker()->UpdateFromServer(kTag1, kValue2, 1, "");
1408
1409 // Ensure the re-commit has the correct value.
1410 EXPECT_EQ(2U, worker()->GetNumPendingCommits());
1411 worker()->ExpectNthPendingCommit(1, kTag1, kValue1);
1412 EXPECT_EQ(kValue1, db().GetValue(kTag1));
1413 }
1414
1415 // Test that re-encrypting enqueues the right data for USE_REMOTE conflicts.
1416 TEST_F(SharedModelTypeProcessorTest, ReEncryptConflictResolutionUseRemote) {
1417 InitializeToReadyState();
1418 worker()->UpdateWithEncryptionKey("k1");
1419 WriteItem(kTag1, kValue1);
1420
1421 SetConflictResolution(ConflictResolution::UseRemote());
1422 // Unencrypted update needs to be re-commited with key k1.
1423 worker()->UpdateFromServer(kTag1, kValue2, 1, "");
1424
1425 // Ensure the re-commit has the correct value.
1426 EXPECT_EQ(2U, worker()->GetNumPendingCommits());
1427 worker()->ExpectNthPendingCommit(1, kTag1, kValue2);
1428 EXPECT_EQ(kValue2, db().GetValue(kTag1));
1429 }
1430
1431 // Test that re-encrypting enqueues the right data for USE_NEW conflicts.
1432 TEST_F(SharedModelTypeProcessorTest, ReEncryptConflictResolutionUseNew) {
1433 InitializeToReadyState();
1434 worker()->UpdateWithEncryptionKey("k1");
1435 WriteItem(kTag1, kValue1);
1436
1437 SetConflictResolution(
1438 ConflictResolution::UseNew(GenerateEntityData(kTag1, kValue3)));
1439 // Unencrypted update needs to be re-commited with key k1.
1440 worker()->UpdateFromServer(kTag1, kValue2, 1, "");
1441
1442 // Ensure the re-commit has the correct value.
1443 EXPECT_EQ(2U, worker()->GetNumPendingCommits());
1444 worker()->ExpectNthPendingCommit(1, kTag1, kValue3);
1445 EXPECT_EQ(kValue3, db().GetValue(kTag1));
1446 }
1447
1448 TEST_F(SharedModelTypeProcessorTest, ReEncryptConflictWhileLoading) {
1449 InitializeToReadyState();
1450 // Create item and ack so its data is no longer cached.
1451 WriteItemAndAck(kTag1, kValue1);
1452 // Update key so that it needs to fetch data to re-commit.
1453 worker()->UpdateWithEncryptionKey("k1");
1454 EXPECT_EQ(0U, worker()->GetNumPendingCommits());
1455
1456 // Unencrypted update needs to be re-commited with key k1.
1457 worker()->UpdateFromServer(kTag1, kValue2, 1, "");
1458
1459 // Ensure the re-commit has the correct value.
1460 EXPECT_EQ(1U, worker()->GetNumPendingCommits());
1461 worker()->ExpectNthPendingCommit(0, kTag1, kValue2);
1462 EXPECT_EQ(kValue2, db().GetValue(kTag1));
1463
1464 // Data load completing shouldn't change anything.
1465 OnPendingCommitDataLoaded();
1466 EXPECT_EQ(1U, worker()->GetNumPendingCommits());
1467 }
1468
1469 // Tests that a real remote change wins over a local encryption-only change.
1470 TEST_F(SharedModelTypeProcessorTest, IgnoreLocalEncryption) {
1471 InitializeToReadyState();
1472 WriteItemAndAck(kTag1, kValue1);
1473 worker()->UpdateWithEncryptionKey("k1");
1474 OnPendingCommitDataLoaded();
1475 EXPECT_EQ(1U, worker()->GetNumPendingCommits());
1476 worker()->ExpectNthPendingCommit(0, kTag1, kValue1);
1477
1478 worker()->UpdateFromServer(kTag1, kValue2);
1479 EXPECT_EQ(1U, worker()->GetNumPendingCommits());
1480 }
1481
1482 // Tests that a real local change wins over a remote encryption-only change.
1483 TEST_F(SharedModelTypeProcessorTest, IgnoreRemoteEncryption) {
1484 InitializeToReadyState();
1485 WriteItemAndAck(kTag1, kValue1);
1486
1487 WriteItem(kTag1, kValue2);
1488 UpdateResponseDataList update;
1489 update.push_back(worker()->GenerateUpdateData(kTag1, kValue1, 1, "k1"));
1490 worker()->UpdateWithEncryptionKey("k1", update);
1491
1492 EXPECT_EQ(2U, worker()->GetNumPendingCommits());
1493 worker()->ExpectNthPendingCommit(1, kTag1, kValue2);
1494 }
1495
1496 // Same as above but with two commit requests before one ack.
1497 TEST_F(SharedModelTypeProcessorTest, IgnoreRemoteEncryptionInterleaved) {
1498 InitializeToReadyState();
1499 WriteItem(kTag1, kValue1);
1500 WriteItem(kTag1, kValue2);
1501 worker()->AckOnePendingCommit();
1502 // kValue1 is now the base value.
1503 EXPECT_EQ(1U, worker()->GetNumPendingCommits());
1504 worker()->ExpectNthPendingCommit(0, kTag1, kValue2);
1505
1506 UpdateResponseDataList update;
1507 update.push_back(worker()->GenerateUpdateData(kTag1, kValue1, 1, "k1"));
1508 worker()->UpdateWithEncryptionKey("k1", update);
1509
1510 EXPECT_EQ(2U, worker()->GetNumPendingCommits());
1511 worker()->ExpectNthPendingCommit(1, kTag1, kValue2);
1512 }
1513
1514 } // namespace syncer_v2
OLDNEW
« no previous file with comments | « sync/internal_api/shared_model_type_processor.cc ('k') | sync/internal_api/sync_db_util.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698