| // 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), | 
 |       socket_interface_1_0_(NULL), | 
 |       socket_interface_1_1_(NULL) { | 
 | } | 
 |  | 
 | 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"); | 
 |  | 
 |   socket_interface_1_0_ = | 
 |       static_cast<const PPB_UDPSocket_1_0*>( | 
 |           pp::Module::Get()->GetBrowserInterface(PPB_UDPSOCKET_INTERFACE_1_0)); | 
 |   if (!socket_interface_1_0_) | 
 |     instance_->AppendError("PPB_UDPSocket_1_0 interface not available"); | 
 |  | 
 |   socket_interface_1_1_ = | 
 |       static_cast<const PPB_UDPSocket_1_1*>( | 
 |           pp::Module::Get()->GetBrowserInterface(PPB_UDPSOCKET_INTERFACE_1_1)); | 
 |   if (!socket_interface_1_1_) | 
 |     instance_->AppendError("PPB_UDPSocket_1_1 interface not available"); | 
 |  | 
 |   return tcp_socket_is_available && | 
 |       udp_socket_is_available && | 
 |       net_address_is_available && | 
 |       init_address && | 
 |       CheckTestingInterface() && | 
 |       EnsureRunningOverHTTP() && | 
 |       socket_interface_1_0_ != NULL && | 
 |       socket_interface_1_1_ != NULL; | 
 | } | 
 |  | 
 | void TestUDPSocket::RunTests(const std::string& filter) { | 
 |   RUN_CALLBACK_TEST(TestUDPSocket, ReadWrite, filter); | 
 |   RUN_CALLBACK_TEST(TestUDPSocket, Broadcast, filter); | 
 |   RUN_CALLBACK_TEST(TestUDPSocket, SetOption_1_0, filter); | 
 |   RUN_CALLBACK_TEST(TestUDPSocket, SetOption_1_1, filter); | 
 |   RUN_CALLBACK_TEST(TestUDPSocket, SetOption, filter); | 
 |   RUN_CALLBACK_TEST(TestUDPSocket, ParallelSend, filter); | 
 |   RUN_CALLBACK_TEST(TestUDPSocket, Multicast, 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; | 
 |   std::string ret; | 
 |   for (uint16_t port = kPortScanFrom; port < kPortScanTo; ++port) { | 
 |     pp::NetAddress new_address = ReplacePort(instance_, base_address, port); | 
 |     ASSERT_NE(0, new_address.pp_resource()); | 
 |     ret = BindUDPSocket(socket, new_address); | 
 |     if (ret.empty()) { | 
 |       is_free_port_found = true; | 
 |       break; | 
 |     } | 
 |   } | 
 |   if (!is_free_port_found) | 
 |     return "Can't find available port (" + ret + ")"; | 
 |  | 
 |   *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], static_cast<int32_t>(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(), | 
 |                               static_cast<int32_t>(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::SetMulticastOptions(pp::UDPSocket* socket) { | 
 |   TestCompletionCallback callback(instance_->pp_instance(), callback_type()); | 
 |   callback.WaitForResult(socket->SetOption( | 
 |       PP_UDPSOCKET_OPTION_MULTICAST_LOOP, pp::Var(true), | 
 |       callback.GetCallback())); | 
 |   CHECK_CALLBACK_BEHAVIOR(callback); | 
 |   ASSERT_EQ(PP_OK, callback.result()); | 
 |  | 
 |   callback.WaitForResult(socket->SetOption( | 
 |       PP_UDPSOCKET_OPTION_MULTICAST_TTL, pp::Var(1), callback.GetCallback())); | 
 |   CHECK_CALLBACK_BEHAVIOR(callback); | 
 |   ASSERT_EQ(PP_OK, callback.result()); | 
 |  | 
 |   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(); | 
 | } | 
 |  | 
 | int32_t TestUDPSocket::SetOptionValue(UDPSocketSetOption func, | 
 |                                       PP_Resource socket, | 
 |                                       PP_UDPSocket_Option option, | 
 |                                       const PP_Var& value) { | 
 |   PP_TimeTicks start_time(NowInTimeTicks()); | 
 |   TestCompletionCallback cb(instance_->pp_instance(), callback_type()); | 
 |   cb.WaitForResult(func(socket, option, value, | 
 |                         cb.GetCallback().pp_completion_callback())); | 
 |  | 
 |   // Expanded from CHECK_CALLBACK_BEHAVIOR macro. | 
 |   if (cb.failed()) { | 
 |     std::string msg = MakeFailureMessage(__FILE__, __LINE__, | 
 |                                          cb.errors().c_str()); | 
 |  | 
 |     instance_->LogTest("SetOptionValue", msg, start_time); | 
 |     return PP_ERROR_FAILED; | 
 |   } | 
 |   return cb.result(); | 
 | } | 
 |  | 
 | std::string TestUDPSocket::TestSetOption_1_0() { | 
 |   PP_Resource socket = socket_interface_1_0_->Create(instance_->pp_instance()); | 
 |   ASSERT_NE(0, socket); | 
 |  | 
 |   // Multicast options are not supported in interface 1.0. | 
 |   ASSERT_EQ(PP_ERROR_BADARGUMENT, | 
 |             SetOptionValue(socket_interface_1_0_->SetOption, | 
 |                            socket, | 
 |                            PP_UDPSOCKET_OPTION_MULTICAST_LOOP, | 
 |                            PP_MakeBool(PP_TRUE))); | 
 |  | 
 |   ASSERT_EQ(PP_ERROR_BADARGUMENT, | 
 |             SetOptionValue(socket_interface_1_0_->SetOption, | 
 |                            socket, | 
 |                            PP_UDPSOCKET_OPTION_MULTICAST_TTL, | 
 |                            PP_MakeInt32(1))); | 
 |  | 
 |   socket_interface_1_0_->Close(socket); | 
 |   pp::Module::Get()->core()->ReleaseResource(socket); | 
 |  | 
 |   PASS(); | 
 | } | 
 |  | 
 | std::string TestUDPSocket::TestSetOption_1_1() { | 
 |   PP_Resource socket = socket_interface_1_1_->Create(instance_->pp_instance()); | 
 |   ASSERT_NE(0, socket); | 
 |  | 
 |   // Multicast options are not supported in interface 1.1. | 
 |   ASSERT_EQ(PP_ERROR_BADARGUMENT, | 
 |             SetOptionValue(socket_interface_1_1_->SetOption, | 
 |                            socket, | 
 |                            PP_UDPSOCKET_OPTION_MULTICAST_LOOP, | 
 |                            PP_MakeBool(PP_TRUE))); | 
 |  | 
 |   ASSERT_EQ(PP_ERROR_BADARGUMENT, | 
 |             SetOptionValue(socket_interface_1_1_->SetOption, | 
 |                            socket, | 
 |                            PP_UDPSOCKET_OPTION_MULTICAST_TTL, | 
 |                            PP_MakeInt32(1))); | 
 |  | 
 |   socket_interface_1_1_->Close(socket); | 
 |   pp::Module::Get()->core()->ReleaseResource(socket); | 
 |  | 
 |   PASS(); | 
 | } | 
 |  | 
 | std::string TestUDPSocket::TestSetOption() { | 
 |   pp::UDPSocket socket(instance_); | 
 |  | 
 |   ASSERT_SUBTEST_SUCCESS(SetBroadcastOptions(&socket)); | 
 |   ASSERT_SUBTEST_SUCCESS(SetMulticastOptions(&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()); | 
 |  | 
 |   // Invalid multicast TTL values (less than 0 and greater than 255). | 
 |   callback.WaitForResult(socket.SetOption( | 
 |       PP_UDPSOCKET_OPTION_MULTICAST_TTL, pp::Var(-1), callback.GetCallback())); | 
 |   CHECK_CALLBACK_BEHAVIOR(callback); | 
 |   ASSERT_EQ(PP_ERROR_BADARGUMENT, callback.result()); | 
 |  | 
 |   callback.WaitForResult(socket.SetOption( | 
 |       PP_UDPSOCKET_OPTION_MULTICAST_TTL, pp::Var(256), 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()); | 
 |  | 
 |   callback.WaitForResult(socket.SetOption( | 
 |       PP_UDPSOCKET_OPTION_SEND_BUFFER_SIZE, pp::Var(4096), | 
 |       callback.GetCallback())); | 
 |   CHECK_CALLBACK_BEHAVIOR(callback); | 
 |   ASSERT_EQ(PP_OK, callback.result()); | 
 |  | 
 |   callback.WaitForResult(socket.SetOption( | 
 |       PP_UDPSOCKET_OPTION_RECV_BUFFER_SIZE, pp::Var(512), | 
 |       callback.GetCallback())); | 
 |   CHECK_CALLBACK_BEHAVIOR(callback); | 
 |   ASSERT_EQ(PP_OK, callback.result()); | 
 |  | 
 |   pp::NetAddress address; | 
 |   ASSERT_SUBTEST_SUCCESS(LookupPortAndBindUDPSocket(&socket, &address)); | 
 |  | 
 |   // ADDRESS_REUSE 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()); | 
 |  | 
 |   // BROADCAST, SEND_BUFFER_SIZE and RECV_BUFFER_SIZE can be set after the | 
 |   // socket is bound. | 
 |   callback.WaitForResult(socket.SetOption( | 
 |       PP_UDPSOCKET_OPTION_BROADCAST, pp::Var(true), callback.GetCallback())); | 
 |   CHECK_CALLBACK_BEHAVIOR(callback); | 
 |   ASSERT_EQ(PP_OK, callback.result()); | 
 |  | 
 |   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(); | 
 | } | 
 |  | 
 | std::string TestUDPSocket::TestParallelSend() { | 
 |   // This test only makes sense when callbacks are optional. | 
 |   if (callback_type() != PP_OPTIONAL) | 
 |     PASS(); | 
 |  | 
 |   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; | 
 |  | 
 |   const size_t kParallelSends = 10; | 
 |   std::vector<TestCompletionCallback*> sendto_callbacks(kParallelSends); | 
 |   std::vector<int32_t> sendto_results(kParallelSends); | 
 |   size_t pending = 0; | 
 |   for (size_t i = 0; i < kParallelSends; i++) { | 
 |     sendto_callbacks[i] = | 
 |         new TestCompletionCallback(instance_->pp_instance(), callback_type()); | 
 |     sendto_results[i] = | 
 |         client_socket.SendTo(message.c_str(), | 
 |                              static_cast<int32_t>(message.size()), | 
 |                              server_address, | 
 |                              sendto_callbacks[i]->GetCallback()); | 
 |  | 
 |     if (sendto_results[i] == PP_ERROR_INPROGRESS) { | 
 |       // Run a pending send to completion to free a slot for the current send. | 
 |       ASSERT_GT(i, pending); | 
 |       sendto_callbacks[pending]->WaitForResult(sendto_results[pending]); | 
 |       CHECK_CALLBACK_BEHAVIOR(*sendto_callbacks[pending]); | 
 |       ASSERT_EQ(message.size(), | 
 |                 static_cast<size_t>(sendto_callbacks[pending]->result())); | 
 |       pending++; | 
 |       // Try to send the message again. | 
 |       sendto_results[i] = | 
 |           client_socket.SendTo(message.c_str(), | 
 |                                static_cast<int32_t>(message.size()), | 
 |                                server_address, | 
 |                                sendto_callbacks[i]->GetCallback()); | 
 |       ASSERT_NE(PP_ERROR_INPROGRESS, sendto_results[i]); | 
 |     } | 
 |   } | 
 |  | 
 |   // Finish all pending sends. | 
 |   for (size_t i = pending; i < kParallelSends; i++) { | 
 |     sendto_callbacks[i]->WaitForResult(sendto_results[i]); | 
 |     CHECK_CALLBACK_BEHAVIOR(*sendto_callbacks[i]); | 
 |     ASSERT_EQ(message.size(), | 
 |               static_cast<size_t>(sendto_callbacks[i]->result())); | 
 |   } | 
 |  | 
 |   for (size_t i = 0; i < kParallelSends; ++i) | 
 |     delete sendto_callbacks[i]; | 
 |  | 
 |   for (size_t i = 0; i < kParallelSends; i++) { | 
 |     std::string str; | 
 |     ASSERT_SUBTEST_SUCCESS( | 
 |         ReadSocket(&server_socket, &recvfrom_address, message.size(), &str)); | 
 |     ASSERT_EQ(message, str); | 
 |   } | 
 |  | 
 |   server_socket.Close(); | 
 |   client_socket.Close(); | 
 |  | 
 |   PASS(); | 
 | } | 
 |  | 
 | std::string TestUDPSocket::TestMulticast() { | 
 |   pp::UDPSocket server1(instance_), server2(instance_); | 
 |  | 
 |   ASSERT_SUBTEST_SUCCESS(SetMulticastOptions(&server1)); | 
 |   ASSERT_SUBTEST_SUCCESS(SetMulticastOptions(&server2)); | 
 |  | 
 |   server1.Close(); | 
 |   server2.Close(); | 
 |  | 
 |   PASS(); | 
 | } |