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 const std::vector
<char> data(kMaxIDBValueSizeInBytes
+ 1);
92 const WebData
value(&data
.front(), data
.size());
93 const WebVector
<WebBlobInfo
> web_blob_info
;
94 const int32 ipc_dummy_id
= -1;
95 const int64 transaction_id
= 1;
96 const int64 object_store_id
= 2;
98 MockCallbacks callbacks
;
99 IndexedDBDispatcher
dispatcher(thread_safe_sender_
.get());
100 IndexedDBKey
key(0, blink::WebIDBKeyTypeNumber
);
101 dispatcher
.RequestIDBDatabasePut(ipc_dummy_id
,
107 blink::WebIDBPutModeAddOrUpdate
,
109 WebVector
<long long>(),
110 WebVector
<WebVector
<WebIDBKey
> >());
112 EXPECT_TRUE(callbacks
.error_seen());
115 TEST_F(IndexedDBDispatcherTest
, KeyAndValueSizeTest
) {
116 const size_t kKeySize
= 1024 * 1024;
118 const std::vector
<char> data(kMaxIDBValueSizeInBytes
- kKeySize
);
119 const WebData
value(&data
.front(), data
.size());
120 const WebVector
<WebBlobInfo
> web_blob_info
;
121 const IndexedDBKey
key(
122 base::string16(kKeySize
/ sizeof(base::string16::value_type
), 'x'));
124 const int32 ipc_dummy_id
= -1;
125 const int64 transaction_id
= 1;
126 const int64 object_store_id
= 2;
128 MockCallbacks callbacks
;
129 IndexedDBDispatcher
dispatcher(thread_safe_sender_
.get());
130 dispatcher
.RequestIDBDatabasePut(ipc_dummy_id
,
136 blink::WebIDBPutModeAddOrUpdate
,
138 WebVector
<long long>(),
139 WebVector
<WebVector
<WebIDBKey
> >());
141 EXPECT_TRUE(callbacks
.error_seen());
146 class CursorCallbacks
: public WebIDBCallbacks
{
148 explicit CursorCallbacks(scoped_ptr
<WebIDBCursor
>* cursor
)
151 void onSuccess(const WebIDBValue
&) override
{}
152 void onSuccess(WebIDBCursor
* cursor
,
153 const WebIDBKey
& key
,
154 const WebIDBKey
& primaryKey
,
155 const WebData
& value
,
156 const WebVector
<WebBlobInfo
>&) override
{
157 cursor_
->reset(cursor
);
161 scoped_ptr
<WebIDBCursor
>* cursor_
;
163 DISALLOW_COPY_AND_ASSIGN(CursorCallbacks
);
168 TEST_F(IndexedDBDispatcherTest
, CursorTransactionId
) {
169 const int32 ipc_database_id
= -1;
170 const int64 transaction_id
= 1234;
171 const int64 object_store_id
= 2;
172 const int32 index_id
= 3;
173 const blink::WebIDBCursorDirection direction
=
174 blink::WebIDBCursorDirectionNext
;
175 const bool key_only
= false;
177 MockDispatcher
dispatcher(thread_safe_sender_
.get());
179 // First case: successful cursor open.
181 scoped_ptr
<WebIDBCursor
> cursor
;
182 EXPECT_EQ(0UL, dispatcher
.cursor_transaction_ids_
.size());
184 // Make a cursor request. This should record the transaction id.
185 dispatcher
.RequestIDBDatabaseOpenCursor(ipc_database_id
,
192 blink::WebIDBTaskTypeNormal
,
193 new CursorCallbacks(&cursor
));
195 // Verify that the transaction id was captured.
196 EXPECT_EQ(1UL, dispatcher
.cursor_transaction_ids_
.size());
197 EXPECT_FALSE(cursor
.get());
199 int32 ipc_callbacks_id
= dispatcher
.cursor_transaction_ids_
.begin()->first
;
201 IndexedDBMsg_CallbacksSuccessIDBCursor_Params params
;
202 params
.ipc_thread_id
= dispatcher
.CurrentWorkerId();
203 params
.ipc_callbacks_id
= ipc_callbacks_id
;
205 // Now simululate the cursor response.
206 params
.ipc_cursor_id
= WebIDBCursorImpl::kInvalidCursorId
;
207 dispatcher
.OnSuccessOpenCursor(params
);
209 EXPECT_EQ(0UL, dispatcher
.cursor_transaction_ids_
.size());
211 EXPECT_TRUE(cursor
.get());
213 WebIDBCursorImpl
* impl
= static_cast<WebIDBCursorImpl
*>(cursor
.get());
215 // This is the primary expectation of this test: the transaction id was
216 // applied to the cursor.
217 EXPECT_EQ(transaction_id
, impl
->transaction_id());
220 // Second case: null cursor (no data in range)
222 scoped_ptr
<WebIDBCursor
> cursor
;
223 EXPECT_EQ(0UL, dispatcher
.cursor_transaction_ids_
.size());
225 // Make a cursor request. This should record the transaction id.
226 dispatcher
.RequestIDBDatabaseOpenCursor(ipc_database_id
,
233 blink::WebIDBTaskTypeNormal
,
234 new CursorCallbacks(&cursor
));
236 // Verify that the transaction id was captured.
237 EXPECT_EQ(1UL, dispatcher
.cursor_transaction_ids_
.size());
238 EXPECT_FALSE(cursor
.get());
240 int32 ipc_callbacks_id
= dispatcher
.cursor_transaction_ids_
.begin()->first
;
242 // Now simululate a "null cursor" response.
243 IndexedDBMsg_CallbacksSuccessValue_Params params
;
244 params
.ipc_thread_id
= dispatcher
.CurrentWorkerId();
245 params
.ipc_callbacks_id
= ipc_callbacks_id
;
246 dispatcher
.OnSuccessValue(params
);
248 // Ensure the map result was deleted.
249 EXPECT_EQ(0UL, dispatcher
.cursor_transaction_ids_
.size());
250 EXPECT_FALSE(cursor
.get());
256 class MockCursor
: public WebIDBCursorImpl
{
258 MockCursor(int32 ipc_cursor_id
,
259 int64 transaction_id
,
260 ThreadSafeSender
* thread_safe_sender
)
261 : WebIDBCursorImpl(ipc_cursor_id
, transaction_id
, thread_safe_sender
),
264 // This method is virtual so it can be overridden in unit tests.
265 void ResetPrefetchCache() override
{ ++reset_count_
; }
267 int reset_count() const { return reset_count_
; }
272 DISALLOW_COPY_AND_ASSIGN(MockCursor
);
277 TEST_F(IndexedDBDispatcherTest
, CursorReset
) {
278 scoped_ptr
<WebIDBCursor
> cursor
;
279 MockDispatcher
dispatcher(thread_safe_sender_
.get());
281 const int32 ipc_database_id
= 0;
282 const int32 object_store_id
= 0;
283 const int32 index_id
= 0;
284 const bool key_only
= false;
285 const int cursor1_ipc_id
= 1;
286 const int cursor2_ipc_id
= 2;
287 const int other_cursor_ipc_id
= 2;
288 const int cursor1_transaction_id
= 1;
289 const int cursor2_transaction_id
= 2;
290 const int other_transaction_id
= 3;
292 scoped_ptr
<MockCursor
> cursor1(
293 new MockCursor(WebIDBCursorImpl::kInvalidCursorId
,
294 cursor1_transaction_id
,
295 thread_safe_sender_
.get()));
297 scoped_ptr
<MockCursor
> cursor2(
298 new MockCursor(WebIDBCursorImpl::kInvalidCursorId
,
299 cursor2_transaction_id
,
300 thread_safe_sender_
.get()));
302 dispatcher
.cursors_
[cursor1_ipc_id
] = cursor1
.get();
303 dispatcher
.cursors_
[cursor2_ipc_id
] = cursor2
.get();
305 EXPECT_EQ(0, cursor1
->reset_count());
306 EXPECT_EQ(0, cursor2
->reset_count());
308 // Other transaction:
309 dispatcher
.RequestIDBDatabaseGet(ipc_database_id
,
310 other_transaction_id
,
315 new MockCallbacks());
317 EXPECT_EQ(0, cursor1
->reset_count());
318 EXPECT_EQ(0, cursor2
->reset_count());
321 dispatcher
.RequestIDBDatabaseGet(ipc_database_id
,
322 cursor1_transaction_id
,
327 new MockCallbacks());
329 EXPECT_EQ(1, cursor1
->reset_count());
330 EXPECT_EQ(0, cursor2
->reset_count());
332 // Same transaction and same cursor:
333 dispatcher
.RequestIDBCursorContinue(IndexedDBKey(),
337 cursor1_transaction_id
);
339 EXPECT_EQ(1, cursor1
->reset_count());
340 EXPECT_EQ(0, cursor2
->reset_count());
342 // Same transaction and different cursor:
343 dispatcher
.RequestIDBCursorContinue(IndexedDBKey(),
347 cursor1_transaction_id
);
349 EXPECT_EQ(2, cursor1
->reset_count());
350 EXPECT_EQ(0, cursor2
->reset_count());
356 } // namespace content