| // Copyright 2012 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/extras/sqlite/sqlite_persistent_cookie_store.h" | 
 |  | 
 | #include <vector> | 
 |  | 
 | #include "base/compiler_specific.h" | 
 | #include "base/files/scoped_temp_dir.h" | 
 | #include "base/functional/bind.h" | 
 | #include "base/rand_util.h" | 
 | #include "base/strings/stringprintf.h" | 
 | #include "base/synchronization/waitable_event.h" | 
 | #include "base/task/sequenced_task_runner.h" | 
 | #include "base/task/thread_pool.h" | 
 | #include "base/test/task_environment.h" | 
 | #include "base/time/time.h" | 
 | #include "base/timer/elapsed_timer.h" | 
 | #include "net/base/test_completion_callback.h" | 
 | #include "net/cookies/canonical_cookie.h" | 
 | #include "net/cookies/cookie_constants.h" | 
 | #include "net/extras/sqlite/cookie_crypto_delegate.h" | 
 | #include "net/log/net_log_with_source.h" | 
 | #include "testing/gtest/include/gtest/gtest.h" | 
 | #include "testing/perf/perf_result_reporter.h" | 
 | #include "url/gurl.h" | 
 |  | 
 | namespace net { | 
 |  | 
 | namespace { | 
 |  | 
 | const base::FilePath::CharType cookie_filename[] = FILE_PATH_LITERAL("Cookies"); | 
 |  | 
 | static const int kNumDomains = 300; | 
 | static const int kCookiesPerDomain = 50; | 
 |  | 
 | // Prime number noticeably larger than kNumDomains or kCookiesPerDomain | 
 | // so that multiplying this number by an incrementing index and moduloing | 
 | // with those values will return semi-random results. | 
 | static const int kRandomSeed = 13093; | 
 | static_assert(kRandomSeed > 10 * kNumDomains, | 
 |               "kRandomSeed not high enough for number of domains"); | 
 | static_assert(kRandomSeed > 10 * kCookiesPerDomain, | 
 |               "kRandomSeed not high enough for number of cookies per domain"); | 
 |  | 
 | static constexpr char kMetricPrefixSQLPCS[] = "SQLitePersistentCookieStore."; | 
 | static constexpr char kMetricOperationDurationMs[] = "operation_duration"; | 
 |  | 
 | perf_test::PerfResultReporter SetUpSQLPCSReporter(const std::string& story) { | 
 |   perf_test::PerfResultReporter reporter(kMetricPrefixSQLPCS, story); | 
 |   reporter.RegisterImportantMetric(kMetricOperationDurationMs, "ms"); | 
 |   return reporter; | 
 | } | 
 |  | 
 | }  // namespace | 
 |  | 
 | class SQLitePersistentCookieStorePerfTest : public testing::Test { | 
 |  public: | 
 |   SQLitePersistentCookieStorePerfTest() | 
 |       : test_start_(base::Time::Now()), | 
 |         loaded_event_(base::WaitableEvent::ResetPolicy::AUTOMATIC, | 
 |                       base::WaitableEvent::InitialState::NOT_SIGNALED), | 
 |         key_loaded_event_(base::WaitableEvent::ResetPolicy::AUTOMATIC, | 
 |                           base::WaitableEvent::InitialState::NOT_SIGNALED) {} | 
 |  | 
 |   void OnLoaded(std::vector<std::unique_ptr<CanonicalCookie>> cookies) { | 
 |     cookies_.swap(cookies); | 
 |     loaded_event_.Signal(); | 
 |   } | 
 |  | 
 |   void OnKeyLoaded(std::vector<std::unique_ptr<CanonicalCookie>> cookies) { | 
 |     cookies_.swap(cookies); | 
 |     key_loaded_event_.Signal(); | 
 |   } | 
 |  | 
 |   void Load() { | 
 |     store_->Load(base::BindOnce(&SQLitePersistentCookieStorePerfTest::OnLoaded, | 
 |                                 base::Unretained(this)), | 
 |                  NetLogWithSource()); | 
 |     loaded_event_.Wait(); | 
 |   } | 
 |  | 
 |   CanonicalCookie CookieFromIndices(int domain_num, int cookie_num) { | 
 |     base::Time t( | 
 |         test_start_ + | 
 |         base::Microseconds(domain_num * kCookiesPerDomain + cookie_num)); | 
 |     std::string domain_name(base::StringPrintf(".domain_%d.com", domain_num)); | 
 |     return *CanonicalCookie::CreateUnsafeCookieForTesting( | 
 |         base::StringPrintf("Cookie_%d", cookie_num), "1", domain_name, "/", t, | 
 |         t, t, t, false, false, CookieSameSite::NO_RESTRICTION, | 
 |         COOKIE_PRIORITY_DEFAULT); | 
 |   } | 
 |  | 
 |   void SetUp() override { | 
 |     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); | 
 |     store_ = base::MakeRefCounted<SQLitePersistentCookieStore>( | 
 |         temp_dir_.GetPath().Append(cookie_filename), client_task_runner_, | 
 |         background_task_runner_, /*restore_old_session_cookies=*/true, | 
 |         /*crypto_delegate=*/nullptr, /*enable_exclusive_access=*/false); | 
 |     std::vector<CanonicalCookie*> cookies; | 
 |     Load(); | 
 |     ASSERT_EQ(0u, cookies_.size()); | 
 |     // Creates kNumDomains*kCookiesPerDomain cookies from kNumDomains eTLD+1s. | 
 |     for (int domain_num = 0; domain_num < kNumDomains; domain_num++) { | 
 |       for (int cookie_num = 0; cookie_num < kCookiesPerDomain; ++cookie_num) { | 
 |         store_->AddCookie(CookieFromIndices(domain_num, cookie_num)); | 
 |       } | 
 |     } | 
 |     // Replace the store effectively destroying the current one and forcing it | 
 |     // to write its data to disk. | 
 |     store_ = nullptr; | 
 |  | 
 |     // Flush ThreadPool tasks, causing pending commits to run. | 
 |     task_environment_.RunUntilIdle(); | 
 |  | 
 |     store_ = base::MakeRefCounted<SQLitePersistentCookieStore>( | 
 |         temp_dir_.GetPath().Append(cookie_filename), client_task_runner_, | 
 |         background_task_runner_, /*restore_old_session_cookies=*/true, | 
 |         /*crypto_delegate=*/nullptr, /*enable_exclusive_access=*/false); | 
 |   } | 
 |  | 
 |   // Pick a random cookie out of the 15000 in the store and return it. | 
 |   // Note that this distribution is intended to be random for purposes of | 
 |   // probing, but will be the same each time the test is run for | 
 |   // reproducibility of performance. | 
 |   CanonicalCookie RandomCookie() { | 
 |     int consistent_random_value = ++seed_multiple_ * kRandomSeed; | 
 |     int domain = consistent_random_value % kNumDomains; | 
 |     int cookie_num = consistent_random_value % kCookiesPerDomain; | 
 |     return CookieFromIndices(domain, cookie_num); | 
 |   } | 
 |  | 
 |   void TearDown() override { store_ = nullptr; } | 
 |  | 
 |   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 = SetUpSQLPCSReporter(story); | 
 |     reporter.AddResult(kMetricOperationDurationMs, elapsed.InMillisecondsF()); | 
 |   } | 
 |  | 
 |  protected: | 
 |   int seed_multiple_ = 1; | 
 |   base::Time test_start_; | 
 |   base::test::TaskEnvironment task_environment_; | 
 |   const scoped_refptr<base::SequencedTaskRunner> background_task_runner_ = | 
 |       base::ThreadPool::CreateSequencedTaskRunner({base::MayBlock()}); | 
 |   const scoped_refptr<base::SequencedTaskRunner> client_task_runner_ = | 
 |       base::ThreadPool::CreateSequencedTaskRunner({base::MayBlock()}); | 
 |   base::WaitableEvent loaded_event_; | 
 |   base::WaitableEvent key_loaded_event_; | 
 |   std::vector<std::unique_ptr<CanonicalCookie>> cookies_; | 
 |   base::ScopedTempDir temp_dir_; | 
 |   scoped_refptr<SQLitePersistentCookieStore> store_; | 
 |   base::Time perf_measurement_start_; | 
 | }; | 
 |  | 
 | // Test the performance of priority load of cookies for a specific domain key | 
 | TEST_F(SQLitePersistentCookieStorePerfTest, TestLoadForKeyPerformance) { | 
 |   ASSERT_LT(3, kNumDomains); | 
 |   for (int domain_num = 0; domain_num < 3; ++domain_num) { | 
 |     std::string domain_name(base::StringPrintf("domain_%d.com", domain_num)); | 
 |     StartPerfMeasurement(); | 
 |     store_->LoadCookiesForKey( | 
 |         domain_name, | 
 |         base::BindOnce(&SQLitePersistentCookieStorePerfTest::OnKeyLoaded, | 
 |                        base::Unretained(this))); | 
 |     key_loaded_event_.Wait(); | 
 |     EndPerfMeasurement("load_for_key"); | 
 |  | 
 |     ASSERT_EQ(50U, cookies_.size()); | 
 |   } | 
 | } | 
 |  | 
 | // Test the performance of load | 
 | TEST_F(SQLitePersistentCookieStorePerfTest, TestLoadPerformance) { | 
 |   StartPerfMeasurement(); | 
 |   Load(); | 
 |   EndPerfMeasurement("load"); | 
 |  | 
 |   ASSERT_EQ(kNumDomains * kCookiesPerDomain, static_cast<int>(cookies_.size())); | 
 | } | 
 |  | 
 | // Test deletion performance. | 
 | TEST_F(SQLitePersistentCookieStorePerfTest, TestDeletePerformance) { | 
 |   const int kNumToDelete = 50; | 
 |   const int kNumIterations = 400; | 
 |  | 
 |   // Figure out the kNumToDelete cookies. | 
 |   std::vector<CanonicalCookie> cookies; | 
 |   cookies.reserve(kNumToDelete); | 
 |   for (int cookie = 0; cookie < kNumToDelete; ++cookie) { | 
 |     cookies.push_back(RandomCookie()); | 
 |   } | 
 |   ASSERT_EQ(static_cast<size_t>(kNumToDelete), cookies.size()); | 
 |  | 
 |   StartPerfMeasurement(); | 
 |   for (int i = 0; i < kNumIterations; ++i) { | 
 |     // Delete and flush | 
 |     for (int cookie = 0; cookie < kNumToDelete; ++cookie) { | 
 |       store_->DeleteCookie(cookies[cookie]); | 
 |     } | 
 |     { | 
 |       TestClosure test_closure; | 
 |       store_->Flush(test_closure.closure()); | 
 |       test_closure.WaitForResult(); | 
 |     } | 
 |  | 
 |     // Add and flush | 
 |     for (int cookie = 0; cookie < kNumToDelete; ++cookie) { | 
 |       store_->AddCookie(cookies[cookie]); | 
 |     } | 
 |  | 
 |     TestClosure test_closure; | 
 |     store_->Flush(test_closure.closure()); | 
 |     test_closure.WaitForResult(); | 
 |   } | 
 |   EndPerfMeasurement("delete"); | 
 | } | 
 |  | 
 | // Test update performance. | 
 | TEST_F(SQLitePersistentCookieStorePerfTest, TestUpdatePerformance) { | 
 |   const int kNumToUpdate = 50; | 
 |   const int kNumIterations = 400; | 
 |  | 
 |   // Figure out the kNumToUpdate cookies. | 
 |   std::vector<CanonicalCookie> cookies; | 
 |   cookies.reserve(kNumToUpdate); | 
 |   for (int cookie = 0; cookie < kNumToUpdate; ++cookie) { | 
 |     cookies.push_back(RandomCookie()); | 
 |   } | 
 |   ASSERT_EQ(static_cast<size_t>(kNumToUpdate), cookies.size()); | 
 |  | 
 |   StartPerfMeasurement(); | 
 |   for (int i = 0; i < kNumIterations; ++i) { | 
 |     // Update and flush | 
 |     for (int cookie = 0; cookie < kNumToUpdate; ++cookie) { | 
 |       store_->UpdateCookieAccessTime(cookies[cookie]); | 
 |     } | 
 |  | 
 |     TestClosure test_closure; | 
 |     store_->Flush(test_closure.closure()); | 
 |     test_closure.WaitForResult(); | 
 |   } | 
 |   EndPerfMeasurement("update"); | 
 | } | 
 |  | 
 | }  // namespace net |