| // Copyright 2019 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 "net/extras/sqlite/sqlite_persistent_reporting_and_nel_store.h" |
| |
| #include <memory> |
| #include <vector> |
| |
| #include "base/bind.h" |
| #include "base/files/file_util.h" |
| #include "base/files/scoped_temp_dir.h" |
| #include "base/run_loop.h" |
| #include "base/sequenced_task_runner.h" |
| #include "base/synchronization/waitable_event.h" |
| #include "base/task/post_task.h" |
| #include "base/task/thread_pool.h" |
| #include "base/test/bind_test_util.h" |
| #include "base/test/metrics/histogram_tester.h" |
| #include "base/test/simple_test_clock.h" |
| #include "base/threading/thread_restrictions.h" |
| #include "base/threading/thread_task_runner_handle.h" |
| #include "net/network_error_logging/network_error_logging_service.h" |
| #include "net/reporting/reporting_test_util.h" |
| #include "net/test/test_with_task_environment.h" |
| #include "sql/database.h" |
| #include "sql/meta_table.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace net { |
| |
| namespace { |
| |
| const base::FilePath::CharType kReportingAndNELStoreFilename[] = |
| FILE_PATH_LITERAL("ReportingAndNEL"); |
| |
| const IPAddress kServerIP = IPAddress(192, 168, 0, 1); |
| const std::string kHeader = "{\"report_to\":\"group\",\"max_age\":86400}"; |
| const std::string kHeaderMaxAge0 = "{\"report_to\":\"group\",\"max_age\":0}"; |
| const std::string kGroupName1 = "group1"; |
| const std::string kGroupName2 = "group2"; |
| const base::Time kExpires = base::Time::Now() + base::TimeDelta::FromDays(7); |
| |
| enum class Op { kAdd, kDelete, kUpdate, kUpdateDetails }; |
| |
| struct TestCase { |
| std::vector<Op> operations; |
| size_t expected_queue_length; |
| }; |
| |
| // Testcases for coalescing of pending operations. In each case, the given |
| // sequence of operations should be coalesced down to |expected_queue_length| |
| // actual operations queued. |
| const std::vector<TestCase> kCoalescingTestcases = { |
| {{Op::kAdd, Op::kDelete}, 1u}, |
| {{Op::kUpdate, Op::kDelete}, 1u}, |
| {{Op::kAdd, Op::kUpdate, Op::kDelete}, 1u}, |
| {{Op::kUpdate, Op::kUpdate}, 1u}, |
| {{Op::kAdd, Op::kUpdate, Op::kUpdate}, 2u}, |
| {{Op::kDelete, Op::kAdd}, 2u}, |
| {{Op::kDelete, Op::kAdd, Op::kUpdate}, 3u}, |
| {{Op::kDelete, Op::kAdd, Op::kUpdate, Op::kUpdate}, 3u}, |
| {{Op::kDelete, Op::kDelete}, 1u}, |
| {{Op::kDelete, Op::kAdd, Op::kDelete}, 1u}, |
| {{Op::kDelete, Op::kAdd, Op::kUpdate, Op::kDelete}, 1u}}; |
| |
| // This is for Reporting endpoint groups, which have both UPDATE_DETAILS and |
| // UPDATE_ACCESS_TIME. These additional testcases test that UPDATE_DETAILS |
| // overwrites UPDATE_ACCESS_TIME, but not vice versa. |
| const std::vector<TestCase> kCoalescingTestcasesForUpdateDetails = { |
| {{Op::kUpdateDetails, Op::kDelete}, 1u}, |
| {{Op::kAdd, Op::kUpdateDetails, Op::kDelete}, 1u}, |
| {{Op::kUpdateDetails, Op::kUpdateDetails}, 1u}, |
| {{Op::kUpdate, Op::kUpdateDetails}, 1u}, |
| {{Op::kUpdateDetails, Op::kUpdate}, 2u}, |
| {{Op::kAdd, Op::kUpdateDetails, Op::kUpdate}, 3u}, |
| {{Op::kAdd, Op::kUpdateDetails, Op::kUpdate, Op::kUpdateDetails}, 2u}, |
| {{Op::kDelete, Op::kAdd, Op::kUpdateDetails}, 3u}, |
| {{Op::kDelete, Op::kAdd, Op::kUpdateDetails, Op::kUpdateDetails}, 3u}, |
| {{Op::kDelete, Op::kAdd, Op::kUpdate, Op::kUpdateDetails}, 3u}, |
| {{Op::kDelete, Op::kAdd, Op::kUpdateDetails, Op::kUpdate}, 4u}}; |
| |
| } // namespace |
| |
| class SQLitePersistentReportingAndNelStoreTest |
| : public TestWithTaskEnvironment { |
| public: |
| SQLitePersistentReportingAndNelStoreTest() {} |
| |
| void CreateStore() { |
| store_ = std::make_unique<SQLitePersistentReportingAndNelStore>( |
| temp_dir_.GetPath().Append(kReportingAndNELStoreFilename), |
| client_task_runner_, background_task_runner_); |
| } |
| |
| void DestroyStore() { |
| store_.reset(); |
| // Make sure we wait until the destructor has run by running all |
| // TaskEnvironment tasks. |
| RunUntilIdle(); |
| } |
| |
| // Call this on a brand new database that should have nothing stored in it. |
| void InitializeStore() { |
| std::vector<NetworkErrorLoggingService::NelPolicy> nel_policies; |
| LoadNelPolicies(&nel_policies); |
| EXPECT_EQ(0u, nel_policies.size()); |
| |
| // One load should be sufficient to initialize the database, but we might as |
| // well load everything to check that there is nothing in the database. |
| std::vector<ReportingEndpoint> endpoints; |
| std::vector<CachedReportingEndpointGroup> groups; |
| LoadReportingClients(&endpoints, &groups); |
| EXPECT_EQ(0u, endpoints.size()); |
| EXPECT_EQ(0u, groups.size()); |
| } |
| |
| void LoadNelPolicies( |
| std::vector<NetworkErrorLoggingService::NelPolicy>* policies_out) { |
| base::RunLoop run_loop; |
| store_->LoadNelPolicies(base::BindOnce( |
| &SQLitePersistentReportingAndNelStoreTest::OnNelPoliciesLoaded, |
| base::Unretained(this), &run_loop, policies_out)); |
| run_loop.Run(); |
| } |
| |
| void OnNelPoliciesLoaded( |
| base::RunLoop* run_loop, |
| std::vector<NetworkErrorLoggingService::NelPolicy>* policies_out, |
| std::vector<NetworkErrorLoggingService::NelPolicy> policies) { |
| policies_out->swap(policies); |
| run_loop->Quit(); |
| } |
| |
| void LoadReportingClients( |
| std::vector<ReportingEndpoint>* endpoints_out, |
| std::vector<CachedReportingEndpointGroup>* groups_out) { |
| base::RunLoop run_loop; |
| store_->LoadReportingClients(base::BindOnce( |
| &SQLitePersistentReportingAndNelStoreTest::OnReportingClientsLoaded, |
| base::Unretained(this), &run_loop, endpoints_out, groups_out)); |
| run_loop.Run(); |
| } |
| |
| void OnReportingClientsLoaded( |
| base::RunLoop* run_loop, |
| std::vector<ReportingEndpoint>* endpoints_out, |
| std::vector<CachedReportingEndpointGroup>* groups_out, |
| std::vector<ReportingEndpoint> endpoints, |
| std::vector<CachedReportingEndpointGroup> groups) { |
| endpoints_out->swap(endpoints); |
| groups_out->swap(groups); |
| run_loop->Quit(); |
| } |
| |
| std::string ReadRawDBContents() { |
| std::string contents; |
| if (!base::ReadFileToString( |
| temp_dir_.GetPath().Append(kReportingAndNELStoreFilename), |
| &contents)) { |
| return std::string(); |
| } |
| return contents; |
| } |
| |
| bool WithinOneMicrosecond(base::Time t1, base::Time t2) { |
| base::TimeDelta delta = t1 - t2; |
| return delta.magnitude() < base::TimeDelta::FromMicroseconds(1); |
| } |
| |
| void WaitOnEvent(base::WaitableEvent* event) { |
| base::ScopedAllowBaseSyncPrimitivesForTesting allow_base_sync_primitives; |
| event->Wait(); |
| } |
| |
| void SetUp() override { ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); } |
| |
| void TearDown() override { DestroyStore(); } |
| |
| NetworkErrorLoggingService::NelPolicy MakeNelPolicy(url::Origin origin, |
| base::Time last_used) { |
| NetworkErrorLoggingService::NelPolicy policy; |
| policy.origin = origin; |
| policy.received_ip_address = IPAddress::IPv4Localhost(); |
| policy.report_to = "group"; |
| policy.expires = kExpires; |
| policy.success_fraction = 0.0; |
| policy.failure_fraction = 1.0; |
| policy.include_subdomains = false; |
| policy.last_used = last_used; |
| return policy; |
| } |
| |
| ReportingEndpoint MakeReportingEndpoint( |
| url::Origin origin, |
| std::string group_name, |
| GURL url, |
| int priority = ReportingEndpoint::EndpointInfo::kDefaultPriority, |
| int weight = ReportingEndpoint::EndpointInfo::kDefaultWeight) { |
| ReportingEndpoint::EndpointInfo info; |
| info.url = url; |
| info.priority = priority; |
| info.weight = weight; |
| ReportingEndpoint endpoint( |
| ReportingEndpointGroupKey(NetworkIsolationKey(), origin, group_name), |
| std::move(info)); |
| return endpoint; |
| } |
| |
| CachedReportingEndpointGroup MakeReportingEndpointGroup( |
| url::Origin origin, |
| std::string group_name, |
| base::Time last_used, |
| OriginSubdomains include_subdomains = OriginSubdomains::DEFAULT, |
| base::Time expires = kExpires) { |
| return CachedReportingEndpointGroup( |
| ReportingEndpointGroupKey(NetworkIsolationKey(), origin, group_name), |
| include_subdomains, expires, last_used); |
| } |
| |
| protected: |
| base::ScopedTempDir temp_dir_; |
| std::unique_ptr<SQLitePersistentReportingAndNelStore> store_; |
| const scoped_refptr<base::SequencedTaskRunner> client_task_runner_ = |
| base::ThreadTaskRunnerHandle::Get(); |
| const scoped_refptr<base::SequencedTaskRunner> background_task_runner_ = |
| base::ThreadPool::CreateSequencedTaskRunner({base::MayBlock()}); |
| }; |
| |
| TEST_F(SQLitePersistentReportingAndNelStoreTest, CreateDBAndTables) { |
| CreateStore(); |
| InitializeStore(); |
| EXPECT_NE(nullptr, store_.get()); |
| std::string contents = ReadRawDBContents(); |
| EXPECT_NE("", contents); |
| EXPECT_NE(std::string::npos, contents.find("nel_policies")); |
| EXPECT_NE(std::string::npos, contents.find("reporting_endpoints")); |
| EXPECT_NE(std::string::npos, contents.find("reporting_endpoint_groups")); |
| } |
| |
| TEST_F(SQLitePersistentReportingAndNelStoreTest, TestInvalidMetaTableRecovery) { |
| CreateStore(); |
| InitializeStore(); |
| base::Time now = base::Time::Now(); |
| NetworkErrorLoggingService::NelPolicy policy1 = |
| MakeNelPolicy(url::Origin::Create(GURL("https://www.foo.test")), now); |
| store_->AddNelPolicy(policy1); |
| |
| // Close and reopen the database. |
| DestroyStore(); |
| CreateStore(); |
| |
| // Load the stored policy. |
| std::vector<NetworkErrorLoggingService::NelPolicy> policies; |
| LoadNelPolicies(&policies); |
| ASSERT_EQ(1u, policies.size()); |
| EXPECT_EQ(policy1.origin, policies[0].origin); |
| EXPECT_EQ(policy1.received_ip_address, policies[0].received_ip_address); |
| EXPECT_EQ(policy1.report_to, policies[0].report_to); |
| EXPECT_TRUE(WithinOneMicrosecond(policy1.expires, policies[0].expires)); |
| EXPECT_EQ(policy1.include_subdomains, policies[0].include_subdomains); |
| EXPECT_EQ(policy1.success_fraction, policies[0].success_fraction); |
| EXPECT_EQ(policy1.failure_fraction, policies[0].failure_fraction); |
| EXPECT_TRUE(WithinOneMicrosecond(policy1.last_used, policies[0].last_used)); |
| DestroyStore(); |
| policies.clear(); |
| |
| // Now corrupt the meta table. |
| { |
| sql::Database db; |
| ASSERT_TRUE( |
| db.Open(temp_dir_.GetPath().Append(kReportingAndNELStoreFilename))); |
| sql::MetaTable meta_table; |
| meta_table.Init(&db, 1, 1); |
| ASSERT_TRUE(db.Execute("DELETE FROM meta")); |
| db.Close(); |
| } |
| |
| base::HistogramTester hist_tester; |
| |
| // Upon loading, the database should be reset to a good, blank state. |
| CreateStore(); |
| LoadNelPolicies(&policies); |
| ASSERT_EQ(0U, policies.size()); |
| |
| hist_tester.ExpectUniqueSample("ReportingAndNEL.CorruptMetaTable", 1, 1); |
| |
| // Verify that, after, recovery, the database persists properly. |
| NetworkErrorLoggingService::NelPolicy policy2 = |
| MakeNelPolicy(url::Origin::Create(GURL("https://www.bar.test")), now); |
| store_->AddNelPolicy(policy2); |
| DestroyStore(); |
| |
| CreateStore(); |
| LoadNelPolicies(&policies); |
| ASSERT_EQ(1u, policies.size()); |
| EXPECT_EQ(policy2.origin, policies[0].origin); |
| EXPECT_EQ(policy2.received_ip_address, policies[0].received_ip_address); |
| EXPECT_EQ(policy2.report_to, policies[0].report_to); |
| EXPECT_TRUE(WithinOneMicrosecond(policy2.expires, policies[0].expires)); |
| EXPECT_EQ(policy2.include_subdomains, policies[0].include_subdomains); |
| EXPECT_EQ(policy2.success_fraction, policies[0].success_fraction); |
| EXPECT_EQ(policy2.failure_fraction, policies[0].failure_fraction); |
| EXPECT_TRUE(WithinOneMicrosecond(policy2.last_used, policies[0].last_used)); |
| } |
| |
| TEST_F(SQLitePersistentReportingAndNelStoreTest, PersistNelPolicy) { |
| CreateStore(); |
| InitializeStore(); |
| base::Time now = base::Time::Now(); |
| NetworkErrorLoggingService::NelPolicy policy = |
| MakeNelPolicy(url::Origin::Create(GURL("https://www.foo.test")), now); |
| store_->AddNelPolicy(policy); |
| |
| // Close and reopen the database. |
| DestroyStore(); |
| CreateStore(); |
| |
| // Load the stored policy. |
| std::vector<NetworkErrorLoggingService::NelPolicy> policies; |
| LoadNelPolicies(&policies); |
| ASSERT_EQ(1u, policies.size()); |
| EXPECT_EQ(policy.origin, policies[0].origin); |
| EXPECT_EQ(policy.received_ip_address, policies[0].received_ip_address); |
| EXPECT_EQ(policy.report_to, policies[0].report_to); |
| EXPECT_TRUE(WithinOneMicrosecond(policy.expires, policies[0].expires)); |
| EXPECT_EQ(policy.include_subdomains, policies[0].include_subdomains); |
| EXPECT_EQ(policy.success_fraction, policies[0].success_fraction); |
| EXPECT_EQ(policy.failure_fraction, policies[0].failure_fraction); |
| EXPECT_TRUE(WithinOneMicrosecond(policy.last_used, policies[0].last_used)); |
| } |
| |
| TEST_F(SQLitePersistentReportingAndNelStoreTest, LoadFailed) { |
| // Inject a db initialization failure by creating a directory where the db |
| // file should be. |
| ASSERT_TRUE(base::CreateDirectory( |
| temp_dir_.GetPath().Append(kReportingAndNELStoreFilename))); |
| store_ = std::make_unique<SQLitePersistentReportingAndNelStore>( |
| temp_dir_.GetPath().Append(kReportingAndNELStoreFilename), |
| client_task_runner_, background_task_runner_); |
| |
| // InitializeStore() checks that we receive empty vectors of NEL policies, |
| // reporting endpoints, and reporting endpoint groups, signifying the failure |
| // to load. |
| InitializeStore(); |
| } |
| |
| TEST_F(SQLitePersistentReportingAndNelStoreTest, UpdateNelPolicyAccessTime) { |
| CreateStore(); |
| InitializeStore(); |
| base::Time now = base::Time::Now(); |
| NetworkErrorLoggingService::NelPolicy policy = |
| MakeNelPolicy(url::Origin::Create(GURL("https://www.foo.test")), now); |
| store_->AddNelPolicy(policy); |
| |
| policy.last_used = now + base::TimeDelta::FromDays(1); |
| store_->UpdateNelPolicyAccessTime(policy); |
| |
| // Close and reopen the database. |
| DestroyStore(); |
| CreateStore(); |
| |
| // Load the stored policy. |
| std::vector<NetworkErrorLoggingService::NelPolicy> policies; |
| LoadNelPolicies(&policies); |
| ASSERT_EQ(1u, policies.size()); |
| EXPECT_EQ(policy.origin, policies[0].origin); |
| EXPECT_TRUE(WithinOneMicrosecond(policy.last_used, policies[0].last_used)); |
| } |
| |
| TEST_F(SQLitePersistentReportingAndNelStoreTest, DeleteNelPolicy) { |
| CreateStore(); |
| InitializeStore(); |
| base::Time now = base::Time::Now(); |
| NetworkErrorLoggingService::NelPolicy policy1 = |
| MakeNelPolicy(url::Origin::Create(GURL("https://www.foo.test")), now); |
| NetworkErrorLoggingService::NelPolicy policy2 = |
| MakeNelPolicy(url::Origin::Create(GURL("https://www.bar.test")), now); |
| store_->AddNelPolicy(policy1); |
| store_->AddNelPolicy(policy2); |
| |
| store_->DeleteNelPolicy(policy1); |
| |
| // Close and reopen the database. |
| DestroyStore(); |
| CreateStore(); |
| |
| // |policy1| is no longer in the database but |policy2| remains. |
| std::vector<NetworkErrorLoggingService::NelPolicy> policies; |
| LoadNelPolicies(&policies); |
| ASSERT_EQ(1u, policies.size()); |
| EXPECT_EQ(policy2.origin, policies[0].origin); |
| |
| // Delete after having closed and reopened. |
| store_->DeleteNelPolicy(policy2); |
| DestroyStore(); |
| CreateStore(); |
| |
| // |policy2| is also gone. |
| policies.clear(); |
| LoadNelPolicies(&policies); |
| EXPECT_EQ(0u, policies.size()); |
| } |
| |
| TEST_F(SQLitePersistentReportingAndNelStoreTest, |
| NelPolicyUniquenessConstraint) { |
| const url::Origin kOrigin = url::Origin::Create(GURL("https://www.foo.test")); |
| |
| CreateStore(); |
| InitializeStore(); |
| base::Time now = base::Time::Now(); |
| NetworkErrorLoggingService::NelPolicy policy1 = MakeNelPolicy(kOrigin, now); |
| // Different NEL policy (different last_used) with the same origin. |
| NetworkErrorLoggingService::NelPolicy policy2 = |
| MakeNelPolicy(kOrigin, now + base::TimeDelta::FromDays(1)); |
| |
| store_->AddNelPolicy(policy1); |
| // Adding a policy with the same origin should trigger a warning and fail to |
| // execute. |
| store_->AddNelPolicy(policy2); |
| |
| // Close and reopen the database. |
| DestroyStore(); |
| CreateStore(); |
| |
| std::vector<NetworkErrorLoggingService::NelPolicy> policies; |
| LoadNelPolicies(&policies); |
| // Only the first policy we added should be in the store. |
| ASSERT_EQ(1u, policies.size()); |
| EXPECT_EQ(policy1.origin, policies[0].origin); |
| EXPECT_TRUE(WithinOneMicrosecond(policy1.last_used, policies[0].last_used)); |
| } |
| |
| TEST_F(SQLitePersistentReportingAndNelStoreTest, CoalesceNelPolicyOperations) { |
| NetworkErrorLoggingService::NelPolicy policy = MakeNelPolicy( |
| url::Origin::Create(GURL("https://www.foo.test")), base::Time::Now()); |
| |
| base::WaitableEvent event(base::WaitableEvent::ResetPolicy::AUTOMATIC, |
| base::WaitableEvent::InitialState::NOT_SIGNALED); |
| |
| for (const TestCase& testcase : kCoalescingTestcases) { |
| CreateStore(); |
| base::RunLoop run_loop; |
| store_->LoadNelPolicies(base::BindLambdaForTesting( |
| [&](std::vector<NetworkErrorLoggingService::NelPolicy>) { |
| run_loop.Quit(); |
| })); |
| run_loop.Run(); |
| |
| // Wedge the background thread to make sure it doesn't start consuming the |
| // queue. |
| background_task_runner_->PostTask( |
| FROM_HERE, |
| base::BindOnce(&SQLitePersistentReportingAndNelStoreTest::WaitOnEvent, |
| base::Unretained(this), &event)); |
| |
| // Now run the ops, and check how much gets queued. |
| for (const Op op : testcase.operations) { |
| switch (op) { |
| case Op::kAdd: |
| store_->AddNelPolicy(policy); |
| break; |
| |
| case Op::kDelete: |
| store_->DeleteNelPolicy(policy); |
| break; |
| |
| case Op::kUpdate: |
| store_->UpdateNelPolicyAccessTime(policy); |
| break; |
| |
| default: |
| NOTREACHED(); |
| break; |
| } |
| } |
| |
| EXPECT_EQ(testcase.expected_queue_length, |
| store_->GetQueueLengthForTesting()); |
| |
| event.Signal(); |
| RunUntilIdle(); |
| } |
| } |
| |
| TEST_F(SQLitePersistentReportingAndNelStoreTest, |
| DontCoalesceUnrelatedNelPolicies) { |
| CreateStore(); |
| InitializeStore(); |
| |
| base::Time now = base::Time::Now(); |
| NetworkErrorLoggingService::NelPolicy policy1 = |
| MakeNelPolicy(url::Origin::Create(GURL("https://www.foo.test")), now); |
| NetworkErrorLoggingService::NelPolicy policy2 = |
| MakeNelPolicy(url::Origin::Create(GURL("https://www.bar.test")), now); |
| |
| base::WaitableEvent event(base::WaitableEvent::ResetPolicy::AUTOMATIC, |
| base::WaitableEvent::InitialState::NOT_SIGNALED); |
| |
| // Wedge the background thread to make sure it doesn't start consuming the |
| // queue. |
| background_task_runner_->PostTask( |
| FROM_HERE, |
| base::BindOnce(&SQLitePersistentReportingAndNelStoreTest::WaitOnEvent, |
| base::Unretained(this), &event)); |
| |
| // Delete on |policy2| should not cancel addition of unrelated |policy1|. |
| store_->AddNelPolicy(policy1); |
| store_->DeleteNelPolicy(policy2); |
| EXPECT_EQ(2u, store_->GetQueueLengthForTesting()); |
| |
| event.Signal(); |
| RunUntilIdle(); |
| } |
| |
| // These tests test that a SQLitePersistentReportingAndNelStore |
| // can be used by a NetworkErrorLoggingService to persist NEL policies. |
| class SQLitePersistNelTest : public SQLitePersistentReportingAndNelStoreTest { |
| public: |
| SQLitePersistNelTest() {} |
| |
| void SetUp() override { |
| SQLitePersistentReportingAndNelStoreTest::SetUp(); |
| SetUpNetworkErrorLoggingService(); |
| } |
| |
| void TearDown() override { |
| service_->OnShutdown(); |
| service_.reset(); |
| reporting_service_.reset(); |
| SQLitePersistentReportingAndNelStoreTest::TearDown(); |
| } |
| |
| void SetUpNetworkErrorLoggingService() { |
| CreateStore(); |
| service_ = NetworkErrorLoggingService::Create(store_.get()); |
| reporting_service_ = std::make_unique<TestReportingService>(); |
| service_->SetReportingService(reporting_service_.get()); |
| service_->SetClockForTesting(&clock_); |
| } |
| |
| void SimulateRestart() { |
| TearDown(); |
| SetUpNetworkErrorLoggingService(); |
| } |
| |
| NetworkErrorLoggingService::RequestDetails MakeRequestDetails( |
| GURL url, |
| Error error_type) { |
| NetworkErrorLoggingService::RequestDetails details; |
| |
| details.uri = url; |
| details.referrer = GURL("https://referrer.com/"); |
| details.user_agent = "Mozilla/1.0"; |
| details.server_ip = kServerIP; |
| details.method = "GET"; |
| details.status_code = 0; |
| details.elapsed_time = base::TimeDelta::FromSeconds(1); |
| details.type = error_type; |
| details.reporting_upload_depth = 0; |
| |
| return details; |
| } |
| |
| protected: |
| base::SimpleTestClock clock_; |
| std::unique_ptr<NetworkErrorLoggingService> service_; |
| std::unique_ptr<TestReportingService> reporting_service_; |
| }; |
| |
| TEST_F(SQLitePersistNelTest, AddAndRetrieveNelPolicy) { |
| const GURL kUrl("https://www.foo.test"); |
| const url::Origin kOrigin = url::Origin::Create(kUrl); |
| |
| service_->OnHeader(kOrigin, kServerIP, kHeader); |
| RunUntilIdle(); |
| |
| EXPECT_EQ(1u, service_->GetPolicyOriginsForTesting().count(kOrigin)); |
| SimulateRestart(); |
| |
| service_->OnRequest(MakeRequestDetails(kUrl, ERR_INVALID_RESPONSE)); |
| RunUntilIdle(); |
| |
| EXPECT_EQ(1u, service_->GetPolicyOriginsForTesting().count( |
| url::Origin::Create(kUrl))); |
| |
| EXPECT_THAT(reporting_service_->reports(), |
| testing::ElementsAre(ReportUrlIs(kUrl))); |
| } |
| |
| TEST_F(SQLitePersistNelTest, AddAndDeleteNelPolicy) { |
| const GURL kUrl("https://www.foo.test"); |
| const url::Origin kOrigin = url::Origin::Create(kUrl); |
| |
| service_->OnHeader(kOrigin, kServerIP, kHeader); |
| RunUntilIdle(); |
| |
| EXPECT_EQ(1u, service_->GetPolicyOriginsForTesting().count(kOrigin)); |
| SimulateRestart(); |
| |
| // Deletes the stored policy. |
| service_->OnHeader(kOrigin, kServerIP, kHeaderMaxAge0); |
| RunUntilIdle(); |
| |
| EXPECT_EQ(0u, service_->GetPolicyOriginsForTesting().count(kOrigin)); |
| SimulateRestart(); |
| |
| service_->OnRequest(MakeRequestDetails(kUrl, ERR_INVALID_RESPONSE)); |
| RunUntilIdle(); |
| |
| EXPECT_EQ(0u, service_->GetPolicyOriginsForTesting().count(kOrigin)); |
| EXPECT_EQ(0u, reporting_service_->reports().size()); |
| } |
| |
| TEST_F(SQLitePersistNelTest, ExpirationTimeIsPersisted) { |
| const GURL kUrl("https://www.foo.test"); |
| const url::Origin kOrigin = url::Origin::Create(kUrl); |
| |
| service_->OnHeader(kOrigin, kServerIP, kHeader); |
| RunUntilIdle(); |
| |
| // Makes the policy we just added expired. |
| clock_.Advance(base::TimeDelta::FromSeconds(86401)); |
| |
| SimulateRestart(); |
| |
| service_->OnRequest(MakeRequestDetails(kUrl, ERR_INVALID_RESPONSE)); |
| RunUntilIdle(); |
| |
| EXPECT_EQ(0u, reporting_service_->reports().size()); |
| |
| // Add the policy again so that it is not expired. |
| service_->OnHeader(kOrigin, kServerIP, kHeader); |
| |
| SimulateRestart(); |
| |
| service_->OnRequest(MakeRequestDetails(kUrl, ERR_INVALID_RESPONSE)); |
| RunUntilIdle(); |
| |
| EXPECT_THAT(reporting_service_->reports(), |
| testing::ElementsAre(ReportUrlIs(kUrl))); |
| } |
| |
| TEST_F(SQLitePersistNelTest, OnRequestUpdatesAccessTime) { |
| const GURL kUrl("https://www.foo.test"); |
| const url::Origin kOrigin = url::Origin::Create(kUrl); |
| |
| service_->OnHeader(kOrigin, kServerIP, kHeader); |
| RunUntilIdle(); |
| |
| SimulateRestart(); |
| |
| // Update the access time by sending a request. |
| clock_.Advance(base::TimeDelta::FromSeconds(100)); |
| service_->OnRequest(MakeRequestDetails(kUrl, ERR_INVALID_RESPONSE)); |
| RunUntilIdle(); |
| |
| EXPECT_THAT(reporting_service_->reports(), |
| testing::ElementsAre(ReportUrlIs(kUrl))); |
| |
| SimulateRestart(); |
| // Check that the policy's access time has been updated. |
| base::Time now = clock_.Now(); |
| NetworkErrorLoggingService::NelPolicy policy = MakeNelPolicy(kOrigin, now); |
| std::vector<NetworkErrorLoggingService::NelPolicy> policies; |
| LoadNelPolicies(&policies); |
| ASSERT_EQ(1u, policies.size()); |
| EXPECT_EQ(policy.origin, policies[0].origin); |
| EXPECT_TRUE(WithinOneMicrosecond(policy.last_used, policies[0].last_used)); |
| } |
| |
| TEST_F(SQLitePersistNelTest, RemoveSomeBrowsingData) { |
| const GURL kUrl1("https://www.foo.test"); |
| const url::Origin kOrigin1 = url::Origin::Create(kUrl1); |
| const url::Origin kOrigin2 = |
| url::Origin::Create(GURL("https://www.bar.test")); |
| |
| service_->OnHeader(kOrigin1, kServerIP, kHeader); |
| service_->OnHeader(kOrigin2, kServerIP, kHeader); |
| RunUntilIdle(); |
| |
| SimulateRestart(); |
| |
| service_->OnRequest(MakeRequestDetails(kUrl1, ERR_INVALID_RESPONSE)); |
| RunUntilIdle(); |
| |
| ASSERT_EQ(1u, service_->GetPolicyOriginsForTesting().count(kOrigin1)); |
| ASSERT_EQ(1u, service_->GetPolicyOriginsForTesting().count(kOrigin2)); |
| EXPECT_THAT(reporting_service_->reports(), |
| testing::ElementsAre(ReportUrlIs(kUrl1))); |
| |
| SimulateRestart(); |
| |
| service_->RemoveBrowsingData(base::BindRepeating( |
| [](const std::string& host, const GURL& origin) { |
| return origin.host() == host; |
| }, |
| kOrigin1.host())); |
| RunUntilIdle(); |
| |
| EXPECT_EQ(0u, service_->GetPolicyOriginsForTesting().count(kOrigin1)); |
| EXPECT_EQ(1u, service_->GetPolicyOriginsForTesting().count(kOrigin2)); |
| |
| SimulateRestart(); |
| |
| service_->OnRequest(MakeRequestDetails(kUrl1, ERR_INVALID_RESPONSE)); |
| RunUntilIdle(); |
| EXPECT_EQ(0u, service_->GetPolicyOriginsForTesting().count(kOrigin1)); |
| EXPECT_EQ(1u, service_->GetPolicyOriginsForTesting().count(kOrigin2)); |
| EXPECT_EQ(0u, reporting_service_->reports().size()); |
| } |
| |
| TEST_F(SQLitePersistNelTest, RemoveAllBrowsingData) { |
| const GURL kUrl1("https://www.foo.test"); |
| const url::Origin kOrigin1 = url::Origin::Create(kUrl1); |
| const GURL kUrl2("https://www.bar.test"); |
| const url::Origin kOrigin2 = url::Origin::Create(kUrl2); |
| |
| service_->OnHeader(kOrigin1, kServerIP, kHeader); |
| service_->OnHeader(kOrigin2, kServerIP, kHeader); |
| RunUntilIdle(); |
| |
| SimulateRestart(); |
| |
| service_->OnRequest(MakeRequestDetails(kUrl1, ERR_INVALID_RESPONSE)); |
| service_->OnRequest(MakeRequestDetails(kUrl2, ERR_INVALID_RESPONSE)); |
| RunUntilIdle(); |
| |
| ASSERT_EQ(1u, service_->GetPolicyOriginsForTesting().count(kOrigin1)); |
| ASSERT_EQ(1u, service_->GetPolicyOriginsForTesting().count(kOrigin2)); |
| EXPECT_THAT(reporting_service_->reports(), |
| testing::ElementsAre(ReportUrlIs(kUrl1), ReportUrlIs(kUrl2))); |
| |
| SimulateRestart(); |
| |
| service_->RemoveAllBrowsingData(); |
| RunUntilIdle(); |
| |
| EXPECT_EQ(0u, service_->GetPolicyOriginsForTesting().count(kOrigin1)); |
| EXPECT_EQ(0u, service_->GetPolicyOriginsForTesting().count(kOrigin2)); |
| |
| SimulateRestart(); |
| |
| service_->OnRequest(MakeRequestDetails(kUrl1, ERR_INVALID_RESPONSE)); |
| service_->OnRequest(MakeRequestDetails(kUrl2, ERR_INVALID_RESPONSE)); |
| RunUntilIdle(); |
| EXPECT_EQ(0u, service_->GetPolicyOriginsForTesting().count(kOrigin1)); |
| EXPECT_EQ(0u, service_->GetPolicyOriginsForTesting().count(kOrigin2)); |
| EXPECT_EQ(0u, reporting_service_->reports().size()); |
| } |
| |
| TEST_F(SQLitePersistentReportingAndNelStoreTest, PersistReportingClients) { |
| const url::Origin kOrigin = url::Origin::Create(GURL("https://www.foo.test")); |
| |
| CreateStore(); |
| InitializeStore(); |
| base::Time now = base::Time::Now(); |
| ReportingEndpoint endpoint = MakeReportingEndpoint( |
| kOrigin, kGroupName1, GURL("https://endpoint.test/1")); |
| CachedReportingEndpointGroup group = |
| MakeReportingEndpointGroup(kOrigin, kGroupName1, now); |
| |
| store_->AddReportingEndpoint(endpoint); |
| store_->AddReportingEndpointGroup(group); |
| |
| // Close and reopen the database. |
| DestroyStore(); |
| CreateStore(); |
| |
| // Load the stored clients. |
| std::vector<ReportingEndpoint> endpoints; |
| std::vector<CachedReportingEndpointGroup> groups; |
| LoadReportingClients(&endpoints, &groups); |
| ASSERT_EQ(1u, endpoints.size()); |
| EXPECT_EQ(endpoint.group_key.origin, endpoints[0].group_key.origin); |
| EXPECT_EQ(endpoint.group_key.group_name, endpoints[0].group_key.group_name); |
| EXPECT_EQ(endpoint.info.url, endpoints[0].info.url); |
| EXPECT_EQ(endpoint.info.priority, endpoints[0].info.priority); |
| EXPECT_EQ(endpoint.info.weight, endpoints[0].info.weight); |
| ASSERT_EQ(1u, groups.size()); |
| EXPECT_EQ(group.group_key.origin, groups[0].group_key.origin); |
| EXPECT_EQ(group.group_key.group_name, groups[0].group_key.group_name); |
| EXPECT_EQ(group.include_subdomains, groups[0].include_subdomains); |
| EXPECT_TRUE(WithinOneMicrosecond(group.expires, groups[0].expires)); |
| EXPECT_TRUE(WithinOneMicrosecond(group.last_used, groups[0].last_used)); |
| } |
| |
| TEST_F(SQLitePersistentReportingAndNelStoreTest, |
| UpdateReportingEndpointGroupAccessTime) { |
| CreateStore(); |
| InitializeStore(); |
| base::Time now = base::Time::Now(); |
| CachedReportingEndpointGroup group = MakeReportingEndpointGroup( |
| url::Origin::Create(GURL("https://www.foo.test")), kGroupName1, now); |
| |
| store_->AddReportingEndpointGroup(group); |
| |
| group.last_used = now + base::TimeDelta::FromDays(1); |
| store_->UpdateReportingEndpointGroupAccessTime(group); |
| |
| // Close and reopen the database. |
| DestroyStore(); |
| CreateStore(); |
| |
| std::vector<ReportingEndpoint> endpoints; |
| std::vector<CachedReportingEndpointGroup> groups; |
| LoadReportingClients(&endpoints, &groups); |
| ASSERT_EQ(1u, groups.size()); |
| EXPECT_EQ(group.group_key.origin, groups[0].group_key.origin); |
| EXPECT_EQ(group.group_key.group_name, groups[0].group_key.group_name); |
| EXPECT_TRUE(WithinOneMicrosecond(group.last_used, groups[0].last_used)); |
| } |
| |
| TEST_F(SQLitePersistentReportingAndNelStoreTest, |
| UpdateReportingEndpointDetails) { |
| CreateStore(); |
| InitializeStore(); |
| ReportingEndpoint endpoint = |
| MakeReportingEndpoint(url::Origin::Create(GURL("https://www.foo.test")), |
| kGroupName1, GURL("https://endpoint.test/1")); |
| |
| store_->AddReportingEndpoint(endpoint); |
| |
| endpoint.info.priority = 10; |
| endpoint.info.weight = 10; |
| store_->UpdateReportingEndpointDetails(endpoint); |
| |
| // Close and reopen the database. |
| DestroyStore(); |
| CreateStore(); |
| |
| std::vector<ReportingEndpoint> endpoints; |
| std::vector<CachedReportingEndpointGroup> groups; |
| LoadReportingClients(&endpoints, &groups); |
| ASSERT_EQ(1u, endpoints.size()); |
| EXPECT_EQ(endpoint.group_key.origin, endpoints[0].group_key.origin); |
| EXPECT_EQ(endpoint.group_key.group_name, endpoints[0].group_key.group_name); |
| EXPECT_EQ(endpoint.info.url, endpoints[0].info.url); |
| EXPECT_EQ(endpoint.info.priority, endpoints[0].info.priority); |
| EXPECT_EQ(endpoint.info.weight, endpoints[0].info.weight); |
| } |
| |
| TEST_F(SQLitePersistentReportingAndNelStoreTest, |
| UpdateReportingEndpointGroupDetails) { |
| CreateStore(); |
| InitializeStore(); |
| base::Time now = base::Time::Now(); |
| CachedReportingEndpointGroup group = MakeReportingEndpointGroup( |
| url::Origin::Create(GURL("https://www.foo.test")), kGroupName1, now, |
| OriginSubdomains::EXCLUDE, kExpires); |
| |
| store_->AddReportingEndpointGroup(group); |
| |
| group.last_used = now + base::TimeDelta::FromDays(1); |
| group.expires = kExpires + base::TimeDelta::FromDays(1); |
| group.include_subdomains = OriginSubdomains::INCLUDE; |
| store_->UpdateReportingEndpointGroupDetails(group); |
| |
| // Close and reopen the database. |
| DestroyStore(); |
| CreateStore(); |
| |
| std::vector<ReportingEndpoint> endpoints; |
| std::vector<CachedReportingEndpointGroup> groups; |
| LoadReportingClients(&endpoints, &groups); |
| ASSERT_EQ(1u, groups.size()); |
| EXPECT_EQ(group.group_key.origin, groups[0].group_key.origin); |
| EXPECT_EQ(group.group_key.group_name, groups[0].group_key.group_name); |
| EXPECT_EQ(group.include_subdomains, groups[0].include_subdomains); |
| EXPECT_TRUE(WithinOneMicrosecond(group.expires, groups[0].expires)); |
| EXPECT_TRUE(WithinOneMicrosecond(group.last_used, groups[0].last_used)); |
| } |
| |
| TEST_F(SQLitePersistentReportingAndNelStoreTest, DeleteReportingEndpoint) { |
| CreateStore(); |
| InitializeStore(); |
| ReportingEndpoint endpoint1 = |
| MakeReportingEndpoint(url::Origin::Create(GURL("https://www.foo.test")), |
| kGroupName1, GURL("https://endpoint.test/1")); |
| ReportingEndpoint endpoint2 = |
| MakeReportingEndpoint(url::Origin::Create(GURL("https://www.bar.test")), |
| kGroupName2, GURL("https://endpoint.test/2")); |
| |
| store_->AddReportingEndpoint(endpoint1); |
| store_->AddReportingEndpoint(endpoint2); |
| |
| store_->DeleteReportingEndpoint(endpoint1); |
| |
| // Close and reopen the database. |
| DestroyStore(); |
| CreateStore(); |
| |
| std::vector<ReportingEndpoint> endpoints; |
| std::vector<CachedReportingEndpointGroup> groups; |
| LoadReportingClients(&endpoints, &groups); |
| ASSERT_EQ(1u, endpoints.size()); |
| EXPECT_EQ(endpoint2.info.url, endpoints[0].info.url); |
| |
| store_->DeleteReportingEndpoint(endpoint2); |
| DestroyStore(); |
| CreateStore(); |
| |
| endpoints.clear(); |
| LoadReportingClients(&endpoints, &groups); |
| EXPECT_EQ(0u, endpoints.size()); |
| } |
| |
| TEST_F(SQLitePersistentReportingAndNelStoreTest, DeleteReportingEndpointGroup) { |
| CreateStore(); |
| InitializeStore(); |
| base::Time now = base::Time::Now(); |
| CachedReportingEndpointGroup group1 = MakeReportingEndpointGroup( |
| url::Origin::Create(GURL("https://www.foo.test")), kGroupName1, now); |
| CachedReportingEndpointGroup group2 = MakeReportingEndpointGroup( |
| url::Origin::Create(GURL("https://www.bar.test")), kGroupName2, now); |
| |
| store_->AddReportingEndpointGroup(group1); |
| store_->AddReportingEndpointGroup(group2); |
| |
| store_->DeleteReportingEndpointGroup(group1); |
| |
| // Close and reopen the database. |
| DestroyStore(); |
| CreateStore(); |
| |
| std::vector<ReportingEndpoint> endpoints; |
| std::vector<CachedReportingEndpointGroup> groups; |
| LoadReportingClients(&endpoints, &groups); |
| ASSERT_EQ(1u, groups.size()); |
| EXPECT_EQ(group2.group_key.group_name, groups[0].group_key.group_name); |
| |
| store_->DeleteReportingEndpointGroup(group2); |
| DestroyStore(); |
| CreateStore(); |
| |
| groups.clear(); |
| LoadReportingClients(&endpoints, &groups); |
| EXPECT_EQ(0u, groups.size()); |
| } |
| |
| TEST_F(SQLitePersistentReportingAndNelStoreTest, |
| ReportingEndpointUniquenessConstraint) { |
| const url::Origin kOrigin = url::Origin::Create(GURL("https://www.foo.test")); |
| const GURL kEndpoint("https://endpoint.test/1"); |
| |
| CreateStore(); |
| InitializeStore(); |
| ReportingEndpoint endpoint1 = MakeReportingEndpoint( |
| kOrigin, kGroupName1, kEndpoint, 1 /* priority */, 1 /* weight */); |
| ReportingEndpoint endpoint2 = MakeReportingEndpoint( |
| kOrigin, kGroupName1, kEndpoint, 2 /* priority */, 2 /* weight */); |
| |
| store_->AddReportingEndpoint(endpoint1); |
| // Adding an endpoint with the same origin, group name, and url should trigger |
| // a warning and fail to execute. |
| store_->AddReportingEndpoint(endpoint2); |
| |
| DestroyStore(); |
| CreateStore(); |
| |
| std::vector<ReportingEndpoint> endpoints; |
| std::vector<CachedReportingEndpointGroup> groups; |
| LoadReportingClients(&endpoints, &groups); |
| // Only the first endpoint we added should be in the store. |
| ASSERT_EQ(1u, endpoints.size()); |
| EXPECT_EQ(endpoint1.group_key.origin, endpoints[0].group_key.origin); |
| EXPECT_EQ(endpoint1.group_key.group_name, endpoints[0].group_key.group_name); |
| EXPECT_EQ(endpoint1.info.url, endpoints[0].info.url); |
| EXPECT_EQ(endpoint1.info.priority, endpoints[0].info.priority); |
| EXPECT_EQ(endpoint1.info.weight, endpoints[0].info.weight); |
| } |
| |
| TEST_F(SQLitePersistentReportingAndNelStoreTest, |
| ReportingEndpointGroupUniquenessConstraint) { |
| const url::Origin kOrigin = url::Origin::Create(GURL("https://www.foo.test")); |
| |
| CreateStore(); |
| InitializeStore(); |
| base::Time now = base::Time::Now(); |
| CachedReportingEndpointGroup group1 = |
| MakeReportingEndpointGroup(kOrigin, kGroupName1, now); |
| base::Time time2 = now + base::TimeDelta::FromDays(7); |
| CachedReportingEndpointGroup group2 = |
| MakeReportingEndpointGroup(kOrigin, kGroupName1, time2); |
| LOG(INFO) << "foo"; |
| |
| store_->AddReportingEndpointGroup(group1); |
| // Adding an endpoint group with the same origin and group name should trigger |
| // a warning and fail to execute. |
| store_->AddReportingEndpointGroup(group2); |
| |
| DestroyStore(); |
| CreateStore(); |
| |
| std::vector<ReportingEndpoint> endpoints; |
| std::vector<CachedReportingEndpointGroup> groups; |
| LoadReportingClients(&endpoints, &groups); |
| // Only the first group we added should be in the store. |
| ASSERT_EQ(1u, groups.size()); |
| EXPECT_EQ(group1.group_key.origin, groups[0].group_key.origin); |
| EXPECT_EQ(group1.group_key.group_name, groups[0].group_key.group_name); |
| EXPECT_EQ(group1.include_subdomains, groups[0].include_subdomains); |
| EXPECT_TRUE(WithinOneMicrosecond(group1.expires, groups[0].expires)); |
| EXPECT_TRUE(WithinOneMicrosecond(group1.last_used, groups[0].last_used)); |
| } |
| |
| TEST_F(SQLitePersistentReportingAndNelStoreTest, |
| CoalesceReportingEndpointOperations) { |
| ReportingEndpoint endpoint = |
| MakeReportingEndpoint(url::Origin::Create(GURL("https://www.foo.test")), |
| kGroupName1, GURL("https://endpoint.test/1")); |
| |
| base::WaitableEvent event(base::WaitableEvent::ResetPolicy::AUTOMATIC, |
| base::WaitableEvent::InitialState::NOT_SIGNALED); |
| |
| for (const TestCase& testcase : kCoalescingTestcases) { |
| CreateStore(); |
| base::RunLoop run_loop; |
| store_->LoadReportingClients(base::BindLambdaForTesting( |
| [&](std::vector<ReportingEndpoint>, |
| std::vector<CachedReportingEndpointGroup>) { run_loop.Quit(); })); |
| run_loop.Run(); |
| |
| // Wedge the background thread to make sure it doesn't start consuming the |
| // queue. |
| background_task_runner_->PostTask( |
| FROM_HERE, |
| base::BindOnce(&SQLitePersistentReportingAndNelStoreTest::WaitOnEvent, |
| base::Unretained(this), &event)); |
| |
| // Now run the ops, and check how much gets queued. |
| for (const Op op : testcase.operations) { |
| switch (op) { |
| case Op::kAdd: |
| store_->AddReportingEndpoint(endpoint); |
| break; |
| |
| case Op::kDelete: |
| store_->DeleteReportingEndpoint(endpoint); |
| break; |
| |
| case Op::kUpdate: |
| // Endpoints only have UPDATE_DETAILS, so in this case we use kUpdate |
| // for that. |
| store_->UpdateReportingEndpointDetails(endpoint); |
| break; |
| |
| default: |
| NOTREACHED(); |
| break; |
| } |
| } |
| |
| EXPECT_EQ(testcase.expected_queue_length, |
| store_->GetQueueLengthForTesting()); |
| |
| event.Signal(); |
| RunUntilIdle(); |
| } |
| } |
| |
| TEST_F(SQLitePersistentReportingAndNelStoreTest, |
| DontCoalesceUnrelatedReportingEndpoints) { |
| CreateStore(); |
| InitializeStore(); |
| |
| ReportingEndpoint endpoint1 = |
| MakeReportingEndpoint(url::Origin::Create(GURL("https://www.foo.test")), |
| kGroupName1, GURL("https://endpoint.test/1")); |
| ReportingEndpoint endpoint2 = |
| MakeReportingEndpoint(url::Origin::Create(GURL("https://www.bar.test")), |
| kGroupName2, GURL("https://endpoint.test/2")); |
| |
| base::WaitableEvent event(base::WaitableEvent::ResetPolicy::AUTOMATIC, |
| base::WaitableEvent::InitialState::NOT_SIGNALED); |
| |
| // Wedge the background thread to make sure it doesn't start consuming the |
| // queue. |
| background_task_runner_->PostTask( |
| FROM_HERE, |
| base::BindOnce(&SQLitePersistentReportingAndNelStoreTest::WaitOnEvent, |
| base::Unretained(this), &event)); |
| |
| // Delete on |endpoint2| should not cancel addition of unrelated |endpoint1|. |
| store_->AddReportingEndpoint(endpoint1); |
| store_->DeleteReportingEndpoint(endpoint2); |
| EXPECT_EQ(2u, store_->GetQueueLengthForTesting()); |
| |
| event.Signal(); |
| RunUntilIdle(); |
| } |
| |
| TEST_F(SQLitePersistentReportingAndNelStoreTest, |
| CoalesceReportingEndpointGroupOperations) { |
| base::Time now = base::Time::Now(); |
| CachedReportingEndpointGroup group = MakeReportingEndpointGroup( |
| url::Origin::Create(GURL("https://www.foo.test")), kGroupName1, now); |
| |
| base::WaitableEvent event(base::WaitableEvent::ResetPolicy::AUTOMATIC, |
| base::WaitableEvent::InitialState::NOT_SIGNALED); |
| |
| for (const TestCase& testcase : kCoalescingTestcases) { |
| CreateStore(); |
| base::RunLoop run_loop; |
| store_->LoadReportingClients(base::BindLambdaForTesting( |
| [&](std::vector<ReportingEndpoint>, |
| std::vector<CachedReportingEndpointGroup>) { run_loop.Quit(); })); |
| run_loop.Run(); |
| |
| // Wedge the background thread to make sure it doesn't start consuming the |
| // queue. |
| background_task_runner_->PostTask( |
| FROM_HERE, |
| base::BindOnce(&SQLitePersistentReportingAndNelStoreTest::WaitOnEvent, |
| base::Unretained(this), &event)); |
| |
| // Now run the ops, and check how much gets queued. |
| for (const Op op : testcase.operations) { |
| switch (op) { |
| case Op::kAdd: |
| store_->AddReportingEndpointGroup(group); |
| break; |
| |
| case Op::kDelete: |
| store_->DeleteReportingEndpointGroup(group); |
| break; |
| |
| case Op::kUpdate: |
| store_->UpdateReportingEndpointGroupAccessTime(group); |
| break; |
| |
| default: |
| NOTREACHED(); |
| break; |
| } |
| } |
| |
| EXPECT_EQ(testcase.expected_queue_length, |
| store_->GetQueueLengthForTesting()); |
| |
| event.Signal(); |
| RunUntilIdle(); |
| } |
| |
| // Additional test cases for UPDATE_DETAILS. |
| for (const TestCase& testcase : kCoalescingTestcasesForUpdateDetails) { |
| CreateStore(); |
| base::RunLoop run_loop; |
| store_->LoadReportingClients(base::BindLambdaForTesting( |
| [&](std::vector<ReportingEndpoint>, |
| std::vector<CachedReportingEndpointGroup>) { run_loop.Quit(); })); |
| run_loop.Run(); |
| |
| // Wedge the background thread to make sure it doesn't start consuming the |
| // queue. |
| background_task_runner_->PostTask( |
| FROM_HERE, |
| base::BindOnce(&SQLitePersistentReportingAndNelStoreTest::WaitOnEvent, |
| base::Unretained(this), &event)); |
| |
| // Now run the ops, and check how much gets queued. |
| for (const Op op : testcase.operations) { |
| switch (op) { |
| case Op::kAdd: |
| store_->AddReportingEndpointGroup(group); |
| break; |
| |
| case Op::kDelete: |
| store_->DeleteReportingEndpointGroup(group); |
| break; |
| |
| case Op::kUpdate: |
| store_->UpdateReportingEndpointGroupAccessTime(group); |
| break; |
| |
| case Op::kUpdateDetails: |
| store_->UpdateReportingEndpointGroupDetails(group); |
| break; |
| } |
| } |
| |
| EXPECT_EQ(testcase.expected_queue_length, |
| store_->GetQueueLengthForTesting()); |
| |
| event.Signal(); |
| RunUntilIdle(); |
| } |
| } |
| |
| TEST_F(SQLitePersistentReportingAndNelStoreTest, |
| DontCoalesceUnrelatedReportingEndpointGroups) { |
| CreateStore(); |
| InitializeStore(); |
| |
| base::Time now = base::Time::Now(); |
| CachedReportingEndpointGroup group1 = MakeReportingEndpointGroup( |
| url::Origin::Create(GURL("https://www.foo.test")), kGroupName1, now); |
| CachedReportingEndpointGroup group2 = MakeReportingEndpointGroup( |
| url::Origin::Create(GURL("https://www.bar.test")), kGroupName2, now); |
| |
| base::WaitableEvent event(base::WaitableEvent::ResetPolicy::AUTOMATIC, |
| base::WaitableEvent::InitialState::NOT_SIGNALED); |
| |
| // Wedge the background thread to make sure it doesn't start consuming the |
| // queue. |
| background_task_runner_->PostTask( |
| FROM_HERE, |
| base::BindOnce(&SQLitePersistentReportingAndNelStoreTest::WaitOnEvent, |
| base::Unretained(this), &event)); |
| |
| // Delete on |group2| should not cancel addition of unrelated |group2|. |
| store_->AddReportingEndpointGroup(group1); |
| store_->DeleteReportingEndpointGroup(group2); |
| EXPECT_EQ(2u, store_->GetQueueLengthForTesting()); |
| |
| event.Signal(); |
| RunUntilIdle(); |
| } |
| |
| } // namespace net |