Index: net/quic/quic_session_test.cc |
diff --git a/net/quic/quic_session_test.cc b/net/quic/quic_session_test.cc |
index e4b67b0331d0b4a572551b12974bd02414cb5468..699d588b32634a316435af3f40a8fc9086bbbe43 100644 |
--- a/net/quic/quic_session_test.cc |
+++ b/net/quic/quic_session_test.cc |
@@ -96,6 +96,8 @@ class TestStream : public QuicSpdyStream { |
WriteOrBufferData(data, fin, nullptr); |
} |
+ using QuicSpdyStream::set_priority; |
+ |
MOCK_METHOD0(OnCanWrite, void()); |
}; |
@@ -111,6 +113,10 @@ class StreamBlocker { |
session_->MarkConnectionLevelWriteBlocked(stream_id_, kSomeMiddlePriority); |
} |
+ void MarkHighPriorityWriteBlocked() { |
+ session_->MarkConnectionLevelWriteBlocked(stream_id_, kHighestPriority); |
+ } |
+ |
private: |
QuicSession* const session_; |
const QuicStreamId stream_id_; |
@@ -158,13 +164,14 @@ class TestSession : public QuicSpdySession { |
bool fin, |
FecProtection fec_protection, |
QuicAckListenerInterface* ack_notifier_delegate) override { |
- // Always consumes everything. |
- if (writev_consumes_all_data_) { |
- return QuicConsumedData(data.total_length, fin); |
- } else { |
- return QuicSession::WritevData(id, data, offset, fin, fec_protection, |
- ack_notifier_delegate); |
+ QuicConsumedData consumed(data.total_length, fin); |
+ if (!writev_consumes_all_data_) { |
+ consumed = QuicSession::WritevData(id, data, offset, fin, fec_protection, |
+ ack_notifier_delegate); |
} |
+ QuicSessionPeer::GetWriteBlockedStreams(this)->UpdateBytesForStream( |
+ id, consumed.bytes_consumed); |
+ return consumed; |
} |
void set_writev_consumes_all_data(bool val) { |
@@ -177,6 +184,15 @@ class TestSession : public QuicSpdySession { |
MAY_FEC_PROTECT, nullptr); |
} |
+ QuicConsumedData SendLargeFakeData(QuicStreamId id, int bytes) { |
+ DCHECK(writev_consumes_all_data_); |
+ struct iovec iov; |
+ iov.iov_base = nullptr; // should not be read. |
+ iov.iov_len = static_cast<size_t>(bytes); |
+ return WritevData(id, QuicIOVector(&iov, 1, bytes), 0, true, |
+ MAY_FEC_PROTECT, nullptr); |
+ } |
+ |
using QuicSession::PostProcessAfterData; |
private: |
@@ -390,16 +406,102 @@ TEST_P(QuicSessionTestServer, OnCanWrite) { |
InSequence s; |
StreamBlocker stream2_blocker(&session_, stream2->id()); |
- // Reregister, to test the loop limit. |
- EXPECT_CALL(*stream2, OnCanWrite()) |
- .WillOnce(Invoke(&stream2_blocker, |
- &StreamBlocker::MarkConnectionLevelWriteBlocked)); |
- EXPECT_CALL(*stream6, OnCanWrite()); |
- EXPECT_CALL(*stream4, OnCanWrite()); |
+ |
+ if (FLAGS_quic_batch_writes) { |
+ // Reregister, to test the loop limit. |
+ EXPECT_CALL(*stream2, OnCanWrite()) |
+ .WillOnce(Invoke(&stream2_blocker, |
+ &StreamBlocker::MarkConnectionLevelWriteBlocked)); |
+ // 2 will get called a second time as it didn't finish its block |
+ EXPECT_CALL(*stream2, OnCanWrite()); |
+ EXPECT_CALL(*stream6, OnCanWrite()); |
+ // 4 will not get called, as we exceeded the loop limit. |
+ } else { |
+ // Reregister, to test the loop limit. |
+ EXPECT_CALL(*stream2, OnCanWrite()) |
+ .WillOnce(Invoke(&stream2_blocker, |
+ &StreamBlocker::MarkConnectionLevelWriteBlocked)); |
+ EXPECT_CALL(*stream6, OnCanWrite()); |
+ EXPECT_CALL(*stream4, OnCanWrite()); |
+ } |
session_.OnCanWrite(); |
EXPECT_TRUE(session_.WillingAndAbleToWrite()); |
} |
+TEST_P(QuicSessionTestServer, TestBatchedWrites) { |
+ FLAGS_quic_batch_writes = true; |
+ TestStream* stream2 = session_.CreateOutgoingDynamicStream(); |
+ TestStream* stream4 = session_.CreateOutgoingDynamicStream(); |
+ TestStream* stream6 = session_.CreateOutgoingDynamicStream(); |
+ |
+ session_.set_writev_consumes_all_data(true); |
+ session_.MarkConnectionLevelWriteBlocked(stream2->id(), kSomeMiddlePriority); |
+ session_.MarkConnectionLevelWriteBlocked(stream4->id(), kSomeMiddlePriority); |
+ |
+ StreamBlocker stream2_blocker(&session_, stream2->id()); |
+ StreamBlocker stream4_blocker(&session_, stream4->id()); |
+ StreamBlocker stream6_blocker(&session_, stream6->id()); |
+ // With two sessions blocked, we should get two write calls. They should both |
+ // go to the first stream as it will only write 6k and mark itself blocked |
+ // again. |
+ InSequence s; |
+ EXPECT_CALL(*stream2, OnCanWrite()) |
+ .WillOnce(DoAll(testing::IgnoreResult(Invoke(CreateFunctor( |
+ &session_, &TestSession::SendLargeFakeData, stream2->id(), 6000))), |
+ Invoke(&stream2_blocker, |
+ &StreamBlocker::MarkConnectionLevelWriteBlocked))); |
+ EXPECT_CALL(*stream2, OnCanWrite()) |
+ .WillOnce(DoAll(testing::IgnoreResult(Invoke(CreateFunctor( |
+ &session_, &TestSession::SendLargeFakeData, stream2->id(), 6000))), |
+ Invoke(&stream2_blocker, |
+ &StreamBlocker::MarkConnectionLevelWriteBlocked))); |
+ session_.OnCanWrite(); |
+ |
+ // We should get one more call for stream2, at which point it has used its |
+ // write quota and we move over to stream 4. |
+ EXPECT_CALL(*stream2, OnCanWrite()) |
+ .WillOnce(DoAll(testing::IgnoreResult(Invoke(CreateFunctor( |
+ &session_, &TestSession::SendLargeFakeData, stream2->id(), 6000))), |
+ Invoke(&stream2_blocker, |
+ &StreamBlocker::MarkConnectionLevelWriteBlocked))); |
+ EXPECT_CALL(*stream4, OnCanWrite()) |
+ .WillOnce(DoAll(testing::IgnoreResult(Invoke(CreateFunctor( |
+ &session_, &TestSession::SendLargeFakeData, stream4->id(), 6000))), |
+ Invoke(&stream4_blocker, |
+ &StreamBlocker::MarkConnectionLevelWriteBlocked))); |
+ session_.OnCanWrite(); |
+ |
+ // Now let stream 4 do the 2nd of its 3 writes, but add a block for a high |
+ // priority stream 6. 4 should be preempted. 6 will write but *not* block so |
+ // will cede back to 4. |
+ stream6->set_priority(kHighestPriority); |
+ EXPECT_CALL(*stream4, OnCanWrite()) |
+ .WillOnce(DoAll(testing::IgnoreResult(Invoke(CreateFunctor( |
+ &session_, &TestSession::SendLargeFakeData, stream4->id(), 6000))), |
+ Invoke(&stream4_blocker, |
+ &StreamBlocker::MarkConnectionLevelWriteBlocked), |
+ Invoke(&stream6_blocker, |
+ &StreamBlocker::MarkHighPriorityWriteBlocked))); |
+ EXPECT_CALL(*stream6, OnCanWrite()) |
+ .WillOnce(testing::IgnoreResult(Invoke(CreateFunctor( |
+ &session_, &TestSession::SendLargeFakeData, stream4->id(), 6000)))); |
+ session_.OnCanWrite(); |
+ |
+ // Stream4 alread did 6k worth of writes, so after doing another 12k it should |
+ // cede and 2 should resume. |
+ EXPECT_CALL(*stream4, OnCanWrite()) |
+ .WillOnce(DoAll(testing::IgnoreResult(Invoke(CreateFunctor( |
+ &session_, &TestSession::SendLargeFakeData, stream4->id(), 12000))), |
+ Invoke(&stream4_blocker, |
+ &StreamBlocker::MarkConnectionLevelWriteBlocked))); |
+ EXPECT_CALL(*stream2, OnCanWrite()) |
+ .WillOnce(DoAll(testing::IgnoreResult(Invoke(CreateFunctor( |
+ &session_, &TestSession::SendLargeFakeData, stream2->id(), 6000))), |
+ Invoke(&stream2_blocker, |
+ &StreamBlocker::MarkConnectionLevelWriteBlocked))); |
+ session_.OnCanWrite(); |
+} |
+ |
TEST_P(QuicSessionTestServer, OnCanWriteBundlesStreams) { |
// Drive congestion control manually. |
MockSendAlgorithm* send_algorithm = new StrictMock<MockSendAlgorithm>; |
@@ -516,13 +618,8 @@ TEST_P(QuicSessionTestServer, BufferedHandshake) { |
TestCryptoStream* crypto_stream = session_.GetCryptoStream(); |
EXPECT_CALL(*crypto_stream, OnCanWrite()); |
- // Re-register all other streams, to show they weren't able to proceed. |
- EXPECT_CALL(*stream2, OnCanWrite()) |
- .WillOnce(Invoke(&stream2_blocker, |
- &StreamBlocker::MarkConnectionLevelWriteBlocked)); |
- EXPECT_CALL(*stream3, OnCanWrite()) |
- .WillOnce(Invoke(&stream3_blocker, |
- &StreamBlocker::MarkConnectionLevelWriteBlocked)); |
+ EXPECT_CALL(*stream2, OnCanWrite()); |
+ EXPECT_CALL(*stream3, OnCanWrite()); |
EXPECT_CALL(*stream4, OnCanWrite()) |
.WillOnce(Invoke(&stream4_blocker, |
&StreamBlocker::MarkConnectionLevelWriteBlocked)); |