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);
69 namespace passwords_helper {
71 void AddLogin(PasswordStore* store, const PasswordForm& form) {
73 base::WaitableEvent wait_event(true, false);
74 store->AddLogin(form);
75 store->ScheduleTask(base::Bind(&PasswordStoreCallback, &wait_event));
79 void UpdateLogin(PasswordStore* store, const PasswordForm& form) {
81 base::WaitableEvent wait_event(true, false);
82 store->UpdateLogin(form);
83 store->ScheduleTask(base::Bind(&PasswordStoreCallback, &wait_event));
87 void GetLogins(PasswordStore* store, std::vector<PasswordForm>& matches) {
89 PasswordForm matcher_form;
90 matcher_form.signon_realm = kFakeSignonRealm;
91 PasswordStoreConsumerHelper consumer(&matches);
92 store->GetLogins(matcher_form, PasswordStore::DISALLOW_PROMPT, &consumer);
93 content::RunMessageLoop();
96 void RemoveLogin(PasswordStore* store, const PasswordForm& form) {
98 base::WaitableEvent wait_event(true, false);
99 store->RemoveLogin(form);
100 store->ScheduleTask(base::Bind(&PasswordStoreCallback, &wait_event));
104 void RemoveLogins(PasswordStore* store) {
105 std::vector<PasswordForm> forms;
106 GetLogins(store, forms);
107 for (std::vector<PasswordForm>::iterator it = forms.begin();
108 it != forms.end(); ++it) {
109 RemoveLogin(store, *it);
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 Profile::IMPLICIT_ACCESS).get();
130 PasswordStore* GetVerifierPasswordStore() {
131 return PasswordStoreFactory::GetForProfile(test()->verifier(),
132 Profile::IMPLICIT_ACCESS).get();
135 bool ProfileContainsSamePasswordFormsAsVerifier(int index) {
136 std::vector<PasswordForm> verifier_forms;
137 std::vector<PasswordForm> forms;
138 GetLogins(GetVerifierPasswordStore(), verifier_forms);
139 GetLogins(GetPasswordStore(index), forms);
141 password_manager::ContainsSamePasswordForms(verifier_forms, forms);
143 LOG(ERROR) << "Password forms in Verifier Profile:";
144 for (std::vector<PasswordForm>::iterator it = verifier_forms.begin();
145 it != verifier_forms.end(); ++it) {
146 LOG(ERROR) << *it << std::endl;
148 LOG(ERROR) << "Password forms in Profile" << index << ":";
149 for (std::vector<PasswordForm>::iterator it = forms.begin();
150 it != forms.end(); ++it) {
151 LOG(ERROR) << *it << std::endl;
157 bool ProfilesContainSamePasswordForms(int index_a, int index_b) {
158 std::vector<PasswordForm> forms_a;
159 std::vector<PasswordForm> forms_b;
160 GetLogins(GetPasswordStore(index_a), forms_a);
161 GetLogins(GetPasswordStore(index_b), forms_b);
162 bool result = password_manager::ContainsSamePasswordForms(forms_a, forms_b);
164 LOG(ERROR) << "Password forms in Profile" << index_a << ":";
165 for (std::vector<PasswordForm>::iterator it = forms_a.begin();
166 it != forms_a.end(); ++it) {
167 LOG(ERROR) << *it << std::endl;
169 LOG(ERROR) << "Password forms in Profile" << index_b << ":";
170 for (std::vector<PasswordForm>::iterator it = forms_b.begin();
171 it != forms_b.end(); ++it) {
172 LOG(ERROR) << *it << std::endl;
178 bool AllProfilesContainSamePasswordFormsAsVerifier() {
179 for (int i = 0; i < test()->num_clients(); ++i) {
180 if (!ProfileContainsSamePasswordFormsAsVerifier(i)) {
181 DVLOG(1) << "Profile " << i << " does not contain the same password"
182 " forms as the verifier.";
189 bool AllProfilesContainSamePasswordForms() {
190 for (int i = 1; i < test()->num_clients(); ++i) {
191 if (!ProfilesContainSamePasswordForms(0, i)) {
192 DVLOG(1) << "Profile " << i << " does not contain the same password"
193 " forms as Profile 0.";
202 // Helper class used in the implementation of
203 // AwaitAllProfilesContainSamePasswordForms.
204 class SamePasswordFormsChecker : public MultiClientStatusChangeChecker {
206 SamePasswordFormsChecker();
207 virtual ~SamePasswordFormsChecker();
209 virtual bool IsExitConditionSatisfied() OVERRIDE;
210 virtual std::string GetDebugMessage() const OVERRIDE;
217 SamePasswordFormsChecker::SamePasswordFormsChecker()
218 : MultiClientStatusChangeChecker(
219 sync_datatype_helper::test()->GetSyncServices()),
221 needs_recheck_(false) {}
223 SamePasswordFormsChecker::~SamePasswordFormsChecker() {}
225 // This method needs protection against re-entrancy.
227 // This function indirectly calls GetLogins(), which starts a RunLoop on the UI
228 // thread. This can be a problem, since the next task to execute could very
229 // well contain a ProfileSyncService::OnStateChanged() event, which would
230 // trigger another call to this here function, and start another layer of
231 // nested RunLoops. That makes the StatusChangeChecker's Quit() method
234 // The work-around is to not allow re-entrancy. But we can't just drop
235 // IsExitConditionSatisifed() calls if one is already in progress. Instead, we
236 // set a flag to ask the current execution of IsExitConditionSatisfied() to be
237 // re-run. This ensures that the return value is always based on the most
239 bool SamePasswordFormsChecker::IsExitConditionSatisfied() {
241 LOG(WARNING) << "Setting flag and returning early to prevent nesting.";
242 needs_recheck_ = true;
246 // Keep retrying until we get a good reading.
250 needs_recheck_ = false;
251 result = AllProfilesContainSamePasswordForms();
252 } while (needs_recheck_);
253 in_progress_ = false;
257 std::string SamePasswordFormsChecker::GetDebugMessage() const {
258 return "Waiting for matching passwords";
263 bool AwaitAllProfilesContainSamePasswordForms() {
264 SamePasswordFormsChecker checker;
266 return !checker.TimedOut();
271 // Helper class used in the implementation of
272 // AwaitProfileContainSamePasswordFormsAsVerifier.
273 class SamePasswordFormsAsVerifierChecker
274 : public SingleClientStatusChangeChecker {
276 explicit SamePasswordFormsAsVerifierChecker(int index);
277 virtual ~SamePasswordFormsAsVerifierChecker();
279 virtual bool IsExitConditionSatisfied() OVERRIDE;
280 virtual std::string GetDebugMessage() const OVERRIDE;
289 SamePasswordFormsAsVerifierChecker::SamePasswordFormsAsVerifierChecker(int i)
290 : SingleClientStatusChangeChecker(
291 sync_datatype_helper::test()->GetSyncService(i)),
294 needs_recheck_(false) {
297 SamePasswordFormsAsVerifierChecker::~SamePasswordFormsAsVerifierChecker() {
300 // This method uses the same re-entrancy prevention trick as
301 // the SamePasswordFormsChecker.
302 bool SamePasswordFormsAsVerifierChecker::IsExitConditionSatisfied() {
304 LOG(WARNING) << "Setting flag and returning early to prevent nesting.";
305 needs_recheck_ = true;
309 // Keep retrying until we get a good reading.
313 needs_recheck_ = false;
314 result = ProfileContainsSamePasswordFormsAsVerifier(index_);
315 } while (needs_recheck_);
316 in_progress_ = false;
320 std::string SamePasswordFormsAsVerifierChecker::GetDebugMessage() const {
321 return "Waiting for passwords to match verifier";
326 bool AwaitProfileContainsSamePasswordFormsAsVerifier(int index) {
327 SamePasswordFormsAsVerifierChecker checker(index);
329 return !checker.TimedOut();
332 int GetPasswordCount(int index) {
333 std::vector<PasswordForm> forms;
334 GetLogins(GetPasswordStore(index), forms);
338 int GetVerifierPasswordCount() {
339 std::vector<PasswordForm> verifier_forms;
340 GetLogins(GetVerifierPasswordStore(), verifier_forms);
341 return verifier_forms.size();
344 PasswordForm CreateTestPasswordForm(int index) {
346 form.signon_realm = kFakeSignonRealm;
347 form.origin = GURL(base::StringPrintf(kIndexedFakeOrigin, index));
348 form.username_value =
349 base::ASCIIToUTF16(base::StringPrintf("username%d", index));
350 form.password_value =
351 base::ASCIIToUTF16(base::StringPrintf("password%d", index));
352 form.date_created = base::Time::Now();
356 } // namespace passwords_helper