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
17using blink::WebBlobInfo;
18using blink::WebData;
19using blink::WebIDBCallbacks;
20using blink::WebIDBDatabase;
21using blink::WebIDBKey;
22using blink::WebIDBKeyTypeNumber;
23using blink::WebVector;
24
25namespace content {
26
27namespace {
28
29class 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
96class 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
119class 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
141TEST_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
211TEST_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
282TEST_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