summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorvasilii <vasilii@chromium.org>2015-11-06 05:56:19 -0800
committerCommit bot <commit-bot@chromium.org>2015-11-06 13:57:08 +0000
commit63dee83012df3592e8f3702f7878ac480439f3be (patch)
tree2f385ccd420a5d443355ee7bb3c29a36df1ec1a4
parente8a3f08d333280d0afd26d593b8f2b7ee45b7ee9 (diff)
downloadchromium_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}
-rw-r--r--chrome/browser/password_manager/password_store_mac.cc6
-rw-r--r--chrome/browser/password_manager/password_store_mac.h2
-rw-r--r--chrome/browser/password_manager/password_store_proxy_mac.cc2
-rw-r--r--chrome/browser/password_manager/password_store_proxy_mac.h2
-rw-r--r--components/password_manager/core/browser/login_database.cc24
-rw-r--r--components/password_manager/core/browser/mock_password_store.h4
-rw-r--r--components/password_manager/core/browser/password_store.cc2
-rw-r--r--components/password_manager/core/browser/password_store.h4
-rw-r--r--components/password_manager/core/browser/password_store_consumer.cc3
-rw-r--r--components/password_manager/core/browser/password_store_consumer.h2
-rw-r--r--components/password_manager/core/browser/password_store_default.cc6
-rw-r--r--components/password_manager/core/browser/password_store_default.h2
-rw-r--r--components/password_manager/core/browser/statistics_table.cc69
-rw-r--r--components/password_manager/core/browser/statistics_table.h36
-rw-r--r--components/password_manager/core/browser/statistics_table_unittest.cc93
-rw-r--r--components/password_manager/core/browser/test_password_store.cc4
-rw-r--r--components/password_manager/core/browser/test_password_store.h2
-rw-r--r--components/test/data/password_manager/login_db_v12.sql5
-rw-r--r--components/test/data/password_manager/login_db_v13.sql5
-rw-r--r--components/test/data/password_manager/login_db_v14.sql5
-rw-r--r--components/test/data/password_manager/login_db_v15.sql87
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;