OLD | NEW |
| (Empty) |
1 /* | |
2 * libjingle | |
3 * Copyright 2010, 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 #include "webrtc/base/gunit.h" | |
30 #include "webrtc/base/messagehandler.h" | |
31 #include "webrtc/base/scoped_ptr.h" | |
32 #include "webrtc/base/stream.h" | |
33 #include "webrtc/base/thread.h" | |
34 #include "webrtc/base/timeutils.h" | |
35 #include "webrtc/libjingle/session/sessionmanager.h" | |
36 #include "webrtc/libjingle/session/tunnel/tunnelsessionclient.h" | |
37 #include "webrtc/p2p/base/transport.h" | |
38 #include "webrtc/p2p/client/fakeportallocator.h" | |
39 | |
40 static const int kTimeoutMs = 10000; | |
41 static const int kBlockSize = 4096; | |
42 static const buzz::Jid kLocalJid("local@localhost"); | |
43 static const buzz::Jid kRemoteJid("remote@localhost"); | |
44 | |
45 // This test fixture creates the necessary plumbing to create and run | |
46 // two TunnelSessionClients that talk to each other. | |
47 class TunnelSessionClientTest : public testing::Test, | |
48 public rtc::MessageHandler, | |
49 public sigslot::has_slots<> { | |
50 public: | |
51 TunnelSessionClientTest() | |
52 : local_pa_(rtc::Thread::Current(), NULL), | |
53 remote_pa_(rtc::Thread::Current(), NULL), | |
54 local_sm_(&local_pa_, rtc::Thread::Current()), | |
55 remote_sm_(&remote_pa_, rtc::Thread::Current()), | |
56 local_client_(kLocalJid, &local_sm_), | |
57 remote_client_(kRemoteJid, &remote_sm_), | |
58 done_(false) { | |
59 local_sm_.SignalRequestSignaling.connect(this, | |
60 &TunnelSessionClientTest::OnLocalRequestSignaling); | |
61 local_sm_.SignalOutgoingMessage.connect(this, | |
62 &TunnelSessionClientTest::OnOutgoingMessage); | |
63 remote_sm_.SignalRequestSignaling.connect(this, | |
64 &TunnelSessionClientTest::OnRemoteRequestSignaling); | |
65 remote_sm_.SignalOutgoingMessage.connect(this, | |
66 &TunnelSessionClientTest::OnOutgoingMessage); | |
67 remote_client_.SignalIncomingTunnel.connect(this, | |
68 &TunnelSessionClientTest::OnIncomingTunnel); | |
69 } | |
70 | |
71 // Transfer the desired amount of data from the local to the remote client. | |
72 void TestTransfer(int size) { | |
73 // Create some dummy data to send. | |
74 send_stream_.ReserveSize(size); | |
75 for (int i = 0; i < size; ++i) { | |
76 char ch = static_cast<char>(i); | |
77 send_stream_.Write(&ch, 1, NULL, NULL); | |
78 } | |
79 send_stream_.Rewind(); | |
80 // Prepare the receive stream. | |
81 recv_stream_.ReserveSize(size); | |
82 // Create the tunnel and set things in motion. | |
83 local_tunnel_.reset(local_client_.CreateTunnel(kRemoteJid, "test")); | |
84 local_tunnel_->SignalEvent.connect(this, | |
85 &TunnelSessionClientTest::OnStreamEvent); | |
86 EXPECT_TRUE_WAIT(done_, kTimeoutMs); | |
87 // Make sure we received the right data. | |
88 EXPECT_EQ(0, memcmp(send_stream_.GetBuffer(), | |
89 recv_stream_.GetBuffer(), size)); | |
90 } | |
91 | |
92 private: | |
93 enum { MSG_LSIGNAL, MSG_RSIGNAL }; | |
94 | |
95 // There's no SessionManager* argument in this callback, so we need 2 of them. | |
96 void OnLocalRequestSignaling() { | |
97 local_sm_.OnSignalingReady(); | |
98 } | |
99 void OnRemoteRequestSignaling() { | |
100 remote_sm_.OnSignalingReady(); | |
101 } | |
102 | |
103 // Post a message, to avoid problems with directly connecting the callbacks. | |
104 void OnOutgoingMessage(cricket::SessionManager* manager, | |
105 const buzz::XmlElement* stanza) { | |
106 if (manager == &local_sm_) { | |
107 rtc::Thread::Current()->Post(this, MSG_LSIGNAL, | |
108 rtc::WrapMessageData(*stanza)); | |
109 } else if (manager == &remote_sm_) { | |
110 rtc::Thread::Current()->Post(this, MSG_RSIGNAL, | |
111 rtc::WrapMessageData(*stanza)); | |
112 } | |
113 } | |
114 | |
115 // Need to add a "from=" attribute (normally added by the server) | |
116 // Then route the incoming signaling message to the "other" session manager. | |
117 virtual void OnMessage(rtc::Message* message) { | |
118 rtc::TypedMessageData<buzz::XmlElement>* data = | |
119 static_cast<rtc::TypedMessageData<buzz::XmlElement>*>( | |
120 message->pdata); | |
121 bool response = data->data().Attr(buzz::QN_TYPE) == buzz::STR_RESULT; | |
122 if (message->message_id == MSG_RSIGNAL) { | |
123 data->data().AddAttr(buzz::QN_FROM, remote_client_.jid().Str()); | |
124 if (!response) { | |
125 local_sm_.OnIncomingMessage(&data->data()); | |
126 } else { | |
127 local_sm_.OnIncomingResponse(NULL, &data->data()); | |
128 } | |
129 } else if (message->message_id == MSG_LSIGNAL) { | |
130 data->data().AddAttr(buzz::QN_FROM, local_client_.jid().Str()); | |
131 if (!response) { | |
132 remote_sm_.OnIncomingMessage(&data->data()); | |
133 } else { | |
134 remote_sm_.OnIncomingResponse(NULL, &data->data()); | |
135 } | |
136 } | |
137 delete data; | |
138 } | |
139 | |
140 // Accept the tunnel when it arrives and wire up the stream. | |
141 void OnIncomingTunnel(cricket::TunnelSessionClient* client, | |
142 buzz::Jid jid, std::string description, | |
143 cricket::Session* session) { | |
144 remote_tunnel_.reset(remote_client_.AcceptTunnel(session)); | |
145 remote_tunnel_->SignalEvent.connect(this, | |
146 &TunnelSessionClientTest::OnStreamEvent); | |
147 } | |
148 | |
149 // Send from send_stream_ as long as we're not flow-controlled. | |
150 // Read bytes out into recv_stream_ as they arrive. | |
151 // End the test when we are notified that the local side has closed the | |
152 // tunnel. All data has been read out at this point. | |
153 void OnStreamEvent(rtc::StreamInterface* stream, int events, | |
154 int error) { | |
155 if (events & rtc::SE_READ) { | |
156 if (stream == remote_tunnel_.get()) { | |
157 ReadData(); | |
158 } | |
159 } | |
160 if (events & rtc::SE_WRITE) { | |
161 if (stream == local_tunnel_.get()) { | |
162 bool done = false; | |
163 WriteData(&done); | |
164 if (done) { | |
165 local_tunnel_->Close(); | |
166 } | |
167 } | |
168 } | |
169 if (events & rtc::SE_CLOSE) { | |
170 if (stream == remote_tunnel_.get()) { | |
171 remote_tunnel_->Close(); | |
172 done_ = true; | |
173 } | |
174 } | |
175 } | |
176 | |
177 // Spool from the tunnel into recv_stream. | |
178 // Flow() doesn't work here because it won't write if the read blocks. | |
179 void ReadData() { | |
180 char block[kBlockSize]; | |
181 size_t read, position; | |
182 rtc::StreamResult res; | |
183 while ((res = remote_tunnel_->Read(block, sizeof(block), &read, NULL)) == | |
184 rtc::SR_SUCCESS) { | |
185 recv_stream_.Write(block, read, NULL, NULL); | |
186 } | |
187 ASSERT(res != rtc::SR_EOS); | |
188 recv_stream_.GetPosition(&position); | |
189 LOG(LS_VERBOSE) << "Recv position: " << position; | |
190 } | |
191 // Spool from send_stream into the tunnel. Back up if we get flow controlled. | |
192 void WriteData(bool* done) { | |
193 char block[kBlockSize]; | |
194 size_t leftover = 0, position; | |
195 rtc::StreamResult res = rtc::Flow(&send_stream_, | |
196 block, sizeof(block), local_tunnel_.get(), &leftover); | |
197 if (res == rtc::SR_BLOCK) { | |
198 send_stream_.GetPosition(&position); | |
199 send_stream_.SetPosition(position - leftover); | |
200 LOG(LS_VERBOSE) << "Send position: " << position - leftover; | |
201 *done = false; | |
202 } else if (res == rtc::SR_SUCCESS) { | |
203 *done = true; | |
204 } else { | |
205 ASSERT(false); // shouldn't happen | |
206 } | |
207 } | |
208 | |
209 private: | |
210 cricket::FakePortAllocator local_pa_; | |
211 cricket::FakePortAllocator remote_pa_; | |
212 cricket::SessionManager local_sm_; | |
213 cricket::SessionManager remote_sm_; | |
214 cricket::TunnelSessionClient local_client_; | |
215 cricket::TunnelSessionClient remote_client_; | |
216 rtc::scoped_ptr<rtc::StreamInterface> local_tunnel_; | |
217 rtc::scoped_ptr<rtc::StreamInterface> remote_tunnel_; | |
218 rtc::MemoryStream send_stream_; | |
219 rtc::MemoryStream recv_stream_; | |
220 bool done_; | |
221 }; | |
222 | |
223 // Test the normal case of sending data from one side to the other. | |
224 TEST_F(TunnelSessionClientTest, TestTransfer) { | |
225 TestTransfer(1000000); | |
226 } | |
OLD | NEW |