blob: 537f95f8db7bc6fb79f828453a387bde02740eb1 [file] [log] [blame]
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/core/fetch/bytes_uploader.h"
#include "base/test/mock_callback.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "net/base/net_errors.h"
#include "services/network/public/mojom/chunked_data_pipe_getter.mojom-blink.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h"
#include "third_party/blink/renderer/platform/heap/persistent.h"
#include "third_party/blink/renderer/platform/scheduler/public/thread.h"
#include "third_party/blink/renderer/platform/testing/task_environment.h"
#include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
using network::mojom::blink::ChunkedDataPipeGetter;
using testing::_;
using testing::InSequence;
using testing::Invoke;
using testing::NiceMock;
using testing::Return;
using testing::StrictMock;
namespace blink {
typedef testing::StrictMock<testing::MockFunction<void(int)>> Checkpoint;
class MockBytesConsumer : public BytesConsumer {
public:
MockBytesConsumer() = default;
~MockBytesConsumer() override = default;
MOCK_METHOD2(BeginRead, Result(const char**, size_t*));
MOCK_METHOD1(EndRead, Result(size_t));
MOCK_METHOD1(SetClient, void(Client*));
MOCK_METHOD0(ClearClient, void());
MOCK_METHOD0(Cancel, void());
PublicState GetPublicState() const override { return state_; }
void SetPublicState(PublicState state) { state_ = state; }
MOCK_CONST_METHOD0(GetError, Error());
MOCK_CONST_METHOD0(DebugName, String());
private:
PublicState state_ = PublicState::kReadableOrWaiting;
};
class BytesUploaderTest : public ::testing::Test {
public:
~BytesUploaderTest() override {
// Avoids leaking mocked objects passed to `bytes_uploader_`.
bytes_uploader_.Release();
}
void InitializeBytesUploader(MockBytesConsumer* mock_bytes_consumer,
uint32_t capacity = 100u) {
bytes_uploader_ = MakeGarbageCollected<BytesUploader>(
nullptr, mock_bytes_consumer, remote_.BindNewPipeAndPassReceiver(),
blink::scheduler::GetSingleThreadTaskRunnerForTesting(),
/*client=*/nullptr);
const MojoCreateDataPipeOptions data_pipe_options{
sizeof(MojoCreateDataPipeOptions), MOJO_CREATE_DATA_PIPE_FLAG_NONE, 1,
capacity};
ASSERT_EQ(MOJO_RESULT_OK,
mojo::CreateDataPipe(&data_pipe_options, writable_, readable_));
}
mojo::ScopedDataPipeProducerHandle& Writable() { return writable_; }
mojo::ScopedDataPipeConsumerHandle& Readable() { return readable_; }
mojo::Remote<ChunkedDataPipeGetter>& Remote() { return remote_; }
protected:
Persistent<BytesUploader> bytes_uploader_;
private:
test::TaskEnvironment task_environment_;
mojo::ScopedDataPipeProducerHandle writable_;
mojo::ScopedDataPipeConsumerHandle readable_;
mojo::Remote<ChunkedDataPipeGetter> remote_;
};
TEST_F(BytesUploaderTest, Create) {
auto* mock_bytes_consumer =
MakeGarbageCollected<StrictMock<MockBytesConsumer>>();
Checkpoint checkpoint;
{
InSequence s;
EXPECT_CALL(checkpoint, Call(1));
}
checkpoint.Call(1);
mojo::PendingRemote<ChunkedDataPipeGetter> pending_remote;
BytesUploader* bytes_uploader_ = MakeGarbageCollected<BytesUploader>(
nullptr, mock_bytes_consumer,
pending_remote.InitWithNewPipeAndPassReceiver(),
blink::scheduler::GetSingleThreadTaskRunnerForTesting(),
/*client=*/nullptr);
ASSERT_TRUE(bytes_uploader_);
}
// TODO(yoichio): Needs BytesConsumer state tests.
TEST_F(BytesUploaderTest, ReadEmpty) {
auto* mock_bytes_consumer =
MakeGarbageCollected<StrictMock<MockBytesConsumer>>();
base::MockCallback<ChunkedDataPipeGetter::GetSizeCallback> get_size_callback;
Checkpoint checkpoint;
{
InSequence s;
EXPECT_CALL(checkpoint, Call(1));
EXPECT_CALL(checkpoint, Call(2));
EXPECT_CALL(*mock_bytes_consumer, SetClient(_));
EXPECT_CALL(*mock_bytes_consumer, BeginRead(_, _))
.WillOnce(Return(BytesConsumer::Result::kDone));
EXPECT_CALL(*mock_bytes_consumer, Cancel());
EXPECT_CALL(get_size_callback, Run(net::OK, 0u));
EXPECT_CALL(checkpoint, Call(3));
}
checkpoint.Call(1);
InitializeBytesUploader(mock_bytes_consumer);
Remote()->GetSize(get_size_callback.Get());
Remote()->StartReading(std::move(Writable()));
checkpoint.Call(2);
test::RunPendingTasks();
checkpoint.Call(3);
char buffer[20] = {};
size_t num_bytes = sizeof(buffer);
MojoResult rv =
Readable()->ReadData(buffer, &num_bytes, MOJO_READ_DATA_FLAG_NONE);
EXPECT_EQ(MOJO_RESULT_SHOULD_WAIT, rv);
}
TEST_F(BytesUploaderTest, ReadSmall) {
auto* mock_bytes_consumer =
MakeGarbageCollected<StrictMock<MockBytesConsumer>>();
base::MockCallback<ChunkedDataPipeGetter::GetSizeCallback> get_size_callback;
Checkpoint checkpoint;
{
InSequence s;
EXPECT_CALL(checkpoint, Call(1));
EXPECT_CALL(checkpoint, Call(2));
EXPECT_CALL(*mock_bytes_consumer, SetClient(_));
EXPECT_CALL(*mock_bytes_consumer, BeginRead(_, _))
.WillOnce(Invoke([](const char** buffer, size_t* size) {
*size = 6;
*buffer = "foobar";
return BytesConsumer::Result::kOk;
}));
EXPECT_CALL(*mock_bytes_consumer, EndRead(6u))
.WillOnce(Return(BytesConsumer::Result::kDone));
EXPECT_CALL(*mock_bytes_consumer, Cancel());
EXPECT_CALL(get_size_callback, Run(net::OK, 6u));
EXPECT_CALL(checkpoint, Call(3));
}
checkpoint.Call(1);
InitializeBytesUploader(mock_bytes_consumer);
Remote()->GetSize(get_size_callback.Get());
Remote()->StartReading(std::move(Writable()));
checkpoint.Call(2);
test::RunPendingTasks();
checkpoint.Call(3);
char buffer[20] = {};
size_t num_bytes = sizeof(buffer);
EXPECT_EQ(MOJO_RESULT_OK,
Readable()->ReadData(buffer, &num_bytes, MOJO_READ_DATA_FLAG_NONE));
EXPECT_EQ(6u, num_bytes);
EXPECT_STREQ("foobar", buffer);
}
TEST_F(BytesUploaderTest, ReadOverPipeCapacity) {
auto* mock_bytes_consumer =
MakeGarbageCollected<StrictMock<MockBytesConsumer>>();
base::MockCallback<ChunkedDataPipeGetter::GetSizeCallback> get_size_callback;
Checkpoint checkpoint;
{
InSequence s;
EXPECT_CALL(checkpoint, Call(1));
EXPECT_CALL(checkpoint, Call(2));
EXPECT_CALL(*mock_bytes_consumer, SetClient(_));
EXPECT_CALL(*mock_bytes_consumer, BeginRead(_, _))
.WillOnce(Invoke([](const char** buffer, size_t* size) {
*size = 12;
*buffer = "foobarFOOBAR";
return BytesConsumer::Result::kOk;
}));
EXPECT_CALL(*mock_bytes_consumer, EndRead(10u))
.WillOnce(Return(BytesConsumer::Result::kOk));
EXPECT_CALL(*mock_bytes_consumer, BeginRead(_, _))
.WillOnce(Invoke([](const char** buffer, size_t* size) {
*size = 2;
*buffer = "AR";
return BytesConsumer::Result::kOk;
}));
EXPECT_CALL(*mock_bytes_consumer, EndRead(0u))
.WillOnce(Return(BytesConsumer::Result::kOk));
EXPECT_CALL(checkpoint, Call(3));
EXPECT_CALL(checkpoint, Call(4));
EXPECT_CALL(*mock_bytes_consumer, BeginRead(_, _))
.WillOnce(Invoke([](const char** buffer, size_t* size) {
*size = 2;
*buffer = "AR";
return BytesConsumer::Result::kOk;
}));
EXPECT_CALL(*mock_bytes_consumer, EndRead(2u))
.WillOnce(Return(BytesConsumer::Result::kDone));
EXPECT_CALL(*mock_bytes_consumer, Cancel());
EXPECT_CALL(get_size_callback, Run(net::OK, 12u));
}
checkpoint.Call(1);
InitializeBytesUploader(mock_bytes_consumer, 10u);
Remote()->GetSize(get_size_callback.Get());
Remote()->StartReading(std::move(Writable()));
checkpoint.Call(2);
test::RunPendingTasks();
checkpoint.Call(3);
char buffer[20] = {};
size_t num_bytes = sizeof(buffer);
EXPECT_EQ(MOJO_RESULT_OK,
Readable()->ReadData(buffer, &num_bytes, MOJO_READ_DATA_FLAG_NONE));
EXPECT_EQ(10u, num_bytes);
EXPECT_STREQ("foobarFOOB", buffer);
checkpoint.Call(4);
test::RunPendingTasks();
char buffer2[20] = {};
num_bytes = sizeof(buffer2);
EXPECT_EQ(MOJO_RESULT_OK, Readable()->ReadData(buffer2, &num_bytes,
MOJO_READ_DATA_FLAG_NONE));
EXPECT_EQ(2u, num_bytes);
EXPECT_STREQ("AR", buffer2);
}
TEST_F(BytesUploaderTest, StartReadingWithoutGetSize) {
auto* mock_bytes_consumer =
MakeGarbageCollected<NiceMock<MockBytesConsumer>>();
InitializeBytesUploader(mock_bytes_consumer);
Remote()->StartReading(std::move(Writable()));
test::RunPendingTasks();
// The operation is rejected, and the connection is shut down.
EXPECT_FALSE(Remote().is_connected());
}
} // namespace blink