blob: 63a8c41b4a8261e1ec4456891154e9c82abe41e8 [file] [log] [blame]
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/permissions/permission_auditing_database.h"
#include <memory>
#include "base/bind.h"
#include "base/check.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/strings/stringprintf.h"
#include "base/time/time.h"
#include "sql/test/scoped_error_expecter.h"
#include "testing/gmock/include/gmock/gmock-matchers.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
namespace permissions {
using ::testing::ElementsAre;
using ::testing::IsEmpty;
namespace {
base::Time TimeFromTimestamp(const int64_t& time) {
return base::Time::FromDeltaSinceWindowsEpoch(base::Microseconds(time));
}
constexpr ContentSettingsType kTestTypes[] = {
ContentSettingsType::GEOLOCATION, ContentSettingsType::NOTIFICATIONS};
constexpr char kTestUrl1[] = "http://www.example1.com";
constexpr char kTestUrl2[] = "http://www.example2.com";
std::string GetUniqueUrl(int id) {
return base::StringPrintf("http://www.example%d.com", id);
}
url::Origin GetOrigin(const char* url) {
return url::Origin::Create(GURL(url));
}
PermissionUsageSession SessionLike(ContentSettingsType type,
const char* url,
const PermissionUsageSession& session) {
return {.origin = GetOrigin(url),
.type = type,
.usage_start = session.usage_start,
.usage_end = session.usage_end,
.had_user_activation = session.had_user_activation,
.was_foreground = session.was_foreground,
.had_focus = session.had_focus};
}
} // namespace
class PermissionAuditingDatabaseTest : public testing::Test {
public:
PermissionAuditingDatabaseTest()
: test_sessions_(GeneratePermissionSessions()) {}
PermissionAuditingDatabaseTest(const PermissionAuditingDatabaseTest&) =
delete;
PermissionAuditingDatabaseTest& operator=(
const PermissionAuditingDatabaseTest&) = delete;
protected:
void SetUp() override {
ASSERT_TRUE(temp_directory_.CreateUniqueTempDir());
path_ = temp_directory_.GetPath().Append(
FILE_PATH_LITERAL("test_permission_auditing_database"));
ASSERT_TRUE(db_.Init(path_));
}
std::vector<PermissionUsageSession> GetPermissionUsageHistory(
ContentSettingsType type,
const char* url,
base::Time starting_from = base::Time()) {
return db_.GetPermissionUsageHistory(type, GetOrigin(url), starting_from);
}
absl::optional<base::Time> GetLastUsageTime(ContentSettingsType type,
const char* url) {
return db_.GetLastPermissionUsageTime(type, GetOrigin(url));
}
bool UpdateEndTime(ContentSettingsType type,
const char* url,
base::Time ordering_time,
base::Time new_end_time) {
return db_.UpdateEndTime(type, GetOrigin(url), ordering_time, new_end_time);
}
void StoreSessionForEachTime() {
for (const auto& time : test_times_) {
ASSERT_TRUE(db().StorePermissionUsage({.origin = GetOrigin(kTestUrl1),
.type = kTestTypes[0],
.usage_start = time,
.usage_end = time,
.had_user_activation = false,
.was_foreground = false,
.had_focus = false}));
}
}
PermissionAuditingDatabase& db() { return db_; }
const base::Time test_times_[3] = {TimeFromTimestamp(12864787200000000),
TimeFromTimestamp(12864787200000001),
TimeFromTimestamp(12864787200000002)};
const std::vector<PermissionUsageSession> test_sessions_;
private:
std::vector<PermissionUsageSession> GeneratePermissionSessions() {
std::vector<PermissionUsageSession> sessions;
for (size_t i = 0; i < std::size(test_times_); ++i) {
for (size_t j = i + 1; j <= std::size(test_times_); ++j) {
for (bool had_user_activation : {false, true}) {
for (bool was_foreground : {false, true}) {
for (bool had_focus : {false, true}) {
base::Time start = test_times_[i];
base::Time end = (j == std::size(test_times_)) ? test_times_[i]
: test_times_[j];
sessions.push_back({.usage_start = start,
.usage_end = end,
.had_user_activation = had_user_activation,
.was_foreground = was_foreground,
.had_focus = had_focus});
}
}
}
}
}
return sessions;
}
PermissionAuditingDatabase db_;
base::ScopedTempDir temp_directory_;
base::FilePath path_;
};
TEST_F(PermissionAuditingDatabaseTest, IsUsageHistorySizeCorrect) {
auto session = SessionLike(kTestTypes[0], kTestUrl1, test_sessions_[0]);
EXPECT_THAT(GetPermissionUsageHistory(kTestTypes[0], kTestUrl1), IsEmpty());
size_t current_size = 0;
for (const auto& time : test_times_) {
session.usage_start = time;
session.usage_end = time;
ASSERT_TRUE(db().StorePermissionUsage(
SessionLike(kTestTypes[0], kTestUrl1, session)));
ASSERT_EQ(GetPermissionUsageHistory(kTestTypes[0], kTestUrl1).size(),
++current_size);
}
}
TEST_F(PermissionAuditingDatabaseTest,
IsUsageHistoryDifferentForDifferentPermissionsAndOrigins) {
const auto& session1 =
SessionLike(kTestTypes[0], kTestUrl1, test_sessions_[0]);
const auto& session2 =
SessionLike(kTestTypes[1], kTestUrl2, test_sessions_[1]);
ASSERT_TRUE(db().StorePermissionUsage(session1));
ASSERT_TRUE(db().StorePermissionUsage(session2));
EXPECT_THAT(GetPermissionUsageHistory(kTestTypes[0], kTestUrl1),
ElementsAre(session1));
EXPECT_THAT(GetPermissionUsageHistory(kTestTypes[1], kTestUrl2),
ElementsAre(session2));
EXPECT_THAT(GetPermissionUsageHistory(kTestTypes[0], kTestUrl2), IsEmpty());
EXPECT_THAT(GetPermissionUsageHistory(kTestTypes[1], kTestUrl1), IsEmpty());
}
TEST_F(PermissionAuditingDatabaseTest, AreFieldsStoredCorrectlyInUsageHistory) {
int counter = 0;
for (const auto& session : test_sessions_) {
const std::string url = GetUniqueUrl(++counter);
auto updated_session = SessionLike(kTestTypes[0], url.c_str(), session);
ASSERT_TRUE(db().StorePermissionUsage(updated_session));
EXPECT_THAT(GetPermissionUsageHistory(kTestTypes[0], url.c_str()),
ElementsAre(updated_session));
}
}
TEST_F(PermissionAuditingDatabaseTest, UsageHistoryContainsOnlyLastSessions) {
for (const auto time : test_times_) {
ASSERT_TRUE(
db().StorePermissionUsage({.origin = GetOrigin(kTestUrl1),
.type = kTestTypes[0],
.usage_start = time,
.usage_end = time + base::Microseconds(1),
.had_user_activation = false,
.was_foreground = false,
.had_focus = false}));
}
EXPECT_EQ(GetPermissionUsageHistory(kTestTypes[0], kTestUrl1).size(),
std::size(test_times_));
for (size_t i = 0; i < std::size(test_times_); ++i) {
EXPECT_EQ(GetPermissionUsageHistory(kTestTypes[0], kTestUrl1,
test_times_[i] + base::Microseconds(2))
.size(),
std::size(test_times_) - i - 1);
}
}
TEST_F(PermissionAuditingDatabaseTest, GetLastPermissionUsageTime) {
EXPECT_FALSE(GetLastUsageTime(kTestTypes[0], kTestUrl1));
for (const auto& time : test_times_) {
ASSERT_TRUE(db().StorePermissionUsage({.origin = GetOrigin(kTestUrl1),
.type = kTestTypes[0],
.usage_start = time,
.usage_end = time,
.had_user_activation = false,
.was_foreground = false,
.had_focus = false}));
EXPECT_EQ(GetLastUsageTime(kTestTypes[0], kTestUrl1), time);
}
}
TEST_F(PermissionAuditingDatabaseTest, UpdateEndTime) {
int counter = 0;
for (const auto& session : test_sessions_) {
const std::string url = GetUniqueUrl(++counter);
ASSERT_TRUE(db().StorePermissionUsage(
SessionLike(kTestTypes[0], url.c_str(), session)));
const auto& end_time = session.usage_end;
auto tomorrow = end_time + base::Days(1);
ASSERT_TRUE(GetLastUsageTime(kTestTypes[0], url.c_str()) == end_time);
ASSERT_TRUE(UpdateEndTime(kTestTypes[0], url.c_str(), session.usage_start,
tomorrow));
EXPECT_EQ(GetLastUsageTime(kTestTypes[0], url.c_str()), tomorrow);
auto history = GetPermissionUsageHistory(kTestTypes[0], url.c_str());
ASSERT_EQ(history.size(), 1u);
EXPECT_EQ(history[0].usage_end, tomorrow);
}
}
TEST_F(PermissionAuditingDatabaseTest, DeleteSessionsBetween) {
size_t current_size = std::size(test_times_);
StoreSessionForEachTime();
for (const auto& time : test_times_) {
ASSERT_TRUE(db().DeleteSessionsBetween(time, time));
ASSERT_EQ(GetPermissionUsageHistory(kTestTypes[0], kTestUrl1).size(),
--current_size);
}
}
TEST_F(PermissionAuditingDatabaseTest,
DeleteSessionsBetweenWithUnspecifiedEndTime) {
StoreSessionForEachTime();
ASSERT_TRUE(db().DeleteSessionsBetween(test_times_[1], base::Time()));
ASSERT_EQ(GetPermissionUsageHistory(kTestTypes[0], kTestUrl1).size(), 1u);
ASSERT_TRUE(db().DeleteSessionsBetween(test_times_[0], base::Time()));
EXPECT_THAT(GetPermissionUsageHistory(kTestTypes[0], kTestUrl1), IsEmpty());
}
TEST_F(PermissionAuditingDatabaseTest,
DeleteSessionsBetweenWithUnspecifiedStartTime) {
StoreSessionForEachTime();
ASSERT_TRUE(db().DeleteSessionsBetween(
base::Time(), test_times_[std::size(test_times_) - 2]));
ASSERT_EQ(GetPermissionUsageHistory(kTestTypes[0], kTestUrl1).size(), 1u);
ASSERT_TRUE(db().DeleteSessionsBetween(
base::Time(), test_times_[std::size(test_times_) - 1]));
EXPECT_THAT(GetPermissionUsageHistory(kTestTypes[0], kTestUrl1), IsEmpty());
}
TEST_F(PermissionAuditingDatabaseTest,
DeleteSessionsBetweenWithUnspecifiedStartAndEndTime) {
StoreSessionForEachTime();
ASSERT_EQ(GetPermissionUsageHistory(kTestTypes[0], kTestUrl1).size(),
std::size(test_times_));
ASSERT_TRUE(db().DeleteSessionsBetween(base::Time(), base::Time()));
EXPECT_THAT(GetPermissionUsageHistory(kTestTypes[0], kTestUrl1), IsEmpty());
}
TEST_F(PermissionAuditingDatabaseTest,
StorePermissionUsageChecksTimerangeConstraint) {
auto session = SessionLike(kTestTypes[0], kTestUrl1, test_sessions_[0]);
session.usage_start = session.usage_end;
EXPECT_TRUE(db().StorePermissionUsage(session));
}
TEST_F(PermissionAuditingDatabaseTest,
StorePermissionUsageDoesntAccpetExistingRecord) {
auto session = SessionLike(kTestTypes[0], kTestUrl1, test_sessions_[0]);
EXPECT_TRUE(db().StorePermissionUsage(session));
{
sql::test::ScopedErrorExpecter expecter;
expecter.ExpectError(SQLITE_CONSTRAINT);
EXPECT_FALSE(db().StorePermissionUsage(session));
EXPECT_TRUE(expecter.SawExpectedErrors());
}
}
TEST_F(PermissionAuditingDatabaseTest, UpdateEndTimeChecksTimerangeConstraint) {
auto session = SessionLike(kTestTypes[0], kTestUrl1, test_sessions_[0]);
ASSERT_TRUE(db().StorePermissionUsage(session));
EXPECT_TRUE(UpdateEndTime(kTestTypes[0], kTestUrl1, session.usage_start,
session.usage_start));
}
} // namespace permissions