support/testing: openjdk JNI test cases

This test case builds a native library and ensures a Java class can load
and interact with the native library. The test also verifies Java code
can make system calls via the native library.

Signed-off-by: Daniel J. Leach <dleach@belcan.com>
Acked-by: Matthew Weber <matthew.weber@rockwellcollins.com>
Tested-by: Adam Duskett <aduskett@gmail.com>
Signed-off-by: Arnout Vandecappelle (Essensium/Mind) <arnout@mind.be>
This commit is contained in:
Daniel J. Leach 2019-04-25 14:40:45 -05:00 committed by Arnout Vandecappelle (Essensium/Mind)
parent 7e99d1de50
commit 6aee78c894
12 changed files with 371 additions and 0 deletions

View File

@ -1 +1,2 @@
source "$BR2_EXTERNAL_OPENJDK_PATH/package/openjdk-hello-world/Config.in"
source "$BR2_EXTERNAL_OPENJDK_PATH/package/openjdk-jni-test/Config.in"

View File

@ -0,0 +1,5 @@
config BR2_PACKAGE_OPENJDK_JNI_TEST
bool "openjdk JNI test"
depends on BR2_PACKAGE_OPENJDK
help
Tests openjdk JNI support

View File

@ -0,0 +1,9 @@
public class JniHelper
{
public void HelloManagedWorld()
{
stringMember = "Hello, Managed World";
}
public String stringMember = "Set from Java";
}

View File

@ -0,0 +1,92 @@
public class JniTest
{
private static void Test(
String name,
Object actual,
Object expected,
String actualAsString,
String expectedAsString)
{
if (!actual.equals(expected))
{
System.out.println(String.format(
"Test: %s failed\nExpected: \"%s\", Actual: \"%s\"",
name,
expected,
actual));
JniTest.exitCode = -1;
}
else
{
System.out.println(String.format("Test: %s passed", name));
}
}
private static void Test(
String name,
String actual,
String expected)
{
JniTest.Test(name, actual, expected, actual, expected);
}
public static void main(String[] args)
{
var actualVersion = JniWrapper.get_jni_version();
var expectedVersion = 0x000A0000;
JniTest.Test(
"Get JNI Version",
actualVersion,
expectedVersion,
String.format("0x%08X", actualVersion),
String.format("0x%08X", expectedVersion));
JniTest.Test(
"Read Native String Constant",
JniWrapper.read_constant_string(),
"Hello from C");
JniTest.Test(
"Write Java String to Native Library",
JniWrapper.write_string("Hello from Java"),
"Hello from Java");
JniTest.Test(
"Write Java Char Array to Native Library",
JniWrapper.write_char_array("Hello from Java".toCharArray()),
"Hello from Java");
var helper = new JniHelper();
JniTest.Test(
"Write String Member to Native Library",
JniWrapper.write_string_member(helper),
"Set from Java");
JniWrapper.set_string_member(helper);
JniTest.Test(
"Set String Member from Native Library",
helper.stringMember,
"Set from C");
JniWrapper.execute_java_function(helper);
JniTest.Test(
"Execeute Java Function from Native Library",
helper.stringMember,
"Hello, Managed World");
helper = JniWrapper.instantiate_java_class();
JniTest.Test(
"Instantiate Java Class",
helper.stringMember,
"Instantiated from C");
JniTest.Test(
"Call Native Library to Set System Time",
JniWrapper.set_and_write_time_in_seconds(1000),
"1000");
System.exit(exitCode);
}
public static int exitCode = 0;
}

View File

@ -0,0 +1,50 @@
#include "JniWrapper.h"
#include "jni_helper.h"
// Proxies the generated function calls to the jni_helper
JNIEXPORT jint JNICALL Java_JniWrapper_get_1jni_1version
(JNIEnv* env, jclass class)
{
return get_jni_version(env);
}
JNIEXPORT jstring JNICALL Java_JniWrapper_read_1constant_1string
(JNIEnv* env, jclass class)
{
return read_constant_jstring(env);
}
JNIEXPORT jstring JNICALL Java_JniWrapper_write_1string
(JNIEnv* env, jclass class, jstring string)
{
return write_jstring(env, string);
}
JNIEXPORT jstring JNICALL Java_JniWrapper_write_1char_1array
(JNIEnv* env, jclass class, jcharArray chars)
{
return write_jchar_array(env, chars);
}
JNIEXPORT jstring JNICALL Java_JniWrapper_write_1string_1member
(JNIEnv* env, jclass class, jobject helper)
{
return write_string_member(env, helper);
}
JNIEXPORT void JNICALL Java_JniWrapper_set_1string_1member
(JNIEnv* env, jclass class, jobject helper)
{
set_string_member(env, helper);
}
JNIEXPORT void JNICALL Java_JniWrapper_execute_1java_1function
(JNIEnv* env, jclass class, jobject helper)
{
execute_java_function(env, helper);
}
JNIEXPORT jobject JNICALL Java_JniWrapper_instantiate_1java_1class
(JNIEnv* env, jclass class)
{
return instantiate_java_class(env);
}
JNIEXPORT jstring JNICALL Java_JniWrapper_set_1and_1write_1time_1in_1seconds
(JNIEnv* env, jclass class, jint seconds)
{
return set_and_write_time_in_seconds(env, seconds);
}

View File

@ -0,0 +1,17 @@
public class JniWrapper
{
static
{
System.loadLibrary("jni_native");
}
public static native int get_jni_version();
public static native String read_constant_string();
public static native String write_string(String string);
public static native String write_char_array(char[] string);
public static native String write_string_member(JniHelper helper);
public static native void set_string_member(JniHelper helper);
public static native void execute_java_function(JniHelper helper);
public static native JniHelper instantiate_java_class();
public static native String set_and_write_time_in_seconds(int seconds);
}

View File

@ -0,0 +1,94 @@
#include "jni_helper.h"
#include "native.h"
// Handles Java/C interop
jint get_jni_version(JNIEnv* env)
{
return (*env)->GetVersion(env);
}
jstring read_constant_jstring(JNIEnv* env)
{
return (*env)->NewStringUTF(env, read_constant_string());
}
static jstring read_internal_string_as_jstring(JNIEnv* env)
{
return (*env)->NewStringUTF(env, read_internal_string());
}
jstring write_jstring(JNIEnv* env, jstring string)
{
const char* utf8_string = (*env)->GetStringUTFChars(env, string, NULL);
write_internal_string(utf8_string);
(*env)->ReleaseStringUTFChars(env, string, utf8_string);
return read_internal_string_as_jstring(env);
}
jstring write_jchar_array(JNIEnv* env, jcharArray chars)
{
jsize length = (*env)->GetArrayLength(env, chars);
jchar* body = (*env)->GetCharArrayElements(env, chars, NULL);
jstring input = (*env)->NewString(env, body, length);
jstring output = write_jstring(env, input);
(*env)->ReleaseCharArrayElements(env, chars, body, JNI_ABORT);
return output;
}
static jfieldID get_string_member_field(JNIEnv* env, jobject helper)
{
jclass class = (*env)->GetObjectClass(env, helper);
return (*env)->GetFieldID(env, class, "stringMember", "Ljava/lang/String;");
}
jstring write_string_member(JNIEnv* env, jobject helper)
{
jfieldID fieldID = get_string_member_field(env, helper);
jstring string = (*env)->GetObjectField(env, helper, fieldID);
return write_jstring(env, string);
}
static void set_string_member_helper(JNIEnv* env, jobject helper, const char* utf8_string)
{
jfieldID fieldID = get_string_member_field(env, helper);
jstring string = (*env)->NewStringUTF(env, utf8_string);
(*env)->SetObjectField(env, helper, fieldID, string);
}
void set_string_member(JNIEnv* env, jobject helper)
{
char stringBuffer[256];
write_external_string(stringBuffer, 256);
set_string_member_helper(env, helper, stringBuffer);
}
typedef struct
{
JNIEnv* env;
jobject object;
jmethodID methodID;
} method_parameters;
static void call_void_java_method(void* context)
{
method_parameters* parameters = (method_parameters*)context;
(*parameters->env)->CallVoidMethod(parameters->env, parameters->object, parameters->methodID);
}
void execute_java_function(JNIEnv* env, jobject helper)
{
jclass class = (*env)->GetObjectClass(env, helper);
jmethodID methodID = (*env)->GetMethodID(env, class, "HelloManagedWorld", "()V");
method_parameters parameters = {env, helper, methodID};
execute_function(call_void_java_method, (void*)&parameters);
}
jobject instantiate_java_class(JNIEnv* env)
{
jclass class = (*env)->FindClass(env, "JniHelper");
jmethodID methodID = (*env)->GetMethodID(env, class, "<init>", "()V");
jobject object =(*env)->NewObject(env, class, methodID);
set_string_member_helper(env, object, "Instantiated from C");
return object;
}
jstring set_and_write_time_in_seconds(JNIEnv* env, jint seconds)
{
set_time_in_seconds((int)seconds);
write_internal_time_in_seconds();
return read_internal_string_as_jstring(env);
}

View File

@ -0,0 +1,13 @@
#pragma once
#include <jni.h>
jint get_jni_version(JNIEnv* env);
jstring read_constant_jstring(JNIEnv* env);
jstring write_jstring(JNIEnv* env, jstring string);
jstring write_jchar_array(JNIEnv* env, jcharArray chars);
jstring write_string_member(JNIEnv* env, jobject helper);
void set_string_member(JNIEnv* env, jobject helper);
void execute_java_function(JNIEnv* env, jobject helper);
jobject instantiate_java_class(JNIEnv* env);
jstring set_and_write_time_in_seconds(JNIEnv* env, jint seconds);

View File

@ -0,0 +1,39 @@
#include "native.h"
#include <stdio.h>
#include <time.h>
// Pure native functions
#define CHAR_BUFFER_SIZE 256
static char buffer[CHAR_BUFFER_SIZE];
const char* read_constant_string()
{
return "Hello from C";
}
const char* read_internal_string()
{
return buffer;
}
void write_internal_string(const char* string)
{
snprintf(buffer, CHAR_BUFFER_SIZE, "%s", string);
}
void write_external_string(char* string, size_t maxLength)
{
snprintf(string, maxLength, "Set from C");
}
void execute_function(void(*function)(void*), void* context)
{
function(context);
}
void set_time_in_seconds(int seconds)
{
time_t timeToSet = seconds;
stime(&timeToSet);
}
void write_internal_time_in_seconds()
{
time_t systemTime = time(NULL);
snprintf(buffer, CHAR_BUFFER_SIZE, "%u", systemTime);
}

View File

@ -0,0 +1,11 @@
#pragma once
#include <stddef.h>
const char* read_constant_string();
const char* read_internal_string();
void write_internal_string(const char* string);
void write_external_string(char* string, size_t maxLength);
void execute_function(void(*function)(void*), void* context);
void set_time_in_seconds(int seconds);
void write_internal_time_in_seconds();

View File

@ -0,0 +1,34 @@
################################################################################
#
# openjdk jni test
#
################################################################################
OPENJDK_JNI_TEST_DEPENDENCIES = openjdk
JNI_INCLUDE_PATH = $(BUILD_DIR)/openjdk-$(OPENJDK_VERSION)/build/linux-aarch64-server-release/jdk/include
define OPENJDK_JNI_TEST_BUILD_CMDS
# Compile Java classes and generate native headers
$(HOST_DIR)/bin/javac -d $(@D) -h $(@D) \
$(OPENJDK_JNI_TEST_PKGDIR)/JniTest.java \
$(OPENJDK_JNI_TEST_PKGDIR)/JniWrapper.java \
$(OPENJDK_JNI_TEST_PKGDIR)/JniHelper.java
# Compile shared library
$(TARGET_MAKE_ENV) $(TARGET_CC) -shared -fPIC \
-I$(JNI_INCLUDE_PATH) -I$(JNI_INCLUDE_PATH)/linux -I$(@D) \
-o $(@D)/libjni_native.so \
$(OPENJDK_JNI_TEST_PKGDIR)/JniWrapper.c \
$(OPENJDK_JNI_TEST_PKGDIR)/jni_helper.c \
$(OPENJDK_JNI_TEST_PKGDIR)/native.c
endef
define OPENJDK_JNI_TEST_INSTALL_TARGET_CMDS
$(INSTALL) -D -m 755 $(@D)/JniTest.class $(TARGET_DIR)/usr/bin/JniTest.class
$(INSTALL) -D -m 755 $(@D)/JniWrapper.class $(TARGET_DIR)/usr/bin/JniWrapper.class
$(INSTALL) -D -m 755 $(@D)/JniHelper.class $(TARGET_DIR)/usr/bin/JniHelper.class
$(INSTALL) -D -m 755 $(@D)/libjni_native.so $(TARGET_DIR)/usr/lib/libjni_native.so
endef
$(eval $(generic-package))

View File

@ -21,6 +21,7 @@ class TestOpenJdk(infra.basetest.BRTest):
BR2_PACKAGE_XORG7=y
BR2_PACKAGE_OPENJDK=y
BR2_PACKAGE_OPENJDK_HELLO_WORLD=y
BR2_PACKAGE_OPENJDK_JNI_TEST=y
"""
def login(self):
@ -40,3 +41,8 @@ class TestOpenJdk(infra.basetest.BRTest):
print(output)
self.assertEqual(exit_code, 0)
self.assertEqual(output, ["Hello, World"])
cmd = "java -cp /usr/bin JniTest"
output, exit_code = self.emulator.run(cmd, 120)
print(output)
self.assertEqual(exit_code, 0)