media/mtransport/test/sctp_unittest.cpp

changeset 0
6474c204b198
     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 +}

mercurial