Index: components/password_manager/core/browser/form_fetcher_impl_unittest.cc |
diff --git a/components/password_manager/core/browser/form_fetcher_impl_unittest.cc b/components/password_manager/core/browser/form_fetcher_impl_unittest.cc |
index 1059fc7c016961e38afb31cde3e5b886c51d9d55..301bc7ec7bd75e0504d830133eb0958948543854 100644 |
--- a/components/password_manager/core/browser/form_fetcher_impl_unittest.cc |
+++ b/components/password_manager/core/browser/form_fetcher_impl_unittest.cc |
@@ -11,10 +11,15 @@ |
#include "base/macros.h" |
#include "base/memory/ptr_util.h" |
+#include "base/message_loop/message_loop.h" |
+#include "base/run_loop.h" |
#include "base/strings/string16.h" |
#include "base/strings/string_piece.h" |
#include "base/strings/utf_string_conversions.h" |
+#include "build/build_config.h" |
#include "components/autofill/core/common/password_form.h" |
+#include "components/password_manager/core/browser/mock_password_store.h" |
+#include "components/password_manager/core/browser/password_store.h" |
#include "components/password_manager/core/browser/statistics_table.h" |
#include "components/password_manager/core/browser/stub_credentials_filter.h" |
#include "components/password_manager/core/browser/stub_password_manager_client.h" |
@@ -29,6 +34,7 @@ using base::StringPiece; |
using testing::_; |
using testing::IsEmpty; |
using testing::Pointee; |
+using testing::Return; |
using testing::UnorderedElementsAre; |
namespace password_manager { |
@@ -80,13 +86,18 @@ class FakePasswordManagerClient : public StubPasswordManagerClient { |
filter_ = std::move(filter); |
} |
+ void set_store(PasswordStore* store) { store_ = store; } |
+ |
private: |
const CredentialsFilter* GetStoreResultFilter() const override { |
return filter_ ? filter_.get() |
: StubPasswordManagerClient::GetStoreResultFilter(); |
} |
+ PasswordStore* GetPasswordStore() const override { return store_; } |
+ |
std::unique_ptr<CredentialsFilter> filter_; |
+ PasswordStore* store_ = nullptr; |
DISALLOW_COPY_AND_ASSIGN(FakePasswordManagerClient); |
}; |
@@ -124,70 +135,95 @@ PasswordForm CreateAndroidFederated() { |
class FormFetcherImplTest : public testing::Test { |
public: |
- FormFetcherImplTest() : form_fetcher_(&client_) {} |
+ FormFetcherImplTest() |
+ : form_digest_(PasswordForm::SCHEME_HTML, |
+ "http://accounts.google.com", |
+ GURL("http://accounts.google.com/a/LoginAuth")) { |
+ mock_store_ = new MockPasswordStore(); |
+ client_.set_store(mock_store_.get()); |
+ |
+ form_fetcher_ = base::MakeUnique<FormFetcherImpl>(form_digest_, &client_); |
+ } |
- ~FormFetcherImplTest() override = default; |
+ ~FormFetcherImplTest() override { mock_store_->ShutdownOnUIThread(); } |
protected: |
+ // A wrapper around form_fetcher_.Fetch(), adding the call expectations. |
+ void Fetch() { |
+#if !defined(OS_IOS) && !defined(OS_ANDROID) |
+ EXPECT_CALL(*mock_store_, GetSiteStatsMock(_)) |
+ .WillOnce(Return(std::vector<InteractionsStats*>())); |
+#endif |
+ EXPECT_CALL(*mock_store_, GetLogins(form_digest_, form_fetcher_.get())); |
+ form_fetcher_->Fetch(); |
+ base::RunLoop().RunUntilIdle(); |
+ testing::Mock::VerifyAndClearExpectations(mock_store_.get()); |
+ } |
+ |
+ base::MessageLoop message_loop_; // Used by mock_store_. |
+ PasswordStore::FormDigest form_digest_; |
+ std::unique_ptr<FormFetcherImpl> form_fetcher_; |
+ MockConsumer consumer_; |
+ scoped_refptr<MockPasswordStore> mock_store_; |
FakePasswordManagerClient client_; |
- FormFetcherImpl form_fetcher_; |
- testing::NiceMock<MockConsumer> consumer_; |
private: |
DISALLOW_COPY_AND_ASSIGN(FormFetcherImplTest); |
}; |
-// Check that no PasswordStore results are handled correctly. |
+// Check that the absence of PasswordStore results is handled correctly. |
TEST_F(FormFetcherImplTest, NoStoreResults) { |
+ Fetch(); |
EXPECT_CALL(consumer_, ProcessMatches(_, _)).Times(0); |
- form_fetcher_.set_state(FormFetcher::State::WAITING); |
- form_fetcher_.AddConsumer(&consumer_); |
- EXPECT_EQ(FormFetcher::State::WAITING, form_fetcher_.GetState()); |
+ form_fetcher_->AddConsumer(&consumer_); |
+ EXPECT_EQ(FormFetcher::State::WAITING, form_fetcher_->GetState()); |
} |
// Check that empty PasswordStore results are handled correctly. |
TEST_F(FormFetcherImplTest, Empty) { |
- form_fetcher_.AddConsumer(&consumer_); |
- form_fetcher_.set_state(FormFetcher::State::WAITING); |
+ Fetch(); |
+ form_fetcher_->AddConsumer(&consumer_); |
EXPECT_CALL(consumer_, ProcessMatches(IsEmpty(), 0u)); |
- form_fetcher_.SetResults(std::vector<std::unique_ptr<PasswordForm>>()); |
- EXPECT_EQ(FormFetcher::State::NOT_WAITING, form_fetcher_.GetState()); |
- EXPECT_THAT(form_fetcher_.GetFederatedMatches(), IsEmpty()); |
+ form_fetcher_->OnGetPasswordStoreResults( |
+ std::vector<std::unique_ptr<PasswordForm>>()); |
+ EXPECT_EQ(FormFetcher::State::NOT_WAITING, form_fetcher_->GetState()); |
+ EXPECT_THAT(form_fetcher_->GetFederatedMatches(), IsEmpty()); |
} |
// Check that non-federated PasswordStore results are handled correctly. |
TEST_F(FormFetcherImplTest, NonFederated) { |
+ Fetch(); |
PasswordForm non_federated = CreateNonFederated(); |
- form_fetcher_.AddConsumer(&consumer_); |
- form_fetcher_.set_state(FormFetcher::State::WAITING); |
+ form_fetcher_->AddConsumer(&consumer_); |
std::vector<std::unique_ptr<PasswordForm>> results; |
results.push_back(base::MakeUnique<PasswordForm>(non_federated)); |
EXPECT_CALL(consumer_, |
ProcessMatches(UnorderedElementsAre(Pointee(non_federated)), 0u)); |
- form_fetcher_.SetResults(std::move(results)); |
- EXPECT_EQ(FormFetcher::State::NOT_WAITING, form_fetcher_.GetState()); |
- EXPECT_THAT(form_fetcher_.GetFederatedMatches(), IsEmpty()); |
+ form_fetcher_->OnGetPasswordStoreResults(std::move(results)); |
+ EXPECT_EQ(FormFetcher::State::NOT_WAITING, form_fetcher_->GetState()); |
+ EXPECT_THAT(form_fetcher_->GetFederatedMatches(), IsEmpty()); |
} |
// Check that federated PasswordStore results are handled correctly. |
TEST_F(FormFetcherImplTest, Federated) { |
+ Fetch(); |
PasswordForm federated = CreateFederated(); |
PasswordForm android_federated = CreateAndroidFederated(); |
- form_fetcher_.AddConsumer(&consumer_); |
- form_fetcher_.set_state(FormFetcher::State::WAITING); |
+ form_fetcher_->AddConsumer(&consumer_); |
std::vector<std::unique_ptr<PasswordForm>> results; |
results.push_back(base::MakeUnique<PasswordForm>(federated)); |
results.push_back(base::MakeUnique<PasswordForm>(android_federated)); |
EXPECT_CALL(consumer_, ProcessMatches(IsEmpty(), 0u)); |
- form_fetcher_.SetResults(std::move(results)); |
- EXPECT_EQ(FormFetcher::State::NOT_WAITING, form_fetcher_.GetState()); |
+ form_fetcher_->OnGetPasswordStoreResults(std::move(results)); |
+ EXPECT_EQ(FormFetcher::State::NOT_WAITING, form_fetcher_->GetState()); |
EXPECT_THAT( |
- form_fetcher_.GetFederatedMatches(), |
+ form_fetcher_->GetFederatedMatches(), |
UnorderedElementsAre(Pointee(federated), Pointee(android_federated))); |
} |
// Check that mixed PasswordStore results are handled correctly. |
TEST_F(FormFetcherImplTest, Mixed) { |
+ Fetch(); |
PasswordForm federated1 = CreateFederated(); |
federated1.username_value = ASCIIToUTF16("user"); |
PasswordForm federated2 = CreateFederated(); |
@@ -201,8 +237,7 @@ TEST_F(FormFetcherImplTest, Mixed) { |
PasswordForm non_federated3 = CreateNonFederated(); |
non_federated3.username_value = ASCIIToUTF16("user_D"); |
- form_fetcher_.AddConsumer(&consumer_); |
- form_fetcher_.set_state(FormFetcher::State::WAITING); |
+ form_fetcher_->AddConsumer(&consumer_); |
std::vector<std::unique_ptr<PasswordForm>> results; |
results.push_back(base::MakeUnique<PasswordForm>(federated1)); |
results.push_back(base::MakeUnique<PasswordForm>(federated2)); |
@@ -215,15 +250,16 @@ TEST_F(FormFetcherImplTest, Mixed) { |
Pointee(non_federated2), |
Pointee(non_federated3)), |
0u)); |
- form_fetcher_.SetResults(std::move(results)); |
- EXPECT_EQ(FormFetcher::State::NOT_WAITING, form_fetcher_.GetState()); |
- EXPECT_THAT(form_fetcher_.GetFederatedMatches(), |
+ form_fetcher_->OnGetPasswordStoreResults(std::move(results)); |
+ EXPECT_EQ(FormFetcher::State::NOT_WAITING, form_fetcher_->GetState()); |
+ EXPECT_THAT(form_fetcher_->GetFederatedMatches(), |
UnorderedElementsAre(Pointee(federated1), Pointee(federated2), |
Pointee(federated3))); |
} |
// Check that PasswordStore results are filtered correctly. |
TEST_F(FormFetcherImplTest, Filtered) { |
+ Fetch(); |
PasswordForm federated = CreateFederated(); |
federated.username_value = ASCIIToUTF16("user"); |
PasswordForm non_federated1 = CreateNonFederated(); |
@@ -234,8 +270,7 @@ TEST_F(FormFetcherImplTest, Filtered) { |
// Set up a filter to remove all credentials with the username "user". |
client_.set_filter(base::MakeUnique<NameFilter>("user")); |
- form_fetcher_.AddConsumer(&consumer_); |
- form_fetcher_.set_state(FormFetcher::State::WAITING); |
+ form_fetcher_->AddConsumer(&consumer_); |
std::vector<std::unique_ptr<PasswordForm>> results; |
results.push_back(base::MakeUnique<PasswordForm>(federated)); |
results.push_back(base::MakeUnique<PasswordForm>(non_federated1)); |
@@ -245,20 +280,88 @@ TEST_F(FormFetcherImplTest, Filtered) { |
EXPECT_CALL(consumer_, |
ProcessMatches(UnorderedElementsAre(Pointee(non_federated2)), |
kNumFiltered)); |
- form_fetcher_.SetResults(std::move(results)); |
- EXPECT_EQ(FormFetcher::State::NOT_WAITING, form_fetcher_.GetState()); |
+ form_fetcher_->OnGetPasswordStoreResults(std::move(results)); |
+ EXPECT_EQ(FormFetcher::State::NOT_WAITING, form_fetcher_->GetState()); |
// However, federated results should not be filtered. |
- EXPECT_THAT(form_fetcher_.GetFederatedMatches(), |
+ EXPECT_THAT(form_fetcher_->GetFederatedMatches(), |
UnorderedElementsAre(Pointee(federated))); |
} |
// Check that stats from PasswordStore are handled correctly. |
TEST_F(FormFetcherImplTest, Stats) { |
- form_fetcher_.AddConsumer(&consumer_); |
+ Fetch(); |
+ form_fetcher_->AddConsumer(&consumer_); |
std::vector<std::unique_ptr<InteractionsStats>> stats; |
stats.push_back(base::MakeUnique<InteractionsStats>()); |
- form_fetcher_.SetStats(std::move(stats)); |
- EXPECT_EQ(1u, form_fetcher_.GetInteractionsStats().size()); |
+ form_fetcher_->OnGetSiteStatistics(std::move(stats)); |
+ EXPECT_EQ(1u, form_fetcher_->GetInteractionsStats().size()); |
+} |
+ |
+// Test that multiple calls of Fetch() are handled gracefully, and that they |
+// always result in passing the most up-to-date information to the consumers. |
+TEST_F(FormFetcherImplTest, Update_Reentrance) { |
+ Fetch(); |
+ form_fetcher_->AddConsumer(&consumer_); |
+ // The fetcher is currently waiting for a store response, after it fired a |
+ // GetLogins request during the Fetch() above. The second and third Fetch |
+ // (below) won't cause a GetLogins right now, but will ensure that a second |
+ // GetLogins will be called later. |
+ form_fetcher_->Fetch(); |
+ form_fetcher_->Fetch(); |
+ |
+ // First response from the store, should be ignored. |
+ PasswordForm form_a = CreateNonFederated(); |
+ form_a.username_value = ASCIIToUTF16("a@gmail.com"); |
+ std::vector<std::unique_ptr<PasswordForm>> old_results; |
+ old_results.push_back(base::MakeUnique<PasswordForm>(form_a)); |
+ // Because of the pending updates, the old PasswordStore results are not |
+ // forwarded to the consumers. |
+ EXPECT_CALL(consumer_, ProcessMatches(_, _)).Times(0); |
+ // Delivering the first results will trigger the new GetLogins call, because |
+ // of the Fetch() above. |
+ EXPECT_CALL(*mock_store_, GetLogins(form_digest_, form_fetcher_.get())); |
+ form_fetcher_->OnGetPasswordStoreResults(std::move(old_results)); |
+ |
+ // Second response from the store should not be ignored. |
+ PasswordForm form_b = CreateNonFederated(); |
+ form_b.username_value = ASCIIToUTF16("b@gmail.com"); |
+ |
+ PasswordForm form_c = CreateNonFederated(); |
+ form_c.username_value = ASCIIToUTF16("c@gmail.com"); |
+ |
+ EXPECT_CALL(consumer_, |
+ ProcessMatches( |
+ UnorderedElementsAre(Pointee(form_b), Pointee(form_c)), 0u)); |
+ std::vector<std::unique_ptr<PasswordForm>> results; |
+ results.push_back(base::MakeUnique<PasswordForm>(form_b)); |
+ results.push_back(base::MakeUnique<PasswordForm>(form_c)); |
+ form_fetcher_->OnGetPasswordStoreResults(std::move(results)); |
+} |
+ |
+#if !defined(OS_IOS) && !defined(OS_ANDROID) |
+TEST_F(FormFetcherImplTest, FetchStatistics) { |
+ InteractionsStats stats; |
+ stats.origin_domain = form_digest_.origin.GetOrigin(); |
+ stats.username_value = ASCIIToUTF16("some username"); |
+ stats.dismissal_count = 5; |
+ std::vector<InteractionsStats*> db_stats; |
+ db_stats.push_back(new InteractionsStats(stats)); |
+ EXPECT_CALL(*mock_store_, GetLogins(form_digest_, form_fetcher_.get())); |
+ EXPECT_CALL(*mock_store_, GetSiteStatsMock(stats.origin_domain)) |
+ .WillOnce(Return(db_stats)); |
+ form_fetcher_->Fetch(); |
+ base::RunLoop().RunUntilIdle(); |
+ |
+ EXPECT_THAT(form_fetcher_->GetInteractionsStats(), |
+ UnorderedElementsAre(Pointee(stats))); |
+} |
+#else |
+TEST_F(FormFetcherImplTest, DontFetchStatistics) { |
+ EXPECT_CALL(*mock_store_, GetLogins(form_digest_, form_fetcher_.get())); |
+ EXPECT_CALL(*mock_store_, GetSiteStatsMock(_)).Times(0); |
+ form_fetcher_->Fetch(); |
+ base::RunLoop().RunUntilIdle(); |
} |
+#endif |
} // namespace password_manager |