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/bind_helpers.h"
8#include "base/files/scoped_temp_dir.h"
9#include "base/prefs/pref_service.h"
10#include "base/stl_util.h"
11#include "base/strings/string_util.h"
12#include "base/strings/utf_string_conversions.h"
13#include "base/synchronization/waitable_event.h"
14#include "base/time/time.h"
15#include "components/password_manager/core/browser/password_form_data.h"
16#include "components/password_manager/core/browser/password_store_change.h"
17#include "components/password_manager/core/browser/password_store_consumer.h"
18#include "components/password_manager/core/browser/password_store_default.h"
19#include "testing/gmock/include/gmock/gmock.h"
20#include "testing/gtest/include/gtest/gtest.h"
21
22using autofill::PasswordForm;
23using base::WaitableEvent;
24using testing::_;
25using testing::DoAll;
26using testing::ElementsAreArray;
27using testing::Pointee;
28using testing::Property;
29using testing::WithArg;
30
31namespace password_manager {
32
33namespace {
34
35class MockPasswordStoreConsumer : public PasswordStoreConsumer {
36 public:
37  MOCK_METHOD1(OnGetPasswordStoreResults,
38               void(const std::vector<PasswordForm*>&));
39};
40
41class MockPasswordStoreObserver : public PasswordStore::Observer {
42 public:
43  MOCK_METHOD1(OnLoginsChanged,
44               void(const PasswordStoreChangeList& changes));
45};
46
47}  // anonymous namespace
48
49class PasswordStoreDefaultTest : public testing::Test {
50 protected:
51  virtual void SetUp() OVERRIDE {
52    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
53    login_db_.reset(new LoginDatabase());
54    ASSERT_TRUE(login_db_->Init(temp_dir_.path().Append(
55        FILE_PATH_LITERAL("login_test"))));
56  }
57
58  virtual void TearDown() OVERRIDE {
59    ASSERT_TRUE(temp_dir_.Delete());
60  }
61
62  base::MessageLoopForUI message_loop_;
63  scoped_ptr<LoginDatabase> login_db_;
64  base::ScopedTempDir temp_dir_;
65};
66
67ACTION(STLDeleteElements0) {
68  STLDeleteContainerPointers(arg0.begin(), arg0.end());
69}
70
71TEST_F(PasswordStoreDefaultTest, NonASCIIData) {
72  scoped_refptr<PasswordStoreDefault> store(new PasswordStoreDefault(
73      base::MessageLoopProxy::current(),
74      base::MessageLoopProxy::current(),
75      login_db_.release()));
76  store->Init(syncer::SyncableService::StartSyncFlare(), "");
77
78  // Some non-ASCII password form data.
79  static const PasswordFormData form_data[] = {
80    { PasswordForm::SCHEME_HTML,
81      "http://foo.example.com",
82      "http://foo.example.com/origin",
83      "http://foo.example.com/action",
84      L"มีสีสัน",
85      L"お元気ですか?",
86      L"盆栽",
87      L"أحب كرة",
88      L"£éä국수çà",
89      true, false, 1 },
90  };
91
92  // Build the expected forms vector and add the forms to the store.
93  std::vector<PasswordForm*> expected_forms;
94  for (unsigned int i = 0; i < ARRAYSIZE_UNSAFE(form_data); ++i) {
95    PasswordForm* form = CreatePasswordFormFromData(form_data[i]);
96    expected_forms.push_back(form);
97    store->AddLogin(*form);
98  }
99
100  base::MessageLoop::current()->RunUntilIdle();
101
102  MockPasswordStoreConsumer consumer;
103
104  // We expect to get the same data back, even though it's not all ASCII.
105  EXPECT_CALL(consumer,
106      OnGetPasswordStoreResults(ContainsAllPasswordForms(expected_forms)))
107      .WillOnce(WithArg<0>(STLDeleteElements0()));
108  store->GetAutofillableLogins(&consumer);
109
110  base::MessageLoop::current()->RunUntilIdle();
111
112  STLDeleteElements(&expected_forms);
113  store->Shutdown();
114  base::MessageLoop::current()->RunUntilIdle();
115}
116
117TEST_F(PasswordStoreDefaultTest, Notifications) {
118  scoped_refptr<PasswordStoreDefault> store(new PasswordStoreDefault(
119      base::MessageLoopProxy::current(),
120      base::MessageLoopProxy::current(),
121      login_db_.release()));
122  store->Init(syncer::SyncableService::StartSyncFlare(), "");
123
124  PasswordFormData form_data =
125  { PasswordForm::SCHEME_HTML,
126    "http://bar.example.com",
127    "http://bar.example.com/origin",
128    "http://bar.example.com/action",
129    L"submit_element",
130    L"username_element",
131    L"password_element",
132    L"username_value",
133    L"password_value",
134    true, false, 1 };
135  scoped_ptr<PasswordForm> form(CreatePasswordFormFromData(form_data));
136
137  MockPasswordStoreObserver observer;
138  store->AddObserver(&observer);
139
140  const PasswordStoreChange expected_add_changes[] = {
141    PasswordStoreChange(PasswordStoreChange::ADD, *form),
142  };
143
144  EXPECT_CALL(
145      observer,
146      OnLoginsChanged(ElementsAreArray(expected_add_changes)));
147
148  // Adding a login should trigger a notification.
149  store->AddLogin(*form);
150  base::MessageLoop::current()->RunUntilIdle();
151
152  // Change the password.
153  form->password_value = base::ASCIIToUTF16("a different password");
154
155  const PasswordStoreChange expected_update_changes[] = {
156    PasswordStoreChange(PasswordStoreChange::UPDATE, *form),
157  };
158
159  EXPECT_CALL(
160      observer,
161      OnLoginsChanged(ElementsAreArray(expected_update_changes)));
162
163  // Updating the login with the new password should trigger a notification.
164  store->UpdateLogin(*form);
165  base::MessageLoop::current()->RunUntilIdle();
166
167  const PasswordStoreChange expected_delete_changes[] = {
168    PasswordStoreChange(PasswordStoreChange::REMOVE, *form),
169  };
170
171  EXPECT_CALL(
172      observer,
173      OnLoginsChanged(ElementsAreArray(expected_delete_changes)));
174
175  // Deleting the login should trigger a notification.
176  store->RemoveLogin(*form);
177  base::MessageLoop::current()->RunUntilIdle();
178
179  store->RemoveObserver(&observer);
180  store->Shutdown();
181  base::MessageLoop::current()->RunUntilIdle();
182}
183
184}  // namespace password_manager
185