blob: 4f4e7a05104a8e131eaa52c91e83525fb4c54d24 [file] [log] [blame]
// Copyright 2014 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/sockets/SocketDrainer.h"
#include "android/base/sockets/SocketUtils.h"
#include "android/utils/looper.h"
#include "android/utils/tempfile.h"
#include "android/utils/socket_drainer.h"
// The following unit tests only work on Linux and OS X, because they rely on "fork()"
// system call, which is not supported by Windows yet.
#ifndef _WIN32
#include <gtest/gtest.h>
#include <errno.h>
#include <stdio.h>
namespace android {
namespace base {
namespace {
const int kBigNumber = (1<<10);
int writeToSocket(const char* filename, int socket_fd) {
FILE* ff = fopen(filename, "w");
int i = 0;
for (i = 0; i < kBigNumber; ++i) {
ssize_t size = socketSend(socket_fd, "a", 1);
if (size == 0) {
break;
}
if (size < 0) {
if (errno == EWOULDBLOCK || errno == EAGAIN) {
continue;
} else {
break;
}
}
}
fprintf(ff, "%d", i);
fclose(ff);
socketShutdownWrites(socket_fd);
return 0;
}
} // namespace
TEST(SocketDrainer, GracefulShutDownFromC) {
int socket_fd1 = -1;
int socket_fd2 = -1;
int error = socketCreatePair(&socket_fd1, &socket_fd2);
EXPECT_EQ(error, 0);
ASSERT_GE(socket_fd1, 0);
ASSERT_GE(socket_fd2, 0);
TempFile* childFile = tempfile_create();
const char* kChildFileName = tempfile_path(childFile);
// create a process to send bytes from the writing socket
int childpid = fork();
int childExitStatus;
if (childpid == 0) {
socketClose(socket_fd2);
_exit(writeToSocket(kChildFileName, socket_fd1));
} else {
socketClose(socket_fd1);
ASSERT_GT(childpid, 0);
}
// drain the reading socket: C style
::Looper* mainLooper = looper_newGeneric();
socket_drainer_start(mainLooper);
socket_drainer_drain_and_close(socket_fd2);
looper_run(mainLooper);
socket_drainer_stop();
looper_free(mainLooper);
wait(&childExitStatus);
// find out how many bytes have been written
FILE* fchild = fopen(kChildFileName, "r");
int numWritten = 0;
fscanf(fchild, "%d", &numWritten);
tempfile_close(childFile);
// should not miss any byte
EXPECT_EQ(kBigNumber, numWritten);
}
TEST(SocketDrainer, GracefulShutDownFromCpp) {
int socket_fd1 = -1;
int socket_fd2 = -1;
int error = socketCreatePair(&socket_fd1, &socket_fd2);
EXPECT_EQ(0, error);
ASSERT_GE(socket_fd1, 0);
ASSERT_GE(socket_fd2, 0);
TempFile* childFile = tempfile_create();
const char* kChildFileName = tempfile_path(childFile);
int childpid = fork();
int childExitStatus;
if (childpid == 0) {
socketClose(socket_fd2);
_exit(writeToSocket(kChildFileName, socket_fd1));
} else {
socketClose(socket_fd1);
ASSERT_GT(childpid, 0);
}
// drain the reading socket: C++ style
Looper* mainLooper = Looper::create();
{
SocketDrainer socketDrainer(mainLooper);
socketDrainer.drainAndClose(socket_fd2);
mainLooper->run();
}
delete mainLooper;
wait(&childExitStatus);
FILE* fchild = fopen(kChildFileName, "r");
int numWritten = 0;
fscanf(fchild, "%d", &numWritten);
tempfile_close(childFile);
EXPECT_EQ(kBigNumber, numWritten);
}
} // namespace base
} // namespace android
#endif