root/ppapi/tests/test_udp_socket.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. ReplacePort
  2. Init
  3. RunTests
  4. GetLocalAddress
  5. SetBroadcastOptions
  6. BindUDPSocket
  7. LookupPortAndBindUDPSocket
  8. ReadSocket
  9. PassMessage
  10. TestReadWrite
  11. TestBroadcast
  12. TestSetOption

// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ppapi/tests/test_udp_socket.h"

#include <vector>

#include "ppapi/cpp/pass_ref.h"
#include "ppapi/cpp/tcp_socket.h"
#include "ppapi/cpp/udp_socket.h"
#include "ppapi/cpp/var.h"
#include "ppapi/tests/test_utils.h"
#include "ppapi/tests/testing_instance.h"

REGISTER_TEST_CASE(UDPSocket);

namespace {

const uint16_t kPortScanFrom = 1024;
const uint16_t kPortScanTo = 4096;

pp::NetAddress ReplacePort(const pp::InstanceHandle& instance,
                           const pp::NetAddress& addr,
                           uint16_t port) {
  switch (addr.GetFamily()) {
    case PP_NETADDRESS_FAMILY_IPV4: {
      PP_NetAddress_IPv4 ipv4_addr;
      if (!addr.DescribeAsIPv4Address(&ipv4_addr))
        break;
      ipv4_addr.port = ConvertToNetEndian16(port);
      return pp::NetAddress(instance, ipv4_addr);
    }
    case PP_NETADDRESS_FAMILY_IPV6: {
      PP_NetAddress_IPv6 ipv6_addr;
      if (!addr.DescribeAsIPv6Address(&ipv6_addr))
        break;
      ipv6_addr.port = ConvertToNetEndian16(port);
      return pp::NetAddress(instance, ipv6_addr);
    }
    default: {
      PP_NOTREACHED();
    }
  }
  return pp::NetAddress();
}

}  // namespace

TestUDPSocket::TestUDPSocket(TestingInstance* instance) : TestCase(instance) {
}

bool TestUDPSocket::Init() {
  bool tcp_socket_is_available = pp::TCPSocket::IsAvailable();
  if (!tcp_socket_is_available)
    instance_->AppendError("PPB_TCPSocket interface not available");

  bool udp_socket_is_available = pp::UDPSocket::IsAvailable();
  if (!udp_socket_is_available)
    instance_->AppendError("PPB_UDPSocket interface not available");

  bool net_address_is_available = pp::NetAddress::IsAvailable();
  if (!net_address_is_available)
    instance_->AppendError("PPB_NetAddress interface not available");

  std::string host;
  uint16_t port = 0;
  bool init_address =
      GetLocalHostPort(instance_->pp_instance(), &host, &port) &&
      ResolveHost(instance_->pp_instance(), host, port, &address_);
  if (!init_address)
    instance_->AppendError("Can't init address");

  return tcp_socket_is_available &&
      udp_socket_is_available &&
      net_address_is_available &&
      init_address &&
      CheckTestingInterface() &&
      EnsureRunningOverHTTP();
}

void TestUDPSocket::RunTests(const std::string& filter) {
  RUN_CALLBACK_TEST(TestUDPSocket, ReadWrite, filter);
  RUN_CALLBACK_TEST(TestUDPSocket, Broadcast, filter);
  RUN_CALLBACK_TEST(TestUDPSocket, SetOption, filter);
}

std::string TestUDPSocket::GetLocalAddress(pp::NetAddress* address) {
  pp::TCPSocket socket(instance_);
  TestCompletionCallback callback(instance_->pp_instance(), callback_type());
  callback.WaitForResult(socket.Connect(address_, callback.GetCallback()));
  CHECK_CALLBACK_BEHAVIOR(callback);
  ASSERT_EQ(PP_OK, callback.result());
  *address = socket.GetLocalAddress();
  ASSERT_NE(0, address->pp_resource());
  socket.Close();
  PASS();
}

std::string TestUDPSocket::SetBroadcastOptions(pp::UDPSocket* socket) {
  TestCompletionCallback callback_1(instance_->pp_instance(), callback_type());
  callback_1.WaitForResult(socket->SetOption(
      PP_UDPSOCKET_OPTION_ADDRESS_REUSE, pp::Var(true),
      callback_1.GetCallback()));
  CHECK_CALLBACK_BEHAVIOR(callback_1);
  ASSERT_EQ(PP_OK, callback_1.result());

  TestCompletionCallback callback_2(instance_->pp_instance(), callback_type());
  callback_2.WaitForResult(socket->SetOption(
      PP_UDPSOCKET_OPTION_BROADCAST, pp::Var(true), callback_2.GetCallback()));
  CHECK_CALLBACK_BEHAVIOR(callback_2);
  ASSERT_EQ(PP_OK, callback_2.result());

  PASS();
}

std::string TestUDPSocket::BindUDPSocket(pp::UDPSocket* socket,
                                         const pp::NetAddress& address) {
  TestCompletionCallback callback(instance_->pp_instance(), callback_type());
  callback.WaitForResult(socket->Bind(address, callback.GetCallback()));
  CHECK_CALLBACK_BEHAVIOR(callback);
  ASSERT_EQ(PP_OK, callback.result());
  PASS();
}

std::string TestUDPSocket::LookupPortAndBindUDPSocket(
    pp::UDPSocket* socket,
    pp::NetAddress* address) {
  pp::NetAddress base_address;
  ASSERT_SUBTEST_SUCCESS(GetLocalAddress(&base_address));

  bool is_free_port_found = false;
  for (uint16_t port = kPortScanFrom; port < kPortScanTo; ++port) {
    pp::NetAddress new_address = ReplacePort(instance_, base_address, port);
    ASSERT_NE(0, new_address.pp_resource());
    if (BindUDPSocket(socket, new_address).empty()) {
      is_free_port_found = true;
      break;
    }
  }
  if (!is_free_port_found)
    return "Can't find available port";

  *address = socket->GetBoundAddress();
  ASSERT_NE(0, address->pp_resource());

  PASS();
}

std::string TestUDPSocket::ReadSocket(pp::UDPSocket* socket,
                                      pp::NetAddress* address,
                                      size_t size,
                                      std::string* message) {
  std::vector<char> buffer(size);
  TestCompletionCallbackWithOutput<pp::NetAddress> callback(
      instance_->pp_instance(), callback_type());
  callback.WaitForResult(
      socket->RecvFrom(&buffer[0], size, callback.GetCallback()));
  CHECK_CALLBACK_BEHAVIOR(callback);
  ASSERT_FALSE(callback.result() < 0);
  ASSERT_EQ(size, static_cast<size_t>(callback.result()));
  *address = callback.output();
  message->assign(buffer.begin(), buffer.end());
  PASS();
}

std::string TestUDPSocket::PassMessage(pp::UDPSocket* target,
                                       pp::UDPSocket* source,
                                       const pp::NetAddress& target_address,
                                       const std::string& message,
                                       pp::NetAddress* recvfrom_address) {
  TestCompletionCallback callback(instance_->pp_instance(), callback_type());
  int32_t rv = source->SendTo(message.c_str(), message.size(),
                              target_address,
                              callback.GetCallback());
  std::string str;
  ASSERT_SUBTEST_SUCCESS(ReadSocket(target, recvfrom_address, message.size(),
                                    &str));

  callback.WaitForResult(rv);
  CHECK_CALLBACK_BEHAVIOR(callback);
  ASSERT_FALSE(callback.result() < 0);
  ASSERT_EQ(message.size(), static_cast<size_t>(callback.result()));
  ASSERT_EQ(message, str);
  PASS();
}

std::string TestUDPSocket::TestReadWrite() {
  pp::UDPSocket server_socket(instance_), client_socket(instance_);
  pp::NetAddress server_address, client_address;

  ASSERT_SUBTEST_SUCCESS(LookupPortAndBindUDPSocket(&server_socket,
                                                    &server_address));
  ASSERT_SUBTEST_SUCCESS(LookupPortAndBindUDPSocket(&client_socket,
                                                    &client_address));
  const std::string message = "Simple message that will be sent via UDP";
  pp::NetAddress recvfrom_address;
  ASSERT_SUBTEST_SUCCESS(PassMessage(&server_socket, &client_socket,
                                     server_address, message,
                                     &recvfrom_address));
  ASSERT_TRUE(EqualNetAddress(recvfrom_address, client_address));

  server_socket.Close();
  client_socket.Close();

  if (server_socket.GetBoundAddress().pp_resource() != 0)
    return "PPB_UDPSocket::GetBoundAddress: expected failure";

  PASS();
}

std::string TestUDPSocket::TestBroadcast() {
  pp::UDPSocket server1(instance_), server2(instance_);

  ASSERT_SUBTEST_SUCCESS(SetBroadcastOptions(&server1));
  ASSERT_SUBTEST_SUCCESS(SetBroadcastOptions(&server2));

  PP_NetAddress_IPv4 any_ipv4_address = { 0, { 0, 0, 0, 0 } };
  pp::NetAddress any_address(instance_, any_ipv4_address);
  ASSERT_SUBTEST_SUCCESS(BindUDPSocket(&server1, any_address));
  // Fill port field of |server_address|.
  pp::NetAddress server_address = server1.GetBoundAddress();
  ASSERT_NE(0, server_address.pp_resource());
  ASSERT_SUBTEST_SUCCESS(BindUDPSocket(&server2, server_address));

  PP_NetAddress_IPv4 server_ipv4_address;
  ASSERT_TRUE(server_address.DescribeAsIPv4Address(&server_ipv4_address));

  PP_NetAddress_IPv4 broadcast_ipv4_address = {
    server_ipv4_address.port, { 0xff, 0xff, 0xff, 0xff }
  };
  pp::NetAddress broadcast_address(instance_, broadcast_ipv4_address);

  std::string message;
  const std::string first_message = "first message";
  const std::string second_message = "second_message";

  pp::NetAddress recvfrom_address;
  ASSERT_SUBTEST_SUCCESS(PassMessage(&server1, &server2, broadcast_address,
                                     first_message, &recvfrom_address));
  // |first_message| was also received by |server2|.
  ASSERT_SUBTEST_SUCCESS(ReadSocket(&server2, &recvfrom_address,
                                    first_message.size(), &message));
  ASSERT_EQ(first_message, message);

  ASSERT_SUBTEST_SUCCESS(PassMessage(&server2, &server1, broadcast_address,
                                     second_message, &recvfrom_address));
  // |second_message| was also received by |server1|.
  ASSERT_SUBTEST_SUCCESS(ReadSocket(&server1, &recvfrom_address,
                                    second_message.size(), &message));
  ASSERT_EQ(second_message, message);

  server1.Close();
  server2.Close();
  PASS();
}

std::string TestUDPSocket::TestSetOption() {
  pp::UDPSocket socket(instance_);

  ASSERT_SUBTEST_SUCCESS(SetBroadcastOptions(&socket));

  // Try to pass incorrect option value's type.
  TestCompletionCallback callback(instance_->pp_instance(), callback_type());
  callback.WaitForResult(socket.SetOption(
      PP_UDPSOCKET_OPTION_ADDRESS_REUSE, pp::Var(1), callback.GetCallback()));
  CHECK_CALLBACK_BEHAVIOR(callback);
  ASSERT_EQ(PP_ERROR_BADARGUMENT, callback.result());

  callback.WaitForResult(socket.SetOption(
      PP_UDPSOCKET_OPTION_BROADCAST, pp::Var(false), callback.GetCallback()));
  CHECK_CALLBACK_BEHAVIOR(callback);
  ASSERT_EQ(PP_OK, callback.result());

  // SEND_BUFFER_SIZE and RECV_BUFFER_SIZE shouldn't be set before the socket is
  // bound.
  callback.WaitForResult(socket.SetOption(
      PP_UDPSOCKET_OPTION_SEND_BUFFER_SIZE, pp::Var(4096),
      callback.GetCallback()));
  CHECK_CALLBACK_BEHAVIOR(callback);
  ASSERT_EQ(PP_ERROR_FAILED, callback.result());

  callback.WaitForResult(socket.SetOption(
      PP_UDPSOCKET_OPTION_RECV_BUFFER_SIZE, pp::Var(512),
      callback.GetCallback()));
  CHECK_CALLBACK_BEHAVIOR(callback);
  ASSERT_EQ(PP_ERROR_FAILED, callback.result());

  pp::NetAddress address;
  ASSERT_SUBTEST_SUCCESS(LookupPortAndBindUDPSocket(&socket, &address));

  // ADDRESS_REUSE and BROADCAST won't take effect after the socket is bound.
  callback.WaitForResult(socket.SetOption(
      PP_UDPSOCKET_OPTION_ADDRESS_REUSE, pp::Var(true),
      callback.GetCallback()));
  CHECK_CALLBACK_BEHAVIOR(callback);
  ASSERT_EQ(PP_ERROR_FAILED, callback.result());

  callback.WaitForResult(socket.SetOption(
      PP_UDPSOCKET_OPTION_BROADCAST, pp::Var(true), callback.GetCallback()));
  CHECK_CALLBACK_BEHAVIOR(callback);
  ASSERT_EQ(PP_ERROR_FAILED, callback.result());

  // SEND_BUFFER_SIZE and RECV_BUFFER_SIZE can be set after the socket is bound.
  callback.WaitForResult(socket.SetOption(
      PP_UDPSOCKET_OPTION_SEND_BUFFER_SIZE, pp::Var(2048),
      callback.GetCallback()));
  CHECK_CALLBACK_BEHAVIOR(callback);
  ASSERT_EQ(PP_OK, callback.result());

  callback.WaitForResult(socket.SetOption(
      PP_UDPSOCKET_OPTION_RECV_BUFFER_SIZE, pp::Var(1024),
      callback.GetCallback()));
  CHECK_CALLBACK_BEHAVIOR(callback);
  ASSERT_EQ(PP_OK, callback.result());

  PASS();
}

/* [<][>][^][v][top][bottom][index][help] */