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.
5 #include "base/file_util.h"
6 #include "base/files/scoped_temp_dir.h"
7 #include "base/test/test_simple_task_runner.h"
8 #include "base/threading/thread.h"
9 #include "content/browser/browser_thread_impl.h"
10 #include "content/browser/indexed_db/indexed_db_connection.h"
11 #include "content/browser/indexed_db/indexed_db_context_impl.h"
12 #include "content/browser/indexed_db/indexed_db_factory_impl.h"
13 #include "content/browser/indexed_db/mock_indexed_db_callbacks.h"
14 #include "content/browser/indexed_db/mock_indexed_db_database_callbacks.h"
15 #include "content/public/browser/storage_partition.h"
16 #include "content/public/common/url_constants.h"
17 #include "content/public/test/mock_special_storage_policy.h"
18 #include "content/public/test/test_browser_context.h"
19 #include "testing/gtest/include/gtest/gtest.h"
20 #include "webkit/browser/quota/quota_manager.h"
21 #include "webkit/browser/quota/special_storage_policy.h"
22 #include "webkit/common/database/database_identifier.h"
26 class IndexedDBTest
: public testing::Test
{
28 const GURL kNormalOrigin
;
29 const GURL kSessionOnlyOrigin
;
32 : kNormalOrigin("http://normal/"),
33 kSessionOnlyOrigin("http://session-only/"),
34 task_runner_(new base::TestSimpleTaskRunner
),
35 special_storage_policy_(new MockSpecialStoragePolicy
),
36 file_thread_(BrowserThread::FILE_USER_BLOCKING
, &message_loop_
),
37 io_thread_(BrowserThread::IO
, &message_loop_
) {
38 special_storage_policy_
->AddSessionOnly(kSessionOnlyOrigin
);
42 void FlushIndexedDBTaskRunner() { task_runner_
->RunUntilIdle(); }
44 base::MessageLoopForIO message_loop_
;
45 scoped_refptr
<base::TestSimpleTaskRunner
> task_runner_
;
46 scoped_refptr
<MockSpecialStoragePolicy
> special_storage_policy_
;
49 BrowserThreadImpl file_thread_
;
50 BrowserThreadImpl io_thread_
;
52 DISALLOW_COPY_AND_ASSIGN(IndexedDBTest
);
55 TEST_F(IndexedDBTest
, ClearSessionOnlyDatabases
) {
56 base::ScopedTempDir temp_dir
;
57 ASSERT_TRUE(temp_dir
.CreateUniqueTempDir());
59 base::FilePath normal_path
;
60 base::FilePath session_only_path
;
62 // Create the scope which will ensure we run the destructor of the context
63 // which should trigger the clean up.
65 scoped_refptr
<IndexedDBContextImpl
> idb_context
=
66 new IndexedDBContextImpl(temp_dir
.path(),
67 special_storage_policy_
.get(),
71 normal_path
= idb_context
->GetFilePathForTesting(
72 storage::GetIdentifierFromOrigin(kNormalOrigin
));
73 session_only_path
= idb_context
->GetFilePathForTesting(
74 storage::GetIdentifierFromOrigin(kSessionOnlyOrigin
));
75 ASSERT_TRUE(base::CreateDirectory(normal_path
));
76 ASSERT_TRUE(base::CreateDirectory(session_only_path
));
77 FlushIndexedDBTaskRunner();
78 message_loop_
.RunUntilIdle();
81 FlushIndexedDBTaskRunner();
82 message_loop_
.RunUntilIdle();
84 EXPECT_TRUE(base::DirectoryExists(normal_path
));
85 EXPECT_FALSE(base::DirectoryExists(session_only_path
));
88 TEST_F(IndexedDBTest
, SetForceKeepSessionState
) {
89 base::ScopedTempDir temp_dir
;
90 ASSERT_TRUE(temp_dir
.CreateUniqueTempDir());
92 base::FilePath normal_path
;
93 base::FilePath session_only_path
;
95 // Create the scope which will ensure we run the destructor of the context.
97 // Create some indexedDB paths.
98 // With the levelDB backend, these are directories.
99 scoped_refptr
<IndexedDBContextImpl
> idb_context
=
100 new IndexedDBContextImpl(temp_dir
.path(),
101 special_storage_policy_
.get(),
105 // Save session state. This should bypass the destruction-time deletion.
106 idb_context
->SetForceKeepSessionState();
108 normal_path
= idb_context
->GetFilePathForTesting(
109 storage::GetIdentifierFromOrigin(kNormalOrigin
));
110 session_only_path
= idb_context
->GetFilePathForTesting(
111 storage::GetIdentifierFromOrigin(kSessionOnlyOrigin
));
112 ASSERT_TRUE(base::CreateDirectory(normal_path
));
113 ASSERT_TRUE(base::CreateDirectory(session_only_path
));
114 message_loop_
.RunUntilIdle();
117 // Make sure we wait until the destructor has run.
118 message_loop_
.RunUntilIdle();
120 // No data was cleared because of SetForceKeepSessionState.
121 EXPECT_TRUE(base::DirectoryExists(normal_path
));
122 EXPECT_TRUE(base::DirectoryExists(session_only_path
));
125 class ForceCloseDBCallbacks
: public IndexedDBCallbacks
{
127 ForceCloseDBCallbacks(scoped_refptr
<IndexedDBContextImpl
> idb_context
,
128 const GURL
& origin_url
)
129 : IndexedDBCallbacks(NULL
, 0, 0),
130 idb_context_(idb_context
),
131 origin_url_(origin_url
) {}
133 virtual void OnSuccess() OVERRIDE
{}
134 virtual void OnSuccess(const std::vector
<base::string16
>&) OVERRIDE
{}
135 virtual void OnSuccess(scoped_ptr
<IndexedDBConnection
> connection
,
136 const IndexedDBDatabaseMetadata
& metadata
) OVERRIDE
{
137 connection_
= connection
.Pass();
138 idb_context_
->ConnectionOpened(origin_url_
, connection_
.get());
141 IndexedDBConnection
* connection() { return connection_
.get(); }
144 virtual ~ForceCloseDBCallbacks() {}
147 scoped_refptr
<IndexedDBContextImpl
> idb_context_
;
149 scoped_ptr
<IndexedDBConnection
> connection_
;
150 DISALLOW_COPY_AND_ASSIGN(ForceCloseDBCallbacks
);
153 TEST_F(IndexedDBTest
, ForceCloseOpenDatabasesOnDelete
) {
154 base::ScopedTempDir temp_dir
;
155 ASSERT_TRUE(temp_dir
.CreateUniqueTempDir());
157 scoped_refptr
<MockIndexedDBDatabaseCallbacks
> open_db_callbacks(
158 new MockIndexedDBDatabaseCallbacks());
159 scoped_refptr
<MockIndexedDBDatabaseCallbacks
> closed_db_callbacks(
160 new MockIndexedDBDatabaseCallbacks());
162 base::FilePath test_path
;
164 // Create the scope which will ensure we run the destructor of the context.
166 TestBrowserContext browser_context
;
168 const GURL
kTestOrigin("http://test/");
170 scoped_refptr
<IndexedDBContextImpl
> idb_context
=
171 new IndexedDBContextImpl(temp_dir
.path(),
172 special_storage_policy_
.get(),
176 scoped_refptr
<ForceCloseDBCallbacks
> open_callbacks
=
177 new ForceCloseDBCallbacks(idb_context
, kTestOrigin
);
179 scoped_refptr
<ForceCloseDBCallbacks
> closed_callbacks
=
180 new ForceCloseDBCallbacks(idb_context
, kTestOrigin
);
182 IndexedDBFactory
* factory
= idb_context
->GetIDBFactory();
184 test_path
= idb_context
->GetFilePathForTesting(
185 storage::GetIdentifierFromOrigin(kTestOrigin
));
187 IndexedDBPendingConnection
open_connection(open_callbacks
,
189 0 /* child_process_id */,
190 0 /* host_transaction_id */,
192 factory
->Open(base::ASCIIToUTF16("opendb"),
194 NULL
/* request_context */,
196 idb_context
->data_path());
197 IndexedDBPendingConnection
closed_connection(closed_callbacks
,
199 0 /* child_process_id */,
200 0 /* host_transaction_id */,
202 factory
->Open(base::ASCIIToUTF16("closeddb"),
204 NULL
/* request_context */,
206 idb_context
->data_path());
208 closed_callbacks
->connection()->Close();
210 idb_context
->TaskRunner()->PostTask(
213 &IndexedDBContextImpl::DeleteForOrigin
, idb_context
, kTestOrigin
));
214 FlushIndexedDBTaskRunner();
215 message_loop_
.RunUntilIdle();
218 // Make sure we wait until the destructor has run.
219 message_loop_
.RunUntilIdle();
221 EXPECT_TRUE(open_db_callbacks
->forced_close_called());
222 EXPECT_FALSE(closed_db_callbacks
->forced_close_called());
223 EXPECT_FALSE(base::DirectoryExists(test_path
));
226 TEST_F(IndexedDBTest
, DeleteFailsIfDirectoryLocked
) {
227 base::ScopedTempDir temp_dir
;
228 ASSERT_TRUE(temp_dir
.CreateUniqueTempDir());
229 const GURL
kTestOrigin("http://test/");
231 scoped_refptr
<IndexedDBContextImpl
> idb_context
= new IndexedDBContextImpl(
232 temp_dir
.path(), special_storage_policy_
.get(), NULL
, task_runner_
.get());
234 base::FilePath test_path
= idb_context
->GetFilePathForTesting(
235 storage::GetIdentifierFromOrigin(kTestOrigin
));
236 ASSERT_TRUE(base::CreateDirectory(test_path
));
238 scoped_ptr
<LevelDBLock
> lock
=
239 LevelDBDatabase::LockForTesting(test_path
);
242 idb_context
->TaskRunner()->PostTask(
245 &IndexedDBContextImpl::DeleteForOrigin
, idb_context
, kTestOrigin
));
246 FlushIndexedDBTaskRunner();
248 EXPECT_TRUE(base::DirectoryExists(test_path
));
251 TEST_F(IndexedDBTest
, ForceCloseOpenDatabasesOnCommitFailure
) {
252 const GURL
kTestOrigin("http://test/");
254 base::ScopedTempDir temp_dir
;
255 ASSERT_TRUE(temp_dir
.CreateUniqueTempDir());
257 scoped_refptr
<IndexedDBContextImpl
> context
= new IndexedDBContextImpl(
258 temp_dir
.path(), special_storage_policy_
.get(), NULL
, task_runner_
.get());
260 scoped_refptr
<IndexedDBFactoryImpl
> factory
=
261 static_cast<IndexedDBFactoryImpl
*>(context
->GetIDBFactory());
263 scoped_refptr
<MockIndexedDBCallbacks
> callbacks(new MockIndexedDBCallbacks());
264 scoped_refptr
<MockIndexedDBDatabaseCallbacks
> db_callbacks(
265 new MockIndexedDBDatabaseCallbacks());
266 const int64 transaction_id
= 1;
267 IndexedDBPendingConnection
connection(
270 0 /* child_process_id */,
272 IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION
);
273 factory
->Open(base::ASCIIToUTF16("db"),
275 NULL
/* request_context */,
279 EXPECT_TRUE(callbacks
->connection());
281 // ConnectionOpened() is usually called by the dispatcher.
282 context
->ConnectionOpened(kTestOrigin
, callbacks
->connection());
284 EXPECT_TRUE(factory
->IsBackingStoreOpen(kTestOrigin
));
286 // Simulate the write failure.
287 leveldb::Status status
= leveldb::Status::IOError("Simulated failure");
288 callbacks
->connection()->database()->TransactionCommitFailed(status
);
290 EXPECT_TRUE(db_callbacks
->forced_close_called());
291 EXPECT_FALSE(factory
->IsBackingStoreOpen(kTestOrigin
));
294 } // namespace content