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/values.h" 7 #include "content/child/indexed_db/indexed_db_dispatcher.h" 8 #include "content/child/indexed_db/indexed_db_key_builders.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 "ipc/ipc_sync_message_filter.h" 13 #include "testing/gtest/include/gtest/gtest.h" 14 #include "third_party/WebKit/public/platform/WebData.h" 15 #include "third_party/WebKit/public/platform/WebIDBCallbacks.h" 16 17 using blink::WebBlobInfo; 18 using blink::WebData; 19 using blink::WebIDBCallbacks; 20 using blink::WebIDBDatabase; 21 using blink::WebIDBKey; 22 using blink::WebIDBKeyTypeNumber; 23 using blink::WebVector; 24 25 namespace content { 26 27 namespace { 28 29 class MockDispatcher : public IndexedDBDispatcher { 30 public: 31 explicit MockDispatcher(ThreadSafeSender* thread_safe_sender) 32 : IndexedDBDispatcher(thread_safe_sender), 33 prefetch_calls_(0), 34 last_prefetch_count_(0), 35 reset_calls_(0), 36 last_used_count_(0), 37 advance_calls_(0), 38 continue_calls_(0), 39 destroyed_cursor_id_(0) {} 40 41 virtual void RequestIDBCursorPrefetch(int n, 42 WebIDBCallbacks* callbacks, 43 int32 ipc_cursor_id) OVERRIDE { 44 ++prefetch_calls_; 45 last_prefetch_count_ = n; 46 callbacks_.reset(callbacks); 47 } 48 49 virtual void RequestIDBCursorPrefetchReset(int used_prefetches, 50 int unused_prefetches, 51 int32 ipc_cursor_id) OVERRIDE { 52 ++reset_calls_; 53 last_used_count_ = used_prefetches; 54 } 55 56 virtual void RequestIDBCursorAdvance(unsigned long count, 57 WebIDBCallbacks* callbacks, 58 int32 ipc_cursor_id, 59 int64 transaction_id) OVERRIDE { 60 ++advance_calls_; 61 callbacks_.reset(callbacks); 62 } 63 64 virtual void RequestIDBCursorContinue(const IndexedDBKey& key, 65 const IndexedDBKey& primary_key, 66 WebIDBCallbacks* callbacks, 67 int32 ipc_cursor_id, 68 int64 transaction_id) OVERRIDE { 69 ++continue_calls_; 70 callbacks_.reset(callbacks); 71 } 72 73 virtual void CursorDestroyed(int32 ipc_cursor_id) OVERRIDE { 74 destroyed_cursor_id_ = ipc_cursor_id; 75 } 76 77 int prefetch_calls() { return prefetch_calls_; } 78 int last_prefetch_count() { return last_prefetch_count_; } 79 int reset_calls() { return reset_calls_; } 80 int last_used_count() { return last_used_count_; } 81 int advance_calls() { return advance_calls_; } 82 int continue_calls() { return continue_calls_; } 83 int32 destroyed_cursor_id() { return destroyed_cursor_id_; } 84 85 private: 86 int prefetch_calls_; 87 int last_prefetch_count_; 88 int reset_calls_; 89 int last_used_count_; 90 int advance_calls_; 91 int continue_calls_; 92 int32 destroyed_cursor_id_; 93 scoped_ptr<WebIDBCallbacks> callbacks_; 94 }; 95 96 class MockContinueCallbacks : public WebIDBCallbacks { 97 public: 98 MockContinueCallbacks(IndexedDBKey* key = 0, 99 WebVector<WebBlobInfo>* webBlobInfo = 0) 100 : key_(key), webBlobInfo_(webBlobInfo) {} 101 102 virtual void onSuccess(const WebIDBKey& key, 103 const WebIDBKey& primaryKey, 104 const WebData& value, 105 const WebVector<WebBlobInfo>& webBlobInfo) OVERRIDE { 106 if (key_) 107 *key_ = IndexedDBKeyBuilder::Build(key); 108 if (webBlobInfo_) 109 *webBlobInfo_ = webBlobInfo; 110 } 111 112 private: 113 IndexedDBKey* key_; 114 WebVector<WebBlobInfo>* webBlobInfo_; 115 }; 116 117 } // namespace 118 119 class WebIDBCursorImplTest : public testing::Test { 120 public: 121 WebIDBCursorImplTest() { 122 null_key_.assignNull(); 123 sync_message_filter_ = new IPC::SyncMessageFilter(NULL); 124 thread_safe_sender_ = new ThreadSafeSender( 125 base::MessageLoopProxy::current(), sync_message_filter_.get()); 126 dispatcher_ = 127 make_scoped_ptr(new MockDispatcher(thread_safe_sender_.get())); 128 } 129 130 protected: 131 WebIDBKey null_key_; 132 scoped_refptr<ThreadSafeSender> thread_safe_sender_; 133 scoped_ptr<MockDispatcher> dispatcher_; 134 135 private: 136 scoped_refptr<IPC::SyncMessageFilter> sync_message_filter_; 137 138 DISALLOW_COPY_AND_ASSIGN(WebIDBCursorImplTest); 139 }; 140 141 TEST_F(WebIDBCursorImplTest, PrefetchTest) { 142 const int64 transaction_id = 1; 143 { 144 WebIDBCursorImpl cursor(WebIDBCursorImpl::kInvalidCursorId, 145 transaction_id, 146 thread_safe_sender_.get()); 147 148 // Call continue() until prefetching should kick in. 149 int continue_calls = 0; 150 EXPECT_EQ(dispatcher_->continue_calls(), 0); 151 for (int i = 0; i < WebIDBCursorImpl::kPrefetchContinueThreshold; ++i) { 152 cursor.continueFunction(null_key_, new MockContinueCallbacks()); 153 EXPECT_EQ(++continue_calls, dispatcher_->continue_calls()); 154 EXPECT_EQ(0, dispatcher_->prefetch_calls()); 155 } 156 157 // Do enough repetitions to verify that the count grows each time, 158 // but not so many that the maximum limit is hit. 159 const int kPrefetchRepetitions = 5; 160 161 int expected_key = 0; 162 int last_prefetch_count = 0; 163 for (int repetitions = 0; repetitions < kPrefetchRepetitions; 164 ++repetitions) { 165 // Initiate the prefetch 166 cursor.continueFunction(null_key_, new MockContinueCallbacks()); 167 EXPECT_EQ(continue_calls, dispatcher_->continue_calls()); 168 EXPECT_EQ(repetitions + 1, dispatcher_->prefetch_calls()); 169 170 // Verify that the requested count has increased since last time. 171 int prefetch_count = dispatcher_->last_prefetch_count(); 172 EXPECT_GT(prefetch_count, last_prefetch_count); 173 last_prefetch_count = prefetch_count; 174 175 // Fill the prefetch cache as requested. 176 std::vector<IndexedDBKey> keys; 177 std::vector<IndexedDBKey> primary_keys(prefetch_count); 178 std::vector<WebData> values(prefetch_count); 179 std::vector<WebVector<WebBlobInfo> > blob_info; 180 for (int i = 0; i < prefetch_count; ++i) { 181 keys.push_back(IndexedDBKey(expected_key + i, WebIDBKeyTypeNumber)); 182 blob_info.push_back( 183 WebVector<WebBlobInfo>(static_cast<size_t>(expected_key + i))); 184 } 185 cursor.SetPrefetchData(keys, primary_keys, values, blob_info); 186 187 // Note that the real dispatcher would call cursor->CachedContinue() 188 // immediately after cursor->SetPrefetchData() to service the request 189 // that initiated the prefetch. 190 191 // Verify that the cache is used for subsequent continue() calls. 192 for (int i = 0; i < prefetch_count; ++i) { 193 IndexedDBKey key; 194 WebVector<WebBlobInfo> web_blob_info; 195 cursor.continueFunction( 196 null_key_, new MockContinueCallbacks(&key, &web_blob_info)); 197 EXPECT_EQ(continue_calls, dispatcher_->continue_calls()); 198 EXPECT_EQ(repetitions + 1, dispatcher_->prefetch_calls()); 199 200 EXPECT_EQ(WebIDBKeyTypeNumber, key.type()); 201 EXPECT_EQ(expected_key, static_cast<int>(web_blob_info.size())); 202 EXPECT_EQ(expected_key++, key.number()); 203 } 204 } 205 } 206 207 EXPECT_EQ(dispatcher_->destroyed_cursor_id(), 208 WebIDBCursorImpl::kInvalidCursorId); 209 } 210 211 TEST_F(WebIDBCursorImplTest, AdvancePrefetchTest) { 212 const int64 transaction_id = 1; 213 WebIDBCursorImpl cursor(WebIDBCursorImpl::kInvalidCursorId, 214 transaction_id, 215 thread_safe_sender_.get()); 216 217 // Call continue() until prefetching should kick in. 218 EXPECT_EQ(0, dispatcher_->continue_calls()); 219 for (int i = 0; i < WebIDBCursorImpl::kPrefetchContinueThreshold; ++i) { 220 cursor.continueFunction(null_key_, new MockContinueCallbacks()); 221 } 222 EXPECT_EQ(0, dispatcher_->prefetch_calls()); 223 224 // Initiate the prefetch 225 cursor.continueFunction(null_key_, new MockContinueCallbacks()); 226 227 EXPECT_EQ(1, dispatcher_->prefetch_calls()); 228 EXPECT_EQ(static_cast<int>(WebIDBCursorImpl::kPrefetchContinueThreshold), 229 dispatcher_->continue_calls()); 230 EXPECT_EQ(0, dispatcher_->advance_calls()); 231 232 const int prefetch_count = dispatcher_->last_prefetch_count(); 233 234 // Fill the prefetch cache as requested. 235 int expected_key = 0; 236 std::vector<IndexedDBKey> keys; 237 std::vector<IndexedDBKey> primary_keys(prefetch_count); 238 std::vector<WebData> values(prefetch_count); 239 std::vector<WebVector<WebBlobInfo> > blob_info; 240 for (int i = 0; i < prefetch_count; ++i) { 241 keys.push_back(IndexedDBKey(expected_key + i, WebIDBKeyTypeNumber)); 242 blob_info.push_back( 243 WebVector<WebBlobInfo>(static_cast<size_t>(expected_key + i))); 244 } 245 cursor.SetPrefetchData(keys, primary_keys, values, blob_info); 246 247 // Note that the real dispatcher would call cursor->CachedContinue() 248 // immediately after cursor->SetPrefetchData() to service the request 249 // that initiated the prefetch. 250 251 // Need at least this many in the cache for the test steps. 252 ASSERT_GE(prefetch_count, 5); 253 254 // IDBCursor.continue() 255 IndexedDBKey key; 256 cursor.continueFunction(null_key_, new MockContinueCallbacks(&key)); 257 EXPECT_EQ(0, key.number()); 258 259 // IDBCursor.advance(1) 260 cursor.advance(1, new MockContinueCallbacks(&key)); 261 EXPECT_EQ(1, key.number()); 262 263 // IDBCursor.continue() 264 cursor.continueFunction(null_key_, new MockContinueCallbacks(&key)); 265 EXPECT_EQ(2, key.number()); 266 267 // IDBCursor.advance(2) 268 cursor.advance(2, new MockContinueCallbacks(&key)); 269 EXPECT_EQ(4, key.number()); 270 271 EXPECT_EQ(0, dispatcher_->advance_calls()); 272 273 // IDBCursor.advance(lots) - beyond the fetched amount 274 cursor.advance(WebIDBCursorImpl::kMaxPrefetchAmount, 275 new MockContinueCallbacks(&key)); 276 EXPECT_EQ(1, dispatcher_->advance_calls()); 277 EXPECT_EQ(1, dispatcher_->prefetch_calls()); 278 EXPECT_EQ(static_cast<int>(WebIDBCursorImpl::kPrefetchContinueThreshold), 279 dispatcher_->continue_calls()); 280 } 281 282 TEST_F(WebIDBCursorImplTest, PrefetchReset) { 283 const int64 transaction_id = 1; 284 WebIDBCursorImpl cursor(WebIDBCursorImpl::kInvalidCursorId, 285 transaction_id, 286 thread_safe_sender_.get()); 287 288 // Call continue() until prefetching should kick in. 289 int continue_calls = 0; 290 EXPECT_EQ(dispatcher_->continue_calls(), 0); 291 for (int i = 0; i < WebIDBCursorImpl::kPrefetchContinueThreshold; ++i) { 292 cursor.continueFunction(null_key_, new MockContinueCallbacks()); 293 EXPECT_EQ(++continue_calls, dispatcher_->continue_calls()); 294 EXPECT_EQ(0, dispatcher_->prefetch_calls()); 295 } 296 297 // Initiate the prefetch 298 cursor.continueFunction(null_key_, new MockContinueCallbacks()); 299 EXPECT_EQ(continue_calls, dispatcher_->continue_calls()); 300 EXPECT_EQ(1, dispatcher_->prefetch_calls()); 301 EXPECT_EQ(0, dispatcher_->reset_calls()); 302 303 // Now invalidate it 304 cursor.ResetPrefetchCache(); 305 306 // No reset should have been sent since nothing has been received yet. 307 EXPECT_EQ(0, dispatcher_->reset_calls()); 308 309 // Fill the prefetch cache as requested. 310 int prefetch_count = dispatcher_->last_prefetch_count(); 311 std::vector<IndexedDBKey> keys(prefetch_count); 312 std::vector<IndexedDBKey> primary_keys(prefetch_count); 313 std::vector<WebData> values(prefetch_count); 314 std::vector<WebVector<WebBlobInfo> > blob_info(prefetch_count); 315 cursor.SetPrefetchData(keys, primary_keys, values, blob_info); 316 317 // No reset should have been sent since prefetch data hasn't been used. 318 EXPECT_EQ(0, dispatcher_->reset_calls()); 319 320 // The real dispatcher would call cursor->CachedContinue(), so do that: 321 scoped_ptr<WebIDBCallbacks> callbacks(new MockContinueCallbacks()); 322 cursor.CachedContinue(callbacks.get()); 323 324 // Now the cursor should have reset the rest of the cache. 325 EXPECT_EQ(1, dispatcher_->reset_calls()); 326 EXPECT_EQ(1, dispatcher_->last_used_count()); 327 } 328 329 } // namespace content 330