Index: net/quic/quic_chromium_client_stream_test.cc |
diff --git a/net/quic/quic_chromium_client_stream_test.cc b/net/quic/quic_chromium_client_stream_test.cc |
index f384304002dec9dadbd690ddf45fa22ba522dd1d..340375526e475fd8676061da5420b8dff185c490 100644 |
--- a/net/quic/quic_chromium_client_stream_test.cc |
+++ b/net/quic/quic_chromium_client_stream_test.cc |
@@ -5,6 +5,8 @@ |
#include "net/quic/quic_chromium_client_stream.h" |
#include "base/macros.h" |
+#include "base/run_loop.h" |
+#include "base/strings/string_number_conversions.h" |
#include "net/base/net_errors.h" |
#include "net/base/test_completion_callback.h" |
#include "net/quic/quic_chromium_client_session.h" |
@@ -35,7 +37,8 @@ class MockDelegate : public QuicChromiumClientStream::Delegate { |
MOCK_METHOD0(OnSendData, int()); |
MOCK_METHOD2(OnSendDataComplete, int(int, bool*)); |
- MOCK_METHOD2(OnHeadersAvailable, void(const SpdyHeaderBlock&, size_t)); |
+ MOCK_METHOD2(OnHeadersAvailable, |
+ void(const SpdyHeaderBlock& headers, size_t frame_len)); |
MOCK_METHOD2(OnDataReceived, int(const char*, int)); |
MOCK_METHOD0(OnDataAvailable, void()); |
MOCK_METHOD1(OnClose, void(QuicErrorCode)); |
@@ -315,14 +318,103 @@ TEST_P(QuicChromiumClientStreamTest, OnTrailers) { |
SpdyHeaderBlock trailers; |
trailers["bar"] = "foo"; |
+ trailers[kFinalOffsetHeaderKey] = base::IntToString(strlen(data)); |
std::string uncompressed_trailers = |
SpdyUtils::SerializeUncompressedHeaders(trailers); |
stream_->OnStreamHeaders(uncompressed_trailers); |
stream_->OnStreamHeadersComplete(true, uncompressed_trailers.length()); |
+ SpdyHeaderBlock actual_trailers; |
+ |
+ base::RunLoop run_loop; |
+ EXPECT_CALL(delegate_, OnHeadersAvailable(_, uncompressed_trailers.length())) |
+ .WillOnce(testing::DoAll( |
+ testing::SaveArg<0>(&actual_trailers), |
+ testing::InvokeWithoutArgs([&run_loop]() { run_loop.Quit(); }))); |
+ |
+ run_loop.Run(); |
+ // Make sure kFinalOffsetHeaderKey is gone from the delivered actual trailers. |
+ trailers.erase(kFinalOffsetHeaderKey); |
+ EXPECT_EQ(trailers, actual_trailers); |
+ base::MessageLoop::current()->RunUntilIdle(); |
+ EXPECT_CALL(delegate_, OnClose(QUIC_NO_ERROR)); |
+} |
+ |
+// Tests that trailers are marked as consumed only before delegate is to be |
+// immediately notified about trailers. |
+TEST_P(QuicChromiumClientStreamTest, MarkTrailersConsumedWhenNotifyDelegate) { |
+ InitializeHeaders(); |
+ std::string uncompressed_headers = |
+ SpdyUtils::SerializeUncompressedHeaders(headers_); |
+ stream_->OnStreamHeaders(uncompressed_headers); |
+ stream_->OnStreamHeadersComplete(false, uncompressed_headers.length()); |
+ |
+ EXPECT_CALL(delegate_, |
+ OnHeadersAvailable(headers_, uncompressed_headers.length())); |
+ base::MessageLoop::current()->RunUntilIdle(); |
+ EXPECT_TRUE(stream_->decompressed_headers().empty()); |
+ |
+ const char data[] = "hello world!"; |
+ stream_->OnStreamFrame(QuicStreamFrame(kTestStreamId, /*fin=*/false, |
+ /*offset=*/0, data)); |
+ |
+ base::RunLoop run_loop; |
+ EXPECT_CALL(delegate_, OnDataAvailable()) |
+ .Times(1) |
+ .WillOnce(testing::DoAll( |
+ testing::Invoke(CreateFunctor( |
+ &QuicChromiumClientStreamTest::ReadData, base::Unretained(this), |
+ StringPiece(data, arraysize(data) - 1))), |
+ testing::Invoke([&run_loop]() { run_loop.Quit(); }))); |
+ |
+ // Wait for the read to complete. |
+ run_loop.Run(); |
+ |
+ // Read again, and it will be pending. |
+ scoped_refptr<IOBuffer> buffer(new IOBuffer(1)); |
+ EXPECT_EQ(ERR_IO_PENDING, stream_->Read(buffer.get(), 1)); |
+ |
+ SpdyHeaderBlock trailers; |
+ trailers["bar"] = "foo"; |
+ trailers[kFinalOffsetHeaderKey] = base::IntToString(strlen(data)); |
+ std::string uncompressed_trailers = |
+ SpdyUtils::SerializeUncompressedHeaders(trailers); |
+ |
+ stream_->OnStreamHeaders(uncompressed_trailers); |
+ stream_->OnStreamHeadersComplete(true, uncompressed_trailers.length()); |
+ EXPECT_FALSE(stream_->IsDoneReading()); |
+ |
+ // Now the pending should complete. Make sure that IsDoneReading() is false |
+ // even though ReadData returns 0 byte, because OnHeadersAvailable callback |
+ // comes after this OnDataAvailable callback. |
+ base::RunLoop run_loop2; |
+ EXPECT_CALL(delegate_, OnDataAvailable()) |
+ .Times(1) |
+ .WillOnce(testing::DoAll( |
+ testing::Invoke(CreateFunctor(&QuicChromiumClientStreamTest::ReadData, |
+ base::Unretained(this), StringPiece())), |
+ testing::InvokeWithoutArgs([&run_loop2]() { run_loop2.Quit(); }))); |
+ run_loop2.Run(); |
+ // Make sure that the stream is not closed, even though ReadData returns 0. |
+ EXPECT_FALSE(stream_->IsDoneReading()); |
+ |
+ // The OnHeadersAvailable call should follow. |
+ base::RunLoop run_loop3; |
+ SpdyHeaderBlock actual_trailers; |
EXPECT_CALL(delegate_, |
- OnHeadersAvailable(trailers, uncompressed_trailers.length())); |
+ OnHeadersAvailable(trailers, uncompressed_trailers.length())) |
+ .WillOnce(testing::DoAll( |
+ testing::SaveArg<0>(&actual_trailers), |
+ testing::InvokeWithoutArgs([&run_loop3]() { run_loop3.Quit(); }))); |
+ |
+ run_loop3.Run(); |
+ // Make sure the stream is properly closed since trailers and data are all |
+ // consumed. |
+ EXPECT_TRUE(stream_->IsDoneReading()); |
+ // Make sure kFinalOffsetHeaderKey is gone from the delivered actual trailers. |
+ trailers.erase(kFinalOffsetHeaderKey); |
+ EXPECT_EQ(trailers, actual_trailers); |
base::MessageLoop::current()->RunUntilIdle(); |
EXPECT_CALL(delegate_, OnClose(QUIC_NO_ERROR)); |