blob: c074be8afdc62f624c2784e52ef35157a984fd65 [file] [log] [blame]
// Copyright (c) 2016 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 "net/quic/quartc/quartc_stream.h"
#include "base/threading/thread_task_runner_handle.h"
#include "net/quic/core/crypto/quic_random.h"
#include "net/quic/core/quic_session.h"
#include "net/quic/core/quic_simple_buffer_allocator.h"
#include "net/quic/platform/impl/quic_chromium_clock.h"
#include "net/quic/quartc/quartc_alarm_factory.h"
#include "net/quic/quartc/quartc_stream_interface.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace net {
namespace test {
namespace {
static const SpdyPriority kDefaultPriority = 3;
static const QuicStreamId kStreamId = 5;
static const QuartcStreamInterface::WriteParameters kDefaultParam;
// MockQuicSession that does not create streams and writes data from
// QuicStream to a string.
class MockQuicSession : public QuicSession {
public:
MockQuicSession(QuicConnection* connection,
const QuicConfig& config,
std::string* write_buffer)
: QuicSession(connection, nullptr /*visitor*/, config),
write_buffer_(write_buffer) {}
// Writes outgoing data from QuicStream to a string.
QuicConsumedData WritevData(
QuicStream* stream,
QuicStreamId id,
QuicIOVector iovector,
QuicStreamOffset offset,
bool fin,
QuicReferenceCountedPointer<
QuicAckListenerInterface> /*ack_notifier_delegate*/) override {
if (!writable_) {
return QuicConsumedData(0, false);
}
const char* data = reinterpret_cast<const char*>(iovector.iov->iov_base);
size_t len = iovector.total_length;
write_buffer_->append(data, len);
return QuicConsumedData(len, fin);
}
QuartcStream* CreateIncomingDynamicStream(QuicStreamId id) override {
return nullptr;
}
QuartcStream* CreateOutgoingDynamicStream(SpdyPriority priority) override {
return nullptr;
}
QuicCryptoStream* GetCryptoStream() override { return nullptr; }
// Called by QuicStream when they want to close stream.
void SendRstStream(QuicStreamId id,
QuicRstStreamErrorCode error,
QuicStreamOffset bytes_written) override {}
// Sets whether data is written to buffer, or else if this is write blocked.
void set_writable(bool writable) { writable_ = writable; }
// Tracks whether the stream is write blocked and its priority.
void RegisterReliableStream(QuicStreamId stream_id, SpdyPriority priority) {
write_blocked_streams()->RegisterStream(stream_id, priority);
}
// The session take ownership of the stream.
void ActivateReliableStream(std::unique_ptr<QuicStream> stream) {
ActivateStream(std::move(stream));
}
private:
// Stores written data from ReliableQuicStreamAdapter.
std::string* write_buffer_;
// Whether data is written to write_buffer_.
bool writable_ = true;
};
// Packet writer that does nothing. This is required for QuicConnection but
// isn't used for writing data.
class DummyPacketWriter : public QuicPacketWriter {
public:
DummyPacketWriter() {}
// QuicPacketWriter overrides.
WriteResult WritePacket(const char* buffer,
size_t buf_len,
const QuicIpAddress& self_address,
const QuicSocketAddress& peer_address,
PerPacketOptions* options) override {
return WriteResult(WRITE_STATUS_ERROR, 0);
}
bool IsWriteBlockedDataBuffered() const override { return false; }
bool IsWriteBlocked() const override { return false; };
void SetWritable() override {}
QuicByteCount GetMaxPacketSize(
const QuicSocketAddress& peer_address) const override {
return 0;
}
};
class MockQuartcStreamDelegate : public QuartcStreamInterface::Delegate {
public:
MockQuartcStreamDelegate(int id, std::string& read_buffer)
: id_(id), read_buffer_(read_buffer) {}
void OnBufferedAmountDecrease(QuartcStreamInterface* stream) override {
queued_bytes_amount_ = stream->buffered_amount();
}
void OnReceived(QuartcStreamInterface* stream,
const char* data,
size_t size) override {
EXPECT_EQ(id_, stream->stream_id());
read_buffer_.append(data, size);
}
void OnClose(QuartcStreamInterface* stream, int error_code) override {
closed_ = true;
}
bool closed() { return closed_; }
int queued_bytes_amount() { return queued_bytes_amount_; }
protected:
uint32_t id_;
// Data read by the QuicStream.
std::string& read_buffer_;
// Whether the QuicStream is closed.
bool closed_ = false;
int queued_bytes_amount_ = -1;
};
class QuartcStreamTest : public ::testing::Test,
public QuicConnectionHelperInterface {
public:
void CreateReliableQuicStream() {
// Arbitrary values for QuicConnection.
Perspective perspective = Perspective::IS_SERVER;
QuicIpAddress ip;
ip.FromString("0.0.0.0");
bool owns_writer = true;
alarm_factory_.reset(new QuartcAlarmFactory(
base::ThreadTaskRunnerHandle::Get().get(), GetClock()));
connection_.reset(new QuicConnection(
0, QuicSocketAddress(ip, 0), this /*QuicConnectionHelperInterface*/,
alarm_factory_.get(), new DummyPacketWriter(), owns_writer, perspective,
AllSupportedVersions()));
session_.reset(
new MockQuicSession(connection_.get(), QuicConfig(), &write_buffer_));
mock_stream_delegate_.reset(
new MockQuartcStreamDelegate(kStreamId, read_buffer_));
stream_ = new QuartcStream(kStreamId, session_.get());
stream_->SetDelegate(mock_stream_delegate_.get());
session_->RegisterReliableStream(stream_->stream_id(), kDefaultPriority);
session_->ActivateReliableStream(std::unique_ptr<QuartcStream>(stream_));
}
const QuicClock* GetClock() const override { return &clock_; }
QuicRandom* GetRandomGenerator() override {
return QuicRandom::GetInstance();
}
QuicBufferAllocator* GetBufferAllocator() override {
return &buffer_allocator_;
}
protected:
// The QuicSession will take the ownership.
QuartcStream* stream_;
std::unique_ptr<MockQuartcStreamDelegate> mock_stream_delegate_;
std::unique_ptr<MockQuicSession> session_;
// Data written by the ReliableQuicStreamAdapterTest.
std::string write_buffer_;
// Data read by the ReliableQuicStreamAdapterTest.
std::string read_buffer_;
std::unique_ptr<QuartcAlarmFactory> alarm_factory_;
std::unique_ptr<QuicConnection> connection_;
// Used to implement the QuicConnectionHelperInterface.
SimpleBufferAllocator buffer_allocator_;
QuicChromiumClock clock_;
};
// Write an entire string.
TEST_F(QuartcStreamTest, WriteDataWhole) {
CreateReliableQuicStream();
stream_->Write("Foo bar", 7, kDefaultParam);
EXPECT_EQ("Foo bar", write_buffer_);
}
// Write part of a string.
TEST_F(QuartcStreamTest, WriteDataPartial) {
CreateReliableQuicStream();
stream_->Write("Foo bar", 5, kDefaultParam);
EXPECT_EQ("Foo b", write_buffer_);
}
// Test that strings are buffered correctly.
TEST_F(QuartcStreamTest, BufferData) {
CreateReliableQuicStream();
session_->set_writable(false);
stream_->Write("Foo bar", 7, kDefaultParam);
// The data will be buffered.
EXPECT_EQ(0ul, write_buffer_.size());
EXPECT_TRUE(stream_->HasBufferedData());
EXPECT_EQ(-1, mock_stream_delegate_->queued_bytes_amount());
// The session is writable and the buffered data amount will change.
session_->set_writable(true);
stream_->OnCanWrite();
EXPECT_EQ(0, mock_stream_delegate_->queued_bytes_amount());
EXPECT_FALSE(stream_->HasBufferedData());
EXPECT_EQ("Foo bar", write_buffer_);
stream_->Write("xyzzy", 5, kDefaultParam);
EXPECT_EQ("Foo barxyzzy", write_buffer_);
}
// Read an entire string.
TEST_F(QuartcStreamTest, ReadDataWhole) {
CreateReliableQuicStream();
QuicStreamFrame frame(kStreamId, false, 0, "Hello, World!");
stream_->OnStreamFrame(frame);
EXPECT_EQ("Hello, World!", read_buffer_);
}
// Read part of a string.
TEST_F(QuartcStreamTest, ReadDataPartial) {
CreateReliableQuicStream();
QuicStreamFrame frame(kStreamId, false, 0, "Hello, World!");
frame.data_length = 5;
stream_->OnStreamFrame(frame);
EXPECT_EQ("Hello", read_buffer_);
}
// Test that closing the stream results in a callback.
TEST_F(QuartcStreamTest, CloseStream) {
CreateReliableQuicStream();
EXPECT_FALSE(mock_stream_delegate_->closed());
stream_->OnClose();
EXPECT_TRUE(mock_stream_delegate_->closed());
}
} // namespace
} // namespace test
} // namespace net