| // Copyright 2014 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 <stdint.h> |
| |
| #include <map> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/bind.h" |
| #include "base/callback_helpers.h" |
| #include "base/containers/contains.h" |
| #include "base/files/file_path.h" |
| #include "base/location.h" |
| #include "base/memory/scoped_refptr.h" |
| #include "base/run_loop.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/task/single_thread_task_runner_forward.h" |
| #include "base/test/bind.h" |
| #include "base/test/task_environment.h" |
| #include "base/threading/sequenced_task_runner_handle.h" |
| #include "components/services/storage/public/mojom/quota_client.mojom.h" |
| #include "net/base/completion_once_callback.h" |
| #include "net/base/net_errors.h" |
| #include "storage/browser/database/database_quota_client.h" |
| #include "storage/browser/database/database_tracker.h" |
| #include "storage/browser/database/database_util.h" |
| #include "storage/browser/quota/quota_manager_proxy.h" |
| #include "storage/browser/quota/special_storage_policy.h" |
| #include "storage/common/database/database_identifier.h" |
| #include "testing/gmock/include/gmock/gmock-matchers.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "third_party/blink/public/common/storage_key/storage_key.h" |
| #include "third_party/blink/public/mojom/quota/quota_types.mojom.h" |
| #include "url/gurl.h" |
| #include "url/origin.h" |
| |
| namespace storage { |
| |
| // Declared to shorten the line lengths. |
| static const blink::mojom::StorageType kTemp = |
| blink::mojom::StorageType::kTemporary; |
| |
| // Mocks DatabaseTracker methods used by DatabaseQuotaClient. |
| class MockDatabaseTracker : public DatabaseTracker { |
| public: |
| MockDatabaseTracker() |
| : DatabaseTracker(base::FilePath(), |
| /*is_incognito=*/false, |
| /*special_storage_policy=*/nullptr, |
| /*quota_manager_proxy=*/nullptr, |
| DatabaseTracker::CreatePassKey()) {} |
| |
| bool GetOriginInfo(const std::string& origin_identifier, |
| OriginInfo* info) override { |
| auto found = |
| mock_origin_infos_.find(GetOriginFromIdentifier(origin_identifier)); |
| if (found == mock_origin_infos_.end()) |
| return false; |
| *info = OriginInfo(found->second); |
| return true; |
| } |
| |
| bool GetAllOriginIdentifiers( |
| std::vector<std::string>* origins_identifiers) override { |
| for (const auto& origin_info : mock_origin_infos_) |
| origins_identifiers->push_back(origin_info.second.GetOriginIdentifier()); |
| return true; |
| } |
| |
| bool GetAllOriginsInfo(std::vector<OriginInfo>* origins_info) override { |
| for (const auto& origin_info : mock_origin_infos_) |
| origins_info->push_back(OriginInfo(origin_info.second)); |
| return true; |
| } |
| |
| void DeleteDataForOrigin(const url::Origin& origin, |
| net::CompletionOnceCallback callback) override { |
| ++delete_called_count_; |
| if (async_delete()) { |
| base::SequencedTaskRunnerHandle::Get()->PostTask( |
| FROM_HERE, |
| base::BindOnce(&MockDatabaseTracker::AsyncDeleteDataForOrigin, this, |
| std::move(callback))); |
| return; |
| } |
| std::move(callback).Run(net::OK); |
| } |
| |
| void AsyncDeleteDataForOrigin(net::CompletionOnceCallback callback) { |
| std::move(callback).Run(net::OK); |
| } |
| |
| void AddMockDatabase(const url::Origin& origin, const char* name, int size) { |
| MockStorageKeyInfo& info = mock_origin_infos_[origin]; |
| info.set_origin(GetIdentifierFromOrigin(origin)); |
| info.AddMockDatabase(base::ASCIIToUTF16(name), size); |
| } |
| |
| int delete_called_count() { return delete_called_count_; } |
| bool async_delete() { return async_delete_; } |
| void set_async_delete(bool async) { async_delete_ = async; } |
| |
| protected: |
| ~MockDatabaseTracker() override = default; |
| |
| private: |
| class MockStorageKeyInfo : public OriginInfo { |
| public: |
| void set_origin(const std::string& origin_identifier) { |
| origin_identifier_ = origin_identifier; |
| } |
| |
| void AddMockDatabase(const std::u16string& name, int size) { |
| EXPECT_FALSE(base::Contains(database_sizes_, name)); |
| database_sizes_[name] = size; |
| total_size_ += size; |
| } |
| }; |
| |
| int delete_called_count_ = 0; |
| bool async_delete_ = false; |
| std::map<url::Origin, MockStorageKeyInfo> mock_origin_infos_; |
| }; |
| |
| // Base class for our test fixtures. |
| class DatabaseQuotaClientTest : public testing::Test { |
| public: |
| const blink::StorageKey kStorageKeyA; |
| const blink::StorageKey kStorageKeyB; |
| const blink::StorageKey kStorageKeyOther; |
| |
| DatabaseQuotaClientTest() |
| : kStorageKeyA( |
| blink::StorageKey::CreateFromStringForTesting("http://host")), |
| kStorageKeyB( |
| blink::StorageKey::CreateFromStringForTesting("http://host:8000")), |
| kStorageKeyOther( |
| blink::StorageKey::CreateFromStringForTesting("http://other")), |
| mock_tracker_(base::MakeRefCounted<MockDatabaseTracker>()) {} |
| |
| void TearDown() override { |
| base::RunLoop run_loop; |
| mock_tracker_->task_runner()->PostTask(FROM_HERE, |
| base::BindLambdaForTesting([&]() { |
| mock_tracker_->Shutdown(); |
| run_loop.Quit(); |
| })); |
| run_loop.Run(); |
| } |
| |
| static int64_t GetStorageKeyUsage(mojom::QuotaClient& client, |
| const blink::StorageKey& storage_key, |
| blink::mojom::StorageType type) { |
| int result = -1; |
| base::RunLoop loop; |
| client.GetStorageKeyUsage(storage_key, type, |
| base::BindLambdaForTesting([&](int64_t usage) { |
| result = usage; |
| loop.Quit(); |
| })); |
| loop.Run(); |
| EXPECT_GT(result, -1); |
| return result; |
| } |
| |
| static std::vector<blink::StorageKey> GetStorageKeysForType( |
| mojom::QuotaClient& client, |
| blink::mojom::StorageType type) { |
| std::vector<blink::StorageKey> result; |
| base::RunLoop loop; |
| client.GetStorageKeysForType( |
| type, base::BindLambdaForTesting( |
| [&](const std::vector<blink::StorageKey>& storage_keys) { |
| result = storage_keys; |
| loop.Quit(); |
| })); |
| loop.Run(); |
| return result; |
| } |
| |
| static std::vector<blink::StorageKey> GetStorageKeysForHost( |
| mojom::QuotaClient& client, |
| blink::mojom::StorageType type, |
| const std::string& host) { |
| std::vector<blink::StorageKey> result; |
| base::RunLoop loop; |
| client.GetStorageKeysForHost( |
| type, host, |
| base::BindLambdaForTesting( |
| [&](const std::vector<blink::StorageKey>& storage_keys) { |
| result = storage_keys; |
| loop.Quit(); |
| })); |
| loop.Run(); |
| return result; |
| } |
| |
| static blink::mojom::QuotaStatusCode DeleteStorageKeyData( |
| mojom::QuotaClient& client, |
| blink::mojom::StorageType type, |
| const blink::StorageKey& storage_key) { |
| blink::mojom::QuotaStatusCode result = |
| blink::mojom::QuotaStatusCode::kUnknown; |
| base::RunLoop loop; |
| client.DeleteStorageKeyData( |
| storage_key, type, |
| base::BindLambdaForTesting([&](blink::mojom::QuotaStatusCode code) { |
| result = code; |
| loop.Quit(); |
| })); |
| loop.Run(); |
| return result; |
| } |
| |
| base::test::TaskEnvironment task_environment_; |
| scoped_refptr<MockDatabaseTracker> mock_tracker_; |
| base::WeakPtrFactory<DatabaseQuotaClientTest> weak_factory_{this}; |
| }; |
| |
| TEST_F(DatabaseQuotaClientTest, GetStorageKeyUsage) { |
| DatabaseQuotaClient client(*mock_tracker_); |
| |
| EXPECT_EQ(0, GetStorageKeyUsage(client, kStorageKeyA, kTemp)); |
| |
| mock_tracker_->AddMockDatabase(kStorageKeyA.origin(), "fooDB", 1000); |
| EXPECT_EQ(1000, GetStorageKeyUsage(client, kStorageKeyA, kTemp)); |
| |
| EXPECT_EQ(0, GetStorageKeyUsage(client, kStorageKeyB, kTemp)); |
| } |
| |
| TEST_F(DatabaseQuotaClientTest, GetStorageKeysForHost) { |
| DatabaseQuotaClient client(*mock_tracker_); |
| |
| EXPECT_EQ(kStorageKeyA.origin().host(), kStorageKeyB.origin().host()); |
| EXPECT_NE(kStorageKeyA.origin().host(), kStorageKeyOther.origin().host()); |
| |
| std::vector<blink::StorageKey> storage_keys = |
| GetStorageKeysForHost(client, kTemp, kStorageKeyA.origin().host()); |
| EXPECT_TRUE(storage_keys.empty()); |
| |
| mock_tracker_->AddMockDatabase(kStorageKeyA.origin(), "fooDB", 1000); |
| storage_keys = |
| GetStorageKeysForHost(client, kTemp, kStorageKeyA.origin().host()); |
| EXPECT_EQ(storage_keys.size(), 1ul); |
| EXPECT_THAT(storage_keys, testing::Contains(kStorageKeyA)); |
| |
| mock_tracker_->AddMockDatabase(kStorageKeyB.origin(), "barDB", 1000); |
| storage_keys = |
| GetStorageKeysForHost(client, kTemp, kStorageKeyA.origin().host()); |
| EXPECT_EQ(storage_keys.size(), 2ul); |
| EXPECT_THAT(storage_keys, testing::Contains(kStorageKeyA)); |
| EXPECT_THAT(storage_keys, testing::Contains(kStorageKeyB)); |
| |
| EXPECT_TRUE( |
| GetStorageKeysForHost(client, kTemp, kStorageKeyOther.origin().host()) |
| .empty()); |
| } |
| |
| TEST_F(DatabaseQuotaClientTest, GetStorageKeysForType) { |
| DatabaseQuotaClient client(*mock_tracker_); |
| |
| EXPECT_TRUE(GetStorageKeysForType(client, kTemp).empty()); |
| |
| mock_tracker_->AddMockDatabase(kStorageKeyA.origin(), "fooDB", 1000); |
| std::vector<blink::StorageKey> storage_keys = |
| GetStorageKeysForType(client, kTemp); |
| EXPECT_EQ(storage_keys.size(), 1ul); |
| EXPECT_THAT(storage_keys, testing::Contains(kStorageKeyA)); |
| } |
| |
| TEST_F(DatabaseQuotaClientTest, DeleteStorageKeyData) { |
| DatabaseQuotaClient client(*mock_tracker_); |
| |
| mock_tracker_->set_async_delete(false); |
| EXPECT_EQ(blink::mojom::QuotaStatusCode::kOk, |
| DeleteStorageKeyData(client, kTemp, kStorageKeyA)); |
| EXPECT_EQ(1, mock_tracker_->delete_called_count()); |
| |
| mock_tracker_->set_async_delete(true); |
| EXPECT_EQ(blink::mojom::QuotaStatusCode::kOk, |
| DeleteStorageKeyData(client, kTemp, kStorageKeyA)); |
| EXPECT_EQ(2, mock_tracker_->delete_called_count()); |
| } |
| |
| } // namespace storage |