blob: 7a994c89adc86505f5d60cef1a2d21d81583aeaa [file] [log] [blame]
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/device_bound_sessions/session_store_impl.h"
#include <memory>
#include <vector>
#include "base/files/file_path.h"
#include "base/files/scoped_temp_dir.h"
#include "base/logging.h"
#include "base/run_loop.h"
#include "base/strings/stringprintf.h"
#include "base/test/bind.h"
#include "base/test/task_environment.h"
#include "base/test/test_future.h"
#include "base/time/time.h"
#include "base/timer/elapsed_timer.h"
#include "components/unexportable_keys/unexportable_key_service_impl.h"
#include "components/unexportable_keys/unexportable_key_task_manager.h"
#include "crypto/scoped_mock_unexportable_key_provider.h"
#include "crypto/unexportable_key.h"
#include "net/base/schemeful_site.h"
#include "net/base/test_completion_callback.h"
#include "net/cookies/canonical_cookie.h"
#include "net/cookies/cookie_constants.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/perf/perf_result_reporter.h"
#include "url/gurl.h"
namespace net::device_bound_sessions {
namespace {
const base::FilePath::CharType dbsc_filename[] =
FILE_PATH_LITERAL("DBSC_Sessions");
static constexpr char kMetricPrefixDbscSS[] = "DBSCSessionStore.";
static constexpr char kMetricOperationDurationMs[] = "operation_duration";
static const int kNumSites = 200;
static const int kSessionsPerSite = 5;
constexpr crypto::SignatureVerifier::SignatureAlgorithm
kAcceptableAlgorithms[] = {crypto::SignatureVerifier::ECDSA_SHA256};
constexpr unexportable_keys::BackgroundTaskPriority kTaskPriority =
unexportable_keys::BackgroundTaskPriority::kUserBlocking;
perf_test::PerfResultReporter SetUpDbscSSReporter(const std::string& story) {
perf_test::PerfResultReporter reporter(kMetricPrefixDbscSS, story);
reporter.RegisterImportantMetric(kMetricOperationDurationMs, "ms");
return reporter;
}
} // namespace
class DBSCSessionStorePerfTest : public testing::Test {
public:
DBSCSessionStorePerfTest() : key_service_(task_manager_) {}
void CreateStore() {
store_ = std::make_unique<SessionStoreImpl>(
temp_dir_.GetPath().Append(dbsc_filename), key_service_);
}
void DeleteStore() {
base::RunLoop run_loop;
store_->SetShutdownCallbackForTesting(run_loop.QuitClosure());
store_ = nullptr;
run_loop.Run();
}
SessionStore::SessionsMap LoadSessions() {
base::RunLoop run_loop;
SessionStore::SessionsMap loaded_sessions;
store_->LoadSessions(base::BindLambdaForTesting(
[&run_loop, &loaded_sessions](SessionStore::SessionsMap sessions) {
loaded_sessions = std::move(sessions);
run_loop.Quit();
}));
run_loop.Run();
return loaded_sessions;
}
unexportable_keys::UnexportableKeyId GenerateNewKey() {
base::test::TestFuture<
unexportable_keys::ServiceErrorOr<unexportable_keys::UnexportableKeyId>>
generate_future;
key_service_.GenerateSigningKeySlowlyAsync(
kAcceptableAlgorithms, kTaskPriority, generate_future.GetCallback());
unexportable_keys::ServiceErrorOr<unexportable_keys::UnexportableKeyId>
key_id = generate_future.Get();
CHECK(key_id.has_value());
return *key_id;
}
void AddSession(int site_idx, int session_idx) {
std::string session_str = base::StringPrintf("session_id_%d", session_idx);
std::string url_str =
base::StringPrintf("https://%d.example%d.test", session_idx, site_idx);
std::string refresh_url =
base::StringPrintf("https://example%d.test/refresh.html", site_idx);
std::string cookie_name =
base::StringPrintf("cookie_%d_%d", site_idx, session_idx);
std::string cookie_attr =
base::StringPrintf("Secure; Domain=example%d.test", site_idx);
SessionParams::Scope scope;
std::vector<SessionParams::Credential> cookie_credentials(
{SessionParams::Credential{cookie_name, cookie_attr}});
SessionParams params{session_str, refresh_url, std::move(scope),
std::move(cookie_credentials)};
std::unique_ptr<Session> session =
Session::CreateIfValid(params, GURL(url_str));
ASSERT_TRUE(session);
session->set_unexportable_key_id(GenerateNewKey());
store_->SaveSession(SchemefulSite(GURL(url_str)), *session);
}
unsigned int NumSessionsInStore() { return store_->GetAllSessions().size(); }
void SetUp() override {
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
CreateStore();
LoadSessions(); // empty load
ASSERT_TRUE(store_);
// StartPerfMeasurement();
for (int site_num = 0; site_num < kNumSites; site_num++) {
for (int session_num = 0; session_num < kSessionsPerSite; ++session_num) {
AddSession(site_num, session_num);
}
}
// Delete the store. This action will cause all the session data to be
// written to disk.
DeleteStore();
}
void TearDown() override {
if (store_) {
DeleteStore();
}
}
void StartPerfMeasurement() {
DCHECK(perf_measurement_start_.is_null());
perf_measurement_start_ = base::Time::Now();
}
void EndPerfMeasurement(const std::string& story) {
DCHECK(!perf_measurement_start_.is_null());
base::TimeDelta elapsed = base::Time::Now() - perf_measurement_start_;
perf_measurement_start_ = base::Time();
auto reporter = SetUpDbscSSReporter(story);
reporter.AddResult(kMetricOperationDurationMs, elapsed.InMillisecondsF());
}
protected:
base::test::TaskEnvironment task_environment_;
base::ScopedTempDir temp_dir_;
std::unique_ptr<SessionStoreImpl> store_;
crypto::ScopedMockUnexportableKeyProvider scoped_key_provider_;
unexportable_keys::UnexportableKeyTaskManager task_manager_{
crypto::UnexportableKeyProvider::Config()};
unexportable_keys::UnexportableKeyServiceImpl key_service_;
base::Time perf_measurement_start_;
};
// Test the performance of load
// TODO(crbug.com/371964293): Refactor this test to use the
// Google Benchmark library instead.
TEST_F(DBSCSessionStorePerfTest, TestLoadPerformance) {
CreateStore();
VLOG(0) << "Beginning load from disk..";
StartPerfMeasurement();
SessionStore::SessionsMap sessions_map = LoadSessions();
EndPerfMeasurement("load");
EXPECT_EQ(NumSessionsInStore(), (unsigned int)(kNumSites * kSessionsPerSite));
VLOG(0) << "Loaded " << sessions_map.size() << " sessions.";
}
} // namespace net::device_bound_sessions