1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <stdint.h>
6 
7 #include <map>
8 #include <memory>
9 #include <utility>
10 
11 #include "base/bind.h"
12 #include "base/files/file_path.h"
13 #include "base/files/file_util.h"
14 #include "base/files/scoped_temp_dir.h"
15 #include "base/macros.h"
16 #include "base/memory/scoped_refptr.h"
17 #include "base/run_loop.h"
18 #include "base/test/bind_test_util.h"
19 #include "base/test/task_environment.h"
20 #include "base/threading/sequenced_task_runner_handle.h"
21 #include "base/threading/thread.h"
22 #include "base/threading/thread_task_runner_handle.h"
23 #include "base/time/default_clock.h"
24 #include "content/browser/indexed_db/indexed_db_context_impl.h"
25 #include "content/browser/indexed_db/indexed_db_quota_client.h"
26 #include "storage/browser/test/mock_quota_manager.h"
27 #include "testing/gtest/include/gtest/gtest.h"
28 
29 using blink::mojom::StorageType;
30 
31 // Declared to shorten the line lengths.
32 static const StorageType kTemp = StorageType::kTemporary;
33 static const StorageType kPerm = StorageType::kPersistent;
34 static const StorageType kSync = StorageType::kSyncable;
35 
36 namespace content {
37 
38 // Base class for our test fixtures.
39 class IndexedDBQuotaClientTest : public testing::Test {
40  public:
41   const url::Origin kOriginA;
42   const url::Origin kOriginB;
43   const url::Origin kOriginOther;
44 
IndexedDBQuotaClientTest()45   IndexedDBQuotaClientTest()
46       : kOriginA(url::Origin::Create(GURL("http://host"))),
47         kOriginB(url::Origin::Create(GURL("http://host:8000"))),
48         kOriginOther(url::Origin::Create(GURL("http://other"))),
49         usage_(0) {
50     CreateTempDir();
51     auto quota_manager = base::MakeRefCounted<storage::MockQuotaManager>(
52         /*in_memory=*/false, temp_dir_.GetPath(),
53         base::ThreadTaskRunnerHandle::Get(), nullptr);
54 
55     idb_context_ = base::MakeRefCounted<IndexedDBContextImpl>(
56         temp_dir_.GetPath(), nullptr, quota_manager->proxy(),
57         base::DefaultClock::GetInstance(),
58         /*blob_storage_context=*/mojo::NullRemote(),
59         /*native_file_system_context=*/mojo::NullRemote(),
60         base::SequencedTaskRunnerHandle::Get(),
61         base::SequencedTaskRunnerHandle::Get());
62     base::RunLoop().RunUntilIdle();
63     SetupTempDir();
64   }
65 
CreateTempDir()66   void CreateTempDir() { ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); }
67 
SetupTempDir()68   void SetupTempDir() {
69     base::FilePath indexeddb_dir =
70         temp_dir_.GetPath().Append(IndexedDBContextImpl::kIndexedDBDirectory);
71     ASSERT_TRUE(base::CreateDirectory(indexeddb_dir));
72     idb_context()->set_data_path_for_testing(indexeddb_dir);
73   }
74 
~IndexedDBQuotaClientTest()75   ~IndexedDBQuotaClientTest() override {
76     base::RunLoop().RunUntilIdle();
77     idb_context_ = nullptr;
78     base::RunLoop().RunUntilIdle();
79   }
80 
GetOriginUsage(scoped_refptr<storage::QuotaClient> client,const url::Origin & origin,StorageType type)81   int64_t GetOriginUsage(scoped_refptr<storage::QuotaClient> client,
82                          const url::Origin& origin,
83                          StorageType type) {
84     usage_ = -1;
85     base::RunLoop loop;
86     client->GetOriginUsage(origin, type,
87                            base::BindLambdaForTesting([&](int64_t usage) {
88                              usage_ = usage;
89                              loop.Quit();
90                            }));
91     loop.Run();
92     EXPECT_GT(usage_, -1);
93     return usage_;
94   }
95 
GetOriginsForType(scoped_refptr<storage::QuotaClient> client,StorageType type)96   const std::set<url::Origin>& GetOriginsForType(
97       scoped_refptr<storage::QuotaClient> client,
98       StorageType type) {
99     origins_.clear();
100     base::RunLoop loop;
101     client->GetOriginsForType(
102         type,
103         base::BindLambdaForTesting([&](const std::set<url::Origin>& origins) {
104           origins_ = origins;
105           loop.Quit();
106         }));
107     loop.Run();
108     return origins_;
109   }
110 
GetOriginsForHost(scoped_refptr<storage::QuotaClient> client,StorageType type,const std::string & host)111   const std::set<url::Origin>& GetOriginsForHost(
112       scoped_refptr<storage::QuotaClient> client,
113       StorageType type,
114       const std::string& host) {
115     origins_.clear();
116     base::RunLoop loop;
117     client->GetOriginsForHost(
118         type, host,
119         base::BindLambdaForTesting([&](const std::set<url::Origin>& origins) {
120           origins_ = origins;
121           loop.Quit();
122         }));
123     loop.Run();
124     return origins_;
125   }
126 
DeleteOrigin(scoped_refptr<storage::QuotaClient> client,const url::Origin & origin,StorageType type)127   blink::mojom::QuotaStatusCode DeleteOrigin(
128       scoped_refptr<storage::QuotaClient> client,
129       const url::Origin& origin,
130       StorageType type) {
131     delete_status_ = blink::mojom::QuotaStatusCode::kUnknown;
132     base::RunLoop loop;
133     client->DeleteOriginData(
134         origin, type,
135         base::BindLambdaForTesting([&](blink::mojom::QuotaStatusCode code) {
136           delete_status_ = code;
137           loop.Quit();
138         }));
139     loop.Run();
140     return delete_status_;
141   }
142 
idb_context()143   IndexedDBContextImpl* idb_context() { return idb_context_.get(); }
144 
SetFileSizeTo(const base::FilePath & path,int size)145   void SetFileSizeTo(const base::FilePath& path, int size) {
146     std::string junk(size, 'a');
147     ASSERT_EQ(size, base::WriteFile(path, junk.c_str(), size));
148   }
149 
AddFakeIndexedDB(const url::Origin & origin,int size)150   void AddFakeIndexedDB(const url::Origin& origin, int size) {
151     base::FilePath file_path_origin;
152     {
153       base::RunLoop run_loop;
154       idb_context()->GetFilePathForTesting(
155           origin, base::BindLambdaForTesting([&](const base::FilePath& path) {
156             file_path_origin = path;
157             run_loop.Quit();
158           }));
159       run_loop.Run();
160     }
161     if (!base::CreateDirectory(file_path_origin)) {
162       LOG(ERROR) << "failed to base::CreateDirectory "
163                  << file_path_origin.value();
164     }
165     file_path_origin = file_path_origin.Append(FILE_PATH_LITERAL("fake_file"));
166     SetFileSizeTo(file_path_origin, size);
167 
168     {
169       base::RunLoop run_loop;
170       idb_context()->ResetCachesForTesting(run_loop.QuitClosure());
171       run_loop.Run();
172     }
173   }
174 
175  private:
176   base::test::TaskEnvironment task_environment_;
177   base::ScopedTempDir temp_dir_;
178   int64_t usage_;
179   std::set<url::Origin> origins_;
180   scoped_refptr<IndexedDBContextImpl> idb_context_;
181   blink::mojom::QuotaStatusCode delete_status_;
182   base::WeakPtrFactory<IndexedDBQuotaClientTest> weak_factory_{this};
183 
184   DISALLOW_COPY_AND_ASSIGN(IndexedDBQuotaClientTest);
185 };
186 
TEST_F(IndexedDBQuotaClientTest,GetOriginUsage)187 TEST_F(IndexedDBQuotaClientTest, GetOriginUsage) {
188   auto client = base::MakeRefCounted<IndexedDBQuotaClient>(idb_context());
189 
190   AddFakeIndexedDB(kOriginA, 6);
191   AddFakeIndexedDB(kOriginB, 3);
192   EXPECT_EQ(6, GetOriginUsage(client, kOriginA, kTemp));
193   EXPECT_EQ(0, GetOriginUsage(client, kOriginA, kPerm));
194   EXPECT_EQ(3, GetOriginUsage(client, kOriginB, kTemp));
195   EXPECT_EQ(0, GetOriginUsage(client, kOriginB, kPerm));
196 
197   AddFakeIndexedDB(kOriginA, 1000);
198   EXPECT_EQ(1000, GetOriginUsage(client, kOriginA, kTemp));
199   EXPECT_EQ(0, GetOriginUsage(client, kOriginA, kPerm));
200   EXPECT_EQ(3, GetOriginUsage(client, kOriginB, kTemp));
201   EXPECT_EQ(0, GetOriginUsage(client, kOriginB, kPerm));
202 }
203 
TEST_F(IndexedDBQuotaClientTest,GetOriginsForHost)204 TEST_F(IndexedDBQuotaClientTest, GetOriginsForHost) {
205   auto client = base::MakeRefCounted<IndexedDBQuotaClient>(idb_context());
206 
207   EXPECT_EQ(kOriginA.host(), kOriginB.host());
208   EXPECT_NE(kOriginA.host(), kOriginOther.host());
209 
210   std::set<url::Origin> origins =
211       GetOriginsForHost(client, kTemp, kOriginA.host());
212   EXPECT_TRUE(origins.empty());
213 
214   AddFakeIndexedDB(kOriginA, 1000);
215   origins = GetOriginsForHost(client, kTemp, kOriginA.host());
216   EXPECT_EQ(origins.size(), 1ul);
217   EXPECT_TRUE(origins.find(kOriginA) != origins.end());
218 
219   AddFakeIndexedDB(kOriginB, 1000);
220   origins = GetOriginsForHost(client, kTemp, kOriginA.host());
221   EXPECT_EQ(origins.size(), 2ul);
222   EXPECT_TRUE(origins.find(kOriginA) != origins.end());
223   EXPECT_TRUE(origins.find(kOriginB) != origins.end());
224 
225   EXPECT_TRUE(GetOriginsForHost(client, kPerm, kOriginA.host()).empty());
226   EXPECT_TRUE(GetOriginsForHost(client, kTemp, kOriginOther.host()).empty());
227 }
228 
TEST_F(IndexedDBQuotaClientTest,GetOriginsForType)229 TEST_F(IndexedDBQuotaClientTest, GetOriginsForType) {
230   auto client = base::MakeRefCounted<IndexedDBQuotaClient>(idb_context());
231 
232   EXPECT_TRUE(GetOriginsForType(client, kTemp).empty());
233   EXPECT_TRUE(GetOriginsForType(client, kPerm).empty());
234 
235   AddFakeIndexedDB(kOriginA, 1000);
236   std::set<url::Origin> origins = GetOriginsForType(client, kTemp);
237   EXPECT_EQ(origins.size(), 1ul);
238   EXPECT_TRUE(origins.find(kOriginA) != origins.end());
239 
240   EXPECT_TRUE(GetOriginsForType(client, kPerm).empty());
241 }
242 
TEST_F(IndexedDBQuotaClientTest,DeleteOrigin)243 TEST_F(IndexedDBQuotaClientTest, DeleteOrigin) {
244   auto client = base::MakeRefCounted<IndexedDBQuotaClient>(idb_context());
245 
246   AddFakeIndexedDB(kOriginA, 1000);
247   AddFakeIndexedDB(kOriginB, 50);
248   EXPECT_EQ(1000, GetOriginUsage(client, kOriginA, kTemp));
249   EXPECT_EQ(50, GetOriginUsage(client, kOriginB, kTemp));
250 
251   blink::mojom::QuotaStatusCode delete_status =
252       DeleteOrigin(client, kOriginA, kTemp);
253   EXPECT_EQ(blink::mojom::QuotaStatusCode::kOk, delete_status);
254   EXPECT_EQ(0, GetOriginUsage(client, kOriginA, kTemp));
255   EXPECT_EQ(50, GetOriginUsage(client, kOriginB, kTemp));
256 
257   // IndexedDB only supports temporary storage; requests to delete other types
258   // are no-ops, but should not fail.
259   delete_status = DeleteOrigin(client, kOriginA, kPerm);
260   EXPECT_EQ(blink::mojom::QuotaStatusCode::kOk, delete_status);
261 
262   delete_status = DeleteOrigin(client, kOriginA, kSync);
263   EXPECT_EQ(blink::mojom::QuotaStatusCode::kOk, delete_status);
264 }
265 
266 }  // namespace content
267