blob: a0ab0a00fd353d83e9a012783454be364556a2b6 [file] [log] [blame]
// Copyright 2011 The Goma Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// This is a Windows-only unit test
#ifdef _WIN32
#include "socket_helper_win.h"
#include <glog/logging.h>
#include <gtest/gtest.h>
#include "absl/time/clock.h"
#include "absl/time/time.h"
#include "lockhelper.h"
#include "platform_thread.h"
namespace devtools_goma {
const char* TEST_STRING = "Hola! Amigo!";
class SocketPairTestThread : public PlatformThread::Delegate {
public:
typedef enum {
kInitial,
kAsync,
kBlockRecv,
kBlockSend,
kTerminate
} State;
explicit SocketPairTestThread(int fd)
: state_(kInitial), signal_(::CreateEvent(nullptr, TRUE, FALSE, nullptr)),
socket_(fd) {
CHECK_NE(signal_, INVALID_HANDLE_VALUE);
}
~SocketPairTestThread() override {
closesocket(socket_);
CloseHandle(signal_);
}
SocketPairTestThread(const SocketPairTestThread&) = delete;
SocketPairTestThread& operator=(const SocketPairTestThread&) = delete;
void ThreadMain() override {
bool terminate_signaled = false;
for (; !terminate_signaled; ) {
WaitForSingleObject(signal_, INFINITE);
AutoLock lock(&lock_);
switch (state_) {
case kInitial:
break;
case kAsync:
{
timeval tv;
tv.tv_sec = 2;
tv.tv_usec = 0;
bool r_done = false;
fd_set r_set;
send(socket_, TEST_STRING,
static_cast<int>(strlen(TEST_STRING)), 0);
FD_ZERO(&r_set);
MSVC_PUSH_DISABLE_WARNING_FOR_FD_SET();
FD_SET(static_cast<SOCKET>(socket_), &r_set);
MSVC_POP_WARNING();
while (!r_done) {
int result = select(socket_ + 1, &r_set, 0, 0, &tv);
if (FD_ISSET(socket_, &r_set)) {
char buf[256] = {0};
recv(socket_, buf, 256, 0);
message_ = buf;
r_done = true;
}
if (result == 0 || result == SOCKET_ERROR) {
break; // recv timeout or select failed
}
}
}
break;
case kBlockRecv:
{
char buf[256] = {0};
recv(socket_, buf, 256, 0);
message_ = buf;
}
break;
case kBlockSend:
send(socket_, TEST_STRING, static_cast<int>(strlen(TEST_STRING)), 0);
break;
default: // kTerminate
terminate_signaled = true;
break;
}
ResetEvent(signal_);
}
}
void set_state(State state) {
// Block set_state until |signal_| is not set.
while (::WaitForSingleObjectEx(signal_, 0, TRUE) != WAIT_TIMEOUT) {
absl::SleepFor(absl::Milliseconds(100));
}
AutoLock lock(&lock_);
state_ = state;
SetEvent(signal_);
}
std::string message() {
AutoLock lock(&lock_);
return message_;
}
void Reset() {
AutoLock lock(&lock_);
message_.clear();
}
private:
Lock lock_;
State state_;
std::string message_;
HANDLE signal_;
int socket_;
};
TEST(SocketHelperWin, BlockingSocketPair) {
int fd[2] = {0};
EXPECT_NE(SOCKET_ERROR, socketpair(AF_INET, SOCK_STREAM, 0, fd));
SocketPairTestThread thread0(fd[0]);
SocketPairTestThread thread1(fd[1]);
PlatformThreadHandle handle0 = kNullThreadHandle;
PlatformThreadHandle handle1 = kNullThreadHandle;
EXPECT_TRUE(PlatformThread::Create(&thread0, &handle0));
EXPECT_TRUE(PlatformThread::Create(&thread1, &handle1));
thread0.set_state(SocketPairTestThread::kBlockRecv);
thread1.set_state(SocketPairTestThread::kBlockSend);
thread0.set_state(SocketPairTestThread::kBlockSend);
thread1.set_state(SocketPairTestThread::kBlockRecv);
thread0.set_state(SocketPairTestThread::kTerminate);
thread1.set_state(SocketPairTestThread::kTerminate);
WaitForSingleObject(handle0, 2000);
WaitForSingleObject(handle1, 2000);
EXPECT_STREQ(TEST_STRING, thread0.message().c_str());
EXPECT_STREQ(TEST_STRING, thread1.message().c_str());
}
TEST(SocketHelperWin, AsyncSocketPair) {
int fd[2] = {0};
EXPECT_EQ(0, async_socketpair(fd));
SocketPairTestThread thread0(fd[0]);
SocketPairTestThread thread1(fd[1]);
PlatformThreadHandle handle0 = kNullThreadHandle;
PlatformThreadHandle handle1 = kNullThreadHandle;
EXPECT_TRUE(PlatformThread::Create(&thread0, &handle0));
EXPECT_TRUE(PlatformThread::Create(&thread1, &handle1));
thread0.set_state(SocketPairTestThread::kAsync);
thread1.set_state(SocketPairTestThread::kAsync);
thread0.set_state(SocketPairTestThread::kTerminate);
thread1.set_state(SocketPairTestThread::kTerminate);
WaitForSingleObject(handle0, 2000);
WaitForSingleObject(handle1, 2000);
EXPECT_STREQ(TEST_STRING, thread0.message().c_str());
EXPECT_STREQ(TEST_STRING, thread1.message().c_str());
}
} // namespace devtools_goma
#endif // _WIN32