Skip to content

Commit

Permalink
Android: Refactor native test setup in a util class.
Browse files Browse the repository at this point in the history
This is also needed by content_browsertests.
Also redirecting stdout,stderr for content_browsertests to a fifo file to be read by the test runner scripts.
BUG=138275

TBR=jam@chromium.org

Review URL: https://chromiumcodereview.appspot.com/12213035

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@181587 0039d316-1c4b-4281-b951-d872f2087c98
  • Loading branch information
nileshagrawal@chromium.org committed Feb 9, 2013
1 parent c5e5790 commit 06078a2
Show file tree
Hide file tree
Showing 6 changed files with 179 additions and 136 deletions.
1 change: 1 addition & 0 deletions content/content_tests.gypi
Original file line number Diff line number Diff line change
Expand Up @@ -843,6 +843,7 @@
'dependencies': [
'content_shell_jni_headers',
'content_shell_lib',
'../testing/android/native_test.gyp:native_test_util',
],
}],
['OS=="mac"', {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,60 +22,23 @@
#include "content/public/app/android_library_loader_hooks.h"
#include "content/shell/android/shell_jni_registrar.h"
#include "jni/ContentBrowserTestsActivity_jni.h"
#include "testing/android/native_test_util.h"

using testing::native_test_util::ArgsToArgv;
using testing::native_test_util::CreateFIFO;
using testing::native_test_util::ParseArgsFromCommandLineFile;
using testing::native_test_util::RedirectStream;
using testing::native_test_util::ScopedMainEntryLogger;

// The main function of the program to be wrapped as an apk.
extern int main(int argc, char** argv);

namespace {

void ParseArgsFromString(const std::string& command_line,
std::vector<std::string>* args) {
base::StringTokenizer tokenizer(command_line, kWhitespaceASCII);
tokenizer.set_quote_chars("\"");
while (tokenizer.GetNext()) {
std::string token;
RemoveChars(tokenizer.token(), "\"", &token);
args->push_back(token);
}
}

void ParseArgsFromCommandLineFile(std::vector<std::string>* args) {
// The test runner script writes the command line file in
// "/data/local/tmp".
static const char kCommandLineFilePath[] =
"/data/local/tmp/content-browser-tests-command-line";
base::FilePath command_line(kCommandLineFilePath);
std::string command_line_string;
if (file_util::ReadFileToString(command_line, &command_line_string)) {
ParseArgsFromString(command_line_string, args);
}
}

int ArgsToArgv(const std::vector<std::string>& args,
std::vector<char*>* argv) {
// We need to pass in a non-const char**.
int argc = args.size();

argv->resize(argc + 1);
for (int i = 0; i < argc; ++i)
(*argv)[i] = const_cast<char*>(args[i].c_str());
(*argv)[argc] = NULL; // argv must be NULL terminated.

return argc;
}

class ScopedMainEntryLogger {
public:
ScopedMainEntryLogger() {
printf(">>ScopedMainEntryLogger\n");
}

~ScopedMainEntryLogger() {
printf("<<ScopedMainEntryLogger\n");
fflush(stdout);
fflush(stderr);
}
};
// The test runner script writes the command line file in
// "/data/local/tmp".
static const char kCommandLineFilePath[] =
"/data/local/tmp/content-browser-tests-command-line";

} // namespace

Expand All @@ -95,16 +58,22 @@ static void RunTests(JNIEnv* env,
base::android::RegisterJni(env);

std::vector<std::string> args;
ParseArgsFromCommandLineFile(&args);
ParseArgsFromCommandLineFile(kCommandLineFilePath, &args);

// We need to pass in a non-const char**.
std::vector<char*> argv;
int argc = ArgsToArgv(args, &argv);

// Fully initialize command line with arguments.
CommandLine::ForCurrentProcess()->AppendArguments(
CommandLine(argc, &argv[0]), false);

// Create fifo and redirect stdout and stderr to it.
FilePath files_dir(base::android::ConvertJavaStringToUTF8(env, jfiles_dir));
FilePath fifo_path(files_dir.Append(FilePath("test.fifo")));
CreateFIFO(fifo_path.value().c_str());
RedirectStream(stdout, fifo_path.value().c_str(), "w");
dup2(STDOUT_FILENO, STDERR_FILENO);

ScopedMainEntryLogger scoped_main_entry_logger;
main(argc, &argv[0]);
}
Expand Down
12 changes: 12 additions & 0 deletions testing/android/native_test.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
'../../base/base.gyp:test_support_base',
'../gtest.gyp:gtest',
'native_test_jni_headers',
'native_test_util',
],
},
{
Expand All @@ -89,6 +90,17 @@
],
},
},
{
'target_name': 'native_test_util',
'type': 'static_library',
'sources': [
'native_test_util.cc',
'native_test_util.h',
],
'dependencies': [
'../../base/base.gyp:base',
],
},
],
}]
],
Expand Down
102 changes: 16 additions & 86 deletions testing/android/native_test_launcher.cc
Original file line number Diff line number Diff line change
Expand Up @@ -11,27 +11,27 @@

#include <android/log.h>
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>

#include "base/android/base_jni_registrar.h"
#include "base/android/jni_android.h"
#include "base/android/jni_string.h"
#include "base/android/locale_utils.h"
#include "base/android/path_utils.h"
#include "base/android/scoped_java_ref.h"
#include "base/at_exit.h"
#include "base/base_switches.h"
#include "base/command_line.h"
#include "base/file_path.h"
#include "base/file_util.h"
#include "base/logging.h"
#include "base/string_util.h"
#include "base/stringprintf.h"
#include "base/strings/string_tokenizer.h"
#include "gtest/gtest.h"
#include "testing/android/native_test_util.h"
#include "testing/jni/ChromeNativeTestActivity_jni.h"

using testing::native_test_util::ArgsToArgv;
using testing::native_test_util::CreateFIFO;
using testing::native_test_util::ParseArgsFromCommandLineFile;
using testing::native_test_util::RedirectStream;
using testing::native_test_util::ScopedMainEntryLogger;

// The main function of the program to be wrapped as a test apk.
extern int main(int argc, char** argv);

Expand All @@ -42,16 +42,14 @@ namespace {
const char kSeparateStderrFifo[] = "separate-stderr-fifo";
const char kCreateStdinFifo[] = "create-stdin-fifo";

// The test runner script writes the command line file in
// "/data/local/tmp".
static const char kCommandLineFilePath[] =
"/data/local/tmp/chrome-native-tests-command-line";

const char kLogTag[] = "chromium";
const char kCrashedMarker[] = "[ CRASHED ]\n";

void AndroidLogError(const char* format, ...) {
va_list args;
va_start(args, format);
__android_log_vprint(ANDROID_LOG_ERROR, kLogTag, format, args);
va_end(args);
}

// The list of signals which are considered to be crashes.
const int kExceptionSignals[] = {
SIGSEGV, SIGABRT, SIGFPE, SIGILL, SIGBUS, -1
Expand Down Expand Up @@ -80,73 +78,6 @@ void InstallHandlers() {
}
}

void ParseArgsFromString(const std::string& command_line,
std::vector<std::string>* args) {
base::StringTokenizer tokenizer(command_line, kWhitespaceASCII);
tokenizer.set_quote_chars("\"");
while (tokenizer.GetNext()) {
std::string token;
RemoveChars(tokenizer.token(), "\"", &token);
args->push_back(token);
}
}

void ParseArgsFromCommandLineFile(std::vector<std::string>* args) {
// The test runner script writes the command line file in
// "/data/local/tmp".
static const char kCommandLineFilePath[] =
"/data/local/tmp/chrome-native-tests-command-line";
FilePath command_line(kCommandLineFilePath);
std::string command_line_string;
if (file_util::ReadFileToString(command_line, &command_line_string)) {
ParseArgsFromString(command_line_string, args);
}
}

int ArgsToArgv(const std::vector<std::string>& args,
std::vector<char*>* argv) {
// We need to pass in a non-const char**.
int argc = args.size();

argv->resize(argc + 1);
for (int i = 0; i < argc; ++i)
(*argv)[i] = const_cast<char*>(args[i].c_str());
(*argv)[argc] = NULL; // argv must be NULL terminated.

return argc;
}

void CreateFIFO(const char* fifo_path) {
unlink(fifo_path);
// Default permissions for mkfifo is ignored, chmod is required.
if (mkfifo(fifo_path, 0666) || chmod(fifo_path, 0666)) {
AndroidLogError("Failed to create fifo %s: %s\n",
fifo_path, strerror(errno));
exit(EXIT_FAILURE);
}
}

void Redirect(FILE* stream, const char* path, const char* mode) {
if (!freopen(path, mode, stream)) {
AndroidLogError("Failed to redirect stream to file: %s: %s\n",
path, strerror(errno));
exit(EXIT_FAILURE);
}
}

class ScopedMainEntryLogger {
public:
ScopedMainEntryLogger() {
printf(">>ScopedMainEntryLogger\n");
}

~ScopedMainEntryLogger() {
printf("<<ScopedMainEntryLogger\n");
fflush(stdout);
fflush(stderr);
}
};

} // namespace

// This method is called on a separate java thread so that we won't trigger
Expand All @@ -168,9 +99,8 @@ static void RunTests(JNIEnv* env,
base::android::RegisterJni(env);

std::vector<std::string> args;
ParseArgsFromCommandLineFile(&args);
ParseArgsFromCommandLineFile(kCommandLineFilePath, &args);

// We need to pass in a non-const char**.
std::vector<char*> argv;
int argc = ArgsToArgv(args, &argv);

Expand Down Expand Up @@ -202,11 +132,11 @@ static void RunTests(JNIEnv* env,
}

// Only redirect the streams after all fifos have been created.
Redirect(stdout, fifo_path.value().c_str(), "w");
RedirectStream(stdout, fifo_path.value().c_str(), "w");
if (!stdin_fifo_path.empty())
Redirect(stdin, stdin_fifo_path.value().c_str(), "r");
RedirectStream(stdin, stdin_fifo_path.value().c_str(), "r");
if (!stderr_fifo_path.empty())
Redirect(stderr, stderr_fifo_path.value().c_str(), "w");
RedirectStream(stderr, stderr_fifo_path.value().c_str(), "w");
else
dup2(STDOUT_FILENO, STDERR_FILENO);

Expand Down
90 changes: 90 additions & 0 deletions testing/android/native_test_util.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "testing/android/native_test_util.h"

#include <android/log.h>
#include <stdarg.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

#include "base/command_line.h"
#include "base/file_path.h"
#include "base/file_util.h"
#include "base/string_util.h"
#include "base/strings/string_tokenizer.h"

namespace {

const char kLogTag[] = "chromium";

void AndroidLogError(const char* format, ...) {
va_list args;
va_start(args, format);
__android_log_vprint(ANDROID_LOG_ERROR, kLogTag, format, args);
va_end(args);
}

void ParseArgsFromString(const std::string& command_line,
std::vector<std::string>* args) {
base::StringTokenizer tokenizer(command_line, kWhitespaceASCII);
tokenizer.set_quote_chars("\"");
while (tokenizer.GetNext()) {
std::string token;
RemoveChars(tokenizer.token(), "\"", &token);
args->push_back(token);
}
}

} // namespace

namespace testing {
namespace native_test_util {

void CreateFIFO(const char* fifo_path) {
unlink(fifo_path);
// Default permissions for mkfifo is ignored, chmod is required.
if (mkfifo(fifo_path, 0666) || chmod(fifo_path, 0666)) {
AndroidLogError("Failed to create fifo %s: %s\n",
fifo_path, strerror(errno));
exit(EXIT_FAILURE);
}
}

void RedirectStream(
FILE* stream, const char* path, const char* mode) {
if (!freopen(path, mode, stream)) {
AndroidLogError("Failed to redirect stream to file: %s: %s\n",
path, strerror(errno));
exit(EXIT_FAILURE);
}
}

void ParseArgsFromCommandLineFile(
const char* path, std::vector<std::string>* args) {
base::FilePath command_line(path);
std::string command_line_string;
if (file_util::ReadFileToString(command_line, &command_line_string)) {
ParseArgsFromString(command_line_string, args);
}
}

int ArgsToArgv(const std::vector<std::string>& args,
std::vector<char*>* argv) {
// We need to pass in a non-const char**.
int argc = args.size();

argv->resize(argc + 1);
for (int i = 0; i < argc; ++i) {
(*argv)[i] = const_cast<char*>(args[i].c_str());
}
(*argv)[argc] = NULL; // argv must be NULL terminated.

return argc;
}

} // namespace native_test_util
} // namespace testing
Loading

0 comments on commit 06078a2

Please sign in to comment.