Upstream version 5.34.98.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / password_manager / password_store_unittest.cc
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.
4
5 #include "base/basictypes.h"
6 #include "base/bind.h"
7 #include "base/stl_util.h"
8 #include "base/strings/string_util.h"
9 #include "base/synchronization/waitable_event.h"
10 #include "base/time/time.h"
11 #include "chrome/browser/chrome_notification_types.h"
12 #include "chrome/browser/password_manager/password_form_data.h"
13 #include "chrome/browser/password_manager/password_store_consumer.h"
14 #include "chrome/browser/password_manager/password_store_default.h"
15 #include "chrome/test/base/testing_profile.h"
16 #include "content/public/browser/notification_details.h"
17 #include "content/public/browser/notification_registrar.h"
18 #include "content/public/browser/notification_source.h"
19 #include "content/public/test/mock_notification_observer.h"
20 #include "content/public/test/test_browser_thread.h"
21 #include "testing/gmock/include/gmock/gmock.h"
22 #include "testing/gtest/include/gtest/gtest.h"
23
24 using autofill::PasswordForm;
25 using base::WaitableEvent;
26 using content::BrowserThread;
27 using testing::_;
28 using testing::DoAll;
29 using testing::WithArg;
30
31 namespace {
32
33 class MockPasswordStoreConsumer : public PasswordStoreConsumer {
34  public:
35   MOCK_METHOD1(OnGetPasswordStoreResults,
36                void(const std::vector<PasswordForm*>&));
37 };
38
39 // This class will add and remove a mock notification observer from
40 // the DB thread.
41 class DBThreadObserverHelper
42     : public base::RefCountedThreadSafe<DBThreadObserverHelper,
43                                         BrowserThread::DeleteOnDBThread> {
44  public:
45   DBThreadObserverHelper() : done_event_(true, false) {}
46
47   void Init(PasswordStore* password_store) {
48     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
49     BrowserThread::PostTask(
50         BrowserThread::DB,
51         FROM_HERE,
52         base::Bind(&DBThreadObserverHelper::AddObserverTask,
53                    this,
54                    make_scoped_refptr(password_store)));
55     done_event_.Wait();
56   }
57
58   content::MockNotificationObserver& observer() {
59     return observer_;
60   }
61
62  protected:
63   virtual ~DBThreadObserverHelper() {
64     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
65     registrar_.RemoveAll();
66   }
67
68   void AddObserverTask(PasswordStore* password_store) {
69     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
70     registrar_.Add(&observer_,
71                    chrome::NOTIFICATION_LOGINS_CHANGED,
72                    content::Source<PasswordStore>(password_store));
73     done_event_.Signal();
74   }
75
76   WaitableEvent done_event_;
77   content::NotificationRegistrar registrar_;
78   content::MockNotificationObserver observer_;
79
80  private:
81   friend struct BrowserThread::DeleteOnThread<BrowserThread::DB>;
82   friend class base::DeleteHelper<DBThreadObserverHelper>;
83 };
84
85 }  // anonymous namespace
86
87 class PasswordStoreTest : public testing::Test {
88  protected:
89   PasswordStoreTest()
90       : ui_thread_(BrowserThread::UI, &message_loop_),
91         db_thread_(BrowserThread::DB) {
92   }
93
94   virtual void SetUp() {
95     ASSERT_TRUE(db_thread_.Start());
96
97     profile_.reset(new TestingProfile());
98
99     login_db_.reset(new LoginDatabase());
100     ASSERT_TRUE(login_db_->Init(profile_->GetPath().Append(
101         FILE_PATH_LITERAL("login_test"))));
102   }
103
104   virtual void TearDown() {
105     db_thread_.Stop();
106     base::MessageLoop::current()->PostTask(FROM_HERE,
107                                            base::MessageLoop::QuitClosure());
108     base::MessageLoop::current()->Run();
109   }
110
111   base::MessageLoopForUI message_loop_;
112   content::TestBrowserThread ui_thread_;
113   // PasswordStore schedules work on this thread.
114   content::TestBrowserThread db_thread_;
115
116   scoped_ptr<LoginDatabase> login_db_;
117   scoped_ptr<TestingProfile> profile_;
118 };
119
120 ACTION(STLDeleteElements0) {
121   STLDeleteContainerPointers(arg0.begin(), arg0.end());
122 }
123
124 ACTION(QuitUIMessageLoop) {
125   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
126   base::MessageLoop::current()->Quit();
127 }
128
129 TEST_F(PasswordStoreTest, IgnoreOldWwwGoogleLogins) {
130   scoped_refptr<PasswordStoreDefault> store(
131       new PasswordStoreDefault(login_db_.release(), profile_.get()));
132   store->Init();
133
134   const time_t cutoff = 1325376000;  // 00:00 Jan 1 2012 UTC
135   // The passwords are all empty because PasswordStoreDefault doesn't store the
136   // actual passwords on OS X (they're stored in the Keychain instead). We could
137   // special-case it, but it's easier to just have empty passwords.
138   static const PasswordFormData form_data[] = {
139     // A form on https://www.google.com/ older than the cutoff. Will be ignored.
140     { PasswordForm::SCHEME_HTML,
141       "https://www.google.com",
142       "https://www.google.com/origin",
143       "https://www.google.com/action",
144       L"submit_element",
145       L"username_element",
146       L"password_element",
147       L"username_value_1",
148       L"",
149       true, true, cutoff - 1 },
150     // A form on https://www.google.com/ older than the cutoff. Will be ignored.
151     { PasswordForm::SCHEME_HTML,
152       "https://www.google.com",
153       "https://www.google.com/origin",
154       "https://www.google.com/action",
155       L"submit_element",
156       L"username_element",
157       L"password_element",
158       L"username_value_2",
159       L"",
160       true, true, cutoff - 1 },
161     // A form on https://www.google.com/ newer than the cutoff.
162     { PasswordForm::SCHEME_HTML,
163       "https://www.google.com",
164       "https://www.google.com/origin",
165       "https://www.google.com/action",
166       L"submit_element",
167       L"username_element",
168       L"password_element",
169       L"username_value_3",
170       L"",
171       true, true, cutoff + 1 },
172     // A form on https://accounts.google.com/ older than the cutoff.
173     { PasswordForm::SCHEME_HTML,
174       "https://accounts.google.com",
175       "https://accounts.google.com/origin",
176       "https://accounts.google.com/action",
177       L"submit_element",
178       L"username_element",
179       L"password_element",
180       L"username_value",
181       L"",
182       true, true, cutoff - 1 },
183     // A form on http://bar.example.com/ older than the cutoff.
184     { PasswordForm::SCHEME_HTML,
185       "http://bar.example.com",
186       "http://bar.example.com/origin",
187       "http://bar.example.com/action",
188       L"submit_element",
189       L"username_element",
190       L"password_element",
191       L"username_value",
192       L"",
193       true, false, cutoff - 1 },
194   };
195
196   // Build the forms vector and add the forms to the store.
197   std::vector<PasswordForm*> all_forms;
198   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(form_data); ++i) {
199     PasswordForm* form = CreatePasswordFormFromData(form_data[i]);
200     all_forms.push_back(form);
201     store->AddLogin(*form);
202   }
203
204   // The PasswordStore schedules tasks to run on the DB thread so we schedule
205   // yet another task to notify us that it's safe to carry on with the test.
206   // The PasswordStore doesn't really understand that it's "done" once the tasks
207   // we posted above have completed, so there's no formal notification for that.
208   WaitableEvent done(false, false);
209   BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
210       base::Bind(&WaitableEvent::Signal, base::Unretained(&done)));
211   done.Wait();
212
213   // We expect to get back only the "recent" www.google.com login.
214   // Theoretically these should never actually exist since there are no longer
215   // any login forms on www.google.com to save, but we technically allow them.
216   // We should not get back the older saved password though.
217   PasswordForm www_google;
218   www_google.scheme = PasswordForm::SCHEME_HTML;
219   www_google.signon_realm = "https://www.google.com";
220   std::vector<PasswordForm*> www_google_expected;
221   www_google_expected.push_back(all_forms[2]);
222
223   // We should still get the accounts.google.com login even though it's older
224   // than our cutoff - this is the new location of all Google login forms.
225   PasswordForm accounts_google;
226   accounts_google.scheme = PasswordForm::SCHEME_HTML;
227   accounts_google.signon_realm = "https://accounts.google.com";
228   std::vector<PasswordForm*> accounts_google_expected;
229   accounts_google_expected.push_back(all_forms[3]);
230
231   // Same thing for a generic saved login.
232   PasswordForm bar_example;
233   bar_example.scheme = PasswordForm::SCHEME_HTML;
234   bar_example.signon_realm = "http://bar.example.com";
235   std::vector<PasswordForm*> bar_example_expected;
236   bar_example_expected.push_back(all_forms[4]);
237
238   MockPasswordStoreConsumer consumer;
239
240   // Make sure we quit the MessageLoop even if the test fails.
241   ON_CALL(consumer, OnGetPasswordStoreResults(_))
242       .WillByDefault(QuitUIMessageLoop());
243
244   // Expect the appropriate replies, as above, in reverse order than we will
245   // issue the queries. Each retires on saturation to avoid matcher spew, except
246   // the last which quits the message loop.
247   EXPECT_CALL(consumer,
248       OnGetPasswordStoreResults(ContainsAllPasswordForms(bar_example_expected)))
249       .WillOnce(DoAll(WithArg<0>(STLDeleteElements0()), QuitUIMessageLoop()));
250   EXPECT_CALL(consumer,
251       OnGetPasswordStoreResults(
252           ContainsAllPasswordForms(accounts_google_expected)))
253       .WillOnce(WithArg<0>(STLDeleteElements0())).RetiresOnSaturation();
254   EXPECT_CALL(consumer,
255       OnGetPasswordStoreResults(
256           ContainsAllPasswordForms(www_google_expected)))
257       .WillOnce(WithArg<0>(STLDeleteElements0())).RetiresOnSaturation();
258
259   store->GetLogins(www_google, PasswordStore::ALLOW_PROMPT, &consumer);
260   store->GetLogins(accounts_google, PasswordStore::ALLOW_PROMPT, &consumer);
261   store->GetLogins(bar_example, PasswordStore::ALLOW_PROMPT, &consumer);
262
263   base::MessageLoop::current()->Run();
264
265   STLDeleteElements(&all_forms);
266 }