blob: 18bfa258969a3497f1e0c2430ac0c4bfbbb47ca2 [file] [log] [blame]
// Copyright 2017 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/browser/indexed_db/indexed_db_pre_close_task_queue.h"
#include <memory>
#include "base/memory/ptr_util.h"
#include "base/run_loop.h"
#include "base/strings/string16.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/scoped_task_environment.h"
#include "base/time/time.h"
#include "base/timer/mock_timer.h"
#include "base/timer/timer.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/indexeddb/indexeddb_metadata.h"
using blink::IndexedDBDatabaseMetadata;
namespace content {
using PreCloseTask = IndexedDBPreCloseTaskQueue::PreCloseTask;
using StopReason = IndexedDBPreCloseTaskQueue::StopReason;
namespace {
constexpr base::TimeDelta kTestMaxRunTime = base::TimeDelta::FromSeconds(30);
const base::string16 kDBName = base::ASCIIToUTF16("TestDBName");
constexpr int64_t kDBId = 1;
constexpr int64_t kDBVersion = 2;
constexpr int64_t kDBMaxObjectStoreId = 29;
ACTION_P2(RunClosureThenReturn, closure, ret) {
closure.Run();
return ret;
}
class MockPreCloseTask : public PreCloseTask {
public:
MockPreCloseTask() {}
~MockPreCloseTask() override {}
MOCK_METHOD1(SetMetadata,
void(std::vector<IndexedDBDatabaseMetadata> const* metadata));
MOCK_METHOD1(Stop, void(StopReason reason));
MOCK_METHOD0(RunRound, bool());
};
void SetBoolValue(bool* pointer, bool value_to_set) {
DCHECK(pointer);
*pointer = value_to_set;
}
leveldb::Status MetadataFetcher(
bool* called,
leveldb::Status return_status,
std::vector<IndexedDBDatabaseMetadata>* metadata,
std::vector<IndexedDBDatabaseMetadata>* output_metadata) {
*called = true;
*output_metadata = *metadata;
return return_status;
}
class IndexedDBPreCloseTaskQueueTest : public testing::Test {
public:
IndexedDBPreCloseTaskQueueTest() {
metadata_.push_back(IndexedDBDatabaseMetadata(kDBName, kDBId, kDBVersion,
kDBMaxObjectStoreId));
}
~IndexedDBPreCloseTaskQueueTest() override {}
protected:
std::vector<IndexedDBDatabaseMetadata> metadata_;
base::test::ScopedTaskEnvironment scoped_task_environment_;
};
TEST_F(IndexedDBPreCloseTaskQueueTest, NoTasks) {
bool done_called = false;
bool metadata_called = false;
base::MockOneShotTimer* fake_timer = new base::MockOneShotTimer;
IndexedDBPreCloseTaskQueue queue(
std::list<std::unique_ptr<PreCloseTask>>(),
base::BindOnce(&SetBoolValue, &done_called, true), kTestMaxRunTime,
base::WrapUnique(fake_timer));
queue.Start(base::BindOnce(&MetadataFetcher, &metadata_called,
leveldb::Status::OK(), &metadata_));
EXPECT_FALSE(metadata_called);
EXPECT_TRUE(done_called);
EXPECT_TRUE(queue.started());
EXPECT_TRUE(queue.done());
}
TEST_F(IndexedDBPreCloseTaskQueueTest, TaskOneRound) {
bool done_called = false;
bool metadata_called = false;
MockPreCloseTask* task = new testing::StrictMock<MockPreCloseTask>();
EXPECT_CALL(*task,
SetMetadata(testing::Pointee(testing::ContainerEq(metadata_))));
base::MockOneShotTimer* fake_timer = new base::MockOneShotTimer;
std::list<std::unique_ptr<PreCloseTask>> tasks;
tasks.push_back(base::WrapUnique(task));
IndexedDBPreCloseTaskQueue queue(
std::move(tasks), base::BindOnce(&SetBoolValue, &done_called, true),
kTestMaxRunTime, base::WrapUnique(fake_timer));
queue.Start(base::BindOnce(&MetadataFetcher, &metadata_called,
leveldb::Status::OK(), &metadata_));
// Expect calls are posted as tasks.
EXPECT_CALL(*task, RunRound()).WillOnce(testing::Return(true));
scoped_task_environment_.RunUntilIdle();
EXPECT_TRUE(metadata_called);
EXPECT_TRUE(done_called);
EXPECT_TRUE(queue.started());
EXPECT_TRUE(queue.done());
}
TEST_F(IndexedDBPreCloseTaskQueueTest, TaskTwoRounds) {
bool done_called = false;
bool metadata_called = false;
MockPreCloseTask* task = new testing::StrictMock<MockPreCloseTask>();
EXPECT_CALL(*task,
SetMetadata(testing::Pointee(testing::ContainerEq(metadata_))));
base::MockOneShotTimer* fake_timer = new base::MockOneShotTimer;
std::list<std::unique_ptr<PreCloseTask>> tasks;
tasks.push_back(base::WrapUnique(task));
IndexedDBPreCloseTaskQueue queue(
std::move(tasks), base::BindOnce(&SetBoolValue, &done_called, true),
kTestMaxRunTime, base::WrapUnique(fake_timer));
queue.Start(base::BindOnce(&MetadataFetcher, &metadata_called,
leveldb::Status::OK(), &metadata_));
EXPECT_FALSE(queue.done());
{
base::RunLoop loop;
EXPECT_CALL(*task, RunRound())
.WillOnce(RunClosureThenReturn(loop.QuitClosure(), false));
loop.Run();
}
EXPECT_FALSE(done_called);
EXPECT_TRUE(queue.started());
EXPECT_FALSE(queue.done());
EXPECT_CALL(*task, RunRound()).WillOnce(testing::Return(true));
scoped_task_environment_.RunUntilIdle();
EXPECT_TRUE(metadata_called);
EXPECT_TRUE(done_called);
EXPECT_TRUE(queue.started());
EXPECT_TRUE(queue.done());
}
TEST_F(IndexedDBPreCloseTaskQueueTest, TwoTasks) {
bool done_called = false;
bool metadata_called = false;
MockPreCloseTask* task1 = new testing::StrictMock<MockPreCloseTask>();
MockPreCloseTask* task2 = new testing::StrictMock<MockPreCloseTask>();
EXPECT_CALL(*task1,
SetMetadata(testing::Pointee(testing::ContainerEq(metadata_))));
base::MockOneShotTimer* fake_timer = new base::MockOneShotTimer;
std::list<std::unique_ptr<PreCloseTask>> tasks;
tasks.push_back(base::WrapUnique(task1));
tasks.push_back(base::WrapUnique(task2));
IndexedDBPreCloseTaskQueue queue(
std::move(tasks), base::BindOnce(&SetBoolValue, &done_called, true),
kTestMaxRunTime, base::WrapUnique(fake_timer));
queue.Start(base::BindOnce(&MetadataFetcher, &metadata_called,
leveldb::Status::OK(), &metadata_));
EXPECT_FALSE(queue.done());
{
base::RunLoop loop;
EXPECT_CALL(*task1, RunRound())
.WillOnce(RunClosureThenReturn(loop.QuitClosure(), true));
EXPECT_CALL(*task2,
SetMetadata(testing::Pointee(testing::ContainerEq(metadata_))));
loop.Run();
}
{
base::RunLoop loop;
EXPECT_CALL(*task2, RunRound())
.WillOnce(RunClosureThenReturn(loop.QuitClosure(), true));
loop.Run();
}
EXPECT_TRUE(metadata_called);
EXPECT_TRUE(done_called);
EXPECT_TRUE(queue.started());
EXPECT_TRUE(queue.done());
}
TEST_F(IndexedDBPreCloseTaskQueueTest, StopForNewConnectionBeforeStart) {
bool done_called = false;
bool metadata_called = false;
MockPreCloseTask* task1 = new testing::StrictMock<MockPreCloseTask>();
MockPreCloseTask* task2 = new testing::StrictMock<MockPreCloseTask>();
EXPECT_CALL(*task1,
SetMetadata(testing::Pointee(testing::ContainerEq(metadata_))));
base::MockOneShotTimer* fake_timer = new base::MockOneShotTimer;
std::list<std::unique_ptr<PreCloseTask>> tasks;
tasks.push_back(base::WrapUnique(task1));
tasks.push_back(base::WrapUnique(task2));
IndexedDBPreCloseTaskQueue queue(
std::move(tasks), base::BindOnce(&SetBoolValue, &done_called, true),
kTestMaxRunTime, base::WrapUnique(fake_timer));
queue.Start(base::BindOnce(&MetadataFetcher, &metadata_called,
leveldb::Status::OK(), &metadata_));
EXPECT_CALL(*task1, Stop(StopReason::NEW_CONNECTION));
EXPECT_CALL(*task2, Stop(StopReason::NEW_CONNECTION));
queue.StopForNewConnection();
scoped_task_environment_.RunUntilIdle();
EXPECT_TRUE(metadata_called);
EXPECT_TRUE(done_called);
EXPECT_TRUE(queue.started());
EXPECT_TRUE(queue.done());
}
TEST_F(IndexedDBPreCloseTaskQueueTest, StopForNewConnectionAfterRound) {
bool done_called = false;
bool metadata_called = false;
MockPreCloseTask* task = new testing::StrictMock<MockPreCloseTask>();
EXPECT_CALL(*task,
SetMetadata(testing::Pointee(testing::ContainerEq(metadata_))));
base::MockOneShotTimer* fake_timer = new base::MockOneShotTimer;
std::list<std::unique_ptr<PreCloseTask>> tasks;
tasks.push_back(base::WrapUnique(task));
IndexedDBPreCloseTaskQueue queue(
std::move(tasks), base::BindOnce(&SetBoolValue, &done_called, true),
kTestMaxRunTime, base::WrapUnique(fake_timer));
queue.Start(base::BindOnce(&MetadataFetcher, &metadata_called,
leveldb::Status::OK(), &metadata_));
{
base::RunLoop loop;
EXPECT_CALL(*task, RunRound())
.WillOnce(RunClosureThenReturn(loop.QuitClosure(), false));
loop.Run();
}
EXPECT_CALL(*task, Stop(StopReason::NEW_CONNECTION));
queue.StopForNewConnection();
scoped_task_environment_.RunUntilIdle();
EXPECT_TRUE(metadata_called);
EXPECT_TRUE(done_called);
EXPECT_TRUE(queue.started());
EXPECT_TRUE(queue.done());
}
TEST_F(IndexedDBPreCloseTaskQueueTest, StopForNewConnectionAfterTaskCompletes) {
bool done_called = false;
bool metadata_called = false;
MockPreCloseTask* task1 = new testing::StrictMock<MockPreCloseTask>();
MockPreCloseTask* task2 = new testing::StrictMock<MockPreCloseTask>();
EXPECT_CALL(*task1,
SetMetadata(testing::Pointee(testing::ContainerEq(metadata_))));
base::MockOneShotTimer* fake_timer = new base::MockOneShotTimer;
std::list<std::unique_ptr<PreCloseTask>> tasks;
tasks.push_back(base::WrapUnique(task1));
tasks.push_back(base::WrapUnique(task2));
IndexedDBPreCloseTaskQueue queue(
std::move(tasks), base::BindOnce(&SetBoolValue, &done_called, true),
kTestMaxRunTime, base::WrapUnique(fake_timer));
queue.Start(base::BindOnce(&MetadataFetcher, &metadata_called,
leveldb::Status::OK(), &metadata_));
{
base::RunLoop loop;
EXPECT_CALL(*task1, RunRound())
.WillOnce(RunClosureThenReturn(loop.QuitClosure(), true));
EXPECT_CALL(*task2,
SetMetadata(testing::Pointee(testing::ContainerEq(metadata_))));
loop.Run();
}
EXPECT_CALL(*task2, Stop(StopReason::NEW_CONNECTION));
queue.StopForNewConnection();
scoped_task_environment_.RunUntilIdle();
EXPECT_TRUE(metadata_called);
EXPECT_TRUE(done_called);
EXPECT_TRUE(queue.started());
EXPECT_TRUE(queue.done());
}
TEST_F(IndexedDBPreCloseTaskQueueTest, StopForTimout) {
bool done_called = false;
bool metadata_called = false;
MockPreCloseTask* task1 = new testing::StrictMock<MockPreCloseTask>();
MockPreCloseTask* task2 = new testing::StrictMock<MockPreCloseTask>();
EXPECT_CALL(*task1,
SetMetadata(testing::Pointee(testing::ContainerEq(metadata_))));
base::MockOneShotTimer* fake_timer = new base::MockOneShotTimer;
std::list<std::unique_ptr<PreCloseTask>> tasks;
tasks.push_back(base::WrapUnique(task1));
tasks.push_back(base::WrapUnique(task2));
IndexedDBPreCloseTaskQueue queue(
std::move(tasks), base::BindOnce(&SetBoolValue, &done_called, true),
kTestMaxRunTime, base::WrapUnique(fake_timer));
queue.Start(base::BindOnce(&MetadataFetcher, &metadata_called,
leveldb::Status::OK(), &metadata_));
EXPECT_FALSE(queue.done());
{
base::RunLoop loop;
EXPECT_CALL(*task1, RunRound())
.WillOnce(RunClosureThenReturn(loop.QuitClosure(), true));
EXPECT_CALL(*task2,
SetMetadata(testing::Pointee(testing::ContainerEq(metadata_))));
loop.Run();
}
EXPECT_CALL(*task2, Stop(StopReason::TIMEOUT));
fake_timer->Fire();
scoped_task_environment_.RunUntilIdle();
EXPECT_TRUE(metadata_called);
EXPECT_TRUE(done_called);
EXPECT_TRUE(queue.started());
EXPECT_TRUE(queue.done());
}
TEST_F(IndexedDBPreCloseTaskQueueTest, MetadataError) {
bool done_called = false;
bool metadata_called = false;
MockPreCloseTask* task1 = new testing::StrictMock<MockPreCloseTask>();
MockPreCloseTask* task2 = new testing::StrictMock<MockPreCloseTask>();
base::MockOneShotTimer* fake_timer = new base::MockOneShotTimer;
std::list<std::unique_ptr<PreCloseTask>> tasks;
tasks.push_back(base::WrapUnique(task1));
tasks.push_back(base::WrapUnique(task2));
IndexedDBPreCloseTaskQueue queue(
std::move(tasks), base::BindOnce(&SetBoolValue, &done_called, true),
kTestMaxRunTime, base::WrapUnique(fake_timer));
EXPECT_CALL(*task1, Stop(StopReason::METADATA_ERROR));
EXPECT_CALL(*task2, Stop(StopReason::METADATA_ERROR));
queue.Start(base::BindOnce(&MetadataFetcher, &metadata_called,
leveldb::Status::IOError(""), &metadata_));
scoped_task_environment_.RunUntilIdle();
EXPECT_TRUE(metadata_called);
EXPECT_TRUE(done_called);
EXPECT_TRUE(queue.started());
EXPECT_TRUE(queue.done());
}
} // namespace
} // namespace content