Index: webrtc/p2p/quic/reliablequicstream_unittest.cc |
diff --git a/webrtc/p2p/quic/reliablequicstream_unittest.cc b/webrtc/p2p/quic/reliablequicstream_unittest.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..f9fb5cd7cd63b7cd7eea04c4c2b9e1c8d59ee5a2 |
--- /dev/null |
+++ b/webrtc/p2p/quic/reliablequicstream_unittest.cc |
@@ -0,0 +1,255 @@ |
+/* |
+ * Copyright 2016 The WebRTC Project Authors. All rights reserved. |
+ * |
+ * Use of this source code is governed by a BSD-style license |
+ * that can be found in the LICENSE file in the root of the source |
+ * tree. An additional intellectual property rights grant can be found |
+ * in the file PATENTS. All contributing project authors may |
+ * be found in the AUTHORS file in the root of the source tree. |
+ */ |
+ |
+#include "webrtc/p2p/quic/reliablequicstream.h" |
+ |
+#include <string> |
+ |
+#include "net/base/ip_address_number.h" |
+#include "net/quic/quic_connection.h" |
+#include "net/quic/quic_protocol.h" |
+#include "net/quic/quic_session.h" |
+#include "webrtc/base/buffer.h" |
+#include "webrtc/base/gunit.h" |
+#include "webrtc/base/sigslot.h" |
+#include "webrtc/base/stream.h" |
+#include "webrtc/p2p/quic/quicconnectionhelper.h" |
+ |
+using cricket::QuicConnectionHelper; |
+using cricket::ReliableQuicStream; |
+ |
+using net::FecProtection; |
+using net::IPAddressNumber; |
+using net::IPEndPoint; |
+using net::Perspective; |
+using net::QuicAckListenerInterface; |
+using net::QuicConfig; |
+using net::QuicConnection; |
+using net::QuicConsumedData; |
+using net::QuicCryptoStream; |
+using net::QuicErrorCode; |
+using net::QuicIOVector; |
+using net::QuicPacketWriter; |
+using net::QuicRstStreamErrorCode; |
+using net::QuicSession; |
+using net::QuicStreamId; |
+using net::QuicStreamOffset; |
+using net::SpdyPriority; |
+ |
+using rtc::SR_SUCCESS; |
+using rtc::SR_BLOCK; |
+ |
+// QuicSession that does not create streams and writes data from |
+// ReliableQuicStream to a string. |
+class MockQuicSession : public QuicSession { |
+ public: |
+ MockQuicSession(QuicConnection* connection, |
+ const QuicConfig& config, |
+ std::string* write_buffer) |
+ : QuicSession(connection, config), write_buffer_(write_buffer) {} |
+ |
+ // Writes outgoing data from ReliableQuicStream to a string. |
+ QuicConsumedData WritevData( |
+ QuicStreamId id, |
+ QuicIOVector iovector, |
+ QuicStreamOffset offset, |
+ bool fin, |
+ FecProtection fec_protection, |
+ 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, false); |
+ } |
+ |
+ net::ReliableQuicStream* CreateIncomingDynamicStream( |
+ QuicStreamId id) override { |
+ return nullptr; |
+ } |
+ |
+ net::ReliableQuicStream* CreateOutgoingDynamicStream( |
+ SpdyPriority priority) override { |
+ return nullptr; |
+ } |
+ |
+ QuicCryptoStream* GetCryptoStream() override { return nullptr; } |
+ |
+ // Called by ReliableQuicStream 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 register_write_blocked_stream(QuicStreamId stream_id, |
+ SpdyPriority priority) { |
+ write_blocked_streams()->RegisterStream(stream_id, priority); |
+ } |
+ |
+ private: |
+ // Stores written data from ReliableQuicStream. |
+ 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. |
+ virtual net::WriteResult WritePacket(const char* buffer, |
+ size_t buf_len, |
+ const IPAddressNumber& self_address, |
+ const IPEndPoint& peer_address) { |
+ return net::WriteResult(net::WRITE_STATUS_ERROR, 0); |
+ } |
+ |
+ bool IsWriteBlockedDataBuffered() const override { return false; } |
+ |
+ bool IsWriteBlocked() const override { return false; }; |
+ |
+ void SetWritable() override {} |
+ |
+ net::QuicByteCount GetMaxPacketSize( |
+ const net::IPEndPoint& peer_address) const override { |
+ return 0; |
+ } |
+}; |
+ |
+// QuicPacketWriter is not necessary, so this creates a packet writer that |
+// doesn't do anything. |
+class DummyPacketWriterFactory : public QuicConnection::PacketWriterFactory { |
+ public: |
+ DummyPacketWriterFactory() {} |
+ |
+ QuicPacketWriter* Create(QuicConnection* connection) const override { |
+ return new DummyPacketWriter(); |
+ } |
+}; |
+ |
+class ReliableQuicStreamTest : public ::testing::Test, |
+ public sigslot::has_slots<> { |
+ public: |
+ ReliableQuicStreamTest() {} |
+ |
+ void CreateReliableQuicStream() { |
+ const net::QuicStreamId kStreamId = 5; |
+ |
+ // Arbitrary values for QuicConnection. |
+ QuicConnectionHelper* quic_helper = |
+ new QuicConnectionHelper(rtc::Thread::Current()); |
+ Perspective perspective = Perspective::IS_SERVER; |
+ net::IPAddressNumber ip(net::kIPv4AddressSize, 0); |
+ |
+ bool owns_writer = false; |
+ |
+ QuicConnection* connection = new QuicConnection( |
+ 0, IPEndPoint(ip, 0), quic_helper, DummyPacketWriterFactory(), |
+ owns_writer, perspective, net::QuicSupportedVersions()); |
+ |
+ session_.reset( |
+ new MockQuicSession(connection, QuicConfig(), &write_buffer_)); |
+ stream_.reset(new ReliableQuicStream(kStreamId, session_.get())); |
+ stream_->SignalDataReceived.connect( |
+ this, &ReliableQuicStreamTest::OnDataReceived); |
+ stream_->SignalClosed.connect(this, &ReliableQuicStreamTest::OnClosed); |
+ |
+ session_->register_write_blocked_stream(stream_->id(), stream_->Priority()); |
+ } |
+ |
+ void OnDataReceived(QuicStreamId id, const char* data, size_t length) { |
+ ASSERT_EQ(id, stream_->id()); |
+ read_buffer_.append(data, length); |
+ } |
+ |
+ void OnClosed(QuicStreamId id, QuicErrorCode err) { closed_ = true; } |
+ |
+ protected: |
+ scoped_ptr<ReliableQuicStream> stream_; |
+ scoped_ptr<MockQuicSession> session_; |
+ |
+ // Data written by the ReliableQuicStream. |
+ std::string write_buffer_; |
+ // Data read by the ReliableQuicStream. |
+ std::string read_buffer_; |
+ // Whether the ReliableQuicStream is closed. |
+ bool closed_ = false; |
+}; |
+ |
+// Write an entire string. |
+TEST_F(ReliableQuicStreamTest, WriteDataWhole) { |
+ CreateReliableQuicStream(); |
+ EXPECT_EQ(SR_SUCCESS, stream_->Write("Foo bar", 7)); |
+ |
+ EXPECT_EQ("Foo bar", write_buffer_); |
+} |
+ |
+// Write part of a string. |
+TEST_F(ReliableQuicStreamTest, WriteDataPartial) { |
+ CreateReliableQuicStream(); |
+ EXPECT_EQ(SR_SUCCESS, stream_->Write("Hello, World!", 8)); |
+ EXPECT_EQ("Hello, W", write_buffer_); |
+} |
+ |
+// Test that strings are buffered correctly. |
+TEST_F(ReliableQuicStreamTest, BufferData) { |
+ CreateReliableQuicStream(); |
+ |
+ session_->set_writable(false); |
+ EXPECT_EQ(SR_BLOCK, stream_->Write("Foo bar", 7)); |
+ |
+ EXPECT_EQ(0ul, write_buffer_.size()); |
+ EXPECT_TRUE(stream_->HasBufferedData()); |
+ |
+ session_->set_writable(true); |
+ stream_->OnCanWrite(); |
+ |
+ EXPECT_FALSE(stream_->HasBufferedData()); |
+ EXPECT_EQ("Foo bar", write_buffer_); |
+ |
+ EXPECT_EQ(SR_SUCCESS, stream_->Write("xyzzy", 5)); |
+ EXPECT_EQ("Foo barxyzzy", write_buffer_); |
+} |
+ |
+// Read an entire string. |
+TEST_F(ReliableQuicStreamTest, ReadDataWhole) { |
+ CreateReliableQuicStream(); |
+ net::QuicStreamFrame frame(-1, false, 0, "Hello, World!"); |
+ stream_->OnStreamFrame(frame); |
+ |
+ EXPECT_EQ("Hello, World!", read_buffer_); |
+} |
+ |
+// Read part of a string. |
+TEST_F(ReliableQuicStreamTest, ReadDataPartial) { |
+ CreateReliableQuicStream(); |
+ net::QuicStreamFrame frame(-1, false, 0, "Hello, World!"); |
+ frame.frame_length = 5; |
+ stream_->OnStreamFrame(frame); |
+ |
+ EXPECT_EQ("Hello", read_buffer_); |
+} |
+ |
+// Test that closing the stream results in a callback. |
+TEST_F(ReliableQuicStreamTest, CloseStream) { |
+ CreateReliableQuicStream(); |
+ EXPECT_FALSE(closed_); |
+ stream_->OnClose(); |
+ EXPECT_TRUE(closed_); |
+} |