1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/sync/test/integration/passwords_helper.h"
9 #include "base/compiler_specific.h"
10 #include "base/strings/stringprintf.h"
11 #include "base/strings/utf_string_conversions.h"
12 #include "base/synchronization/waitable_event.h"
13 #include "base/time/time.h"
14 #include "chrome/browser/password_manager/password_store_factory.h"
15 #include "chrome/browser/sync/profile_sync_service.h"
16 #include "chrome/browser/sync/profile_sync_service_factory.h"
17 #include "chrome/browser/sync/test/integration/multi_client_status_change_checker.h"
18 #include "chrome/browser/sync/test/integration/profile_sync_service_harness.h"
19 #include "chrome/browser/sync/test/integration/single_client_status_change_checker.h"
20 #include "chrome/browser/sync/test/integration/sync_datatype_helper.h"
21 #include "components/password_manager/core/browser/password_manager_test_utils.h"
22 #include "components/password_manager/core/browser/password_store.h"
23 #include "components/password_manager/core/browser/password_store_consumer.h"
24 #include "content/public/test/test_utils.h"
26 using autofill::PasswordForm
;
27 using password_manager::PasswordStore
;
28 using sync_datatype_helper::test
;
32 const char kFakeSignonRealm
[] = "http://fake-signon-realm.google.com/";
33 const char kIndexedFakeOrigin
[] = "http://fake-signon-realm.google.com/%d";
35 // We use a WaitableEvent to wait when logins are added, removed, or updated
36 // instead of running the UI message loop because of a restriction that
37 // prevents a DB thread from initiating a quit of the UI message loop.
38 void PasswordStoreCallback(base::WaitableEvent
* wait_event
) {
39 // Wake up passwords_helper::AddLogin.
43 class PasswordStoreConsumerHelper
44 : public password_manager::PasswordStoreConsumer
{
46 PasswordStoreConsumerHelper() {}
48 void OnGetPasswordStoreResults(ScopedVector
<PasswordForm
> results
) override
{
49 result_
.swap(results
);
50 // Quit the message loop to wake up passwords_helper::GetLogins.
51 base::MessageLoopForUI::current()->Quit();
54 ScopedVector
<PasswordForm
> result() { return result_
.Pass(); }
57 ScopedVector
<PasswordForm
> result_
;
59 DISALLOW_COPY_AND_ASSIGN(PasswordStoreConsumerHelper
);
62 // PasswordForm::date_synced is a local field. Therefore it may be different
64 void ClearSyncDateField(std::vector
<PasswordForm
*>* forms
) {
65 for (PasswordForm
* form
: *forms
) {
66 form
->date_synced
= base::Time();
72 namespace passwords_helper
{
74 void AddLogin(PasswordStore
* store
, const PasswordForm
& form
) {
76 base::WaitableEvent
wait_event(true, false);
77 store
->AddLogin(form
);
78 store
->ScheduleTask(base::Bind(&PasswordStoreCallback
, &wait_event
));
82 void UpdateLogin(PasswordStore
* store
, const PasswordForm
& form
) {
84 base::WaitableEvent
wait_event(true, false);
85 store
->UpdateLogin(form
);
86 store
->ScheduleTask(base::Bind(&PasswordStoreCallback
, &wait_event
));
90 ScopedVector
<PasswordForm
> GetLogins(PasswordStore
* store
) {
92 PasswordForm matcher_form
;
93 matcher_form
.signon_realm
= kFakeSignonRealm
;
94 PasswordStoreConsumerHelper consumer
;
95 store
->GetLogins(matcher_form
, PasswordStore::DISALLOW_PROMPT
, &consumer
);
96 content::RunMessageLoop();
97 return consumer
.result();
100 void RemoveLogin(PasswordStore
* store
, const PasswordForm
& form
) {
102 base::WaitableEvent
wait_event(true, false);
103 store
->RemoveLogin(form
);
104 store
->ScheduleTask(base::Bind(&PasswordStoreCallback
, &wait_event
));
108 void RemoveLogins(PasswordStore
* store
) {
109 ScopedVector
<PasswordForm
> forms
= GetLogins(store
);
110 for (const PasswordForm
* form
: forms
) {
111 RemoveLogin(store
, *form
);
115 void SetEncryptionPassphrase(int index
,
116 const std::string
& passphrase
,
117 ProfileSyncService::PassphraseType type
) {
118 ProfileSyncServiceFactory::GetForProfile(
119 test()->GetProfile(index
))->SetEncryptionPassphrase(passphrase
, type
);
122 bool SetDecryptionPassphrase(int index
, const std::string
& passphrase
) {
123 return ProfileSyncServiceFactory::GetForProfile(
124 test()->GetProfile(index
))->SetDecryptionPassphrase(passphrase
);
127 PasswordStore
* GetPasswordStore(int index
) {
128 return PasswordStoreFactory::GetForProfile(test()->GetProfile(index
),
129 ServiceAccessType::IMPLICIT_ACCESS
)
133 PasswordStore
* GetVerifierPasswordStore() {
134 return PasswordStoreFactory::GetForProfile(
135 test()->verifier(), ServiceAccessType::IMPLICIT_ACCESS
).get();
138 bool ProfileContainsSamePasswordFormsAsVerifier(int index
) {
139 ScopedVector
<PasswordForm
> verifier_forms
=
140 GetLogins(GetVerifierPasswordStore());
141 ScopedVector
<PasswordForm
> forms
= GetLogins(GetPasswordStore(index
));
142 ClearSyncDateField(&forms
.get());
144 std::ostringstream mismatch_details_stream
;
145 bool is_matching
= password_manager::ContainsEqualPasswordFormsUnordered(
146 verifier_forms
.get(), forms
.get(), &mismatch_details_stream
);
148 VLOG(1) << "Profile " << index
149 << " does not contain the same Password forms as Verifier Profile.";
150 VLOG(1) << mismatch_details_stream
.str();
155 bool ProfilesContainSamePasswordForms(int index_a
, int index_b
) {
156 ScopedVector
<PasswordForm
> forms_a
= GetLogins(GetPasswordStore(index_a
));
157 ScopedVector
<PasswordForm
> forms_b
= GetLogins(GetPasswordStore(index_b
));
158 ClearSyncDateField(&forms_a
.get());
159 ClearSyncDateField(&forms_b
.get());
161 std::ostringstream mismatch_details_stream
;
162 bool is_matching
= password_manager::ContainsEqualPasswordFormsUnordered(
163 forms_a
.get(), forms_b
.get(), &mismatch_details_stream
);
165 VLOG(1) << "Password forms in Profile " << index_a
166 << " (listed as 'expected forms' below)"
167 << " do not match those in Profile " << index_b
168 << " (listed as 'actual forms' below)";
169 VLOG(1) << mismatch_details_stream
.str();
174 bool AllProfilesContainSamePasswordFormsAsVerifier() {
175 for (int i
= 0; i
< test()->num_clients(); ++i
) {
176 if (!ProfileContainsSamePasswordFormsAsVerifier(i
)) {
177 DVLOG(1) << "Profile " << i
<< " does not contain the same password"
178 " forms as the verifier.";
185 bool AllProfilesContainSamePasswordForms() {
186 for (int i
= 1; i
< test()->num_clients(); ++i
) {
187 if (!ProfilesContainSamePasswordForms(0, i
)) {
188 DVLOG(1) << "Profile " << i
<< " does not contain the same password"
189 " forms as Profile 0.";
198 // Helper class used in the implementation of
199 // AwaitAllProfilesContainSamePasswordForms.
200 class SamePasswordFormsChecker
: public MultiClientStatusChangeChecker
{
202 SamePasswordFormsChecker();
203 ~SamePasswordFormsChecker() override
;
205 bool IsExitConditionSatisfied() override
;
206 std::string
GetDebugMessage() const override
;
213 SamePasswordFormsChecker::SamePasswordFormsChecker()
214 : MultiClientStatusChangeChecker(
215 sync_datatype_helper::test()->GetSyncServices()),
217 needs_recheck_(false) {}
219 SamePasswordFormsChecker::~SamePasswordFormsChecker() {}
221 // This method needs protection against re-entrancy.
223 // This function indirectly calls GetLogins(), which starts a RunLoop on the UI
224 // thread. This can be a problem, since the next task to execute could very
225 // well contain a ProfileSyncService::OnStateChanged() event, which would
226 // trigger another call to this here function, and start another layer of
227 // nested RunLoops. That makes the StatusChangeChecker's Quit() method
230 // The work-around is to not allow re-entrancy. But we can't just drop
231 // IsExitConditionSatisifed() calls if one is already in progress. Instead, we
232 // set a flag to ask the current execution of IsExitConditionSatisfied() to be
233 // re-run. This ensures that the return value is always based on the most
235 bool SamePasswordFormsChecker::IsExitConditionSatisfied() {
237 LOG(WARNING
) << "Setting flag and returning early to prevent nesting.";
238 needs_recheck_
= true;
242 // Keep retrying until we get a good reading.
246 needs_recheck_
= false;
247 result
= AllProfilesContainSamePasswordForms();
248 } while (needs_recheck_
);
249 in_progress_
= false;
253 std::string
SamePasswordFormsChecker::GetDebugMessage() const {
254 return "Waiting for matching passwords";
259 bool AwaitAllProfilesContainSamePasswordForms() {
260 SamePasswordFormsChecker checker
;
262 return !checker
.TimedOut();
267 // Helper class used in the implementation of
268 // AwaitProfileContainSamePasswordFormsAsVerifier.
269 class SamePasswordFormsAsVerifierChecker
270 : public SingleClientStatusChangeChecker
{
272 explicit SamePasswordFormsAsVerifierChecker(int index
);
273 ~SamePasswordFormsAsVerifierChecker() override
;
275 bool IsExitConditionSatisfied() override
;
276 std::string
GetDebugMessage() const override
;
285 SamePasswordFormsAsVerifierChecker::SamePasswordFormsAsVerifierChecker(int i
)
286 : SingleClientStatusChangeChecker(
287 sync_datatype_helper::test()->GetSyncService(i
)),
290 needs_recheck_(false) {
293 SamePasswordFormsAsVerifierChecker::~SamePasswordFormsAsVerifierChecker() {
296 // This method uses the same re-entrancy prevention trick as
297 // the SamePasswordFormsChecker.
298 bool SamePasswordFormsAsVerifierChecker::IsExitConditionSatisfied() {
300 LOG(WARNING
) << "Setting flag and returning early to prevent nesting.";
301 needs_recheck_
= true;
305 // Keep retrying until we get a good reading.
309 needs_recheck_
= false;
310 result
= ProfileContainsSamePasswordFormsAsVerifier(index_
);
311 } while (needs_recheck_
);
312 in_progress_
= false;
316 std::string
SamePasswordFormsAsVerifierChecker::GetDebugMessage() const {
317 return "Waiting for passwords to match verifier";
322 bool AwaitProfileContainsSamePasswordFormsAsVerifier(int index
) {
323 SamePasswordFormsAsVerifierChecker
checker(index
);
325 return !checker
.TimedOut();
328 int GetPasswordCount(int index
) {
329 ScopedVector
<PasswordForm
> forms
= GetLogins(GetPasswordStore(index
));
333 int GetVerifierPasswordCount() {
334 ScopedVector
<PasswordForm
> verifier_forms
=
335 GetLogins(GetVerifierPasswordStore());
336 return verifier_forms
.size();
339 PasswordForm
CreateTestPasswordForm(int index
) {
341 form
.signon_realm
= kFakeSignonRealm
;
342 form
.origin
= GURL(base::StringPrintf(kIndexedFakeOrigin
, index
));
343 form
.username_value
=
344 base::ASCIIToUTF16(base::StringPrintf("username%d", index
));
345 form
.password_value
=
346 base::ASCIIToUTF16(base::StringPrintf("password%d", index
));
347 form
.date_created
= base::Time::Now();
351 } // namespace passwords_helper