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 <vector>
29
30#include "talk/p2p/base/basicpacketsocketfactory.h"
31#include "talk/p2p/base/portallocatorsessionproxy.h"
32#include "talk/p2p/client/basicportallocator.h"
33#include "talk/p2p/client/fakeportallocator.h"
34#include "webrtc/base/fakenetwork.h"
35#include "webrtc/base/gunit.h"
36#include "webrtc/base/thread.h"
37
38using cricket::Candidate;
39using cricket::PortAllocatorSession;
40using cricket::PortAllocatorSessionMuxer;
41using cricket::PortAllocatorSessionProxy;
42
43// Based on ICE_UFRAG_LENGTH
44static const char kIceUfrag0[] = "TESTICEUFRAG0000";
45// Based on ICE_PWD_LENGTH
46static const char kIcePwd0[] = "TESTICEPWD00000000000000";
47
48class TestSessionChannel : public sigslot::has_slots<> {
49 public:
50  explicit TestSessionChannel(PortAllocatorSessionProxy* proxy)
51      : proxy_session_(proxy),
52        candidates_count_(0),
53        allocation_complete_(false),
54        ports_count_(0) {
55    proxy_session_->SignalCandidatesAllocationDone.connect(
56        this, &TestSessionChannel::OnCandidatesAllocationDone);
57    proxy_session_->SignalCandidatesReady.connect(
58        this, &TestSessionChannel::OnCandidatesReady);
59    proxy_session_->SignalPortReady.connect(
60        this, &TestSessionChannel::OnPortReady);
61  }
62  virtual ~TestSessionChannel() {
63    delete proxy_session_;
64  }
65  void OnCandidatesReady(PortAllocatorSession* session,
66                         const std::vector<Candidate>& candidates) {
67    EXPECT_EQ(proxy_session_, session);
68    candidates_count_ += static_cast<int>(candidates.size());
69  }
70  void OnCandidatesAllocationDone(PortAllocatorSession* session) {
71    EXPECT_EQ(proxy_session_, session);
72    allocation_complete_ = true;
73  }
74  void OnPortReady(PortAllocatorSession* session,
75                   cricket::PortInterface* port) {
76    EXPECT_EQ(proxy_session_, session);
77    ++ports_count_;
78  }
79  int candidates_count() { return candidates_count_; }
80  bool allocation_complete() { return allocation_complete_; }
81  int ports_count() { return ports_count_; }
82
83  void StartGettingPorts() {
84    proxy_session_->StartGettingPorts();
85  }
86
87  void StopGettingPorts() {
88    proxy_session_->StopGettingPorts();
89  }
90
91  bool IsGettingPorts() {
92    return proxy_session_->IsGettingPorts();
93  }
94
95 private:
96  PortAllocatorSessionProxy* proxy_session_;
97  int candidates_count_;
98  bool allocation_complete_;
99  int ports_count_;
100};
101
102class PortAllocatorSessionProxyTest : public testing::Test {
103 public:
104  PortAllocatorSessionProxyTest()
105      : socket_factory_(rtc::Thread::Current()),
106        allocator_(rtc::Thread::Current(), NULL),
107        session_(new cricket::FakePortAllocatorSession(
108                     rtc::Thread::Current(), &socket_factory_,
109                     "test content", 1,
110                     kIceUfrag0, kIcePwd0)),
111        session_muxer_(new PortAllocatorSessionMuxer(session_)) {
112  }
113  virtual ~PortAllocatorSessionProxyTest() {}
114  void RegisterSessionProxy(PortAllocatorSessionProxy* proxy) {
115    session_muxer_->RegisterSessionProxy(proxy);
116  }
117
118  TestSessionChannel* CreateChannel() {
119    PortAllocatorSessionProxy* proxy =
120        new PortAllocatorSessionProxy("test content", 1, 0);
121    TestSessionChannel* channel = new TestSessionChannel(proxy);
122    session_muxer_->RegisterSessionProxy(proxy);
123    channel->StartGettingPorts();
124    return channel;
125  }
126
127 protected:
128  rtc::BasicPacketSocketFactory socket_factory_;
129  cricket::FakePortAllocator allocator_;
130  cricket::FakePortAllocatorSession* session_;
131  // Muxer object will be delete itself after all registered session proxies
132  // are deleted.
133  PortAllocatorSessionMuxer* session_muxer_;
134};
135
136TEST_F(PortAllocatorSessionProxyTest, TestBasic) {
137  TestSessionChannel* channel = CreateChannel();
138  EXPECT_EQ_WAIT(1, channel->candidates_count(), 1000);
139  EXPECT_EQ(1, channel->ports_count());
140  EXPECT_TRUE(channel->allocation_complete());
141  delete channel;
142}
143
144TEST_F(PortAllocatorSessionProxyTest, TestLateBinding) {
145  TestSessionChannel* channel1 = CreateChannel();
146  EXPECT_EQ_WAIT(1, channel1->candidates_count(), 1000);
147  EXPECT_EQ(1, channel1->ports_count());
148  EXPECT_TRUE(channel1->allocation_complete());
149  EXPECT_EQ(1, session_->port_config_count());
150  // Creating another PortAllocatorSessionProxy and it also should receive
151  // already happened events.
152  PortAllocatorSessionProxy* proxy =
153      new PortAllocatorSessionProxy("test content", 2, 0);
154  TestSessionChannel* channel2 = new TestSessionChannel(proxy);
155  session_muxer_->RegisterSessionProxy(proxy);
156  EXPECT_TRUE(channel2->IsGettingPorts());
157  EXPECT_EQ_WAIT(1, channel2->candidates_count(), 1000);
158  EXPECT_EQ(1, channel2->ports_count());
159  EXPECT_TRUE_WAIT(channel2->allocation_complete(), 1000);
160  EXPECT_EQ(1, session_->port_config_count());
161  delete channel1;
162  delete channel2;
163}
164