OLD | NEW |
---|---|
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "components/password_manager/core/browser/password_syncable_service.h" | 5 #include "components/password_manager/core/browser/password_syncable_service.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 #include <memory> | 8 #include <memory> |
9 #include <string> | 9 #include <string> |
10 #include <utility> | 10 #include <utility> |
11 #include <vector> | 11 #include <vector> |
12 | 12 |
13 #include "base/location.h" | 13 #include "base/location.h" |
14 #include "base/macros.h" | 14 #include "base/macros.h" |
15 #include "base/memory/ptr_util.h" | 15 #include "base/memory/ptr_util.h" |
16 #include "base/memory/ref_counted.h" | 16 #include "base/memory/ref_counted.h" |
17 #include "base/message_loop/message_loop.h" | |
18 #include "base/strings/utf_string_conversions.h" | 17 #include "base/strings/utf_string_conversions.h" |
18 #include "base/test/scoped_task_environment.h" | |
19 #include "base/test/simple_test_clock.h" | |
19 #include "components/password_manager/core/browser/mock_password_store.h" | 20 #include "components/password_manager/core/browser/mock_password_store.h" |
21 #include "components/password_manager/core/browser/password_manager_test_utils.h " | |
20 #include "components/sync/model/sync_change_processor.h" | 22 #include "components/sync/model/sync_change_processor.h" |
21 #include "components/sync/model/sync_error.h" | 23 #include "components/sync/model/sync_error.h" |
22 #include "components/sync/model/sync_error_factory_mock.h" | 24 #include "components/sync/model/sync_error_factory_mock.h" |
23 #include "testing/gmock/include/gmock/gmock.h" | 25 #include "testing/gmock/include/gmock/gmock.h" |
24 #include "testing/gtest/include/gtest/gtest.h" | 26 #include "testing/gtest/include/gtest/gtest.h" |
25 | 27 |
26 using syncer::SyncChange; | 28 using syncer::SyncChange; |
27 using syncer::SyncData; | 29 using syncer::SyncData; |
28 using syncer::SyncDataList; | 30 using syncer::SyncDataList; |
29 using syncer::SyncError; | 31 using syncer::SyncError; |
(...skipping 14 matching lines...) Expand all Loading... | |
44 // Defined in the implementation file corresponding to this test. | 46 // Defined in the implementation file corresponding to this test. |
45 syncer::SyncData SyncDataFromPassword(const autofill::PasswordForm& password); | 47 syncer::SyncData SyncDataFromPassword(const autofill::PasswordForm& password); |
46 autofill::PasswordForm PasswordFromSpecifics( | 48 autofill::PasswordForm PasswordFromSpecifics( |
47 const sync_pb::PasswordSpecificsData& password); | 49 const sync_pb::PasswordSpecificsData& password); |
48 std::string MakePasswordSyncTag(const sync_pb::PasswordSpecificsData& password); | 50 std::string MakePasswordSyncTag(const sync_pb::PasswordSpecificsData& password); |
49 std::string MakePasswordSyncTag(const autofill::PasswordForm& password); | 51 std::string MakePasswordSyncTag(const autofill::PasswordForm& password); |
50 | 52 |
51 namespace { | 53 namespace { |
52 | 54 |
53 // PasswordForm values for tests. | 55 // PasswordForm values for tests. |
54 const autofill::PasswordForm::Type kArbitraryType = | 56 constexpr autofill::PasswordForm::Type kArbitraryType = |
55 autofill::PasswordForm::TYPE_GENERATED; | 57 autofill::PasswordForm::TYPE_GENERATED; |
56 const char kIconUrl[] = "https://fb.com/Icon"; | 58 constexpr char kAndroidAutofillRealm[] = "android://hash@com.magisto"; |
57 const char kDisplayName[] = "Agent Smith"; | 59 constexpr char kAndroidCorrectRealm[] = "android://hash@com.magisto/"; |
58 const char kFederationUrl[] = "https://fb.com/"; | 60 constexpr char kIconUrl[] = "https://fb.com/Icon"; |
59 const char kPassword[] = "abcdef"; | 61 constexpr char kDisplayName[] = "Agent Smith"; |
60 const char kSignonRealm[] = "abc"; | 62 constexpr char kFederationUrl[] = "https://fb.com/"; |
61 const char kSignonRealm2[] = "def"; | 63 constexpr char kPassword[] = "abcdef"; |
62 const char kSignonRealm3[] = "xyz"; | 64 constexpr char kSignonRealm[] = "abc"; |
63 const int kTimesUsed = 5; | 65 constexpr char kSignonRealm2[] = "def"; |
64 const char kUsername[] = "godzilla"; | 66 constexpr char kSignonRealm3[] = "xyz"; |
67 constexpr int kTimesUsed = 5; | |
68 constexpr char kUsername[] = "godzilla"; | |
65 | 69 |
66 typedef std::vector<SyncChange> SyncChangeList; | 70 typedef std::vector<SyncChange> SyncChangeList; |
67 | 71 |
68 const sync_pb::PasswordSpecificsData& GetPasswordSpecifics( | 72 const sync_pb::PasswordSpecificsData& GetPasswordSpecifics( |
69 const syncer::SyncData& sync_data) { | 73 const syncer::SyncData& sync_data) { |
70 return sync_data.GetSpecifics().password().client_only_encrypted_data(); | 74 return sync_data.GetSpecifics().password().client_only_encrypted_data(); |
71 } | 75 } |
72 | 76 |
73 MATCHER(HasDateSynced, "") { | 77 MATCHER_P(HasDateSynced, time, "") { |
74 return !arg.date_synced.is_null() && !arg.date_synced.is_max(); | 78 return arg.date_synced == time; |
75 } | 79 } |
76 | 80 |
77 MATCHER_P(PasswordIs, form, "") { | 81 MATCHER_P(PasswordIs, form, "") { |
78 sync_pb::PasswordSpecificsData actual_password = | 82 sync_pb::PasswordSpecificsData actual_password = |
79 GetPasswordSpecifics(SyncDataFromPassword(arg)); | 83 GetPasswordSpecifics(SyncDataFromPassword(arg)); |
80 sync_pb::PasswordSpecificsData expected_password = | 84 sync_pb::PasswordSpecificsData expected_password = |
81 GetPasswordSpecifics(SyncDataFromPassword(form)); | 85 GetPasswordSpecifics(SyncDataFromPassword(form)); |
82 if (expected_password.scheme() == actual_password.scheme() && | 86 if (expected_password.scheme() == actual_password.scheme() && |
83 expected_password.signon_realm() == actual_password.signon_realm() && | 87 expected_password.signon_realm() == actual_password.signon_realm() && |
84 expected_password.origin() == actual_password.origin() && | 88 expected_password.origin() == actual_password.origin() && |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
117 Matches(PasswordIs(password))(form))); | 121 Matches(PasswordIs(password))(form))); |
118 } | 122 } |
119 | 123 |
120 // The argument is std::vector<autofill::PasswordForm*>*. The caller is | 124 // The argument is std::vector<autofill::PasswordForm*>*. The caller is |
121 // responsible for the lifetime of all the password forms. | 125 // responsible for the lifetime of all the password forms. |
122 ACTION_P(AppendForm, form) { | 126 ACTION_P(AppendForm, form) { |
123 arg0->push_back(base::MakeUnique<autofill::PasswordForm>(form)); | 127 arg0->push_back(base::MakeUnique<autofill::PasswordForm>(form)); |
124 return true; | 128 return true; |
125 } | 129 } |
126 | 130 |
131 // The argument is std::vector<autofill::PasswordForm*>*. The caller is | |
132 // responsible for the lifetime of all the password forms. | |
133 ACTION_P(AppendForms, forms) { | |
134 for (const autofill::PasswordForm& form : forms) | |
135 arg0->push_back(base::MakeUnique<autofill::PasswordForm>(form)); | |
136 return true; | |
137 } | |
138 | |
127 // Creates a sync data consisting of password specifics. The sign on realm is | 139 // Creates a sync data consisting of password specifics. The sign on realm is |
128 // set to |signon_realm|. | 140 // set to |signon_realm|. |
129 SyncData CreateSyncData(const std::string& signon_realm) { | 141 SyncData CreateSyncData(const std::string& signon_realm) { |
130 sync_pb::EntitySpecifics password_data; | 142 sync_pb::EntitySpecifics password_data; |
131 sync_pb::PasswordSpecificsData* password_specifics = | 143 sync_pb::PasswordSpecificsData* password_specifics = |
132 password_data.mutable_password()->mutable_client_only_encrypted_data(); | 144 password_data.mutable_password()->mutable_client_only_encrypted_data(); |
133 password_specifics->set_signon_realm(signon_realm); | 145 password_specifics->set_signon_realm(signon_realm); |
134 password_specifics->set_type(autofill::PasswordForm::TYPE_GENERATED); | 146 password_specifics->set_type(autofill::PasswordForm::TYPE_GENERATED); |
135 password_specifics->set_times_used(3); | 147 password_specifics->set_times_used(3); |
136 password_specifics->set_display_name("Mr. X"); | 148 password_specifics->set_display_name("Mr. X"); |
137 password_specifics->set_avatar_url("https://accounts.google.com/Icon"); | 149 password_specifics->set_avatar_url("https://accounts.google.com/Icon"); |
138 password_specifics->set_federation_url("https://google.com"); | 150 password_specifics->set_federation_url("https://google.com"); |
139 password_specifics->set_username_value("kingkong"); | 151 password_specifics->set_username_value("kingkong"); |
140 password_specifics->set_password_value("sicrit"); | 152 password_specifics->set_password_value("sicrit"); |
141 | 153 |
142 std::string tag = MakePasswordSyncTag(*password_specifics); | 154 std::string tag = MakePasswordSyncTag(*password_specifics); |
143 return syncer::SyncData::CreateLocalData(tag, tag, password_data); | 155 return syncer::SyncData::CreateLocalData(tag, tag, password_data); |
144 } | 156 } |
145 | 157 |
146 SyncChange CreateSyncChange(const autofill::PasswordForm& password, | 158 SyncChange CreateSyncChange(const autofill::PasswordForm& password, |
147 SyncChange::SyncChangeType type) { | 159 SyncChange::SyncChangeType type) { |
148 SyncData data = SyncDataFromPassword(password); | 160 SyncData data = SyncDataFromPassword(password); |
149 return SyncChange(FROM_HERE, type, data); | 161 return SyncChange(FROM_HERE, type, data); |
150 } | 162 } |
151 | 163 |
152 // A testable implementation of the |PasswordSyncableService| that mocks | |
153 // out all interaction with the password database. | |
154 class MockPasswordSyncableService : public PasswordSyncableService { | |
155 public: | |
156 explicit MockPasswordSyncableService(PasswordStoreSync* password_store) | |
157 : PasswordSyncableService(password_store) {} | |
158 | |
159 MOCK_METHOD1(StartSyncFlare, void(syncer::ModelType)); | |
160 | |
161 private: | |
162 DISALLOW_COPY_AND_ASSIGN(MockPasswordSyncableService); | |
163 }; | |
164 | |
165 // Mock implementation of SyncChangeProcessor. | 164 // Mock implementation of SyncChangeProcessor. |
166 class MockSyncChangeProcessor : public syncer::SyncChangeProcessor { | 165 class MockSyncChangeProcessor : public syncer::SyncChangeProcessor { |
167 public: | 166 public: |
168 MockSyncChangeProcessor() {} | 167 MockSyncChangeProcessor() {} |
169 | 168 |
170 MOCK_METHOD2(ProcessSyncChanges, | 169 MOCK_METHOD2(ProcessSyncChanges, |
171 SyncError(const tracked_objects::Location&, | 170 SyncError(const tracked_objects::Location&, |
172 const SyncChangeList& list)); | 171 const SyncChangeList& list)); |
173 SyncDataList GetAllSyncData(syncer::ModelType type) const override { | 172 SyncDataList GetAllSyncData(syncer::ModelType type) const override { |
174 NOTREACHED(); | 173 NOTREACHED(); |
175 return SyncDataList(); | 174 return SyncDataList(); |
176 } | 175 } |
177 | 176 |
178 private: | 177 private: |
179 DISALLOW_COPY_AND_ASSIGN(MockSyncChangeProcessor); | 178 DISALLOW_COPY_AND_ASSIGN(MockSyncChangeProcessor); |
180 }; | 179 }; |
181 | 180 |
182 // Convenience wrapper around a PasswordSyncableService and PasswordStore | 181 // Convenience wrapper around a PasswordSyncableService and PasswordStore |
183 // pair. | 182 // pair. |
184 class PasswordSyncableServiceWrapper { | 183 class PasswordSyncableServiceWrapper { |
185 public: | 184 public: |
186 PasswordSyncableServiceWrapper() { | 185 PasswordSyncableServiceWrapper() { |
187 password_store_ = new testing::StrictMock<MockPasswordStore>; | 186 password_store_ = new testing::StrictMock<MockPasswordStore>; |
188 service_.reset( | 187 service_.reset( |
189 new MockPasswordSyncableService(password_store_->GetSyncInterface())); | 188 new PasswordSyncableService(password_store_->GetSyncInterface())); |
190 ON_CALL(*password_store_, AddLoginImpl(HasDateSynced())) | 189 auto clock = base::MakeUnique<base::SimpleTestClock>(); |
190 clock->SetNow(time()); | |
191 service_->set_clock(std::move(clock)); | |
192 ON_CALL(*password_store_, AddLoginImpl(HasDateSynced(time()))) | |
191 .WillByDefault(Return(PasswordStoreChangeList())); | 193 .WillByDefault(Return(PasswordStoreChangeList())); |
192 ON_CALL(*password_store_, RemoveLoginImpl(_)) | 194 ON_CALL(*password_store_, RemoveLoginImpl(_)) |
193 .WillByDefault(Return(PasswordStoreChangeList())); | 195 .WillByDefault(Return(PasswordStoreChangeList())); |
194 ON_CALL(*password_store_, UpdateLoginImpl(HasDateSynced())) | 196 ON_CALL(*password_store_, UpdateLoginImpl(HasDateSynced(time()))) |
195 .WillByDefault(Return(PasswordStoreChangeList())); | 197 .WillByDefault(Return(PasswordStoreChangeList())); |
196 EXPECT_CALL(*password_store(), NotifyLoginsChanged(_)).Times(AnyNumber()); | 198 EXPECT_CALL(*password_store(), NotifyLoginsChanged(_)).Times(AnyNumber()); |
197 } | 199 } |
198 | 200 |
199 ~PasswordSyncableServiceWrapper() { password_store_->ShutdownOnUIThread(); } | 201 ~PasswordSyncableServiceWrapper() { password_store_->ShutdownOnUIThread(); } |
200 | 202 |
201 MockPasswordStore* password_store() { return password_store_.get(); } | 203 MockPasswordStore* password_store() { return password_store_.get(); } |
202 | 204 |
203 MockPasswordSyncableService* service() { return service_.get(); } | 205 PasswordSyncableService* service() { return service_.get(); } |
206 | |
207 static base::Time time() { return base::Time::FromInternalValue(100000); } | |
204 | 208 |
205 // Returnes the scoped_ptr to |service_| thus NULLing out it. | 209 // Returnes the scoped_ptr to |service_| thus NULLing out it. |
206 std::unique_ptr<syncer::SyncChangeProcessor> ReleaseSyncableService() { | 210 std::unique_ptr<syncer::SyncChangeProcessor> ReleaseSyncableService() { |
207 return std::move(service_); | 211 return std::move(service_); |
208 } | 212 } |
209 | 213 |
210 private: | 214 private: |
211 scoped_refptr<MockPasswordStore> password_store_; | 215 scoped_refptr<MockPasswordStore> password_store_; |
212 std::unique_ptr<MockPasswordSyncableService> service_; | 216 std::unique_ptr<PasswordSyncableService> service_; |
213 | 217 |
214 DISALLOW_COPY_AND_ASSIGN(PasswordSyncableServiceWrapper); | 218 DISALLOW_COPY_AND_ASSIGN(PasswordSyncableServiceWrapper); |
215 }; | 219 }; |
216 | 220 |
217 class PasswordSyncableServiceTest : public testing::Test { | 221 class PasswordSyncableServiceTest : public testing::Test { |
218 public: | 222 public: |
219 PasswordSyncableServiceTest() | 223 PasswordSyncableServiceTest() |
220 : processor_(new testing::StrictMock<MockSyncChangeProcessor>) { | 224 : processor_(new testing::StrictMock<MockSyncChangeProcessor>) { |
221 ON_CALL(*processor_, ProcessSyncChanges(_, _)) | 225 ON_CALL(*processor_, ProcessSyncChanges(_, _)) |
222 .WillByDefault(Return(SyncError())); | 226 .WillByDefault(Return(SyncError())); |
223 } | 227 } |
224 MockPasswordStore* password_store() { return wrapper_.password_store(); } | 228 MockPasswordStore* password_store() { return wrapper_.password_store(); } |
225 MockPasswordSyncableService* service() { return wrapper_.service(); } | 229 PasswordSyncableService* service() { return wrapper_.service(); } |
230 | |
231 MOCK_METHOD1(StartSyncFlare, void(syncer::ModelType)); | |
226 | 232 |
227 protected: | 233 protected: |
228 base::MessageLoop message_loop_; | |
229 std::unique_ptr<MockSyncChangeProcessor> processor_; | 234 std::unique_ptr<MockSyncChangeProcessor> processor_; |
230 | 235 |
231 private: | 236 private: |
237 base::test::ScopedTaskEnvironment scoped_task_environment_; | |
232 PasswordSyncableServiceWrapper wrapper_; | 238 PasswordSyncableServiceWrapper wrapper_; |
233 }; | 239 }; |
234 | 240 |
235 // Both sync and password db have data that are not present in the other. | 241 // Both sync and password db have data that are not present in the other. |
236 TEST_F(PasswordSyncableServiceTest, AdditionsInBoth) { | 242 TEST_F(PasswordSyncableServiceTest, AdditionsInBoth) { |
237 autofill::PasswordForm form; | 243 autofill::PasswordForm form; |
238 form.signon_realm = kSignonRealm; | 244 form.signon_realm = kSignonRealm; |
239 form.username_value = base::ASCIIToUTF16(kUsername); | 245 form.username_value = base::ASCIIToUTF16(kUsername); |
240 form.password_value = base::ASCIIToUTF16(kPassword); | 246 form.password_value = base::ASCIIToUTF16(kPassword); |
241 | 247 |
(...skipping 239 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
481 form.signon_realm = kSignonRealm; | 487 form.signon_realm = kSignonRealm; |
482 form.username_value = base::ASCIIToUTF16(kUsername); | 488 form.username_value = base::ASCIIToUTF16(kUsername); |
483 form.password_value = base::ASCIIToUTF16(kPassword); | 489 form.password_value = base::ASCIIToUTF16(kPassword); |
484 PasswordStoreChangeList list; | 490 PasswordStoreChangeList list; |
485 list.push_back(PasswordStoreChange(PasswordStoreChange::ADD, form)); | 491 list.push_back(PasswordStoreChange(PasswordStoreChange::ADD, form)); |
486 | 492 |
487 // No flare and no SyncChangeProcessor, the call shouldn't crash. | 493 // No flare and no SyncChangeProcessor, the call shouldn't crash. |
488 service()->ActOnPasswordStoreChanges(list); | 494 service()->ActOnPasswordStoreChanges(list); |
489 | 495 |
490 // Set the flare. It should be called as there is no SyncChangeProcessor. | 496 // Set the flare. It should be called as there is no SyncChangeProcessor. |
491 service()->InjectStartSyncFlare( | 497 service()->InjectStartSyncFlare(base::Bind( |
492 base::Bind(&MockPasswordSyncableService::StartSyncFlare, | 498 &PasswordSyncableServiceTest::StartSyncFlare, base::Unretained(this))); |
493 base::Unretained(service()))); | 499 EXPECT_CALL(*this, StartSyncFlare(syncer::PASSWORDS)); |
494 EXPECT_CALL(*service(), StartSyncFlare(syncer::PASSWORDS)); | |
495 service()->ActOnPasswordStoreChanges(list); | 500 service()->ActOnPasswordStoreChanges(list); |
496 } | 501 } |
497 | 502 |
498 // Start syncing with an error. Subsequent password store updates shouldn't be | 503 // Start syncing with an error. Subsequent password store updates shouldn't be |
499 // propagated to Sync. | 504 // propagated to Sync. |
500 TEST_F(PasswordSyncableServiceTest, FailedReadFromPasswordStore) { | 505 TEST_F(PasswordSyncableServiceTest, FailedReadFromPasswordStore) { |
501 std::unique_ptr<syncer::SyncErrorFactoryMock> error_factory( | 506 std::unique_ptr<syncer::SyncErrorFactoryMock> error_factory( |
502 new syncer::SyncErrorFactoryMock); | 507 new syncer::SyncErrorFactoryMock); |
503 syncer::SyncError error(FROM_HERE, syncer::SyncError::DATATYPE_ERROR, | 508 syncer::SyncError error(FROM_HERE, syncer::SyncError::DATATYPE_ERROR, |
504 "Failed to get passwords from store.", | 509 "Failed to get passwords from store.", |
(...skipping 161 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
666 EXPECT_TRUE(specifics.has_times_used()); | 671 EXPECT_TRUE(specifics.has_times_used()); |
667 EXPECT_EQ(11, specifics.times_used()); | 672 EXPECT_EQ(11, specifics.times_used()); |
668 EXPECT_TRUE(specifics.has_display_name()); | 673 EXPECT_TRUE(specifics.has_display_name()); |
669 EXPECT_EQ("Great Peter", specifics.display_name()); | 674 EXPECT_EQ("Great Peter", specifics.display_name()); |
670 EXPECT_TRUE(specifics.has_avatar_url()); | 675 EXPECT_TRUE(specifics.has_avatar_url()); |
671 EXPECT_EQ("https://google.com/icon", specifics.avatar_url()); | 676 EXPECT_EQ("https://google.com/icon", specifics.avatar_url()); |
672 EXPECT_TRUE(specifics.has_federation_url()); | 677 EXPECT_TRUE(specifics.has_federation_url()); |
673 EXPECT_EQ("https://google.com", specifics.federation_url()); | 678 EXPECT_EQ("https://google.com", specifics.federation_url()); |
674 } | 679 } |
675 | 680 |
681 // Tests for Android Autofill credentials. Those are saved in the wrong format | |
682 // without trailing '/'. Nevertheless, password store should always contain the | |
683 // correct values. | |
684 class PasswordSyncableServiceAndroidAutofillTest : public testing::Test { | |
685 public: | |
686 PasswordSyncableServiceAndroidAutofillTest() = default; | |
687 | |
688 static PasswordFormData android_incorrect(double creation_time) { | |
689 PasswordFormData data = {autofill::PasswordForm::SCHEME_HTML, | |
690 kAndroidAutofillRealm, | |
691 kAndroidAutofillRealm, | |
692 "", | |
693 L"", | |
694 L"", | |
695 L"", | |
696 L"username_value_1", | |
697 L"11111", | |
698 true, | |
699 creation_time}; | |
700 return data; | |
701 } | |
702 | |
703 static PasswordFormData android_correct(double creation_time) { | |
704 PasswordFormData data = {autofill::PasswordForm::SCHEME_HTML, | |
705 kAndroidCorrectRealm, | |
706 kAndroidCorrectRealm, | |
707 "", | |
708 L"", | |
709 L"", | |
710 L"", | |
711 L"username_value_1", | |
712 L"22222", | |
713 true, | |
714 creation_time}; | |
715 return data; | |
716 } | |
717 | |
718 static PasswordFormData android_incorrect2(double creation_time) { | |
719 PasswordFormData data = {autofill::PasswordForm::SCHEME_HTML, | |
720 kAndroidAutofillRealm, | |
721 kAndroidAutofillRealm, | |
722 "", | |
723 L"", | |
724 L"", | |
725 L"", | |
726 L"username_value_1", | |
727 L"33333", | |
728 false, | |
729 creation_time}; | |
730 return data; | |
731 } | |
732 | |
733 static PasswordFormData android_correct2(double creation_time) { | |
734 PasswordFormData data = {autofill::PasswordForm::SCHEME_HTML, | |
735 kAndroidCorrectRealm, | |
736 kAndroidCorrectRealm, | |
737 "", | |
738 L"", | |
739 L"", | |
740 L"", | |
741 L"username_value_1", | |
742 L"444444", | |
743 false, | |
744 creation_time}; | |
745 return data; | |
746 } | |
747 | |
748 static autofill::PasswordForm FormWithCorrectTag(PasswordFormData data) { | |
749 autofill::PasswordForm form = *CreatePasswordFormFromDataForTesting(data); | |
750 form.signon_realm = kAndroidCorrectRealm; | |
751 form.origin = GURL(kAndroidCorrectRealm); | |
752 form.date_synced = PasswordSyncableServiceWrapper::time(); | |
753 return form; | |
754 } | |
755 | |
756 static autofill::PasswordForm FormWithAndroidAutofillTag( | |
757 PasswordFormData data) { | |
758 autofill::PasswordForm form = *CreatePasswordFormFromDataForTesting(data); | |
759 form.signon_realm = kAndroidAutofillRealm; | |
760 form.origin = GURL(kAndroidAutofillRealm); | |
761 form.date_synced = PasswordSyncableServiceWrapper::time(); | |
762 return form; | |
763 } | |
764 | |
765 // Transforms |val| into |count| numbers from 0 to |count|. | |
engedy
2017/07/20 19:13:07
nit: 1 to |count|, inclusive.
vasilii
2017/07/20 19:54:46
Done.
| |
766 static std::vector<unsigned> ExtractTimestamps(unsigned val, unsigned count) { | |
767 std::vector<unsigned> result; | |
768 for (unsigned i = 0; i < count; ++i) { | |
769 result.push_back(val % count + 1); | |
770 val /= count; | |
771 } | |
772 return result; | |
773 } | |
774 | |
775 static testing::Message FormDataMessage(const std::string& prefix, | |
776 const PasswordFormData* data) { | |
777 testing::Message message; | |
778 message << prefix; | |
779 if (data) | |
780 message << *CreatePasswordFormFromDataForTesting(*data); | |
781 else | |
782 message << "NULL"; | |
783 return message; | |
784 } | |
785 | |
786 protected: | |
787 base::test::ScopedTaskEnvironment scoped_task_environment_; | |
788 }; | |
789 | |
790 TEST_F(PasswordSyncableServiceAndroidAutofillTest, FourWayMerge) { | |
791 for (unsigned val = 0; val < 4 * 4 * 4 * 4; ++val) { | |
792 // Generate 4 creation timestamps for all the entries. | |
793 std::vector<unsigned> dates = ExtractTimestamps(val, 4); | |
794 ASSERT_EQ(4u, dates.size()); | |
795 const unsigned latest = *std::max_element(dates.begin(), dates.end()); | |
796 // Sync correct, Sync Android autofill, local correct, local incorrect. | |
797 const PasswordFormData data[4] = { | |
798 android_correct(dates[0]), android_incorrect(dates[1]), | |
799 android_correct2(dates[2]), android_incorrect2(dates[3])}; | |
800 const PasswordFormData* latest_data = | |
801 std::find_if(data, data + 4, [latest](const PasswordFormData& data) { | |
802 return data.creation_time == latest; | |
803 }); | |
804 ASSERT_TRUE(latest_data); | |
805 std::vector<autofill::PasswordForm> expected_sync_updates; | |
806 if (latest_data != &data[0]) | |
807 expected_sync_updates.push_back(FormWithCorrectTag(*latest_data)); | |
808 if (latest_data != &data[1]) | |
809 expected_sync_updates.push_back(FormWithAndroidAutofillTag(*latest_data)); | |
810 autofill::PasswordForm local_correct = | |
811 *CreatePasswordFormFromDataForTesting(data[2]); | |
812 autofill::PasswordForm local_incorrect = | |
813 *CreatePasswordFormFromDataForTesting(data[3]); | |
814 syncer::SyncData sync_correct = | |
815 SyncDataFromPassword(*CreatePasswordFormFromDataForTesting(data[0])); | |
816 syncer::SyncData sync_incorrect = | |
817 SyncDataFromPassword(*CreatePasswordFormFromDataForTesting(data[1])); | |
818 | |
819 SCOPED_TRACE(*CreatePasswordFormFromDataForTesting(data[0])); | |
820 SCOPED_TRACE(*CreatePasswordFormFromDataForTesting(data[1])); | |
821 SCOPED_TRACE(*CreatePasswordFormFromDataForTesting(data[2])); | |
822 SCOPED_TRACE(*CreatePasswordFormFromDataForTesting(data[3])); | |
823 | |
824 for (bool correct_sync_first : {true, false}) { | |
825 auto wrapper = base::MakeUnique<PasswordSyncableServiceWrapper>(); | |
826 auto processor = | |
827 base::MakeUnique<testing::StrictMock<MockSyncChangeProcessor>>(); | |
828 | |
829 std::vector<autofill::PasswordForm> stored_forms = {local_correct, | |
830 local_incorrect}; | |
831 EXPECT_CALL(*wrapper->password_store(), FillAutofillableLogins(_)) | |
832 .WillOnce(AppendForms(stored_forms)); | |
833 EXPECT_CALL(*wrapper->password_store(), FillBlacklistLogins(_)) | |
834 .WillOnce(Return(true)); | |
835 if (latest_data != &data[2]) { | |
836 EXPECT_CALL(*wrapper->password_store(), | |
837 UpdateLoginImpl(FormWithCorrectTag(*latest_data))); | |
838 } | |
839 if (latest_data != &data[3]) { | |
840 EXPECT_CALL(*wrapper->password_store(), | |
841 UpdateLoginImpl(FormWithAndroidAutofillTag(*latest_data))); | |
842 } | |
843 | |
844 if (expected_sync_updates.size() == 1) { | |
845 EXPECT_CALL(*processor, | |
846 ProcessSyncChanges(_, ElementsAre(SyncChangeIs( | |
847 SyncChange::ACTION_UPDATE, | |
848 expected_sync_updates[0])))); | |
849 } else { | |
850 EXPECT_CALL( | |
851 *processor, | |
852 ProcessSyncChanges(_, UnorderedElementsAre( | |
853 SyncChangeIs(SyncChange::ACTION_UPDATE, | |
854 expected_sync_updates[0]), | |
855 SyncChangeIs(SyncChange::ACTION_UPDATE, | |
856 expected_sync_updates[1])))); | |
857 } | |
858 | |
859 SyncDataList sync_list = {sync_correct, sync_incorrect}; | |
860 if (!correct_sync_first) | |
861 std::swap(sync_list[0], sync_list[1]); | |
862 wrapper->service()->MergeDataAndStartSyncing( | |
863 syncer::PASSWORDS, sync_list, std::move(processor), | |
864 std::unique_ptr<syncer::SyncErrorFactory>()); | |
865 wrapper.reset(); | |
866 // Wait til PasswordStore is destroy end therefore all the expectations on | |
867 // it are checked. | |
868 scoped_task_environment_.RunUntilIdle(); | |
869 } | |
870 } | |
871 } | |
872 | |
873 TEST_F(PasswordSyncableServiceAndroidAutofillTest, ThreeWayMerge) { | |
874 for (int j = 0; j < 4; ++j) { | |
875 // Whether the entry exists: Sync correct, Sync Android autofill, | |
876 // local correct, local incorrect. | |
877 bool entry_present[4] = {true, true, true, true}; | |
878 entry_present[j] = false; | |
879 for (unsigned val = 0; val < 3 * 3 * 3; ++val) { | |
880 // Generate 3 creation timestamps for all the entries. | |
881 std::vector<unsigned> dates = ExtractTimestamps(val, 3); | |
882 ASSERT_EQ(3u, dates.size()); | |
883 const unsigned latest = *std::max_element(dates.begin(), dates.end()); | |
884 | |
885 // Sync correct, Sync Android autofill, local correct, local incorrect. | |
886 std::vector<std::unique_ptr<PasswordFormData>> data; | |
887 int date_index = 0; | |
888 data.push_back(entry_present[0] | |
889 ? base::MakeUnique<PasswordFormData>( | |
890 android_correct(dates[date_index++])) | |
891 : nullptr); | |
892 data.push_back(entry_present[1] | |
893 ? base::MakeUnique<PasswordFormData>( | |
894 android_incorrect(dates[date_index++])) | |
895 : nullptr); | |
896 data.push_back(entry_present[2] | |
897 ? base::MakeUnique<PasswordFormData>( | |
898 android_correct2(dates[date_index++])) | |
899 : nullptr); | |
900 data.push_back(entry_present[3] | |
901 ? base::MakeUnique<PasswordFormData>( | |
902 android_incorrect2(dates[date_index++])) | |
903 : nullptr); | |
904 | |
905 SCOPED_TRACE(val); | |
906 SCOPED_TRACE(j); | |
907 SCOPED_TRACE(FormDataMessage("data[0]=", data[0].get())); | |
908 SCOPED_TRACE(FormDataMessage("data[1]=", data[1].get())); | |
909 SCOPED_TRACE(FormDataMessage("data[2]=", data[2].get())); | |
910 SCOPED_TRACE(FormDataMessage("data[3]=", data[3].get())); | |
911 | |
912 const PasswordFormData* latest_data = | |
913 std::find_if(data.begin(), data.end(), | |
914 [latest](const std::unique_ptr<PasswordFormData>& data) { | |
915 return data && data->creation_time == latest; | |
916 }) | |
917 ->get(); | |
918 ASSERT_TRUE(latest_data); | |
919 std::vector<std::pair<SyncChange::SyncChangeType, autofill::PasswordForm>> | |
920 expected_sync_updates; | |
921 for (int i = 0; i < 2; ++i) { | |
922 if (latest_data != data[i].get()) { | |
923 expected_sync_updates.push_back(std::make_pair( | |
924 data[i] ? SyncChange::ACTION_UPDATE : SyncChange::ACTION_ADD, | |
925 i == 0 ? FormWithCorrectTag(*latest_data) | |
926 : FormWithAndroidAutofillTag(*latest_data))); | |
927 } | |
928 } | |
929 | |
930 std::vector<autofill::PasswordForm> stored_forms; | |
931 for (int i = 2; i < 4; ++i) { | |
932 if (data[i]) | |
933 stored_forms.push_back( | |
934 *CreatePasswordFormFromDataForTesting(*data[i])); | |
935 } | |
936 | |
937 SyncDataList sync_list; | |
938 for (int i = 0; i < 2; ++i) { | |
939 if (data[i]) { | |
940 sync_list.push_back(SyncDataFromPassword( | |
941 *CreatePasswordFormFromDataForTesting(*data[i]))); | |
942 } | |
943 } | |
944 | |
945 for (bool swap_sync_list : {false, true}) { | |
946 auto wrapper = base::MakeUnique<PasswordSyncableServiceWrapper>(); | |
947 auto processor = | |
948 base::MakeUnique<testing::StrictMock<MockSyncChangeProcessor>>(); | |
949 | |
950 EXPECT_CALL(*wrapper->password_store(), FillAutofillableLogins(_)) | |
951 .WillOnce(AppendForms(stored_forms)); | |
952 EXPECT_CALL(*wrapper->password_store(), FillBlacklistLogins(_)) | |
953 .WillOnce(Return(true)); | |
954 for (int i = 2; i < 4; ++i) { | |
955 if (latest_data != data[i].get()) { | |
956 autofill::PasswordForm latest_form = | |
957 i == 2 ? FormWithCorrectTag(*latest_data) | |
958 : FormWithAndroidAutofillTag(*latest_data); | |
959 if (data[i]) { | |
960 EXPECT_CALL(*wrapper->password_store(), | |
961 UpdateLoginImpl(latest_form)); | |
962 } else { | |
963 EXPECT_CALL(*wrapper->password_store(), | |
964 AddLoginImpl(latest_form)); | |
965 } | |
966 } | |
967 } | |
968 | |
969 if (expected_sync_updates.size() == 0) { | |
970 EXPECT_CALL(*processor, ProcessSyncChanges(_, IsEmpty())); | |
971 } else if (expected_sync_updates.size() == 1) { | |
972 EXPECT_CALL( | |
973 *processor, | |
974 ProcessSyncChanges(_, ElementsAre(SyncChangeIs( | |
975 expected_sync_updates[0].first, | |
976 expected_sync_updates[0].second)))); | |
977 } else if (expected_sync_updates.size() == 2) { | |
978 EXPECT_CALL( | |
979 *processor, | |
980 ProcessSyncChanges( | |
981 _, UnorderedElementsAre( | |
982 SyncChangeIs(expected_sync_updates[0].first, | |
983 expected_sync_updates[0].second), | |
984 SyncChangeIs(expected_sync_updates[1].first, | |
985 expected_sync_updates[1].second)))); | |
986 } | |
987 | |
988 if (swap_sync_list && sync_list.size() == 2) | |
989 std::swap(sync_list[0], sync_list[1]); | |
990 wrapper->service()->MergeDataAndStartSyncing( | |
991 syncer::PASSWORDS, sync_list, std::move(processor), | |
992 std::unique_ptr<syncer::SyncErrorFactory>()); | |
993 wrapper.reset(); | |
994 // Wait til PasswordStore is destroy end therefore all the expectations | |
995 // on it are checked. | |
996 scoped_task_environment_.RunUntilIdle(); | |
997 } | |
998 } | |
999 } | |
1000 } | |
1001 | |
1002 TEST_F(PasswordSyncableServiceAndroidAutofillTest, TwoWayServerAndLocalMerge) { | |
1003 for (unsigned i = 0; i < 2 * 2; ++i) { | |
1004 // Generate 4 different combinations for local/server entries. | |
1005 std::vector<unsigned> combination = ExtractTimestamps(i, 2); | |
1006 ASSERT_EQ(2u, combination.size()); | |
1007 const bool sync_data_correct = !!combination[0]; | |
1008 const bool local_data_correct = !!combination[1]; | |
1009 | |
1010 for (unsigned val = 0; val < 2 * 2; ++val) { | |
1011 std::vector<unsigned> dates = ExtractTimestamps(i, 2); | |
1012 ASSERT_EQ(2u, dates.size()); | |
1013 | |
1014 const PasswordFormData sync_data = sync_data_correct | |
1015 ? android_correct(dates[0]) | |
1016 : android_incorrect(dates[0]); | |
1017 const PasswordFormData local_data = local_data_correct | |
1018 ? android_correct2(dates[1]) | |
1019 : android_incorrect2(dates[1]); | |
1020 | |
1021 const PasswordFormData* latest_data = | |
1022 dates[1] > dates[0] ? &local_data : &sync_data; | |
1023 | |
1024 auto wrapper = base::MakeUnique<PasswordSyncableServiceWrapper>(); | |
1025 auto processor = | |
1026 base::MakeUnique<testing::StrictMock<MockSyncChangeProcessor>>(); | |
1027 | |
1028 EXPECT_CALL(*wrapper->password_store(), FillAutofillableLogins(_)) | |
1029 .WillOnce( | |
1030 AppendForm(*CreatePasswordFormFromDataForTesting(local_data))); | |
1031 EXPECT_CALL(*wrapper->password_store(), FillBlacklistLogins(_)) | |
1032 .WillOnce(Return(true)); | |
1033 if (!local_data_correct || latest_data == &sync_data) { | |
1034 if (local_data_correct) { | |
1035 EXPECT_CALL(*wrapper->password_store(), | |
1036 UpdateLoginImpl(FormWithCorrectTag(*latest_data))); | |
1037 } else { | |
1038 EXPECT_CALL(*wrapper->password_store(), | |
1039 AddLoginImpl(FormWithCorrectTag(*latest_data))); | |
1040 } | |
1041 } | |
1042 if (!local_data_correct && latest_data == &sync_data) { | |
1043 EXPECT_CALL(*wrapper->password_store(), | |
1044 UpdateLoginImpl(FormWithAndroidAutofillTag(*latest_data))); | |
1045 } else if (local_data_correct && !sync_data_correct) { | |
1046 EXPECT_CALL(*wrapper->password_store(), | |
1047 AddLoginImpl(FormWithAndroidAutofillTag(*latest_data))); | |
1048 } | |
1049 | |
1050 std::vector<std::pair<SyncChange::SyncChangeType, autofill::PasswordForm>> | |
1051 expected_sync_updates; | |
1052 // Deal with the correct sync entry and incorrect one. | |
1053 if (sync_data_correct) { | |
1054 if (latest_data != &sync_data) { | |
1055 expected_sync_updates.push_back(std::make_pair( | |
1056 SyncChange::ACTION_UPDATE, FormWithCorrectTag(*latest_data))); | |
1057 } | |
1058 if (!local_data_correct) { | |
1059 expected_sync_updates.push_back( | |
1060 std::make_pair(SyncChange::ACTION_ADD, | |
1061 FormWithAndroidAutofillTag(*latest_data))); | |
1062 } | |
1063 } else { | |
1064 expected_sync_updates.push_back(std::make_pair( | |
1065 SyncChange::ACTION_ADD, FormWithCorrectTag(*latest_data))); | |
1066 if (latest_data != &sync_data) { | |
1067 expected_sync_updates.push_back( | |
1068 std::make_pair(SyncChange::ACTION_UPDATE, | |
1069 FormWithAndroidAutofillTag(*latest_data))); | |
1070 } | |
1071 } | |
1072 | |
1073 // Set expectation on |processor|. | |
1074 if (expected_sync_updates.size() == 0) { | |
1075 EXPECT_CALL(*processor, ProcessSyncChanges(_, IsEmpty())); | |
1076 } else if (expected_sync_updates.size() == 1) { | |
1077 EXPECT_CALL( | |
1078 *processor, | |
1079 ProcessSyncChanges( | |
1080 _, ElementsAre(SyncChangeIs(expected_sync_updates[0].first, | |
1081 expected_sync_updates[0].second)))); | |
1082 } else if (expected_sync_updates.size() == 2) { | |
1083 EXPECT_CALL(*processor, | |
1084 ProcessSyncChanges( | |
1085 _, UnorderedElementsAre( | |
1086 SyncChangeIs(expected_sync_updates[0].first, | |
1087 expected_sync_updates[0].second), | |
1088 SyncChangeIs(expected_sync_updates[1].first, | |
1089 expected_sync_updates[1].second)))); | |
1090 } | |
1091 | |
1092 SyncDataList sync_list = {SyncDataFromPassword( | |
1093 *CreatePasswordFormFromDataForTesting(sync_data))}; | |
1094 wrapper->service()->MergeDataAndStartSyncing( | |
1095 syncer::PASSWORDS, sync_list, std::move(processor), | |
1096 std::unique_ptr<syncer::SyncErrorFactory>()); | |
1097 wrapper.reset(); | |
1098 // Wait til PasswordStore is destroy end therefore all the expectations on | |
1099 // it are checked. | |
1100 scoped_task_environment_.RunUntilIdle(); | |
1101 } | |
1102 } | |
1103 } | |
1104 | |
1105 TEST_F(PasswordSyncableServiceAndroidAutofillTest, OneEntryOnly) { | |
1106 for (int i = 0; i < 3; ++i) { | |
1107 // The case when only local incorrect entry exists is excluded. It's very | |
1108 // exotic because a local incorrect entry can come only from the server. | |
1109 // In such a case a copy will be uploaded to the server and next | |
1110 // MergeDataAndStartSyncing will do a proper migration. | |
1111 SCOPED_TRACE(i); | |
1112 // Whether the entry exists: Sync correct, Sync Android autofill, | |
1113 // local correct, local incorrect. | |
1114 const bool entry_is_correct = i == 0 || i == 2; | |
1115 const bool entry_is_local = i >= 2; | |
1116 PasswordFormData data = | |
1117 entry_is_correct ? android_correct(100) : android_incorrect(100); | |
1118 | |
1119 auto wrapper = base::MakeUnique<PasswordSyncableServiceWrapper>(); | |
1120 auto processor = | |
1121 base::MakeUnique<testing::StrictMock<MockSyncChangeProcessor>>(); | |
1122 | |
1123 if (entry_is_local) { | |
1124 EXPECT_CALL(*wrapper->password_store(), FillAutofillableLogins(_)) | |
1125 .WillOnce(AppendForm(*CreatePasswordFormFromDataForTesting(data))); | |
1126 } else { | |
1127 EXPECT_CALL(*wrapper->password_store(), FillAutofillableLogins(_)) | |
1128 .WillOnce(Return(true)); | |
1129 } | |
1130 EXPECT_CALL(*wrapper->password_store(), FillBlacklistLogins(_)) | |
1131 .WillOnce(Return(true)); | |
1132 if (!entry_is_local && !entry_is_correct) { | |
1133 EXPECT_CALL(*wrapper->password_store(), | |
1134 AddLoginImpl(FormWithAndroidAutofillTag(data))); | |
1135 } | |
1136 if (!entry_is_local) { | |
1137 EXPECT_CALL(*wrapper->password_store(), | |
1138 AddLoginImpl(FormWithCorrectTag(data))); | |
1139 } | |
1140 | |
1141 if (entry_is_correct && !entry_is_local) { | |
1142 EXPECT_CALL(*processor, ProcessSyncChanges(_, IsEmpty())); | |
1143 } else { | |
1144 EXPECT_CALL(*processor, | |
1145 ProcessSyncChanges( | |
1146 _, ElementsAre(SyncChangeIs(SyncChange::ACTION_ADD, | |
1147 FormWithCorrectTag(data))))); | |
1148 } | |
1149 | |
1150 SyncDataList sync_list; | |
1151 if (!entry_is_local) { | |
1152 sync_list.push_back( | |
1153 SyncDataFromPassword(*CreatePasswordFormFromDataForTesting(data))); | |
1154 } | |
1155 wrapper->service()->MergeDataAndStartSyncing( | |
1156 syncer::PASSWORDS, sync_list, std::move(processor), | |
1157 std::unique_ptr<syncer::SyncErrorFactory>()); | |
1158 wrapper.reset(); | |
1159 // Wait til PasswordStore is destroy end therefore all the expectations on | |
1160 // it are checked. | |
1161 scoped_task_environment_.RunUntilIdle(); | |
1162 } | |
1163 } | |
1164 | |
1165 TEST_F(PasswordSyncableServiceAndroidAutofillTest, FourEqualEntries) { | |
1166 // Sync correct, Sync Android autofill, local correct, local incorrect with | |
1167 // the same content. Nothing should happen. | |
1168 const PasswordFormData data = android_correct(100); | |
1169 autofill::PasswordForm local_correct = FormWithCorrectTag(data); | |
1170 autofill::PasswordForm local_incorrect = FormWithAndroidAutofillTag(data); | |
1171 syncer::SyncData sync_correct = SyncDataFromPassword(local_correct); | |
1172 syncer::SyncData sync_incorrect = SyncDataFromPassword(local_incorrect); | |
1173 | |
1174 for (bool correct_sync_first : {true, false}) { | |
1175 auto wrapper = base::MakeUnique<PasswordSyncableServiceWrapper>(); | |
1176 auto processor = | |
1177 base::MakeUnique<testing::StrictMock<MockSyncChangeProcessor>>(); | |
1178 | |
1179 std::vector<autofill::PasswordForm> stored_forms = {local_correct, | |
1180 local_incorrect}; | |
1181 EXPECT_CALL(*wrapper->password_store(), FillAutofillableLogins(_)) | |
1182 .WillOnce(AppendForms(stored_forms)); | |
1183 EXPECT_CALL(*wrapper->password_store(), FillBlacklistLogins(_)) | |
1184 .WillOnce(Return(true)); | |
1185 EXPECT_CALL(*processor, ProcessSyncChanges(_, IsEmpty())); | |
1186 | |
1187 SyncDataList sync_list = {sync_correct, sync_incorrect}; | |
1188 if (!correct_sync_first) | |
1189 std::swap(sync_list[0], sync_list[1]); | |
1190 wrapper->service()->MergeDataAndStartSyncing( | |
1191 syncer::PASSWORDS, sync_list, std::move(processor), | |
1192 std::unique_ptr<syncer::SyncErrorFactory>()); | |
1193 wrapper.reset(); | |
1194 // Wait til PasswordStore is destroy end therefore all the expectations on | |
1195 // it are checked. | |
1196 scoped_task_environment_.RunUntilIdle(); | |
1197 } | |
1198 } | |
1199 | |
1200 TEST_F(PasswordSyncableServiceAndroidAutofillTest, AndroidCorrectEqualEntries) { | |
1201 // Sync correct, local correct with the same content. Nothing should happen. | |
1202 const PasswordFormData data = android_correct(100); | |
1203 autofill::PasswordForm local_correct = FormWithCorrectTag(data); | |
1204 syncer::SyncData sync_correct = SyncDataFromPassword(local_correct); | |
1205 | |
1206 auto wrapper = base::MakeUnique<PasswordSyncableServiceWrapper>(); | |
1207 auto processor = | |
1208 base::MakeUnique<testing::StrictMock<MockSyncChangeProcessor>>(); | |
1209 | |
1210 EXPECT_CALL(*wrapper->password_store(), FillAutofillableLogins(_)) | |
1211 .WillOnce(AppendForm(local_correct)); | |
1212 EXPECT_CALL(*wrapper->password_store(), FillBlacklistLogins(_)) | |
1213 .WillOnce(Return(true)); | |
1214 EXPECT_CALL(*processor, ProcessSyncChanges(_, IsEmpty())); | |
1215 | |
1216 wrapper->service()->MergeDataAndStartSyncing( | |
1217 syncer::PASSWORDS, {sync_correct}, std::move(processor), | |
1218 std::unique_ptr<syncer::SyncErrorFactory>()); | |
1219 wrapper.reset(); | |
1220 // Wait til PasswordStore is destroy end therefore all the expectations on | |
1221 // it are checked. | |
1222 scoped_task_environment_.RunUntilIdle(); | |
1223 } | |
1224 | |
676 } // namespace | 1225 } // namespace |
677 | 1226 |
678 } // namespace password_manager | 1227 } // namespace password_manager |
OLD | NEW |