| Index: webrtc/api/quicdatachannel_unittest.cc | 
| diff --git a/webrtc/api/quicdatachannel_unittest.cc b/webrtc/api/quicdatachannel_unittest.cc | 
| new file mode 100644 | 
| index 0000000000000000000000000000000000000000..7ecfaddcf91db588fb1d4c13a5706a006ea29e30 | 
| --- /dev/null | 
| +++ b/webrtc/api/quicdatachannel_unittest.cc | 
| @@ -0,0 +1,406 @@ | 
| +/* | 
| + *  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/api/quicdatachannel.h" | 
| + | 
| +#include <map> | 
| +#include <sstream> | 
| +#include <string> | 
| +#include <vector> | 
| + | 
| +#include "webrtc/base/bind.h" | 
| +#include "webrtc/base/gunit.h" | 
| +#include "webrtc/base/scoped_ptr.h" | 
| +#include "webrtc/base/scoped_ref_ptr.h" | 
| +#include "webrtc/p2p/base/faketransportcontroller.h" | 
| +#include "webrtc/p2p/quic/quictransportchannel.h" | 
| +#include "webrtc/p2p/quic/reliablequicstream.h" | 
| + | 
| +using cricket::FakeTransportChannel; | 
| +using cricket::QuicTransportChannel; | 
| +using cricket::ReliableQuicStream; | 
| + | 
| +using webrtc::DataBuffer; | 
| +using webrtc::DataChannelObserver; | 
| +using webrtc::DataChannelInit; | 
| +using webrtc::QuicDataChannel; | 
| + | 
| +namespace { | 
| + | 
| +// Timeout for asynchronous operations. | 
| +static const int kTimeoutMs = 1000;  // milliseconds | 
| + | 
| +// Small messages that can be sent within a single QUIC packet. | 
| +static const std::string kSmallMessage1 = "Hello, world!"; | 
| +static const std::string kSmallMessage2 = "WebRTC"; | 
| +static const std::string kSmallMessage3 = "1"; | 
| +static const std::string kSmallMessage4 = "abcdefghijklmnopqrstuvwxyz"; | 
| +static const DataBuffer kSmallBuffer1(kSmallMessage1); | 
| +static const DataBuffer kSmallBuffer2(kSmallMessage2); | 
| +static const DataBuffer kSmallBuffer3(kSmallMessage3); | 
| +static const DataBuffer kSmallBuffer4(kSmallMessage4); | 
| + | 
| +// Large messages (> 1350 bytes) that exceed the max size of a QUIC packet. | 
| +// These are < 16 KB so they don't exceed the QUIC stream flow control limit. | 
| +static const std::string kLargeMessage1 = std::string("a", 2000); | 
| +static const std::string kLargeMessage2 = std::string("a", 4000); | 
| +static const std::string kLargeMessage3 = std::string("a", 8000); | 
| +static const std::string kLargeMessage4 = std::string("a", 12000); | 
| +static const DataBuffer kLargeBuffer1(kLargeMessage1); | 
| +static const DataBuffer kLargeBuffer2(kLargeMessage2); | 
| +static const DataBuffer kLargeBuffer3(kLargeMessage3); | 
| +static const DataBuffer kLargeBuffer4(kLargeMessage4); | 
| + | 
| +// Oversized message (> 16 KB) that violates the QUIC stream flow control limit. | 
| +static const std::string kOversizedMessage = std::string("a", 20000); | 
| +static const DataBuffer kOversizedBuffer(kOversizedMessage); | 
| + | 
| +// FakeObserver receives messages from the QuicDataChannel. | 
| +class FakeObserver : public DataChannelObserver { | 
| + public: | 
| +  FakeObserver() | 
| +      : on_state_change_count_(0), on_buffered_amount_change_count_(0) {} | 
| + | 
| +  void OnStateChange() override { ++on_state_change_count_; } | 
| + | 
| +  void OnBufferedAmountChange(uint64_t previous_amount) override { | 
| +    ++on_buffered_amount_change_count_; | 
| +  } | 
| + | 
| +  void OnMessage(const webrtc::DataBuffer& buffer) override { | 
| +    messages_.push_back(std::string(buffer.data.data<char>(), buffer.size())); | 
| +  } | 
| + | 
| +  const std::vector<std::string>& messages() const { return messages_; } | 
| + | 
| +  size_t messages_received() const { return messages_.size(); } | 
| + | 
| +  size_t on_state_change_count() const { return on_state_change_count_; } | 
| + | 
| +  size_t on_buffered_amount_change_count() const { | 
| +    return on_buffered_amount_change_count_; | 
| +  } | 
| + | 
| + private: | 
| +  std::vector<std::string> messages_; | 
| +  size_t on_state_change_count_; | 
| +  size_t on_buffered_amount_change_count_; | 
| +}; | 
| + | 
| +// FakeQuicDataTransport simulates QuicDataTransport by dispatching QUIC | 
| +// stream messages to data channels and encoding/decoding messages. | 
| +class FakeQuicDataTransport : public QuicDataChannel::MessageHelper, | 
| +                              public sigslot::has_slots<> { | 
| + public: | 
| +  explicit FakeQuicDataTransport(QuicTransportChannel* quic_transport_channel) | 
| +      : quic_transport_channel_(quic_transport_channel) { | 
| +    quic_transport_channel_->SignalIncomingStream.connect( | 
| +        this, &FakeQuicDataTransport::OnIncomingStream); | 
| +  } | 
| + | 
| +  QuicDataChannel* CreateDataChannel(int id, | 
| +                                     const std::string& label, | 
| +                                     const std::string& protocol) { | 
| +    DataChannelInit config; | 
| +    config.id = id; | 
| +    config.protocol = protocol; | 
| +    QuicDataChannel* data_channel = new QuicDataChannel( | 
| +        rtc::Thread::Current(), rtc::Thread::Current(), label, &config, *this); | 
| +    data_channel->SetTransportChannel(quic_transport_channel_); | 
| +    data_channel_by_id_[id] = rtc::scoped_refptr<QuicDataChannel>(data_channel); | 
| +    return data_channel; | 
| +  } | 
| + | 
| +  void OnIncomingStream(cricket::ReliableQuicStream* stream) { | 
| +    incoming_stream_ = stream; | 
| +    incoming_stream_->SignalDataReceived.connect( | 
| +        this, &FakeQuicDataTransport::OnDataReceived); | 
| +  } | 
| + | 
| +  void Encode(const DataBuffer& message, | 
| +              int data_channel_id, | 
| +              uint64_t message_id, | 
| +              rtc::CopyOnWriteBuffer* payload) const override { | 
| +    std::ostringstream oss; | 
| +    oss << data_channel_id << "\n"; | 
| +    oss << message_id << "\n"; | 
| +    oss << std::string(message.data.data<char>(), message.size()); | 
| +    std::string output = oss.str(); | 
| +    payload->SetData(output.data(), output.size()); | 
| +  } | 
| + | 
| + private: | 
| +  void OnDataReceived(net::QuicStreamId id, const char* data, size_t len) { | 
| +    ASSERT_EQ(incoming_stream_->id(), id); | 
| +    incoming_stream_->SignalDataReceived.disconnect(this); | 
| +    int data_channel_id; | 
| +    uint64_t message_id; | 
| +    std::string first_bytes; | 
| +    Decode(std::string(data, len), &data_channel_id, &message_id, &first_bytes); | 
| +    const auto& kv = data_channel_by_id_.find(data_channel_id); | 
| +    ASSERT_NE(kv, data_channel_by_id_.end()); | 
| +    QuicDataChannel* data_channel = kv->second; | 
| +    Dispatch(data_channel, message_id, first_bytes.data(), first_bytes.size(), | 
| +             incoming_stream_); | 
| +    incoming_stream_ = nullptr; | 
| +  } | 
| + | 
| +  void Decode(std::string input, | 
| +              int* data_channel_id, | 
| +              uint64_t* message_id, | 
| +              std::string* first_bytes) { | 
| +    std::istringstream iss(input); | 
| +    std::string str; | 
| +    std::getline(iss, str); | 
| +    *data_channel_id = std::stoi(str); | 
| +    std::getline(iss, str); | 
| +    *message_id = std::stoul(str); | 
| +    std::getline(iss, *first_bytes); | 
| +  } | 
| + | 
| +  std::map<net::QuicStreamId, rtc::scoped_refptr<QuicDataChannel>> | 
| +      data_channel_by_id_; | 
| +  QuicTransportChannel* const quic_transport_channel_; | 
| +  cricket::ReliableQuicStream* incoming_stream_ = nullptr; | 
| +}; | 
| + | 
| +// A peer who creates one or more QuicDataChannels to send or receive data. | 
| +class QuicDataChannelPeer { | 
| + public: | 
| +  QuicDataChannelPeer() | 
| +      : ice_transport_channel_("data", 0), | 
| +        quic_transport_channel_(&ice_transport_channel_), | 
| +        fake_quic_data_transport_(&quic_transport_channel_) { | 
| +    ice_transport_channel_.SetAsync(true); | 
| +  } | 
| + | 
| +  void GenerateCertificateAndFingerprint() { | 
| +    rtc::scoped_refptr<rtc::RTCCertificate> local_cert = | 
| +        rtc::RTCCertificate::Create(rtc::scoped_ptr<rtc::SSLIdentity>( | 
| +            rtc::SSLIdentity::Generate("cert_name", rtc::KT_DEFAULT))); | 
| +    quic_transport_channel_.SetLocalCertificate(local_cert); | 
| +    local_fingerprint_.reset(CreateFingerprint(local_cert.get())); | 
| +  } | 
| + | 
| +  QuicDataChannel* CreateDataChannel(int id, | 
| +                                     const std::string& label, | 
| +                                     const std::string& protocol) { | 
| +    return fake_quic_data_transport_.CreateDataChannel(id, label, protocol); | 
| +  } | 
| + | 
| +  // Connects |ice_transport_channel_| to that of the other peer. | 
| +  void Connect(QuicDataChannelPeer* other_peer) { | 
| +    ice_transport_channel_.Connect(); | 
| +    other_peer->ice_transport_channel_.Connect(); | 
| +    ice_transport_channel_.SetDestination(&other_peer->ice_transport_channel_); | 
| +  } | 
| + | 
| +  rtc::scoped_ptr<rtc::SSLFingerprint>& local_fingerprint() { | 
| +    return local_fingerprint_; | 
| +  } | 
| + | 
| +  QuicTransportChannel* quic_transport_channel() { | 
| +    return &quic_transport_channel_; | 
| +  } | 
| + | 
| + private: | 
| +  // Creates a fingerprint from a certificate. | 
| +  rtc::SSLFingerprint* CreateFingerprint(rtc::RTCCertificate* cert) { | 
| +    std::string digest_algorithm; | 
| +    cert->ssl_certificate().GetSignatureDigestAlgorithm(&digest_algorithm); | 
| +    rtc::scoped_ptr<rtc::SSLFingerprint> fingerprint( | 
| +        rtc::SSLFingerprint::Create(digest_algorithm, cert->identity())); | 
| +    return fingerprint.release(); | 
| +  } | 
| + | 
| +  FakeTransportChannel ice_transport_channel_; | 
| +  QuicTransportChannel quic_transport_channel_; | 
| + | 
| +  rtc::scoped_ptr<rtc::SSLFingerprint> local_fingerprint_; | 
| + | 
| +  FakeQuicDataTransport fake_quic_data_transport_; | 
| +}; | 
| + | 
| +class QuicDataChannelTest : public testing::Test { | 
| + public: | 
| +  QuicDataChannelTest() {} | 
| + | 
| +  // Connect the QuicTransportChannels and complete the crypto handshake. | 
| +  void ConnectTransportChannels() { | 
| +    SetCryptoParameters(); | 
| +    peer1_.Connect(&peer2_); | 
| +    ASSERT_TRUE_WAIT(peer1_.quic_transport_channel()->writable() && | 
| +                         peer2_.quic_transport_channel()->writable(), | 
| +                     kTimeoutMs); | 
| +  } | 
| + | 
| +  // Sets crypto parameters required for the QUIC handshake. | 
| +  void SetCryptoParameters() { | 
| +    peer1_.GenerateCertificateAndFingerprint(); | 
| +    peer2_.GenerateCertificateAndFingerprint(); | 
| + | 
| +    peer1_.quic_transport_channel()->SetSslRole(rtc::SSL_CLIENT); | 
| +    peer2_.quic_transport_channel()->SetSslRole(rtc::SSL_SERVER); | 
| + | 
| +    rtc::scoped_ptr<rtc::SSLFingerprint>& peer1_fingerprint = | 
| +        peer1_.local_fingerprint(); | 
| +    rtc::scoped_ptr<rtc::SSLFingerprint>& peer2_fingerprint = | 
| +        peer2_.local_fingerprint(); | 
| + | 
| +    peer1_.quic_transport_channel()->SetRemoteFingerprint( | 
| +        peer2_fingerprint->algorithm, | 
| +        reinterpret_cast<const uint8_t*>(peer2_fingerprint->digest.data()), | 
| +        peer2_fingerprint->digest.size()); | 
| +    peer2_.quic_transport_channel()->SetRemoteFingerprint( | 
| +        peer1_fingerprint->algorithm, | 
| +        reinterpret_cast<const uint8_t*>(peer1_fingerprint->digest.data()), | 
| +        peer1_fingerprint->digest.size()); | 
| +  } | 
| + | 
| + protected: | 
| +  QuicDataChannelPeer peer1_; | 
| +  QuicDataChannelPeer peer2_; | 
| +}; | 
| + | 
| +// Test that QuicDataChannel transfers messages small enough to fit into a | 
| +// single QUIC stream frame. | 
| +TEST_F(QuicDataChannelTest, TransferSmallMessage) { | 
| +  ConnectTransportChannels(); | 
| +  // Init data channels. | 
| +  int data_channel_id = 2; | 
| +  std::string label = "label"; | 
| +  std::string protocol = "protocol"; | 
| +  rtc::scoped_refptr<QuicDataChannel> peer1_data_channel = | 
| +      peer1_.CreateDataChannel(data_channel_id, label, protocol); | 
| +  ASSERT_TRUE(peer1_data_channel->state() == | 
| +              webrtc::DataChannelInterface::kOpen); | 
| +  rtc::scoped_refptr<QuicDataChannel> peer2_data_channel = | 
| +      peer2_.CreateDataChannel(data_channel_id, label, protocol); | 
| +  ASSERT_TRUE(peer2_data_channel->state() == | 
| +              webrtc::DataChannelInterface::kOpen); | 
| +  FakeObserver peer1_observer; | 
| +  peer1_data_channel->RegisterObserver(&peer1_observer); | 
| +  FakeObserver peer2_observer; | 
| +  peer2_data_channel->RegisterObserver(&peer2_observer); | 
| +  // peer1 -> peer2 | 
| +  ASSERT_TRUE(peer1_data_channel->Send(kSmallBuffer1)); | 
| +  ASSERT_TRUE_WAIT(peer2_observer.messages_received() == 1, kTimeoutMs); | 
| +  EXPECT_EQ(kSmallMessage1, peer2_observer.messages()[0]); | 
| +  // peer2 -> peer1 | 
| +  ASSERT_TRUE(peer2_data_channel->Send(kSmallBuffer2)); | 
| +  ASSERT_TRUE_WAIT(peer1_observer.messages_received() == 1, kTimeoutMs); | 
| +  EXPECT_EQ(kSmallMessage2, peer1_observer.messages()[0]); | 
| +  // peer2 -> peer1 | 
| +  ASSERT_TRUE(peer2_data_channel->Send(kSmallBuffer3)); | 
| +  ASSERT_TRUE_WAIT(peer1_observer.messages_received() == 2, kTimeoutMs); | 
| +  EXPECT_EQ(kSmallMessage3, peer1_observer.messages()[1]); | 
| +  // peer1 -> peer2 | 
| +  ASSERT_TRUE(peer1_data_channel->Send(kSmallBuffer4)); | 
| +  ASSERT_TRUE_WAIT(peer2_observer.messages_received() == 2, kTimeoutMs); | 
| +  EXPECT_EQ(kSmallMessage4, peer2_observer.messages()[1]); | 
| +} | 
| + | 
| +// Test that QuicDataChannel transfers messages large enough to fit into | 
| +// multiple QUIC stream frames, which don't violate the QUIC flow control limit. | 
| +// These require buffering by the QuicDataChannel. | 
| +TEST_F(QuicDataChannelTest, TransferLargeMessage) { | 
| +  ConnectTransportChannels(); | 
| +  // Init data channels. | 
| +  int data_channel_id = 347; | 
| +  std::string label = "label"; | 
| +  std::string protocol = "protocol"; | 
| +  rtc::scoped_refptr<QuicDataChannel> peer1_data_channel = | 
| +      peer1_.CreateDataChannel(data_channel_id, label, protocol); | 
| +  ASSERT_TRUE(peer1_data_channel->state() == | 
| +              webrtc::DataChannelInterface::kOpen); | 
| +  rtc::scoped_refptr<QuicDataChannel> peer2_data_channel = | 
| +      peer2_.CreateDataChannel(data_channel_id, label, protocol); | 
| +  ASSERT_TRUE(peer2_data_channel->state() == | 
| +              webrtc::DataChannelInterface::kOpen); | 
| +  FakeObserver peer1_observer; | 
| +  peer1_data_channel->RegisterObserver(&peer1_observer); | 
| +  FakeObserver peer2_observer; | 
| +  peer2_data_channel->RegisterObserver(&peer2_observer); | 
| +  // peer1 -> peer2 | 
| +  ASSERT_TRUE(peer1_data_channel->Send(kLargeBuffer1)); | 
| +  ASSERT_TRUE_WAIT(peer2_observer.messages_received() == 1, kTimeoutMs); | 
| +  EXPECT_EQ(kLargeMessage1, peer2_observer.messages()[0]); | 
| +  // peer2 -> peer1 | 
| +  ASSERT_TRUE(peer2_data_channel->Send(kLargeBuffer2)); | 
| +  ASSERT_TRUE_WAIT(peer1_observer.messages_received() == 1, kTimeoutMs); | 
| +  EXPECT_EQ(kLargeMessage2, peer1_observer.messages()[0]); | 
| +  // peer2 -> peer1 | 
| +  ASSERT_TRUE(peer2_data_channel->Send(kLargeBuffer3)); | 
| +  ASSERT_TRUE_WAIT(peer1_observer.messages_received() == 2, kTimeoutMs); | 
| +  EXPECT_EQ(kLargeMessage3, peer1_observer.messages()[1]); | 
| +  // peer1 -> peer2 | 
| +  ASSERT_TRUE(peer1_data_channel->Send(kLargeBuffer4)); | 
| +  ASSERT_TRUE_WAIT(peer2_observer.messages_received() == 2, kTimeoutMs); | 
| +  EXPECT_EQ(kLargeMessage4, peer2_observer.messages()[1]); | 
| +} | 
| + | 
| +// Test that when a message size exceeds the flow control limit (> 16KB), | 
| +// QuicDataChannel becomes write blocked. The first 16KB are sent to the remote | 
| +// peer, while the remaining bytes are buffered until the remote peer sends a | 
| +// WINDOW_UPDATE frame. | 
| +TEST_F(QuicDataChannelTest, TransferOversizedMessage) { | 
| +  ConnectTransportChannels(); | 
| +  int data_channel_id = 189; | 
| +  std::string label = "label"; | 
| +  std::string protocol = "protocol"; | 
| +  rtc::scoped_refptr<QuicDataChannel> peer1_data_channel = | 
| +      peer1_.CreateDataChannel(data_channel_id, label, protocol); | 
| +  rtc::scoped_refptr<QuicDataChannel> peer2_data_channel = | 
| +      peer2_.CreateDataChannel(data_channel_id, label, protocol); | 
| +  FakeObserver peer1_observer; | 
| +  peer1_data_channel->RegisterObserver(&peer1_observer); | 
| +  FakeObserver peer2_observer; | 
| +  peer2_data_channel->RegisterObserver(&peer2_observer); | 
| +  ASSERT_TRUE(peer1_data_channel->Send(kOversizedBuffer)); | 
| +  EXPECT_EQ(1, peer1_observer.on_buffered_amount_change_count()); | 
| +} | 
| + | 
| +// Test that QuicDataChannel does not send before it is open. | 
| +TEST_F(QuicDataChannelTest, TransferDataBeforeChannelOpen) { | 
| +  int data_channel_id = 6; | 
| +  std::string label = "label"; | 
| +  std::string protocol = "protocol"; | 
| +  rtc::scoped_refptr<QuicDataChannel> data_channel = | 
| +      peer1_.CreateDataChannel(data_channel_id, label, protocol); | 
| +  ASSERT_TRUE(data_channel->state() == | 
| +              webrtc::DataChannelInterface::kConnecting); | 
| +  EXPECT_FALSE(data_channel->Send(kSmallBuffer1)); | 
| +} | 
| + | 
| +// Test that QuicDataChannel does not send after it is closed. | 
| +TEST_F(QuicDataChannelTest, TransferDataAfterChannelClosed) { | 
| +  int data_channel_id = 42; | 
| +  std::string label = "label"; | 
| +  std::string protocol = "protocol"; | 
| +  rtc::scoped_refptr<QuicDataChannel> data_channel = | 
| +      peer1_.CreateDataChannel(data_channel_id, label, protocol); | 
| +  data_channel->Close(); | 
| +  ASSERT_TRUE(data_channel->state() == webrtc::DataChannelInterface::kClosed); | 
| +  EXPECT_FALSE(data_channel->Send(kSmallBuffer1)); | 
| +} | 
| + | 
| +// If a message is empty, nothing is sent and QuicDataChannel returns true. | 
| +TEST_F(QuicDataChannelTest, TransferEmptyData) { | 
| +  ConnectTransportChannels(); | 
| +  int data_channel_id = 69; | 
| +  std::string label = "label"; | 
| +  std::string protocol = "protocol"; | 
| +  rtc::scoped_refptr<QuicDataChannel> data_channel = | 
| +      peer1_.CreateDataChannel(data_channel_id, label, protocol); | 
| +  ASSERT_TRUE(data_channel->state() == webrtc::DataChannelInterface::kOpen); | 
| +  EXPECT_TRUE(data_channel->Send(DataBuffer(""))); | 
| +} | 
| + | 
| +}  // namespace | 
|  |