1// Copyright 2014 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 <set>
6
7#include "base/test/test_simple_task_runner.h"
8#include "content/browser/indexed_db/indexed_db_active_blob_registry.h"
9#include "content/browser/indexed_db/indexed_db_backing_store.h"
10#include "content/browser/indexed_db/indexed_db_fake_backing_store.h"
11#include "content/browser/indexed_db/mock_indexed_db_factory.h"
12#include "testing/gtest/include/gtest/gtest.h"
13
14namespace content {
15
16namespace {
17
18class RegistryTestMockFactory : public MockIndexedDBFactory {
19 public:
20  RegistryTestMockFactory() : duplicate_calls_(false) {}
21
22  virtual void ReportOutstandingBlobs(const GURL& origin_url,
23                                      bool blobs_outstanding) OVERRIDE {
24    if (blobs_outstanding) {
25      if (origins_.count(origin_url)) {
26        duplicate_calls_ = true;
27      } else {
28        origins_.insert(origin_url);
29      }
30    } else {
31      if (!origins_.count(origin_url)) {
32        duplicate_calls_ = true;
33      } else {
34        origins_.erase(origin_url);
35      }
36    }
37  }
38
39  bool CheckNoOriginsInUse() const {
40    return !duplicate_calls_ && !origins_.size();
41  }
42
43  bool CheckSingleOriginInUse(const GURL& origin) const {
44    return !duplicate_calls_ && origins_.size() == 1 && origins_.count(origin);
45  }
46
47 private:
48  virtual ~RegistryTestMockFactory() {}
49
50  std::set<GURL> origins_;
51  bool duplicate_calls_;
52
53  DISALLOW_COPY_AND_ASSIGN(RegistryTestMockFactory);
54};
55
56class MockIDBBackingStore : public IndexedDBFakeBackingStore {
57 public:
58  typedef std::pair<int64, int64> KeyPair;
59  typedef std::set<KeyPair> KeyPairSet;
60
61  MockIDBBackingStore(IndexedDBFactory* factory,
62                      base::SequencedTaskRunner* task_runner)
63      : IndexedDBFakeBackingStore(factory, task_runner),
64        duplicate_calls_(false) {}
65
66  virtual void ReportBlobUnused(int64 database_id, int64 blob_key) OVERRIDE {
67    unused_blobs_.insert(std::make_pair(database_id, blob_key));
68  }
69
70  bool CheckUnusedBlobsEmpty() const {
71    return !duplicate_calls_ && !unused_blobs_.size();
72  }
73  bool CheckSingleUnusedBlob(int64 database_id, int64 blob_key) const {
74    return !duplicate_calls_ && unused_blobs_.size() == 1 &&
75           unused_blobs_.count(std::make_pair(database_id, blob_key));
76  }
77
78  const KeyPairSet& unused_blobs() const { return unused_blobs_; }
79
80 protected:
81  virtual ~MockIDBBackingStore() {}
82
83 private:
84  KeyPairSet unused_blobs_;
85  bool duplicate_calls_;
86
87  DISALLOW_COPY_AND_ASSIGN(MockIDBBackingStore);
88};
89
90// Base class for our test fixtures.
91class IndexedDBActiveBlobRegistryTest : public testing::Test {
92 public:
93  typedef storage::ShareableFileReference::FinalReleaseCallback
94      ReleaseCallback;
95
96  static const int64 kDatabaseId0 = 7;
97  static const int64 kDatabaseId1 = 12;
98  static const int64 kBlobKey0 = 77;
99  static const int64 kBlobKey1 = 14;
100
101  IndexedDBActiveBlobRegistryTest()
102      : task_runner_(new base::TestSimpleTaskRunner),
103        factory_(new RegistryTestMockFactory),
104        backing_store_(
105            new MockIDBBackingStore(factory_.get(), task_runner_.get())),
106        registry_(new IndexedDBActiveBlobRegistry(backing_store_.get())) {}
107
108  void RunUntilIdle() { task_runner_->RunUntilIdle(); }
109  RegistryTestMockFactory* factory() const { return factory_.get(); }
110  MockIDBBackingStore* backing_store() const { return backing_store_.get(); }
111  IndexedDBActiveBlobRegistry* registry() const { return registry_.get(); }
112
113 private:
114  scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
115  scoped_refptr<RegistryTestMockFactory> factory_;
116  scoped_refptr<MockIDBBackingStore> backing_store_;
117  scoped_ptr<IndexedDBActiveBlobRegistry> registry_;
118
119  DISALLOW_COPY_AND_ASSIGN(IndexedDBActiveBlobRegistryTest);
120};
121
122TEST_F(IndexedDBActiveBlobRegistryTest, DeleteUnused) {
123  EXPECT_TRUE(factory()->CheckNoOriginsInUse());
124  EXPECT_TRUE(backing_store()->CheckUnusedBlobsEmpty());
125
126  EXPECT_FALSE(registry()->MarkDeletedCheckIfUsed(kDatabaseId0, kBlobKey0));
127  RunUntilIdle();
128
129  EXPECT_TRUE(factory()->CheckNoOriginsInUse());
130  EXPECT_TRUE(backing_store()->CheckUnusedBlobsEmpty());
131}
132
133TEST_F(IndexedDBActiveBlobRegistryTest, SimpleUse) {
134  EXPECT_TRUE(factory()->CheckNoOriginsInUse());
135  EXPECT_TRUE(backing_store()->CheckUnusedBlobsEmpty());
136
137  base::Closure add_ref =
138      registry()->GetAddBlobRefCallback(kDatabaseId0, kBlobKey0);
139  ReleaseCallback release =
140      registry()->GetFinalReleaseCallback(kDatabaseId0, kBlobKey0);
141  add_ref.Run();
142  RunUntilIdle();
143
144  EXPECT_TRUE(factory()->CheckSingleOriginInUse(backing_store()->origin_url()));
145  EXPECT_TRUE(backing_store()->CheckUnusedBlobsEmpty());
146
147  release.Run(base::FilePath());
148  RunUntilIdle();
149
150  EXPECT_TRUE(factory()->CheckNoOriginsInUse());
151  EXPECT_TRUE(backing_store()->CheckUnusedBlobsEmpty());
152}
153
154TEST_F(IndexedDBActiveBlobRegistryTest, DeleteWhileInUse) {
155  EXPECT_TRUE(factory()->CheckNoOriginsInUse());
156  EXPECT_TRUE(backing_store()->CheckUnusedBlobsEmpty());
157
158  base::Closure add_ref =
159      registry()->GetAddBlobRefCallback(kDatabaseId0, kBlobKey0);
160  ReleaseCallback release =
161      registry()->GetFinalReleaseCallback(kDatabaseId0, kBlobKey0);
162
163  add_ref.Run();
164  RunUntilIdle();
165
166  EXPECT_TRUE(factory()->CheckSingleOriginInUse(backing_store()->origin_url()));
167  EXPECT_TRUE(backing_store()->CheckUnusedBlobsEmpty());
168
169  EXPECT_TRUE(registry()->MarkDeletedCheckIfUsed(kDatabaseId0, kBlobKey0));
170  RunUntilIdle();
171
172  EXPECT_TRUE(factory()->CheckSingleOriginInUse(backing_store()->origin_url()));
173  EXPECT_TRUE(backing_store()->CheckUnusedBlobsEmpty());
174
175  release.Run(base::FilePath());
176  RunUntilIdle();
177
178  EXPECT_TRUE(factory()->CheckNoOriginsInUse());
179  EXPECT_TRUE(backing_store()->CheckSingleUnusedBlob(kDatabaseId0, kBlobKey0));
180}
181
182TEST_F(IndexedDBActiveBlobRegistryTest, MultipleBlobs) {
183  EXPECT_TRUE(factory()->CheckNoOriginsInUse());
184  EXPECT_TRUE(backing_store()->CheckUnusedBlobsEmpty());
185
186  base::Closure add_ref_00 =
187      registry()->GetAddBlobRefCallback(kDatabaseId0, kBlobKey0);
188  ReleaseCallback release_00 =
189      registry()->GetFinalReleaseCallback(kDatabaseId0, kBlobKey0);
190  base::Closure add_ref_01 =
191      registry()->GetAddBlobRefCallback(kDatabaseId0, kBlobKey1);
192  ReleaseCallback release_01 =
193      registry()->GetFinalReleaseCallback(kDatabaseId0, kBlobKey1);
194  base::Closure add_ref_10 =
195      registry()->GetAddBlobRefCallback(kDatabaseId1, kBlobKey0);
196  ReleaseCallback release_10 =
197      registry()->GetFinalReleaseCallback(kDatabaseId1, kBlobKey0);
198  base::Closure add_ref_11 =
199      registry()->GetAddBlobRefCallback(kDatabaseId1, kBlobKey1);
200  ReleaseCallback release_11 =
201      registry()->GetFinalReleaseCallback(kDatabaseId1, kBlobKey1);
202
203  add_ref_00.Run();
204  add_ref_01.Run();
205  RunUntilIdle();
206
207  EXPECT_TRUE(factory()->CheckSingleOriginInUse(backing_store()->origin_url()));
208  EXPECT_TRUE(backing_store()->CheckUnusedBlobsEmpty());
209
210  release_00.Run(base::FilePath());
211  add_ref_10.Run();
212  add_ref_11.Run();
213  RunUntilIdle();
214
215  EXPECT_TRUE(factory()->CheckSingleOriginInUse(backing_store()->origin_url()));
216  EXPECT_TRUE(backing_store()->CheckUnusedBlobsEmpty());
217
218  EXPECT_TRUE(registry()->MarkDeletedCheckIfUsed(kDatabaseId0, kBlobKey1));
219  RunUntilIdle();
220
221  EXPECT_TRUE(factory()->CheckSingleOriginInUse(backing_store()->origin_url()));
222  EXPECT_TRUE(backing_store()->CheckUnusedBlobsEmpty());
223
224  release_01.Run(base::FilePath());
225  release_11.Run(base::FilePath());
226  RunUntilIdle();
227
228  EXPECT_TRUE(factory()->CheckSingleOriginInUse(backing_store()->origin_url()));
229  EXPECT_TRUE(backing_store()->CheckSingleUnusedBlob(kDatabaseId0, kBlobKey1));
230
231  release_10.Run(base::FilePath());
232  RunUntilIdle();
233
234  EXPECT_TRUE(factory()->CheckNoOriginsInUse());
235  EXPECT_TRUE(backing_store()->CheckSingleUnusedBlob(kDatabaseId0, kBlobKey1));
236}
237
238TEST_F(IndexedDBActiveBlobRegistryTest, ForceShutdown) {
239  EXPECT_TRUE(factory()->CheckNoOriginsInUse());
240  EXPECT_TRUE(backing_store()->CheckUnusedBlobsEmpty());
241
242  base::Closure add_ref_0 =
243      registry()->GetAddBlobRefCallback(kDatabaseId0, kBlobKey0);
244  ReleaseCallback release_0 =
245      registry()->GetFinalReleaseCallback(kDatabaseId0, kBlobKey0);
246  base::Closure add_ref_1 =
247      registry()->GetAddBlobRefCallback(kDatabaseId0, kBlobKey1);
248  ReleaseCallback release_1 =
249      registry()->GetFinalReleaseCallback(kDatabaseId0, kBlobKey1);
250
251  add_ref_0.Run();
252  RunUntilIdle();
253
254  EXPECT_TRUE(factory()->CheckSingleOriginInUse(backing_store()->origin_url()));
255  EXPECT_TRUE(backing_store()->CheckUnusedBlobsEmpty());
256
257  registry()->ForceShutdown();
258
259  add_ref_1.Run();
260  RunUntilIdle();
261
262  // Nothing changes.
263  EXPECT_TRUE(factory()->CheckSingleOriginInUse(backing_store()->origin_url()));
264  EXPECT_TRUE(backing_store()->CheckUnusedBlobsEmpty());
265
266  release_0.Run(base::FilePath());
267  release_1.Run(base::FilePath());
268  RunUntilIdle();
269
270  // Nothing changes.
271  EXPECT_TRUE(factory()->CheckSingleOriginInUse(backing_store()->origin_url()));
272  EXPECT_TRUE(backing_store()->CheckUnusedBlobsEmpty());
273}
274
275}  // namespace
276
277}  // namespace content
278