// Copyright (c) 2010 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 "net/base/listen_socket_unittest.h" #include #include #include "base/eintr_wrapper.h" #include "net/base/net_util.h" #include "testing/platform_test.h" const int ListenSocketTester::kTestPort = 9999; static const int kReadBufSize = 1024; static const char kHelloWorld[] = "HELLO, WORLD"; static const int kMaxQueueSize = 20; static const char kLoopback[] = "127.0.0.1"; static const int kDefaultTimeoutMs = 5000; ListenSocketTester::ListenSocketTester() : thread_(NULL), loop_(NULL), server_(NULL), connection_(NULL), cv_(&lock_) { } void ListenSocketTester::SetUp() { base::Thread::Options options; options.message_loop_type = MessageLoop::TYPE_IO; thread_.reset(new base::Thread("socketio_test")); thread_->StartWithOptions(options); loop_ = reinterpret_cast(thread_->message_loop()); loop_->PostTask(FROM_HERE, NewRunnableMethod( this, &ListenSocketTester::Listen)); // verify Listen succeeded NextAction(); ASSERT_FALSE(server_ == NULL); ASSERT_EQ(ACTION_LISTEN, last_action_.type()); // verify the connect/accept and setup test_socket_ test_socket_ = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); ASSERT_NE(INVALID_SOCKET, test_socket_); struct sockaddr_in client; client.sin_family = AF_INET; client.sin_addr.s_addr = inet_addr(kLoopback); client.sin_port = htons(kTestPort); int ret = HANDLE_EINTR( connect(test_socket_, reinterpret_cast(&client), sizeof(client))); ASSERT_NE(ret, SOCKET_ERROR); NextAction(); ASSERT_EQ(ACTION_ACCEPT, last_action_.type()); } void ListenSocketTester::TearDown() { #if defined(OS_WIN) ASSERT_EQ(0, closesocket(test_socket_)); #elif defined(OS_POSIX) ASSERT_EQ(0, HANDLE_EINTR(close(test_socket_))); #endif NextAction(); ASSERT_EQ(ACTION_CLOSE, last_action_.type()); loop_->PostTask(FROM_HERE, NewRunnableMethod( this, &ListenSocketTester::Shutdown)); NextAction(); ASSERT_EQ(ACTION_SHUTDOWN, last_action_.type()); thread_.reset(); loop_ = NULL; } void ListenSocketTester::ReportAction(const ListenSocketTestAction& action) { base::AutoLock locked(lock_); queue_.push_back(action); cv_.Broadcast(); } void ListenSocketTester::NextAction() { base::AutoLock locked(lock_); while (queue_.empty()) cv_.Wait(); last_action_ = queue_.front(); queue_.pop_front(); } int ListenSocketTester::ClearTestSocket() { char buf[kReadBufSize]; int len_ret = 0; do { int len = HANDLE_EINTR(recv(test_socket_, buf, kReadBufSize, 0)); if (len == SOCKET_ERROR || len == 0) { break; } else { len_ret += len; } } while (true); return len_ret; } void ListenSocketTester::Shutdown() { connection_->Release(); connection_ = NULL; server_->Release(); server_ = NULL; ReportAction(ListenSocketTestAction(ACTION_SHUTDOWN)); } void ListenSocketTester::Listen() { server_ = DoListen(); if (server_) { server_->AddRef(); ReportAction(ListenSocketTestAction(ACTION_LISTEN)); } } void ListenSocketTester::SendFromTester() { connection_->Send(kHelloWorld); ReportAction(ListenSocketTestAction(ACTION_SEND)); } void ListenSocketTester::TestClientSend() { ASSERT_TRUE(Send(test_socket_, kHelloWorld)); NextAction(); ASSERT_EQ(ACTION_READ, last_action_.type()); ASSERT_EQ(last_action_.data(), kHelloWorld); } void ListenSocketTester::TestClientSendLong() { size_t hello_len = strlen(kHelloWorld); std::string long_string; size_t long_len = 0; for (int i = 0; i < 200; i++) { long_string += kHelloWorld; long_len += hello_len; } ASSERT_TRUE(Send(test_socket_, long_string)); size_t read_len = 0; while (read_len < long_len) { NextAction(); ASSERT_EQ(ACTION_READ, last_action_.type()); std::string last_data = last_action_.data(); size_t len = last_data.length(); if (long_string.compare(read_len, len, last_data)) { ASSERT_EQ(long_string.compare(read_len, len, last_data), 0); } read_len += last_data.length(); } ASSERT_EQ(read_len, long_len); } void ListenSocketTester::TestServerSend() { loop_->PostTask(FROM_HERE, NewRunnableMethod( this, &ListenSocketTester::SendFromTester)); NextAction(); ASSERT_EQ(ACTION_SEND, last_action_.type()); const int buf_len = 200; char buf[buf_len+1]; unsigned recv_len = 0; while (recv_len < strlen(kHelloWorld)) { int r = HANDLE_EINTR(recv(test_socket_, buf, buf_len, 0)); ASSERT_GE(r, 0); recv_len += static_cast(r); if (!r) break; } buf[recv_len] = 0; ASSERT_STREQ(buf, kHelloWorld); } bool ListenSocketTester::Send(SOCKET sock, const std::string& str) { int len = static_cast(str.length()); int send_len = HANDLE_EINTR(send(sock, str.data(), len, 0)); if (send_len == SOCKET_ERROR) { LOG(ERROR) << "send failed: " << errno; return false; } else if (send_len != len) { return false; } return true; } void ListenSocketTester::DidAccept(ListenSocket *server, ListenSocket *connection) { connection_ = connection; connection_->AddRef(); ReportAction(ListenSocketTestAction(ACTION_ACCEPT)); } void ListenSocketTester::DidRead(ListenSocket *connection, const char* data, int len) { std::string str(data, len); ReportAction(ListenSocketTestAction(ACTION_READ, str)); } void ListenSocketTester::DidClose(ListenSocket *sock) { ReportAction(ListenSocketTestAction(ACTION_CLOSE)); } ListenSocketTester::~ListenSocketTester() {} ListenSocket* ListenSocketTester::DoListen() { return ListenSocket::Listen(kLoopback, kTestPort, this); } class ListenSocketTest: public PlatformTest { public: ListenSocketTest() { tester_ = NULL; } virtual void SetUp() { PlatformTest::SetUp(); tester_ = new ListenSocketTester(); tester_->SetUp(); } virtual void TearDown() { PlatformTest::TearDown(); tester_->TearDown(); tester_ = NULL; } scoped_refptr tester_; }; TEST_F(ListenSocketTest, ClientSend) { tester_->TestClientSend(); } TEST_F(ListenSocketTest, ClientSendLong) { tester_->TestClientSendLong(); } TEST_F(ListenSocketTest, ServerSend) { tester_->TestServerSend(); }