[android] Implement android test runner for core unit tests
This commit is contained in:
22
Makefile
22
Makefile
@@ -500,7 +500,7 @@ MBGL_ANDROID_ABIS += x86-64;x86_64
|
||||
MBGL_ANDROID_LOCAL_WORK_DIR = /data/local/tmp/core-tests
|
||||
MBGL_ANDROID_LIBDIR = lib$(if $(filter arm-v8 x86-64,$1),64)
|
||||
MBGL_ANDROID_DALVIKVM = dalvikvm$(if $(filter arm-v8 x86-64,$1),64,32)
|
||||
MBGL_ANDROID_APK_SUFFIX = $(if $(filter Release,$(BUILDTYPE)),release-unsigned,debug)
|
||||
MBGL_ANDROID_APK_SUFFIX = $(if $(filter Release,$(BUILDTYPE)),release,debug)
|
||||
MBGL_ANDROID_CORE_TEST_DIR = platform/android/MapboxGLAndroidSDK/.externalNativeBuild/cmake/$(buildtype)/$2/core-tests
|
||||
MBGL_ANDROID_STL ?= c++_static
|
||||
MBGL_ANDROID_GRADLE = ./gradlew --parallel --max-workers=$(JOBS) -Pmapbox.buildtype=$(buildtype) -Pmapbox.stl=$(MBGL_ANDROID_STL)
|
||||
@@ -542,27 +542,21 @@ android-$1: platform/android/gradle/configuration.gradle
|
||||
android-core-test-$1: android-test-lib-$1
|
||||
# Compile main sources and extract the classes (using the test app to get all transitive dependencies in one place)
|
||||
mkdir -p $(MBGL_ANDROID_CORE_TEST_DIR)
|
||||
unzip -o platform/android/MapboxGLAndroidSDKTestApp/build/outputs/apk/MapboxGLAndroidSDKTestApp-$(MBGL_ANDROID_APK_SUFFIX).apk classes.dex -d $(MBGL_ANDROID_CORE_TEST_DIR)
|
||||
|
||||
# Compile Test runner
|
||||
find platform/android/src/test -name "*.java" > $(MBGL_ANDROID_CORE_TEST_DIR)/java-sources.txt
|
||||
javac -sourcepath platform/android/src/test -d $(MBGL_ANDROID_CORE_TEST_DIR) -source 1.7 -target 1.7 @$(MBGL_ANDROID_CORE_TEST_DIR)/java-sources.txt
|
||||
|
||||
# Combine and dex
|
||||
cd $(MBGL_ANDROID_CORE_TEST_DIR) && $(ANDROID_HOME)/build-tools/25.0.0/dx --dex --output=test.jar *.class classes.dex
|
||||
unzip -o platform/android/MapboxGLAndroidSDKTestApp/build/outputs/apk/$(buildtype)/MapboxGLAndroidSDKTestApp-$(MBGL_ANDROID_APK_SUFFIX).apk classes.dex -d $(MBGL_ANDROID_CORE_TEST_DIR)
|
||||
|
||||
run-android-core-test-$1-%: android-core-test-$1
|
||||
# Ensure clean state on the device
|
||||
adb shell "rm -Rf $(MBGL_ANDROID_LOCAL_WORK_DIR) && mkdir -p $(MBGL_ANDROID_LOCAL_WORK_DIR)/test"
|
||||
adb shell "rm -Rf $(MBGL_ANDROID_LOCAL_WORK_DIR) && mkdir -p $(MBGL_ANDROID_LOCAL_WORK_DIR)/test && mkdir -p $(MBGL_ANDROID_LOCAL_WORK_DIR)/mapbox-gl-js/src/style-spec/reference"
|
||||
|
||||
# Push all needed files to the device
|
||||
adb push $(MBGL_ANDROID_CORE_TEST_DIR)/test.jar $(MBGL_ANDROID_LOCAL_WORK_DIR) > /dev/null 2>&1
|
||||
adb push $(MBGL_ANDROID_CORE_TEST_DIR)/classes.dex $(MBGL_ANDROID_LOCAL_WORK_DIR) > /dev/null 2>&1
|
||||
adb push platform/android/MapboxGLAndroidSDK/build/intermediates/intermediate-jars/$(buildtype)/jni/$2/libmapbox-gl.so $(MBGL_ANDROID_LOCAL_WORK_DIR) > /dev/null 2>&1
|
||||
adb push test/fixtures $(MBGL_ANDROID_LOCAL_WORK_DIR)/test > /dev/null 2>&1
|
||||
adb push platform/android/MapboxGLAndroidSDK/build/intermediates/bundles/default/jni/$2/libmapbox-gl.so $(MBGL_ANDROID_LOCAL_WORK_DIR) > /dev/null 2>&1
|
||||
adb push platform/android/MapboxGLAndroidSDK/build/intermediates/bundles/default/jni/$2/libmbgl-test.so $(MBGL_ANDROID_LOCAL_WORK_DIR) > /dev/null 2>&1
|
||||
adb push mapbox-gl-js/src/style-spec/reference/v8.json $(MBGL_ANDROID_LOCAL_WORK_DIR)/mapbox-gl-js/src/style-spec/reference > /dev/null 2>&1
|
||||
adb push platform/android/MapboxGLAndroidSDK/build/intermediates/cmake/$(buildtype)/obj/$2/mbgl-test $(MBGL_ANDROID_LOCAL_WORK_DIR) > /dev/null 2>&1
|
||||
|
||||
# Kick off the tests
|
||||
adb shell "export LD_LIBRARY_PATH=/system/$(MBGL_ANDROID_LIBDIR):$(MBGL_ANDROID_LOCAL_WORK_DIR) && cd $(MBGL_ANDROID_LOCAL_WORK_DIR) && $(MBGL_ANDROID_DALVIKVM) -cp $(MBGL_ANDROID_LOCAL_WORK_DIR)/test.jar Main --gtest_filter=$$*"
|
||||
adb shell "export LD_LIBRARY_PATH=$(MBGL_ANDROID_LOCAL_WORK_DIR) && cd $(MBGL_ANDROID_LOCAL_WORK_DIR) && chmod +x mbgl-test && ./mbgl-test --class_path=$(MBGL_ANDROID_LOCAL_WORK_DIR)/classes.dex --gtest_filter=$$*"
|
||||
|
||||
# Gather the results and unpack them
|
||||
adb shell "cd $(MBGL_ANDROID_LOCAL_WORK_DIR) && tar -cvzf results.tgz test/fixtures/* > /dev/null 2>&1"
|
||||
|
||||
@@ -85,23 +85,25 @@ target_link_libraries(mapbox-gl
|
||||
PRIVATE mbgl-filesource
|
||||
)
|
||||
|
||||
## Test library ##
|
||||
|
||||
set(MBGL_TEST_TARGET_TYPE "library")
|
||||
## Test executable ##
|
||||
macro(mbgl_platform_test)
|
||||
target_sources(mbgl-test
|
||||
PRIVATE platform/default/src/mbgl/test/main.cpp
|
||||
|
||||
# Main test entry point
|
||||
platform/android/src/test/main.jni.cpp
|
||||
PRIVATE platform/android/src/test/test_runner.cpp
|
||||
PRIVATE platform/android/src/test/runtime.cpp
|
||||
)
|
||||
|
||||
target_include_directories(mbgl-test
|
||||
PRIVATE platform/android/include
|
||||
)
|
||||
|
||||
set_target_properties(mbgl-test
|
||||
PROPERTIES
|
||||
LINK_FLAGS
|
||||
"-fPIE -pie \
|
||||
-Wl,--export-dynamic \
|
||||
-Wl,--version-script=${CMAKE_SOURCE_DIR}/platform/android/src/test/version-script")
|
||||
|
||||
target_link_libraries(mbgl-test
|
||||
PRIVATE mbgl-core
|
||||
PRIVATE mbgl-filesource
|
||||
)
|
||||
endmacro()
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
73
platform/android/src/test/runtime.cpp
Normal file
73
platform/android/src/test/runtime.cpp
Normal 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
|
||||
9
platform/android/src/test/runtime.hpp
Normal file
9
platform/android/src/test/runtime.hpp
Normal file
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
namespace mbgl {
|
||||
namespace android {
|
||||
|
||||
bool initRuntime(int argc, char *argv[]);
|
||||
|
||||
} // namespace android
|
||||
} // namespace mbgl
|
||||
10
platform/android/src/test/test_runner.cpp
Normal file
10
platform/android/src/test/test_runner.cpp
Normal 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);
|
||||
}
|
||||
8
platform/android/src/test/version-script
Normal file
8
platform/android/src/test/version-script
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
global:
|
||||
EnsureFrontOfChain;
|
||||
AddSpecialSignalHandlerFn;
|
||||
RemoveSpecialSignalHandlerFn;
|
||||
local:
|
||||
*;
|
||||
};
|
||||
Reference in New Issue
Block a user