3 * Copyright 2013, Google Inc.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
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.
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.
28 #include "talk/app/webrtc/test/peerconnectiontestwrapper.h"
29 #include "talk/app/webrtc/test/mockpeerconnectionobservers.h"
30 #include "webrtc/base/gunit.h"
31 #include "webrtc/base/logging.h"
32 #include "webrtc/base/ssladapter.h"
33 #include "webrtc/base/sslstreamadapter.h"
34 #include "webrtc/base/stringencode.h"
35 #include "webrtc/base/stringutils.h"
37 #define MAYBE_SKIP_TEST(feature) \
39 LOG(LS_INFO) << "Feature disabled... skipping"; \
43 using webrtc::DataChannelInterface;
44 using webrtc::FakeConstraints;
45 using webrtc::MediaConstraintsInterface;
46 using webrtc::MediaStreamInterface;
47 using webrtc::PeerConnectionInterface;
51 const char kExternalGiceUfrag[] = "1234567890123456";
52 const char kExternalGicePwd[] = "123456789012345678901234";
53 const size_t kMaxWait = 10000;
55 void RemoveLinesFromSdp(const std::string& line_start,
57 const char kSdpLineEnd[] = "\r\n";
59 while ((ssrc_pos = sdp->find(line_start, ssrc_pos)) !=
61 size_t end_ssrc = sdp->find(kSdpLineEnd, ssrc_pos);
62 sdp->erase(ssrc_pos, end_ssrc - ssrc_pos + strlen(kSdpLineEnd));
66 // Add |newlines| to the |message| after |line|.
67 void InjectAfter(const std::string& line,
68 const std::string& newlines,
69 std::string* message) {
70 const std::string tmp = line + newlines;
71 rtc::replace_substrs(line.c_str(), line.length(),
72 tmp.c_str(), tmp.length(), message);
75 void Replace(const std::string& line,
76 const std::string& newlines,
77 std::string* message) {
78 rtc::replace_substrs(line.c_str(), line.length(),
79 newlines.c_str(), newlines.length(), message);
82 void UseExternalSdes(std::string* sdp) {
83 // Remove current crypto specification.
84 RemoveLinesFromSdp("a=crypto", sdp);
85 RemoveLinesFromSdp("a=fingerprint", sdp);
86 // Add external crypto.
87 const char kAudioSdes[] =
88 "a=crypto:1 AES_CM_128_HMAC_SHA1_80 "
89 "inline:PS1uQCVeeCFCanVmcjkpPywjNWhcYD0mXXtxaVBR\r\n";
90 const char kVideoSdes[] =
91 "a=crypto:1 AES_CM_128_HMAC_SHA1_80 "
92 "inline:d0RmdmcmVCspeEc3QGZiNWpVLFJhQX1cfHAwJSoj\r\n";
93 const char kDataSdes[] =
94 "a=crypto:1 AES_CM_128_HMAC_SHA1_80 "
95 "inline:NzB4d1BINUAvLEw6UzF3WSJ+PSdFcGdUJShpX1Zj\r\n";
96 InjectAfter("a=mid:audio\r\n", kAudioSdes, sdp);
97 InjectAfter("a=mid:video\r\n", kVideoSdes, sdp);
98 InjectAfter("a=mid:data\r\n", kDataSdes, sdp);
101 void UseGice(std::string* sdp) {
102 InjectAfter("t=0 0\r\n", "a=ice-options:google-ice\r\n", sdp);
104 std::string ufragline = "a=ice-ufrag:";
105 std::string pwdline = "a=ice-pwd:";
106 RemoveLinesFromSdp(ufragline, sdp);
107 RemoveLinesFromSdp(pwdline, sdp);
108 ufragline.append(kExternalGiceUfrag);
109 ufragline.append("\r\n");
110 pwdline.append(kExternalGicePwd);
111 pwdline.append("\r\n");
112 const std::string ufrag_pwd = ufragline + pwdline;
114 InjectAfter("a=mid:audio\r\n", ufrag_pwd, sdp);
115 InjectAfter("a=mid:video\r\n", ufrag_pwd, sdp);
116 InjectAfter("a=mid:data\r\n", ufrag_pwd, sdp);
119 void RemoveBundle(std::string* sdp) {
120 RemoveLinesFromSdp("a=group:BUNDLE", sdp);
125 class PeerConnectionEndToEndTest
126 : public sigslot::has_slots<>,
127 public testing::Test {
129 typedef std::vector<rtc::scoped_refptr<DataChannelInterface> >
132 PeerConnectionEndToEndTest()
133 : caller_(new rtc::RefCountedObject<PeerConnectionTestWrapper>(
135 callee_(new rtc::RefCountedObject<PeerConnectionTestWrapper>(
143 void CreatePcs(const MediaConstraintsInterface* pc_constraints) {
144 EXPECT_TRUE(caller_->CreatePc(pc_constraints));
145 EXPECT_TRUE(callee_->CreatePc(pc_constraints));
146 PeerConnectionTestWrapper::Connect(caller_.get(), callee_.get());
148 caller_->SignalOnDataChannel.connect(
149 this, &PeerConnectionEndToEndTest::OnCallerAddedDataChanel);
150 callee_->SignalOnDataChannel.connect(
151 this, &PeerConnectionEndToEndTest::OnCalleeAddedDataChannel);
154 void GetAndAddUserMedia() {
155 FakeConstraints audio_constraints;
156 FakeConstraints video_constraints;
157 GetAndAddUserMedia(true, audio_constraints, true, video_constraints);
160 void GetAndAddUserMedia(bool audio, FakeConstraints audio_constraints,
161 bool video, FakeConstraints video_constraints) {
162 caller_->GetAndAddUserMedia(audio, audio_constraints,
163 video, video_constraints);
164 callee_->GetAndAddUserMedia(audio, audio_constraints,
165 video, video_constraints);
169 caller_->CreateOffer(NULL);
172 void WaitForCallEstablished() {
173 caller_->WaitForCallEstablished();
174 callee_->WaitForCallEstablished();
177 void WaitForConnection() {
178 caller_->WaitForConnection();
179 callee_->WaitForConnection();
182 void SetupLegacySdpConverter() {
183 caller_->SignalOnSdpCreated.connect(
184 this, &PeerConnectionEndToEndTest::ConvertToLegacySdp);
185 callee_->SignalOnSdpCreated.connect(
186 this, &PeerConnectionEndToEndTest::ConvertToLegacySdp);
189 void ConvertToLegacySdp(std::string* sdp) {
190 UseExternalSdes(sdp);
193 LOG(LS_INFO) << "ConvertToLegacySdp: " << *sdp;
196 void SetupGiceConverter() {
197 caller_->SignalOnIceCandidateCreated.connect(
198 this, &PeerConnectionEndToEndTest::AddGiceCredsToCandidate);
199 callee_->SignalOnIceCandidateCreated.connect(
200 this, &PeerConnectionEndToEndTest::AddGiceCredsToCandidate);
203 void AddGiceCredsToCandidate(std::string* sdp) {
204 std::string gice_creds = " username ";
205 gice_creds.append(kExternalGiceUfrag);
206 gice_creds.append(" password ");
207 gice_creds.append(kExternalGicePwd);
208 gice_creds.append("\r\n");
209 Replace("\r\n", gice_creds, sdp);
210 LOG(LS_INFO) << "AddGiceCredsToCandidate: " << *sdp;
213 void OnCallerAddedDataChanel(DataChannelInterface* dc) {
214 caller_signaled_data_channels_.push_back(dc);
217 void OnCalleeAddedDataChannel(DataChannelInterface* dc) {
218 callee_signaled_data_channels_.push_back(dc);
221 // Tests that |dc1| and |dc2| can send to and receive from each other.
222 void TestDataChannelSendAndReceive(
223 DataChannelInterface* dc1, DataChannelInterface* dc2) {
224 rtc::scoped_ptr<webrtc::MockDataChannelObserver> dc1_observer(
225 new webrtc::MockDataChannelObserver(dc1));
227 rtc::scoped_ptr<webrtc::MockDataChannelObserver> dc2_observer(
228 new webrtc::MockDataChannelObserver(dc2));
230 static const std::string kDummyData = "abcdefg";
231 webrtc::DataBuffer buffer(kDummyData);
232 EXPECT_TRUE(dc1->Send(buffer));
233 EXPECT_EQ_WAIT(kDummyData, dc2_observer->last_message(), kMaxWait);
235 EXPECT_TRUE(dc2->Send(buffer));
236 EXPECT_EQ_WAIT(kDummyData, dc1_observer->last_message(), kMaxWait);
238 EXPECT_EQ(1U, dc1_observer->received_message_count());
239 EXPECT_EQ(1U, dc2_observer->received_message_count());
242 void WaitForDataChannelsToOpen(DataChannelInterface* local_dc,
243 const DataChannelList& remote_dc_list,
244 size_t remote_dc_index) {
245 EXPECT_EQ_WAIT(DataChannelInterface::kOpen, local_dc->state(), kMaxWait);
247 EXPECT_TRUE_WAIT(remote_dc_list.size() > remote_dc_index, kMaxWait);
248 EXPECT_EQ_WAIT(DataChannelInterface::kOpen,
249 remote_dc_list[remote_dc_index]->state(),
251 EXPECT_EQ(local_dc->id(), remote_dc_list[remote_dc_index]->id());
254 void CloseDataChannels(DataChannelInterface* local_dc,
255 const DataChannelList& remote_dc_list,
256 size_t remote_dc_index) {
258 EXPECT_EQ_WAIT(DataChannelInterface::kClosed, local_dc->state(), kMaxWait);
259 EXPECT_EQ_WAIT(DataChannelInterface::kClosed,
260 remote_dc_list[remote_dc_index]->state(),
265 rtc::scoped_refptr<PeerConnectionTestWrapper> caller_;
266 rtc::scoped_refptr<PeerConnectionTestWrapper> callee_;
267 DataChannelList caller_signaled_data_channels_;
268 DataChannelList callee_signaled_data_channels_;
271 // Disable for TSan v2, see
272 // https://code.google.com/p/webrtc/issues/detail?id=1205 for details.
273 #if !defined(THREAD_SANITIZER)
275 TEST_F(PeerConnectionEndToEndTest, Call) {
277 GetAndAddUserMedia();
279 WaitForCallEstablished();
282 // Disabled per b/14899892
283 TEST_F(PeerConnectionEndToEndTest, DISABLED_CallWithLegacySdp) {
284 FakeConstraints pc_constraints;
285 pc_constraints.AddMandatory(MediaConstraintsInterface::kEnableDtlsSrtp,
287 CreatePcs(&pc_constraints);
288 SetupLegacySdpConverter();
289 SetupGiceConverter();
290 GetAndAddUserMedia();
292 WaitForCallEstablished();
295 // Verifies that a DataChannel created before the negotiation can transition to
296 // "OPEN" and transfer data.
297 TEST_F(PeerConnectionEndToEndTest, CreateDataChannelBeforeNegotiate) {
298 MAYBE_SKIP_TEST(rtc::SSLStreamAdapter::HaveDtlsSrtp);
302 webrtc::DataChannelInit init;
303 rtc::scoped_refptr<DataChannelInterface> caller_dc(
304 caller_->CreateDataChannel("data", init));
305 rtc::scoped_refptr<DataChannelInterface> callee_dc(
306 callee_->CreateDataChannel("data", init));
311 WaitForDataChannelsToOpen(caller_dc, callee_signaled_data_channels_, 0);
312 WaitForDataChannelsToOpen(callee_dc, caller_signaled_data_channels_, 0);
314 TestDataChannelSendAndReceive(caller_dc, callee_signaled_data_channels_[0]);
315 TestDataChannelSendAndReceive(callee_dc, caller_signaled_data_channels_[0]);
317 CloseDataChannels(caller_dc, callee_signaled_data_channels_, 0);
318 CloseDataChannels(callee_dc, caller_signaled_data_channels_, 0);
321 // Verifies that a DataChannel created after the negotiation can transition to
322 // "OPEN" and transfer data.
323 #if defined(MEMORY_SANITIZER)
324 // Fails under MemorySanitizer:
325 // See https://code.google.com/p/webrtc/issues/detail?id=3980.
326 #define MAYBE_CreateDataChannelAfterNegotiate DISABLED_CreateDataChannelAfterNegotiate
328 #define MAYBE_CreateDataChannelAfterNegotiate CreateDataChannelAfterNegotiate
330 TEST_F(PeerConnectionEndToEndTest, MAYBE_CreateDataChannelAfterNegotiate) {
331 MAYBE_SKIP_TEST(rtc::SSLStreamAdapter::HaveDtlsSrtp);
335 webrtc::DataChannelInit init;
337 // This DataChannel is for creating the data content in the negotiation.
338 rtc::scoped_refptr<DataChannelInterface> dummy(
339 caller_->CreateDataChannel("data", init));
343 // Creates new DataChannels after the negotiation and verifies their states.
344 rtc::scoped_refptr<DataChannelInterface> caller_dc(
345 caller_->CreateDataChannel("hello", init));
346 rtc::scoped_refptr<DataChannelInterface> callee_dc(
347 callee_->CreateDataChannel("hello", init));
349 WaitForDataChannelsToOpen(caller_dc, callee_signaled_data_channels_, 1);
350 WaitForDataChannelsToOpen(callee_dc, caller_signaled_data_channels_, 0);
352 TestDataChannelSendAndReceive(caller_dc, callee_signaled_data_channels_[1]);
353 TestDataChannelSendAndReceive(callee_dc, caller_signaled_data_channels_[0]);
355 CloseDataChannels(caller_dc, callee_signaled_data_channels_, 1);
356 CloseDataChannels(callee_dc, caller_signaled_data_channels_, 0);
359 // Verifies that DataChannel IDs are even/odd based on the DTLS roles.
360 TEST_F(PeerConnectionEndToEndTest, DataChannelIdAssignment) {
361 MAYBE_SKIP_TEST(rtc::SSLStreamAdapter::HaveDtlsSrtp);
365 webrtc::DataChannelInit init;
366 rtc::scoped_refptr<DataChannelInterface> caller_dc_1(
367 caller_->CreateDataChannel("data", init));
368 rtc::scoped_refptr<DataChannelInterface> callee_dc_1(
369 callee_->CreateDataChannel("data", init));
374 EXPECT_EQ(1U, caller_dc_1->id() % 2);
375 EXPECT_EQ(0U, callee_dc_1->id() % 2);
377 rtc::scoped_refptr<DataChannelInterface> caller_dc_2(
378 caller_->CreateDataChannel("data", init));
379 rtc::scoped_refptr<DataChannelInterface> callee_dc_2(
380 callee_->CreateDataChannel("data", init));
382 EXPECT_EQ(1U, caller_dc_2->id() % 2);
383 EXPECT_EQ(0U, callee_dc_2->id() % 2);
386 // Verifies that the message is received by the right remote DataChannel when
387 // there are multiple DataChannels.
388 TEST_F(PeerConnectionEndToEndTest,
389 MessageTransferBetweenTwoPairsOfDataChannels) {
390 MAYBE_SKIP_TEST(rtc::SSLStreamAdapter::HaveDtlsSrtp);
394 webrtc::DataChannelInit init;
396 rtc::scoped_refptr<DataChannelInterface> caller_dc_1(
397 caller_->CreateDataChannel("data", init));
398 rtc::scoped_refptr<DataChannelInterface> caller_dc_2(
399 caller_->CreateDataChannel("data", init));
403 WaitForDataChannelsToOpen(caller_dc_1, callee_signaled_data_channels_, 0);
404 WaitForDataChannelsToOpen(caller_dc_2, callee_signaled_data_channels_, 1);
406 rtc::scoped_ptr<webrtc::MockDataChannelObserver> dc_1_observer(
407 new webrtc::MockDataChannelObserver(callee_signaled_data_channels_[0]));
409 rtc::scoped_ptr<webrtc::MockDataChannelObserver> dc_2_observer(
410 new webrtc::MockDataChannelObserver(callee_signaled_data_channels_[1]));
412 const std::string message_1 = "hello 1";
413 const std::string message_2 = "hello 2";
415 caller_dc_1->Send(webrtc::DataBuffer(message_1));
416 EXPECT_EQ_WAIT(message_1, dc_1_observer->last_message(), kMaxWait);
418 caller_dc_2->Send(webrtc::DataBuffer(message_2));
419 EXPECT_EQ_WAIT(message_2, dc_2_observer->last_message(), kMaxWait);
421 EXPECT_EQ(1U, dc_1_observer->received_message_count());
422 EXPECT_EQ(1U, dc_2_observer->received_message_count());
424 #endif // if !defined(THREAD_SANITIZER)