1/*
2 * libjingle
3 * Copyright 2012, 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 <string>
29
30#include "talk/app/webrtc/fakeportallocatorfactory.h"
31#include "talk/app/webrtc/mediastreaminterface.h"
32#include "talk/app/webrtc/peerconnectionfactory.h"
33#include "talk/app/webrtc/test/fakevideotrackrenderer.h"
34#include "talk/app/webrtc/videosourceinterface.h"
35#include "talk/media/base/fakevideocapturer.h"
36#include "talk/media/webrtc/webrtccommon.h"
37#include "talk/media/webrtc/webrtcvoe.h"
38#include "webrtc/base/gunit.h"
39#include "webrtc/base/scoped_ptr.h"
40#include "webrtc/base/thread.h"
41
42using webrtc::FakeVideoTrackRenderer;
43using webrtc::MediaStreamInterface;
44using webrtc::PeerConnectionFactoryInterface;
45using webrtc::PeerConnectionInterface;
46using webrtc::PeerConnectionObserver;
47using webrtc::PortAllocatorFactoryInterface;
48using webrtc::VideoSourceInterface;
49using webrtc::VideoTrackInterface;
50
51namespace {
52
53typedef std::vector<PortAllocatorFactoryInterface::StunConfiguration>
54    StunConfigurations;
55typedef std::vector<PortAllocatorFactoryInterface::TurnConfiguration>
56    TurnConfigurations;
57
58static const char kStunIceServer[] = "stun:stun.l.google.com:19302";
59static const char kTurnIceServer[] = "turn:test%40hello.com@test.com:1234";
60static const char kTurnIceServerWithTransport[] =
61    "turn:test@hello.com?transport=tcp";
62static const char kSecureTurnIceServer[] =
63    "turns:test@hello.com?transport=tcp";
64static const char kSecureTurnIceServerWithoutTransportParam[] =
65    "turns:test_no_transport@hello.com:443";
66static const char kSecureTurnIceServerWithoutTransportAndPortParam[] =
67    "turns:test_no_transport@hello.com";
68static const char kTurnIceServerWithNoUsernameInUri[] =
69    "turn:test.com:1234";
70static const char kTurnPassword[] = "turnpassword";
71static const int kDefaultStunPort = 3478;
72static const int kDefaultStunTlsPort = 5349;
73static const char kTurnUsername[] = "test";
74static const char kStunIceServerWithIPv4Address[] = "stun:1.2.3.4:1234";
75static const char kStunIceServerWithIPv4AddressWithoutPort[] = "stun:1.2.3.4";
76static const char kStunIceServerWithIPv6Address[] = "stun:[2401:fa00:4::]:1234";
77static const char kStunIceServerWithIPv6AddressWithoutPort[] =
78    "stun:[2401:fa00:4::]";
79static const char kStunIceServerWithInvalidIPv6Address[] =
80    "stun:[2401:fa00:4:::3478";
81static const char kTurnIceServerWithIPv6Address[] =
82    "turn:test@[2401:fa00:4::]:1234";
83
84class NullPeerConnectionObserver : public PeerConnectionObserver {
85 public:
86  virtual void OnError() {}
87  virtual void OnMessage(const std::string& msg) {}
88  virtual void OnSignalingMessage(const std::string& msg) {}
89  virtual void OnSignalingChange(
90      PeerConnectionInterface::SignalingState new_state) {}
91  virtual void OnAddStream(MediaStreamInterface* stream) {}
92  virtual void OnRemoveStream(MediaStreamInterface* stream) {}
93  virtual void OnRenegotiationNeeded() {}
94  virtual void OnIceConnectionChange(
95      PeerConnectionInterface::IceConnectionState new_state) {}
96  virtual void OnIceGatheringChange(
97      PeerConnectionInterface::IceGatheringState new_state) {}
98  virtual void OnIceCandidate(const webrtc::IceCandidateInterface* candidate) {}
99};
100
101}  // namespace
102
103class PeerConnectionFactoryTest : public testing::Test {
104  void SetUp() {
105    factory_ = webrtc::CreatePeerConnectionFactory(rtc::Thread::Current(),
106                                                   rtc::Thread::Current(),
107                                                   NULL,
108                                                   NULL,
109                                                   NULL);
110
111    ASSERT_TRUE(factory_.get() != NULL);
112    allocator_factory_ =  webrtc::FakePortAllocatorFactory::Create();
113  }
114
115 protected:
116  void VerifyStunConfigurations(StunConfigurations stun_config) {
117    webrtc::FakePortAllocatorFactory* allocator =
118        static_cast<webrtc::FakePortAllocatorFactory*>(
119            allocator_factory_.get());
120    ASSERT_TRUE(allocator != NULL);
121    EXPECT_EQ(stun_config.size(), allocator->stun_configs().size());
122    for (size_t i = 0; i < stun_config.size(); ++i) {
123      EXPECT_EQ(stun_config[i].server.ToString(),
124                allocator->stun_configs()[i].server.ToString());
125    }
126  }
127
128  void VerifyTurnConfigurations(TurnConfigurations turn_config) {
129    webrtc::FakePortAllocatorFactory* allocator =
130        static_cast<webrtc::FakePortAllocatorFactory*>(
131            allocator_factory_.get());
132    ASSERT_TRUE(allocator != NULL);
133    EXPECT_EQ(turn_config.size(), allocator->turn_configs().size());
134    for (size_t i = 0; i < turn_config.size(); ++i) {
135      EXPECT_EQ(turn_config[i].server.ToString(),
136                allocator->turn_configs()[i].server.ToString());
137      EXPECT_EQ(turn_config[i].username, allocator->turn_configs()[i].username);
138      EXPECT_EQ(turn_config[i].password, allocator->turn_configs()[i].password);
139      EXPECT_EQ(turn_config[i].transport_type,
140                allocator->turn_configs()[i].transport_type);
141    }
142  }
143
144  rtc::scoped_refptr<PeerConnectionFactoryInterface> factory_;
145  NullPeerConnectionObserver observer_;
146  rtc::scoped_refptr<PortAllocatorFactoryInterface> allocator_factory_;
147};
148
149// Verify creation of PeerConnection using internal ADM, video factory and
150// internal libjingle threads.
151TEST(PeerConnectionFactoryTestInternal, CreatePCUsingInternalModules) {
152  rtc::scoped_refptr<PeerConnectionFactoryInterface> factory(
153      webrtc::CreatePeerConnectionFactory());
154
155  NullPeerConnectionObserver observer;
156  webrtc::PeerConnectionInterface::IceServers servers;
157
158  rtc::scoped_refptr<PeerConnectionInterface> pc(
159      factory->CreatePeerConnection(servers, NULL, NULL, NULL, &observer));
160
161  EXPECT_TRUE(pc.get() != NULL);
162}
163
164// This test verifies creation of PeerConnection with valid STUN and TURN
165// configuration. Also verifies the URL's parsed correctly as expected.
166TEST_F(PeerConnectionFactoryTest, CreatePCUsingIceServers) {
167  PeerConnectionInterface::RTCConfiguration config;
168  webrtc::PeerConnectionInterface::IceServer ice_server;
169  ice_server.uri = kStunIceServer;
170  config.servers.push_back(ice_server);
171  ice_server.uri = kTurnIceServer;
172  ice_server.password = kTurnPassword;
173  config.servers.push_back(ice_server);
174  ice_server.uri = kTurnIceServerWithTransport;
175  ice_server.password = kTurnPassword;
176  config.servers.push_back(ice_server);
177  rtc::scoped_refptr<PeerConnectionInterface> pc(
178      factory_->CreatePeerConnection(config, NULL,
179                                     allocator_factory_.get(),
180                                     NULL,
181                                     &observer_));
182  EXPECT_TRUE(pc.get() != NULL);
183  StunConfigurations stun_configs;
184  webrtc::PortAllocatorFactoryInterface::StunConfiguration stun1(
185      "stun.l.google.com", 19302);
186  stun_configs.push_back(stun1);
187  VerifyStunConfigurations(stun_configs);
188  TurnConfigurations turn_configs;
189  webrtc::PortAllocatorFactoryInterface::TurnConfiguration turn1(
190      "test.com", 1234, "test@hello.com", kTurnPassword, "udp", false);
191  turn_configs.push_back(turn1);
192  webrtc::PortAllocatorFactoryInterface::TurnConfiguration turn2(
193      "hello.com", kDefaultStunPort, "test", kTurnPassword, "tcp", false);
194  turn_configs.push_back(turn2);
195  VerifyTurnConfigurations(turn_configs);
196}
197
198// This test verifies creation of PeerConnection with valid STUN and TURN
199// configuration. Also verifies the URL's parsed correctly as expected.
200// This version doesn't use RTCConfiguration.
201// TODO(mallinath) - Remove this method after clients start using RTCConfig.
202TEST_F(PeerConnectionFactoryTest, CreatePCUsingIceServersOldSignature) {
203  webrtc::PeerConnectionInterface::IceServers ice_servers;
204  webrtc::PeerConnectionInterface::IceServer ice_server;
205  ice_server.uri = kStunIceServer;
206  ice_servers.push_back(ice_server);
207  ice_server.uri = kTurnIceServer;
208  ice_server.password = kTurnPassword;
209  ice_servers.push_back(ice_server);
210  ice_server.uri = kTurnIceServerWithTransport;
211  ice_server.password = kTurnPassword;
212  ice_servers.push_back(ice_server);
213  rtc::scoped_refptr<PeerConnectionInterface> pc(
214      factory_->CreatePeerConnection(ice_servers, NULL,
215                                     allocator_factory_.get(),
216                                     NULL,
217                                     &observer_));
218  EXPECT_TRUE(pc.get() != NULL);
219  StunConfigurations stun_configs;
220  webrtc::PortAllocatorFactoryInterface::StunConfiguration stun1(
221      "stun.l.google.com", 19302);
222  stun_configs.push_back(stun1);
223  VerifyStunConfigurations(stun_configs);
224  TurnConfigurations turn_configs;
225  webrtc::PortAllocatorFactoryInterface::TurnConfiguration turn1(
226      "test.com", 1234, "test@hello.com", kTurnPassword, "udp", false);
227  turn_configs.push_back(turn1);
228  webrtc::PortAllocatorFactoryInterface::TurnConfiguration turn2(
229      "hello.com", kDefaultStunPort, "test", kTurnPassword, "tcp", false);
230  turn_configs.push_back(turn2);
231  VerifyTurnConfigurations(turn_configs);
232}
233
234TEST_F(PeerConnectionFactoryTest, CreatePCUsingNoUsernameInUri) {
235  PeerConnectionInterface::RTCConfiguration config;
236  webrtc::PeerConnectionInterface::IceServer ice_server;
237  ice_server.uri = kStunIceServer;
238  config.servers.push_back(ice_server);
239  ice_server.uri = kTurnIceServerWithNoUsernameInUri;
240  ice_server.username = kTurnUsername;
241  ice_server.password = kTurnPassword;
242  config.servers.push_back(ice_server);
243  rtc::scoped_refptr<PeerConnectionInterface> pc(
244      factory_->CreatePeerConnection(config, NULL,
245                                     allocator_factory_.get(),
246                                     NULL,
247                                     &observer_));
248  EXPECT_TRUE(pc.get() != NULL);
249  TurnConfigurations turn_configs;
250  webrtc::PortAllocatorFactoryInterface::TurnConfiguration turn(
251      "test.com", 1234, kTurnUsername, kTurnPassword, "udp", false);
252  turn_configs.push_back(turn);
253  VerifyTurnConfigurations(turn_configs);
254}
255
256// This test verifies the PeerConnection created properly with TURN url which
257// has transport parameter in it.
258TEST_F(PeerConnectionFactoryTest, CreatePCUsingTurnUrlWithTransportParam) {
259  PeerConnectionInterface::RTCConfiguration config;
260  webrtc::PeerConnectionInterface::IceServer ice_server;
261  ice_server.uri = kTurnIceServerWithTransport;
262  ice_server.password = kTurnPassword;
263  config.servers.push_back(ice_server);
264  rtc::scoped_refptr<PeerConnectionInterface> pc(
265      factory_->CreatePeerConnection(config, NULL,
266                                     allocator_factory_.get(),
267                                     NULL,
268                                     &observer_));
269  EXPECT_TRUE(pc.get() != NULL);
270  TurnConfigurations turn_configs;
271  webrtc::PortAllocatorFactoryInterface::TurnConfiguration turn(
272      "hello.com", kDefaultStunPort, "test", kTurnPassword, "tcp", false);
273  turn_configs.push_back(turn);
274  VerifyTurnConfigurations(turn_configs);
275}
276
277TEST_F(PeerConnectionFactoryTest, CreatePCUsingSecureTurnUrl) {
278  PeerConnectionInterface::RTCConfiguration config;
279  webrtc::PeerConnectionInterface::IceServer ice_server;
280  ice_server.uri = kSecureTurnIceServer;
281  ice_server.password = kTurnPassword;
282  config.servers.push_back(ice_server);
283  ice_server.uri = kSecureTurnIceServerWithoutTransportParam;
284  ice_server.password = kTurnPassword;
285  config.servers.push_back(ice_server);
286  ice_server.uri = kSecureTurnIceServerWithoutTransportAndPortParam;
287  ice_server.password = kTurnPassword;
288  config.servers.push_back(ice_server);
289  rtc::scoped_refptr<PeerConnectionInterface> pc(
290      factory_->CreatePeerConnection(config, NULL,
291                                     allocator_factory_.get(),
292                                     NULL,
293                                     &observer_));
294  EXPECT_TRUE(pc.get() != NULL);
295  TurnConfigurations turn_configs;
296  webrtc::PortAllocatorFactoryInterface::TurnConfiguration turn1(
297      "hello.com", kDefaultStunTlsPort, "test", kTurnPassword, "tcp", true);
298  turn_configs.push_back(turn1);
299  // TURNS with transport param should be default to tcp.
300  webrtc::PortAllocatorFactoryInterface::TurnConfiguration turn2(
301      "hello.com", 443, "test_no_transport", kTurnPassword, "tcp", true);
302  turn_configs.push_back(turn2);
303  webrtc::PortAllocatorFactoryInterface::TurnConfiguration turn3(
304      "hello.com", kDefaultStunTlsPort, "test_no_transport",
305      kTurnPassword, "tcp", true);
306  turn_configs.push_back(turn3);
307  VerifyTurnConfigurations(turn_configs);
308}
309
310TEST_F(PeerConnectionFactoryTest, CreatePCUsingIPLiteralAddress) {
311  PeerConnectionInterface::RTCConfiguration config;
312  webrtc::PeerConnectionInterface::IceServer ice_server;
313  ice_server.uri = kStunIceServerWithIPv4Address;
314  config.servers.push_back(ice_server);
315  ice_server.uri = kStunIceServerWithIPv4AddressWithoutPort;
316  config.servers.push_back(ice_server);
317  ice_server.uri = kStunIceServerWithIPv6Address;
318  config.servers.push_back(ice_server);
319  ice_server.uri = kStunIceServerWithIPv6AddressWithoutPort;
320  config.servers.push_back(ice_server);
321  ice_server.uri = kStunIceServerWithInvalidIPv6Address;
322  config.servers.push_back(ice_server);
323  ice_server.uri = kTurnIceServerWithIPv6Address;
324  ice_server.password = kTurnPassword;
325  config.servers.push_back(ice_server);
326  rtc::scoped_refptr<PeerConnectionInterface> pc(
327      factory_->CreatePeerConnection(config, NULL,
328                                     allocator_factory_.get(),
329                                     NULL,
330                                     &observer_));
331  EXPECT_TRUE(pc.get() != NULL);
332  StunConfigurations stun_configs;
333  webrtc::PortAllocatorFactoryInterface::StunConfiguration stun1(
334      "1.2.3.4", 1234);
335  stun_configs.push_back(stun1);
336  webrtc::PortAllocatorFactoryInterface::StunConfiguration stun2(
337      "1.2.3.4", 3478);
338  stun_configs.push_back(stun2);  // Default port
339  webrtc::PortAllocatorFactoryInterface::StunConfiguration stun3(
340      "2401:fa00:4::", 1234);
341  stun_configs.push_back(stun3);
342  webrtc::PortAllocatorFactoryInterface::StunConfiguration stun4(
343      "2401:fa00:4::", 3478);
344  stun_configs.push_back(stun4);  // Default port
345  VerifyStunConfigurations(stun_configs);
346
347  TurnConfigurations turn_configs;
348  webrtc::PortAllocatorFactoryInterface::TurnConfiguration turn1(
349      "2401:fa00:4::", 1234, "test", kTurnPassword, "udp", false);
350  turn_configs.push_back(turn1);
351  VerifyTurnConfigurations(turn_configs);
352}
353
354// This test verifies the captured stream is rendered locally using a
355// local video track.
356TEST_F(PeerConnectionFactoryTest, LocalRendering) {
357  cricket::FakeVideoCapturer* capturer = new cricket::FakeVideoCapturer();
358  // The source take ownership of |capturer|.
359  rtc::scoped_refptr<VideoSourceInterface> source(
360      factory_->CreateVideoSource(capturer, NULL));
361  ASSERT_TRUE(source.get() != NULL);
362  rtc::scoped_refptr<VideoTrackInterface> track(
363      factory_->CreateVideoTrack("testlabel", source));
364  ASSERT_TRUE(track.get() != NULL);
365  FakeVideoTrackRenderer local_renderer(track);
366
367  EXPECT_EQ(0, local_renderer.num_rendered_frames());
368  EXPECT_TRUE(capturer->CaptureFrame());
369  EXPECT_EQ(1, local_renderer.num_rendered_frames());
370
371  track->set_enabled(false);
372  EXPECT_TRUE(capturer->CaptureFrame());
373  EXPECT_EQ(1, local_renderer.num_rendered_frames());
374
375  track->set_enabled(true);
376  EXPECT_TRUE(capturer->CaptureFrame());
377  EXPECT_EQ(2, local_renderer.num_rendered_frames());
378}
379