blob: 0d9bf62b11c653f093d40b71b6355fd052c4b55f [file] [log] [blame]
Steve Anton8d3444d2017-10-20 15:30:51 -07001/*
2 * Copyright 2017 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11// This file contains tests that check the PeerConnection's signaling state
12// machine, as well as tests that check basic, media-agnostic aspects of SDP.
13
14#include <tuple>
15
16#include "api/audio_codecs/builtin_audio_decoder_factory.h"
17#include "api/audio_codecs/builtin_audio_encoder_factory.h"
18#include "api/peerconnectionproxy.h"
19#include "pc/peerconnection.h"
20#include "pc/peerconnectionwrapper.h"
21#include "pc/sdputils.h"
22#ifdef WEBRTC_ANDROID
23#include "pc/test/androidtestinitializer.h"
24#endif
25#include "pc/test/fakeaudiocapturemodule.h"
26#include "pc/test/fakertccertificategenerator.h"
27#include "rtc_base/gunit.h"
28#include "rtc_base/ptr_util.h"
29#include "rtc_base/stringutils.h"
30#include "rtc_base/virtualsocketserver.h"
31#include "test/gmock.h"
32
33namespace webrtc {
34
35using SignalingState = PeerConnectionInterface::SignalingState;
36using RTCConfiguration = PeerConnectionInterface::RTCConfiguration;
37using RTCOfferAnswerOptions = PeerConnectionInterface::RTCOfferAnswerOptions;
38using ::testing::Bool;
39using ::testing::Combine;
40using ::testing::Values;
41
42class PeerConnectionWrapperForSignalingTest : public PeerConnectionWrapper {
43 public:
44 using PeerConnectionWrapper::PeerConnectionWrapper;
45
46 bool initial_offerer() {
47 return GetInternalPeerConnection()->initial_offerer();
48 }
49
50 PeerConnection* GetInternalPeerConnection() {
51 auto* pci = reinterpret_cast<
52 PeerConnectionProxyWithInternal<PeerConnectionInterface>*>(pc());
53 return reinterpret_cast<PeerConnection*>(pci->internal());
54 }
55};
56
57class PeerConnectionSignalingTest : public ::testing::Test {
58 protected:
59 typedef std::unique_ptr<PeerConnectionWrapperForSignalingTest> WrapperPtr;
60
61 PeerConnectionSignalingTest()
62 : vss_(new rtc::VirtualSocketServer()), main_(vss_.get()) {
63#ifdef WEBRTC_ANDROID
64 InitializeAndroidObjects();
65#endif
66 pc_factory_ = CreatePeerConnectionFactory(
67 rtc::Thread::Current(), rtc::Thread::Current(), rtc::Thread::Current(),
68 FakeAudioCaptureModule::Create(), CreateBuiltinAudioEncoderFactory(),
69 CreateBuiltinAudioDecoderFactory(), nullptr, nullptr);
70 }
71
72 WrapperPtr CreatePeerConnection() {
73 return CreatePeerConnection(RTCConfiguration());
74 }
75
76 WrapperPtr CreatePeerConnection(const RTCConfiguration& config) {
77 auto observer = rtc::MakeUnique<MockPeerConnectionObserver>();
78 auto pc = pc_factory_->CreatePeerConnection(config, nullptr, nullptr,
79 observer.get());
80 if (!pc) {
81 return nullptr;
82 }
83
84 return rtc::MakeUnique<PeerConnectionWrapperForSignalingTest>(
85 pc_factory_, pc, std::move(observer));
86 }
87
88 // Accepts the same arguments as CreatePeerConnection and adds default audio
89 // and video tracks.
90 template <typename... Args>
91 WrapperPtr CreatePeerConnectionWithAudioVideo(Args&&... args) {
92 auto wrapper = CreatePeerConnection(std::forward<Args>(args)...);
93 if (!wrapper) {
94 return nullptr;
95 }
96 wrapper->AddAudioTrack("a");
97 wrapper->AddVideoTrack("v");
98 return wrapper;
99 }
100
101 std::unique_ptr<rtc::VirtualSocketServer> vss_;
102 rtc::AutoSocketServerThread main_;
103 rtc::scoped_refptr<PeerConnectionFactoryInterface> pc_factory_;
104};
105
106TEST_F(PeerConnectionSignalingTest, SetLocalOfferTwiceWorks) {
107 auto caller = CreatePeerConnection();
108
109 EXPECT_TRUE(caller->SetLocalDescription(caller->CreateOffer()));
110 EXPECT_TRUE(caller->SetLocalDescription(caller->CreateOffer()));
111}
112
113TEST_F(PeerConnectionSignalingTest, SetRemoteOfferTwiceWorks) {
114 auto caller = CreatePeerConnection();
115 auto callee = CreatePeerConnection();
116
117 EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOffer()));
118 EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOffer()));
119}
120
121TEST_F(PeerConnectionSignalingTest, FailToSetNullLocalDescription) {
122 auto caller = CreatePeerConnection();
123 std::string error;
124 ASSERT_FALSE(caller->SetLocalDescription(nullptr, &error));
125 EXPECT_EQ("SessionDescription is NULL.", error);
126}
127
128TEST_F(PeerConnectionSignalingTest, FailToSetNullRemoteDescription) {
129 auto caller = CreatePeerConnection();
130 std::string error;
131 ASSERT_FALSE(caller->SetRemoteDescription(nullptr, &error));
132 EXPECT_EQ("SessionDescription is NULL.", error);
133}
134
135// The following parameterized test verifies that calls to various signaling
136// methods on PeerConnection will succeed/fail depending on what is the
137// PeerConnection's signaling state. Note that the test tries many different
138// forms of SignalingState::kClosed by arriving at a valid state then calling
139// |Close()|. This is intended to catch cases where the PeerConnection signaling
140// method ignores the closed flag but may work/not work because of the single
141// state the PeerConnection was created in before it was closed.
142
143class PeerConnectionSignalingStateTest
144 : public PeerConnectionSignalingTest,
145 public ::testing::WithParamInterface<std::tuple<SignalingState, bool>> {
146 protected:
147 RTCConfiguration GetConfig() {
148 RTCConfiguration config;
149 config.certificates.push_back(
150 FakeRTCCertificateGenerator::GenerateCertificate());
151 return config;
152 }
153
154 WrapperPtr CreatePeerConnectionInState(SignalingState state) {
155 return CreatePeerConnectionInState(std::make_tuple(state, false));
156 }
157
158 WrapperPtr CreatePeerConnectionInState(
159 std::tuple<SignalingState, bool> state_tuple) {
160 SignalingState state = std::get<0>(state_tuple);
161 bool closed = std::get<1>(state_tuple);
162
163 auto wrapper = CreatePeerConnectionWithAudioVideo(GetConfig());
164 switch (state) {
165 case SignalingState::kStable: {
166 break;
167 }
168 case SignalingState::kHaveLocalOffer: {
169 wrapper->SetLocalDescription(wrapper->CreateOffer());
170 break;
171 }
172 case SignalingState::kHaveLocalPrAnswer: {
173 auto caller = CreatePeerConnectionWithAudioVideo(GetConfig());
174 wrapper->SetRemoteDescription(caller->CreateOffer());
175 auto answer = wrapper->CreateAnswer();
176 wrapper->SetLocalDescription(CloneSessionDescriptionAsType(
177 answer.get(), SessionDescriptionInterface::kPrAnswer));
178 break;
179 }
180 case SignalingState::kHaveRemoteOffer: {
181 auto caller = CreatePeerConnectionWithAudioVideo(GetConfig());
182 wrapper->SetRemoteDescription(caller->CreateOffer());
183 break;
184 }
185 case SignalingState::kHaveRemotePrAnswer: {
186 auto callee = CreatePeerConnectionWithAudioVideo(GetConfig());
187 callee->SetRemoteDescription(wrapper->CreateOfferAndSetAsLocal());
188 auto answer = callee->CreateAnswer();
189 wrapper->SetRemoteDescription(CloneSessionDescriptionAsType(
190 answer.get(), SessionDescriptionInterface::kPrAnswer));
191 break;
192 }
193 case SignalingState::kClosed: {
194 RTC_NOTREACHED() << "Set the second member of the tuple to true to "
195 "achieve a closed state from an existing, valid "
196 "state.";
197 }
198 }
199
200 RTC_DCHECK_EQ(state, wrapper->pc()->signaling_state());
201
202 if (closed) {
203 wrapper->pc()->Close();
204 RTC_DCHECK_EQ(SignalingState::kClosed, wrapper->signaling_state());
205 }
206
207 return wrapper;
208 }
209};
210
211::testing::AssertionResult AssertStartsWith(const char* str_expr,
212 const char* prefix_expr,
213 const std::string& str,
214 const std::string& prefix) {
215 if (rtc::starts_with(str.c_str(), prefix.c_str())) {
216 return ::testing::AssertionSuccess();
217 } else {
218 return ::testing::AssertionFailure()
219 << str_expr << "\nwhich is\n\"" << str << "\"\ndoes not start with\n"
220 << prefix_expr << "\nwhich is\n\"" << prefix << "\"";
221 }
222}
223
224TEST_P(PeerConnectionSignalingStateTest, CreateOffer) {
225 auto wrapper = CreatePeerConnectionInState(GetParam());
226 if (wrapper->signaling_state() != SignalingState::kClosed) {
227 EXPECT_TRUE(wrapper->CreateOffer());
228 } else {
229 std::string error;
230 ASSERT_FALSE(wrapper->CreateOffer(RTCOfferAnswerOptions(), &error));
231 EXPECT_PRED_FORMAT2(AssertStartsWith, error,
232 "CreateOffer called when PeerConnection is closed.");
233 }
234}
235
236TEST_P(PeerConnectionSignalingStateTest, CreateAnswer) {
237 auto wrapper = CreatePeerConnectionInState(GetParam());
238 if (wrapper->signaling_state() == SignalingState::kHaveLocalPrAnswer ||
239 wrapper->signaling_state() == SignalingState::kHaveRemoteOffer) {
240 EXPECT_TRUE(wrapper->CreateAnswer());
241 } else {
242 std::string error;
243 ASSERT_FALSE(wrapper->CreateAnswer(RTCOfferAnswerOptions(), &error));
244 if (wrapper->signaling_state() == SignalingState::kClosed) {
245 EXPECT_PRED_FORMAT2(AssertStartsWith, error,
246 "CreateAnswer called when PeerConnection is closed.");
247 } else if (wrapper->signaling_state() ==
248 SignalingState::kHaveRemotePrAnswer) {
249 EXPECT_PRED_FORMAT2(AssertStartsWith, error,
250 "CreateAnswer called without remote offer.");
251 } else {
252 EXPECT_PRED_FORMAT2(
253 AssertStartsWith, error,
254 "CreateAnswer can't be called before SetRemoteDescription.");
255 }
256 }
257}
258
259TEST_P(PeerConnectionSignalingStateTest, SetLocalOffer) {
260 auto wrapper = CreatePeerConnectionInState(GetParam());
261 if (wrapper->signaling_state() == SignalingState::kStable ||
262 wrapper->signaling_state() == SignalingState::kHaveLocalOffer) {
263 // Need to call CreateOffer on the PeerConnection under test, otherwise when
264 // setting the local offer it will want to verify the DTLS fingerprint
265 // against the locally generated certificate, but without a call to
266 // CreateOffer the certificate will never be generated.
267 EXPECT_TRUE(wrapper->SetLocalDescription(wrapper->CreateOffer()));
268 } else {
269 auto wrapper_for_offer =
270 CreatePeerConnectionInState(SignalingState::kHaveLocalOffer);
271 auto offer =
272 CloneSessionDescription(wrapper_for_offer->pc()->local_description());
273
274 std::string error;
275 ASSERT_FALSE(wrapper->SetLocalDescription(std::move(offer), &error));
276 EXPECT_PRED_FORMAT2(
277 AssertStartsWith, error,
278 "Failed to set local offer sdp: Called in wrong state:");
279 }
280}
281
282TEST_P(PeerConnectionSignalingStateTest, SetLocalPrAnswer) {
283 auto wrapper_for_pranswer =
284 CreatePeerConnectionInState(SignalingState::kHaveLocalPrAnswer);
285 auto pranswer =
286 CloneSessionDescription(wrapper_for_pranswer->pc()->local_description());
287
288 auto wrapper = CreatePeerConnectionInState(GetParam());
289 if (wrapper->signaling_state() == SignalingState::kHaveLocalPrAnswer ||
290 wrapper->signaling_state() == SignalingState::kHaveRemoteOffer) {
291 EXPECT_TRUE(wrapper->SetLocalDescription(std::move(pranswer)));
292 } else {
293 std::string error;
294 ASSERT_FALSE(wrapper->SetLocalDescription(std::move(pranswer), &error));
295 EXPECT_PRED_FORMAT2(
296 AssertStartsWith, error,
297 "Failed to set local pranswer sdp: Called in wrong state:");
298 }
299}
300
301TEST_P(PeerConnectionSignalingStateTest, SetLocalAnswer) {
302 auto wrapper_for_answer =
303 CreatePeerConnectionInState(SignalingState::kHaveRemoteOffer);
304 auto answer = wrapper_for_answer->CreateAnswer();
305
306 auto wrapper = CreatePeerConnectionInState(GetParam());
307 if (wrapper->signaling_state() == SignalingState::kHaveLocalPrAnswer ||
308 wrapper->signaling_state() == SignalingState::kHaveRemoteOffer) {
309 EXPECT_TRUE(wrapper->SetLocalDescription(std::move(answer)));
310 } else {
311 std::string error;
312 ASSERT_FALSE(wrapper->SetLocalDescription(std::move(answer), &error));
313 EXPECT_PRED_FORMAT2(
314 AssertStartsWith, error,
315 "Failed to set local answer sdp: Called in wrong state:");
316 }
317}
318
319TEST_P(PeerConnectionSignalingStateTest, SetRemoteOffer) {
320 auto wrapper_for_offer =
321 CreatePeerConnectionInState(SignalingState::kHaveRemoteOffer);
322 auto offer =
323 CloneSessionDescription(wrapper_for_offer->pc()->remote_description());
324
325 auto wrapper = CreatePeerConnectionInState(GetParam());
326 if (wrapper->signaling_state() == SignalingState::kStable ||
327 wrapper->signaling_state() == SignalingState::kHaveRemoteOffer) {
328 EXPECT_TRUE(wrapper->SetRemoteDescription(std::move(offer)));
329 } else {
330 std::string error;
331 ASSERT_FALSE(wrapper->SetRemoteDescription(std::move(offer), &error));
332 EXPECT_PRED_FORMAT2(
333 AssertStartsWith, error,
334 "Failed to set remote offer sdp: Called in wrong state:");
335 }
336}
337
338TEST_P(PeerConnectionSignalingStateTest, SetRemotePrAnswer) {
339 auto wrapper_for_pranswer =
340 CreatePeerConnectionInState(SignalingState::kHaveRemotePrAnswer);
341 auto pranswer =
342 CloneSessionDescription(wrapper_for_pranswer->pc()->remote_description());
343
344 auto wrapper = CreatePeerConnectionInState(GetParam());
345 if (wrapper->signaling_state() == SignalingState::kHaveLocalOffer ||
346 wrapper->signaling_state() == SignalingState::kHaveRemotePrAnswer) {
347 EXPECT_TRUE(wrapper->SetRemoteDescription(std::move(pranswer)));
348 } else {
349 std::string error;
350 ASSERT_FALSE(wrapper->SetRemoteDescription(std::move(pranswer), &error));
351 EXPECT_PRED_FORMAT2(
352 AssertStartsWith, error,
353 "Failed to set remote pranswer sdp: Called in wrong state:");
354 }
355}
356
357TEST_P(PeerConnectionSignalingStateTest, SetRemoteAnswer) {
358 auto wrapper_for_answer =
359 CreatePeerConnectionInState(SignalingState::kHaveRemoteOffer);
360 auto answer = wrapper_for_answer->CreateAnswer();
361
362 auto wrapper = CreatePeerConnectionInState(GetParam());
363 if (wrapper->signaling_state() == SignalingState::kHaveLocalOffer ||
364 wrapper->signaling_state() == SignalingState::kHaveRemotePrAnswer) {
365 EXPECT_TRUE(wrapper->SetRemoteDescription(std::move(answer)));
366 } else {
367 std::string error;
368 ASSERT_FALSE(wrapper->SetRemoteDescription(std::move(answer), &error));
369 EXPECT_PRED_FORMAT2(
370 AssertStartsWith, error,
371 "Failed to set remote answer sdp: Called in wrong state:");
372 }
373}
374
375INSTANTIATE_TEST_CASE_P(PeerConnectionSignalingTest,
376 PeerConnectionSignalingStateTest,
377 Combine(Values(SignalingState::kStable,
378 SignalingState::kHaveLocalOffer,
379 SignalingState::kHaveLocalPrAnswer,
380 SignalingState::kHaveRemoteOffer,
381 SignalingState::kHaveRemotePrAnswer),
382 Bool()));
383
384TEST_F(PeerConnectionSignalingTest,
385 CreateAnswerSucceedsIfStableAndRemoteDescriptionIsOffer) {
386 auto caller = CreatePeerConnection();
387 auto callee = CreatePeerConnection();
388
389 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
390 ASSERT_TRUE(
391 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
392
393 ASSERT_EQ(SignalingState::kStable, callee->signaling_state());
394 EXPECT_TRUE(callee->CreateAnswer());
395}
396
397TEST_F(PeerConnectionSignalingTest,
398 CreateAnswerFailsIfStableButRemoteDescriptionIsAnswer) {
399 auto caller = CreatePeerConnection();
400 auto callee = CreatePeerConnection();
401
402 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
403 ASSERT_TRUE(
404 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
405
406 ASSERT_EQ(SignalingState::kStable, caller->signaling_state());
407 std::string error;
408 ASSERT_FALSE(caller->CreateAnswer(RTCOfferAnswerOptions(), &error));
409 EXPECT_EQ("CreateAnswer called without remote offer.", error);
410}
411
412// According to https://tools.ietf.org/html/rfc3264#section-8, the session id
413// stays the same but the version must be incremented if a later, different
414// session description is generated. These two tests verify that is the case for
415// both offers and answers.
416TEST_F(PeerConnectionSignalingTest,
417 SessionVersionIncrementedInSubsequentDifferentOffer) {
418 auto caller = CreatePeerConnection();
419 auto callee = CreatePeerConnection();
420
421 auto original_offer = caller->CreateOfferAndSetAsLocal();
422 const std::string original_id = original_offer->session_id();
423 const std::string original_version = original_offer->session_version();
424
425 ASSERT_TRUE(callee->SetRemoteDescription(std::move(original_offer)));
426 ASSERT_TRUE(caller->SetRemoteDescription(callee->CreateAnswer()));
427
428 // Add track to get a different offer.
429 caller->AddAudioTrack("a");
430
431 auto later_offer = caller->CreateOffer();
432
433 EXPECT_EQ(original_id, later_offer->session_id());
434 EXPECT_LT(rtc::FromString<uint64_t>(original_version),
435 rtc::FromString<uint64_t>(later_offer->session_version()));
436}
437TEST_F(PeerConnectionSignalingTest,
438 SessionVersionIncrementedInSubsequentDifferentAnswer) {
439 auto caller = CreatePeerConnection();
440 auto callee = CreatePeerConnection();
441
442 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
443
444 auto original_answer = callee->CreateAnswerAndSetAsLocal();
445 const std::string original_id = original_answer->session_id();
446 const std::string original_version = original_answer->session_version();
447
448 // Add track to get a different answer.
449 callee->AddAudioTrack("a");
450
451 auto later_answer = callee->CreateAnswer();
452
453 EXPECT_EQ(original_id, later_answer->session_id());
454 EXPECT_LT(rtc::FromString<uint64_t>(original_version),
455 rtc::FromString<uint64_t>(later_answer->session_version()));
456}
457
458TEST_F(PeerConnectionSignalingTest, InitiatorFlagSetOnCallerAndNotOnCallee) {
459 auto caller = CreatePeerConnectionWithAudioVideo();
460 auto callee = CreatePeerConnectionWithAudioVideo();
461
462 EXPECT_FALSE(caller->initial_offerer());
463 EXPECT_FALSE(callee->initial_offerer());
464
465 ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
466
467 EXPECT_TRUE(caller->initial_offerer());
468 EXPECT_FALSE(callee->initial_offerer());
469
470 ASSERT_TRUE(
471 caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
472
473 EXPECT_TRUE(caller->initial_offerer());
474 EXPECT_FALSE(callee->initial_offerer());
475}
476
477// Test creating a PeerConnection, request multiple offers, destroy the
478// PeerConnection and make sure we get success/failure callbacks for all of the
479// requests.
480// Background: crbug.com/507307
481TEST_F(PeerConnectionSignalingTest, CreateOffersAndShutdown) {
482 auto caller = CreatePeerConnection();
483
484 RTCOfferAnswerOptions options;
485 options.offer_to_receive_audio =
486 RTCOfferAnswerOptions::kOfferToReceiveMediaTrue;
487
488 rtc::scoped_refptr<MockCreateSessionDescriptionObserver> observers[100];
489 for (auto& observer : observers) {
490 observer =
491 new rtc::RefCountedObject<MockCreateSessionDescriptionObserver>();
492 caller->pc()->CreateOffer(observer, options);
493 }
494
495 // Destroy the PeerConnection.
496 caller.reset(nullptr);
497
498 for (auto& observer : observers) {
499 // We expect to have received a notification now even if the PeerConnection
500 // was terminated. The offer creation may or may not have succeeded, but we
501 // must have received a notification.
502 EXPECT_TRUE(observer->called());
503 }
504}
505
506} // namespace webrtc