diff options
Diffstat (limited to 'ppapi/tests')
-rw-r--r-- | ppapi/tests/all_c_includes.h | 1 | ||||
-rw-r--r-- | ppapi/tests/all_cpp_includes.h | 1 | ||||
-rw-r--r-- | ppapi/tests/test_tcp_socket.cc | 160 | ||||
-rw-r--r-- | ppapi/tests/test_tcp_socket.h | 37 | ||||
-rw-r--r-- | ppapi/tests/test_utils.cc | 81 | ||||
-rw-r--r-- | ppapi/tests/test_utils.h | 11 |
6 files changed, 291 insertions, 0 deletions
diff --git a/ppapi/tests/all_c_includes.h b/ppapi/tests/all_c_includes.h index d550c21..4a8ae13 100644 --- a/ppapi/tests/all_c_includes.h +++ b/ppapi/tests/all_c_includes.h @@ -26,6 +26,7 @@ #include "ppapi/c/dev/ppb_printing_dev.h" #include "ppapi/c/dev/ppb_resource_array_dev.h" #include "ppapi/c/dev/ppb_scrollbar_dev.h" +#include "ppapi/c/dev/ppb_tcp_socket_dev.h" #include "ppapi/c/dev/ppb_testing_dev.h" #include "ppapi/c/dev/ppb_text_input_dev.h" #include "ppapi/c/dev/ppb_trace_event_dev.h" diff --git a/ppapi/tests/all_cpp_includes.h b/ppapi/tests/all_cpp_includes.h index 45fddf2..37a4100 100644 --- a/ppapi/tests/all_cpp_includes.h +++ b/ppapi/tests/all_cpp_includes.h @@ -26,6 +26,7 @@ #include "ppapi/cpp/dev/scriptable_object_deprecated.h" #include "ppapi/cpp/dev/scrollbar_dev.h" #include "ppapi/cpp/dev/selection_dev.h" +#include "ppapi/cpp/dev/tcp_socket_dev.h" #include "ppapi/cpp/dev/text_input_dev.h" #include "ppapi/cpp/dev/url_util_dev.h" #include "ppapi/cpp/dev/var_array_dev.h" diff --git a/ppapi/tests/test_tcp_socket.cc b/ppapi/tests/test_tcp_socket.cc new file mode 100644 index 0000000..7a14fe7 --- /dev/null +++ b/ppapi/tests/test_tcp_socket.cc @@ -0,0 +1,160 @@ +// 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_tcp_socket.h" + +#include "ppapi/cpp/dev/tcp_socket_dev.h" +#include "ppapi/tests/test_utils.h" +#include "ppapi/tests/testing_instance.h" + +namespace { + +// Validates the first line of an HTTP response. +bool ValidateHttpResponse(const std::string& s) { + // Just check that it begins with "HTTP/" and ends with a "\r\n". + return s.size() >= 5 && + s.substr(0, 5) == "HTTP/" && + s.substr(s.size() - 2) == "\r\n"; +} + +} // namespace + +REGISTER_TEST_CASE(TCPSocket); + +TestTCPSocket::TestTCPSocket(TestingInstance* instance) : TestCase(instance) { +} + +bool TestTCPSocket::Init() { + if (!pp::TCPSocket_Dev::IsAvailable()) + return false; + + // We need something to connect to, so we connect to the HTTP server whence we + // came. Grab the host and port. + if (!EnsureRunningOverHTTP()) + return false; + + std::string host; + uint16_t port = 0; + if (!GetLocalHostPort(instance_->pp_instance(), &host, &port)) + return false; + + if (!ResolveHost(instance_->pp_instance(), host, port, &addr_)) + return false; + + return true; +} + +void TestTCPSocket::RunTests(const std::string& filter) { + RUN_CALLBACK_TEST(TestTCPSocket, Connect, filter); + RUN_CALLBACK_TEST(TestTCPSocket, ReadWrite, filter); + RUN_CALLBACK_TEST(TestTCPSocket, SetOption, filter); +} + +std::string TestTCPSocket::TestConnect() { + pp::TCPSocket_Dev socket(instance_); + TestCompletionCallback cb(instance_->pp_instance(), callback_type()); + + cb.WaitForResult(socket.Connect(addr_, cb.GetCallback())); + CHECK_CALLBACK_BEHAVIOR(cb); + ASSERT_EQ(PP_OK, cb.result()); + + pp::NetAddress_Dev local_addr, remote_addr; + local_addr = socket.GetLocalAddress(); + remote_addr = socket.GetRemoteAddress(); + + ASSERT_NE(0, local_addr.pp_resource()); + ASSERT_NE(0, remote_addr.pp_resource()); + ASSERT_TRUE(EqualNetAddress(addr_, remote_addr)); + + socket.Close(); + + PASS(); +} + +std::string TestTCPSocket::TestReadWrite() { + pp::TCPSocket_Dev socket(instance_); + TestCompletionCallback cb(instance_->pp_instance(), callback_type()); + + cb.WaitForResult(socket.Connect(addr_, cb.GetCallback())); + CHECK_CALLBACK_BEHAVIOR(cb); + ASSERT_EQ(PP_OK, cb.result()); + + ASSERT_EQ(PP_OK, WriteStringToSocket(&socket, "GET / HTTP/1.0\r\n\r\n")); + + // Read up to the first \n and check that it looks like valid HTTP response. + std::string s; + ASSERT_EQ(PP_OK, ReadFirstLineFromSocket(&socket, &s)); + ASSERT_TRUE(ValidateHttpResponse(s)); + + PASS(); +} + +std::string TestTCPSocket::TestSetOption() { + pp::TCPSocket_Dev socket(instance_); + TestCompletionCallback cb(instance_->pp_instance(), callback_type()); + + cb.WaitForResult( + socket.SetOption(PP_TCPSOCKET_OPTION_NO_DELAY, true, cb.GetCallback())); + CHECK_CALLBACK_BEHAVIOR(cb); + ASSERT_EQ(PP_ERROR_FAILED, cb.result()); + + cb.WaitForResult(socket.Connect(addr_, cb.GetCallback())); + CHECK_CALLBACK_BEHAVIOR(cb); + ASSERT_EQ(PP_OK, cb.result()); + + cb.WaitForResult( + socket.SetOption(PP_TCPSOCKET_OPTION_NO_DELAY, true, cb.GetCallback())); + CHECK_CALLBACK_BEHAVIOR(cb); + ASSERT_EQ(PP_OK, cb.result()); + + PASS(); +} + +int32_t TestTCPSocket::ReadFirstLineFromSocket(pp::TCPSocket_Dev* socket, + std::string* s) { + char buffer[1000]; + + s->clear(); + // Make sure we don't just hang if |Read()| spews. + while (s->size() < 10000) { + TestCompletionCallback cb(instance_->pp_instance(), callback_type()); + int32_t rv = socket->Read(buffer, sizeof(buffer), cb.GetCallback()); + if (callback_type() == PP_REQUIRED && rv != PP_OK_COMPLETIONPENDING) + return PP_ERROR_FAILED; + cb.WaitForResult(rv); + if (cb.result() < 0) + return cb.result(); + if (cb.result() == 0) + return PP_ERROR_FAILED; // Didn't get a \n-terminated line. + s->reserve(s->size() + cb.result()); + for (int32_t i = 0; i < cb.result(); i++) { + s->push_back(buffer[i]); + if (buffer[i] == '\n') + return PP_OK; + } + } + return PP_ERROR_FAILED; +} + +int32_t TestTCPSocket::WriteStringToSocket(pp::TCPSocket_Dev* socket, + const std::string& s) { + const char* buffer = s.data(); + size_t written = 0; + while (written < s.size()) { + TestCompletionCallback cb(instance_->pp_instance(), callback_type()); + int32_t rv = socket->Write(buffer + written, s.size() - written, + cb.GetCallback()); + if (callback_type() == PP_REQUIRED && rv != PP_OK_COMPLETIONPENDING) + return PP_ERROR_FAILED; + cb.WaitForResult(rv); + if (cb.result() < 0) + return cb.result(); + if (cb.result() == 0) + return PP_ERROR_FAILED; + written += cb.result(); + } + if (written != s.size()) + return PP_ERROR_FAILED; + return PP_OK; +} diff --git a/ppapi/tests/test_tcp_socket.h b/ppapi/tests/test_tcp_socket.h new file mode 100644 index 0000000..0ff7e33 --- /dev/null +++ b/ppapi/tests/test_tcp_socket.h @@ -0,0 +1,37 @@ +// 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. + +#ifndef PAPPI_TESTS_TEST_TCP_SOCKET_H_ +#define PAPPI_TESTS_TEST_TCP_SOCKET_H_ + +#include <string> + +#include "ppapi/c/pp_stdint.h" +#include "ppapi/cpp/dev/net_address_dev.h" +#include "ppapi/tests/test_case.h" + +namespace pp { +class TCPSocket_Dev; +} + +class TestTCPSocket: public TestCase { + public: + explicit TestTCPSocket(TestingInstance* instance); + + // TestCase implementation. + virtual bool Init(); + virtual void RunTests(const std::string& filter); + + private: + std::string TestConnect(); + std::string TestReadWrite(); + std::string TestSetOption(); + + int32_t ReadFirstLineFromSocket(pp::TCPSocket_Dev* socket, std::string* s); + int32_t WriteStringToSocket(pp::TCPSocket_Dev* socket, const std::string& s); + + pp::NetAddress_Dev addr_; +}; + +#endif // PAPPI_TESTS_TEST_TCP_SOCKET_H_ diff --git a/ppapi/tests/test_utils.cc b/ppapi/tests/test_utils.cc index 86a9bc1..509d6ff 100644 --- a/ppapi/tests/test_utils.cc +++ b/ppapi/tests/test_utils.cc @@ -6,6 +6,7 @@ #include <stdio.h> #include <stdlib.h> +#include <string.h> #if defined(_MSC_VER) #include <windows.h> #else @@ -13,7 +14,11 @@ #endif #include "ppapi/c/pp_errors.h" +#include "ppapi/cpp/dev/net_address_dev.h" +#include "ppapi/cpp/instance_handle.h" #include "ppapi/cpp/module.h" +#include "ppapi/cpp/private/host_resolver_private.h" +#include "ppapi/cpp/private/net_address_private.h" #include "ppapi/cpp/var.h" namespace { @@ -98,6 +103,82 @@ uint16_t ConvertToNetEndian16(uint16_t x) { return (x << 8) | (x >> 8); } +bool EqualNetAddress(const pp::NetAddress_Dev& addr1, + const pp::NetAddress_Dev& addr2) { + if (addr1.GetFamily() == PP_NETADDRESS_FAMILY_UNSPECIFIED || + addr2.GetFamily() == PP_NETADDRESS_FAMILY_UNSPECIFIED) { + return false; + } + + if (addr1.GetFamily() == PP_NETADDRESS_FAMILY_IPV4) { + PP_NetAddress_IPv4_Dev ipv4_addr1, ipv4_addr2; + if (!addr1.DescribeAsIPv4Address(&ipv4_addr1) || + !addr2.DescribeAsIPv4Address(&ipv4_addr2)) { + return false; + } + + return ipv4_addr1.port == ipv4_addr2.port && + !memcmp(ipv4_addr1.addr, ipv4_addr2.addr, sizeof(ipv4_addr1.addr)); + } else { + PP_NetAddress_IPv6_Dev ipv6_addr1, ipv6_addr2; + if (!addr1.DescribeAsIPv6Address(&ipv6_addr1) || + !addr2.DescribeAsIPv6Address(&ipv6_addr2)) { + return false; + } + + return ipv6_addr1.port == ipv6_addr2.port && + !memcmp(ipv6_addr1.addr, ipv6_addr2.addr, sizeof(ipv6_addr1.addr)); + } +} + +bool ResolveHost(PP_Instance instance, + const std::string& host, + uint16_t port, + pp::NetAddress_Dev* addr) { + // TODO(yzshen): Change to use the public host resolver once it is supported. + pp::InstanceHandle instance_handle(instance); + pp::HostResolverPrivate host_resolver(instance_handle); + PP_HostResolver_Private_Hint hint = { PP_NETADDRESSFAMILY_UNSPECIFIED, 0 }; + + TestCompletionCallback callback(instance); + callback.WaitForResult( + host_resolver.Resolve(host, port, hint, callback.GetCallback())); + + PP_NetAddress_Private addr_private; + if (callback.result() != PP_OK || host_resolver.GetSize() == 0 || + !host_resolver.GetNetAddress(0, &addr_private)) { + return false; + } + + switch (pp::NetAddressPrivate::GetFamily(addr_private)) { + case PP_NETADDRESSFAMILY_IPV4: { + PP_NetAddress_IPv4_Dev ipv4_addr; + ipv4_addr.port = ConvertToNetEndian16( + pp::NetAddressPrivate::GetPort(addr_private)); + if (!pp::NetAddressPrivate::GetAddress(addr_private, ipv4_addr.addr, + sizeof(ipv4_addr.addr))) { + return false; + } + *addr = pp::NetAddress_Dev(instance_handle, ipv4_addr); + return true; + } + case PP_NETADDRESSFAMILY_IPV6: { + PP_NetAddress_IPv6_Dev ipv6_addr; + ipv6_addr.port = ConvertToNetEndian16( + pp::NetAddressPrivate::GetPort(addr_private)); + if (!pp::NetAddressPrivate::GetAddress(addr_private, ipv6_addr.addr, + sizeof(ipv6_addr.addr))) { + return false; + } + *addr = pp::NetAddress_Dev(instance_handle, ipv6_addr); + return true; + } + default: { + return false; + } + } +} + void NestedEvent::Wait() { PP_DCHECK(pp::Module::Get()->core()->IsMainThread()); // Don't allow nesting more than once; it doesn't work with the code as-is, diff --git a/ppapi/tests/test_utils.h b/ppapi/tests/test_utils.h index 0410018..04aa452 100644 --- a/ppapi/tests/test_utils.h +++ b/ppapi/tests/test_utils.h @@ -14,6 +14,10 @@ #include "ppapi/cpp/message_loop.h" #include "ppapi/utility/completion_callback_factory.h" +namespace pp { +class NetAddress_Dev; +} + // Timeout to wait for some action to complete. extern const int kActionTimeoutMs; @@ -24,6 +28,13 @@ bool GetLocalHostPort(PP_Instance instance, std::string* host, uint16_t* port); uint16_t ConvertFromNetEndian16(uint16_t x); uint16_t ConvertToNetEndian16(uint16_t x); +bool EqualNetAddress(const pp::NetAddress_Dev& addr1, + const pp::NetAddress_Dev& addr2); +// Only returns the first address if there are more than one available. +bool ResolveHost(PP_Instance instance, + const std::string& host, + uint16_t port, + pp::NetAddress_Dev* addr); // NestedEvent allows you to run a nested MessageLoop and wait for a particular // event to complete. For example, you can use it to wait for a callback on a |