support/testing: openjdk JNI test cases
authorDaniel J. Leach <daniel.j.leach@gmail.com>
Thu, 25 Apr 2019 19:40:45 +0000 (14:40 -0500)
committerArnout Vandecappelle (Essensium/Mind) <arnout@mind.be>
Sun, 30 Jun 2019 15:32:34 +0000 (17:32 +0200)
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>
12 files changed:
support/testing/tests/package/br2-external/openjdk/Config.in
support/testing/tests/package/br2-external/openjdk/package/openjdk-jni-test/Config.in [new file with mode: 0644]
support/testing/tests/package/br2-external/openjdk/package/openjdk-jni-test/JniHelper.java [new file with mode: 0644]
support/testing/tests/package/br2-external/openjdk/package/openjdk-jni-test/JniTest.java [new file with mode: 0644]
support/testing/tests/package/br2-external/openjdk/package/openjdk-jni-test/JniWrapper.c [new file with mode: 0644]
support/testing/tests/package/br2-external/openjdk/package/openjdk-jni-test/JniWrapper.java [new file with mode: 0644]
support/testing/tests/package/br2-external/openjdk/package/openjdk-jni-test/jni_helper.c [new file with mode: 0644]
support/testing/tests/package/br2-external/openjdk/package/openjdk-jni-test/jni_helper.h [new file with mode: 0644]
support/testing/tests/package/br2-external/openjdk/package/openjdk-jni-test/native.c [new file with mode: 0644]
support/testing/tests/package/br2-external/openjdk/package/openjdk-jni-test/native.h [new file with mode: 0644]
support/testing/tests/package/br2-external/openjdk/package/openjdk-jni-test/openjdk-jni-test.mk [new file with mode: 0644]
support/testing/tests/package/test_openjdk.py

index 00c7fd4799c7478105916270553c9c1b0c8cba62..2588b340540df245855908efdf07e9197bbbb75d 100644 (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"
diff --git a/support/testing/tests/package/br2-external/openjdk/package/openjdk-jni-test/Config.in b/support/testing/tests/package/br2-external/openjdk/package/openjdk-jni-test/Config.in
new file mode 100644 (file)
index 0000000..22cd0c0
--- /dev/null
@@ -0,0 +1,5 @@
+config BR2_PACKAGE_OPENJDK_JNI_TEST
+       bool "openjdk JNI test"
+       depends on BR2_PACKAGE_OPENJDK
+       help
+         Tests openjdk JNI support
diff --git a/support/testing/tests/package/br2-external/openjdk/package/openjdk-jni-test/JniHelper.java b/support/testing/tests/package/br2-external/openjdk/package/openjdk-jni-test/JniHelper.java
new file mode 100644 (file)
index 0000000..9e2a038
--- /dev/null
@@ -0,0 +1,9 @@
+public class JniHelper
+{
+       public void HelloManagedWorld()
+       {
+               stringMember = "Hello, Managed World";
+       }
+
+       public String stringMember = "Set from Java";
+}
diff --git a/support/testing/tests/package/br2-external/openjdk/package/openjdk-jni-test/JniTest.java b/support/testing/tests/package/br2-external/openjdk/package/openjdk-jni-test/JniTest.java
new file mode 100644 (file)
index 0000000..3d6c17b
--- /dev/null
@@ -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;
+}
diff --git a/support/testing/tests/package/br2-external/openjdk/package/openjdk-jni-test/JniWrapper.c b/support/testing/tests/package/br2-external/openjdk/package/openjdk-jni-test/JniWrapper.c
new file mode 100644 (file)
index 0000000..0dde4e0
--- /dev/null
@@ -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);
+}
diff --git a/support/testing/tests/package/br2-external/openjdk/package/openjdk-jni-test/JniWrapper.java b/support/testing/tests/package/br2-external/openjdk/package/openjdk-jni-test/JniWrapper.java
new file mode 100644 (file)
index 0000000..d11e99b
--- /dev/null
@@ -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);
+}
diff --git a/support/testing/tests/package/br2-external/openjdk/package/openjdk-jni-test/jni_helper.c b/support/testing/tests/package/br2-external/openjdk/package/openjdk-jni-test/jni_helper.c
new file mode 100644 (file)
index 0000000..e6e2eec
--- /dev/null
@@ -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);
+}
diff --git a/support/testing/tests/package/br2-external/openjdk/package/openjdk-jni-test/jni_helper.h b/support/testing/tests/package/br2-external/openjdk/package/openjdk-jni-test/jni_helper.h
new file mode 100644 (file)
index 0000000..4c9aab3
--- /dev/null
@@ -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);
diff --git a/support/testing/tests/package/br2-external/openjdk/package/openjdk-jni-test/native.c b/support/testing/tests/package/br2-external/openjdk/package/openjdk-jni-test/native.c
new file mode 100644 (file)
index 0000000..ed87e34
--- /dev/null
@@ -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);
+}
diff --git a/support/testing/tests/package/br2-external/openjdk/package/openjdk-jni-test/native.h b/support/testing/tests/package/br2-external/openjdk/package/openjdk-jni-test/native.h
new file mode 100644 (file)
index 0000000..a25f177
--- /dev/null
@@ -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();
diff --git a/support/testing/tests/package/br2-external/openjdk/package/openjdk-jni-test/openjdk-jni-test.mk b/support/testing/tests/package/br2-external/openjdk/package/openjdk-jni-test/openjdk-jni-test.mk
new file mode 100644 (file)
index 0000000..f279e5c
--- /dev/null
@@ -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))
index 5fbb500fbd49516bef9d32011b32b2fd0bbc634d..3a1d1a9b09f060b743cadbc17520588bdf73bb24 100644 (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)