1// Copyright (c) 2012 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 <map>
6
7#include "base/bind.h"
8#include "base/files/file_path.h"
9#include "base/message_loop/message_loop.h"
10#include "base/message_loop/message_loop_proxy.h"
11#include "base/strings/utf_string_conversions.h"
12#include "net/base/completion_callback.h"
13#include "net/base/net_errors.h"
14#include "testing/gtest/include/gtest/gtest.h"
15#include "webkit/browser/database/database_quota_client.h"
16#include "webkit/browser/database/database_tracker.h"
17#include "webkit/browser/database/database_util.h"
18#include "webkit/common/database/database_identifier.h"
19
20namespace webkit_database {
21
22// Declared to shorten the line lengths.
23static const quota::StorageType kTemp = quota::kStorageTypeTemporary;
24static const quota::StorageType kPerm = quota::kStorageTypePersistent;
25
26// Mock tracker class the mocks up those methods of the tracker
27// that are used by the QuotaClient.
28class MockDatabaseTracker : public DatabaseTracker {
29 public:
30  MockDatabaseTracker()
31      : DatabaseTracker(base::FilePath(), false, NULL, NULL, NULL),
32        delete_called_count_(0),
33        async_delete_(false) {}
34
35  virtual bool GetOriginInfo(
36      const std::string& origin_identifier,
37      OriginInfo* info) OVERRIDE {
38    std::map<GURL, MockOriginInfo>::const_iterator found =
39        mock_origin_infos_.find(
40            webkit_database::GetOriginFromIdentifier(origin_identifier));
41    if (found == mock_origin_infos_.end())
42      return false;
43    *info = OriginInfo(found->second);
44    return true;
45  }
46
47  virtual bool GetAllOriginIdentifiers(
48      std::vector<std::string>* origins_identifiers) OVERRIDE {
49    std::map<GURL, MockOriginInfo>::const_iterator iter;
50    for (iter = mock_origin_infos_.begin();
51         iter != mock_origin_infos_.end();
52         ++iter) {
53      origins_identifiers->push_back(iter->second.GetOriginIdentifier());
54    }
55    return true;
56  }
57
58  virtual bool GetAllOriginsInfo(
59      std::vector<OriginInfo>* origins_info) OVERRIDE {
60    std::map<GURL, MockOriginInfo>::const_iterator iter;
61    for (iter = mock_origin_infos_.begin();
62         iter != mock_origin_infos_.end();
63         ++iter) {
64      origins_info->push_back(OriginInfo(iter->second));
65    }
66    return true;
67  }
68
69  virtual int DeleteDataForOrigin(
70      const std::string& origin_identifier,
71      const net::CompletionCallback& callback) OVERRIDE {
72    ++delete_called_count_;
73    if (async_delete()) {
74      base::MessageLoopProxy::current()->PostTask(
75          FROM_HERE,
76          base::Bind(&MockDatabaseTracker::AsyncDeleteDataForOrigin, this,
77                     callback));
78      return net::ERR_IO_PENDING;
79    }
80    return net::OK;
81  }
82
83  void AsyncDeleteDataForOrigin(const net::CompletionCallback& callback) {
84    callback.Run(net::OK);
85  }
86
87  void AddMockDatabase(const GURL& origin,  const char* name, int size) {
88    MockOriginInfo& info = mock_origin_infos_[origin];
89    info.set_origin(webkit_database::GetIdentifierFromOrigin(origin));
90    info.AddMockDatabase(ASCIIToUTF16(name), size);
91  }
92
93  int delete_called_count() { return delete_called_count_; }
94  bool async_delete() { return async_delete_; }
95  void set_async_delete(bool async) { async_delete_ = async; }
96
97 protected:
98  virtual ~MockDatabaseTracker() {}
99
100 private:
101  class MockOriginInfo : public OriginInfo {
102   public:
103    void set_origin(const std::string& origin_identifier) {
104      origin_identifier_ = origin_identifier;
105    }
106
107    void AddMockDatabase(const base::string16& name, int size) {
108      EXPECT_TRUE(database_info_.find(name) == database_info_.end());
109      database_info_[name].first = size;
110      total_size_ += size;
111    }
112  };
113
114  int delete_called_count_;
115  bool async_delete_;
116  std::map<GURL, MockOriginInfo> mock_origin_infos_;
117};
118
119
120// Base class for our test fixtures.
121class DatabaseQuotaClientTest : public testing::Test {
122 public:
123  const GURL kOriginA;
124  const GURL kOriginB;
125  const GURL kOriginOther;
126
127  DatabaseQuotaClientTest()
128      : kOriginA("http://host"),
129        kOriginB("http://host:8000"),
130        kOriginOther("http://other"),
131        usage_(0),
132        mock_tracker_(new MockDatabaseTracker),
133        weak_factory_(this) {
134  }
135
136  int64 GetOriginUsage(
137      quota::QuotaClient* client,
138      const GURL& origin,
139      quota::StorageType type) {
140    usage_ = 0;
141    client->GetOriginUsage(
142        origin, type,
143        base::Bind(&DatabaseQuotaClientTest::OnGetOriginUsageComplete,
144                   weak_factory_.GetWeakPtr()));
145    base::MessageLoop::current()->RunUntilIdle();
146    return usage_;
147  }
148
149  const std::set<GURL>& GetOriginsForType(
150      quota::QuotaClient* client,
151      quota::StorageType type) {
152    origins_.clear();
153    client->GetOriginsForType(
154        type,
155        base::Bind(&DatabaseQuotaClientTest::OnGetOriginsComplete,
156                   weak_factory_.GetWeakPtr()));
157    base::MessageLoop::current()->RunUntilIdle();
158    return origins_;
159  }
160
161  const std::set<GURL>& GetOriginsForHost(
162      quota::QuotaClient* client,
163      quota::StorageType type,
164      const std::string& host) {
165    origins_.clear();
166    client->GetOriginsForHost(
167        type, host,
168        base::Bind(&DatabaseQuotaClientTest::OnGetOriginsComplete,
169                   weak_factory_.GetWeakPtr()));
170    base::MessageLoop::current()->RunUntilIdle();
171    return origins_;
172  }
173
174  bool DeleteOriginData(
175      quota::QuotaClient* client,
176      quota::StorageType type,
177      const GURL& origin) {
178    delete_status_ = quota::kQuotaStatusUnknown;
179    client->DeleteOriginData(
180        origin, type,
181        base::Bind(&DatabaseQuotaClientTest::OnDeleteOriginDataComplete,
182                   weak_factory_.GetWeakPtr()));
183    base::MessageLoop::current()->RunUntilIdle();
184    return delete_status_ == quota::kQuotaStatusOk;
185  }
186
187  MockDatabaseTracker* mock_tracker() { return mock_tracker_.get(); }
188
189
190 private:
191  void OnGetOriginUsageComplete(int64 usage) {
192    usage_ = usage;
193  }
194
195  void OnGetOriginsComplete(const std::set<GURL>& origins) {
196    origins_ = origins;
197  }
198
199  void OnDeleteOriginDataComplete(quota::QuotaStatusCode status) {
200    delete_status_ = status;
201  }
202
203  base::MessageLoop message_loop_;
204  int64 usage_;
205  std::set<GURL> origins_;
206  quota::QuotaStatusCode delete_status_;
207  scoped_refptr<MockDatabaseTracker> mock_tracker_;
208  base::WeakPtrFactory<DatabaseQuotaClientTest> weak_factory_;
209};
210
211
212TEST_F(DatabaseQuotaClientTest, GetOriginUsage) {
213  DatabaseQuotaClient client(base::MessageLoopProxy::current().get(),
214                             mock_tracker());
215
216  EXPECT_EQ(0, GetOriginUsage(&client, kOriginA, kTemp));
217  EXPECT_EQ(0, GetOriginUsage(&client, kOriginA, kPerm));
218
219  mock_tracker()->AddMockDatabase(kOriginA, "fooDB", 1000);
220  EXPECT_EQ(1000, GetOriginUsage(&client, kOriginA, kTemp));
221  EXPECT_EQ(0, GetOriginUsage(&client, kOriginA, kPerm));
222
223  EXPECT_EQ(0, GetOriginUsage(&client, kOriginB, kPerm));
224  EXPECT_EQ(0, GetOriginUsage(&client, kOriginB, kTemp));
225}
226
227TEST_F(DatabaseQuotaClientTest, GetOriginsForHost) {
228  DatabaseQuotaClient client(base::MessageLoopProxy::current().get(),
229                             mock_tracker());
230
231  EXPECT_EQ(kOriginA.host(), kOriginB.host());
232  EXPECT_NE(kOriginA.host(), kOriginOther.host());
233
234  std::set<GURL> origins = GetOriginsForHost(&client, kTemp, kOriginA.host());
235  EXPECT_TRUE(origins.empty());
236
237  mock_tracker()->AddMockDatabase(kOriginA, "fooDB", 1000);
238  origins = GetOriginsForHost(&client, kTemp, kOriginA.host());
239  EXPECT_EQ(origins.size(), 1ul);
240  EXPECT_TRUE(origins.find(kOriginA) != origins.end());
241
242  mock_tracker()->AddMockDatabase(kOriginB, "barDB", 1000);
243  origins = GetOriginsForHost(&client, kTemp, kOriginA.host());
244  EXPECT_EQ(origins.size(), 2ul);
245  EXPECT_TRUE(origins.find(kOriginA) != origins.end());
246  EXPECT_TRUE(origins.find(kOriginB) != origins.end());
247
248  EXPECT_TRUE(GetOriginsForHost(&client, kPerm, kOriginA.host()).empty());
249  EXPECT_TRUE(GetOriginsForHost(&client, kTemp, kOriginOther.host()).empty());
250}
251
252TEST_F(DatabaseQuotaClientTest, GetOriginsForType) {
253  DatabaseQuotaClient client(base::MessageLoopProxy::current().get(),
254                             mock_tracker());
255
256  EXPECT_TRUE(GetOriginsForType(&client, kTemp).empty());
257  EXPECT_TRUE(GetOriginsForType(&client, kPerm).empty());
258
259  mock_tracker()->AddMockDatabase(kOriginA, "fooDB", 1000);
260  std::set<GURL> origins = GetOriginsForType(&client, kTemp);
261  EXPECT_EQ(origins.size(), 1ul);
262  EXPECT_TRUE(origins.find(kOriginA) != origins.end());
263
264  EXPECT_TRUE(GetOriginsForType(&client, kPerm).empty());
265}
266
267TEST_F(DatabaseQuotaClientTest, DeleteOriginData) {
268  DatabaseQuotaClient client(base::MessageLoopProxy::current().get(),
269                             mock_tracker());
270
271  // Perm deletions are short circuited in the Client and
272  // should not reach the DatabaseTracker.
273  EXPECT_TRUE(DeleteOriginData(&client, kPerm, kOriginA));
274  EXPECT_EQ(0, mock_tracker()->delete_called_count());
275
276  mock_tracker()->set_async_delete(false);
277  EXPECT_TRUE(DeleteOriginData(&client, kTemp, kOriginA));
278  EXPECT_EQ(1, mock_tracker()->delete_called_count());
279
280  mock_tracker()->set_async_delete(true);
281  EXPECT_TRUE(DeleteOriginData(&client, kTemp, kOriginA));
282  EXPECT_EQ(2, mock_tracker()->delete_called_count());
283}
284
285}  // namespace webkit_database
286