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 "chrome/test/base/ui_test_utils.h"
20 #include "components/password_manager/core/browser/password_form_data.h"
21 #include "components/password_manager/core/browser/password_store.h"
22 #include "components/password_manager/core/browser/password_store_consumer.h"
24 using autofill::PasswordForm
;
25 using password_manager::PasswordStore
;
26 using sync_datatype_helper::test
;
28 const std::string kFakeSignonRealm
= "http://fake-signon-realm.google.com/";
29 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 explicit PasswordStoreConsumerHelper(std::vector
<PasswordForm
>* result
)
45 : password_manager::PasswordStoreConsumer(), result_(result
) {}
47 virtual void OnGetPasswordStoreResults(
48 const std::vector
<PasswordForm
*>& result
) OVERRIDE
{
50 for (std::vector
<PasswordForm
*>::const_iterator it
= result
.begin();
53 result_
->push_back(**it
);
57 // Quit the message loop to wake up passwords_helper::GetLogins.
58 base::MessageLoopForUI::current()->Quit();
62 std::vector
<PasswordForm
>* result_
;
64 DISALLOW_COPY_AND_ASSIGN(PasswordStoreConsumerHelper
);
67 // PasswordForm::date_synced is a local field. Therefore it may be different
69 void ClearSyncDateField(std::vector
<PasswordForm
>* forms
) {
70 for (std::vector
<PasswordForm
>::iterator it
= forms
->begin();
73 it
->date_synced
= base::Time();
79 namespace passwords_helper
{
81 void AddLogin(PasswordStore
* store
, const PasswordForm
& form
) {
83 base::WaitableEvent
wait_event(true, false);
84 store
->AddLogin(form
);
85 store
->ScheduleTask(base::Bind(&PasswordStoreCallback
, &wait_event
));
89 void UpdateLogin(PasswordStore
* store
, const PasswordForm
& form
) {
91 base::WaitableEvent
wait_event(true, false);
92 store
->UpdateLogin(form
);
93 store
->ScheduleTask(base::Bind(&PasswordStoreCallback
, &wait_event
));
97 void GetLogins(PasswordStore
* store
, std::vector
<PasswordForm
>& matches
) {
99 PasswordForm matcher_form
;
100 matcher_form
.signon_realm
= kFakeSignonRealm
;
101 PasswordStoreConsumerHelper
consumer(&matches
);
102 store
->GetLogins(matcher_form
, PasswordStore::DISALLOW_PROMPT
, &consumer
);
103 content::RunMessageLoop();
106 void RemoveLogin(PasswordStore
* store
, const PasswordForm
& form
) {
108 base::WaitableEvent
wait_event(true, false);
109 store
->RemoveLogin(form
);
110 store
->ScheduleTask(base::Bind(&PasswordStoreCallback
, &wait_event
));
114 void RemoveLogins(PasswordStore
* store
) {
115 std::vector
<PasswordForm
> forms
;
116 GetLogins(store
, forms
);
117 for (std::vector
<PasswordForm
>::iterator it
= forms
.begin();
118 it
!= forms
.end(); ++it
) {
119 RemoveLogin(store
, *it
);
123 void SetEncryptionPassphrase(int index
,
124 const std::string
& passphrase
,
125 ProfileSyncService::PassphraseType type
) {
126 ProfileSyncServiceFactory::GetForProfile(
127 test()->GetProfile(index
))->SetEncryptionPassphrase(passphrase
, type
);
130 bool SetDecryptionPassphrase(int index
, const std::string
& passphrase
) {
131 return ProfileSyncServiceFactory::GetForProfile(
132 test()->GetProfile(index
))->SetDecryptionPassphrase(passphrase
);
135 PasswordStore
* GetPasswordStore(int index
) {
136 return PasswordStoreFactory::GetForProfile(test()->GetProfile(index
),
137 Profile::IMPLICIT_ACCESS
).get();
140 PasswordStore
* GetVerifierPasswordStore() {
141 return PasswordStoreFactory::GetForProfile(test()->verifier(),
142 Profile::IMPLICIT_ACCESS
).get();
145 bool ProfileContainsSamePasswordFormsAsVerifier(int index
) {
146 std::vector
<PasswordForm
> verifier_forms
;
147 std::vector
<PasswordForm
> forms
;
148 GetLogins(GetVerifierPasswordStore(), verifier_forms
);
149 GetLogins(GetPasswordStore(index
), forms
);
150 ClearSyncDateField(&forms
);
152 password_manager::ContainsSamePasswordForms(verifier_forms
, forms
);
154 LOG(ERROR
) << "Password forms in Verifier Profile:";
155 for (std::vector
<PasswordForm
>::iterator it
= verifier_forms
.begin();
156 it
!= verifier_forms
.end(); ++it
) {
157 LOG(ERROR
) << *it
<< std::endl
;
159 LOG(ERROR
) << "Password forms in Profile" << index
<< ":";
160 for (std::vector
<PasswordForm
>::iterator it
= forms
.begin();
161 it
!= forms
.end(); ++it
) {
162 LOG(ERROR
) << *it
<< std::endl
;
168 bool ProfilesContainSamePasswordForms(int index_a
, int index_b
) {
169 std::vector
<PasswordForm
> forms_a
;
170 std::vector
<PasswordForm
> forms_b
;
171 GetLogins(GetPasswordStore(index_a
), forms_a
);
172 GetLogins(GetPasswordStore(index_b
), forms_b
);
173 ClearSyncDateField(&forms_a
);
174 ClearSyncDateField(&forms_b
);
175 bool result
= password_manager::ContainsSamePasswordForms(forms_a
, forms_b
);
177 LOG(ERROR
) << "Password forms in Profile" << index_a
<< ":";
178 for (std::vector
<PasswordForm
>::iterator it
= forms_a
.begin();
179 it
!= forms_a
.end(); ++it
) {
180 LOG(ERROR
) << *it
<< std::endl
;
182 LOG(ERROR
) << "Password forms in Profile" << index_b
<< ":";
183 for (std::vector
<PasswordForm
>::iterator it
= forms_b
.begin();
184 it
!= forms_b
.end(); ++it
) {
185 LOG(ERROR
) << *it
<< std::endl
;
191 bool AllProfilesContainSamePasswordFormsAsVerifier() {
192 for (int i
= 0; i
< test()->num_clients(); ++i
) {
193 if (!ProfileContainsSamePasswordFormsAsVerifier(i
)) {
194 DVLOG(1) << "Profile " << i
<< " does not contain the same password"
195 " forms as the verifier.";
202 bool AllProfilesContainSamePasswordForms() {
203 for (int i
= 1; i
< test()->num_clients(); ++i
) {
204 if (!ProfilesContainSamePasswordForms(0, i
)) {
205 DVLOG(1) << "Profile " << i
<< " does not contain the same password"
206 " forms as Profile 0.";
215 // Helper class used in the implementation of
216 // AwaitAllProfilesContainSamePasswordForms.
217 class SamePasswordFormsChecker
: public MultiClientStatusChangeChecker
{
219 SamePasswordFormsChecker();
220 virtual ~SamePasswordFormsChecker();
222 virtual bool IsExitConditionSatisfied() OVERRIDE
;
223 virtual std::string
GetDebugMessage() const OVERRIDE
;
230 SamePasswordFormsChecker::SamePasswordFormsChecker()
231 : MultiClientStatusChangeChecker(
232 sync_datatype_helper::test()->GetSyncServices()),
234 needs_recheck_(false) {}
236 SamePasswordFormsChecker::~SamePasswordFormsChecker() {}
238 // This method needs protection against re-entrancy.
240 // This function indirectly calls GetLogins(), which starts a RunLoop on the UI
241 // thread. This can be a problem, since the next task to execute could very
242 // well contain a ProfileSyncService::OnStateChanged() event, which would
243 // trigger another call to this here function, and start another layer of
244 // nested RunLoops. That makes the StatusChangeChecker's Quit() method
247 // The work-around is to not allow re-entrancy. But we can't just drop
248 // IsExitConditionSatisifed() calls if one is already in progress. Instead, we
249 // set a flag to ask the current execution of IsExitConditionSatisfied() to be
250 // re-run. This ensures that the return value is always based on the most
252 bool SamePasswordFormsChecker::IsExitConditionSatisfied() {
254 LOG(WARNING
) << "Setting flag and returning early to prevent nesting.";
255 needs_recheck_
= true;
259 // Keep retrying until we get a good reading.
263 needs_recheck_
= false;
264 result
= AllProfilesContainSamePasswordForms();
265 } while (needs_recheck_
);
266 in_progress_
= false;
270 std::string
SamePasswordFormsChecker::GetDebugMessage() const {
271 return "Waiting for matching passwords";
276 bool AwaitAllProfilesContainSamePasswordForms() {
277 SamePasswordFormsChecker checker
;
279 return !checker
.TimedOut();
284 // Helper class used in the implementation of
285 // AwaitProfileContainSamePasswordFormsAsVerifier.
286 class SamePasswordFormsAsVerifierChecker
287 : public SingleClientStatusChangeChecker
{
289 explicit SamePasswordFormsAsVerifierChecker(int index
);
290 virtual ~SamePasswordFormsAsVerifierChecker();
292 virtual bool IsExitConditionSatisfied() OVERRIDE
;
293 virtual std::string
GetDebugMessage() const OVERRIDE
;
302 SamePasswordFormsAsVerifierChecker::SamePasswordFormsAsVerifierChecker(int i
)
303 : SingleClientStatusChangeChecker(
304 sync_datatype_helper::test()->GetSyncService(i
)),
307 needs_recheck_(false) {
310 SamePasswordFormsAsVerifierChecker::~SamePasswordFormsAsVerifierChecker() {
313 // This method uses the same re-entrancy prevention trick as
314 // the SamePasswordFormsChecker.
315 bool SamePasswordFormsAsVerifierChecker::IsExitConditionSatisfied() {
317 LOG(WARNING
) << "Setting flag and returning early to prevent nesting.";
318 needs_recheck_
= true;
322 // Keep retrying until we get a good reading.
326 needs_recheck_
= false;
327 result
= ProfileContainsSamePasswordFormsAsVerifier(index_
);
328 } while (needs_recheck_
);
329 in_progress_
= false;
333 std::string
SamePasswordFormsAsVerifierChecker::GetDebugMessage() const {
334 return "Waiting for passwords to match verifier";
339 bool AwaitProfileContainsSamePasswordFormsAsVerifier(int index
) {
340 SamePasswordFormsAsVerifierChecker
checker(index
);
342 return !checker
.TimedOut();
345 int GetPasswordCount(int index
) {
346 std::vector
<PasswordForm
> forms
;
347 GetLogins(GetPasswordStore(index
), forms
);
351 int GetVerifierPasswordCount() {
352 std::vector
<PasswordForm
> verifier_forms
;
353 GetLogins(GetVerifierPasswordStore(), verifier_forms
);
354 return verifier_forms
.size();
357 PasswordForm
CreateTestPasswordForm(int index
) {
359 form
.signon_realm
= kFakeSignonRealm
;
360 form
.origin
= GURL(base::StringPrintf(kIndexedFakeOrigin
, index
));
361 form
.username_value
=
362 base::ASCIIToUTF16(base::StringPrintf("username%d", index
));
363 form
.password_value
=
364 base::ASCIIToUTF16(base::StringPrintf("password%d", index
));
365 form
.date_created
= base::Time::Now();
369 } // namespace passwords_helper