blob: ca3088c22ff3954bd9607309185d0917d148d9f3 [file] [log] [blame]
// Copyright 2018 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 "third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_stream.h"
#include "net/test/gtest_util.h"
#include "net/third_party/quiche/src/quic/core/quic_data_writer.h"
#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_stream_impl.h"
#include "third_party/blink/renderer/modules/peerconnection/adapters/test/mock_p2p_quic_stream_delegate.h"
namespace blink {
namespace {
using testing::_;
using testing::ElementsAre;
using testing::ElementsAreArray;
using testing::Invoke;
using testing::InvokeWithoutArgs;
const uint32_t kDefaultStreamWriteBufferSize = 1024;
const uint32_t kDefaultStreamDelegateReadBufferSize = 1024;
const quic::QuicStreamId kStreamId = 5;
const uint8_t kSomeData[] = {'h', 'o', 'w', 'd', 'y'};
const uint8_t kMoreData[] = {'m', 'o', 'r', 'e'};
} // namespace
// Unit tests for the P2PQuicStream, using a mock QuicSession, which allows
// us to isolate testing the behaviors of reading a writing.
class P2PQuicStreamTest : public testing::Test {
public:
P2PQuicStreamTest()
: connection_(
new quic::test::MockQuicConnection(&connection_helper_,
&alarm_factory_,
quic::Perspective::IS_CLIENT)),
session_(connection_) {
session_.Initialize();
// DCHECKS get hit when the clock is at 0.
connection_helper_.AdvanceTime(quic::QuicTime::Delta::FromSeconds(1));
}
~P2PQuicStreamTest() override {}
void InitializeStream(
uint32_t delegate_read_buffer_size = kDefaultStreamDelegateReadBufferSize,
uint32_t write_buffer_size = kDefaultStreamWriteBufferSize) {
stream_ = new P2PQuicStreamImpl(
kStreamId, &session_, delegate_read_buffer_size, write_buffer_size);
stream_->SetDelegate(&delegate_);
// The session takes the ownership of the stream.
session_.ActivateStream(std::unique_ptr<P2PQuicStreamImpl>(stream_));
}
template <wtf_size_t Size>
static quic::QuicStringPiece StringPieceFromArray(
const uint8_t (&array)[Size]) {
return quic::QuicStringPiece(reinterpret_cast<const char*>(array), Size);
}
template <wtf_size_t Size>
static Vector<uint8_t> VectorFromArray(const uint8_t (&array)[Size]) {
Vector<uint8_t> vector;
vector.Append(array, Size);
return vector;
}
quic::test::MockQuicConnectionHelper connection_helper_;
quic::test::MockAlarmFactory alarm_factory_;
// Owned by the |session_|.
quic::test::MockQuicConnection* connection_;
// The MockQuicSession allows us to see data that is being written and control
// whether the data is being "sent across" or blocked.
quic::test::MockQuicSession session_;
MockP2PQuicStreamDelegate delegate_;
// Owned by |session_|.
P2PQuicStreamImpl* stream_;
};
TEST_F(P2PQuicStreamTest, StreamSendsFinAndCanNoLongerWrite) {
InitializeStream();
EXPECT_CALL(session_, WritevData(stream_, kStreamId, _, _, _))
.WillOnce(Invoke(quic::test::MockQuicSession::ConsumeData));
stream_->WriteData({}, /*fin=*/true);
EXPECT_TRUE(stream_->fin_sent());
EXPECT_TRUE(stream_->write_side_closed());
EXPECT_FALSE(stream_->reading_stopped());
}
TEST_F(P2PQuicStreamTest, StreamResetSendsRst) {
InitializeStream();
EXPECT_CALL(session_, SendRstStream(kStreamId, _, _));
stream_->Reset();
EXPECT_TRUE(stream_->rst_sent());
}
// Tests that when a stream receives a stream frame with the FIN bit set it
// will fire the appropriate callback and close the stream for reading.
TEST_F(P2PQuicStreamTest, StreamOnStreamFrameWithFin) {
InitializeStream();
EXPECT_CALL(delegate_, OnDataReceived(_, /*fin=*/true));
quic::QuicStreamFrame fin_frame(kStreamId, /*fin=*/true, 0, 0);
stream_->OnStreamFrame(fin_frame);
EXPECT_TRUE(stream_->reading_stopped());
EXPECT_FALSE(stream_->write_side_closed());
}
// Tests that when a stream receives a stream frame with the FIN bit set after
// it has written the FIN bit, then the stream will close.
TEST_F(P2PQuicStreamTest, StreamClosedAfterSendingThenReceivingFin) {
InitializeStream();
EXPECT_CALL(session_, WritevData(stream_, kStreamId, _, _, _))
.WillOnce(Invoke(quic::test::MockQuicSession::ConsumeData));
stream_->WriteData({}, /*fin=*/true);
EXPECT_FALSE(stream_->IsClosedForTesting());
quic::QuicStreamFrame fin_frame(stream_->id(), /*fin=*/true, 0, 0);
stream_->OnStreamFrame(fin_frame);
EXPECT_TRUE(stream_->reading_stopped());
EXPECT_TRUE(stream_->write_side_closed());
EXPECT_TRUE(stream_->IsClosedForTesting());
}
// Tests that when a stream writes a FIN bit after receiving a stream frame with
// the FIN bit then the stream will close.
TEST_F(P2PQuicStreamTest, StreamClosedAfterReceivingThenSendingFin) {
InitializeStream();
quic::QuicStreamFrame fin_frame(stream_->id(), /*fin=*/true, 0, 0);
stream_->OnStreamFrame(fin_frame);
EXPECT_FALSE(stream_->IsClosedForTesting());
EXPECT_CALL(session_, WritevData(stream_, kStreamId, _, _, _))
.WillOnce(Invoke(quic::test::MockQuicSession::ConsumeData));
stream_->WriteData({}, /*fin=*/true);
EXPECT_TRUE(stream_->IsClosedForTesting());
}
// Tests that when a stream writes some data with the FIN bit set, and receives
// data with the FIN bit set it will become closed.
TEST_F(P2PQuicStreamTest, StreamClosedAfterWritingAndReceivingDataWithFin) {
InitializeStream();
EXPECT_CALL(session_,
WritevData(stream_, kStreamId,
/*write_length=*/base::size(kSomeData), _, _))
.WillOnce(Invoke(quic::test::MockQuicSession::ConsumeData));
stream_->WriteData(VectorFromArray(kSomeData),
/*fin=*/true);
EXPECT_FALSE(stream_->IsClosedForTesting());
quic::QuicStreamFrame fin_frame_with_data(stream_->id(), /*fin=*/true, 0,
StringPieceFromArray(kSomeData));
stream_->OnStreamFrame(fin_frame_with_data);
EXPECT_TRUE(stream_->reading_stopped());
EXPECT_TRUE(stream_->write_side_closed());
EXPECT_TRUE(stream_->IsClosedForTesting());
}
// Tests that when a stream receives a RST_STREAM frame it will fire the
// appropriate callback and the stream will become closed.
TEST_F(P2PQuicStreamTest, StreamClosedAfterReceivingReset) {
InitializeStream();
EXPECT_CALL(delegate_, OnRemoteReset());
quic::QuicRstStreamFrame rst_frame(quic::kInvalidControlFrameId, kStreamId,
quic::QUIC_STREAM_CANCELLED, 0);
if (!VersionHasIetfQuicFrames(connection_->version().transport_version)) {
// Google RST_STREAM closes the stream in both directions. A RST_STREAM
// is then sent to the peer to communicate the final byte offset.
EXPECT_CALL(session_,
SendRstStream(kStreamId, quic::QUIC_RST_ACKNOWLEDGEMENT, 0));
}
stream_->OnStreamReset(rst_frame);
if (VersionHasIetfQuicFrames(connection_->version().transport_version)) {
// In IETF QUIC, the RST_STREAM only closes the stream in one direction.
// A STOP_SENDING frame in require to induce the a RST_STREAM being
// send to close the other direction.
EXPECT_CALL(*connection_, SendControlFrame(_));
EXPECT_CALL(*connection_, OnStreamReset(kStreamId, testing::_));
quic::QuicStopSendingFrame stop_sending_frame(quic::kInvalidControlFrameId,
kStreamId, 0);
session_.OnStopSendingFrame(stop_sending_frame);
}
EXPECT_TRUE(stream_->IsClosedForTesting());
}
// Tests that data written to the P2PQuicStream will appropriately get written
// to the underlying QUIC library.
TEST_F(P2PQuicStreamTest, StreamWritesData) {
InitializeStream();
EXPECT_CALL(session_,
WritevData(stream_, kStreamId,
/*write_length=*/base::size(kSomeData), _, _))
.WillOnce(Invoke([](quic::QuicStream* stream, quic::QuicStreamId id,
size_t write_length, quic::QuicStreamOffset offset,
quic::StreamSendingState state) {
// quic::QuicSession::WritevData does not pass the data. The data is
// saved to the stream, so we must grab it before it's consumed, in
// order to check that it's what was written.
std::string data_consumed_by_quic(write_length, 'a');
quic::QuicDataWriter writer(write_length, &data_consumed_by_quic[0],
quic::NETWORK_BYTE_ORDER);
stream->WriteStreamData(offset, write_length, &writer);
EXPECT_THAT(data_consumed_by_quic, ElementsAreArray(kSomeData));
EXPECT_EQ(quic::StreamSendingState::NO_FIN, state);
return quic::QuicConsumedData(
write_length, state != quic::StreamSendingState::NO_FIN);
}));
EXPECT_CALL(delegate_, OnWriteDataConsumed(base::size(kSomeData)));
stream_->WriteData(VectorFromArray(kSomeData), /*fin=*/false);
}
// Tests that data written to the P2PQuicStream will appropriately get written
// to the underlying QUIC library with the FIN bit set.
TEST_F(P2PQuicStreamTest, StreamWritesDataWithFin) {
InitializeStream();
EXPECT_CALL(session_,
WritevData(stream_, kStreamId,
/*write_length=*/base::size(kSomeData), _, _))
.WillOnce(Invoke([](quic::QuicStream* stream, quic::QuicStreamId id,
size_t write_length, quic::QuicStreamOffset offset,
quic::StreamSendingState state) {
// WritevData does not pass the data. The data is saved to the stream,
// so we must grab it before it's consumed, in order to check that it's
// what was written.
std::string data_consumed_by_quic(write_length, 'a');
quic::QuicDataWriter writer(write_length, &data_consumed_by_quic[0],
quic::NETWORK_BYTE_ORDER);
stream->WriteStreamData(offset, write_length, &writer);
EXPECT_THAT(data_consumed_by_quic, ElementsAreArray(kSomeData));
EXPECT_EQ(quic::StreamSendingState::FIN, state);
return quic::QuicConsumedData(
write_length, state != quic::StreamSendingState::NO_FIN);
}));
EXPECT_CALL(delegate_, OnWriteDataConsumed(base::size(kSomeData)));
stream_->WriteData(VectorFromArray(kSomeData), /*fin=*/true);
}
// Tests that when written data is not consumed by QUIC (due to buffering),
// the OnWriteDataConsumed will not get fired.
TEST_F(P2PQuicStreamTest, StreamWritesDataAndNotConsumedByQuic) {
InitializeStream();
EXPECT_CALL(delegate_, OnWriteDataConsumed(_)).Times(0);
EXPECT_CALL(session_,
WritevData(stream_, kStreamId,
/*write_length=*/base::size(kSomeData), _, _))
.WillOnce(Invoke([](quic::QuicStream* stream, quic::QuicStreamId id,
size_t write_length, quic::QuicStreamOffset offset,
quic::StreamSendingState state) {
// We mock that the QUIC library is not consuming the data, meaning it's
// being buffered. In this case, the OnWriteDataConsumed() callback
// should not be called.
return quic::QuicConsumedData(/*bytes_consumed=*/0,
quic::StreamSendingState::NO_FIN);
}));
stream_->WriteData(VectorFromArray(kSomeData), /*fin=*/true);
}
// Tests that OnWriteDataConsumed() is fired with the amount consumed by QUIC.
// This tests the case when amount consumed by QUIC is less than what is written
// with P2PQuicStream::WriteData. This can happen when QUIC is receiving back
// pressure from the receive side, and its "send window" is smaller than the
// amount attempted to be written.
TEST_F(P2PQuicStreamTest, StreamWritesDataAndPartiallyConsumedByQuic) {
InitializeStream();
size_t amount_consumed_by_quic = 2;
EXPECT_CALL(delegate_, OnWriteDataConsumed(amount_consumed_by_quic));
EXPECT_CALL(session_,
WritevData(stream_, kStreamId,
/*write_length=*/base::size(kSomeData), _, _))
.WillOnce(Invoke([&amount_consumed_by_quic](
quic::QuicStream* stream, quic::QuicStreamId id,
size_t write_length, quic::QuicStreamOffset offset,
quic::StreamSendingState state) {
// We mock that the QUIC library is only consuming some of the data,
// meaning the rest is being buffered.
return quic::QuicConsumedData(
/*bytes_consumed=*/amount_consumed_by_quic,
quic::StreamSendingState::NO_FIN);
}));
stream_->WriteData(VectorFromArray(kSomeData), /*fin=*/true);
}
// Tests if a P2PQuicStream receives data it will appropriately fire the
// OnDataReceived callback to the delegate.
TEST_F(P2PQuicStreamTest, StreamReceivesData) {
InitializeStream();
quic::QuicStreamFrame stream_frame(stream_->id(), /*fin=*/false, 0,
StringPieceFromArray(kSomeData));
EXPECT_CALL(delegate_, OnDataReceived(VectorFromArray(kSomeData),
/*fin=*/false));
stream_->OnStreamFrame(stream_frame);
}
// Tests that when received data is marked consumed it is appropriately
// reflected in the P2PQuicStream's view of the delegate read buffer size.
TEST_F(P2PQuicStreamTest, MarkConsumedData) {
InitializeStream();
quic::QuicStreamFrame stream_frame(stream_->id(), /*fin=*/false, 0,
StringPieceFromArray(kSomeData));
EXPECT_CALL(delegate_, OnDataReceived(_, _));
stream_->OnStreamFrame(stream_frame);
// At this point the application has received data but not marked is as
// consumed, so from the P2PQuicStream perspective that data has been
// buffered.
EXPECT_EQ(base::size(kSomeData),
stream_->DelegateReadBufferedAmountForTesting());
stream_->MarkReceivedDataConsumed(base::size(kSomeData));
EXPECT_EQ(0u, stream_->DelegateReadBufferedAmountForTesting());
}
// Tests that if the delegate's read buffer is "full" from the
// P2PQuicStream's perspective, then getting more data will not fire the
// OnDataReceived callback.
TEST_F(P2PQuicStreamTest, StreamReceivesDataWithFullReadBuffer) {
// The P2PQuicStream is created with a delegate read buffer size equal
// to the size of the data that is being received.
InitializeStream(/*delegate_read_buffer_size=*/base::size(kSomeData));
quic::QuicStreamFrame stream_frame(stream_->id(), /*fin=*/false, 0,
StringPieceFromArray(kSomeData));
EXPECT_CALL(delegate_, OnDataReceived(_, _)).Times(1);
stream_->OnStreamFrame(stream_frame);
EXPECT_EQ(base::size(kSomeData),
stream_->DelegateReadBufferedAmountForTesting());
// Delegate read buffer is now full. Receiving more data should not fire the
// callback.
quic::QuicStreamFrame new_stream_frame(stream_->id(), /*fin=*/false,
/*offset=*/base::size(kSomeData),
StringPieceFromArray(kMoreData));
stream_->OnStreamFrame(new_stream_frame);
EXPECT_EQ(base::size(kSomeData),
stream_->DelegateReadBufferedAmountForTesting());
}
// Tests that if the delegate's read buffer is "full" from the
// P2PQuicStream's perspective, and then getting an empty STREAM frame with the
// FIN bit set, will fire Delegate::OnDataReceived with fin set to true.
TEST_F(P2PQuicStreamTest, StreamReceivesFinWithFullReadBuffer) {
// The P2PQuicStream is created with a delegate read buffer size equal
// to the size of the data that is being received.
InitializeStream(/*delegate_read_buffer_size=*/base::size(kSomeData));
quic::QuicStreamFrame stream_frame(stream_->id(), /*fin=*/false, 0,
StringPieceFromArray(kSomeData));
EXPECT_CALL(delegate_, OnDataReceived(ElementsAreArray(kSomeData),
/*fin=*/false))
.Times(1);
stream_->OnStreamFrame(stream_frame);
EXPECT_EQ(base::size(kSomeData),
stream_->DelegateReadBufferedAmountForTesting());
// Delegate read buffer is now full, but because the STREAM frame with the FIN
// bit doesn't contain any data, it means that all data has been consumed from
// the sequencer up to the FIN bit. This fires OnDataReceived with the
// fin=true.
EXPECT_CALL(delegate_, OnDataReceived(_, true)).Times(1);
quic::QuicStreamFrame new_stream_frame(stream_->id(), /*fin=*/true,
/*offset=*/base::size(kSomeData), 0);
stream_->OnStreamFrame(new_stream_frame);
EXPECT_EQ(base::size(kSomeData),
stream_->DelegateReadBufferedAmountForTesting());
}
// Tests that when the delegate's read buffer is "full" from the
// P2PQuicStream's perspective, the Delegate::OnDataReceived callback is
// fired after the received data is marked as consumed by the delegate.
TEST_F(P2PQuicStreamTest, StreamDataConsumedWithFullDelegateReadBuffer) {
// The P2PQuicStream is created with a delegate read buffer size equal
// to the size of the data that is being received.
InitializeStream(/*delegate_read_buffer_size=*/base::size(kSomeData));
quic::QuicStreamFrame stream_frame(stream_->id(), /*fin=*/false, 0,
StringPieceFromArray(kSomeData));
EXPECT_CALL(delegate_, OnDataReceived(ElementsAreArray(kSomeData),
/*fin=*/false))
.Times(1);
stream_->OnStreamFrame(stream_frame);
EXPECT_EQ(base::size(kSomeData),
stream_->DelegateReadBufferedAmountForTesting());
// Delegate read buffer is now full. Receiving more data should not fire the
// callback.
quic::QuicStreamFrame new_stream_frame(stream_->id(), /*fin=*/true,
/*offset=*/base::size(kSomeData),
StringPieceFromArray(kMoreData));
stream_->OnStreamFrame(new_stream_frame);
EXPECT_EQ(base::size(kSomeData),
stream_->DelegateReadBufferedAmountForTesting());
// Marking the original data as consumed should fire the new data to be
// received.
EXPECT_CALL(delegate_, OnDataReceived(ElementsAreArray(kMoreData),
/*fin=*/true))
.Times(1);
stream_->MarkReceivedDataConsumed(base::size(kSomeData));
EXPECT_EQ(base::size(kMoreData),
stream_->DelegateReadBufferedAmountForTesting());
}
// Tests that when receiving more data than available in the delegate read
// buffer, that the delegate will get an OnDataReceived callback for the amount
// available in its buffer. Then later when the delegate marks the data as
// consumed it will get another OnDataReceived callback.
TEST_F(P2PQuicStreamTest, StreamReceivesMoreDataThanDelegateReadBufferSize) {
const uint8_t kData[] = {'s', 'o', 'm', 'e', 'd', 'a', 't', 'a'};
// The P2PQuicStream is created with a delegate read buffer size equal
// to half of the data being sent.
InitializeStream(/*delegate_read_buffer_size=*/4);
quic::QuicStreamFrame stream_frame(stream_->id(), /*fin=*/true, 0,
StringPieceFromArray(kData));
// Upon receiving the stream frame the Delegate should receive "some", because
// that's all it has space to buffer.
EXPECT_CALL(delegate_, OnDataReceived(ElementsAre('s', 'o', 'm', 'e'),
/*fin=*/false))
.Times(1);
stream_->OnStreamFrame(stream_frame);
EXPECT_EQ(4u, stream_->DelegateReadBufferedAmountForTesting());
// Upon consuming 2 bytes of data, the delegate should receive the next part
// of the message - "da".
EXPECT_CALL(delegate_, OnDataReceived(ElementsAre('d', 'a'), /*fin=*/false))
.Times(1);
stream_->MarkReceivedDataConsumed(2);
EXPECT_EQ(4u, stream_->DelegateReadBufferedAmountForTesting());
// After consuming 4 bytes of data (all received data thus far), the delegate
// should receive the next part of the message - "ta" and the FIN bit.
EXPECT_CALL(delegate_, OnDataReceived(ElementsAre('t', 'a'), /*fin=*/true))
.Times(1);
stream_->MarkReceivedDataConsumed(4);
// Just the last data received ("ta") is held in the delegate's read buffer.
EXPECT_EQ(2u, stream_->DelegateReadBufferedAmountForTesting());
}
// Tests that after a delegate unsets itself, it will no longer receive the
// OnWriteDataConsumed callback.
TEST_F(P2PQuicStreamTest, UnsetDelegateDoesNotFireOnWriteDataConsumed) {
InitializeStream();
stream_->SetDelegate(nullptr);
// Mock out the QuicSession to get the QuicStream::OnStreamDataConsumed
// callback to fire.
EXPECT_CALL(session_,
WritevData(stream_, kStreamId,
/*write_length=*/base::size(kSomeData), _, _))
.WillOnce(Invoke([](quic::QuicStream* stream, quic::QuicStreamId id,
size_t write_length, quic::QuicStreamOffset offset,
quic::StreamSendingState state) {
return quic::QuicConsumedData(
write_length, state != quic::StreamSendingState::NO_FIN);
}));
EXPECT_CALL(delegate_, OnWriteDataConsumed(_)).Times(0);
stream_->WriteData(VectorFromArray(kSomeData), /*fin=*/false);
}
// Tests that after a delegate unsets itself, it will no longer receive the
// OnRemoteReset callback.
TEST_F(P2PQuicStreamTest, UnsetDelegateDoesNotFireOnRemoteReset) {
InitializeStream();
stream_->SetDelegate(nullptr);
EXPECT_CALL(delegate_, OnRemoteReset()).Times(0);
quic::QuicRstStreamFrame rst_frame(quic::kInvalidControlFrameId, kStreamId,
quic::QUIC_STREAM_CANCELLED, 0);
stream_->OnStreamReset(rst_frame);
}
// Tests that after a delegate unsets itself, it will no longer receive the
// OnDataReceived callback when receiving a stream frame with data and no FIN
// bit.
TEST_F(P2PQuicStreamTest, UnsetDelegateDoesNotFireOnDataReceivedWithData) {
InitializeStream();
stream_->SetDelegate(nullptr);
EXPECT_CALL(delegate_, OnDataReceived(_, _)).Times(0);
quic::QuicStreamFrame stream_frame(stream_->id(), /*fin=*/false, 0,
StringPieceFromArray(kSomeData));
stream_->OnStreamFrame(stream_frame);
}
// Tests that after a delegate unsets itself, it will no longer receive the
// OnDataReceived callback when receiving a stream frame with the FIN bit.
TEST_F(P2PQuicStreamTest, UnsetDelegateDoesNotFireOnDataReceivedWithFin) {
InitializeStream();
stream_->SetDelegate(nullptr);
EXPECT_CALL(delegate_, OnDataReceived(_, _)).Times(0);
quic::QuicStreamFrame stream_frame(stream_->id(), /*fin=*/true, 0, {});
stream_->OnStreamFrame(stream_frame);
}
} // namespace blink