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. 4 5 #include "base/memory/scoped_ptr.h" 6 #include "base/message_loop/message_loop_proxy.h" 7 #include "base/values.h" 8 #include "content/child/indexed_db/indexed_db_dispatcher.h" 9 #include "content/child/indexed_db/webidbcursor_impl.h" 10 #include "content/child/thread_safe_sender.h" 11 #include "content/common/indexed_db/indexed_db_key.h" 12 #include "content/common/indexed_db/indexed_db_key_range.h" 13 #include "content/common/indexed_db/indexed_db_messages.h" 14 #include "ipc/ipc_sync_message_filter.h" 15 #include "testing/gtest/include/gtest/gtest.h" 16 #include "third_party/WebKit/public/platform/WebBlobInfo.h" 17 #include "third_party/WebKit/public/platform/WebData.h" 18 #include "third_party/WebKit/public/platform/WebIDBCallbacks.h" 19 20 using blink::WebBlobInfo; 21 using blink::WebData; 22 using blink::WebIDBCallbacks; 23 using blink::WebIDBCursor; 24 using blink::WebIDBDatabase; 25 using blink::WebIDBDatabaseError; 26 using blink::WebIDBKey; 27 using blink::WebVector; 28 29 namespace content { 30 namespace { 31 32 class MockCallbacks : public WebIDBCallbacks { 33 public: 34 MockCallbacks() : error_seen_(false) {} 35 36 virtual void onError(const WebIDBDatabaseError&) { error_seen_ = true; } 37 38 bool error_seen() const { return error_seen_; } 39 40 private: 41 bool error_seen_; 42 43 DISALLOW_COPY_AND_ASSIGN(MockCallbacks); 44 }; 45 46 class MockDispatcher : public IndexedDBDispatcher { 47 public: 48 explicit MockDispatcher(ThreadSafeSender* sender) 49 : IndexedDBDispatcher(sender) {} 50 51 virtual bool Send(IPC::Message* msg) OVERRIDE { 52 delete msg; 53 return true; 54 } 55 56 private: 57 DISALLOW_COPY_AND_ASSIGN(MockDispatcher); 58 }; 59 60 } // namespace 61 62 class IndexedDBDispatcherTest : public testing::Test { 63 public: 64 IndexedDBDispatcherTest() 65 : message_loop_proxy_(base::MessageLoopProxy::current()), 66 sync_message_filter_(new IPC::SyncMessageFilter(NULL)), 67 thread_safe_sender_(new ThreadSafeSender(message_loop_proxy_.get(), 68 sync_message_filter_.get())) {} 69 70 protected: 71 scoped_refptr<base::MessageLoopProxy> message_loop_proxy_; 72 scoped_refptr<IPC::SyncMessageFilter> sync_message_filter_; 73 scoped_refptr<ThreadSafeSender> thread_safe_sender_; 74 75 private: 76 DISALLOW_COPY_AND_ASSIGN(IndexedDBDispatcherTest); 77 }; 78 79 TEST_F(IndexedDBDispatcherTest, ValueSizeTest) { 80 const std::vector<char> data(kMaxIDBValueSizeInBytes + 1); 81 const WebData value(&data.front(), data.size()); 82 const WebVector<WebBlobInfo> web_blob_info; 83 const int32 ipc_dummy_id = -1; 84 const int64 transaction_id = 1; 85 const int64 object_store_id = 2; 86 87 MockCallbacks callbacks; 88 IndexedDBDispatcher dispatcher(thread_safe_sender_.get()); 89 IndexedDBKey key(0, blink::WebIDBKeyTypeNumber); 90 dispatcher.RequestIDBDatabasePut(ipc_dummy_id, 91 transaction_id, 92 object_store_id, 93 value, 94 web_blob_info, 95 key, 96 blink::WebIDBPutModeAddOrUpdate, 97 &callbacks, 98 WebVector<long long>(), 99 WebVector<WebVector<WebIDBKey> >()); 100 101 EXPECT_TRUE(callbacks.error_seen()); 102 } 103 104 TEST_F(IndexedDBDispatcherTest, KeyAndValueSizeTest) { 105 const size_t kKeySize = 1024 * 1024; 106 107 const std::vector<char> data(kMaxIDBValueSizeInBytes - kKeySize); 108 const WebData value(&data.front(), data.size()); 109 const WebVector<WebBlobInfo> web_blob_info; 110 const IndexedDBKey key( 111 base::string16(kKeySize / sizeof(base::string16::value_type), 'x')); 112 113 const int32 ipc_dummy_id = -1; 114 const int64 transaction_id = 1; 115 const int64 object_store_id = 2; 116 117 MockCallbacks callbacks; 118 IndexedDBDispatcher dispatcher(thread_safe_sender_.get()); 119 dispatcher.RequestIDBDatabasePut(ipc_dummy_id, 120 transaction_id, 121 object_store_id, 122 value, 123 web_blob_info, 124 key, 125 blink::WebIDBPutModeAddOrUpdate, 126 &callbacks, 127 WebVector<long long>(), 128 WebVector<WebVector<WebIDBKey> >()); 129 130 EXPECT_TRUE(callbacks.error_seen()); 131 } 132 133 namespace { 134 135 class CursorCallbacks : public WebIDBCallbacks { 136 public: 137 explicit CursorCallbacks(scoped_ptr<WebIDBCursor>* cursor) 138 : cursor_(cursor) {} 139 140 virtual void onSuccess(const WebData&, 141 const WebVector<WebBlobInfo>&) OVERRIDE {} 142 virtual void onSuccess(WebIDBCursor* cursor, 143 const WebIDBKey& key, 144 const WebIDBKey& primaryKey, 145 const WebData& value, 146 const WebVector<WebBlobInfo>&) OVERRIDE { 147 cursor_->reset(cursor); 148 } 149 150 private: 151 scoped_ptr<WebIDBCursor>* cursor_; 152 153 DISALLOW_COPY_AND_ASSIGN(CursorCallbacks); 154 }; 155 156 } // namespace 157 158 TEST_F(IndexedDBDispatcherTest, CursorTransactionId) { 159 const int32 ipc_database_id = -1; 160 const int64 transaction_id = 1234; 161 const int64 object_store_id = 2; 162 const int32 index_id = 3; 163 const blink::WebIDBCursorDirection direction = 164 blink::WebIDBCursorDirectionNext; 165 const bool key_only = false; 166 167 MockDispatcher dispatcher(thread_safe_sender_.get()); 168 169 // First case: successful cursor open. 170 { 171 scoped_ptr<WebIDBCursor> cursor; 172 EXPECT_EQ(0UL, dispatcher.cursor_transaction_ids_.size()); 173 174 // Make a cursor request. This should record the transaction id. 175 dispatcher.RequestIDBDatabaseOpenCursor(ipc_database_id, 176 transaction_id, 177 object_store_id, 178 index_id, 179 IndexedDBKeyRange(), 180 direction, 181 key_only, 182 blink::WebIDBTaskTypeNormal, 183 new CursorCallbacks(&cursor)); 184 185 // Verify that the transaction id was captured. 186 EXPECT_EQ(1UL, dispatcher.cursor_transaction_ids_.size()); 187 EXPECT_FALSE(cursor.get()); 188 189 int32 ipc_callbacks_id = dispatcher.cursor_transaction_ids_.begin()->first; 190 191 IndexedDBMsg_CallbacksSuccessIDBCursor_Params params; 192 params.ipc_thread_id = dispatcher.CurrentWorkerId(); 193 params.ipc_callbacks_id = ipc_callbacks_id; 194 195 // Now simululate the cursor response. 196 params.ipc_cursor_id = WebIDBCursorImpl::kInvalidCursorId; 197 dispatcher.OnSuccessOpenCursor(params); 198 199 EXPECT_EQ(0UL, dispatcher.cursor_transaction_ids_.size()); 200 201 EXPECT_TRUE(cursor.get()); 202 203 WebIDBCursorImpl* impl = static_cast<WebIDBCursorImpl*>(cursor.get()); 204 205 // This is the primary expectation of this test: the transaction id was 206 // applied to the cursor. 207 EXPECT_EQ(transaction_id, impl->transaction_id()); 208 } 209 210 // Second case: null cursor (no data in range) 211 { 212 scoped_ptr<WebIDBCursor> cursor; 213 EXPECT_EQ(0UL, dispatcher.cursor_transaction_ids_.size()); 214 215 // Make a cursor request. This should record the transaction id. 216 dispatcher.RequestIDBDatabaseOpenCursor(ipc_database_id, 217 transaction_id, 218 object_store_id, 219 index_id, 220 IndexedDBKeyRange(), 221 direction, 222 key_only, 223 blink::WebIDBTaskTypeNormal, 224 new CursorCallbacks(&cursor)); 225 226 // Verify that the transaction id was captured. 227 EXPECT_EQ(1UL, dispatcher.cursor_transaction_ids_.size()); 228 EXPECT_FALSE(cursor.get()); 229 230 int32 ipc_callbacks_id = dispatcher.cursor_transaction_ids_.begin()->first; 231 232 // Now simululate a "null cursor" response. 233 IndexedDBMsg_CallbacksSuccessValue_Params params; 234 params.ipc_thread_id = dispatcher.CurrentWorkerId(); 235 params.ipc_callbacks_id = ipc_callbacks_id; 236 dispatcher.OnSuccessValue(params); 237 238 // Ensure the map result was deleted. 239 EXPECT_EQ(0UL, dispatcher.cursor_transaction_ids_.size()); 240 EXPECT_FALSE(cursor.get()); 241 } 242 } 243 244 namespace { 245 246 class MockCursor : public WebIDBCursorImpl { 247 public: 248 MockCursor(int32 ipc_cursor_id, 249 int64 transaction_id, 250 ThreadSafeSender* thread_safe_sender) 251 : WebIDBCursorImpl(ipc_cursor_id, transaction_id, thread_safe_sender), 252 reset_count_(0) {} 253 254 // This method is virtual so it can be overridden in unit tests. 255 virtual void ResetPrefetchCache() OVERRIDE { ++reset_count_; } 256 257 int reset_count() const { return reset_count_; } 258 259 private: 260 int reset_count_; 261 262 DISALLOW_COPY_AND_ASSIGN(MockCursor); 263 }; 264 265 } // namespace 266 267 TEST_F(IndexedDBDispatcherTest, CursorReset) { 268 scoped_ptr<WebIDBCursor> cursor; 269 MockDispatcher dispatcher(thread_safe_sender_.get()); 270 271 const int32 ipc_database_id = 0; 272 const int32 object_store_id = 0; 273 const int32 index_id = 0; 274 const bool key_only = false; 275 const int cursor1_ipc_id = 1; 276 const int cursor2_ipc_id = 2; 277 const int other_cursor_ipc_id = 2; 278 const int cursor1_transaction_id = 1; 279 const int cursor2_transaction_id = 2; 280 const int other_transaction_id = 3; 281 282 scoped_ptr<MockCursor> cursor1( 283 new MockCursor(WebIDBCursorImpl::kInvalidCursorId, 284 cursor1_transaction_id, 285 thread_safe_sender_.get())); 286 287 scoped_ptr<MockCursor> cursor2( 288 new MockCursor(WebIDBCursorImpl::kInvalidCursorId, 289 cursor2_transaction_id, 290 thread_safe_sender_.get())); 291 292 dispatcher.cursors_[cursor1_ipc_id] = cursor1.get(); 293 dispatcher.cursors_[cursor2_ipc_id] = cursor2.get(); 294 295 EXPECT_EQ(0, cursor1->reset_count()); 296 EXPECT_EQ(0, cursor2->reset_count()); 297 298 // Other transaction: 299 dispatcher.RequestIDBDatabaseGet(ipc_database_id, 300 other_transaction_id, 301 object_store_id, 302 index_id, 303 IndexedDBKeyRange(), 304 key_only, 305 new MockCallbacks()); 306 307 EXPECT_EQ(0, cursor1->reset_count()); 308 EXPECT_EQ(0, cursor2->reset_count()); 309 310 // Same transaction: 311 dispatcher.RequestIDBDatabaseGet(ipc_database_id, 312 cursor1_transaction_id, 313 object_store_id, 314 index_id, 315 IndexedDBKeyRange(), 316 key_only, 317 new MockCallbacks()); 318 319 EXPECT_EQ(1, cursor1->reset_count()); 320 EXPECT_EQ(0, cursor2->reset_count()); 321 322 // Same transaction and same cursor: 323 dispatcher.RequestIDBCursorContinue(IndexedDBKey(), 324 IndexedDBKey(), 325 new MockCallbacks(), 326 cursor1_ipc_id, 327 cursor1_transaction_id); 328 329 EXPECT_EQ(1, cursor1->reset_count()); 330 EXPECT_EQ(0, cursor2->reset_count()); 331 332 // Same transaction and different cursor: 333 dispatcher.RequestIDBCursorContinue(IndexedDBKey(), 334 IndexedDBKey(), 335 new MockCallbacks(), 336 other_cursor_ipc_id, 337 cursor1_transaction_id); 338 339 EXPECT_EQ(2, cursor1->reset_count()); 340 EXPECT_EQ(0, cursor2->reset_count()); 341 342 cursor1.reset(); 343 cursor2.reset(); 344 } 345 346 } // namespace content 347