1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
|
// Copyright (c) 2012 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 <stdio.h>
#include "base/android/jni_android.h"
#include "base/android/jni_string.h"
#include "base/android/path_utils.h"
#include "base/at_exit.h"
#include "base/command_line.h"
#include "base/file_path.h"
#include "base/file_util.h"
#include "base/logging.h"
#include "base/stringprintf.h"
#include "base/string_tokenizer.h"
#include "base/string_util.h"
#include "base/test/test_suite.h"
#include "testing/android/jni/chrome_native_test_activity_jni.h"
#include "gtest/gtest.h"
// GTest's main function.
extern int main(int argc, char** argv);
namespace {
void ParseArgsFromString(const std::string& command_line,
std::vector<std::string>* args) {
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(const FilePath& internal_data_path,
std::vector<std::string>* args) {
static const char kCommandLineFile[] = "chrome-native-tests-command-line";
FilePath command_line(internal_data_path.Append(FilePath(kCommandLineFile)));
std::string command_line_string;
if (file_util::ReadFileToString(command_line, &command_line_string)) {
ParseArgsFromString(command_line_string, args);
}
}
void 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);
for (int i = 0; i < argc; ++i)
(*argv)[i] = const_cast<char*>(args[i].c_str());
}
// As we are the native side of an Android app, we don't have any 'console', so
// gtest's standard output goes nowhere.
// Instead, we inject an "EventListener" in gtest and then we print the results
// using LOG, which goes to adb logcat.
class AndroidLogPrinter : public ::testing::EmptyTestEventListener {
public:
void Init(int* argc, char** argv);
// EmptyTestEventListener
virtual void OnTestProgramStart(
const ::testing::UnitTest& unit_test) OVERRIDE;
virtual void OnTestStart(const ::testing::TestInfo& test_info) OVERRIDE;
virtual void OnTestPartResult(
const ::testing::TestPartResult& test_part_result) OVERRIDE;
virtual void OnTestEnd(const ::testing::TestInfo& test_info) OVERRIDE;
virtual void OnTestProgramEnd(const ::testing::UnitTest& unit_test) OVERRIDE;
};
void AndroidLogPrinter::Init(int* argc, char** argv) {
// InitGoogleTest must be called befure we add ourselves as a listener.
::testing::InitGoogleTest(argc, argv);
::testing::TestEventListeners& listeners =
::testing::UnitTest::GetInstance()->listeners();
// Adds a listener to the end. Google Test takes the ownership.
listeners.Append(this);
}
void AndroidLogPrinter::OnTestProgramStart(
const ::testing::UnitTest& unit_test) {
std::string msg = StringPrintf("[ START ] %d",
unit_test.test_to_run_count());
LOG(ERROR) << msg;
}
void AndroidLogPrinter::OnTestStart(const ::testing::TestInfo& test_info) {
std::string msg = StringPrintf("[ RUN ] %s.%s",
test_info.test_case_name(), test_info.name());
LOG(ERROR) << msg;
}
void AndroidLogPrinter::OnTestPartResult(
const ::testing::TestPartResult& test_part_result) {
std::string msg = StringPrintf(
"%s in %s:%d\n%s\n",
test_part_result.failed() ? "*** Failure" : "Success",
test_part_result.file_name(),
test_part_result.line_number(),
test_part_result.summary());
LOG(ERROR) << msg;
}
void AndroidLogPrinter::OnTestEnd(const ::testing::TestInfo& test_info) {
std::string msg = StringPrintf("%s %s.%s",
test_info.result()->Failed() ? "[ FAILED ]" : "[ OK ]",
test_info.test_case_name(), test_info.name());
LOG(ERROR) << msg;
}
void AndroidLogPrinter::OnTestProgramEnd(
const ::testing::UnitTest& unit_test) {
std::string msg = StringPrintf("[ END ] %d",
unit_test.successful_test_count());
LOG(ERROR) << msg;
}
void LibraryLoadedOnMainThread(JNIEnv* env) {
static const char* const kInitialArgv[] = { "ChromeTestActivity" };
{
// We need a test suite to be created before we do any tracing or
// logging: it creates a global at_exit_manager and initializes
// internal gtest data structures based on the command line.
// It needs to be scoped as it also resets the CommandLine.
std::vector<std::string> args;
FilePath path("/data/user/0/org.chromium.native_test/files/");
ParseArgsFromCommandLineFile(path, &args);
std::vector<char*> argv;
ArgsToArgv(args, &argv);
base::TestSuite test_suite(argv.size(), &argv[0]);
}
CommandLine::Init(arraysize(kInitialArgv), kInitialArgv);
logging::InitLogging(NULL,
logging::LOG_ONLY_TO_SYSTEM_DEBUG_LOG,
logging::DONT_LOCK_LOG_FILE,
logging::DELETE_OLD_LOG_FILE,
logging::ENABLE_DCHECK_FOR_NON_OFFICIAL_RELEASE_BUILDS);
// To view log output with IDs and timestamps use "adb logcat -v threadtime".
logging::SetLogItems(false, // Process ID
false, // Thread ID
false, // Timestamp
false); // Tick count
VLOG(0) << "Chromium logging enabled: level = " << logging::GetMinLogLevel()
<< ", default verbosity = " << logging::GetVlogVerbosity();
base::android::RegisterPathUtils(env);
}
} // namespace
// This method is called on a separate java thread so that we won't trigger
// an ANR.
static void RunTests(JNIEnv* env, jobject obj, jstring jfiles_dir) {
FilePath files_dir(base::android::ConvertJavaStringToUTF8(env, jfiles_dir));
// A few options, such "--gtest_list_tests", will just use printf directly
// and won't use the "AndroidLogPrinter". Redirect stdout to a known file.
FilePath stdout_path(files_dir.Append(FilePath("stdout.txt")));
freopen(stdout_path.value().c_str(), "w", stdout);
std::vector<std::string> args;
ParseArgsFromCommandLineFile(files_dir, &args);
// We need to pass in a non-const char**.
std::vector<char*> argv;
ArgsToArgv(args, &argv);
int argc = argv.size();
// This object is owned by gtest.
AndroidLogPrinter* log = new AndroidLogPrinter();
log->Init(&argc, &argv[0]);
main(argc, &argv[0]);
}
// This is called by the VM when the shared library is first loaded.
JNI_EXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
base::android::InitVM(vm);
JNIEnv* env = base::android::AttachCurrentThread();
if (!RegisterNativesImpl(env)) {
return -1;
}
LibraryLoadedOnMainThread(env);
return JNI_VERSION_1_4;
}
|