blob: dc8c30c52d6745820a06194997b5f8f656a9cfc5 [file] [log] [blame]
// Copyright 2018 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/loader/data_pipe_to_source_stream.h"
#include "base/strings/string_piece.h"
#include "base/test/scoped_task_environment.h"
#include "net/base/io_buffer.h"
#include "net/base/test_completion_callback.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace content {
namespace {
const int kBigBufferSize = 4096;
const int kSmallBufferSize = 1;
const int kBigPipeCapacity = 4096;
const int kSmallPipeCapacity = 1;
enum class WriteMode {
// WriteToPipe tries to write as many bytes as possible.
EVERYTHING_AT_ONCE,
// WriteToPipe writes at most one byte at a time.
ONE_BYTE_AT_A_TIME,
};
struct DataPipeToSourceStreamTestParam {
DataPipeToSourceStreamTestParam(uint32_t pipe_capacity,
int buffer_size,
WriteMode write_mode)
: pipe_capacity(pipe_capacity),
buffer_size(buffer_size),
write_mode(write_mode) {}
const uint32_t pipe_capacity;
const int buffer_size;
const WriteMode write_mode;
};
} // namespace
class DataPipeToSourceStreamTest
: public ::testing::TestWithParam<DataPipeToSourceStreamTestParam> {
protected:
DataPipeToSourceStreamTest()
: output_buffer_(base::MakeRefCounted<net::IOBufferWithSize>(
GetParam().buffer_size)) {}
void Init(base::StringPiece message) {
message_ = message;
const MojoCreateDataPipeOptions data_pipe_options{
sizeof(MojoCreateDataPipeOptions), MOJO_CREATE_DATA_PIPE_FLAG_NONE, 1,
GetParam().pipe_capacity};
mojo::ScopedDataPipeConsumerHandle consumer_end;
CHECK_EQ(MOJO_RESULT_OK,
mojo::CreateDataPipe(&data_pipe_options, &producer_end_,
&consumer_end));
adapter_ =
std::make_unique<DataPipeToSourceStream>(std::move(consumer_end));
}
// Makes a call to |producer_end_->WriteData| to write the content of
// |message_| to the data pipe, and closes the producer handle once all data
// has been written.
void WriteToPipe() {
if (message_.empty()) {
if (producer_end_.is_valid())
CloseProducerHandle();
return;
}
bool one_byte_at_a_time =
GetParam().write_mode == WriteMode::ONE_BYTE_AT_A_TIME;
uint32_t num_bytes = one_byte_at_a_time ? 1 : message_.size();
MojoResult result = producer_end_->WriteData(message_.data(), &num_bytes,
MOJO_WRITE_DATA_FLAG_NONE);
if (result == MOJO_RESULT_OK) {
message_.remove_prefix(num_bytes);
if (message_.empty())
CloseProducerHandle();
return;
}
EXPECT_EQ(result, MOJO_RESULT_SHOULD_WAIT);
}
// Reads from |adapter_| until an error occurs or the EOF is reached.
// When an error occurs, returns the net error code. When an EOF is reached,
// returns 0 and appends data read to |output|.
int ReadLoop(std::string* output) {
while (true) {
net::TestCompletionCallback callback;
int rv = adapter_->Read(output_buffer_.get(), output_buffer_->size(),
callback.callback());
if (rv == net::ERR_IO_PENDING) {
WriteToPipe();
rv = callback.WaitForResult();
}
if (rv == net::OK)
break;
if (rv < net::OK)
return rv;
EXPECT_GT(rv, net::OK);
output->append(output_buffer_->data(), rv);
}
return 0;
}
mojo::DataPipeProducerHandle producer_end() { return producer_end_.get(); }
void CloseProducerHandle() { producer_end_.reset(); }
void CloseAdapter() { adapter_ = nullptr; }
private:
base::test::ScopedTaskEnvironment scoped_task_environment_;
scoped_refptr<net::IOBufferWithSize> output_buffer_;
std::unique_ptr<DataPipeToSourceStream> adapter_;
mojo::ScopedDataPipeProducerHandle producer_end_;
base::StringPiece message_;
};
INSTANTIATE_TEST_CASE_P(
DataPipeToSourceStreamTests,
DataPipeToSourceStreamTest,
::testing::Values(
DataPipeToSourceStreamTestParam(kBigPipeCapacity,
kBigBufferSize,
WriteMode::EVERYTHING_AT_ONCE),
DataPipeToSourceStreamTestParam(kSmallPipeCapacity,
kBigBufferSize,
WriteMode::EVERYTHING_AT_ONCE),
DataPipeToSourceStreamTestParam(kBigPipeCapacity,
kSmallBufferSize,
WriteMode::EVERYTHING_AT_ONCE),
DataPipeToSourceStreamTestParam(kSmallPipeCapacity,
kSmallBufferSize,
WriteMode::EVERYTHING_AT_ONCE),
DataPipeToSourceStreamTestParam(kBigPipeCapacity,
kBigBufferSize,
WriteMode::ONE_BYTE_AT_A_TIME),
DataPipeToSourceStreamTestParam(kSmallPipeCapacity,
kBigBufferSize,
WriteMode::ONE_BYTE_AT_A_TIME),
DataPipeToSourceStreamTestParam(kBigPipeCapacity,
kSmallBufferSize,
WriteMode::ONE_BYTE_AT_A_TIME),
DataPipeToSourceStreamTestParam(kSmallPipeCapacity,
kSmallBufferSize,
WriteMode::ONE_BYTE_AT_A_TIME)));
TEST_P(DataPipeToSourceStreamTest, EmptyStream) {
Init("");
std::string output;
EXPECT_EQ(ReadLoop(&output), net::OK);
EXPECT_TRUE(output.empty());
}
TEST_P(DataPipeToSourceStreamTest, Simple) {
const char message[] = "Hello, world!";
Init(message);
std::string output;
EXPECT_EQ(ReadLoop(&output), net::OK);
EXPECT_EQ(output, message);
}
TEST_P(DataPipeToSourceStreamTest, DestructorClosesConsumerEnd) {
const char message[] = "Hello, world!";
Init(message);
CloseAdapter();
uint32_t num_bytes = sizeof(message) - 1;
MojoResult result =
producer_end().WriteData(message, &num_bytes, MOJO_WRITE_DATA_FLAG_NONE);
EXPECT_EQ(result, MOJO_RESULT_FAILED_PRECONDITION);
}
} // namespace content