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"
7 #include "base/compiler_specific.h"
8 #include "base/strings/stringprintf.h"
9 #include "base/strings/utf_string_conversions.h"
10 #include "base/synchronization/waitable_event.h"
11 #include "base/time/time.h"
12 #include "chrome/browser/password_manager/password_store_factory.h"
13 #include "chrome/browser/sync/profile_sync_service.h"
14 #include "chrome/browser/sync/profile_sync_service_factory.h"
15 #include "chrome/browser/sync/test/integration/multi_client_status_change_checker.h"
16 #include "chrome/browser/sync/test/integration/profile_sync_service_harness.h"
17 #include "chrome/browser/sync/test/integration/single_client_status_change_checker.h"
18 #include "chrome/browser/sync/test/integration/sync_datatype_helper.h"
19 #include "components/password_manager/core/browser/password_manager_test_utils.h"
20 #include "components/password_manager/core/browser/password_store.h"
21 #include "components/password_manager/core/browser/password_store_consumer.h"
22 #include "content/public/test/test_utils.h"
24 using autofill::PasswordForm
;
25 using password_manager::PasswordStore
;
26 using sync_datatype_helper::test
;
30 const char kFakeSignonRealm
[] = "http://fake-signon-realm.google.com/";
31 const char kIndexedFakeOrigin
[] = "http://fake-signon-realm.google.com/%d";
33 // We use a WaitableEvent to wait when logins are added, removed, or updated
34 // instead of running the UI message loop because of a restriction that
35 // prevents a DB thread from initiating a quit of the UI message loop.
36 void PasswordStoreCallback(base::WaitableEvent
* wait_event
) {
37 // Wake up passwords_helper::AddLogin.
41 class PasswordStoreConsumerHelper
42 : public password_manager::PasswordStoreConsumer
{
44 PasswordStoreConsumerHelper() {}
46 void OnGetPasswordStoreResults(ScopedVector
<PasswordForm
> results
) override
{
47 result_
.swap(results
);
48 // Quit the message loop to wake up passwords_helper::GetLogins.
49 base::MessageLoopForUI::current()->Quit();
52 ScopedVector
<PasswordForm
> result() { return result_
.Pass(); }
55 ScopedVector
<PasswordForm
> result_
;
57 DISALLOW_COPY_AND_ASSIGN(PasswordStoreConsumerHelper
);
60 // PasswordForm::date_synced is a local field. Therefore it may be different
62 void ClearSyncDateField(std::vector
<PasswordForm
*>* forms
) {
63 for (PasswordForm
* form
: *forms
) {
64 form
->date_synced
= base::Time();
70 namespace passwords_helper
{
72 void AddLogin(PasswordStore
* store
, const PasswordForm
& form
) {
74 base::WaitableEvent
wait_event(true, false);
75 store
->AddLogin(form
);
76 store
->ScheduleTask(base::Bind(&PasswordStoreCallback
, &wait_event
));
80 void UpdateLogin(PasswordStore
* store
, const PasswordForm
& form
) {
82 base::WaitableEvent
wait_event(true, false);
83 store
->UpdateLogin(form
);
84 store
->ScheduleTask(base::Bind(&PasswordStoreCallback
, &wait_event
));
88 ScopedVector
<PasswordForm
> GetLogins(PasswordStore
* store
) {
90 PasswordForm matcher_form
;
91 matcher_form
.signon_realm
= kFakeSignonRealm
;
92 PasswordStoreConsumerHelper consumer
;
93 store
->GetLogins(matcher_form
, PasswordStore::DISALLOW_PROMPT
, &consumer
);
94 content::RunMessageLoop();
95 return consumer
.result();
98 void RemoveLogin(PasswordStore
* store
, const PasswordForm
& form
) {
100 base::WaitableEvent
wait_event(true, false);
101 store
->RemoveLogin(form
);
102 store
->ScheduleTask(base::Bind(&PasswordStoreCallback
, &wait_event
));
106 void RemoveLogins(PasswordStore
* store
) {
107 ScopedVector
<PasswordForm
> forms
= GetLogins(store
);
108 for (const PasswordForm
* form
: forms
) {
109 RemoveLogin(store
, *form
);
113 void SetEncryptionPassphrase(int index
,
114 const std::string
& passphrase
,
115 ProfileSyncService::PassphraseType type
) {
116 ProfileSyncServiceFactory::GetForProfile(
117 test()->GetProfile(index
))->SetEncryptionPassphrase(passphrase
, type
);
120 bool SetDecryptionPassphrase(int index
, const std::string
& passphrase
) {
121 return ProfileSyncServiceFactory::GetForProfile(
122 test()->GetProfile(index
))->SetDecryptionPassphrase(passphrase
);
125 PasswordStore
* GetPasswordStore(int index
) {
126 return PasswordStoreFactory::GetForProfile(test()->GetProfile(index
),
127 ServiceAccessType::IMPLICIT_ACCESS
)
131 PasswordStore
* GetVerifierPasswordStore() {
132 return PasswordStoreFactory::GetForProfile(
133 test()->verifier(), ServiceAccessType::IMPLICIT_ACCESS
).get();
136 bool ProfileContainsSamePasswordFormsAsVerifier(int index
) {
137 ScopedVector
<PasswordForm
> verifier_forms
=
138 GetLogins(GetVerifierPasswordStore());
139 ScopedVector
<PasswordForm
> forms
= GetLogins(GetPasswordStore(index
));
140 ClearSyncDateField(&forms
.get());
141 bool result
= password_manager::ContainsSamePasswordFormsPtr(
142 verifier_forms
.get(), forms
.get());
144 VLOG(1) << "Password forms in Verifier Profile:";
145 for (const PasswordForm
* form
: verifier_forms
) {
148 VLOG(1) << "Password forms in Profile" << index
<< ":";
149 for (const PasswordForm
* form
: forms
) {
156 bool ProfilesContainSamePasswordForms(int index_a
, int index_b
) {
157 ScopedVector
<PasswordForm
> forms_a
= GetLogins(GetPasswordStore(index_a
));
158 ScopedVector
<PasswordForm
> forms_b
= GetLogins(GetPasswordStore(index_b
));
159 ClearSyncDateField(&forms_a
.get());
160 ClearSyncDateField(&forms_b
.get());
161 bool result
= password_manager::ContainsSamePasswordFormsPtr(forms_a
.get(),
164 VLOG(1) << "Password forms in Profile" << index_a
<< ":";
165 for (const PasswordForm
* form
: forms_a
) {
168 VLOG(1) << "Password forms in Profile" << index_b
<< ":";
169 for (const PasswordForm
* form
: forms_b
) {
176 bool AllProfilesContainSamePasswordFormsAsVerifier() {
177 for (int i
= 0; i
< test()->num_clients(); ++i
) {
178 if (!ProfileContainsSamePasswordFormsAsVerifier(i
)) {
179 DVLOG(1) << "Profile " << i
<< " does not contain the same password"
180 " forms as the verifier.";
187 bool AllProfilesContainSamePasswordForms() {
188 for (int i
= 1; i
< test()->num_clients(); ++i
) {
189 if (!ProfilesContainSamePasswordForms(0, i
)) {
190 DVLOG(1) << "Profile " << i
<< " does not contain the same password"
191 " forms as Profile 0.";
200 // Helper class used in the implementation of
201 // AwaitAllProfilesContainSamePasswordForms.
202 class SamePasswordFormsChecker
: public MultiClientStatusChangeChecker
{
204 SamePasswordFormsChecker();
205 ~SamePasswordFormsChecker() override
;
207 bool IsExitConditionSatisfied() override
;
208 std::string
GetDebugMessage() const override
;
215 SamePasswordFormsChecker::SamePasswordFormsChecker()
216 : MultiClientStatusChangeChecker(
217 sync_datatype_helper::test()->GetSyncServices()),
219 needs_recheck_(false) {}
221 SamePasswordFormsChecker::~SamePasswordFormsChecker() {}
223 // This method needs protection against re-entrancy.
225 // This function indirectly calls GetLogins(), which starts a RunLoop on the UI
226 // thread. This can be a problem, since the next task to execute could very
227 // well contain a ProfileSyncService::OnStateChanged() event, which would
228 // trigger another call to this here function, and start another layer of
229 // nested RunLoops. That makes the StatusChangeChecker's Quit() method
232 // The work-around is to not allow re-entrancy. But we can't just drop
233 // IsExitConditionSatisifed() calls if one is already in progress. Instead, we
234 // set a flag to ask the current execution of IsExitConditionSatisfied() to be
235 // re-run. This ensures that the return value is always based on the most
237 bool SamePasswordFormsChecker::IsExitConditionSatisfied() {
239 LOG(WARNING
) << "Setting flag and returning early to prevent nesting.";
240 needs_recheck_
= true;
244 // Keep retrying until we get a good reading.
248 needs_recheck_
= false;
249 result
= AllProfilesContainSamePasswordForms();
250 } while (needs_recheck_
);
251 in_progress_
= false;
255 std::string
SamePasswordFormsChecker::GetDebugMessage() const {
256 return "Waiting for matching passwords";
261 bool AwaitAllProfilesContainSamePasswordForms() {
262 SamePasswordFormsChecker checker
;
264 return !checker
.TimedOut();
269 // Helper class used in the implementation of
270 // AwaitProfileContainSamePasswordFormsAsVerifier.
271 class SamePasswordFormsAsVerifierChecker
272 : public SingleClientStatusChangeChecker
{
274 explicit SamePasswordFormsAsVerifierChecker(int index
);
275 ~SamePasswordFormsAsVerifierChecker() override
;
277 bool IsExitConditionSatisfied() override
;
278 std::string
GetDebugMessage() const override
;
287 SamePasswordFormsAsVerifierChecker::SamePasswordFormsAsVerifierChecker(int i
)
288 : SingleClientStatusChangeChecker(
289 sync_datatype_helper::test()->GetSyncService(i
)),
292 needs_recheck_(false) {
295 SamePasswordFormsAsVerifierChecker::~SamePasswordFormsAsVerifierChecker() {
298 // This method uses the same re-entrancy prevention trick as
299 // the SamePasswordFormsChecker.
300 bool SamePasswordFormsAsVerifierChecker::IsExitConditionSatisfied() {
302 LOG(WARNING
) << "Setting flag and returning early to prevent nesting.";
303 needs_recheck_
= true;
307 // Keep retrying until we get a good reading.
311 needs_recheck_
= false;
312 result
= ProfileContainsSamePasswordFormsAsVerifier(index_
);
313 } while (needs_recheck_
);
314 in_progress_
= false;
318 std::string
SamePasswordFormsAsVerifierChecker::GetDebugMessage() const {
319 return "Waiting for passwords to match verifier";
324 bool AwaitProfileContainsSamePasswordFormsAsVerifier(int index
) {
325 SamePasswordFormsAsVerifierChecker
checker(index
);
327 return !checker
.TimedOut();
330 int GetPasswordCount(int index
) {
331 ScopedVector
<PasswordForm
> forms
= GetLogins(GetPasswordStore(index
));
335 int GetVerifierPasswordCount() {
336 ScopedVector
<PasswordForm
> verifier_forms
=
337 GetLogins(GetVerifierPasswordStore());
338 return verifier_forms
.size();
341 PasswordForm
CreateTestPasswordForm(int index
) {
343 form
.signon_realm
= kFakeSignonRealm
;
344 form
.origin
= GURL(base::StringPrintf(kIndexedFakeOrigin
, index
));
345 form
.username_value
=
346 base::ASCIIToUTF16(base::StringPrintf("username%d", index
));
347 form
.password_value
=
348 base::ASCIIToUTF16(base::StringPrintf("password%d", index
));
349 form
.date_created
= base::Time::Now();
353 } // namespace passwords_helper