Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / sandbox / linux / tests / unit_tests.cc
index 02996b7..b7c3af6 100644 (file)
@@ -8,9 +8,12 @@
 #include <stdio.h>
 #include <sys/resource.h>
 #include <sys/time.h>
+#include <time.h>
 #include <unistd.h>
 
-#include "base/file_util.h"
+#include "base/debug/leak_annotations.h"
+#include "base/files/file_util.h"
+#include "base/posix/eintr_wrapper.h"
 #include "base/third_party/valgrind/valgrind.h"
 #include "build/build_config.h"
 #include "sandbox/linux/tests/unit_tests.h"
@@ -59,23 +62,26 @@ bool IsArchitectureArm() {
 
 // TODO(jln): figure out why base/.../dynamic_annotations.h's
 // RunningOnValgrind() cannot link.
-bool IsRunningOnValgrind() {
-  return RUNNING_ON_VALGRIND;
-}
+bool IsRunningOnValgrind() { return RUNNING_ON_VALGRIND; }
 
 static const int kExpectedValue = 42;
 static const int kIgnoreThisTest = 43;
 static const int kExitWithAssertionFailure = 1;
 static const int kExitForTimeout = 2;
 
+#if !defined(OS_ANDROID)
+// This is due to StackDumpSignalHandler() performing _exit(1).
+// TODO(jln): get rid of the collision with kExitWithAssertionFailure.
+const int kExitAfterSIGSEGV = 1;
+#endif
+
 static void SigAlrmHandler(int) {
-    const char failure_message[] = "Timeout reached!\n";
-    // Make sure that we never block here.
-    if (!fcntl(2, F_SETFL,  O_NONBLOCK)) {
-      if (write(2, failure_message, sizeof(failure_message) - 1) < 0) {
-      }
-    }
-    _exit(kExitForTimeout);
+  const char failure_message[] = "Timeout reached!\n";
+  // Make sure that we never block here.
+  if (!fcntl(2, F_SETFL, O_NONBLOCK)) {
+    ignore_result(write(2, failure_message, sizeof(failure_message) - 1));
+  }
+  _exit(kExitForTimeout);
 }
 
 // Set a timeout with a handler that will automatically fail the
@@ -105,22 +111,40 @@ static void SetProcessTimeout(int time_in_seconds) {
 // in the BPF sandbox, as it potentially makes global state changes and as
 // it also tends to raise fatal errors, if the code has been used in an
 // insecure manner.
-void UnitTests::RunTestInProcess(UnitTests::Test test, void *arg,
-                                 DeathCheck death, const void *death_aux) {
+void UnitTests::RunTestInProcess(SandboxTestRunner* test_runner,
+                                 DeathCheck death,
+                                 const void* death_aux) {
+  CHECK(test_runner);
   // We need to fork(), so we can't be multi-threaded, as threads could hold
   // locks.
   int num_threads = CountThreads();
-#if defined(THREAD_SANITIZER)
+#if !defined(THREAD_SANITIZER)
+  const int kNumExpectedThreads = 1;
+#else
   // Under TSAN, there is a special helper thread. It should be completely
   // invisible to our testing, so we ignore it. It should be ok to fork()
   // with this thread. It's currently buggy, but it's the best we can do until
   // there is a way to delay the start of the thread
   // (https://code.google.com/p/thread-sanitizer/issues/detail?id=19).
-  num_threads--;
+  const int kNumExpectedThreads = 2;
 #endif
-  ASSERT_EQ(1, num_threads) << "Running sandbox tests with multiple threads "
-                            << "is not supported and will make the tests "
-                            << "flaky.\n";
+
+  // The kernel is at liberty to wake a thread id futex before updating /proc.
+  // If another test running in the same process has stopped a thread, it may
+  // appear as still running in /proc.
+  // We poll /proc, with an exponential back-off. At most, we'll sleep around
+  // 2^iterations nanoseconds in nanosleep().
+  for (unsigned int iteration = 0; iteration < 30; iteration++) {
+    struct timespec ts = {0, 1L << iteration /* nanoseconds */};
+    PCHECK(0 == HANDLE_EINTR(nanosleep(&ts, &ts)));
+    num_threads = CountThreads();
+    if (kNumExpectedThreads == num_threads)
+      break;
+  }
+
+  ASSERT_EQ(kNumExpectedThreads, num_threads)
+      << "Running sandbox tests with multiple threads "
+      << "is not supported and will make the tests flaky.";
   int fds[2];
   ASSERT_EQ(0, pipe(fds));
   // Check that our pipe is not on one of the standard file descriptor.
@@ -144,22 +168,27 @@ void UnitTests::RunTestInProcess(UnitTests::Test test, void *arg,
 
     // Disable core files. They are not very useful for our individual test
     // cases.
-    struct rlimit no_core = { 0 };
+    struct rlimit no_core = {0};
     setrlimit(RLIMIT_CORE, &no_core);
 
-    test(arg);
+    test_runner->Run();
+    if (test_runner->ShouldCheckForLeaks()) {
+#if defined(LEAK_SANITIZER)
+      __lsan_do_leak_check();
+#endif
+    }
     _exit(kExpectedValue);
   }
 
-  (void)HANDLE_EINTR(close(fds[1]));
+  close(fds[1]);
   std::vector<char> msg_buf;
   ssize_t rc;
 
   // Make sure read() will never block as we'll use poll() to
   // block with a timeout instead.
-  const int fcntl_ret = fcntl(fds[0], F_SETFL,  O_NONBLOCK);
-  ASSERT_EQ(fcntl_ret, 0);
-  struct pollfd poll_fd = { fds[0], POLLIN | POLLRDHUP, 0 };
+  const int fcntl_ret = fcntl(fds[0], F_SETFL, O_NONBLOCK);
+  ASSERT_EQ(0, fcntl_ret);
+  struct pollfd poll_fd = {fds[0], POLLIN | POLLRDHUP, 0};
 
   int poll_ret;
   // We prefer the SIGALRM timeout to trigger in the child than this timeout
@@ -176,7 +205,7 @@ void UnitTests::RunTestInProcess(UnitTests::Test test, void *arg,
   }
   ASSERT_NE(poll_ret, -1) << "poll() failed";
   ASSERT_NE(poll_ret, 0) << "Timeout while reading child state";
-  (void)HANDLE_EINTR(close(fds[0]));
+  close(fds[0]);
   std::string msg(msg_buf.begin(), msg_buf.end());
 
   int status = 0;
@@ -198,8 +227,7 @@ void UnitTests::RunTestInProcess(UnitTests::Test test, void *arg,
   }
 }
 
-void UnitTests::DeathSuccess(int status, const std::string& msg,
-                             const void *) {
+void UnitTests::DeathSuccess(int status, const std::string& msg, const void*) {
   std::string details(TestFailedMessage(msg));
 
   bool subprocess_terminated_normally = WIFEXITED(status);
@@ -210,44 +238,81 @@ void UnitTests::DeathSuccess(int status, const std::string& msg,
   EXPECT_FALSE(subprocess_exited_but_printed_messages) << details;
 }
 
-void UnitTests::DeathMessage(int status, const std::string& msg,
-                             const void *aux) {
+void UnitTests::DeathSuccessAllowNoise(int status,
+                                       const std::string& msg,
+                                       const void*) {
   std::string details(TestFailedMessage(msg));
-  const char *expected_msg = static_cast<const char *>(aux);
 
   bool subprocess_terminated_normally = WIFEXITED(status);
   ASSERT_TRUE(subprocess_terminated_normally) << details;
   int subprocess_exit_status = WEXITSTATUS(status);
-  ASSERT_EQ(kExitWithAssertionFailure, subprocess_exit_status) << details;
+  ASSERT_EQ(kExpectedValue, subprocess_exit_status) << details;
+}
+
+void UnitTests::DeathMessage(int status,
+                             const std::string& msg,
+                             const void* aux) {
+  std::string details(TestFailedMessage(msg));
+  const char* expected_msg = static_cast<const char*>(aux);
+
+  bool subprocess_terminated_normally = WIFEXITED(status);
+  ASSERT_TRUE(subprocess_terminated_normally) << "Exit status: " << status
+                                              << " " << details;
+  int subprocess_exit_status = WEXITSTATUS(status);
+  ASSERT_EQ(1, subprocess_exit_status) << details;
+
+  bool subprocess_exited_without_matching_message =
+      msg.find(expected_msg) == std::string::npos;
+  EXPECT_FALSE(subprocess_exited_without_matching_message) << details;
+}
+
+void UnitTests::DeathSEGVMessage(int status,
+                                 const std::string& msg,
+                                 const void* aux) {
+  std::string details(TestFailedMessage(msg));
+  const char* expected_msg = static_cast<const char*>(aux);
+
+#if defined(OS_ANDROID)
+  const bool subprocess_got_sigsegv =
+      WIFSIGNALED(status) && (SIGSEGV == WTERMSIG(status));
+#else
+  const bool subprocess_got_sigsegv =
+      WIFEXITED(status) && (kExitAfterSIGSEGV == WEXITSTATUS(status));
+#endif
+
+  ASSERT_TRUE(subprocess_got_sigsegv) << "Exit status: " << status
+                                      << " " << details;
+
   bool subprocess_exited_without_matching_message =
-    msg.find(expected_msg) == std::string::npos;
+      msg.find(expected_msg) == std::string::npos;
   EXPECT_FALSE(subprocess_exited_without_matching_message) << details;
 }
 
-void UnitTests::DeathExitCode(int status, const std::string& msg,
-                              const void *aux) {
+void UnitTests::DeathExitCode(int status,
+                              const std::string& msg,
+                              const void* aux) {
   int expected_exit_code = static_cast<int>(reinterpret_cast<intptr_t>(aux));
   std::string details(TestFailedMessage(msg));
 
   bool subprocess_terminated_normally = WIFEXITED(status);
   ASSERT_TRUE(subprocess_terminated_normally) << details;
   int subprocess_exit_status = WEXITSTATUS(status);
-  ASSERT_EQ(subprocess_exit_status, expected_exit_code) << details;
+  ASSERT_EQ(expected_exit_code, subprocess_exit_status) << details;
 }
 
-void UnitTests::DeathBySignal(int status, const std::string& msg,
-                              const void *aux) {
+void UnitTests::DeathBySignal(int status,
+                              const std::string& msg,
+                              const void* aux) {
   int expected_signo = static_cast<int>(reinterpret_cast<intptr_t>(aux));
   std::string details(TestFailedMessage(msg));
 
   bool subprocess_terminated_by_signal = WIFSIGNALED(status);
   ASSERT_TRUE(subprocess_terminated_by_signal) << details;
   int subprocess_signal_number = WTERMSIG(status);
-  ASSERT_EQ(subprocess_signal_number, expected_signo) << details;
+  ASSERT_EQ(expected_signo, subprocess_signal_number) << details;
 }
 
-void UnitTests::AssertionFailure(const char *expr, const char *file,
-                                 int line) {
+void UnitTests::AssertionFailure(const char* expr, const char* file, int line) {
   fprintf(stderr, "%s:%d:%s", file, line, expr);
   fflush(stderr);
   _exit(kExitWithAssertionFailure);