[android] Implement android test runner for core unit tests

This commit is contained in:
Alexander Shalamov
2018-10-18 17:30:19 +03:00
parent 0a398bd6fd
commit df63be71ce
8 changed files with 118 additions and 113 deletions

View File

@@ -1,15 +0,0 @@
public class Main {
public native void runAllTests(String[] args);
public static void main(String[] args) throws Exception {
// Load the tests
System.loadLibrary("mbgl-test");
// Run the tests
new Main().runAllTests(args);
// Exit explicitly otherwise dalvikvm won't quit
System.exit(0);
}
}

View File

@@ -1,76 +0,0 @@
#include "../jni.hpp"
#include <jni/jni.hpp>
#include <mbgl/util/logging.hpp>
#include <mbgl/test.hpp>
#include <vector>
namespace {
// Main class (entry point for tests from dalvikvm)
struct Main {
static constexpr auto Name() {
return "Main";
}
/**
* JNI Bound to Main#runAllTests()
*/
static void runAllTests(jni::JNIEnv& env, const jni::Object<Main>&, const jni::Array<jni::String>& args) {
mbgl::Log::Warning(mbgl::Event::JNI, "Starting tests");
// We need to create a copy of the argv data since Java-internals are stored in UTF-16.
std::vector<std::string> data;
// Add a fake first argument to align indices. Google Test expects the first argument to
// start at index 1; index 0 is the name of the executable.
data.push_back("main.jar");
const int argc = args.Length(env);
for (auto i = 0; i < argc; i++) {
data.emplace_back(jni::Make<std::string>(env, args.Get(env, i)));
}
// Create an array of char pointers that point back to the data array.
std::vector<const char*> argv;
for (const auto& arg : data) {
argv.push_back(arg.data());
}
mbgl::runTests(argv.size(), const_cast<char**>(argv.data()));
}
};
} // namespace
// We're declaring the function, which is libandroid_runtime.so.
// It is defined in AndroidRuntime.cpp:
// https://github.com/android/platform_frameworks_base/blob/master/core/jni/AndroidRuntime.cpp
// Once this symbol goes away, we'll have to revisit.
// This method loads and registers all of the Android-native frameworks so that we can use
// android.util.Log, android.graphics.Bitmap and so on.
// Setting the weak attribute tells the linker that this symbol is loaded at runtime and avoids
// a missing symbol error.
namespace android {
struct AndroidRuntime {
static int startReg(JNIEnv* env) __attribute__((weak));
};
} // namespace android
// Main entry point
extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *) {
// Load Android-native jni bindings
jni::JNIEnv& env = jni::GetEnv(*vm, jni::jni_version_1_6);
android::AndroidRuntime::startReg(&env);
// Load the main library jni bindings
mbgl::Log::Info(mbgl::Event::JNI, "Registering main JNI Methods");
mbgl::android::registerNatives(vm);
// Load the test library jni bindings
mbgl::Log::Info(mbgl::Event::JNI, "Registering test JNI Methods");
jni::RegisterNatives(env, *jni::Class<Main>::Find(env),
jni::MakeNativeMethod<decltype(Main::runAllTests), &Main::runAllTests>("runAllTests"));
return JNI_VERSION_1_6;
}

View File

@@ -0,0 +1,73 @@
#include "runtime.hpp"
#include "../jni.hpp"
#include <cassert>
#include <dlfcn.h>
#include <jni.h>
#include <signal.h>
#include <string>
// Required for art / libsigchain
extern "C" JNIEXPORT void EnsureFrontOfChain(int, struct sigaction*) { }
extern "C" JNIEXPORT void AddSpecialSignalHandlerFn(int, void*) { }
extern "C" JNIEXPORT void RemoveSpecialSignalHandlerFn(int, bool (*) (int, siginfo_t*, void*)) { }
namespace {
const std::string kClassPathCommand{"--class_path="};
const std::string kClassPath{"-Djava.class.path="};
const std::string kDefaultDex{"/data/local/tmp/core-tests/classes.dex"};
} // namespace
namespace mbgl {
namespace android {
bool initRuntime(int argc, char *argv[]) {
void* vmHandle = dlopen("libart.so", RTLD_NOW);
assert(vmHandle != nullptr);
using CreateJavaVMFn = jint (*)(JavaVM** vm, JNIEnv** env, void* vmArgs);
CreateJavaVMFn createJavaVMFn = reinterpret_cast<CreateJavaVMFn>(dlsym(vmHandle, "JNI_CreateJavaVM"));
assert(createJavaVMFn != nullptr);
std::string classPath = kClassPath + kDefaultDex;
for (int i = 0; i < argc; ++i) {
const std::string arg{argv[i]};
if (arg.compare(0, kClassPathCommand.length(), kClassPathCommand) == 0) {
classPath = kClassPath + arg.substr(kClassPathCommand.length());
break;
}
}
JavaVMOption options[1];
options[0].optionString = classPath.c_str();
JavaVMInitArgs args;
args.version = JNI_VERSION_1_6;
args.nOptions = 1;
args.options = options;
args.ignoreUnrecognized = JNI_FALSE;
JavaVM* vm;
JNIEnv* env;
if (createJavaVMFn(&vm, &env, &args) != JNI_OK) {
return false;
}
void* runtimeHandle = dlopen("libandroid_runtime.so", RTLD_NOW);
assert(runtimeHandle != nullptr);
using RegisterNativesFn = jint (*)(JNIEnv* env);
RegisterNativesFn registerNativesFn = reinterpret_cast<RegisterNativesFn>(dlsym(runtimeHandle, "registerFrameworkNatives"));
assert(registerNativesFn != nullptr);
if (registerNativesFn(env) != JNI_OK) {
return false;
}
mbgl::android::registerNatives(vm);
return true;
}
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,9 @@
#pragma once
namespace mbgl {
namespace android {
bool initRuntime(int argc, char *argv[]);
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,10 @@
#include "runtime.hpp"
#include <mbgl/test.hpp>
int main(int argc, char *argv[]) {
if (!mbgl::android::initRuntime(argc, argv)) {
return 1;
}
mbgl::runTests(argc, argv);
}

View File

@@ -0,0 +1,8 @@
{
global:
EnsureFrontOfChain;
AddSpecialSignalHandlerFn;
RemoveSpecialSignalHandlerFn;
local:
*;
};