build bionic gtest runner on mac.
Change-Id: I39a7e94b6662256646dfaeb8f9ecd5c03cd5fbc6
This commit is contained in:
parent
a992004c9e
commit
767fb1c5c6
|
@ -236,7 +236,13 @@ build_type := target
|
||||||
build_target := STATIC_TEST_LIBRARY
|
build_target := STATIC_TEST_LIBRARY
|
||||||
include $(LOCAL_PATH)/Android.build.mk
|
include $(LOCAL_PATH)/Android.build.mk
|
||||||
build_type := host
|
build_type := host
|
||||||
|
|
||||||
|
ifeq ($(HOST_OS),$(filter $(HOST_OS),linux darwin))
|
||||||
|
saved_build_host := $(build_host)
|
||||||
|
build_host := true
|
||||||
include $(LOCAL_PATH)/Android.build.mk
|
include $(LOCAL_PATH)/Android.build.mk
|
||||||
|
build_host := $(saved_build_host)
|
||||||
|
endif
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
# Library of bionic customized gtest main function, with normal gtest output format,
|
# Library of bionic customized gtest main function, with normal gtest output format,
|
||||||
|
|
|
@ -26,15 +26,25 @@
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
#include <time.h>
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "BionicDeathTest.h" // For selftest.
|
#ifndef TEMP_FAILURE_RETRY
|
||||||
|
|
||||||
|
/* Used to retry syscalls that can return EINTR. */
|
||||||
|
#define TEMP_FAILURE_RETRY(exp) ({ \
|
||||||
|
__typeof__(exp) _rc; \
|
||||||
|
do { \
|
||||||
|
_rc = (exp); \
|
||||||
|
} while (_rc == -1 && errno == EINTR); \
|
||||||
|
_rc; })
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace testing {
|
namespace testing {
|
||||||
namespace internal {
|
namespace internal {
|
||||||
|
@ -221,10 +231,8 @@ void TestResultPrinter::OnTestPartResult(const testing::TestPartResult& result)
|
||||||
}
|
}
|
||||||
|
|
||||||
static int64_t NanoTime() {
|
static int64_t NanoTime() {
|
||||||
struct timespec t;
|
std::chrono::nanoseconds duration(std::chrono::steady_clock::now().time_since_epoch());
|
||||||
t.tv_sec = t.tv_nsec = 0;
|
return static_cast<int64_t>(duration.count());
|
||||||
clock_gettime(CLOCK_MONOTONIC, &t);
|
|
||||||
return static_cast<int64_t>(t.tv_sec) * 1000000000LL + t.tv_nsec;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool EnumerateTests(int argc, char** argv, std::vector<TestCase>& testcase_list) {
|
static bool EnumerateTests(int argc, char** argv, std::vector<TestCase>& testcase_list) {
|
||||||
|
@ -501,6 +509,43 @@ void OnTestIterationEndXmlPrint(const std::string& xml_output_filename,
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool sigint_flag;
|
||||||
|
static bool sigquit_flag;
|
||||||
|
|
||||||
|
static void signal_handler(int sig) {
|
||||||
|
if (sig == SIGINT) {
|
||||||
|
sigint_flag = true;
|
||||||
|
} else if (sig == SIGQUIT) {
|
||||||
|
sigquit_flag = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool RegisterSignalHandler() {
|
||||||
|
sigint_flag = false;
|
||||||
|
sigquit_flag = false;
|
||||||
|
sig_t ret = signal(SIGINT, signal_handler);
|
||||||
|
if (ret != SIG_ERR) {
|
||||||
|
ret = signal(SIGQUIT, signal_handler);
|
||||||
|
}
|
||||||
|
if (ret == SIG_ERR) {
|
||||||
|
perror("RegisterSignalHandler");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool UnregisterSignalHandler() {
|
||||||
|
sig_t ret = signal(SIGINT, SIG_DFL);
|
||||||
|
if (ret != SIG_ERR) {
|
||||||
|
ret = signal(SIGQUIT, SIG_DFL);
|
||||||
|
}
|
||||||
|
if (ret == SIG_ERR) {
|
||||||
|
perror("UnregisterSignalHandler");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
struct ChildProcInfo {
|
struct ChildProcInfo {
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
int64_t start_time_ns;
|
int64_t start_time_ns;
|
||||||
|
@ -530,8 +575,23 @@ static void ChildProcessFn(int argc, char** argv, const std::string& test_name)
|
||||||
exit(result);
|
exit(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(__APPLE__)
|
||||||
|
|
||||||
|
static int pipe2(int pipefd[2], int flags) {
|
||||||
|
int ret = pipe(pipefd);
|
||||||
|
if (ret != -1) {
|
||||||
|
ret = fcntl(pipefd[0], F_SETFL, flags);
|
||||||
|
}
|
||||||
|
if (ret != -1) {
|
||||||
|
ret = fcntl(pipefd[1], F_SETFL, flags);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
static ChildProcInfo RunChildProcess(const std::string& test_name, int testcase_id, int test_id,
|
static ChildProcInfo RunChildProcess(const std::string& test_name, int testcase_id, int test_id,
|
||||||
sigset_t sigmask, int argc, char** argv) {
|
int argc, char** argv) {
|
||||||
int pipefd[2];
|
int pipefd[2];
|
||||||
int ret = pipe2(pipefd, O_NONBLOCK);
|
int ret = pipe2(pipefd, O_NONBLOCK);
|
||||||
if (ret == -1) {
|
if (ret == -1) {
|
||||||
|
@ -550,8 +610,7 @@ static ChildProcInfo RunChildProcess(const std::string& test_name, int testcase_
|
||||||
dup2(pipefd[1], STDOUT_FILENO);
|
dup2(pipefd[1], STDOUT_FILENO);
|
||||||
dup2(pipefd[1], STDERR_FILENO);
|
dup2(pipefd[1], STDERR_FILENO);
|
||||||
|
|
||||||
if (sigprocmask(SIG_SETMASK, &sigmask, NULL) == -1) {
|
if (!UnregisterSignalHandler()) {
|
||||||
perror("sigprocmask SIG_SETMASK");
|
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
ChildProcessFn(argc, argv, test_name);
|
ChildProcessFn(argc, argv, test_name);
|
||||||
|
@ -572,42 +631,29 @@ static ChildProcInfo RunChildProcess(const std::string& test_name, int testcase_
|
||||||
|
|
||||||
static void HandleSignals(std::vector<TestCase>& testcase_list,
|
static void HandleSignals(std::vector<TestCase>& testcase_list,
|
||||||
std::vector<ChildProcInfo>& child_proc_list) {
|
std::vector<ChildProcInfo>& child_proc_list) {
|
||||||
sigset_t waiting_mask;
|
if (sigquit_flag) {
|
||||||
sigemptyset(&waiting_mask);
|
sigquit_flag = false;
|
||||||
sigaddset(&waiting_mask, SIGINT);
|
// Print current running tests.
|
||||||
sigaddset(&waiting_mask, SIGQUIT);
|
printf("List of current running tests:\n");
|
||||||
timespec timeout;
|
for (auto& child_proc : child_proc_list) {
|
||||||
timeout.tv_sec = timeout.tv_nsec = 0;
|
if (child_proc.pid != 0) {
|
||||||
while (true) {
|
std::string test_name = testcase_list[child_proc.testcase_id].GetTestName(child_proc.test_id);
|
||||||
int signo = TEMP_FAILURE_RETRY(sigtimedwait(&waiting_mask, NULL, &timeout));
|
int64_t current_time_ns = NanoTime();
|
||||||
if (signo == -1) {
|
int64_t run_time_ms = (current_time_ns - child_proc.start_time_ns) / 1000000;
|
||||||
if (errno == EAGAIN) {
|
printf(" %s (%" PRId64 " ms)\n", test_name.c_str(), run_time_ms);
|
||||||
return; // Timeout, no pending signals.
|
|
||||||
}
|
}
|
||||||
perror("sigtimedwait");
|
|
||||||
exit(1);
|
|
||||||
} else if (signo == SIGQUIT) {
|
|
||||||
// Print current running tests.
|
|
||||||
printf("List of current running tests:\n");
|
|
||||||
for (auto& child_proc : child_proc_list) {
|
|
||||||
if (child_proc.pid != 0) {
|
|
||||||
std::string test_name = testcase_list[child_proc.testcase_id].GetTestName(child_proc.test_id);
|
|
||||||
int64_t current_time_ns = NanoTime();
|
|
||||||
int64_t run_time_ms = (current_time_ns - child_proc.start_time_ns) / 1000000;
|
|
||||||
printf(" %s (%" PRId64 " ms)\n", test_name.c_str(), run_time_ms);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (signo == SIGINT) {
|
|
||||||
// Kill current running tests.
|
|
||||||
for (auto& child_proc : child_proc_list) {
|
|
||||||
if (child_proc.pid != 0) {
|
|
||||||
// Send SIGKILL to ensure the child process can be killed unconditionally.
|
|
||||||
kill(child_proc.pid, SIGKILL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// SIGINT kills the parent process as well.
|
|
||||||
exit(1);
|
|
||||||
}
|
}
|
||||||
|
} else if (sigint_flag) {
|
||||||
|
sigint_flag = false;
|
||||||
|
// Kill current running tests.
|
||||||
|
for (auto& child_proc : child_proc_list) {
|
||||||
|
if (child_proc.pid != 0) {
|
||||||
|
// Send SIGKILL to ensure the child process can be killed unconditionally.
|
||||||
|
kill(child_proc.pid, SIGKILL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// SIGINT kills the parent process as well.
|
||||||
|
exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -750,13 +796,7 @@ static bool RunTestInSeparateProc(int argc, char** argv, std::vector<TestCase>&
|
||||||
testing::UnitTest::GetInstance()->listeners().default_result_printer());
|
testing::UnitTest::GetInstance()->listeners().default_result_printer());
|
||||||
testing::UnitTest::GetInstance()->listeners().Append(new TestResultPrinter);
|
testing::UnitTest::GetInstance()->listeners().Append(new TestResultPrinter);
|
||||||
|
|
||||||
// Signals are blocked here as we want to handle them in HandleSignals() later.
|
if (!RegisterSignalHandler()) {
|
||||||
sigset_t block_mask, orig_mask;
|
|
||||||
sigemptyset(&block_mask);
|
|
||||||
sigaddset(&block_mask, SIGINT);
|
|
||||||
sigaddset(&block_mask, SIGQUIT);
|
|
||||||
if (sigprocmask(SIG_BLOCK, &block_mask, &orig_mask) == -1) {
|
|
||||||
perror("sigprocmask SIG_BLOCK");
|
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -785,7 +825,7 @@ static bool RunTestInSeparateProc(int argc, char** argv, std::vector<TestCase>&
|
||||||
while (child_proc_list.size() < job_count && next_testcase_id < testcase_list.size()) {
|
while (child_proc_list.size() < job_count && next_testcase_id < testcase_list.size()) {
|
||||||
std::string test_name = testcase_list[next_testcase_id].GetTestName(next_test_id);
|
std::string test_name = testcase_list[next_testcase_id].GetTestName(next_test_id);
|
||||||
ChildProcInfo child_proc = RunChildProcess(test_name, next_testcase_id, next_test_id,
|
ChildProcInfo child_proc = RunChildProcess(test_name, next_testcase_id, next_test_id,
|
||||||
orig_mask, argc, argv);
|
argc, argv);
|
||||||
child_proc_list.push_back(child_proc);
|
child_proc_list.push_back(child_proc);
|
||||||
if (++next_test_id == testcase_list[next_testcase_id].TestCount()) {
|
if (++next_test_id == testcase_list[next_testcase_id].TestCount()) {
|
||||||
next_test_id = 0;
|
next_test_id = 0;
|
||||||
|
@ -830,9 +870,7 @@ static bool RunTestInSeparateProc(int argc, char** argv, std::vector<TestCase>&
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Restore signal mask.
|
if (!UnregisterSignalHandler()) {
|
||||||
if (sigprocmask(SIG_SETMASK, &orig_mask, NULL) == -1) {
|
|
||||||
perror("sigprocmask SIG_SETMASK");
|
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1103,7 +1141,12 @@ TEST(bionic_selftest, test_signal_SEGV_terminated) {
|
||||||
*p = 3;
|
*p = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
class bionic_selftest_DeathTest : public BionicDeathTest {};
|
class bionic_selftest_DeathTest : public ::testing::Test {
|
||||||
|
protected:
|
||||||
|
virtual void SetUp() {
|
||||||
|
::testing::FLAGS_gtest_death_test_style = "threadsafe";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
static void deathtest_helper_success() {
|
static void deathtest_helper_success() {
|
||||||
ASSERT_EQ(1, 1);
|
ASSERT_EQ(1, 1);
|
||||||
|
|
Loading…
Reference in New Issue