Add a simple QuicTransport server for testing and demo purposes.
The server currently has two modes, echo and discard. I've changed the integration test to use the simple server instead of a session with a mock visitor.
This CL also adds some of the missing APIs and fixes some of the bugs I've found while doing this.
gfe-relnote: n/a (code not used)
PiperOrigin-RevId: 278456752
Change-Id: Idf9aa654aa0d66673f300f2f5425f0716d6c3e14
diff --git a/quic/quic_transport/quic_transport_client_session.cc b/quic/quic_transport/quic_transport_client_session.cc
index f738371..72ed9c5 100644
--- a/quic/quic_transport/quic_transport_client_session.cc
+++ b/quic/quic_transport/quic_transport_client_session.cc
@@ -16,9 +16,11 @@
#include "net/third_party/quiche/src/quic/core/quic_session.h"
#include "net/third_party/quiche/src/quic/core/quic_types.h"
#include "net/third_party/quiche/src/quic/core/quic_versions.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+#include "net/third_party/quiche/src/quic/quic_transport/quic_transport_protocol.h"
#include "net/third_party/quiche/src/quic/quic_transport/quic_transport_stream.h"
namespace quic {
@@ -65,17 +67,15 @@
QuicStream* QuicTransportClientSession::CreateIncomingStream(QuicStreamId id) {
QUIC_DVLOG(1) << "Creating incoming QuicTransport stream " << id;
- auto stream = std::make_unique<QuicTransportStream>(id, this, this);
- QuicTransportStream* stream_ptr = stream.get();
- ActivateStream(std::move(stream));
- if (stream_ptr->type() == BIDIRECTIONAL) {
- incoming_bidirectional_streams_.push_back(stream_ptr);
+ QuicTransportStream* stream = CreateStream(id);
+ if (stream->type() == BIDIRECTIONAL) {
+ incoming_bidirectional_streams_.push_back(stream);
visitor_->OnIncomingBidirectionalStreamAvailable();
} else {
- incoming_unidirectional_streams_.push_back(stream_ptr);
+ incoming_unidirectional_streams_.push_back(stream);
visitor_->OnIncomingUnidirectionalStreamAvailable();
}
- return stream_ptr;
+ return stream;
}
void QuicTransportClientSession::OnCryptoHandshakeEvent(
@@ -108,6 +108,31 @@
return stream;
}
+QuicTransportStream*
+QuicTransportClientSession::OpenOutgoingBidirectionalStream() {
+ if (!CanOpenNextOutgoingBidirectionalStream()) {
+ QUIC_BUG << "Attempted to open a stream in violation of flow control";
+ return nullptr;
+ }
+ return CreateStream(GetNextOutgoingBidirectionalStreamId());
+}
+
+QuicTransportStream*
+QuicTransportClientSession::OpenOutgoingUnidirectionalStream() {
+ if (!CanOpenNextOutgoingUnidirectionalStream()) {
+ QUIC_BUG << "Attempted to open a stream in violation of flow control";
+ return nullptr;
+ }
+ return CreateStream(GetNextOutgoingUnidirectionalStreamId());
+}
+
+QuicTransportStream* QuicTransportClientSession::CreateStream(QuicStreamId id) {
+ auto stream = std::make_unique<QuicTransportStream>(id, this, this);
+ QuicTransportStream* stream_ptr = stream.get();
+ ActivateStream(std::move(stream));
+ return stream_ptr;
+}
+
std::string QuicTransportClientSession::SerializeClientIndication() {
std::string serialized_origin = origin_.Serialize();
if (serialized_origin.size() > std::numeric_limits<uint16_t>::max()) {
@@ -151,8 +176,11 @@
}
auto client_indication_owned = std::make_unique<ClientIndication>(
- /*stream_id=*/ClientIndicationStream(), this, /*is_static=*/false,
- WRITE_UNIDIRECTIONAL);
+ /*stream_id=*/GetNextOutgoingUnidirectionalStreamId(), this,
+ /*is_static=*/false, WRITE_UNIDIRECTIONAL);
+ QUIC_BUG_IF(client_indication_owned->id() != ClientIndicationStream())
+ << "Client indication stream is " << client_indication_owned->id()
+ << " instead of expected " << ClientIndicationStream();
ClientIndication* client_indication = client_indication_owned.get();
ActivateStream(std::move(client_indication_owned));
diff --git a/quic/quic_transport/quic_transport_client_session.h b/quic/quic_transport/quic_transport_client_session.h
index 12e5fdf..4df008c 100644
--- a/quic/quic_transport/quic_transport_client_session.h
+++ b/quic/quic_transport/quic_transport_client_session.h
@@ -88,6 +88,11 @@
QuicTransportStream* AcceptIncomingBidirectionalStream();
QuicTransportStream* AcceptIncomingUnidirectionalStream();
+ using QuicSession::CanOpenNextOutgoingBidirectionalStream;
+ using QuicSession::CanOpenNextOutgoingUnidirectionalStream;
+ QuicTransportStream* OpenOutgoingBidirectionalStream();
+ QuicTransportStream* OpenOutgoingUnidirectionalStream();
+
protected:
class QUIC_EXPORT_PRIVATE ClientIndication : public QuicStream {
public:
@@ -100,6 +105,9 @@
}
};
+ // Creates and activates a QuicTransportStream for the given ID.
+ QuicTransportStream* CreateStream(QuicStreamId id);
+
// Serializes the client indication as described in
// https://vasilvv.github.io/webtransport/draft-vvv-webtransport-quic.html#rfc.section.3.2
std::string SerializeClientIndication();
diff --git a/quic/quic_transport/quic_transport_integration_test.cc b/quic/quic_transport/quic_transport_integration_test.cc
index 9936830..dfccb14 100644
--- a/quic/quic_transport/quic_transport_integration_test.cc
+++ b/quic/quic_transport/quic_transport_integration_test.cc
@@ -6,6 +6,7 @@
// server sessions.
#include <memory>
+#include <vector>
#include "url/gurl.h"
#include "url/origin.h"
@@ -15,9 +16,11 @@
#include "net/third_party/quiche/src/quic/core/quic_error_codes.h"
#include "net/third_party/quiche/src/quic/core/quic_types.h"
#include "net/third_party/quiche/src/quic/core/quic_versions.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
#include "net/third_party/quiche/src/quic/quic_transport/quic_transport_client_session.h"
#include "net/third_party/quiche/src/quic/quic_transport/quic_transport_server_session.h"
+#include "net/third_party/quiche/src/quic/quic_transport/quic_transport_stream.h"
#include "net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h"
#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
#include "net/third_party/quiche/src/quic/test_tools/quic_transport_test_tools.h"
@@ -25,6 +28,7 @@
#include "net/third_party/quiche/src/quic/test_tools/simulator/quic_endpoint_base.h"
#include "net/third_party/quiche/src/quic/test_tools/simulator/simulator.h"
#include "net/third_party/quiche/src/quic/test_tools/simulator/switch.h"
+#include "net/third_party/quiche/src/quic/tools/quic_transport_simple_server_session.h"
namespace quic {
namespace test {
@@ -32,8 +36,7 @@
using simulator::QuicEndpointBase;
using simulator::Simulator;
-using testing::_;
-using testing::Return;
+using testing::Assign;
url::Origin GetTestOrigin() {
constexpr char kTestOrigin[] = "https://test-origin.test";
@@ -85,6 +88,7 @@
}
QuicTransportClientSession* session() { return &session_; }
+ MockClientVisitor* visitor() { return &visitor_; }
private:
QuicCryptoClientConfig crypto_config_;
@@ -96,7 +100,9 @@
public:
QuicTransportServerEndpoint(Simulator* simulator,
const std::string& name,
- const std::string& peer_name)
+ const std::string& peer_name,
+ QuicTransportSimpleServerSession::Mode mode,
+ std::vector<url::Origin> accepted_origins)
: QuicTransportEndpointBase(simulator,
name,
peer_name,
@@ -108,25 +114,31 @@
compressed_certs_cache_(
QuicCompressedCertsCache::kQuicCompressedCertsCacheSize),
session_(connection_.get(),
+ /*owns_connection=*/false,
nullptr,
DefaultQuicConfig(),
GetVersions(),
&crypto_config_,
&compressed_certs_cache_,
- &visitor_) {
+ mode,
+ accepted_origins) {
session_.Initialize();
}
QuicTransportServerSession* session() { return &session_; }
- MockServerVisitor* visitor() { return &visitor_; }
private:
QuicCryptoServerConfig crypto_config_;
QuicCompressedCertsCache compressed_certs_cache_;
- QuicTransportServerSession session_;
- MockServerVisitor visitor_;
+ QuicTransportSimpleServerSession session_;
};
+std::unique_ptr<MockStreamVisitor> VisitorExpectingFin() {
+ auto visitor = std::make_unique<MockStreamVisitor>();
+ EXPECT_CALL(*visitor, OnFinRead());
+ return visitor;
+}
+
constexpr QuicBandwidth kClientBandwidth =
QuicBandwidth::FromKBitsPerSecond(10000);
constexpr QuicTime::Delta kClientPropagationDelay =
@@ -142,19 +154,18 @@
(kClientPropagationDelay + kServerPropagationDelay + kTransferTime) * 2;
const QuicByteCount kBdp = kRtt * kServerBandwidth;
-constexpr QuicTime::Delta kHandshakeTimeout = QuicTime::Delta::FromSeconds(3);
+constexpr QuicTime::Delta kDefaultTimeout = QuicTime::Delta::FromSeconds(3);
class QuicTransportIntegrationTest : public QuicTest {
public:
QuicTransportIntegrationTest()
: switch_(&simulator_, "Switch", 8, 2 * kBdp) {}
- void CreateDefaultEndpoints() {
+ void CreateDefaultEndpoints(QuicTransportSimpleServerSession::Mode mode) {
client_ = std::make_unique<QuicTransportClientEndpoint>(
&simulator_, "Client", "Server", GetTestOrigin());
- server_ = std::make_unique<QuicTransportServerEndpoint>(&simulator_,
- "Server", "Client");
- ON_CALL(*server_->visitor(), CheckOrigin(_)).WillByDefault(Return(true));
+ server_ = std::make_unique<QuicTransportServerEndpoint>(
+ &simulator_, "Server", "Client", mode, accepted_origins_);
}
void WireUpEndpoints() {
@@ -173,7 +184,7 @@
return IsHandshakeDone(client_->session()) &&
IsHandshakeDone(server_->session());
},
- kHandshakeTimeout);
+ kDefaultTimeout);
EXPECT_TRUE(result);
}
@@ -190,10 +201,12 @@
std::unique_ptr<QuicTransportClientEndpoint> client_;
std::unique_ptr<QuicTransportServerEndpoint> server_;
+
+ std::vector<url::Origin> accepted_origins_ = {GetTestOrigin()};
};
TEST_F(QuicTransportIntegrationTest, SuccessfulHandshake) {
- CreateDefaultEndpoints();
+ CreateDefaultEndpoints(QuicTransportSimpleServerSession::DISCARD);
WireUpEndpoints();
RunHandshake();
EXPECT_TRUE(client_->session()->IsSessionReady());
@@ -201,15 +214,14 @@
}
TEST_F(QuicTransportIntegrationTest, OriginMismatch) {
- CreateDefaultEndpoints();
+ accepted_origins_ = {url::Origin::Create(GURL{"https://wrong-origin.test"})};
+ CreateDefaultEndpoints(QuicTransportSimpleServerSession::DISCARD);
WireUpEndpoints();
- EXPECT_CALL(*server_->visitor(), CheckOrigin(_))
- .WillRepeatedly(Return(false));
RunHandshake();
// Wait until the client receives CONNECTION_CLOSE.
simulator_.RunUntilOrTimeout(
[this]() { return !client_->session()->connection()->connected(); },
- kHandshakeTimeout);
+ kDefaultTimeout);
EXPECT_TRUE(client_->session()->IsSessionReady());
EXPECT_FALSE(server_->session()->IsSessionReady());
EXPECT_FALSE(client_->session()->connection()->connected());
@@ -220,6 +232,100 @@
QUIC_TRANSPORT_INVALID_CLIENT_INDICATION);
}
+TEST_F(QuicTransportIntegrationTest, SendOutgoingStreams) {
+ CreateDefaultEndpoints(QuicTransportSimpleServerSession::DISCARD);
+ WireUpEndpoints();
+ RunHandshake();
+
+ std::vector<QuicTransportStream*> streams;
+ for (int i = 0; i < 10; i++) {
+ QuicTransportStream* stream =
+ client_->session()->OpenOutgoingUnidirectionalStream();
+ ASSERT_TRUE(stream->Write("test"));
+ streams.push_back(stream);
+ }
+ ASSERT_TRUE(simulator_.RunUntilOrTimeout(
+ [this]() {
+ return server_->session()->GetNumOpenIncomingStreams() == 10;
+ },
+ kDefaultTimeout));
+
+ for (QuicTransportStream* stream : streams) {
+ ASSERT_TRUE(stream->SendFin());
+ }
+ ASSERT_TRUE(simulator_.RunUntilOrTimeout(
+ [this]() { return server_->session()->GetNumOpenIncomingStreams() == 0; },
+ kDefaultTimeout));
+}
+
+TEST_F(QuicTransportIntegrationTest, EchoBidirectionalStreams) {
+ CreateDefaultEndpoints(QuicTransportSimpleServerSession::ECHO);
+ WireUpEndpoints();
+ RunHandshake();
+
+ QuicTransportStream* stream =
+ client_->session()->OpenOutgoingBidirectionalStream();
+ EXPECT_TRUE(stream->Write("Hello!"));
+
+ ASSERT_TRUE(simulator_.RunUntilOrTimeout(
+ [stream]() { return stream->ReadableBytes() == strlen("Hello!"); },
+ kDefaultTimeout));
+ std::string received;
+ EXPECT_EQ(stream->Read(&received), strlen("Hello!"));
+ EXPECT_EQ(received, "Hello!");
+
+ EXPECT_TRUE(stream->SendFin());
+ ASSERT_TRUE(simulator_.RunUntilOrTimeout(
+ [this]() { return server_->session()->GetNumOpenIncomingStreams() == 0; },
+ kDefaultTimeout));
+}
+
+TEST_F(QuicTransportIntegrationTest, EchoUnidirectionalStreams) {
+ CreateDefaultEndpoints(QuicTransportSimpleServerSession::ECHO);
+ WireUpEndpoints();
+ RunHandshake();
+
+ // Send two streams, but only send FIN on the second one.
+ QuicTransportStream* stream1 =
+ client_->session()->OpenOutgoingUnidirectionalStream();
+ EXPECT_TRUE(stream1->Write("Stream One"));
+ QuicTransportStream* stream2 =
+ client_->session()->OpenOutgoingUnidirectionalStream();
+ EXPECT_TRUE(stream2->Write("Stream Two"));
+ EXPECT_TRUE(stream2->SendFin());
+
+ // Wait until a stream is received.
+ bool stream_received = false;
+ EXPECT_CALL(*client_->visitor(), OnIncomingUnidirectionalStreamAvailable())
+ .Times(2)
+ .WillRepeatedly(Assign(&stream_received, true));
+ ASSERT_TRUE(simulator_.RunUntilOrTimeout(
+ [&stream_received]() { return stream_received; }, kDefaultTimeout));
+
+ // Receive a reply stream and expect it to be the second one.
+ QuicTransportStream* reply =
+ client_->session()->AcceptIncomingUnidirectionalStream();
+ ASSERT_TRUE(reply != nullptr);
+ std::string buffer;
+ reply->set_visitor(VisitorExpectingFin());
+ EXPECT_GT(reply->Read(&buffer), 0u);
+ EXPECT_EQ(buffer, "Stream Two");
+
+ // Reset reply-related variables.
+ stream_received = false;
+ buffer = "";
+
+ // Send FIN on the first stream, and expect to receive it back.
+ EXPECT_TRUE(stream1->SendFin());
+ ASSERT_TRUE(simulator_.RunUntilOrTimeout(
+ [&stream_received]() { return stream_received; }, kDefaultTimeout));
+ reply = client_->session()->AcceptIncomingUnidirectionalStream();
+ ASSERT_TRUE(reply != nullptr);
+ reply->set_visitor(VisitorExpectingFin());
+ EXPECT_GT(reply->Read(&buffer), 0u);
+ EXPECT_EQ(buffer, "Stream One");
+}
+
} // namespace
} // namespace test
} // namespace quic
diff --git a/quic/quic_transport/quic_transport_server_session.cc b/quic/quic_transport/quic_transport_server_session.cc
index fe4aec1..7f00acd 100644
--- a/quic/quic_transport/quic_transport_server_session.cc
+++ b/quic/quic_transport/quic_transport_server_session.cc
@@ -66,6 +66,7 @@
auto stream = std::make_unique<QuicTransportStream>(id, this, this);
QuicTransportStream* stream_ptr = stream.get();
ActivateStream(std::move(stream));
+ OnIncomingDataStream(stream_ptr);
return stream_ptr;
}
diff --git a/quic/quic_transport/quic_transport_server_session.h b/quic/quic_transport/quic_transport_server_session.h
index c488775..b3fcfa0 100644
--- a/quic/quic_transport/quic_transport_server_session.h
+++ b/quic/quic_transport/quic_transport_server_session.h
@@ -12,6 +12,7 @@
#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
#include "net/third_party/quiche/src/quic/quic_transport/quic_transport_protocol.h"
#include "net/third_party/quiche/src/quic/quic_transport/quic_transport_session_interface.h"
+#include "net/third_party/quiche/src/quic/quic_transport/quic_transport_stream.h"
namespace quic {
@@ -97,6 +98,8 @@
// https://vasilvv.github.io/webtransport/draft-vvv-webtransport-quic.html#rfc.section.3.2
void ProcessClientIndication(QuicStringPiece indication);
+ virtual void OnIncomingDataStream(QuicTransportStream* /*stream*/) {}
+
std::unique_ptr<QuicCryptoServerStream> crypto_stream_;
bool ready_ = false;
ServerVisitor* visitor_;
diff --git a/quic/quic_transport/quic_transport_stream.cc b/quic/quic_transport/quic_transport_stream.cc
index 9546119..61f4345 100644
--- a/quic/quic_transport/quic_transport_stream.cc
+++ b/quic/quic_transport/quic_transport_stream.cc
@@ -32,7 +32,21 @@
iovec iov;
iov.iov_base = buffer;
iov.iov_len = buffer_size;
- return sequencer()->Readv(&iov, 1);
+ const size_t result = sequencer()->Readv(&iov, 1);
+ if (sequencer()->IsClosed() && visitor_ != nullptr) {
+ visitor_->OnFinRead();
+ }
+ return result;
+}
+
+size_t QuicTransportStream::Read(std::string* output) {
+ const size_t old_size = output->size();
+ const size_t bytes_to_read = ReadableBytes();
+ output->resize(old_size + bytes_to_read);
+ size_t bytes_read = Read(&(*output)[old_size], bytes_to_read);
+ DCHECK_EQ(bytes_to_read, bytes_read);
+ output->resize(old_size + bytes_read);
+ return bytes_read;
}
bool QuicTransportStream::Write(QuicStringPiece data) {
@@ -40,6 +54,7 @@
return false;
}
+ // TODO(vasilvv): use WriteMemSlices()
WriteOrBufferData(data, /*fin=*/false, nullptr);
return true;
}
@@ -66,12 +81,21 @@
}
void QuicTransportStream::OnDataAvailable() {
+ if (sequencer()->IsClosed()) {
+ if (visitor_ != nullptr) {
+ visitor_->OnFinRead();
+ }
+ OnFinRead();
+ return;
+ }
+
+ if (visitor_ == nullptr) {
+ return;
+ }
if (ReadableBytes() == 0) {
return;
}
- if (visitor_ != nullptr) {
- visitor_->OnCanRead();
- }
+ visitor_->OnCanRead();
}
void QuicTransportStream::OnCanWriteNewData() {
diff --git a/quic/quic_transport/quic_transport_stream.h b/quic/quic_transport/quic_transport_stream.h
index 44694db..1651a1c 100644
--- a/quic/quic_transport/quic_transport_stream.h
+++ b/quic/quic_transport/quic_transport_stream.h
@@ -6,6 +6,7 @@
#define QUICHE_QUIC_QUIC_TRANSPORT_QUIC_TRANSPORT_STREAM_H_
#include <cstddef>
+#include <memory>
#include "net/third_party/quiche/src/quic/core/quic_session.h"
#include "net/third_party/quiche/src/quic/core/quic_stream.h"
@@ -25,6 +26,7 @@
public:
virtual ~Visitor() {}
virtual void OnCanRead() = 0;
+ virtual void OnFinRead() = 0;
virtual void OnCanWrite() = 0;
};
@@ -35,6 +37,8 @@
// Reads at most |buffer_size| bytes into |buffer| and returns the number of
// bytes actually read.
size_t Read(char* buffer, size_t buffer_size);
+ // Reads all available data and appends it to the end of |output|.
+ size_t Read(std::string* output);
// Writes |data| into the stream. Returns true on success.
QUIC_MUST_USE_RESULT bool Write(QuicStringPiece data);
// Sends the FIN on the stream. Returns true on success.
@@ -49,11 +53,18 @@
void OnDataAvailable() override;
void OnCanWriteNewData() override;
- void set_visitor(Visitor* visitor) { visitor_ = visitor; }
+ Visitor* visitor() { return visitor_.get(); }
+ void set_visitor(std::unique_ptr<Visitor> visitor) {
+ visitor_ = std::move(visitor);
+ }
protected:
+ // Hide the methods that allow writing data without checking IsSessionReady().
+ using QuicStream::WriteMemSlices;
+ using QuicStream::WriteOrBufferData;
+
QuicTransportSessionInterface* session_interface_;
- Visitor* visitor_ = nullptr;
+ std::unique_ptr<Visitor> visitor_ = nullptr;
};
} // namespace quic
diff --git a/quic/quic_transport/quic_transport_stream_test.cc b/quic/quic_transport/quic_transport_stream_test.cc
index 8c5ca5b..c291b54 100644
--- a/quic/quic_transport/quic_transport_stream_test.cc
+++ b/quic/quic_transport/quic_transport_stream_test.cc
@@ -3,6 +3,7 @@
// found in the LICENSE file.
#include "net/third_party/quiche/src/quic/quic_transport/quic_transport_stream.h"
+
#include <memory>
#include "net/third_party/quiche/src/quic/core/frames/quic_window_update_frame.h"
@@ -12,6 +13,7 @@
#include "net/third_party/quiche/src/quic/quic_transport/quic_transport_session_interface.h"
#include "net/third_party/quiche/src/quic/test_tools/quic_config_peer.h"
#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_transport_test_tools.h"
namespace quic {
namespace test {
@@ -28,12 +30,6 @@
MOCK_CONST_METHOD0(IsSessionReady, bool());
};
-class MockVisitor : public QuicTransportStream::Visitor {
- public:
- MOCK_METHOD0(OnCanRead, void());
- MOCK_METHOD0(OnCanWrite, void());
-};
-
class QuicTransportStreamTest : public QuicTest {
public:
QuicTransportStreamTest()
@@ -46,7 +42,10 @@
stream_ = new QuicTransportStream(0, &session_, &interface_);
session_.ActivateStream(QuicWrapUnique(stream_));
- stream_->set_visitor(&visitor_);
+
+ auto visitor = std::make_unique<MockStreamVisitor>();
+ visitor_ = visitor.get();
+ stream_->set_visitor(std::move(visitor));
}
void ReceiveStreamData(QuicStringPiece data, QuicStreamOffset offset) {
@@ -61,8 +60,8 @@
MockQuicConnection* connection_; // Owned by |session_|.
MockQuicSession session_;
MockQuicTransportSessionInterface interface_;
- MockVisitor visitor_;
QuicTransportStream* stream_; // Owned by |session_|.
+ MockStreamVisitor* visitor_; // Owned by |stream_|.
};
TEST_F(QuicTransportStreamTest, NotReady) {
@@ -95,10 +94,30 @@
TEST_F(QuicTransportStreamTest, ReceiveData) {
EXPECT_CALL(interface_, IsSessionReady()).WillRepeatedly(Return(true));
- EXPECT_CALL(visitor_, OnCanRead());
+ EXPECT_CALL(*visitor_, OnCanRead());
ReceiveStreamData("test", 0);
}
+TEST_F(QuicTransportStreamTest, FinReadWithNoDataPending) {
+ EXPECT_CALL(interface_, IsSessionReady()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*visitor_, OnFinRead());
+ QuicStreamFrame frame(0, true, 0, "");
+ stream_->OnStreamFrame(frame);
+}
+
+TEST_F(QuicTransportStreamTest, FinReadWithDataPending) {
+ EXPECT_CALL(interface_, IsSessionReady()).WillRepeatedly(Return(true));
+
+ EXPECT_CALL(*visitor_, OnCanRead());
+ EXPECT_CALL(*visitor_, OnFinRead()).Times(0);
+ QuicStreamFrame frame(0, true, 0, "test");
+ stream_->OnStreamFrame(frame);
+
+ EXPECT_CALL(*visitor_, OnFinRead()).Times(1);
+ std::string buffer;
+ ASSERT_EQ(stream_->Read(&buffer), 4u);
+}
+
} // namespace
} // namespace test
} // namespace quic