1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/media/mtransport/test/sctp_unittest.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,405 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim: set ts=2 et sw=2 tw=80: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this file, 1.8 + * You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +// Original author: ekr@rtfm.com 1.11 + 1.12 +#include <iostream> 1.13 +#include <string> 1.14 +#include <map> 1.15 + 1.16 +#include "sigslot.h" 1.17 + 1.18 +#include "logging.h" 1.19 +#include "nsNetCID.h" 1.20 +#include "nsITimer.h" 1.21 +#include "nsComponentManagerUtils.h" 1.22 +#include "nsThreadUtils.h" 1.23 +#include "nsXPCOM.h" 1.24 + 1.25 +#include "transportflow.h" 1.26 +#include "transportlayer.h" 1.27 +#include "transportlayerloopback.h" 1.28 + 1.29 +#include "mtransport_test_utils.h" 1.30 +#include "runnable_utils.h" 1.31 +#include "usrsctp.h" 1.32 + 1.33 +#define GTEST_HAS_RTTI 0 1.34 +#include "gtest/gtest.h" 1.35 +#include "gtest_utils.h" 1.36 + 1.37 + 1.38 +using namespace mozilla; 1.39 + 1.40 +MtransportTestUtils *test_utils; 1.41 +static bool sctp_logging = false; 1.42 +static int port_number = 5000; 1.43 + 1.44 +namespace { 1.45 + 1.46 +class TransportTestPeer; 1.47 + 1.48 +class SendPeriodic : public nsITimerCallback { 1.49 + public: 1.50 + SendPeriodic(TransportTestPeer *peer, int to_send) : 1.51 + peer_(peer), 1.52 + to_send_(to_send) {} 1.53 + virtual ~SendPeriodic() {} 1.54 + 1.55 + NS_DECL_THREADSAFE_ISUPPORTS 1.56 + NS_DECL_NSITIMERCALLBACK 1.57 + 1.58 + protected: 1.59 + TransportTestPeer *peer_; 1.60 + int to_send_; 1.61 +}; 1.62 + 1.63 +NS_IMPL_ISUPPORTS(SendPeriodic, nsITimerCallback) 1.64 + 1.65 + 1.66 +class TransportTestPeer : public sigslot::has_slots<> { 1.67 + public: 1.68 + TransportTestPeer(std::string name, int local_port, int remote_port) 1.69 + : name_(name), connected_(false), 1.70 + sent_(0), received_(0), 1.71 + flow_(new TransportFlow()), 1.72 + loopback_(new TransportLayerLoopback()), 1.73 + sctp_(usrsctp_socket(AF_CONN, SOCK_STREAM, IPPROTO_SCTP, receive_cb, nullptr, 0, nullptr)), 1.74 + timer_(do_CreateInstance(NS_TIMER_CONTRACTID)), 1.75 + periodic_(nullptr) { 1.76 + std::cerr << "Creating TransportTestPeer; flow=" << 1.77 + static_cast<void *>(flow_.get()) << 1.78 + " local=" << local_port << 1.79 + " remote=" << remote_port << std::endl; 1.80 + 1.81 + usrsctp_register_address(static_cast<void *>(this)); 1.82 + int r = usrsctp_set_non_blocking(sctp_, 1); 1.83 + EXPECT_GE(r, 0); 1.84 + 1.85 + struct linger l; 1.86 + l.l_onoff = 1; 1.87 + l.l_linger = 0; 1.88 + r = usrsctp_setsockopt(sctp_, SOL_SOCKET, SO_LINGER, &l, 1.89 + (socklen_t)sizeof(l)); 1.90 + EXPECT_GE(r, 0); 1.91 + 1.92 + struct sctp_event subscription; 1.93 + memset(&subscription, 0, sizeof(subscription)); 1.94 + subscription.se_assoc_id = SCTP_ALL_ASSOC; 1.95 + subscription.se_on = 1; 1.96 + subscription.se_type = SCTP_ASSOC_CHANGE; 1.97 + r = usrsctp_setsockopt(sctp_, IPPROTO_SCTP, SCTP_EVENT, &subscription, 1.98 + sizeof(subscription)); 1.99 + EXPECT_GE(r, 0); 1.100 + 1.101 + memset(&local_addr_, 0, sizeof(local_addr_)); 1.102 + local_addr_.sconn_family = AF_CONN; 1.103 +#if !defined(__Userspace_os_Linux) && !defined(__Userspace_os_Windows) && !defined(__Userspace_os_Android) 1.104 + local_addr_.sconn_len = sizeof(struct sockaddr_conn); 1.105 +#endif 1.106 + local_addr_.sconn_port = htons(local_port); 1.107 + local_addr_.sconn_addr = static_cast<void *>(this); 1.108 + 1.109 + 1.110 + memset(&remote_addr_, 0, sizeof(remote_addr_)); 1.111 + remote_addr_.sconn_family = AF_CONN; 1.112 +#if !defined(__Userspace_os_Linux) && !defined(__Userspace_os_Windows) && !defined(__Userspace_os_Android) 1.113 + remote_addr_.sconn_len = sizeof(struct sockaddr_conn); 1.114 +#endif 1.115 + remote_addr_.sconn_port = htons(remote_port); 1.116 + remote_addr_.sconn_addr = static_cast<void *>(this); 1.117 + 1.118 + nsresult res; 1.119 + res = loopback_->Init(); 1.120 + EXPECT_EQ((nsresult)NS_OK, res); 1.121 + } 1.122 + 1.123 + ~TransportTestPeer() { 1.124 + std::cerr << "Destroying sctp connection flow=" << 1.125 + static_cast<void *>(flow_.get()) << std::endl; 1.126 + usrsctp_close(sctp_); 1.127 + usrsctp_deregister_address(static_cast<void *>(this)); 1.128 + 1.129 + test_utils->sts_target()->Dispatch(WrapRunnable(this, 1.130 + &TransportTestPeer::Disconnect_s), 1.131 + NS_DISPATCH_SYNC); 1.132 + 1.133 + std::cerr << "~TransportTestPeer() completed" << std::endl; 1.134 + } 1.135 + 1.136 + void ConnectSocket(TransportTestPeer *peer) { 1.137 + test_utils->sts_target()->Dispatch(WrapRunnable( 1.138 + this, &TransportTestPeer::ConnectSocket_s, peer), 1.139 + NS_DISPATCH_SYNC); 1.140 + } 1.141 + 1.142 + void ConnectSocket_s(TransportTestPeer *peer) { 1.143 + loopback_->Connect(peer->loopback_); 1.144 + 1.145 + ASSERT_EQ((nsresult)NS_OK, flow_->PushLayer(loopback_)); 1.146 + 1.147 + flow_->SignalPacketReceived.connect(this, &TransportTestPeer::PacketReceived); 1.148 + 1.149 + // SCTP here! 1.150 + ASSERT_TRUE(sctp_); 1.151 + std::cerr << "Calling usrsctp_bind()" << std::endl; 1.152 + int r = usrsctp_bind(sctp_, reinterpret_cast<struct sockaddr *>( 1.153 + &local_addr_), sizeof(local_addr_)); 1.154 + ASSERT_GE(0, r); 1.155 + 1.156 + std::cerr << "Calling usrsctp_connect()" << std::endl; 1.157 + r = usrsctp_connect(sctp_, reinterpret_cast<struct sockaddr *>( 1.158 + &remote_addr_), sizeof(remote_addr_)); 1.159 + ASSERT_GE(0, r); 1.160 + } 1.161 + 1.162 + void Disconnect_s() { 1.163 + if (flow_) { 1.164 + flow_ = nullptr; 1.165 + } 1.166 + } 1.167 + 1.168 + void Disconnect() { 1.169 + loopback_->Disconnect(); 1.170 + } 1.171 + 1.172 + 1.173 + void StartTransfer(size_t to_send) { 1.174 + periodic_ = new SendPeriodic(this, to_send); 1.175 + timer_->SetTarget(test_utils->sts_target()); 1.176 + timer_->InitWithCallback(periodic_, 10, nsITimer::TYPE_REPEATING_SLACK); 1.177 + } 1.178 + 1.179 + void SendOne() { 1.180 + unsigned char buf[100]; 1.181 + memset(buf, sent_ & 0xff, sizeof(buf)); 1.182 + 1.183 + struct sctp_sndinfo info; 1.184 + info.snd_sid = 1; 1.185 + info.snd_flags = 0; 1.186 + info.snd_ppid = 50; // What the heck is this? 1.187 + info.snd_context = 0; 1.188 + info.snd_assoc_id = 0; 1.189 + 1.190 + int r = usrsctp_sendv(sctp_, buf, sizeof(buf), nullptr, 0, 1.191 + static_cast<void *>(&info), 1.192 + sizeof(info), SCTP_SENDV_SNDINFO, 0); 1.193 + ASSERT_TRUE(r >= 0); 1.194 + ASSERT_EQ(sizeof(buf), (size_t)r); 1.195 + 1.196 + ++sent_; 1.197 + } 1.198 + 1.199 + int sent() const { return sent_; } 1.200 + int received() const { return received_; } 1.201 + bool connected() const { return connected_; } 1.202 + 1.203 + static TransportResult SendPacket_s(const unsigned char* data, size_t len, 1.204 + const mozilla::RefPtr<TransportFlow>& flow) { 1.205 + TransportResult res = flow->SendPacket(data, len); 1.206 + delete data; // we always allocate 1.207 + return res; 1.208 + } 1.209 + 1.210 + TransportResult SendPacket(const unsigned char* data, size_t len) { 1.211 + unsigned char *buffer = new unsigned char[len]; 1.212 + memcpy(buffer, data, len); 1.213 + 1.214 + // Uses DISPATCH_NORMAL to avoid possible deadlocks when we're called 1.215 + // from MainThread especially during shutdown (same as DataChannels). 1.216 + // RUN_ON_THREAD short-circuits if already on the STS thread, which is 1.217 + // normal for most transfers outside of connect() and close(). Passes 1.218 + // a refptr to flow_ to avoid any async deletion issues (since we can't 1.219 + // make 'this' into a refptr as it isn't refcounted) 1.220 + RUN_ON_THREAD(test_utils->sts_target(), WrapRunnableNM( 1.221 + &TransportTestPeer::SendPacket_s, buffer, len, flow_), 1.222 + NS_DISPATCH_NORMAL); 1.223 + 1.224 + return 0; 1.225 + } 1.226 + 1.227 + void PacketReceived(TransportFlow * flow, const unsigned char* data, 1.228 + size_t len) { 1.229 + std::cerr << "Received " << len << " bytes" << std::endl; 1.230 + 1.231 + // Pass the data to SCTP 1.232 + 1.233 + usrsctp_conninput(static_cast<void *>(this), data, len, 0); 1.234 + } 1.235 + 1.236 + // Process SCTP notification 1.237 + void Notification(union sctp_notification *msg, size_t len) { 1.238 + ASSERT_EQ(msg->sn_header.sn_length, len); 1.239 + 1.240 + if (msg->sn_header.sn_type == SCTP_ASSOC_CHANGE) { 1.241 + struct sctp_assoc_change *change = &msg->sn_assoc_change; 1.242 + 1.243 + if (change->sac_state == SCTP_COMM_UP) { 1.244 + std::cerr << "Connection up" << std::endl; 1.245 + SetConnected(true); 1.246 + } else { 1.247 + std::cerr << "Connection down" << std::endl; 1.248 + SetConnected(false); 1.249 + } 1.250 + } 1.251 + } 1.252 + 1.253 + void SetConnected(bool state) { 1.254 + connected_ = state; 1.255 + } 1.256 + 1.257 + static int conn_output(void *addr, void *buffer, size_t length, uint8_t tos, uint8_t set_df) { 1.258 + TransportTestPeer *peer = static_cast<TransportTestPeer *>(addr); 1.259 + 1.260 + peer->SendPacket(static_cast<unsigned char *>(buffer), length); 1.261 + 1.262 + return 0; 1.263 + } 1.264 + 1.265 + static int receive_cb(struct socket* sock, union sctp_sockstore addr, 1.266 + void *data, size_t datalen, 1.267 + struct sctp_rcvinfo rcv, int flags, void *ulp_info) { 1.268 + TransportTestPeer *me = static_cast<TransportTestPeer *>( 1.269 + addr.sconn.sconn_addr); 1.270 + MOZ_ASSERT(me); 1.271 + 1.272 + if (flags & MSG_NOTIFICATION) { 1.273 + union sctp_notification *notif = 1.274 + static_cast<union sctp_notification *>(data); 1.275 + 1.276 + me->Notification(notif, datalen); 1.277 + return 0; 1.278 + } 1.279 + 1.280 + me->received_ += datalen; 1.281 + 1.282 + std::cerr << "receive_cb: sock " << sock << " data " << data << "(" << datalen << ") total received bytes = " << me->received_ << std::endl; 1.283 + 1.284 + return 0; 1.285 + } 1.286 + 1.287 + 1.288 + private: 1.289 + std::string name_; 1.290 + bool connected_; 1.291 + size_t sent_; 1.292 + size_t received_; 1.293 + mozilla::RefPtr<TransportFlow> flow_; 1.294 + TransportLayerLoopback *loopback_; 1.295 + 1.296 + struct sockaddr_conn local_addr_; 1.297 + struct sockaddr_conn remote_addr_; 1.298 + struct socket *sctp_; 1.299 + nsCOMPtr<nsITimer> timer_; 1.300 + nsRefPtr<SendPeriodic> periodic_; 1.301 +}; 1.302 + 1.303 + 1.304 +// Implemented here because it calls a method of TransportTestPeer 1.305 +NS_IMETHODIMP SendPeriodic::Notify(nsITimer *timer) { 1.306 + peer_->SendOne(); 1.307 + --to_send_; 1.308 + if (!to_send_) { 1.309 + timer->Cancel(); 1.310 + } 1.311 + return NS_OK; 1.312 +} 1.313 + 1.314 +class TransportTest : public ::testing::Test { 1.315 + public: 1.316 + TransportTest() { 1.317 + } 1.318 + 1.319 + ~TransportTest() { 1.320 + if (p1_) 1.321 + p1_->Disconnect(); 1.322 + if (p2_) 1.323 + p2_->Disconnect(); 1.324 + delete p1_; 1.325 + delete p2_; 1.326 + } 1.327 + 1.328 + static void debug_printf(const char *format, ...) { 1.329 + va_list ap; 1.330 + 1.331 + va_start(ap, format); 1.332 + vprintf(format, ap); 1.333 + va_end(ap); 1.334 + } 1.335 + 1.336 + 1.337 + static void SetUpTestCase() { 1.338 + if (sctp_logging) { 1.339 + usrsctp_init(0, &TransportTestPeer::conn_output, debug_printf); 1.340 + usrsctp_sysctl_set_sctp_debug_on(0xffffffff); 1.341 + } else { 1.342 + usrsctp_init(0, &TransportTestPeer::conn_output, nullptr); 1.343 + } 1.344 + } 1.345 + 1.346 + void SetUp() { 1.347 + } 1.348 + 1.349 + void ConnectSocket(int p1port = 0, int p2port = 0) { 1.350 + if (!p1port) 1.351 + p1port = port_number++; 1.352 + if (!p2port) 1.353 + p2port = port_number++; 1.354 + 1.355 + p1_ = new TransportTestPeer("P1", p1port, p2port); 1.356 + p2_ = new TransportTestPeer("P2", p2port, p1port); 1.357 + 1.358 + p1_->ConnectSocket(p2_); 1.359 + p2_->ConnectSocket(p1_); 1.360 + ASSERT_TRUE_WAIT(p1_->connected(), 2000); 1.361 + ASSERT_TRUE_WAIT(p2_->connected(), 2000); 1.362 + } 1.363 + 1.364 + void TestTransfer(int expected = 1) { 1.365 + std::cerr << "Starting trasnsfer test" << std::endl; 1.366 + p1_->StartTransfer(expected); 1.367 + ASSERT_TRUE_WAIT(p1_->sent() == expected, 10000); 1.368 + ASSERT_TRUE_WAIT(p2_->received() == (expected * 100), 10000); 1.369 + std::cerr << "P2 received " << p2_->received() << std::endl; 1.370 + } 1.371 + 1.372 + protected: 1.373 + TransportTestPeer *p1_; 1.374 + TransportTestPeer *p2_; 1.375 +}; 1.376 + 1.377 +TEST_F(TransportTest, TestConnect) { 1.378 + ConnectSocket(); 1.379 +} 1.380 + 1.381 +TEST_F(TransportTest, TestConnectSymmetricalPorts) { 1.382 + ConnectSocket(5002,5002); 1.383 +} 1.384 + 1.385 +TEST_F(TransportTest, TestTransfer) { 1.386 + ConnectSocket(); 1.387 + TestTransfer(50); 1.388 +} 1.389 + 1.390 + 1.391 +} // end namespace 1.392 + 1.393 +int main(int argc, char **argv) 1.394 +{ 1.395 + test_utils = new MtransportTestUtils(); 1.396 + // Start the tests 1.397 + ::testing::InitGoogleTest(&argc, argv); 1.398 + 1.399 + for(int i=0; i<argc; i++) { 1.400 + if (!strcmp(argv[i],"-v")) { 1.401 + sctp_logging = true; 1.402 + } 1.403 + } 1.404 + 1.405 + int rv = RUN_ALL_TESTS(); 1.406 + delete test_utils; 1.407 + return rv; 1.408 +}