| Index: mojo/services/network/udp_socket_unittest.cc
|
| diff --git a/mojo/services/network/udp_socket_unittest.cc b/mojo/services/network/udp_socket_unittest.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..10fcbbb0b017b34039785c6a636c4581d5657232
|
| --- /dev/null
|
| +++ b/mojo/services/network/udp_socket_unittest.cc
|
| @@ -0,0 +1,360 @@
|
| +// Copyright 2014 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 "base/at_exit.h"
|
| +#include "base/macros.h"
|
| +#include "base/memory/scoped_ptr.h"
|
| +#include "mojo/public/cpp/bindings/callback.h"
|
| +#include "mojo/services/public/interfaces/network/network_service.mojom.h"
|
| +#include "mojo/services/public/interfaces/network/udp_socket.mojom.h"
|
| +#include "mojo/shell/shell_test_helper.h"
|
| +#include "net/base/net_errors.h"
|
| +#include "testing/gtest/include/gtest/gtest.h"
|
| +#include "url/gurl.h"
|
| +
|
| +namespace mojo {
|
| +namespace service {
|
| +namespace {
|
| +
|
| +NetAddressPtr GetLocalHostWithAnyPort() {
|
| + NetAddressPtr addr(NetAddress::New());
|
| + addr->family = NET_ADDRESS_FAMILY_IPV4;
|
| + addr->ipv4 = NetAddressIPv4::New();
|
| + addr->ipv4->port = 0;
|
| + addr->ipv4->addr.resize(4);
|
| + addr->ipv4->addr[0] = 127;
|
| + addr->ipv4->addr[1] = 0;
|
| + addr->ipv4->addr[2] = 0;
|
| + addr->ipv4->addr[3] = 1;
|
| +
|
| + return addr.Pass();
|
| +}
|
| +
|
| +Array<uint8_t> CreateTestMessage(uint8_t initial, size_t size) {
|
| + Array<uint8_t> array(size);
|
| + for (size_t i = 0; i < size; ++i)
|
| + array[i] = static_cast<uint8_t>((i + initial) % 256);
|
| + return array.Pass();
|
| +}
|
| +
|
| +bool AreEqualArrays(const Array<uint8_t>& array_1,
|
| + const Array<uint8_t>& array_2) {
|
| + if (array_1.is_null() != array_2.is_null())
|
| + return false;
|
| + else if (array_1.is_null())
|
| + return true;
|
| +
|
| + if (array_1.size() != array_2.size())
|
| + return false;
|
| +
|
| + for (size_t i = 0; i < array_1.size(); ++i) {
|
| + if (array_1[i] != array_2[i])
|
| + return false;
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| +NetAddressPtr CopyAddress(const NetAddressPtr& addr) {
|
| + if (addr.is_null())
|
| + return NetAddressPtr();
|
| +
|
| + NetAddressPtr copy(NetAddress::New());
|
| + copy->family = addr->family;
|
| + if (!addr->ipv4.is_null()) {
|
| + copy->ipv4 = NetAddressIPv4::New();
|
| + copy->ipv4->port = addr->ipv4->port;
|
| + copy->ipv4->addr = Array<uint8_t>::From(addr->ipv4->addr.storage());
|
| + } else if (!addr->ipv6.is_null()) {
|
| + copy->ipv6 = NetAddressIPv6::New();
|
| + copy->ipv6->port = addr->ipv6->port;
|
| + copy->ipv6->addr = Array<uint8_t>::From(addr->ipv6->addr.storage());
|
| + }
|
| + return copy.Pass();
|
| +}
|
| +
|
| +class TestCallback {
|
| + public:
|
| + typedef Callback<void(NetworkErrorPtr)> CallbackType;
|
| +
|
| + TestCallback() : state_(new State(this)),
|
| + callback_(static_cast<CallbackType::Runnable*>(state_)),
|
| + run_loop_(NULL) {
|
| + }
|
| + ~TestCallback() {
|
| + state_->test_callback_ = NULL;
|
| + }
|
| +
|
| + CallbackType callback() const { return callback_; }
|
| + const NetworkErrorPtr& result() const { return result_; }
|
| +
|
| + void WaitForResult() {
|
| + if (!result_.is_null())
|
| + return;
|
| +
|
| + base::RunLoop run_loop;
|
| + run_loop_ = &run_loop;
|
| + run_loop.Run();
|
| + run_loop_ = NULL;
|
| + }
|
| +
|
| + private:
|
| + struct State : public CallbackType::Runnable {
|
| + explicit State(TestCallback* test_callback)
|
| + : test_callback_(test_callback) {}
|
| + virtual ~State() {}
|
| +
|
| + virtual void Run(NetworkErrorPtr result) const OVERRIDE {
|
| + if (test_callback_) {
|
| + test_callback_->result_ = result.Pass();
|
| + if (test_callback_->run_loop_)
|
| + test_callback_->run_loop_->Quit();
|
| + }
|
| + }
|
| +
|
| + TestCallback* test_callback_;
|
| + };
|
| +
|
| + // The lifespan is managed by |callback_| (and its copies).
|
| + State* state_;
|
| + CallbackType callback_;
|
| + base::RunLoop* run_loop_;
|
| + NetworkErrorPtr result_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(TestCallback);
|
| +};
|
| +
|
| +class TestCallbackWithAddress {
|
| + public:
|
| + typedef Callback<void(NetworkErrorPtr, NetAddressPtr)> CallbackType;
|
| +
|
| + TestCallbackWithAddress()
|
| + : state_(new State(this)),
|
| + callback_(static_cast<CallbackType::Runnable*>(state_)),
|
| + run_loop_(NULL) {
|
| + }
|
| + ~TestCallbackWithAddress() {
|
| + state_->test_callback_ = NULL;
|
| + }
|
| +
|
| + CallbackType callback() const { return callback_; }
|
| + const NetworkErrorPtr& result() const { return result_; }
|
| + const NetAddressPtr& net_address() const { return net_address_; }
|
| +
|
| + void WaitForResult() {
|
| + if (!result_.is_null())
|
| + return;
|
| +
|
| + base::RunLoop run_loop;
|
| + run_loop_ = &run_loop;
|
| + run_loop.Run();
|
| + run_loop_ = NULL;
|
| + }
|
| +
|
| + private:
|
| + struct State : public CallbackType::Runnable {
|
| + State(TestCallbackWithAddress* test_callback)
|
| + : test_callback_(test_callback) {}
|
| + virtual ~State() {}
|
| +
|
| + virtual void Run(NetworkErrorPtr result,
|
| + NetAddressPtr net_address) const OVERRIDE {
|
| + if (test_callback_) {
|
| + test_callback_->result_ = result.Pass();
|
| + test_callback_->net_address_ = net_address.Pass();
|
| + if (test_callback_->run_loop_)
|
| + test_callback_->run_loop_->Quit();
|
| + }
|
| + }
|
| +
|
| + TestCallbackWithAddress* test_callback_;
|
| + };
|
| +
|
| + // The lifespan is managed by |callback_| (and its copies).
|
| + State* state_;
|
| + CallbackType callback_;
|
| + base::RunLoop* run_loop_;
|
| + NetworkErrorPtr result_;
|
| + NetAddressPtr net_address_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(TestCallbackWithAddress);
|
| +};
|
| +
|
| +class UDPSocketTest : public testing::Test {
|
| + public:
|
| + UDPSocketTest() {}
|
| + virtual ~UDPSocketTest() {}
|
| +
|
| + virtual void SetUp() OVERRIDE {
|
| + test_helper_.Init();
|
| +
|
| + test_helper_.application_manager()->ConnectToService(
|
| + GURL("mojo:mojo_network_service"), &network_service_);
|
| +
|
| + network_service_->CreateUDPSocket(Get(&udp_socket_));
|
| + udp_socket_.set_client(&udp_socket_client_);
|
| + }
|
| +
|
| + protected:
|
| + struct ReceiveResult {
|
| + NetworkErrorPtr result;
|
| + NetAddressPtr addr;
|
| + Array<uint8_t> data;
|
| + };
|
| +
|
| + class UDPSocketClientImpl : public UDPSocketClient {
|
| + public:
|
| +
|
| + UDPSocketClientImpl() : run_loop_(NULL), expected_receive_count_(0) {}
|
| +
|
| + virtual ~UDPSocketClientImpl() {
|
| + while (!results_.empty()) {
|
| + delete results_.front();
|
| + results_.pop();
|
| + }
|
| + }
|
| +
|
| + virtual void OnReceived(NetworkErrorPtr result,
|
| + NetAddressPtr addr,
|
| + Array<uint8_t> data) OVERRIDE {
|
| + ReceiveResult* entry = new ReceiveResult();
|
| + entry->result = result.Pass();
|
| + entry->addr = addr.Pass();
|
| + entry->data = data.Pass();
|
| +
|
| + results_.push(entry);
|
| +
|
| + if (results_.size() == expected_receive_count_ && run_loop_) {
|
| + expected_receive_count_ = 0;
|
| + run_loop_->Quit();
|
| + }
|
| + }
|
| +
|
| + base::RunLoop* run_loop_;
|
| + std::queue<ReceiveResult*> results_;
|
| + size_t expected_receive_count_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(UDPSocketClientImpl);
|
| + };
|
| +
|
| + std::queue<ReceiveResult*>* GetReceiveResults() {
|
| + return &udp_socket_client_.results_;
|
| + }
|
| +
|
| + void WaitForReceiveResults(size_t count) {
|
| + if (GetReceiveResults()->size() == count)
|
| + return;
|
| +
|
| + udp_socket_client_.expected_receive_count_ = count;
|
| + base::RunLoop run_loop;
|
| + udp_socket_client_.run_loop_ = &run_loop;
|
| + run_loop.Run();
|
| + udp_socket_client_.run_loop_ = NULL;
|
| + }
|
| +
|
| + base::ShadowingAtExitManager at_exit_;
|
| + shell::ShellTestHelper test_helper_;
|
| +
|
| + NetworkServicePtr network_service_;
|
| + UDPSocketPtr udp_socket_;
|
| + UDPSocketClientImpl udp_socket_client_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(UDPSocketTest);
|
| +};
|
| +
|
| +} // namespace
|
| +
|
| +TEST_F(UDPSocketTest, Settings) {
|
| + TestCallback callback1;
|
| + udp_socket_->AllowAddressReuse(callback1.callback());
|
| + callback1.WaitForResult();
|
| + EXPECT_EQ(net::OK, callback1.result()->code);
|
| +
|
| + // Should fail because the socket hasn't been bound.
|
| + TestCallback callback2;
|
| + udp_socket_->SetSendBufferSize(1024, callback2.callback());
|
| + callback2.WaitForResult();
|
| + EXPECT_NE(net::OK, callback2.result()->code);
|
| +
|
| + // Should fail because the socket hasn't been bound.
|
| + TestCallback callback3;
|
| + udp_socket_->SetReceiveBufferSize(2048, callback3.callback());
|
| + callback3.WaitForResult();
|
| + EXPECT_NE(net::OK, callback3.result()->code);
|
| +
|
| + TestCallbackWithAddress callback4;
|
| + udp_socket_->Bind(GetLocalHostWithAnyPort(), callback4.callback());
|
| + callback4.WaitForResult();
|
| + EXPECT_EQ(net::OK, callback4.result()->code);
|
| + EXPECT_NE(0u, callback4.net_address()->ipv4->port);
|
| +
|
| + // Should fail because the socket has been bound.
|
| + TestCallback callback5;
|
| + udp_socket_->AllowAddressReuse(callback5.callback());
|
| + callback5.WaitForResult();
|
| + EXPECT_NE(net::OK, callback5.result()->code);
|
| +
|
| + TestCallback callback6;
|
| + udp_socket_->SetSendBufferSize(1024, callback6.callback());
|
| + callback6.WaitForResult();
|
| + EXPECT_EQ(net::OK, callback6.result()->code);
|
| +
|
| + TestCallback callback7;
|
| + udp_socket_->SetReceiveBufferSize(2048, callback7.callback());
|
| + callback7.WaitForResult();
|
| + EXPECT_EQ(net::OK, callback7.result()->code);
|
| +}
|
| +
|
| +TEST_F(UDPSocketTest, TestReadWrite) {
|
| + TestCallbackWithAddress callback1;
|
| + udp_socket_->Bind(GetLocalHostWithAnyPort(), callback1.callback());
|
| + callback1.WaitForResult();
|
| + ASSERT_EQ(net::OK, callback1.result()->code);
|
| + ASSERT_NE(0u, callback1.net_address()->ipv4->port);
|
| +
|
| + NetAddressPtr server_addr = CopyAddress(callback1.net_address());
|
| +
|
| + UDPSocketPtr client_socket;
|
| + network_service_->CreateUDPSocket(Get(&client_socket));
|
| +
|
| + TestCallbackWithAddress callback2;
|
| + client_socket->Bind(GetLocalHostWithAnyPort(), callback2.callback());
|
| + callback2.WaitForResult();
|
| + ASSERT_EQ(net::OK, callback2.result()->code);
|
| + ASSERT_NE(0u, callback2.net_address()->ipv4->port);
|
| +
|
| + const size_t kPacketCount = 6;
|
| + const size_t kPacketSize = 255;
|
| + udp_socket_->ReceiveMorePackets(kPacketCount);
|
| +
|
| + for (size_t i = 0; i < kPacketCount; ++i) {
|
| + if (i % 2) {
|
| + TestCallback callback;
|
| + client_socket->SendTo(CopyAddress(server_addr),
|
| + CreateTestMessage(static_cast<uint8_t>(i),
|
| + kPacketSize),
|
| + callback.callback());
|
| + callback.WaitForResult();
|
| + EXPECT_EQ(255, callback.result()->code);
|
| + } else {
|
| + client_socket->SendToAndForget(CopyAddress(server_addr),
|
| + CreateTestMessage(static_cast<uint8_t>(i),
|
| + kPacketSize));
|
| + }
|
| + }
|
| +
|
| + WaitForReceiveResults(kPacketCount);
|
| + for (size_t i = 0; i < kPacketCount; ++i) {
|
| + scoped_ptr<ReceiveResult> result(GetReceiveResults()->front());
|
| + GetReceiveResults()->pop();
|
| +
|
| + EXPECT_EQ(static_cast<int>(kPacketSize), result->result->code);
|
| + EXPECT_TRUE(AreEqualArrays(
|
| + CreateTestMessage(static_cast<uint8_t>(i), kPacketSize),
|
| + result->data));
|
| + }
|
| +}
|
| +
|
| +} // namespace service
|
| +} // namespace mojo
|
|
|