1 /* 2 * libjingle 3 * Copyright 2013 Google Inc. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, 9 * this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright notice, 11 * this list of conditions and the following disclaimer in the documentation 12 * and/or other materials provided with the distribution. 13 * 3. The name of the author may not be used to endorse or promote products 14 * derived from this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 #include <errno.h> 29 #include <stdarg.h> 30 #include <stdio.h> 31 #include <string> 32 #include <vector> 33 34 #include "talk/media/base/constants.h" 35 #include "talk/media/base/mediachannel.h" 36 #include "talk/media/sctp/sctpdataengine.h" 37 #include "webrtc/base/bind.h" 38 #include "webrtc/base/buffer.h" 39 #include "webrtc/base/criticalsection.h" 40 #include "webrtc/base/gunit.h" 41 #include "webrtc/base/helpers.h" 42 #include "webrtc/base/messagehandler.h" 43 #include "webrtc/base/messagequeue.h" 44 #include "webrtc/base/scoped_ptr.h" 45 #include "webrtc/base/ssladapter.h" 46 #include "webrtc/base/thread.h" 47 48 enum { 49 MSG_PACKET = 1, 50 }; 51 52 // Fake NetworkInterface that sends/receives sctp packets. The one in 53 // talk/media/base/fakenetworkinterface.h only works with rtp/rtcp. 54 class SctpFakeNetworkInterface : public cricket::MediaChannel::NetworkInterface, 55 public rtc::MessageHandler { 56 public: 57 explicit SctpFakeNetworkInterface(rtc::Thread* thread) 58 : thread_(thread), 59 dest_(NULL) { 60 } 61 62 void SetDestination(cricket::DataMediaChannel* dest) { dest_ = dest; } 63 64 protected: 65 // Called to send raw packet down the wire (e.g. SCTP an packet). 66 virtual bool SendPacket(rtc::Buffer* packet, 67 const rtc::PacketOptions& options) { 68 LOG(LS_VERBOSE) << "SctpFakeNetworkInterface::SendPacket"; 69 70 // TODO(ldixon): Can/should we use Buffer.TransferTo here? 71 // Note: this assignment does a deep copy of data from packet. 72 rtc::Buffer* buffer = new rtc::Buffer(packet->data(), packet->size()); 73 thread_->Post(this, MSG_PACKET, rtc::WrapMessageData(buffer)); 74 LOG(LS_VERBOSE) << "SctpFakeNetworkInterface::SendPacket, Posted message."; 75 return true; 76 } 77 78 // Called when a raw packet has been recieved. This passes the data to the 79 // code that will interpret the packet. e.g. to get the content payload from 80 // an SCTP packet. 81 virtual void OnMessage(rtc::Message* msg) { 82 LOG(LS_VERBOSE) << "SctpFakeNetworkInterface::OnMessage"; 83 rtc::scoped_ptr<rtc::Buffer> buffer( 84 static_cast<rtc::TypedMessageData<rtc::Buffer*>*>( 85 msg->pdata)->data()); 86 if (dest_) { 87 dest_->OnPacketReceived(buffer.get(), rtc::PacketTime()); 88 } 89 delete msg->pdata; 90 } 91 92 // Unsupported functions required to exist by NetworkInterface. 93 // TODO(ldixon): Refactor parent NetworkInterface class so these are not 94 // required. They are RTC specific and should be in an appropriate subclass. 95 virtual bool SendRtcp(rtc::Buffer* packet, 96 const rtc::PacketOptions& options) { 97 LOG(LS_WARNING) << "Unsupported: SctpFakeNetworkInterface::SendRtcp."; 98 return false; 99 } 100 virtual int SetOption(SocketType type, rtc::Socket::Option opt, 101 int option) { 102 LOG(LS_WARNING) << "Unsupported: SctpFakeNetworkInterface::SetOption."; 103 return 0; 104 } 105 virtual void SetDefaultDSCPCode(rtc::DiffServCodePoint dscp) { 106 LOG(LS_WARNING) << "Unsupported: SctpFakeNetworkInterface::SetOption."; 107 } 108 109 private: 110 // Not owned by this class. 111 rtc::Thread* thread_; 112 cricket::DataMediaChannel* dest_; 113 }; 114 115 // This is essentially a buffer to hold recieved data. It stores only the last 116 // received data. Calling OnDataReceived twice overwrites old data with the 117 // newer one. 118 // TODO(ldixon): Implement constraints, and allow new data to be added to old 119 // instead of replacing it. 120 class SctpFakeDataReceiver : public sigslot::has_slots<> { 121 public: 122 SctpFakeDataReceiver() : received_(false) {} 123 124 void Clear() { 125 received_ = false; 126 last_data_ = ""; 127 last_params_ = cricket::ReceiveDataParams(); 128 } 129 130 virtual void OnDataReceived(const cricket::ReceiveDataParams& params, 131 const char* data, size_t length) { 132 received_ = true; 133 last_data_ = std::string(data, length); 134 last_params_ = params; 135 } 136 137 bool received() const { return received_; } 138 std::string last_data() const { return last_data_; } 139 cricket::ReceiveDataParams last_params() const { return last_params_; } 140 141 private: 142 bool received_; 143 std::string last_data_; 144 cricket::ReceiveDataParams last_params_; 145 }; 146 147 class SignalReadyToSendObserver : public sigslot::has_slots<> { 148 public: 149 SignalReadyToSendObserver() : signaled_(false), writable_(false) {} 150 151 void OnSignaled(bool writable) { 152 signaled_ = true; 153 writable_ = writable; 154 } 155 156 bool IsSignaled(bool writable) { 157 return signaled_ && (writable_ == writable); 158 } 159 160 private: 161 bool signaled_; 162 bool writable_; 163 }; 164 165 class SignalChannelClosedObserver : public sigslot::has_slots<> { 166 public: 167 SignalChannelClosedObserver() {} 168 void BindSelf(cricket::SctpDataMediaChannel* channel) { 169 channel->SignalStreamClosedRemotely.connect( 170 this, &SignalChannelClosedObserver::OnStreamClosed); 171 } 172 void OnStreamClosed(uint32_t stream) { streams_.push_back(stream); } 173 174 int StreamCloseCount(uint32_t stream) { 175 return std::count(streams_.begin(), streams_.end(), stream); 176 } 177 178 bool WasStreamClosed(uint32_t stream) { 179 return std::find(streams_.begin(), streams_.end(), stream) 180 != streams_.end(); 181 } 182 183 private: 184 std::vector<uint32_t> streams_; 185 }; 186 187 class SignalChannelClosedReopener : public sigslot::has_slots<> { 188 public: 189 SignalChannelClosedReopener(cricket::SctpDataMediaChannel* channel, 190 cricket::SctpDataMediaChannel* peer) 191 : channel_(channel), peer_(peer) {} 192 193 void OnStreamClosed(int stream) { 194 cricket::StreamParams p(cricket::StreamParams::CreateLegacy(stream)); 195 channel_->AddSendStream(p); 196 channel_->AddRecvStream(p); 197 peer_->AddSendStream(p); 198 peer_->AddRecvStream(p); 199 streams_.push_back(stream); 200 } 201 202 int StreamCloseCount(int stream) { 203 return std::count(streams_.begin(), streams_.end(), stream); 204 } 205 206 private: 207 cricket::SctpDataMediaChannel* channel_; 208 cricket::SctpDataMediaChannel* peer_; 209 std::vector<int> streams_; 210 }; 211 212 // SCTP Data Engine testing framework. 213 class SctpDataMediaChannelTest : public testing::Test, 214 public sigslot::has_slots<> { 215 protected: 216 // usrsctp uses the NSS random number generator on non-Android platforms, 217 // so we need to initialize SSL. 218 static void SetUpTestCase() { 219 } 220 221 virtual void SetUp() { 222 engine_.reset(new cricket::SctpDataEngine()); 223 } 224 225 void SetupConnectedChannels() { 226 net1_.reset(new SctpFakeNetworkInterface(rtc::Thread::Current())); 227 net2_.reset(new SctpFakeNetworkInterface(rtc::Thread::Current())); 228 recv1_.reset(new SctpFakeDataReceiver()); 229 recv2_.reset(new SctpFakeDataReceiver()); 230 chan1_ready_to_send_count_ = 0; 231 chan2_ready_to_send_count_ = 0; 232 chan1_.reset(CreateChannel(net1_.get(), recv1_.get())); 233 chan1_->set_debug_name("chan1/connector"); 234 chan1_->SignalReadyToSend.connect( 235 this, &SctpDataMediaChannelTest::OnChan1ReadyToSend); 236 chan2_.reset(CreateChannel(net2_.get(), recv2_.get())); 237 chan2_->set_debug_name("chan2/listener"); 238 chan2_->SignalReadyToSend.connect( 239 this, &SctpDataMediaChannelTest::OnChan2ReadyToSend); 240 // Setup two connected channels ready to send and receive. 241 net1_->SetDestination(chan2_.get()); 242 net2_->SetDestination(chan1_.get()); 243 244 LOG(LS_VERBOSE) << "Channel setup ----------------------------- "; 245 AddStream(1); 246 AddStream(2); 247 248 LOG(LS_VERBOSE) << "Connect the channels -----------------------------"; 249 // chan1 wants to setup a data connection. 250 chan1_->SetReceive(true); 251 // chan1 will have sent chan2 a request to setup a data connection. After 252 // chan2 accepts the offer, chan2 connects to chan1 with the following. 253 chan2_->SetReceive(true); 254 chan2_->SetSend(true); 255 // Makes sure that network packets are delivered and simulates a 256 // deterministic and realistic small timing delay between the SetSend calls. 257 ProcessMessagesUntilIdle(); 258 259 // chan1 and chan2 are now connected so chan1 enables sending to complete 260 // the creation of the connection. 261 chan1_->SetSend(true); 262 } 263 264 virtual void TearDown() { 265 channel1()->SetSend(false); 266 channel2()->SetSend(false); 267 268 // Process messages until idle to prevent a sent packet from being dropped 269 // and causing memory leaks (not being deleted by the receiver). 270 ProcessMessagesUntilIdle(); 271 } 272 273 bool AddStream(int ssrc) { 274 bool ret = true; 275 cricket::StreamParams p(cricket::StreamParams::CreateLegacy(ssrc)); 276 ret = ret && chan1_->AddSendStream(p); 277 ret = ret && chan1_->AddRecvStream(p); 278 ret = ret && chan2_->AddSendStream(p); 279 ret = ret && chan2_->AddRecvStream(p); 280 return ret; 281 } 282 283 cricket::SctpDataMediaChannel* CreateChannel( 284 SctpFakeNetworkInterface* net, SctpFakeDataReceiver* recv) { 285 cricket::SctpDataMediaChannel* channel = 286 static_cast<cricket::SctpDataMediaChannel*>(engine_->CreateChannel( 287 cricket::DCT_SCTP)); 288 channel->SetInterface(net); 289 // When data is received, pass it to the SctpFakeDataReceiver. 290 channel->SignalDataReceived.connect( 291 recv, &SctpFakeDataReceiver::OnDataReceived); 292 return channel; 293 } 294 295 bool SendData(cricket::SctpDataMediaChannel* chan, 296 uint32_t ssrc, 297 const std::string& msg, 298 cricket::SendDataResult* result) { 299 cricket::SendDataParams params; 300 params.ssrc = ssrc; 301 302 return chan->SendData(params, rtc::Buffer( 303 &msg[0], msg.length()), result); 304 } 305 306 bool ReceivedData(const SctpFakeDataReceiver* recv, 307 uint32_t ssrc, 308 const std::string& msg) { 309 return (recv->received() && 310 recv->last_params().ssrc == ssrc && 311 recv->last_data() == msg); 312 } 313 314 bool ProcessMessagesUntilIdle() { 315 rtc::Thread* thread = rtc::Thread::Current(); 316 while (!thread->empty()) { 317 rtc::Message msg; 318 if (thread->Get(&msg, rtc::Thread::kForever)) { 319 thread->Dispatch(&msg); 320 } 321 } 322 return !thread->IsQuitting(); 323 } 324 325 cricket::SctpDataMediaChannel* channel1() { return chan1_.get(); } 326 cricket::SctpDataMediaChannel* channel2() { return chan2_.get(); } 327 SctpFakeDataReceiver* receiver1() { return recv1_.get(); } 328 SctpFakeDataReceiver* receiver2() { return recv2_.get(); } 329 330 int channel1_ready_to_send_count() { return chan1_ready_to_send_count_; } 331 int channel2_ready_to_send_count() { return chan2_ready_to_send_count_; } 332 private: 333 rtc::scoped_ptr<cricket::SctpDataEngine> engine_; 334 rtc::scoped_ptr<SctpFakeNetworkInterface> net1_; 335 rtc::scoped_ptr<SctpFakeNetworkInterface> net2_; 336 rtc::scoped_ptr<SctpFakeDataReceiver> recv1_; 337 rtc::scoped_ptr<SctpFakeDataReceiver> recv2_; 338 rtc::scoped_ptr<cricket::SctpDataMediaChannel> chan1_; 339 rtc::scoped_ptr<cricket::SctpDataMediaChannel> chan2_; 340 341 int chan1_ready_to_send_count_; 342 int chan2_ready_to_send_count_; 343 344 void OnChan1ReadyToSend(bool send) { 345 if (send) 346 ++chan1_ready_to_send_count_; 347 } 348 void OnChan2ReadyToSend(bool send) { 349 if (send) 350 ++chan2_ready_to_send_count_; 351 } 352 }; 353 354 // Verifies that SignalReadyToSend is fired. 355 TEST_F(SctpDataMediaChannelTest, SignalReadyToSend) { 356 SetupConnectedChannels(); 357 358 SignalReadyToSendObserver signal_observer_1; 359 SignalReadyToSendObserver signal_observer_2; 360 361 channel1()->SignalReadyToSend.connect(&signal_observer_1, 362 &SignalReadyToSendObserver::OnSignaled); 363 channel2()->SignalReadyToSend.connect(&signal_observer_2, 364 &SignalReadyToSendObserver::OnSignaled); 365 366 cricket::SendDataResult result; 367 ASSERT_TRUE(SendData(channel1(), 1, "hello?", &result)); 368 EXPECT_EQ(cricket::SDR_SUCCESS, result); 369 EXPECT_TRUE_WAIT(ReceivedData(receiver2(), 1, "hello?"), 1000); 370 ASSERT_TRUE(SendData(channel2(), 2, "hi chan1", &result)); 371 EXPECT_EQ(cricket::SDR_SUCCESS, result); 372 EXPECT_TRUE_WAIT(ReceivedData(receiver1(), 2, "hi chan1"), 1000); 373 374 EXPECT_TRUE_WAIT(signal_observer_1.IsSignaled(true), 1000); 375 EXPECT_TRUE_WAIT(signal_observer_2.IsSignaled(true), 1000); 376 } 377 378 TEST_F(SctpDataMediaChannelTest, SendData) { 379 SetupConnectedChannels(); 380 381 cricket::SendDataResult result; 382 LOG(LS_VERBOSE) << "chan1 sending: 'hello?' -----------------------------"; 383 ASSERT_TRUE(SendData(channel1(), 1, "hello?", &result)); 384 EXPECT_EQ(cricket::SDR_SUCCESS, result); 385 EXPECT_TRUE_WAIT(ReceivedData(receiver2(), 1, "hello?"), 1000); 386 LOG(LS_VERBOSE) << "recv2.received=" << receiver2()->received() 387 << ", recv2.last_params.ssrc=" 388 << receiver2()->last_params().ssrc 389 << ", recv2.last_params.timestamp=" 390 << receiver2()->last_params().ssrc 391 << ", recv2.last_params.seq_num=" 392 << receiver2()->last_params().seq_num 393 << ", recv2.last_data=" << receiver2()->last_data(); 394 395 LOG(LS_VERBOSE) << "chan2 sending: 'hi chan1' -----------------------------"; 396 ASSERT_TRUE(SendData(channel2(), 2, "hi chan1", &result)); 397 EXPECT_EQ(cricket::SDR_SUCCESS, result); 398 EXPECT_TRUE_WAIT(ReceivedData(receiver1(), 2, "hi chan1"), 1000); 399 LOG(LS_VERBOSE) << "recv1.received=" << receiver1()->received() 400 << ", recv1.last_params.ssrc=" 401 << receiver1()->last_params().ssrc 402 << ", recv1.last_params.timestamp=" 403 << receiver1()->last_params().ssrc 404 << ", recv1.last_params.seq_num=" 405 << receiver1()->last_params().seq_num 406 << ", recv1.last_data=" << receiver1()->last_data(); 407 } 408 409 // Sends a lot of large messages at once and verifies SDR_BLOCK is returned. 410 TEST_F(SctpDataMediaChannelTest, SendDataBlocked) { 411 SetupConnectedChannels(); 412 413 cricket::SendDataResult result; 414 cricket::SendDataParams params; 415 params.ssrc = 1; 416 417 std::vector<char> buffer(1024 * 64, 0); 418 419 for (size_t i = 0; i < 100; ++i) { 420 channel1()->SendData( 421 params, rtc::Buffer(&buffer[0], buffer.size()), &result); 422 if (result == cricket::SDR_BLOCK) 423 break; 424 } 425 426 EXPECT_EQ(cricket::SDR_BLOCK, result); 427 } 428 429 TEST_F(SctpDataMediaChannelTest, ClosesRemoteStream) { 430 SetupConnectedChannels(); 431 SignalChannelClosedObserver chan_1_sig_receiver, chan_2_sig_receiver; 432 chan_1_sig_receiver.BindSelf(channel1()); 433 chan_2_sig_receiver.BindSelf(channel2()); 434 435 cricket::SendDataResult result; 436 ASSERT_TRUE(SendData(channel1(), 1, "hello?", &result)); 437 EXPECT_EQ(cricket::SDR_SUCCESS, result); 438 EXPECT_TRUE_WAIT(ReceivedData(receiver2(), 1, "hello?"), 1000); 439 ASSERT_TRUE(SendData(channel2(), 2, "hi chan1", &result)); 440 EXPECT_EQ(cricket::SDR_SUCCESS, result); 441 EXPECT_TRUE_WAIT(ReceivedData(receiver1(), 2, "hi chan1"), 1000); 442 443 // Close channel 1. Channel 2 should notify us. 444 channel1()->RemoveSendStream(1); 445 EXPECT_TRUE_WAIT(chan_2_sig_receiver.WasStreamClosed(1), 1000); 446 } 447 448 TEST_F(SctpDataMediaChannelTest, ClosesTwoRemoteStreams) { 449 SetupConnectedChannels(); 450 AddStream(3); 451 SignalChannelClosedObserver chan_1_sig_receiver, chan_2_sig_receiver; 452 chan_1_sig_receiver.BindSelf(channel1()); 453 chan_2_sig_receiver.BindSelf(channel2()); 454 455 cricket::SendDataResult result; 456 ASSERT_TRUE(SendData(channel1(), 1, "hello?", &result)); 457 EXPECT_EQ(cricket::SDR_SUCCESS, result); 458 EXPECT_TRUE_WAIT(ReceivedData(receiver2(), 1, "hello?"), 1000); 459 ASSERT_TRUE(SendData(channel2(), 2, "hi chan1", &result)); 460 EXPECT_EQ(cricket::SDR_SUCCESS, result); 461 EXPECT_TRUE_WAIT(ReceivedData(receiver1(), 2, "hi chan1"), 1000); 462 463 // Close two streams on one side. 464 channel2()->RemoveSendStream(2); 465 channel2()->RemoveSendStream(3); 466 EXPECT_TRUE_WAIT(chan_1_sig_receiver.WasStreamClosed(2), 1000); 467 EXPECT_TRUE_WAIT(chan_1_sig_receiver.WasStreamClosed(3), 1000); 468 } 469 470 TEST_F(SctpDataMediaChannelTest, ClosesStreamsOnBothSides) { 471 SetupConnectedChannels(); 472 AddStream(3); 473 AddStream(4); 474 SignalChannelClosedObserver chan_1_sig_receiver, chan_2_sig_receiver; 475 chan_1_sig_receiver.BindSelf(channel1()); 476 chan_2_sig_receiver.BindSelf(channel2()); 477 478 cricket::SendDataResult result; 479 ASSERT_TRUE(SendData(channel1(), 1, "hello?", &result)); 480 EXPECT_EQ(cricket::SDR_SUCCESS, result); 481 EXPECT_TRUE_WAIT(ReceivedData(receiver2(), 1, "hello?"), 1000); 482 ASSERT_TRUE(SendData(channel2(), 2, "hi chan1", &result)); 483 EXPECT_EQ(cricket::SDR_SUCCESS, result); 484 EXPECT_TRUE_WAIT(ReceivedData(receiver1(), 2, "hi chan1"), 1000); 485 486 // Close one stream on channel1(), while closing three streams on 487 // channel2(). They will conflict (only one side can close anything at a 488 // time, apparently). Test the resolution of the conflict. 489 channel1()->RemoveSendStream(1); 490 491 channel2()->RemoveSendStream(2); 492 channel2()->RemoveSendStream(3); 493 channel2()->RemoveSendStream(4); 494 EXPECT_TRUE_WAIT(chan_2_sig_receiver.WasStreamClosed(1), 1000); 495 EXPECT_TRUE_WAIT(chan_1_sig_receiver.WasStreamClosed(2), 1000); 496 EXPECT_TRUE_WAIT(chan_1_sig_receiver.WasStreamClosed(3), 1000); 497 EXPECT_TRUE_WAIT(chan_1_sig_receiver.WasStreamClosed(4), 1000); 498 } 499 500 TEST_F(SctpDataMediaChannelTest, EngineSignalsRightChannel) { 501 SetupConnectedChannels(); 502 EXPECT_TRUE_WAIT(channel1()->socket() != NULL, 1000); 503 struct socket *sock = const_cast<struct socket*>(channel1()->socket()); 504 int prior_count = channel1_ready_to_send_count(); 505 cricket::SctpDataEngine::SendThresholdCallback(sock, 0); 506 EXPECT_GT(channel1_ready_to_send_count(), prior_count); 507 } 508 509 TEST_F(SctpDataMediaChannelTest, RefusesHighNumberedChannels) { 510 SetupConnectedChannels(); 511 EXPECT_TRUE(AddStream(1022)); 512 EXPECT_FALSE(AddStream(1023)); 513 } 514 515 // Flaky on Linux and Windows. See webrtc:4453. 516 #if defined(WEBRTC_WIN) || defined(WEBRTC_LINUX) 517 #define MAYBE_ReusesAStream DISABLED_ReusesAStream 518 #else 519 #define MAYBE_ReusesAStream ReusesAStream 520 #endif 521 TEST_F(SctpDataMediaChannelTest, MAYBE_ReusesAStream) { 522 // Shut down channel 1, then open it up again for reuse. 523 SetupConnectedChannels(); 524 cricket::SendDataResult result; 525 SignalChannelClosedObserver chan_2_sig_receiver; 526 chan_2_sig_receiver.BindSelf(channel2()); 527 528 ASSERT_TRUE(SendData(channel1(), 1, "hello?", &result)); 529 EXPECT_EQ(cricket::SDR_SUCCESS, result); 530 EXPECT_TRUE_WAIT(ReceivedData(receiver2(), 1, "hello?"), 1000); 531 532 channel1()->RemoveSendStream(1); 533 EXPECT_TRUE_WAIT(chan_2_sig_receiver.WasStreamClosed(1), 1000); 534 // Channel 1 is gone now. 535 536 // Create a new channel 1. 537 AddStream(1); 538 ASSERT_TRUE(SendData(channel1(), 1, "hi?", &result)); 539 EXPECT_EQ(cricket::SDR_SUCCESS, result); 540 EXPECT_TRUE_WAIT(ReceivedData(receiver2(), 1, "hi?"), 1000); 541 channel1()->RemoveSendStream(1); 542 EXPECT_TRUE_WAIT(chan_2_sig_receiver.StreamCloseCount(1) == 2, 1000); 543 } 544