blob: fbe57115763a7e9fc0ed29d9a4a91e8f6c8d7726 [file] [log] [blame]
// Copyright 2016 The Android Open Source Project
//
// This software is licensed under the terms of the GNU General Public
// License version 2, as published by the Free Software Foundation, and
// may be copied, distributed, and modified under those terms.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
#include "android/base/async/AsyncSocketServer.h"
#include "android/base/async/Looper.h"
#include "android/base/async/ThreadLooper.h"
#include "android/base/Log.h"
#include "android/base/sockets/ScopedSocket.h"
#include "android/base/threads/Async.h"
#include <gtest/gtest.h>
#include <memory>
#include <functional>
namespace android {
namespace base {
namespace {
struct TestData {
TestData(Looper* looper, int port) : mLooper(looper), mPort(port) {}
// Start a server that will call ::onConnection on each connection attempt.
bool startServer(AsyncSocketServer::LoopbackMode mode) {
auto callback =
std::bind(&TestData::onConnection, this, std::placeholders::_1);
mServer = AsyncSocketServer::createTcpLoopbackServer(mPort, callback,
mode, mLooper);
if (!mServer.get()) {
return false;
}
mPort = mServer->port();
mServer->startListening();
return true;
}
// On every connection, increment the counter, read one byte from the
// socket, then close it! Note that this doesn't start listening for
// new connections, this is left to the test itself.
bool onConnection(int socket) {
this->mCounter++;
char c = 0;
EXPECT_EQ(1, socketRecv(socket, &c, 1));
EXPECT_EQ(0x42, c);
socketClose(socket);
return true;
}
// Connect to IPv4 loopback |port| and send one byte of data.
static intptr_t connectAndWriteTo4(int port) {
// Blocking connect to the socket.
int s = socketTcp4LoopbackClient(port);
if (s < 0) {
PLOG(ERROR) << "Connection failed: ";
return -1;
}
// Write a byte to it.
char c = 0x42;
int ret = socketSend(s, &c, 1);
if (ret < 0) {
PLOG(ERROR) << "Could not send data";
}
socketClose(s);
return (ret == 1) ? 0 : -1;
}
// Connect to IPv6 loopback |port| and send one byte of data.
static intptr_t connectAndWriteTo6(int port) {
// Blocking connect to the socket.
int s = socketTcp6LoopbackClient(port);
if (s < 0) {
return -1;
}
// Write a byte to it.
char c = 0x42;
int ret = socketSend(s, &c, 1);
socketClose(s);
return (ret == 1) ? 0 : -1;
}
Looper* mLooper = nullptr;
int mPort = 0;
int mCounter = 0;
std::unique_ptr<AsyncSocketServer> mServer;
};
} // namespace
TEST(AsyncSocketServer, createTcpLoopbackServerIPv4) {
int kPort = 0; // random high value?
int kDelayMs = 20;
Looper* looper = ThreadLooper::get();
TestData data(looper, kPort);
ASSERT_TRUE(data.startServer(AsyncSocketServer::kIPv4));
EXPECT_EQ(AsyncSocketServer::kIPv4, data.mServer->getListenMode());
EXPECT_EQ(0, data.mCounter);
// Start two background threads that connect to the port.
// Only the first one will succeed because the AsyncSocketServer
// will stop listening automatically after the first connection.
auto connector = std::bind(&TestData::connectAndWriteTo4, data.mPort);
android::base::async(connector);
android::base::async(connector);
int ret = looper->runWithDeadlineMs(looper->nowMs() + kDelayMs);
EXPECT_EQ(1, data.mCounter);
EXPECT_EQ(ETIMEDOUT, ret);
// Start listening, which should unblock the second background thread.
data.mServer->startListening();
ret = looper->runWithDeadlineMs(looper->nowMs() + kDelayMs);
EXPECT_EQ(2, data.mCounter);
EXPECT_EQ(ETIMEDOUT, ret);
}
TEST(AsyncSocketServer, createTcpLoopbackServerIPv6) {
int kPort = 0; // random high value?
int kDelayMs = 20;
Looper* looper = ThreadLooper::get();
TestData data(looper, kPort);
ASSERT_TRUE(data.startServer(AsyncSocketServer::kIPv6));
EXPECT_EQ(AsyncSocketServer::kIPv6, data.mServer->getListenMode());
EXPECT_EQ(0, data.mCounter);
// Start two background threads that connect to the port.
// Only the first one will succeed because the AsyncSocketServer
// will stop listening automatically after the first connection.
auto connector = std::bind(&TestData::connectAndWriteTo6, data.mPort);
android::base::async(connector);
android::base::async(connector);
int ret = looper->runWithDeadlineMs(looper->nowMs() + kDelayMs);
EXPECT_EQ(1, data.mCounter);
EXPECT_EQ(ETIMEDOUT, ret);
// Start listening, which should unblock the second background thread.
data.mServer->startListening();
ret = looper->runWithDeadlineMs(looper->nowMs() + kDelayMs);
EXPECT_EQ(2, data.mCounter);
EXPECT_EQ(ETIMEDOUT, ret);
}
TEST(AsyncSocketServer, createTcpLoopbackServerIPv4AndIPv6) {
int kPort = 0; // random high value?
int kDelayMs = 20;
Looper* looper = ThreadLooper::get();
TestData data(looper, kPort);
ASSERT_TRUE(data.startServer(AsyncSocketServer::kIPv4AndIPv6));
EXPECT_EQ(AsyncSocketServer::kIPv4AndIPv6, data.mServer->getListenMode());
EXPECT_EQ(0, data.mCounter);
// Start two background threads that connect to the port.
// Only the first one will succeed because the AsyncSocketServer
// will stop listening automatically after the first connection.
// NOTE: The first one connects through IPv4, the second one through IPv6.
auto connector4 = std::bind(&TestData::connectAndWriteTo4, data.mPort);
android::base::async(connector4);
auto connector6 = std::bind(&TestData::connectAndWriteTo6, data.mPort);
android::base::async(connector6);
int ret = looper->runWithDeadlineMs(looper->nowMs() + kDelayMs);
EXPECT_EQ(1, data.mCounter);
EXPECT_EQ(ETIMEDOUT, ret);
// Start listening, which should unblock the second background thread.
data.mServer->startListening();
ret = looper->runWithDeadlineMs(looper->nowMs() + kDelayMs);
EXPECT_EQ(2, data.mCounter);
EXPECT_EQ(ETIMEDOUT, ret);
}
} // namespace base
} // namespace android