Home | History | Annotate | Download | only in indexed_db
      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