Home | History | Annotate | Download | only in android
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 // This class sets up the environment for running the native tests inside an
      6 // android application. It outputs (to a fifo) markers identifying the
      7 // START/PASSED/CRASH of the test suite, FAILURE/SUCCESS of individual tests,
      8 // etc.
      9 // These markers are read by the test runner script to generate test results.
     10 // It installs signal handlers to detect crashes.
     11 
     12 #include <android/log.h>
     13 #include <signal.h>
     14 
     15 #include "base/android/base_jni_registrar.h"
     16 #include "base/android/fifo_utils.h"
     17 #include "base/android/jni_android.h"
     18 #include "base/android/jni_string.h"
     19 #include "base/android/scoped_java_ref.h"
     20 #include "base/at_exit.h"
     21 #include "base/base_switches.h"
     22 #include "base/command_line.h"
     23 #include "base/file_util.h"
     24 #include "base/files/file_path.h"
     25 #include "base/logging.h"
     26 #include "base/strings/stringprintf.h"
     27 #include "gtest/gtest.h"
     28 #include "testing/android/native_test_util.h"
     29 #include "testing/jni/ChromeNativeTestActivity_jni.h"
     30 
     31 using testing::native_test_util::ArgsToArgv;
     32 using testing::native_test_util::ParseArgsFromCommandLineFile;
     33 using testing::native_test_util::ScopedMainEntryLogger;
     34 
     35 // The main function of the program to be wrapped as a test apk.
     36 extern int main(int argc, char** argv);
     37 
     38 namespace {
     39 
     40 // These two command line flags are supported for DumpRenderTree, which needs
     41 // three fifos rather than a combined one: one for stderr, stdin and stdout.
     42 const char kSeparateStderrFifo[] = "separate-stderr-fifo";
     43 const char kCreateStdinFifo[] = "create-stdin-fifo";
     44 
     45 // The test runner script writes the command line file in
     46 // "/data/local/tmp".
     47 static const char kCommandLineFilePath[] =
     48     "/data/local/tmp/chrome-native-tests-command-line";
     49 
     50 const char kLogTag[] = "chromium";
     51 const char kCrashedMarker[] = "[ CRASHED      ]\n";
     52 
     53 // The list of signals which are considered to be crashes.
     54 const int kExceptionSignals[] = {
     55   SIGSEGV, SIGABRT, SIGFPE, SIGILL, SIGBUS, -1
     56 };
     57 
     58 struct sigaction g_old_sa[NSIG];
     59 
     60 // This function runs in a compromised context. It should not allocate memory.
     61 void SignalHandler(int sig, siginfo_t* info, void* reserved) {
     62   // Output the crash marker.
     63   write(STDOUT_FILENO, kCrashedMarker, sizeof(kCrashedMarker));
     64   g_old_sa[sig].sa_sigaction(sig, info, reserved);
     65 }
     66 
     67 // TODO(nileshagrawal): now that we're using FIFO, test scripts can detect EOF.
     68 // Remove the signal handlers.
     69 void InstallHandlers() {
     70   struct sigaction sa;
     71   memset(&sa, 0, sizeof(sa));
     72 
     73   sa.sa_sigaction = SignalHandler;
     74   sa.sa_flags = SA_SIGINFO;
     75 
     76   for (unsigned int i = 0; kExceptionSignals[i] != -1; ++i) {
     77     sigaction(kExceptionSignals[i], &sa, &g_old_sa[kExceptionSignals[i]]);
     78   }
     79 }
     80 
     81 // Writes printf() style string to Android's logger where |priority| is one of
     82 // the levels defined in <android/log.h>.
     83 void AndroidLog(int priority, const char* format, ...) {
     84   va_list args;
     85   va_start(args, format);
     86   __android_log_vprint(priority, kLogTag, format, args);
     87   va_end(args);
     88 }
     89 
     90 // Ensures that the fifo at |path| is created by deleting whatever is at |path|
     91 // prior to (re)creating the fifo, otherwise logs the error and terminates the
     92 // program.
     93 void EnsureCreateFIFO(const base::FilePath& path) {
     94   unlink(path.value().c_str());
     95   if (base::android::CreateFIFO(path, 0666))
     96     return;
     97 
     98   AndroidLog(ANDROID_LOG_ERROR, "Failed to create fifo %s: %s\n",
     99              path.value().c_str(), strerror(errno));
    100   exit(EXIT_FAILURE);
    101 }
    102 
    103 // Ensures that |stream| is redirected to |path|, otherwise logs the error and
    104 // terminates the program.
    105 void EnsureRedirectStream(FILE* stream,
    106                           const base::FilePath& path,
    107                           const char* mode) {
    108   if (base::android::RedirectStream(stream, path, mode))
    109     return;
    110 
    111   AndroidLog(ANDROID_LOG_ERROR, "Failed to redirect stream to file: %s: %s\n",
    112              path.value().c_str(), strerror(errno));
    113   exit(EXIT_FAILURE);
    114 }
    115 
    116 }  // namespace
    117 
    118 static void RunTests(JNIEnv* env,
    119                      jobject obj,
    120                      jstring jfiles_dir,
    121                      jobject app_context) {
    122   base::AtExitManager exit_manager;
    123 
    124   // Command line initialized basically, will be fully initialized later.
    125   static const char* const kInitialArgv[] = { "ChromeTestActivity" };
    126   CommandLine::Init(arraysize(kInitialArgv), kInitialArgv);
    127 
    128   // Set the application context in base.
    129   base::android::ScopedJavaLocalRef<jobject> scoped_context(
    130       env, env->NewLocalRef(app_context));
    131   base::android::InitApplicationContext(env, scoped_context);
    132   base::android::RegisterJni(env);
    133 
    134   std::vector<std::string> args;
    135   ParseArgsFromCommandLineFile(kCommandLineFilePath, &args);
    136 
    137   std::vector<char*> argv;
    138   int argc = ArgsToArgv(args, &argv);
    139 
    140   // Fully initialize command line with arguments.
    141   CommandLine::ForCurrentProcess()->AppendArguments(
    142       CommandLine(argc, &argv[0]), false);
    143   const CommandLine& command_line = *CommandLine::ForCurrentProcess();
    144 
    145   base::FilePath files_dir(
    146       base::android::ConvertJavaStringToUTF8(env, jfiles_dir));
    147 
    148   // A few options, such "--gtest_list_tests", will just use printf directly
    149   // Always redirect stdout to a known file.
    150   base::FilePath fifo_path(files_dir.Append(base::FilePath("test.fifo")));
    151   EnsureCreateFIFO(fifo_path);
    152 
    153   base::FilePath stderr_fifo_path, stdin_fifo_path;
    154 
    155   // DumpRenderTree needs a separate fifo for the stderr output. For all
    156   // other tests, insert stderr content to the same fifo we use for stdout.
    157   if (command_line.HasSwitch(kSeparateStderrFifo)) {
    158     stderr_fifo_path = files_dir.Append(base::FilePath("stderr.fifo"));
    159     EnsureCreateFIFO(stderr_fifo_path);
    160   }
    161 
    162   // DumpRenderTree uses stdin to receive input about which test to run.
    163   if (command_line.HasSwitch(kCreateStdinFifo)) {
    164     stdin_fifo_path = files_dir.Append(base::FilePath("stdin.fifo"));
    165     EnsureCreateFIFO(stdin_fifo_path);
    166   }
    167 
    168   // Only redirect the streams after all fifos have been created.
    169   EnsureRedirectStream(stdout, fifo_path, "w");
    170   if (!stdin_fifo_path.empty())
    171     EnsureRedirectStream(stdin, stdin_fifo_path, "r");
    172   if (!stderr_fifo_path.empty())
    173     EnsureRedirectStream(stderr, stderr_fifo_path, "w");
    174   else
    175     dup2(STDOUT_FILENO, STDERR_FILENO);
    176 
    177   if (command_line.HasSwitch(switches::kWaitForDebugger)) {
    178     AndroidLog(ANDROID_LOG_VERBOSE,
    179                "Native test waiting for GDB because flag %s was supplied",
    180                switches::kWaitForDebugger);
    181     base::debug::WaitForDebugger(24 * 60 * 60, false);
    182   }
    183 
    184   ScopedMainEntryLogger scoped_main_entry_logger;
    185   main(argc, &argv[0]);
    186 }
    187 
    188 // This is called by the VM when the shared library is first loaded.
    189 JNI_EXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
    190   // Install signal handlers to detect crashes.
    191   InstallHandlers();
    192 
    193   base::android::InitVM(vm);
    194   JNIEnv* env = base::android::AttachCurrentThread();
    195   if (!RegisterNativesImpl(env)) {
    196     return -1;
    197   }
    198 
    199   return JNI_VERSION_1_4;
    200 }
    201