Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/password_manager/password_store_mac.h" | 5 #include "chrome/browser/password_manager/password_store_mac.h" |
| 6 | 6 |
| 7 #include "base/basictypes.h" | 7 #include "base/basictypes.h" |
| 8 #include "base/files/scoped_temp_dir.h" | 8 #include "base/files/scoped_temp_dir.h" |
| 9 #include "base/scoped_observer.h" | 9 #include "base/scoped_observer.h" |
| 10 #include "base/stl_util.h" | 10 #include "base/stl_util.h" |
| 11 #include "base/strings/string_util.h" | 11 #include "base/strings/string_util.h" |
| 12 #include "base/strings/utf_string_conversions.h" | 12 #include "base/strings/utf_string_conversions.h" |
| 13 #include "base/synchronization/waitable_event.h" | 13 #include "base/synchronization/waitable_event.h" |
| 14 #include "base/test/histogram_tester.h" | 14 #include "base/test/histogram_tester.h" |
| 15 #include "base/thread_task_runner_handle.h" | 15 #include "base/thread_task_runner_handle.h" |
| 16 #include "chrome/browser/password_manager/password_store_mac_internal.h" | 16 #include "chrome/browser/password_manager/password_store_mac_internal.h" |
| 17 #include "chrome/common/chrome_paths.h" | 17 #include "chrome/common/chrome_paths.h" |
| 18 #include "components/os_crypt/os_crypt.h" | 18 #include "components/os_crypt/os_crypt.h" |
| 19 #include "components/password_manager/core/browser/login_database.h" | 19 #include "components/password_manager/core/browser/login_database.h" |
| 20 #include "components/password_manager/core/browser/password_manager_test_utils.h " | 20 #include "components/password_manager/core/browser/password_manager_test_utils.h " |
| 21 #include "components/password_manager/core/browser/password_store_common_unittes t.h" | |
| 21 #include "components/password_manager/core/browser/password_store_consumer.h" | 22 #include "components/password_manager/core/browser/password_store_consumer.h" |
| 22 #include "content/public/test/test_browser_thread.h" | 23 #include "content/public/test/test_browser_thread.h" |
| 23 #include "content/public/test/test_utils.h" | 24 #include "content/public/test/test_utils.h" |
| 24 #include "crypto/mock_apple_keychain.h" | 25 #include "crypto/mock_apple_keychain.h" |
| 25 #include "testing/gmock/include/gmock/gmock.h" | 26 #include "testing/gmock/include/gmock/gmock.h" |
| 26 #include "testing/gtest/include/gtest/gtest.h" | 27 #include "testing/gtest/include/gtest/gtest.h" |
| 27 | 28 |
| 28 using autofill::PasswordForm; | 29 using autofill::PasswordForm; |
| 29 using base::ASCIIToUTF16; | 30 using base::ASCIIToUTF16; |
| 30 using base::WideToUTF16; | 31 using base::WideToUTF16; |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 67 public: | 68 public: |
| 68 MOCK_METHOD1(OnGetPasswordStoreResultsConstRef, | 69 MOCK_METHOD1(OnGetPasswordStoreResultsConstRef, |
| 69 void(const std::vector<PasswordForm*>&)); | 70 void(const std::vector<PasswordForm*>&)); |
| 70 | 71 |
| 71 // GMock cannot mock methods with move-only args. | 72 // GMock cannot mock methods with move-only args. |
| 72 void OnGetPasswordStoreResults(ScopedVector<PasswordForm> results) override { | 73 void OnGetPasswordStoreResults(ScopedVector<PasswordForm> results) override { |
| 73 OnGetPasswordStoreResultsConstRef(results.get()); | 74 OnGetPasswordStoreResultsConstRef(results.get()); |
| 74 } | 75 } |
| 75 }; | 76 }; |
| 76 | 77 |
| 77 class MockPasswordStoreObserver : public PasswordStore::Observer { | |
| 78 public: | |
| 79 MOCK_METHOD1(OnLoginsChanged, | |
| 80 void(const password_manager::PasswordStoreChangeList& changes)); | |
| 81 }; | |
| 82 | |
| 83 // A LoginDatabase that simulates an Init() method that takes a long time. | 78 // A LoginDatabase that simulates an Init() method that takes a long time. |
| 84 class SlowToInitLoginDatabase : public password_manager::LoginDatabase { | 79 class SlowToInitLoginDatabase : public password_manager::LoginDatabase { |
| 85 public: | 80 public: |
| 86 // Creates an instance whose Init() method will block until |event| is | 81 // Creates an instance whose Init() method will block until |event| is |
| 87 // signaled. |event| must outlive |this|. | 82 // signaled. |event| must outlive |this|. |
| 88 SlowToInitLoginDatabase(const base::FilePath& db_path, | 83 SlowToInitLoginDatabase(const base::FilePath& db_path, |
| 89 base::WaitableEvent* event) | 84 base::WaitableEvent* event) |
| 90 : password_manager::LoginDatabase(db_path), event_(event) {} | 85 : password_manager::LoginDatabase(db_path), event_(event) {} |
| 91 ~SlowToInitLoginDatabase() override {} | 86 ~SlowToInitLoginDatabase() override {} |
| 92 | 87 |
| (...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 167 } | 162 } |
| 168 } | 163 } |
| 169 | 164 |
| 170 PasswordStoreChangeList AddChangeForForm(const PasswordForm& form) { | 165 PasswordStoreChangeList AddChangeForForm(const PasswordForm& form) { |
| 171 return PasswordStoreChangeList( | 166 return PasswordStoreChangeList( |
| 172 1, PasswordStoreChange(PasswordStoreChange::ADD, form)); | 167 1, PasswordStoreChange(PasswordStoreChange::ADD, form)); |
| 173 } | 168 } |
| 174 | 169 |
| 175 } // namespace | 170 } // namespace |
| 176 | 171 |
| 172 namespace password_manager { | |
|
vasilii
2015/10/21 16:47:56
You don't need this namespace. Use the anonymous o
Timo Reimann
2015/11/17 23:10:55
Done.
| |
| 173 | |
| 174 class PasswordStoreMacTestDelegate { | |
| 175 public: | |
| 176 PasswordStoreMacTestDelegate() | |
| 177 : ui_thread_(BrowserThread::UI, &message_loop_) { | |
| 178 initialize(); | |
|
vasilii
2015/10/21 16:47:56
Method's name should start with a capital letter.
Timo Reimann
2015/11/17 23:10:55
Done.
| |
| 179 } | |
| 180 ~PasswordStoreMacTestDelegate() { | |
| 181 ClosePasswordStore(); | |
| 182 thread_.reset(); | |
| 183 login_db_.reset(); | |
| 184 } | |
| 185 | |
| 186 PasswordStoreMac* store() { return store_.get(); } | |
| 187 | |
| 188 void FinishAsyncProcessing() { | |
| 189 scoped_refptr<content::MessageLoopRunner> runner = | |
| 190 new content::MessageLoopRunner; | |
| 191 ASSERT_TRUE(thread_->task_runner()->PostTaskAndReply( | |
| 192 FROM_HERE, base::Bind(&Noop), runner->QuitClosure())); | |
| 193 runner->Run(); | |
| 194 } | |
| 195 | |
| 196 base::Thread* thread() { return thread_.get(); } | |
|
vasilii
2015/10/21 16:47:56
Why is it needed?
Timo Reimann
2015/11/17 23:10:55
PasswordStoreMacTest.ImportFromKeychain, PasswordS
vasilii
2015/11/18 16:27:42
You can expose a task runner, not a thread.
Timo Reimann
2015/11/18 23:42:14
I use ThreadTaskRunnerHandle now. Hope that's the
| |
| 197 | |
| 198 private: | |
| 199 base::MessageLoopForUI message_loop_; | |
|
vasilii
2015/10/21 16:47:56
Methods should be above. They are complex enough s
Timo Reimann
2015/11/17 23:10:55
Done (for PasswordStoreDefaultTest too).
| |
| 200 content::TestBrowserThread ui_thread_; | |
| 201 // Thread that the synchronous methods are run on. | |
| 202 scoped_ptr<base::Thread> thread_; | |
| 203 | |
| 204 base::ScopedTempDir db_dir_; | |
| 205 scoped_ptr<LoginDatabase> login_db_; | |
| 206 scoped_refptr<PasswordStoreMac> store_; | |
| 207 | |
| 208 static void InitLoginDatabase(LoginDatabase* login_db) { | |
| 209 ASSERT_TRUE(login_db->Init()); | |
| 210 } | |
| 211 | |
| 212 void initialize() { | |
|
Timo Reimann
2015/10/20 20:47:09
The initialization of PasswordStoreMac for testing
vasilii
2015/10/21 16:47:56
For your tests you can definitely simplify Passwor
| |
| 213 ASSERT_TRUE(db_dir_.CreateUniqueTempDir()); | |
| 214 | |
| 215 // Ensure that LoginDatabase will use the mock keychain if it needs to | |
| 216 // encrypt/decrypt a password. | |
| 217 OSCrypt::UseMockKeychain(true); | |
|
vasilii
2015/10/21 16:47:56
You just pass the Keychain instance into construct
Timo Reimann
2015/11/17 23:10:55
Which constructor do you mean? The test already pa
| |
| 218 login_db_.reset(new LoginDatabase(test_login_db_file_path())); | |
| 219 thread_.reset(new base::Thread("Chrome_PasswordStore_Thread")); | |
| 220 ASSERT_TRUE(thread_->Start()); | |
| 221 ASSERT_TRUE(thread_->task_runner()->PostTask( | |
| 222 FROM_HERE, base::Bind(&PasswordStoreMacTestDelegate::InitLoginDatabase, | |
| 223 base::Unretained(login_db_.get())))); | |
| 224 CreateAndInitPasswordStore(login_db_.get()); | |
| 225 // Make sure deferred initialization is performed before some tests start | |
| 226 // accessing the |login_db| directly. | |
| 227 FinishAsyncProcessing(); | |
| 228 } | |
| 229 | |
| 230 base::FilePath test_login_db_file_path() const { | |
| 231 return db_dir_.path().Append(FILE_PATH_LITERAL("login.db")); | |
| 232 } | |
| 233 | |
| 234 void CreateAndInitPasswordStore(LoginDatabase* login_db) { | |
|
vasilii
2015/10/21 16:47:56
merge the method into initialize()
Timo Reimann
2015/11/17 23:10:55
Done.
| |
| 235 store_ = new PasswordStoreMac( | |
| 236 base::ThreadTaskRunnerHandle::Get(), nullptr, | |
| 237 make_scoped_ptr<AppleKeychain>(new MockAppleKeychain)); | |
| 238 ASSERT_TRUE(thread_->task_runner()->PostTask( | |
| 239 FROM_HERE, base::Bind(&PasswordStoreMac::InitWithTaskRunner, store_, | |
| 240 thread_->task_runner()))); | |
| 241 | |
| 242 ASSERT_TRUE(thread_->task_runner()->PostTask( | |
| 243 FROM_HERE, base::Bind(&PasswordStoreMac::set_login_metadata_db, store_, | |
| 244 base::Unretained(login_db)))); | |
| 245 } | |
| 246 | |
| 247 void ClosePasswordStore() { | |
| 248 if (!store_) | |
|
vasilii
2015/10/21 16:47:56
It can't be true.
Timo Reimann
2015/11/17 23:10:55
I agree, unless there's a bug where store is attem
vasilii
2015/11/18 16:27:42
Just drop.
Timo Reimann
2015/11/18 23:42:14
Done.
| |
| 249 return; | |
| 250 | |
| 251 store_->Shutdown(); | |
| 252 store_ = nullptr; | |
| 253 } | |
| 254 }; | |
| 255 | |
| 256 INSTANTIATE_TYPED_TEST_CASE_P(Mac, | |
| 257 PasswordStoreCommonTest, | |
| 258 PasswordStoreMacTestDelegate); | |
| 259 | |
| 260 } // namespace password_manager | |
| 261 | |
| 177 #pragma mark - | 262 #pragma mark - |
| 178 | 263 |
| 179 class PasswordStoreMacInternalsTest : public testing::Test { | 264 class PasswordStoreMacInternalsTest : public testing::Test { |
| 180 public: | 265 public: |
| 181 void SetUp() override { | 266 void SetUp() override { |
| 182 MockAppleKeychain::KeychainTestData test_data[] = { | 267 MockAppleKeychain::KeychainTestData test_data[] = { |
| 183 // Basic HTML form. | 268 // Basic HTML form. |
| 184 {kSecAuthenticationTypeHTMLForm, | 269 {kSecAuthenticationTypeHTMLForm, |
| 185 "some.domain.com", | 270 "some.domain.com", |
| 186 kSecProtocolTypeHTTP, | 271 kSecProtocolTypeHTTP, |
| (...skipping 979 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1166 | 1251 |
| 1167 ScopedVector<autofill::PasswordForm> owned_passwords = | 1252 ScopedVector<autofill::PasswordForm> owned_passwords = |
| 1168 owned_keychain_adapter.GetAllPasswordFormPasswords(); | 1253 owned_keychain_adapter.GetAllPasswordFormPasswords(); |
| 1169 EXPECT_EQ(arraysize(owned_password_data), owned_passwords.size()); | 1254 EXPECT_EQ(arraysize(owned_password_data), owned_passwords.size()); |
| 1170 } | 1255 } |
| 1171 | 1256 |
| 1172 #pragma mark - | 1257 #pragma mark - |
| 1173 | 1258 |
| 1174 class PasswordStoreMacTest : public testing::Test { | 1259 class PasswordStoreMacTest : public testing::Test { |
| 1175 public: | 1260 public: |
| 1176 PasswordStoreMacTest() : ui_thread_(BrowserThread::UI, &message_loop_) {} | 1261 PasswordStoreMacTest() |
| 1262 : histogram_tester_(new base::HistogramTester), | |
| 1263 store_(delegate_.store()) {} | |
|
vasilii
2015/10/21 16:47:56
I'm generally against changing this class. Instead
Timo Reimann
2015/11/17 23:10:55
I'm not sure I understand: The delegate was create
| |
| 1177 | 1264 |
| 1178 void SetUp() override { | 1265 ~PasswordStoreMacTest() override { |
| 1179 ASSERT_TRUE(db_dir_.CreateUniqueTempDir()); | |
| 1180 histogram_tester_.reset(new base::HistogramTester); | |
| 1181 | |
| 1182 // Ensure that LoginDatabase will use the mock keychain if it needs to | |
| 1183 // encrypt/decrypt a password. | |
| 1184 OSCrypt::UseMockKeychain(true); | |
| 1185 login_db_.reset( | |
| 1186 new password_manager::LoginDatabase(test_login_db_file_path())); | |
| 1187 thread_.reset(new base::Thread("Chrome_PasswordStore_Thread")); | |
| 1188 ASSERT_TRUE(thread_->Start()); | |
| 1189 ASSERT_TRUE(thread_->task_runner()->PostTask( | |
| 1190 FROM_HERE, base::Bind(&PasswordStoreMacTest::InitLoginDatabase, | |
| 1191 base::Unretained(login_db_.get())))); | |
| 1192 CreateAndInitPasswordStore(login_db_.get()); | |
| 1193 // Make sure deferred initialization is performed before some tests start | |
| 1194 // accessing the |login_db| directly. | |
| 1195 FinishAsyncProcessing(); | |
| 1196 } | |
| 1197 | |
| 1198 void TearDown() override { | |
| 1199 ClosePasswordStore(); | |
| 1200 thread_.reset(); | |
| 1201 login_db_.reset(); | |
| 1202 // Whatever a test did, PasswordStoreMac stores only empty password values | 1266 // Whatever a test did, PasswordStoreMac stores only empty password values |
| 1203 // in LoginDatabase. The empty valus do not require encryption and therefore | 1267 // in LoginDatabase. The empty valus do not require encryption and therefore |
| 1204 // OSCrypt shouldn't call the Keychain. The histogram doesn't cover the | 1268 // OSCrypt shouldn't call the Keychain. The histogram doesn't cover the |
| 1205 // internet passwords. | 1269 // internet passwords. |
| 1206 if (histogram_tester_) { | 1270 if (histogram_tester_) { |
| 1207 histogram_tester_->ExpectTotalCount("OSX.Keychain.Access", 0); | 1271 histogram_tester_->ExpectTotalCount("OSX.Keychain.Access", 0); |
| 1208 } | 1272 } |
| 1209 } | 1273 } |
| 1210 | 1274 |
| 1211 static void InitLoginDatabase(password_manager::LoginDatabase* login_db) { | |
| 1212 ASSERT_TRUE(login_db->Init()); | |
| 1213 } | |
| 1214 | |
| 1215 void CreateAndInitPasswordStore(password_manager::LoginDatabase* login_db) { | |
| 1216 store_ = new PasswordStoreMac( | |
| 1217 base::ThreadTaskRunnerHandle::Get(), nullptr, | |
| 1218 make_scoped_ptr<AppleKeychain>(new MockAppleKeychain)); | |
| 1219 ASSERT_TRUE(thread_->task_runner()->PostTask( | |
| 1220 FROM_HERE, base::Bind(&PasswordStoreMac::InitWithTaskRunner, store_, | |
| 1221 thread_->task_runner()))); | |
| 1222 | |
| 1223 ASSERT_TRUE(thread_->task_runner()->PostTask( | |
| 1224 FROM_HERE, base::Bind(&PasswordStoreMac::set_login_metadata_db, store_, | |
| 1225 base::Unretained(login_db)))); | |
| 1226 } | |
| 1227 | |
| 1228 void ClosePasswordStore() { | |
| 1229 if (!store_) | |
| 1230 return; | |
| 1231 | |
| 1232 store_->Shutdown(); | |
| 1233 store_ = nullptr; | |
| 1234 } | |
| 1235 | |
| 1236 // Verifies that the given |form| can be properly stored so that it can be | 1275 // Verifies that the given |form| can be properly stored so that it can be |
| 1237 // retrieved by FillMatchingLogins() and GetAutofillableLogins(), and then it | 1276 // retrieved by FillMatchingLogins() and GetAutofillableLogins(), and then it |
| 1238 // can be properly removed. | 1277 // can be properly removed. |
| 1239 void VerifyCredentialLifecycle(const PasswordForm& form) { | 1278 void VerifyCredentialLifecycle(const PasswordForm& form) { |
| 1240 // Run everything twice to make sure no garbage is left behind that would | 1279 // Run everything twice to make sure no garbage is left behind that would |
| 1241 // prevent storing the form a second time. | 1280 // prevent storing the form a second time. |
| 1242 for (size_t iteration = 0; iteration < 2; ++iteration) { | 1281 for (size_t iteration = 0; iteration < 2; ++iteration) { |
| 1243 SCOPED_TRACE(testing::Message("Iteration: ") << iteration); | 1282 SCOPED_TRACE(testing::Message("Iteration: ") << iteration); |
| 1244 | 1283 |
| 1245 MockPasswordStoreConsumer mock_consumer; | 1284 MockPasswordStoreConsumer mock_consumer; |
| 1246 EXPECT_CALL(mock_consumer, OnGetPasswordStoreResultsConstRef(IsEmpty())) | 1285 EXPECT_CALL(mock_consumer, OnGetPasswordStoreResultsConstRef(IsEmpty())) |
| 1247 .WillOnce(QuitUIMessageLoop()); | 1286 .WillOnce(QuitUIMessageLoop()); |
| 1248 store()->GetAutofillableLogins(&mock_consumer); | 1287 store()->GetAutofillableLogins(&mock_consumer); |
| 1249 base::MessageLoop::current()->Run(); | 1288 base::MessageLoop::current()->Run(); |
| 1250 ::testing::Mock::VerifyAndClearExpectations(&mock_consumer); | 1289 ::testing::Mock::VerifyAndClearExpectations(&mock_consumer); |
| 1251 | 1290 |
| 1252 store()->AddLogin(form); | 1291 store()->AddLogin(form); |
| 1253 FinishAsyncProcessing(); | 1292 delegate_.FinishAsyncProcessing(); |
| 1254 | 1293 |
| 1255 PasswordForm returned_form; | 1294 PasswordForm returned_form; |
| 1256 EXPECT_CALL(mock_consumer, OnGetPasswordStoreResultsConstRef(SizeIs(1u))) | 1295 EXPECT_CALL(mock_consumer, OnGetPasswordStoreResultsConstRef(SizeIs(1u))) |
| 1257 .WillOnce( | 1296 .WillOnce( |
| 1258 DoAll(SaveACopyOfFirstForm(&returned_form), QuitUIMessageLoop())); | 1297 DoAll(SaveACopyOfFirstForm(&returned_form), QuitUIMessageLoop())); |
| 1259 | 1298 |
| 1260 // The query operations will also do some housekeeping: they will remove | 1299 // The query operations will also do some housekeeping: they will remove |
| 1261 // dangling credentials in the LoginDatabase without a matching Keychain | 1300 // dangling credentials in the LoginDatabase without a matching Keychain |
| 1262 // item when one is expected. If the logic that stores the Keychain item | 1301 // item when one is expected. If the logic that stores the Keychain item |
| 1263 // is incorrect, this will wipe the newly added form before the second | 1302 // is incorrect, this will wipe the newly added form before the second |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 1276 store()->GetLogins(query_form, PasswordStore::ALLOW_PROMPT, | 1315 store()->GetLogins(query_form, PasswordStore::ALLOW_PROMPT, |
| 1277 &mock_consumer); | 1316 &mock_consumer); |
| 1278 base::MessageLoop::current()->Run(); | 1317 base::MessageLoop::current()->Run(); |
| 1279 ::testing::Mock::VerifyAndClearExpectations(&mock_consumer); | 1318 ::testing::Mock::VerifyAndClearExpectations(&mock_consumer); |
| 1280 EXPECT_EQ(form, returned_form); | 1319 EXPECT_EQ(form, returned_form); |
| 1281 | 1320 |
| 1282 store()->RemoveLogin(form); | 1321 store()->RemoveLogin(form); |
| 1283 } | 1322 } |
| 1284 } | 1323 } |
| 1285 | 1324 |
| 1286 base::FilePath test_login_db_file_path() const { | |
| 1287 return db_dir_.path().Append(FILE_PATH_LITERAL("login.db")); | |
| 1288 } | |
| 1289 | |
| 1290 password_manager::LoginDatabase* login_db() const { | 1325 password_manager::LoginDatabase* login_db() const { |
| 1291 return store_->login_metadata_db(); | 1326 return store_->login_metadata_db(); |
| 1292 } | 1327 } |
| 1293 | 1328 |
| 1294 MockAppleKeychain* keychain() { | 1329 MockAppleKeychain* keychain() { |
| 1295 return static_cast<MockAppleKeychain*>(store_->keychain()); | 1330 return static_cast<MockAppleKeychain*>(store_->keychain()); |
| 1296 } | 1331 } |
| 1297 | 1332 |
| 1298 void FinishAsyncProcessing() { | 1333 PasswordStoreMac* store() { return store_; } |
| 1299 scoped_refptr<content::MessageLoopRunner> runner = | 1334 |
| 1300 new content::MessageLoopRunner; | 1335 password_manager::PasswordStoreMacTestDelegate& delegate() { |
| 1301 ASSERT_TRUE(thread_->task_runner()->PostTaskAndReply( | 1336 return delegate_; |
| 1302 FROM_HERE, base::Bind(&Noop), runner->QuitClosure())); | |
| 1303 runner->Run(); | |
| 1304 } | 1337 } |
| 1305 | 1338 |
| 1306 PasswordStoreMac* store() { return store_.get(); } | |
| 1307 | |
| 1308 protected: | 1339 protected: |
| 1309 base::MessageLoopForUI message_loop_; | |
| 1310 content::TestBrowserThread ui_thread_; | |
| 1311 // Thread that the synchronous methods are run on. | |
| 1312 scoped_ptr<base::Thread> thread_; | |
| 1313 | |
| 1314 base::ScopedTempDir db_dir_; | |
| 1315 scoped_ptr<password_manager::LoginDatabase> login_db_; | |
| 1316 scoped_refptr<PasswordStoreMac> store_; | |
| 1317 scoped_ptr<base::HistogramTester> histogram_tester_; | 1340 scoped_ptr<base::HistogramTester> histogram_tester_; |
| 1341 password_manager::PasswordStoreMacTestDelegate delegate_; | |
| 1342 PasswordStoreMac* store_; | |
| 1318 }; | 1343 }; |
| 1319 | 1344 |
| 1320 TEST_F(PasswordStoreMacTest, TestStoreUpdate) { | 1345 TEST_F(PasswordStoreMacTest, TestStoreUpdate) { |
| 1321 // Insert a password into both the database and the keychain. | 1346 // Insert a password into both the database and the keychain. |
| 1322 // This is done manually, rather than through store_->AddLogin, because the | 1347 // This is done manually, rather than through store_->AddLogin, because the |
| 1323 // Mock Keychain isn't smart enough to be able to support update generically, | 1348 // Mock Keychain isn't smart enough to be able to support update generically, |
| 1324 // so some.domain.com triggers special handling to test it that make inserting | 1349 // so some.domain.com triggers special handling to test it that make inserting |
| 1325 // fail. | 1350 // fail. |
| 1326 PasswordFormData joint_data = { | 1351 PasswordFormData joint_data = { |
| 1327 PasswordForm::SCHEME_HTML, "http://some.domain.com/", | 1352 PasswordForm::SCHEME_HTML, "http://some.domain.com/", |
| (...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1376 true, false, 2 }, | 1401 true, false, 2 }, |
| 1377 NULL, | 1402 NULL, |
| 1378 }, | 1403 }, |
| 1379 }; | 1404 }; |
| 1380 for (unsigned int i = 0; i < arraysize(updates); ++i) { | 1405 for (unsigned int i = 0; i < arraysize(updates); ++i) { |
| 1381 scoped_ptr<PasswordForm> form = | 1406 scoped_ptr<PasswordForm> form = |
| 1382 CreatePasswordFormFromDataForTesting(updates[i].form_data); | 1407 CreatePasswordFormFromDataForTesting(updates[i].form_data); |
| 1383 store_->UpdateLogin(*form); | 1408 store_->UpdateLogin(*form); |
| 1384 } | 1409 } |
| 1385 | 1410 |
| 1386 FinishAsyncProcessing(); | 1411 delegate_.FinishAsyncProcessing(); |
| 1387 | 1412 |
| 1388 MacKeychainPasswordFormAdapter keychain_adapter(keychain()); | 1413 MacKeychainPasswordFormAdapter keychain_adapter(keychain()); |
| 1389 for (unsigned int i = 0; i < arraysize(updates); ++i) { | 1414 for (unsigned int i = 0; i < arraysize(updates); ++i) { |
| 1390 scoped_ptr<PasswordForm> query_form = | 1415 scoped_ptr<PasswordForm> query_form = |
| 1391 CreatePasswordFormFromDataForTesting(updates[i].form_data); | 1416 CreatePasswordFormFromDataForTesting(updates[i].form_data); |
| 1392 | 1417 |
| 1393 ScopedVector<autofill::PasswordForm> matching_items = | 1418 ScopedVector<autofill::PasswordForm> matching_items = |
| 1394 keychain_adapter.PasswordsFillingForm(query_form->signon_realm, | 1419 keychain_adapter.PasswordsFillingForm(query_form->signon_realm, |
| 1395 query_form->scheme); | 1420 query_form->scheme); |
| 1396 if (updates[i].password) { | 1421 if (updates[i].password) { |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1453 // 3. Add the returned password for m.facebook.com. | 1478 // 3. Add the returned password for m.facebook.com. |
| 1454 returned_form.signon_realm = "http://m.facebook.com"; | 1479 returned_form.signon_realm = "http://m.facebook.com"; |
| 1455 returned_form.origin = GURL("http://m.facebook.com/index.html"); | 1480 returned_form.origin = GURL("http://m.facebook.com/index.html"); |
| 1456 EXPECT_EQ(AddChangeForForm(returned_form), | 1481 EXPECT_EQ(AddChangeForForm(returned_form), |
| 1457 login_db()->AddLogin(returned_form)); | 1482 login_db()->AddLogin(returned_form)); |
| 1458 owned_keychain_adapter.AddPassword(m_form); | 1483 owned_keychain_adapter.AddPassword(m_form); |
| 1459 | 1484 |
| 1460 // 4. Remove both passwords. | 1485 // 4. Remove both passwords. |
| 1461 store_->RemoveLogin(*www_form); | 1486 store_->RemoveLogin(*www_form); |
| 1462 store_->RemoveLogin(m_form); | 1487 store_->RemoveLogin(m_form); |
| 1463 FinishAsyncProcessing(); | 1488 delegate_.FinishAsyncProcessing(); |
| 1464 | 1489 |
| 1465 // No trace of www.facebook.com. | 1490 // No trace of www.facebook.com. |
| 1466 ScopedVector<autofill::PasswordForm> matching_items = | 1491 ScopedVector<autofill::PasswordForm> matching_items = |
| 1467 owned_keychain_adapter.PasswordsFillingForm(www_form->signon_realm, | 1492 owned_keychain_adapter.PasswordsFillingForm(www_form->signon_realm, |
| 1468 www_form->scheme); | 1493 www_form->scheme); |
| 1469 EXPECT_EQ(0u, matching_items.size()); | 1494 EXPECT_EQ(0u, matching_items.size()); |
| 1470 EXPECT_TRUE(login_db()->GetLogins(*www_form, &matching_items)); | 1495 EXPECT_TRUE(login_db()->GetLogins(*www_form, &matching_items)); |
| 1471 EXPECT_EQ(0u, matching_items.size()); | 1496 EXPECT_EQ(0u, matching_items.size()); |
| 1472 // No trace of m.facebook.com. | 1497 // No trace of m.facebook.com. |
| 1473 matching_items = owned_keychain_adapter.PasswordsFillingForm( | 1498 matching_items = owned_keychain_adapter.PasswordsFillingForm( |
| 1474 m_form.signon_realm, m_form.scheme); | 1499 m_form.signon_realm, m_form.scheme); |
| 1475 EXPECT_EQ(0u, matching_items.size()); | 1500 EXPECT_EQ(0u, matching_items.size()); |
| 1476 EXPECT_TRUE(login_db()->GetLogins(m_form, &matching_items)); | 1501 EXPECT_TRUE(login_db()->GetLogins(m_form, &matching_items)); |
| 1477 EXPECT_EQ(0u, matching_items.size()); | 1502 EXPECT_EQ(0u, matching_items.size()); |
| 1478 } | 1503 } |
| 1479 | 1504 |
| 1480 namespace { | 1505 namespace { |
| 1481 | 1506 |
| 1482 class PasswordsChangeObserver : | 1507 class PasswordsChangeObserver : |
| 1483 public password_manager::PasswordStore::Observer { | 1508 public password_manager::PasswordStore::Observer { |
| 1484 public: | 1509 public: |
| 1485 PasswordsChangeObserver(PasswordStoreMac* store) : observer_(this) { | 1510 PasswordsChangeObserver(PasswordStoreMac* store) : observer_(this) { |
| 1486 observer_.Add(store); | 1511 observer_.Add(store); |
| 1487 } | 1512 } |
| 1488 | 1513 |
| 1489 void WaitAndVerify(PasswordStoreMacTest* test) { | 1514 void WaitAndVerify(PasswordStoreMacTest* test) { |
| 1490 test->FinishAsyncProcessing(); | 1515 test->delegate().FinishAsyncProcessing(); |
| 1491 ::testing::Mock::VerifyAndClearExpectations(this); | 1516 ::testing::Mock::VerifyAndClearExpectations(this); |
| 1492 } | 1517 } |
| 1493 | 1518 |
| 1494 // password_manager::PasswordStore::Observer: | 1519 // password_manager::PasswordStore::Observer: |
| 1495 MOCK_METHOD1(OnLoginsChanged, | 1520 MOCK_METHOD1(OnLoginsChanged, |
| 1496 void(const password_manager::PasswordStoreChangeList& changes)); | 1521 void(const password_manager::PasswordStoreChangeList& changes)); |
| 1497 | 1522 |
| 1498 private: | 1523 private: |
| 1499 ScopedObserver<password_manager::PasswordStore, | 1524 ScopedObserver<password_manager::PasswordStore, |
| 1500 PasswordsChangeObserver> observer_; | 1525 PasswordsChangeObserver> observer_; |
| (...skipping 135 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1636 CreatePasswordFormFromDataForTesting(www_form_data1); | 1661 CreatePasswordFormFromDataForTesting(www_form_data1); |
| 1637 EXPECT_TRUE(owned_keychain_adapter.AddPassword(*www_form)); | 1662 EXPECT_TRUE(owned_keychain_adapter.AddPassword(*www_form)); |
| 1638 | 1663 |
| 1639 // Add a password from the current profile. | 1664 // Add a password from the current profile. |
| 1640 PasswordFormData www_form_data2 = { | 1665 PasswordFormData www_form_data2 = { |
| 1641 PasswordForm::SCHEME_HTML, "http://www.facebook.com/", | 1666 PasswordForm::SCHEME_HTML, "http://www.facebook.com/", |
| 1642 "http://www.facebook.com/index.html", "login", L"username", L"password", | 1667 "http://www.facebook.com/index.html", "login", L"username", L"password", |
| 1643 L"submit", L"not_joe_user", L"12345", true, false, 1 }; | 1668 L"submit", L"not_joe_user", L"12345", true, false, 1 }; |
| 1644 www_form = CreatePasswordFormFromDataForTesting(www_form_data2); | 1669 www_form = CreatePasswordFormFromDataForTesting(www_form_data2); |
| 1645 store_->AddLogin(*www_form); | 1670 store_->AddLogin(*www_form); |
| 1646 FinishAsyncProcessing(); | 1671 delegate_.FinishAsyncProcessing(); |
| 1647 | 1672 |
| 1648 ScopedVector<PasswordForm> matching_items; | 1673 ScopedVector<PasswordForm> matching_items; |
| 1649 EXPECT_TRUE(login_db()->GetLogins(*www_form, &matching_items)); | 1674 EXPECT_TRUE(login_db()->GetLogins(*www_form, &matching_items)); |
| 1650 EXPECT_EQ(1u, matching_items.size()); | 1675 EXPECT_EQ(1u, matching_items.size()); |
| 1651 | 1676 |
| 1652 store_->RemoveLoginsCreatedBetween(base::Time(), base::Time(), | 1677 store_->RemoveLoginsCreatedBetween(base::Time(), base::Time(), |
| 1653 base::Closure()); | 1678 base::Closure()); |
| 1654 FinishAsyncProcessing(); | 1679 delegate_.FinishAsyncProcessing(); |
| 1655 | 1680 |
| 1656 // Check the second facebook form is gone. | 1681 // Check the second facebook form is gone. |
| 1657 EXPECT_TRUE(login_db()->GetLogins(*www_form, &matching_items)); | 1682 EXPECT_TRUE(login_db()->GetLogins(*www_form, &matching_items)); |
| 1658 EXPECT_EQ(0u, matching_items.size()); | 1683 EXPECT_EQ(0u, matching_items.size()); |
| 1659 | 1684 |
| 1660 // Check the first facebook form is still there. | 1685 // Check the first facebook form is still there. |
| 1661 matching_items = owned_keychain_adapter.PasswordsFillingForm( | 1686 matching_items = owned_keychain_adapter.PasswordsFillingForm( |
| 1662 www_form->signon_realm, www_form->scheme); | 1687 www_form->signon_realm, www_form->scheme); |
| 1663 ASSERT_EQ(1u, matching_items.size()); | 1688 ASSERT_EQ(1u, matching_items.size()); |
| 1664 EXPECT_EQ(ASCIIToUTF16("joe_user"), matching_items[0]->username_value); | 1689 EXPECT_EQ(ASCIIToUTF16("joe_user"), matching_items[0]->username_value); |
| 1665 | 1690 |
| 1666 // Check the third-party password is still there. | 1691 // Check the third-party password is still there. |
| 1667 owned_keychain_adapter.SetFindsOnlyOwnedItems(false); | 1692 owned_keychain_adapter.SetFindsOnlyOwnedItems(false); |
| 1668 matching_items = owned_keychain_adapter.PasswordsFillingForm( | 1693 matching_items = owned_keychain_adapter.PasswordsFillingForm( |
| 1669 "http://some.domain.com/insecure.html", PasswordForm::SCHEME_HTML); | 1694 "http://some.domain.com/insecure.html", PasswordForm::SCHEME_HTML); |
| 1670 ASSERT_EQ(1u, matching_items.size()); | 1695 ASSERT_EQ(1u, matching_items.size()); |
| 1671 } | 1696 } |
| 1672 | 1697 |
| 1673 // Add a facebook form to the store but not to the keychain. The form is to be | 1698 // Add a facebook form to the store but not to the keychain. The form is to be |
| 1674 // implicitly deleted. However, the observers shouldn't get notified about | 1699 // implicitly deleted. However, the observers shouldn't get notified about |
| 1675 // deletion of non-existent forms like m.facebook.com. | 1700 // deletion of non-existent forms like m.facebook.com. |
| 1676 TEST_F(PasswordStoreMacTest, SilentlyRemoveOrphanedForm) { | 1701 TEST_F(PasswordStoreMacTest, SilentlyRemoveOrphanedForm) { |
| 1677 testing::StrictMock<MockPasswordStoreObserver> mock_observer; | 1702 testing::StrictMock<password_manager::MockPasswordStoreObserver> |
| 1703 mock_observer; | |
| 1678 store()->AddObserver(&mock_observer); | 1704 store()->AddObserver(&mock_observer); |
| 1679 | 1705 |
| 1680 // 1. Add a password for www.facebook.com to the LoginDatabase. | 1706 // 1. Add a password for www.facebook.com to the LoginDatabase. |
| 1681 PasswordFormData www_form_data = { | 1707 PasswordFormData www_form_data = { |
| 1682 PasswordForm::SCHEME_HTML, "http://www.facebook.com/", | 1708 PasswordForm::SCHEME_HTML, "http://www.facebook.com/", |
| 1683 "http://www.facebook.com/index.html", "login", | 1709 "http://www.facebook.com/index.html", "login", |
| 1684 L"username", L"password", L"submit", L"joe_user", L"", true, false, 1 | 1710 L"username", L"password", L"submit", L"joe_user", L"", true, false, 1 |
| 1685 }; | 1711 }; |
| 1686 scoped_ptr<PasswordForm> www_form( | 1712 scoped_ptr<PasswordForm> www_form( |
| 1687 CreatePasswordFormFromDataForTesting(www_form_data)); | 1713 CreatePasswordFormFromDataForTesting(www_form_data)); |
| (...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1763 form2.password_value = ASCIIToUTF16("my_password"); | 1789 form2.password_value = ASCIIToUTF16("my_password"); |
| 1764 | 1790 |
| 1765 PasswordForm blacklisted_form; | 1791 PasswordForm blacklisted_form; |
| 1766 blacklisted_form.origin = GURL("http://badsite.com/Login"); | 1792 blacklisted_form.origin = GURL("http://badsite.com/Login"); |
| 1767 blacklisted_form.signon_realm = "http://badsite.com/"; | 1793 blacklisted_form.signon_realm = "http://badsite.com/"; |
| 1768 blacklisted_form.blacklisted_by_user = true; | 1794 blacklisted_form.blacklisted_by_user = true; |
| 1769 | 1795 |
| 1770 store()->AddLogin(form1); | 1796 store()->AddLogin(form1); |
| 1771 store()->AddLogin(form2); | 1797 store()->AddLogin(form2); |
| 1772 store()->AddLogin(blacklisted_form); | 1798 store()->AddLogin(blacklisted_form); |
| 1773 FinishAsyncProcessing(); | 1799 delegate_.FinishAsyncProcessing(); |
| 1774 | 1800 |
| 1775 ASSERT_TRUE(base::PostTaskAndReplyWithResult( | 1801 ASSERT_TRUE(base::PostTaskAndReplyWithResult( |
| 1776 thread_->task_runner().get(), FROM_HERE, | 1802 delegate_.thread()->task_runner().get(), FROM_HERE, |
| 1777 base::Bind(&PasswordStoreMac::ImportFromKeychain, store()), | 1803 base::Bind(&PasswordStoreMac::ImportFromKeychain, store()), |
| 1778 base::Bind(&CheckMigrationResult, PasswordStoreMac::MIGRATION_OK))); | 1804 base::Bind(&CheckMigrationResult, PasswordStoreMac::MIGRATION_OK))); |
| 1779 FinishAsyncProcessing(); | 1805 delegate_.FinishAsyncProcessing(); |
| 1780 | 1806 |
| 1781 // The password should be stored in the database by now. | 1807 // The password should be stored in the database by now. |
| 1782 ScopedVector<PasswordForm> matching_items; | 1808 ScopedVector<PasswordForm> matching_items; |
| 1783 EXPECT_TRUE(login_db()->GetLogins(form1, &matching_items)); | 1809 EXPECT_TRUE(login_db()->GetLogins(form1, &matching_items)); |
| 1784 ASSERT_EQ(1u, matching_items.size()); | 1810 ASSERT_EQ(1u, matching_items.size()); |
| 1785 EXPECT_EQ(form1, *matching_items[0]); | 1811 EXPECT_EQ(form1, *matching_items[0]); |
| 1786 | 1812 |
| 1787 EXPECT_TRUE(login_db()->GetLogins(form2, &matching_items)); | 1813 EXPECT_TRUE(login_db()->GetLogins(form2, &matching_items)); |
| 1788 ASSERT_EQ(1u, matching_items.size()); | 1814 ASSERT_EQ(1u, matching_items.size()); |
| 1789 EXPECT_EQ(form2, *matching_items[0]); | 1815 EXPECT_EQ(form2, *matching_items[0]); |
| (...skipping 11 matching lines...) Expand all Loading... | |
| 1801 // Import a federated credential while the Keychain is locked. | 1827 // Import a federated credential while the Keychain is locked. |
| 1802 TEST_F(PasswordStoreMacTest, ImportFederatedFromLockedKeychain) { | 1828 TEST_F(PasswordStoreMacTest, ImportFederatedFromLockedKeychain) { |
| 1803 keychain()->set_locked(true); | 1829 keychain()->set_locked(true); |
| 1804 PasswordForm form1; | 1830 PasswordForm form1; |
| 1805 form1.origin = GURL("http://example.com/Login"); | 1831 form1.origin = GURL("http://example.com/Login"); |
| 1806 form1.signon_realm = "http://example.com/"; | 1832 form1.signon_realm = "http://example.com/"; |
| 1807 form1.username_value = ASCIIToUTF16("my_username"); | 1833 form1.username_value = ASCIIToUTF16("my_username"); |
| 1808 form1.federation_url = GURL("https://accounts.google.com/"); | 1834 form1.federation_url = GURL("https://accounts.google.com/"); |
| 1809 | 1835 |
| 1810 store()->AddLogin(form1); | 1836 store()->AddLogin(form1); |
| 1811 FinishAsyncProcessing(); | 1837 delegate_.FinishAsyncProcessing(); |
| 1812 ASSERT_TRUE(base::PostTaskAndReplyWithResult( | 1838 ASSERT_TRUE(base::PostTaskAndReplyWithResult( |
| 1813 thread_->task_runner().get(), FROM_HERE, | 1839 delegate_.thread()->task_runner().get(), FROM_HERE, |
| 1814 base::Bind(&PasswordStoreMac::ImportFromKeychain, store()), | 1840 base::Bind(&PasswordStoreMac::ImportFromKeychain, store()), |
| 1815 base::Bind(&CheckMigrationResult, PasswordStoreMac::MIGRATION_OK))); | 1841 base::Bind(&CheckMigrationResult, PasswordStoreMac::MIGRATION_OK))); |
| 1816 FinishAsyncProcessing(); | 1842 delegate_.FinishAsyncProcessing(); |
| 1817 | 1843 |
| 1818 ScopedVector<PasswordForm> matching_items; | 1844 ScopedVector<PasswordForm> matching_items; |
| 1819 EXPECT_TRUE(login_db()->GetLogins(form1, &matching_items)); | 1845 EXPECT_TRUE(login_db()->GetLogins(form1, &matching_items)); |
| 1820 ASSERT_EQ(1u, matching_items.size()); | 1846 ASSERT_EQ(1u, matching_items.size()); |
| 1821 EXPECT_EQ(form1, *matching_items[0]); | 1847 EXPECT_EQ(form1, *matching_items[0]); |
| 1822 } | 1848 } |
| 1823 | 1849 |
| 1824 // Try to import while the Keychain is locked but the encryption key had been | 1850 // Try to import while the Keychain is locked but the encryption key had been |
| 1825 // read earlier. | 1851 // read earlier. |
| 1826 TEST_F(PasswordStoreMacTest, ImportFromLockedKeychainError) { | 1852 TEST_F(PasswordStoreMacTest, ImportFromLockedKeychainError) { |
| 1827 PasswordForm form1; | 1853 PasswordForm form1; |
| 1828 form1.origin = GURL("http://accounts.google.com/LoginAuth"); | 1854 form1.origin = GURL("http://accounts.google.com/LoginAuth"); |
| 1829 form1.signon_realm = "http://accounts.google.com/"; | 1855 form1.signon_realm = "http://accounts.google.com/"; |
| 1830 form1.username_value = ASCIIToUTF16("my_username"); | 1856 form1.username_value = ASCIIToUTF16("my_username"); |
| 1831 form1.password_value = ASCIIToUTF16("my_password"); | 1857 form1.password_value = ASCIIToUTF16("my_password"); |
| 1832 store()->AddLogin(form1); | 1858 store()->AddLogin(form1); |
| 1833 FinishAsyncProcessing(); | 1859 delegate_.FinishAsyncProcessing(); |
| 1834 | 1860 |
| 1835 // Add a second keychain item matching the Database entry. | 1861 // Add a second keychain item matching the Database entry. |
| 1836 PasswordForm form2 = form1; | 1862 PasswordForm form2 = form1; |
| 1837 form2.origin = GURL("http://accounts.google.com/Login"); | 1863 form2.origin = GURL("http://accounts.google.com/Login"); |
| 1838 form2.password_value = ASCIIToUTF16("1234"); | 1864 form2.password_value = ASCIIToUTF16("1234"); |
| 1839 MacKeychainPasswordFormAdapter adapter(keychain()); | 1865 MacKeychainPasswordFormAdapter adapter(keychain()); |
| 1840 EXPECT_TRUE(adapter.AddPassword(form2)); | 1866 EXPECT_TRUE(adapter.AddPassword(form2)); |
| 1841 | 1867 |
| 1842 keychain()->set_locked(true); | 1868 keychain()->set_locked(true); |
| 1843 ASSERT_TRUE(base::PostTaskAndReplyWithResult( | 1869 ASSERT_TRUE(base::PostTaskAndReplyWithResult( |
| 1844 thread_->task_runner().get(), FROM_HERE, | 1870 delegate_.thread()->task_runner().get(), FROM_HERE, |
| 1845 base::Bind(&PasswordStoreMac::ImportFromKeychain, store()), | 1871 base::Bind(&PasswordStoreMac::ImportFromKeychain, store()), |
| 1846 base::Bind(&CheckMigrationResult, PasswordStoreMac::KEYCHAIN_BLOCKED))); | 1872 base::Bind(&CheckMigrationResult, PasswordStoreMac::KEYCHAIN_BLOCKED))); |
| 1847 FinishAsyncProcessing(); | 1873 delegate_.FinishAsyncProcessing(); |
| 1848 | 1874 |
| 1849 ScopedVector<PasswordForm> matching_items; | 1875 ScopedVector<PasswordForm> matching_items; |
| 1850 EXPECT_TRUE(login_db()->GetLogins(form1, &matching_items)); | 1876 EXPECT_TRUE(login_db()->GetLogins(form1, &matching_items)); |
| 1851 ASSERT_EQ(1u, matching_items.size()); | 1877 ASSERT_EQ(1u, matching_items.size()); |
| 1852 EXPECT_EQ(base::string16(), matching_items[0]->password_value); | 1878 EXPECT_EQ(base::string16(), matching_items[0]->password_value); |
| 1853 | 1879 |
| 1854 histogram_tester_->ExpectUniqueSample( | 1880 histogram_tester_->ExpectUniqueSample( |
| 1855 "PasswordManager.KeychainMigration.NumPasswordsOnFailure", 1, 1); | 1881 "PasswordManager.KeychainMigration.NumPasswordsOnFailure", 1, 1); |
| 1856 histogram_tester_->ExpectUniqueSample( | 1882 histogram_tester_->ExpectUniqueSample( |
| 1857 "PasswordManager.KeychainMigration.NumFailedPasswords", 1, 1); | 1883 "PasswordManager.KeychainMigration.NumFailedPasswords", 1, 1); |
| 1858 histogram_tester_->ExpectUniqueSample( | 1884 histogram_tester_->ExpectUniqueSample( |
| 1859 "PasswordManager.KeychainMigration.NumChromeOwnedInaccessiblePasswords", | 1885 "PasswordManager.KeychainMigration.NumChromeOwnedInaccessiblePasswords", |
| 1860 2, 1); | 1886 2, 1); |
| 1861 // Don't test the encryption key access. | 1887 // Don't test the encryption key access. |
| 1862 histogram_tester_.reset(); | 1888 histogram_tester_.reset(); |
| 1863 } | 1889 } |
| OLD | NEW |