diff options
author | vasilii <vasilii@chromium.org> | 2015-11-06 05:56:19 -0800 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-11-06 13:57:08 +0000 |
commit | 63dee83012df3592e8f3702f7878ac480439f3be (patch) | |
tree | 2f385ccd420a5d443355ee7bb3c29a36df1ec1a4 | |
parent | e8a3f08d333280d0afd26d593b8f2b7ee45b7ee9 (diff) | |
download | chromium_src-63dee83012df3592e8f3702f7878ac480439f3be.zip chromium_src-63dee83012df3592e8f3702f7878ac480439f3be.tar.gz chromium_src-63dee83012df3592e8f3702f7878ac480439f3be.tar.bz2 |
Revive the statistics database for storing interaction history with the "Save password" bubble.
BUG=552010
Review URL: https://codereview.chromium.org/1413993008
Cr-Commit-Position: refs/heads/master@{#358318}
21 files changed, 279 insertions, 86 deletions
diff --git a/chrome/browser/password_manager/password_store_mac.cc b/chrome/browser/password_manager/password_store_mac.cc index 45daca3..1ad1dd6 100644 --- a/chrome/browser/password_manager/password_store_mac.cc +++ b/chrome/browser/password_manager/password_store_mac.cc @@ -1257,12 +1257,12 @@ void PasswordStoreMac::RemoveSiteStatsImpl(const GURL& origin_domain) { login_metadata_db_->stats_table().RemoveRow(origin_domain); } -scoped_ptr<password_manager::InteractionsStats> +ScopedVector<password_manager::InteractionsStats> PasswordStoreMac::GetSiteStatsImpl(const GURL& origin_domain) { DCHECK(GetBackgroundTaskRunner()->BelongsToCurrentThread()); return login_metadata_db_ - ? login_metadata_db_->stats_table().GetRow(origin_domain) - : scoped_ptr<password_manager::InteractionsStats>(); + ? login_metadata_db_->stats_table().GetRows(origin_domain) + : ScopedVector<password_manager::InteractionsStats>(); } bool PasswordStoreMac::AddToKeychainIfNecessary(const PasswordForm& form) { diff --git a/chrome/browser/password_manager/password_store_mac.h b/chrome/browser/password_manager/password_store_mac.h index c857146..1d2f4bf 100644 --- a/chrome/browser/password_manager/password_store_mac.h +++ b/chrome/browser/password_manager/password_store_mac.h @@ -93,7 +93,7 @@ class PasswordStoreMac : public password_manager::PasswordStore { void AddSiteStatsImpl( const password_manager::InteractionsStats& stats) override; void RemoveSiteStatsImpl(const GURL& origin_domain) override; - scoped_ptr<password_manager::InteractionsStats> GetSiteStatsImpl( + ScopedVector<password_manager::InteractionsStats> GetSiteStatsImpl( const GURL& origin_domain) override; // Adds the given form to the Keychain if it's something we want to store diff --git a/chrome/browser/password_manager/password_store_proxy_mac.cc b/chrome/browser/password_manager/password_store_proxy_mac.cc index 3ed1a3f..ad34546 100644 --- a/chrome/browser/password_manager/password_store_proxy_mac.cc +++ b/chrome/browser/password_manager/password_store_proxy_mac.cc @@ -203,7 +203,7 @@ void PasswordStoreProxyMac::RemoveSiteStatsImpl(const GURL& origin_domain) { GetBackend()->RemoveSiteStatsImpl(origin_domain); } -scoped_ptr<password_manager::InteractionsStats> +ScopedVector<password_manager::InteractionsStats> PasswordStoreProxyMac::GetSiteStatsImpl(const GURL& origin_domain) { return GetBackend()->GetSiteStatsImpl(origin_domain); } diff --git a/chrome/browser/password_manager/password_store_proxy_mac.h b/chrome/browser/password_manager/password_store_proxy_mac.h index 38bf539..2556af5 100644 --- a/chrome/browser/password_manager/password_store_proxy_mac.h +++ b/chrome/browser/password_manager/password_store_proxy_mac.h @@ -90,7 +90,7 @@ class PasswordStoreProxyMac : public password_manager::PasswordStore { void AddSiteStatsImpl( const password_manager::InteractionsStats& stats) override; void RemoveSiteStatsImpl(const GURL& origin_domain) override; - scoped_ptr<password_manager::InteractionsStats> GetSiteStatsImpl( + ScopedVector<password_manager::InteractionsStats> GetSiteStatsImpl( const GURL& origin_domain) override; scoped_refptr<PasswordStoreMac> password_store_mac_; diff --git a/components/password_manager/core/browser/login_database.cc b/components/password_manager/core/browser/login_database.cc index 619bf63..b816f62 100644 --- a/components/password_manager/core/browser/login_database.cc +++ b/components/password_manager/core/browser/login_database.cc @@ -33,7 +33,7 @@ using autofill::PasswordForm; namespace password_manager { // The current version number of the login database schema. -const int kCurrentVersionNumber = 15; +const int kCurrentVersionNumber = 16; // The oldest version of the schema such that a legacy Chrome client using that // version can still read/write the current database. const int kCompatibleVersionNumber = 14; @@ -420,13 +420,7 @@ bool LoginDatabase::Init() { db_.Close(); return false; } - - if (!stats_table_.Init(&db_)) { - LogDatabaseInitError(INIT_STATS_ERROR); - LOG(ERROR) << "Unable to initialize the stats table."; - db_.Close(); - return false; - } + stats_table_.Init(&db_); // If the file on disk is an older database version, bring it up to date. if (meta_table_.GetVersionNumber() < kCurrentVersionNumber && @@ -441,6 +435,13 @@ bool LoginDatabase::Init() { return false; } + if (!stats_table_.CreateTableIfNecessary()) { + LogDatabaseInitError(INIT_STATS_ERROR); + LOG(ERROR) << "Unable to create the stats table."; + db_.Close(); + return false; + } + if (!transaction.Commit()) { LogDatabaseInitError(COMMIT_TRANSACTION_ERROR); LOG(ERROR) << "Unable to commit a transaction."; @@ -607,7 +608,11 @@ bool LoginDatabase::MigrateOldVersionsAsNeeded() { // through an otherwise no-op migration process that will, however, now // correctly set the 'compatible version number'. Previously, it was // always being set to (and forever left at) version 1. - // Fall through. + meta_table_.SetCompatibleVersionNumber(kCompatibleVersionNumber); + case 15: + // Recreate the statistics. + if (!stats_table_.MigrateToVersion(16)) + return false; // ------------------------------------------------------------------------- // DO NOT FORGET to update |kCompatibleVersionNumber| if you add a migration @@ -618,7 +623,6 @@ bool LoginDatabase::MigrateOldVersionsAsNeeded() { case kCurrentVersionNumber: // Already up to date. meta_table_.SetVersionNumber(kCurrentVersionNumber); - meta_table_.SetCompatibleVersionNumber(kCompatibleVersionNumber); return true; default: NOTREACHED(); diff --git a/components/password_manager/core/browser/mock_password_store.h b/components/password_manager/core/browser/mock_password_store.h index 60beb7a..1127a3c 100644 --- a/components/password_manager/core/browser/mock_password_store.h +++ b/components/password_manager/core/browser/mock_password_store.h @@ -49,9 +49,9 @@ class MockPasswordStore : public PasswordStore { MOCK_METHOD1(NotifyLoginsChanged, void(const PasswordStoreChangeList&)); void AddSiteStatsImpl(const InteractionsStats& stats) override {} void RemoveSiteStatsImpl(const GURL& origin_domain) override {} - scoped_ptr<InteractionsStats> GetSiteStatsImpl( + ScopedVector<InteractionsStats> GetSiteStatsImpl( const GURL& origin_domain) override { - return scoped_ptr<InteractionsStats>(); + return ScopedVector<InteractionsStats>(); } PasswordStoreSync* GetSyncInterface() { return this; } diff --git a/components/password_manager/core/browser/password_store.cc b/components/password_manager/core/browser/password_store.cc index 252916e..6a2c1877 100644 --- a/components/password_manager/core/browser/password_store.cc +++ b/components/password_manager/core/browser/password_store.cc @@ -62,7 +62,7 @@ void PasswordStore::GetLoginsRequest::NotifyConsumerWithResults( } void PasswordStore::GetLoginsRequest::NotifyWithSiteStatistics( - scoped_ptr<InteractionsStats> stats) { + ScopedVector<InteractionsStats> stats) { origin_task_runner_->PostTask( FROM_HERE, base::Bind(&PasswordStoreConsumer::OnGetSiteStatistics, consumer_weak_, base::Passed(&stats))); diff --git a/components/password_manager/core/browser/password_store.h b/components/password_manager/core/browser/password_store.h index c0ef993..ec22f90 100644 --- a/components/password_manager/core/browser/password_store.h +++ b/components/password_manager/core/browser/password_store.h @@ -205,7 +205,7 @@ class PasswordStore : protected PasswordStoreSync, void NotifyConsumerWithResults( ScopedVector<autofill::PasswordForm> results); - void NotifyWithSiteStatistics(scoped_ptr<InteractionsStats> stats); + void NotifyWithSiteStatistics(ScopedVector<InteractionsStats> stats); void set_ignore_logins_cutoff(base::Time cutoff) { ignore_logins_cutoff_ = cutoff; @@ -283,7 +283,7 @@ class PasswordStore : protected PasswordStoreSync, virtual void AddSiteStatsImpl(const InteractionsStats& stats) = 0; virtual void RemoveSiteStatsImpl(const GURL& origin_domain) = 0; // Returns a raw pointer so that InteractionsStats can be forward declared. - virtual scoped_ptr<InteractionsStats> GetSiteStatsImpl( + virtual ScopedVector<InteractionsStats> GetSiteStatsImpl( const GURL& origin_domain) WARN_UNUSED_RESULT = 0; // Log UMA stats for number of bulk deletions. diff --git a/components/password_manager/core/browser/password_store_consumer.cc b/components/password_manager/core/browser/password_store_consumer.cc index 8347f2b..1aa16c6 100644 --- a/components/password_manager/core/browser/password_store_consumer.cc +++ b/components/password_manager/core/browser/password_store_consumer.cc @@ -15,7 +15,6 @@ PasswordStoreConsumer::~PasswordStoreConsumer() { } void PasswordStoreConsumer::OnGetSiteStatistics( - scoped_ptr<InteractionsStats> stats) { -} + ScopedVector<InteractionsStats> stats) {} } // namespace password_manager diff --git a/components/password_manager/core/browser/password_store_consumer.h b/components/password_manager/core/browser/password_store_consumer.h index b8934995..4ce40ba 100644 --- a/components/password_manager/core/browser/password_store_consumer.h +++ b/components/password_manager/core/browser/password_store_consumer.h @@ -32,7 +32,7 @@ class PasswordStoreConsumer { virtual void OnGetPasswordStoreResults( ScopedVector<autofill::PasswordForm> results) = 0; - virtual void OnGetSiteStatistics(scoped_ptr<InteractionsStats> stats); + virtual void OnGetSiteStatistics(ScopedVector<InteractionsStats> stats); // The base::CancelableTaskTracker can be used for cancelling the // tasks associated with the consumer. diff --git a/components/password_manager/core/browser/password_store_default.cc b/components/password_manager/core/browser/password_store_default.cc index 26599ce..89ca082 100644 --- a/components/password_manager/core/browser/password_store_default.cc +++ b/components/password_manager/core/browser/password_store_default.cc @@ -170,11 +170,11 @@ void PasswordStoreDefault::RemoveSiteStatsImpl(const GURL& origin_domain) { login_db_->stats_table().RemoveRow(origin_domain); } -scoped_ptr<InteractionsStats> PasswordStoreDefault::GetSiteStatsImpl( +ScopedVector<InteractionsStats> PasswordStoreDefault::GetSiteStatsImpl( const GURL& origin_domain) { DCHECK(GetBackgroundTaskRunner()->BelongsToCurrentThread()); - return login_db_ ? login_db_->stats_table().GetRow(origin_domain) - : scoped_ptr<InteractionsStats>(); + return login_db_ ? login_db_->stats_table().GetRows(origin_domain) + : ScopedVector<InteractionsStats>(); } void PasswordStoreDefault::ResetLoginDB() { diff --git a/components/password_manager/core/browser/password_store_default.h b/components/password_manager/core/browser/password_store_default.h index f31be5f..b43c20e 100644 --- a/components/password_manager/core/browser/password_store_default.h +++ b/components/password_manager/core/browser/password_store_default.h @@ -65,7 +65,7 @@ class PasswordStoreDefault : public PasswordStore { ScopedVector<autofill::PasswordForm>* forms) override; void AddSiteStatsImpl(const InteractionsStats& stats) override; void RemoveSiteStatsImpl(const GURL& origin_domain) override; - scoped_ptr<InteractionsStats> GetSiteStatsImpl( + ScopedVector<InteractionsStats> GetSiteStatsImpl( const GURL& origin_domain) override; inline bool DeleteAndRecreateDatabaseFile() { diff --git a/components/password_manager/core/browser/statistics_table.cc b/components/password_manager/core/browser/statistics_table.cc index 1246ee9..6cc4027 100644 --- a/components/password_manager/core/browser/statistics_table.cc +++ b/components/password_manager/core/browser/statistics_table.cc @@ -13,44 +13,60 @@ namespace { // Convenience enum for interacting with SQL queries that use all the columns. enum LoginTableColumns { COLUMN_ORIGIN_DOMAIN = 0, - COLUMN_NOPES, + COLUMN_USERNAME, COLUMN_DISMISSALS, COLUMN_DATE, }; } // namespace +InteractionsStats::InteractionsStats() = default; + StatisticsTable::StatisticsTable() : db_(nullptr) { } -StatisticsTable::~StatisticsTable() { -} +StatisticsTable::~StatisticsTable() = default; -bool StatisticsTable::Init(sql::Connection* db) { +void StatisticsTable::Init(sql::Connection* db) { db_ = db; +} + +bool StatisticsTable::CreateTableIfNecessary() { if (!db_->DoesTableExist("stats")) { const char query[] = "CREATE TABLE stats (" - "origin_domain VARCHAR NOT NULL PRIMARY KEY, " - "nopes_count INTEGER, " + "origin_domain VARCHAR NOT NULL, " + "username_value VARCHAR, " "dismissal_count INTEGER, " - "start_date INTEGER NOT NULL)"; + "update_time INTEGER NOT NULL, " + "UNIQUE(origin_domain, username_value))"; if (!db_->Execute(query)) return false; + const char index[] = "CREATE INDEX stats_origin ON stats(origin_domain)"; + if (!db_->Execute(index)) + return false; } return true; } +bool StatisticsTable::MigrateToVersion(int version) { + if (!db_->DoesTableExist("stats")) + return true; + if (version == 16) + return db_->Execute("DROP TABLE stats"); + return true; +} + bool StatisticsTable::AddRow(const InteractionsStats& stats) { sql::Statement s(db_->GetCachedStatement( SQL_FROM_HERE, "INSERT OR REPLACE INTO stats " - "(origin_domain, nopes_count, dismissal_count, start_date) " + "(origin_domain, username_value, dismissal_count, update_time) " "VALUES (?, ?, ?, ?)")); s.BindString(COLUMN_ORIGIN_DOMAIN, stats.origin_domain.spec()); - s.BindInt(COLUMN_NOPES, stats.nopes_count); + s.BindString16(COLUMN_USERNAME, stats.username_value); s.BindInt(COLUMN_DISMISSALS, stats.dismissal_count); - s.BindInt64(COLUMN_DATE, stats.start_date.ToInternalValue()); + s.BindInt64(COLUMN_DATE, stats.update_time.ToInternalValue()); return s.Run(); } @@ -62,22 +78,33 @@ bool StatisticsTable::RemoveRow(const GURL& domain) { return s.Run(); } -scoped_ptr<InteractionsStats> StatisticsTable::GetRow(const GURL& domain) { +ScopedVector<InteractionsStats> StatisticsTable::GetRows(const GURL& domain) { const char query[] = - "SELECT origin_domain, nopes_count, " - "dismissal_count, start_date FROM stats WHERE origin_domain == ?"; + "SELECT origin_domain, username_value, " + "dismissal_count, update_time FROM stats WHERE origin_domain == ?"; sql::Statement s(db_->GetCachedStatement(SQL_FROM_HERE, query)); s.BindString(0, domain.spec()); - if (s.Step()) { - scoped_ptr<InteractionsStats> stats(new InteractionsStats); - stats->origin_domain = GURL(s.ColumnString(COLUMN_ORIGIN_DOMAIN)); - stats->nopes_count = s.ColumnInt(COLUMN_NOPES); - stats->dismissal_count = s.ColumnInt(COLUMN_DISMISSALS); - stats->start_date = + ScopedVector<InteractionsStats> result; + while (s.Step()) { + result.push_back(new InteractionsStats); + result.back()->origin_domain = GURL(s.ColumnString(COLUMN_ORIGIN_DOMAIN)); + result.back()->username_value = s.ColumnString16(COLUMN_USERNAME); + result.back()->dismissal_count = s.ColumnInt(COLUMN_DISMISSALS); + result.back()->update_time = base::Time::FromInternalValue(s.ColumnInt64(COLUMN_DATE)); - return stats.Pass(); } - return scoped_ptr<InteractionsStats>(); + return result.Pass(); +} + +bool StatisticsTable::RemoveStatsBetween(base::Time delete_begin, + base::Time delete_end) { + sql::Statement s(db_->GetCachedStatement( + SQL_FROM_HERE, + "DELETE FROM stats WHERE update_time >= ? AND update_time < ?")); + s.BindInt64(0, delete_begin.ToInternalValue()); + s.BindInt64(1, delete_end.is_null() ? std::numeric_limits<int64>::max() + : delete_end.ToInternalValue()); + return s.Run(); } } // namespace password_manager diff --git a/components/password_manager/core/browser/statistics_table.h b/components/password_manager/core/browser/statistics_table.h index d135309..9a48f1e 100644 --- a/components/password_manager/core/browser/statistics_table.h +++ b/components/password_manager/core/browser/statistics_table.h @@ -7,6 +7,7 @@ #include "base/macros.h" #include "base/memory/scoped_ptr.h" +#include "base/memory/scoped_vector.h" #include "base/time/time.h" #include "url/gurl.h" @@ -18,29 +19,40 @@ namespace password_manager { // The statistics containing user interactions with a site. struct InteractionsStats { + InteractionsStats(); + // The domain of the site. GURL origin_domain; - // Number of times the user clicked "Don't save the password". - int nopes_count; + // The value of the username. + base::string16 username_value; // Number of times the user dismissed the bubble. - int dismissal_count; + int dismissal_count = 0; - // The beginning date of the measurements. - base::Time start_date; + // The date when the row was updated. + base::Time update_time; }; -// Represents 'stats' table in the Login Database. +// Represents the 'stats' table in the Login Database. class StatisticsTable { public: StatisticsTable(); ~StatisticsTable(); - // Initializes |db_| and creates the statistics table if it doesn't exist. - bool Init(sql::Connection* db); + // Initializes |db_|. + void Init(sql::Connection* db); + + // Creates the statistics table if it doesn't exist. + bool CreateTableIfNecessary(); + + // Migrates this table to |version|. The current version should be less than + // |version|. Returns false if there was migration work to do and it failed, + // true otherwise. + bool MigrateToVersion(int version); - // Adds or replaces the statistics about |stats.origin_domain|. + // Adds or replaces the statistics about |stats.origin_domain| and + // |stats.username_value|. bool AddRow(const InteractionsStats& stats); // Removes the statistics for |domain|. Returns true if the SQL completed @@ -48,7 +60,11 @@ class StatisticsTable { bool RemoveRow(const GURL& domain); // Returns the statistics for |domain| if it exists. - scoped_ptr<InteractionsStats> GetRow(const GURL& domain); + ScopedVector<InteractionsStats> GetRows(const GURL& domain); + + // Removes the statistics between the dates. Returns true if the SQL completed + // successfully. + bool RemoveStatsBetween(base::Time delete_begin, base::Time delete_end); private: sql::Connection* db_; diff --git a/components/password_manager/core/browser/statistics_table_unittest.cc b/components/password_manager/core/browser/statistics_table_unittest.cc index c3d28ff..f592e0a 100644 --- a/components/password_manager/core/browser/statistics_table_unittest.cc +++ b/components/password_manager/core/browser/statistics_table_unittest.cc @@ -5,20 +5,29 @@ #include "components/password_manager/core/browser/statistics_table.h" #include "base/files/scoped_temp_dir.h" +#include "base/strings/utf_string_conversions.h" #include "sql/connection.h" +#include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" namespace password_manager { namespace { const char kTestDomain[] = "http://google.com"; - -void CheckStatsAreEqual(const InteractionsStats& left, - const InteractionsStats& right) { - EXPECT_EQ(left.origin_domain, right.origin_domain); - EXPECT_EQ(left.nopes_count, right.nopes_count); - EXPECT_EQ(left.dismissal_count, right.dismissal_count); - EXPECT_EQ(left.start_date, right.start_date); +const char kTestDomain2[] = "http://example.com"; +const char kUsername1[] = "user1"; +const char kUsername2[] = "user2"; + +using ::testing::ElementsAre; +using ::testing::IsEmpty; +using ::testing::Pointee; +using ::testing::UnorderedElementsAre; + +MATCHER_P(StatsIs, stats, "") { + return arg.origin_domain == stats.origin_domain && + arg.username_value == stats.username_value && + arg.dismissal_count == stats.dismissal_count && + arg.update_time == stats.update_time; } class StatisticsTableTest : public testing::Test { @@ -28,9 +37,9 @@ class StatisticsTableTest : public testing::Test { ReloadDatabase(); test_data_.origin_domain = GURL(kTestDomain); - test_data_.nopes_count = 5; + test_data_.username_value = base::ASCIIToUTF16(kUsername1); test_data_.dismissal_count = 10; - test_data_.start_date = base::Time::FromTimeT(1); + test_data_.update_time = base::Time::FromTimeT(1); } void ReloadDatabase() { @@ -39,7 +48,8 @@ class StatisticsTableTest : public testing::Test { connection_.reset(new sql::Connection); connection_->set_exclusive_locking(); ASSERT_TRUE(connection_->Open(file)); - ASSERT_TRUE(db_->Init(connection_.get())); + db_->Init(connection_.get()); + ASSERT_TRUE(db_->CreateTableIfNecessary()); } InteractionsStats& test_data() { return test_data_; } @@ -54,37 +64,72 @@ class StatisticsTableTest : public testing::Test { TEST_F(StatisticsTableTest, Sanity) { EXPECT_TRUE(db()->AddRow(test_data())); - scoped_ptr<InteractionsStats> stats = db()->GetRow(test_data().origin_domain); - ASSERT_TRUE(stats); - CheckStatsAreEqual(test_data(), *stats); + EXPECT_THAT(db()->GetRows(test_data().origin_domain), + ElementsAre(Pointee(StatsIs(test_data())))); EXPECT_TRUE(db()->RemoveRow(test_data().origin_domain)); - EXPECT_FALSE(db()->GetRow(test_data().origin_domain)); + EXPECT_THAT(db()->GetRows(test_data().origin_domain), IsEmpty()); } TEST_F(StatisticsTableTest, Reload) { EXPECT_TRUE(db()->AddRow(test_data())); - EXPECT_TRUE(db()->GetRow(test_data().origin_domain)); ReloadDatabase(); - scoped_ptr<InteractionsStats> stats = db()->GetRow(test_data().origin_domain); - ASSERT_TRUE(stats); - CheckStatsAreEqual(test_data(), *stats); + EXPECT_THAT(db()->GetRows(test_data().origin_domain), + ElementsAre(Pointee(StatsIs(test_data())))); } TEST_F(StatisticsTableTest, DoubleOperation) { EXPECT_TRUE(db()->AddRow(test_data())); - test_data().nopes_count++; + test_data().dismissal_count++; EXPECT_TRUE(db()->AddRow(test_data())); - scoped_ptr<InteractionsStats> stats = db()->GetRow(test_data().origin_domain); - ASSERT_TRUE(stats); - CheckStatsAreEqual(test_data(), *stats); + EXPECT_THAT(db()->GetRows(test_data().origin_domain), + ElementsAre(Pointee(StatsIs(test_data())))); EXPECT_TRUE(db()->RemoveRow(test_data().origin_domain)); - EXPECT_FALSE(db()->GetRow(test_data().origin_domain)); + EXPECT_THAT(db()->GetRows(test_data().origin_domain), IsEmpty()); EXPECT_TRUE(db()->RemoveRow(test_data().origin_domain)); - EXPECT_FALSE(db()->GetRow(test_data().origin_domain)); +} + +TEST_F(StatisticsTableTest, DifferentUsernames) { + InteractionsStats stats1 = test_data(); + InteractionsStats stats2 = test_data(); + stats2.username_value = base::ASCIIToUTF16(kUsername2); + + EXPECT_TRUE(db()->AddRow(stats1)); + EXPECT_TRUE(db()->AddRow(stats2)); + EXPECT_THAT( + db()->GetRows(test_data().origin_domain), + UnorderedElementsAre(Pointee(StatsIs(stats1)), Pointee(StatsIs(stats2)))); + EXPECT_TRUE(db()->RemoveRow(test_data().origin_domain)); + EXPECT_THAT(db()->GetRows(test_data().origin_domain), IsEmpty()); +} + +TEST_F(StatisticsTableTest, RemoveBetween) { + InteractionsStats stats1 = test_data(); + stats1.update_time = base::Time::FromTimeT(1); + InteractionsStats stats2 = test_data(); + stats2.update_time = base::Time::FromTimeT(2); + stats2.origin_domain = GURL(kTestDomain2); + + EXPECT_TRUE(db()->AddRow(stats1)); + EXPECT_TRUE(db()->AddRow(stats2)); + EXPECT_THAT(db()->GetRows(stats1.origin_domain), + ElementsAre(Pointee(StatsIs(stats1)))); + EXPECT_THAT(db()->GetRows(stats2.origin_domain), + ElementsAre(Pointee(StatsIs(stats2)))); + + // Remove the first one only. + EXPECT_TRUE(db()->RemoveStatsBetween(base::Time(), base::Time::FromTimeT(2))); + EXPECT_THAT(db()->GetRows(stats1.origin_domain), IsEmpty()); + EXPECT_THAT(db()->GetRows(stats2.origin_domain), + ElementsAre(Pointee(StatsIs(stats2)))); + + // Remove the second one only. + EXPECT_TRUE(db()->RemoveStatsBetween(base::Time::FromTimeT(2), base::Time())); + EXPECT_THAT(db()->GetRows(stats1.origin_domain), IsEmpty()); + EXPECT_THAT(db()->GetRows(stats2.origin_domain), IsEmpty()); } } // namespace diff --git a/components/password_manager/core/browser/test_password_store.cc b/components/password_manager/core/browser/test_password_store.cc index fd3285a..776a869 100644 --- a/components/password_manager/core/browser/test_password_store.cc +++ b/components/password_manager/core/browser/test_password_store.cc @@ -127,9 +127,9 @@ void TestPasswordStore::AddSiteStatsImpl(const InteractionsStats& stats) { void TestPasswordStore::RemoveSiteStatsImpl(const GURL& origin_domain) { } -scoped_ptr<InteractionsStats> TestPasswordStore::GetSiteStatsImpl( +ScopedVector<InteractionsStats> TestPasswordStore::GetSiteStatsImpl( const GURL& origin_domain) { - return scoped_ptr<InteractionsStats>(); + return ScopedVector<InteractionsStats>(); } } // namespace password_manager diff --git a/components/password_manager/core/browser/test_password_store.h b/components/password_manager/core/browser/test_password_store.h index 079997f..826ed0e 100644 --- a/components/password_manager/core/browser/test_password_store.h +++ b/components/password_manager/core/browser/test_password_store.h @@ -62,7 +62,7 @@ class TestPasswordStore : public PasswordStore { ScopedVector<autofill::PasswordForm>* forms) override; void AddSiteStatsImpl(const InteractionsStats& stats) override; void RemoveSiteStatsImpl(const GURL& origin_domain) override; - scoped_ptr<InteractionsStats> GetSiteStatsImpl( + ScopedVector<InteractionsStats> GetSiteStatsImpl( const GURL& origin_domain) override; private: diff --git a/components/test/data/password_manager/login_db_v12.sql b/components/test/data/password_manager/login_db_v12.sql index 9530fb5..c25cb0e 100644 --- a/components/test/data/password_manager/login_db_v12.sql +++ b/components/test/data/password_manager/login_db_v12.sql @@ -79,4 +79,9 @@ X'18000000020000000000000000000000000000000000000000000000', /* form_data */ 0 /* generation_upload_status */ ); CREATE INDEX logins_signon ON logins (signon_realm); +CREATE TABLE stats ( +origin_domain VARCHAR NOT NULL PRIMARY KEY, +nopes_count INTEGER, +dismissal_count INTEGER, +start_date INTEGER NOT NULL); COMMIT; diff --git a/components/test/data/password_manager/login_db_v13.sql b/components/test/data/password_manager/login_db_v13.sql index 451c3e4..c71773f 100644 --- a/components/test/data/password_manager/login_db_v13.sql +++ b/components/test/data/password_manager/login_db_v13.sql @@ -79,4 +79,9 @@ X'18000000020000000000000000000000000000000000000000000000', /* form_data */ 0 /* generation_upload_status */ ); CREATE INDEX logins_signon ON logins (signon_realm); +CREATE TABLE stats ( +origin_domain VARCHAR NOT NULL PRIMARY KEY, +nopes_count INTEGER, +dismissal_count INTEGER, +start_date INTEGER NOT NULL); COMMIT; diff --git a/components/test/data/password_manager/login_db_v14.sql b/components/test/data/password_manager/login_db_v14.sql index 063260f..cdf4b31 100644 --- a/components/test/data/password_manager/login_db_v14.sql +++ b/components/test/data/password_manager/login_db_v14.sql @@ -79,4 +79,9 @@ X'18000000020000000000000000000000000000000000000000000000', /* form_data */ 0 /* generation_upload_status */ ); CREATE INDEX logins_signon ON logins (signon_realm); +CREATE TABLE stats ( +origin_domain VARCHAR NOT NULL PRIMARY KEY, +nopes_count INTEGER, +dismissal_count INTEGER, +start_date INTEGER NOT NULL); COMMIT; diff --git a/components/test/data/password_manager/login_db_v15.sql b/components/test/data/password_manager/login_db_v15.sql new file mode 100644 index 0000000..9e3af8f --- /dev/null +++ b/components/test/data/password_manager/login_db_v15.sql @@ -0,0 +1,87 @@ +PRAGMA foreign_keys=OFF; +BEGIN TRANSACTION; +CREATE TABLE meta(key LONGVARCHAR NOT NULL UNIQUE PRIMARY KEY, value LONGVARCHAR); +INSERT INTO "meta" VALUES('last_compatible_version','14'); +INSERT INTO "meta" VALUES('version','15'); +CREATE TABLE logins ( +origin_url VARCHAR NOT NULL, +action_url VARCHAR, +username_element VARCHAR, +username_value VARCHAR, +password_element VARCHAR, +password_value BLOB, +submit_element VARCHAR, +signon_realm VARCHAR NOT NULL, +ssl_valid INTEGER NOT NULL, +preferred INTEGER NOT NULL, +date_created INTEGER NOT NULL, +blacklisted_by_user INTEGER NOT NULL, +scheme INTEGER NOT NULL, +password_type INTEGER, +possible_usernames BLOB, +times_used INTEGER, +form_data BLOB, +date_synced INTEGER, +display_name VARCHAR, +icon_url VARCHAR, +federation_url VARCHAR, +skip_zero_click INTEGER, +generation_upload_status INTEGER, +UNIQUE (origin_url, username_element, username_value, password_element, signon_realm)); +INSERT INTO "logins" VALUES( +'https://accounts.google.com/ServiceLogin', /* origin_url */ +'https://accounts.google.com/ServiceLoginAuth', /* action_url */ +'Email', /* username_element */ +'theerikchen', /* username_value */ +'Passwd', /* password_element */ +X'', /* password_value */ +'', /* submit_element */ +'https://accounts.google.com/', /* signon_realm */ +1, /* ssl_valid */ +1, /* preferred */ +13047429345000000, /* date_created */ +0, /* blacklisted_by_user */ +0, /* scheme */ +0, /* password_type */ +X'00000000', /* possible_usernames */ +1, /* times_used */ +X'18000000020000000000000000000000000000000000000000000000', /* form_data */ +0, /* date_synced */ +'', /* display_name */ +'', /* icon_url */ +'', /* federation_url */ +0, /* skip_zero_click */ +0 /* generation_upload_status */ +); +INSERT INTO "logins" VALUES( +'https://accounts.google.com/ServiceLogin', /* origin_url */ +'https://accounts.google.com/ServiceLoginAuth', /* action_url */ +'Email', /* username_element */ +'theerikchen2', /* username_value */ +'Passwd', /* password_element */ +X'', /* password_value */ +'non-empty', /* submit_element */ +'https://accounts.google.com/', /* signon_realm */ +1, /* ssl_valid */ +1, /* preferred */ +13047423600000000, /* date_created */ +0, /* blacklisted_by_user */ +0, /* scheme */ +0, /* password_type */ +X'00000000', /* possible_usernames */ +1, /* times_used */ +X'18000000020000000000000000000000000000000000000000000000', /* form_data */ +0, /* date_synced */ +'', /* display_name */ +'https://www.google.com/icon', /* icon_url */ +'', /* federation_url */ +0, /* skip_zero_click */ +0 /* generation_upload_status */ +); +CREATE INDEX logins_signon ON logins (signon_realm); +CREATE TABLE stats ( +origin_domain VARCHAR NOT NULL PRIMARY KEY, +nopes_count INTEGER, +dismissal_count INTEGER, +start_date INTEGER NOT NULL); +COMMIT; |