blob: 83b2110ac14140f1c4b927e7aad776f6992bc806 [file] [log] [blame]
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/child/indexed_db/webidbcursor_impl.h"
#include <stddef.h>
#include <stdint.h>
#include <memory>
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/values.h"
#include "content/child/indexed_db/indexed_db_dispatcher.h"
#include "content/child/indexed_db/indexed_db_key_builders.h"
#include "content/child/indexed_db/mock_webidbcallbacks.h"
#include "content/child/thread_safe_sender.h"
#include "content/common/indexed_db/indexed_db_key.h"
#include "ipc/ipc_sync_message_filter.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/WebKit/public/platform/WebData.h"
using blink::WebBlobInfo;
using blink::WebData;
using blink::WebIDBCallbacks;
using blink::WebIDBKey;
using blink::WebIDBKeyTypeNumber;
using blink::WebIDBValue;
using blink::WebVector;
using testing::StrictMock;
namespace content {
namespace {
class MockDispatcher : public IndexedDBDispatcher {
public:
explicit MockDispatcher(ThreadSafeSender* thread_safe_sender)
: IndexedDBDispatcher(thread_safe_sender),
prefetch_calls_(0),
last_prefetch_count_(0),
reset_calls_(0),
last_used_count_(0),
advance_calls_(0),
continue_calls_(0),
destroyed_cursor_id_(0) {}
void RequestIDBCursorPrefetch(int n,
WebIDBCallbacks* callbacks,
int32_t ipc_cursor_id) override {
++prefetch_calls_;
last_prefetch_count_ = n;
callbacks_.reset(callbacks);
}
void RequestIDBCursorPrefetchReset(int used_prefetches,
int unused_prefetches,
int32_t ipc_cursor_id) override {
++reset_calls_;
last_used_count_ = used_prefetches;
}
void RequestIDBCursorAdvance(unsigned long count,
WebIDBCallbacks* callbacks,
int32_t ipc_cursor_id,
int64_t transaction_id) override {
++advance_calls_;
callbacks_.reset(callbacks);
}
void RequestIDBCursorContinue(const IndexedDBKey& key,
const IndexedDBKey& primary_key,
WebIDBCallbacks* callbacks,
int32_t ipc_cursor_id,
int64_t transaction_id) override {
++continue_calls_;
callbacks_.reset(callbacks);
}
void CursorDestroyed(int32_t ipc_cursor_id) override {
destroyed_cursor_id_ = ipc_cursor_id;
}
int prefetch_calls() { return prefetch_calls_; }
int last_prefetch_count() { return last_prefetch_count_; }
int reset_calls() { return reset_calls_; }
int last_used_count() { return last_used_count_; }
int advance_calls() { return advance_calls_; }
int continue_calls() { return continue_calls_; }
int32_t destroyed_cursor_id() { return destroyed_cursor_id_; }
private:
int prefetch_calls_;
int last_prefetch_count_;
int reset_calls_;
int last_used_count_;
int advance_calls_;
int continue_calls_;
int32_t destroyed_cursor_id_;
std::unique_ptr<WebIDBCallbacks> callbacks_;
};
class MockContinueCallbacks : public StrictMock<MockWebIDBCallbacks> {
public:
MockContinueCallbacks(IndexedDBKey* key = 0,
WebVector<WebBlobInfo>* webBlobInfo = 0)
: key_(key), web_blob_info_(webBlobInfo) {}
void onSuccess(const WebIDBKey& key,
const WebIDBKey& primaryKey,
const WebIDBValue& value) override {
if (key_)
*key_ = IndexedDBKeyBuilder::Build(key);
if (web_blob_info_)
*web_blob_info_ = value.webBlobInfo;
}
private:
IndexedDBKey* key_;
WebVector<WebBlobInfo>* web_blob_info_;
};
class MockSyncMessageFilter : public IPC::SyncMessageFilter {
public:
MockSyncMessageFilter() : SyncMessageFilter(nullptr) {}
private:
~MockSyncMessageFilter() override {}
};
} // namespace
class WebIDBCursorImplTest : public testing::Test {
public:
WebIDBCursorImplTest() {
null_key_.assignNull();
thread_safe_sender_ = new ThreadSafeSender(
base::ThreadTaskRunnerHandle::Get(), new MockSyncMessageFilter);
dispatcher_ = base::MakeUnique<MockDispatcher>(thread_safe_sender_.get());
}
protected:
base::MessageLoop message_loop_;
WebIDBKey null_key_;
scoped_refptr<ThreadSafeSender> thread_safe_sender_;
std::unique_ptr<MockDispatcher> dispatcher_;
private:
DISALLOW_COPY_AND_ASSIGN(WebIDBCursorImplTest);
};
TEST_F(WebIDBCursorImplTest, PrefetchTest) {
const int64_t transaction_id = 1;
{
WebIDBCursorImpl cursor(WebIDBCursorImpl::kInvalidCursorId,
transaction_id,
thread_safe_sender_.get());
// Call continue() until prefetching should kick in.
int continue_calls = 0;
EXPECT_EQ(dispatcher_->continue_calls(), 0);
for (int i = 0; i < WebIDBCursorImpl::kPrefetchContinueThreshold; ++i) {
cursor.continueFunction(null_key_, new MockContinueCallbacks());
EXPECT_EQ(++continue_calls, dispatcher_->continue_calls());
EXPECT_EQ(0, dispatcher_->prefetch_calls());
}
// Do enough repetitions to verify that the count grows each time,
// but not so many that the maximum limit is hit.
const int kPrefetchRepetitions = 5;
int expected_key = 0;
int last_prefetch_count = 0;
for (int repetitions = 0; repetitions < kPrefetchRepetitions;
++repetitions) {
// Initiate the prefetch
cursor.continueFunction(null_key_, new MockContinueCallbacks());
EXPECT_EQ(continue_calls, dispatcher_->continue_calls());
EXPECT_EQ(repetitions + 1, dispatcher_->prefetch_calls());
// Verify that the requested count has increased since last time.
int prefetch_count = dispatcher_->last_prefetch_count();
EXPECT_GT(prefetch_count, last_prefetch_count);
last_prefetch_count = prefetch_count;
// Fill the prefetch cache as requested.
std::vector<IndexedDBKey> keys;
std::vector<IndexedDBKey> primary_keys(prefetch_count);
std::vector<WebIDBValue> values(prefetch_count);
for (int i = 0; i < prefetch_count; ++i) {
keys.push_back(IndexedDBKey(expected_key + i, WebIDBKeyTypeNumber));
values[i].webBlobInfo =
WebVector<WebBlobInfo>(static_cast<size_t>(expected_key + i));
}
cursor.SetPrefetchData(keys, primary_keys, values);
// Note that the real dispatcher would call cursor->CachedContinue()
// immediately after cursor->SetPrefetchData() to service the request
// that initiated the prefetch.
// Verify that the cache is used for subsequent continue() calls.
for (int i = 0; i < prefetch_count; ++i) {
IndexedDBKey key;
WebVector<WebBlobInfo> web_blob_info;
cursor.continueFunction(
null_key_, new MockContinueCallbacks(&key, &web_blob_info));
EXPECT_EQ(continue_calls, dispatcher_->continue_calls());
EXPECT_EQ(repetitions + 1, dispatcher_->prefetch_calls());
EXPECT_EQ(WebIDBKeyTypeNumber, key.type());
EXPECT_EQ(expected_key, static_cast<int>(web_blob_info.size()));
EXPECT_EQ(expected_key++, key.number());
}
}
}
EXPECT_EQ(dispatcher_->destroyed_cursor_id(),
WebIDBCursorImpl::kInvalidCursorId);
}
TEST_F(WebIDBCursorImplTest, AdvancePrefetchTest) {
const int64_t transaction_id = 1;
WebIDBCursorImpl cursor(WebIDBCursorImpl::kInvalidCursorId,
transaction_id,
thread_safe_sender_.get());
// Call continue() until prefetching should kick in.
EXPECT_EQ(0, dispatcher_->continue_calls());
for (int i = 0; i < WebIDBCursorImpl::kPrefetchContinueThreshold; ++i) {
cursor.continueFunction(null_key_, new MockContinueCallbacks());
}
EXPECT_EQ(0, dispatcher_->prefetch_calls());
// Initiate the prefetch
cursor.continueFunction(null_key_, new MockContinueCallbacks());
EXPECT_EQ(1, dispatcher_->prefetch_calls());
EXPECT_EQ(static_cast<int>(WebIDBCursorImpl::kPrefetchContinueThreshold),
dispatcher_->continue_calls());
EXPECT_EQ(0, dispatcher_->advance_calls());
const int prefetch_count = dispatcher_->last_prefetch_count();
// Fill the prefetch cache as requested.
int expected_key = 0;
std::vector<IndexedDBKey> keys;
std::vector<IndexedDBKey> primary_keys(prefetch_count);
std::vector<WebIDBValue> values(prefetch_count);
for (int i = 0; i < prefetch_count; ++i) {
keys.push_back(IndexedDBKey(expected_key + i, WebIDBKeyTypeNumber));
values[i].webBlobInfo =
WebVector<WebBlobInfo>(static_cast<size_t>(expected_key + i));
}
cursor.SetPrefetchData(keys, primary_keys, values);
// Note that the real dispatcher would call cursor->CachedContinue()
// immediately after cursor->SetPrefetchData() to service the request
// that initiated the prefetch.
// Need at least this many in the cache for the test steps.
ASSERT_GE(prefetch_count, 5);
// IDBCursor.continue()
IndexedDBKey key;
cursor.continueFunction(null_key_, new MockContinueCallbacks(&key));
EXPECT_EQ(0, key.number());
// IDBCursor.advance(1)
cursor.advance(1, new MockContinueCallbacks(&key));
EXPECT_EQ(1, key.number());
// IDBCursor.continue()
cursor.continueFunction(null_key_, new MockContinueCallbacks(&key));
EXPECT_EQ(2, key.number());
// IDBCursor.advance(2)
cursor.advance(2, new MockContinueCallbacks(&key));
EXPECT_EQ(4, key.number());
EXPECT_EQ(0, dispatcher_->advance_calls());
// IDBCursor.advance(lots) - beyond the fetched amount
cursor.advance(WebIDBCursorImpl::kMaxPrefetchAmount,
new MockContinueCallbacks(&key));
EXPECT_EQ(1, dispatcher_->advance_calls());
EXPECT_EQ(1, dispatcher_->prefetch_calls());
EXPECT_EQ(static_cast<int>(WebIDBCursorImpl::kPrefetchContinueThreshold),
dispatcher_->continue_calls());
}
TEST_F(WebIDBCursorImplTest, PrefetchReset) {
const int64_t transaction_id = 1;
WebIDBCursorImpl cursor(WebIDBCursorImpl::kInvalidCursorId,
transaction_id,
thread_safe_sender_.get());
// Call continue() until prefetching should kick in.
int continue_calls = 0;
EXPECT_EQ(dispatcher_->continue_calls(), 0);
for (int i = 0; i < WebIDBCursorImpl::kPrefetchContinueThreshold; ++i) {
cursor.continueFunction(null_key_, new MockContinueCallbacks());
EXPECT_EQ(++continue_calls, dispatcher_->continue_calls());
EXPECT_EQ(0, dispatcher_->prefetch_calls());
}
// Initiate the prefetch
cursor.continueFunction(null_key_, new MockContinueCallbacks());
EXPECT_EQ(continue_calls, dispatcher_->continue_calls());
EXPECT_EQ(1, dispatcher_->prefetch_calls());
EXPECT_EQ(0, dispatcher_->reset_calls());
// Now invalidate it
cursor.ResetPrefetchCache();
// No reset should have been sent since nothing has been received yet.
EXPECT_EQ(0, dispatcher_->reset_calls());
// Fill the prefetch cache as requested.
int prefetch_count = dispatcher_->last_prefetch_count();
std::vector<IndexedDBKey> keys(prefetch_count);
std::vector<IndexedDBKey> primary_keys(prefetch_count);
std::vector<WebIDBValue> values(prefetch_count);
cursor.SetPrefetchData(keys, primary_keys, values);
// No reset should have been sent since prefetch data hasn't been used.
EXPECT_EQ(0, dispatcher_->reset_calls());
// The real dispatcher would call cursor->CachedContinue(), so do that:
MockContinueCallbacks callbacks;
cursor.CachedContinue(&callbacks);
// Now the cursor should have reset the rest of the cache.
EXPECT_EQ(1, dispatcher_->reset_calls());
EXPECT_EQ(1, dispatcher_->last_used_count());
}
} // namespace content