1 // Copyright 2013 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/memory/scoped_ptr.h"
6 #include "base/single_thread_task_runner.h"
7 #include "base/thread_task_runner_handle.h"
8 #include "base/values.h"
9 #include "content/child/indexed_db/indexed_db_dispatcher.h"
10 #include "content/child/indexed_db/webidbcursor_impl.h"
11 #include "content/child/thread_safe_sender.h"
12 #include "content/common/indexed_db/indexed_db_key.h"
13 #include "content/common/indexed_db/indexed_db_key_range.h"
14 #include "content/common/indexed_db/indexed_db_messages.h"
15 #include "ipc/ipc_sync_message_filter.h"
16 #include "testing/gtest/include/gtest/gtest.h"
17 #include "third_party/WebKit/public/platform/WebBlobInfo.h"
18 #include "third_party/WebKit/public/platform/WebData.h"
19 #include "third_party/WebKit/public/platform/modules/indexeddb/WebIDBCallbacks.h"
20 #include "third_party/WebKit/public/web/WebHeap.h"
22 using blink::WebBlobInfo
;
24 using blink::WebIDBCallbacks
;
25 using blink::WebIDBCursor
;
26 using blink::WebIDBDatabase
;
27 using blink::WebIDBDatabaseError
;
28 using blink::WebIDBKey
;
29 using blink::WebIDBValue
;
30 using blink::WebVector
;
35 class MockCallbacks
: public WebIDBCallbacks
{
37 MockCallbacks() : error_seen_(false) {}
39 virtual void onError(const WebIDBDatabaseError
&) { error_seen_
= true; }
41 bool error_seen() const { return error_seen_
; }
46 DISALLOW_COPY_AND_ASSIGN(MockCallbacks
);
49 class MockDispatcher
: public IndexedDBDispatcher
{
51 explicit MockDispatcher(ThreadSafeSender
* sender
)
52 : IndexedDBDispatcher(sender
) {}
54 bool Send(IPC::Message
* msg
) override
{
60 DISALLOW_COPY_AND_ASSIGN(MockDispatcher
);
63 class MockSyncMessageFilter
: public IPC::SyncMessageFilter
{
65 MockSyncMessageFilter()
66 : SyncMessageFilter(nullptr, false /* is_channel_send_thread_safe */) {}
69 ~MockSyncMessageFilter() override
{}
74 class IndexedDBDispatcherTest
: public testing::Test
{
76 IndexedDBDispatcherTest()
77 : thread_safe_sender_(new ThreadSafeSender(
78 base::ThreadTaskRunnerHandle::Get(), new MockSyncMessageFilter
)) {}
80 void TearDown() override
{ blink::WebHeap::collectAllGarbageForTesting(); }
83 base::MessageLoop message_loop_
;
84 scoped_refptr
<ThreadSafeSender
> thread_safe_sender_
;
87 DISALLOW_COPY_AND_ASSIGN(IndexedDBDispatcherTest
);
90 TEST_F(IndexedDBDispatcherTest
, ValueSizeTest
) {
91 // For testing use a much smaller maximum size to prevent allocating >100 MB
92 // of memory, which crashes on memory-constrained systems.
93 const size_t kMaxValueSizeForTesting
= 10 * 1024 * 1024; // 10 MB
95 const std::vector
<char> data(kMaxValueSizeForTesting
+ 1);
96 const WebData
value(&data
.front(), data
.size());
97 const WebVector
<WebBlobInfo
> web_blob_info
;
98 const int32 ipc_dummy_id
= -1;
99 const int64 transaction_id
= 1;
100 const int64 object_store_id
= 2;
102 MockCallbacks callbacks
;
103 IndexedDBDispatcher
dispatcher(thread_safe_sender_
.get());
104 dispatcher
.max_put_value_size_
= kMaxValueSizeForTesting
;
105 IndexedDBKey
key(0, blink::WebIDBKeyTypeNumber
);
106 dispatcher
.RequestIDBDatabasePut(ipc_dummy_id
,
112 blink::WebIDBPutModeAddOrUpdate
,
114 WebVector
<long long>(),
115 WebVector
<WebVector
<WebIDBKey
> >());
117 EXPECT_TRUE(callbacks
.error_seen());
120 TEST_F(IndexedDBDispatcherTest
, KeyAndValueSizeTest
) {
121 // For testing use a much smaller maximum size to prevent allocating >100 MB
122 // of memory, which crashes on memory-constrained systems.
123 const size_t kMaxValueSizeForTesting
= 10 * 1024 * 1024; // 10 MB
124 const size_t kKeySize
= 1024 * 1024;
126 const std::vector
<char> data(kMaxValueSizeForTesting
- kKeySize
);
127 const WebData
value(&data
.front(), data
.size());
128 const WebVector
<WebBlobInfo
> web_blob_info
;
129 const IndexedDBKey
key(
130 base::string16(kKeySize
/ sizeof(base::string16::value_type
), 'x'));
132 const int32 ipc_dummy_id
= -1;
133 const int64 transaction_id
= 1;
134 const int64 object_store_id
= 2;
136 MockCallbacks callbacks
;
137 IndexedDBDispatcher
dispatcher(thread_safe_sender_
.get());
138 dispatcher
.max_put_value_size_
= kMaxValueSizeForTesting
;
139 dispatcher
.RequestIDBDatabasePut(ipc_dummy_id
,
145 blink::WebIDBPutModeAddOrUpdate
,
147 WebVector
<long long>(),
148 WebVector
<WebVector
<WebIDBKey
> >());
150 EXPECT_TRUE(callbacks
.error_seen());
155 class CursorCallbacks
: public WebIDBCallbacks
{
157 explicit CursorCallbacks(scoped_ptr
<WebIDBCursor
>* cursor
)
160 void onSuccess(const WebIDBValue
&) override
{}
161 void onSuccess(WebIDBCursor
* cursor
,
162 const WebIDBKey
& key
,
163 const WebIDBKey
& primaryKey
,
164 const WebData
& value
,
165 const WebVector
<WebBlobInfo
>&) override
{
166 cursor_
->reset(cursor
);
170 scoped_ptr
<WebIDBCursor
>* cursor_
;
172 DISALLOW_COPY_AND_ASSIGN(CursorCallbacks
);
177 TEST_F(IndexedDBDispatcherTest
, CursorTransactionId
) {
178 const int32 ipc_database_id
= -1;
179 const int64 transaction_id
= 1234;
180 const int64 object_store_id
= 2;
181 const int32 index_id
= 3;
182 const blink::WebIDBCursorDirection direction
=
183 blink::WebIDBCursorDirectionNext
;
184 const bool key_only
= false;
186 MockDispatcher
dispatcher(thread_safe_sender_
.get());
188 // First case: successful cursor open.
190 scoped_ptr
<WebIDBCursor
> cursor
;
191 EXPECT_EQ(0UL, dispatcher
.cursor_transaction_ids_
.size());
193 // Make a cursor request. This should record the transaction id.
194 dispatcher
.RequestIDBDatabaseOpenCursor(ipc_database_id
,
201 blink::WebIDBTaskTypeNormal
,
202 new CursorCallbacks(&cursor
));
204 // Verify that the transaction id was captured.
205 EXPECT_EQ(1UL, dispatcher
.cursor_transaction_ids_
.size());
206 EXPECT_FALSE(cursor
.get());
208 int32 ipc_callbacks_id
= dispatcher
.cursor_transaction_ids_
.begin()->first
;
210 IndexedDBMsg_CallbacksSuccessIDBCursor_Params params
;
211 params
.ipc_thread_id
= dispatcher
.CurrentWorkerId();
212 params
.ipc_callbacks_id
= ipc_callbacks_id
;
214 // Now simululate the cursor response.
215 params
.ipc_cursor_id
= WebIDBCursorImpl::kInvalidCursorId
;
216 dispatcher
.OnSuccessOpenCursor(params
);
218 EXPECT_EQ(0UL, dispatcher
.cursor_transaction_ids_
.size());
220 EXPECT_TRUE(cursor
.get());
222 WebIDBCursorImpl
* impl
= static_cast<WebIDBCursorImpl
*>(cursor
.get());
224 // This is the primary expectation of this test: the transaction id was
225 // applied to the cursor.
226 EXPECT_EQ(transaction_id
, impl
->transaction_id());
229 // Second case: null cursor (no data in range)
231 scoped_ptr
<WebIDBCursor
> cursor
;
232 EXPECT_EQ(0UL, dispatcher
.cursor_transaction_ids_
.size());
234 // Make a cursor request. This should record the transaction id.
235 dispatcher
.RequestIDBDatabaseOpenCursor(ipc_database_id
,
242 blink::WebIDBTaskTypeNormal
,
243 new CursorCallbacks(&cursor
));
245 // Verify that the transaction id was captured.
246 EXPECT_EQ(1UL, dispatcher
.cursor_transaction_ids_
.size());
247 EXPECT_FALSE(cursor
.get());
249 int32 ipc_callbacks_id
= dispatcher
.cursor_transaction_ids_
.begin()->first
;
251 // Now simululate a "null cursor" response.
252 IndexedDBMsg_CallbacksSuccessValue_Params params
;
253 params
.ipc_thread_id
= dispatcher
.CurrentWorkerId();
254 params
.ipc_callbacks_id
= ipc_callbacks_id
;
255 dispatcher
.OnSuccessValue(params
);
257 // Ensure the map result was deleted.
258 EXPECT_EQ(0UL, dispatcher
.cursor_transaction_ids_
.size());
259 EXPECT_FALSE(cursor
.get());
265 class MockCursor
: public WebIDBCursorImpl
{
267 MockCursor(int32 ipc_cursor_id
,
268 int64 transaction_id
,
269 ThreadSafeSender
* thread_safe_sender
)
270 : WebIDBCursorImpl(ipc_cursor_id
, transaction_id
, thread_safe_sender
),
273 // This method is virtual so it can be overridden in unit tests.
274 void ResetPrefetchCache() override
{ ++reset_count_
; }
276 int reset_count() const { return reset_count_
; }
281 DISALLOW_COPY_AND_ASSIGN(MockCursor
);
286 TEST_F(IndexedDBDispatcherTest
, CursorReset
) {
287 scoped_ptr
<WebIDBCursor
> cursor
;
288 MockDispatcher
dispatcher(thread_safe_sender_
.get());
290 const int32 ipc_database_id
= 0;
291 const int32 object_store_id
= 0;
292 const int32 index_id
= 0;
293 const bool key_only
= false;
294 const int cursor1_ipc_id
= 1;
295 const int cursor2_ipc_id
= 2;
296 const int other_cursor_ipc_id
= 2;
297 const int cursor1_transaction_id
= 1;
298 const int cursor2_transaction_id
= 2;
299 const int other_transaction_id
= 3;
301 scoped_ptr
<MockCursor
> cursor1(
302 new MockCursor(WebIDBCursorImpl::kInvalidCursorId
,
303 cursor1_transaction_id
,
304 thread_safe_sender_
.get()));
306 scoped_ptr
<MockCursor
> cursor2(
307 new MockCursor(WebIDBCursorImpl::kInvalidCursorId
,
308 cursor2_transaction_id
,
309 thread_safe_sender_
.get()));
311 dispatcher
.cursors_
[cursor1_ipc_id
] = cursor1
.get();
312 dispatcher
.cursors_
[cursor2_ipc_id
] = cursor2
.get();
314 EXPECT_EQ(0, cursor1
->reset_count());
315 EXPECT_EQ(0, cursor2
->reset_count());
317 // Other transaction:
318 dispatcher
.RequestIDBDatabaseGet(ipc_database_id
,
319 other_transaction_id
,
324 new MockCallbacks());
326 EXPECT_EQ(0, cursor1
->reset_count());
327 EXPECT_EQ(0, cursor2
->reset_count());
330 dispatcher
.RequestIDBDatabaseGet(ipc_database_id
,
331 cursor1_transaction_id
,
336 new MockCallbacks());
338 EXPECT_EQ(1, cursor1
->reset_count());
339 EXPECT_EQ(0, cursor2
->reset_count());
341 // Same transaction and same cursor:
342 dispatcher
.RequestIDBCursorContinue(IndexedDBKey(),
346 cursor1_transaction_id
);
348 EXPECT_EQ(1, cursor1
->reset_count());
349 EXPECT_EQ(0, cursor2
->reset_count());
351 // Same transaction and different cursor:
352 dispatcher
.RequestIDBCursorContinue(IndexedDBKey(),
356 cursor1_transaction_id
);
358 EXPECT_EQ(2, cursor1
->reset_count());
359 EXPECT_EQ(0, cursor2
->reset_count());
365 } // namespace content