/* * Copyright (c) Facebook, Inc. and its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.facebook.jni; import static org.fest.assertions.api.Assertions.assertThat; import static org.mockito.Mockito.verify; import com.facebook.jni.annotations.DoNotStrip; import java.io.IOException; import java.util.ArrayList; import java.util.concurrent.Callable; import org.fest.assertions.api.Fail; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class FBJniTests extends BaseFBJniTests { class CustomException extends Throwable { int mGetMessageCalls = 0; @Override public String getMessage() { return "getMessages: " + (++mGetMessageCalls); } } public interface Callbacks { void voidFoo(); boolean booleanFoo(); byte byteFoo(); char charFoo(); short shortFoo(); int intFoo(); long longFoo(); float floatFoo(); double doubleFoo(); Object objectFoo(); String stringFoo(); } public static class TestThing { int foo; } @Mock private static Callbacks mCallbacksMock; private int mIntFieldTest; private String mStringFieldTest; private TestThing mReferenceFieldTest; private static int sIntFieldTest; private static String sStringFieldTest; private static TestThing sReferenceFieldTest; @DoNotStrip // Resolved from fbjni_tests::TestFieldAccess int bar(double d) { return 42; } // Test case for nonvirtual function public boolean nonVirtualMethod(boolean s) { return s; } private static void verifyAllCallbacksCalled(Callbacks mock) { verify(mock).voidFoo(); verify(mock).booleanFoo(); verify(mock).byteFoo(); verify(mock).charFoo(); verify(mock).shortFoo(); verify(mock).intFoo(); verify(mock).longFoo(); verify(mock).floatFoo(); verify(mock).doubleFoo(); verify(mock).objectFoo(); verify(mock).stringFoo(); } // Instead of mocking, lets call non-static functions and verify them. public static void voidFooStatic() { mCallbacksMock.voidFoo(); } public static boolean booleanFooStatic() { return mCallbacksMock.booleanFoo(); } public static byte byteFooStatic() { return mCallbacksMock.byteFoo(); } public static char charFooStatic(char c, int s) { return mCallbacksMock.charFoo(); } public static short shortFooStatic(short s, short t) { return mCallbacksMock.shortFoo(); } public static int intFooStatic(int s) { return mCallbacksMock.intFoo(); } public static long longFooStatic() { return mCallbacksMock.longFoo(); } public static float floatFooStatic() { return mCallbacksMock.floatFoo(); } public static double doubleFooStatic() { return mCallbacksMock.doubleFoo(); } public static Object objectFooStatic() { return mCallbacksMock.objectFoo(); } public static String stringFooStatic() { return mCallbacksMock.stringFoo(); } @Test public void resolveClass() throws ClassNotFoundException { assertThat(nativeTestClassResolution("java/lang/Object")).isTrue(); } // Some versions of Android throw ClassNotFoundException while others throw NoClassDefFoundError. // Flatten that to always be ClassNotFoundException. private static void wrapClassLoadingErrors(Callable code) throws Exception { try { code.call(); } catch (NoClassDefFoundError ex) { throw new ClassNotFoundException("chained NoClassDefFoundError", ex); } } @Test(expected = ClassNotFoundException.class) public void failingToResolveClass() throws Exception { wrapClassLoadingErrors( new Callable() { @Override public Boolean call() throws Exception { return nativeTestClassResolution("ThisClassDoesNotExist"); } }); } private native boolean nativeTestClassResolution(String className) throws ClassNotFoundException; @Test public void lazyClassResolution() throws ClassNotFoundException { assertThat(nativeTestLazyClassResolution("java/lang/Object")).isTrue(); } @Test(expected = ClassNotFoundException.class) public void failedLazyClassResolution() throws Exception { wrapClassLoadingErrors( new Callable() { @Override public Boolean call() throws Exception { return nativeTestLazyClassResolution("ThisClassDoesNotExist"); } }); } private native boolean nativeTestLazyClassResolution(String className) throws ClassNotFoundException; @Test public void instanceCreation() { assertThat(nativeCreateInstanceOf("java/lang/String")) .isInstanceOf(String.class) .isEqualTo("java/lang/String"); } private native Object nativeCreateInstanceOf(String className); @Test public void typeDescriptors() { assertThat(nativeTestTypeDescriptors()).isTrue(); } private native boolean nativeTestTypeDescriptors(); @Test public void resolveVirtualMethod() throws ClassNotFoundException, NoSuchMethodException { assertThat(nativeTestVirtualMethodResolution_I("java/lang/Object", "hashCode")).isTrue(); } @Test public void resolveVirtualMethodWithArray() throws ClassNotFoundException, NoSuchMethodException { assertThat(nativeTestVirtualMethodResolution_arrB("java/lang/String", "getBytes")).isTrue(); } @Test public void resolveVirtualMethodWithObjectArray() throws ClassNotFoundException, NoSuchMethodException { assertThat(nativeTestVirtualMethodResolution_S_arrS("java/lang/String", "split")).isTrue(); } @Test public void resolveVirtualMethodWithObjectArrayArray() throws ClassNotFoundException, NoSuchMethodException { assertThat( nativeTestVirtualMethodResolution_arrarrS( "com/facebook/jni/FBJniTests", "returnMultidimensionalObjectArray")) .isTrue(); } public static String[][] returnMultidimensionalObjectArray() { return null; } @Test public void resolveVirtualMethodWithPrimitiveArrayArray() throws ClassNotFoundException, NoSuchMethodException { assertThat( nativeTestVirtualMethodResolution_arrarrI( "com/facebook/jni/FBJniTests", "returnMultidimensionalPrimitiveArray")) .isTrue(); } public static int[][] returnMultidimensionalPrimitiveArray() { return null; } @Test(expected = NoSuchMethodError.class) public void failingToResolveVirtualMethod() throws ClassNotFoundException, NoSuchMethodError { nativeTestVirtualMethodResolution_I("java/lang/Object", "ThisMethodDoesNotExist"); } private native boolean nativeTestVirtualMethodResolution_I(String className, String methodName) throws ClassNotFoundException, NoSuchMethodError; private native boolean nativeTestVirtualMethodResolution_arrB(String className, String methodName) throws ClassNotFoundException, NoSuchMethodError; private native boolean nativeTestVirtualMethodResolution_S_arrS( String className, String methodName) throws ClassNotFoundException, NoSuchMethodError; private native boolean nativeTestVirtualMethodResolution_arrarrS( String className, String methodName) throws ClassNotFoundException, NoSuchMethodError; private native boolean nativeTestVirtualMethodResolution_arrarrI( String className, String methodName) throws ClassNotFoundException, NoSuchMethodError; @Test public void lazyMethodResolution() throws ClassNotFoundException, NoSuchMethodError { assertThat(nativeTestLazyVirtualMethodResolution_I("java/lang/Object", "hashCode")).isTrue(); } @Test(expected = NoSuchMethodError.class) public void failedLazyMethodResolution() throws ClassNotFoundException, NoSuchMethodError { nativeTestLazyVirtualMethodResolution_I("java/lang/Object", "ThisMethodDoesNotExist"); } private native boolean nativeTestLazyVirtualMethodResolution_I( String className, String methodName); @Test public void callbacksUsingJMethod() { nativeTestJMethodCallbacks(mCallbacksMock); verifyAllCallbacksCalled(mCallbacksMock); } private native void nativeTestJMethodCallbacks(Callbacks callbacks); @Test public void callbacksUsingJStaticMethod() { nativeTestJStaticMethodCallbacks(); verifyAllCallbacksCalled(mCallbacksMock); } private native void nativeTestJStaticMethodCallbacks(); @Test public void isAssignableFrom() { assertThat(nativeTestIsAssignableFrom(String.class, String.class)).isTrue(); assertThat(nativeTestIsAssignableFrom(String.class, Object.class)).isFalse(); assertThat(nativeTestIsAssignableFrom(Object.class, String.class)).isTrue(); assertThat(nativeTestIsAssignableFrom(ArrayList.class, Iterable.class)).isFalse(); assertThat(nativeTestIsAssignableFrom(Iterable.class, ArrayList.class)).isTrue(); } private native boolean nativeTestIsAssignableFrom(Class cls1, Class cls2); @Test public void isInstanceOf() { assertThat(nativeTestIsInstanceOf("", String.class)).isTrue(); assertThat(nativeTestIsInstanceOf("", Object.class)).isTrue(); assertThat(nativeTestIsInstanceOf(new Object(), String.class)).isFalse(); assertThat(nativeTestIsInstanceOf(new ArrayList(), Iterable.class)).isTrue(); assertThat(nativeTestIsInstanceOf(null, Iterable.class)).isTrue(); } private native boolean nativeTestIsInstanceOf(Object object, Class cls); @Test public void isSameObject() { Object anObject = new Object(); Object anotherObject = new Object(); assertThat(nativeTestIsSameObject(anObject, anObject)).isTrue(); assertThat(nativeTestIsSameObject(anObject, anotherObject)).isFalse(); assertThat(nativeTestIsSameObject(null, anObject)).isFalse(); assertThat(nativeTestIsSameObject(anObject, null)).isFalse(); assertThat(nativeTestIsSameObject(null, null)).isTrue(); } private native boolean nativeTestIsSameObject(Object a, Object b); @Test public void testGetSuperClass() { Class testClass = String.class; Class superClass = Object.class; Class notSuperClass = Integer.class; assertThat(nativeTestGetSuperclass(testClass, superClass)).isTrue(); assertThat(nativeTestGetSuperclass(testClass, notSuperClass)).isFalse(); } private native boolean nativeTestGetSuperclass(Class testClass, Class superOfTest); @Test public void testWeakRefs() { assertThat(nativeTestWeakRefs()).isTrue(); } private native boolean nativeTestWeakRefs(); @Test public void testAliasRefs() { assertThat(nativeTestAlias()).isTrue(); } private native boolean nativeTestAlias(); @Test public void testAliasRefConversions() { assertThat(nativeTestAliasRefConversions()).isTrue(); } private native boolean nativeTestAliasRefConversions(); @Test public void testNullJString() { assertThat(nativeTestNullJString()).isTrue(); } private native boolean nativeTestNullJString(); @Test public void testSwap() { assertThat(nativeTestSwap(new Object())).isTrue(); } private native boolean nativeTestSwap(Object other); @Test public void testEqualOperator() { assertThat(nativeTestEqualOperator(new Object())).isTrue(); } private native boolean nativeTestEqualOperator(Object other); @Test public void testRelaseAlias() { assertThat(nativeTestReleaseAlias()).isTrue(); } private native boolean nativeTestReleaseAlias(); @Test public void testLockingWeakReferences() { assertThat(nativeTestLockingWeakReferences()).isTrue(); } private native boolean nativeTestLockingWeakReferences(); @Test public void testCreatingReferences() { assertThat(nativeTestCreatingReferences()).isTrue(); } private native boolean nativeTestCreatingReferences(); @Test public void testAssignmentAndCopyConstructors() { assertThat(nativeTestAssignmentAndCopyConstructors()).isTrue(); } private native boolean nativeTestAssignmentAndCopyConstructors(); @Test public void testAssignmentAndCopyCrossTypes() { assertThat(nativeTestAssignmentAndCopyCrossTypes()).isTrue(); } private native boolean nativeTestAssignmentAndCopyCrossTypes(); @Test public void testNullReferences() { assertThat(nativeTestNullReferences()).isTrue(); } private native boolean nativeTestNullReferences(); @Test public void testAutoAliasRefReturningVoid() { nativeTestAutoAliasRefReturningVoid(); } private native void nativeTestAutoAliasRefReturningVoid(); @Test public void testFieldAccess() { mIntFieldTest = 17; assertThat(nativeTestFieldAccess("mIntFieldTest", mIntFieldTest, 42)).isTrue(); assertThat(mIntFieldTest).isEqualTo(42); } private native boolean nativeTestFieldAccess(String name, int oldVal, int newVal); @Test public void testStringFieldAccess() { mStringFieldTest = "initial"; assertThat(nativeTestStringFieldAccess("mStringFieldTest", mStringFieldTest, "final")).isTrue(); assertThat(mStringFieldTest).isEqualTo("final"); } private native boolean nativeTestStringFieldAccess(String name, String oldVal, String newVal); @Test public void testReferenceFieldAccess() { for (boolean useWrapper : new boolean[] {false, true}) { mReferenceFieldTest = new TestThing(); TestThing newthing = new TestThing(); assertThat( nativeTestReferenceFieldAccess( "mReferenceFieldTest", mReferenceFieldTest, newthing, useWrapper)) .isTrue(); assertThat(mReferenceFieldTest).isEqualTo(newthing); } } private native boolean nativeTestReferenceFieldAccess( String name, Object oldVal, Object newVal, boolean useWrapper); @Test public void testStaticFieldAccess() { sIntFieldTest = 17; assertThat(nativeTestStaticFieldAccess("sIntFieldTest", sIntFieldTest, 42)).isTrue(); assertThat(sIntFieldTest).isEqualTo(42); } private native boolean nativeTestStaticFieldAccess(String name, int oldVal, int newVal); @Test public void testStaticStringFieldAccess() { sStringFieldTest = "initial"; assertThat(nativeTestStaticStringFieldAccess("sStringFieldTest", sStringFieldTest, "final")) .isTrue(); assertThat(sStringFieldTest).isEqualTo("final"); } private native boolean nativeTestStaticStringFieldAccess(String name, String oVal, String nVal); @Test public void testStaticReferenceFieldAccess() { for (boolean useWrapper : new boolean[] {false, true}) { sReferenceFieldTest = new TestThing(); TestThing newthing = new TestThing(); assertThat( nativeTestStaticReferenceFieldAccess( "sReferenceFieldTest", sReferenceFieldTest, newthing, useWrapper)) .isTrue(); assertThat(sReferenceFieldTest).isEqualTo(newthing); } } private native boolean nativeTestStaticReferenceFieldAccess( String name, Object oldVal, Object newVal, boolean useWrapper); @Test public void testNonVirtualMethod() { assertThat(nativeTestNonVirtualMethod(true)).isTrue(); } private native boolean nativeTestNonVirtualMethod(boolean s); @Test public void testArrayCreation() { String[] expectedStrings = {"one", "two", "three"}; String[] joinedStrings = nativeTestArrayCreation(expectedStrings[0], expectedStrings[1], expectedStrings[2]); assertThat(joinedStrings).isEqualTo(expectedStrings); } private native String[] nativeTestArrayCreation(String s0, String s1, String s2); @Test public void testMultidimensionalObjectArray() { String[] strings = {"one", "two", "three"}; String[][] expectedStrings = {{"one", "two"}, {"three"}}; String[][] joinedStrings = nativeTestMultidimensionalObjectArray(strings[0], strings[1], strings[2]); assertThat(joinedStrings).isEqualTo(expectedStrings); } private native String[][] nativeTestMultidimensionalObjectArray(String s0, String s1, String s2); @Test public void testMultidimensionalPrimitiveArray() { int[] nums = {1, 2, 3}; int[][] expectedNums = {{1, 2}, {3}}; int[][] gotNums = nativeTestMultidimensionalPrimitiveArray(nums[0], nums[1], nums[2]); assertThat(gotNums).isEqualTo(expectedNums); } private native int[][] nativeTestMultidimensionalPrimitiveArray(int i0, int i1, int i2); private String[] mCapturedStringArray = null; @DoNotStrip String captureStringArray(String[] input) { mCapturedStringArray = input; return "Stub"; } @Test public void testBuildStringArray() throws Exception { String[] input = {"Four", "score", "and", "seven", "beers", "ago"}; nativeTestBuildStringArray(input); assertThat(mCapturedStringArray).isEqualTo(input); } private native String nativeTestBuildStringArray(String... input); public Object methodResolutionWithCxxTypes(String t, long val) { if (!"test".equals(t) || val != 3) throw new RuntimeException(); return null; } public void methodResolutionWithCxxTypesVoid(String t, long val) { if (!"test".equals(t) || val != 3) throw new RuntimeException(); } public int methodResolutionWithCxxTypesInt(String t, long val) { if (!"test".equals(t) || val != 3) throw new RuntimeException(); return 0; } public static Object methodResolutionWithCxxTypesStatic(String t, long val) { if (!"test".equals(t) || val != 3) throw new RuntimeException(); return null; } public static void methodResolutionWithCxxTypesVoidStatic(String t, long val) { if (!"test".equals(t) || val != 3) throw new RuntimeException(); } public static int methodResolutionWithCxxTypesIntStatic(String t, long val) { if (!"test".equals(t) || val != 3) throw new RuntimeException(); return 0; } @Test public void testMethodResolutionWithCxxTypes() { testMethodResolutionWithCxxTypesNative("methodResolutionWithCxxTypes", "test", 3); } private native void testMethodResolutionWithCxxTypesNative( String callbackName, String str, long val); @Test(expected = CustomException.class) public void testHandleJavaCustomException() { testHandleJavaCustomExceptionNative(); } private native void testHandleJavaCustomExceptionNative(); @Test public void testHandleNullExceptionMessage() { testHandleNullExceptionMessageNative(); } private native void testHandleNullExceptionMessageNative(); @Test public void testHandleNestedException() { try { nativeTestHandleNestedException(); } catch (Throwable e) { assertThat(e).isInstanceOf(ArrayIndexOutOfBoundsException.class); e = e.getCause(); assertThat(e).isInstanceOf(RuntimeException.class); e = e.getCause(); assertThat(e).isInstanceOf(CustomException.class).hasNoCause(); } } private native void nativeTestHandleNestedException(); @Test(expected = CppException.class) public void testHandleNoRttiException() { nativeTestHandleNoRttiException(); } private native void nativeTestHandleNoRttiException(); @Test public void testCopyConstructor() { assertThat(nativeTestCopyConstructor()) .isEqualTo("com.facebook.jni.FBJniTests$CustomException: getMessages: 1"); } private native String nativeTestCopyConstructor(); @Test public void testMoveConstructorWithEmptyWhat() { assertThat(nativeTestMoveConstructorWithEmptyWhat()) .isEqualTo("com.facebook.jni.FBJniTests$CustomException: getMessages: 1"); } private native String nativeTestMoveConstructorWithEmptyWhat(); @Test public void testMoveConstructorWithPopulatedWhat() { assertThat(nativeTestMoveConstructorWithPopulatedWhat()) .isEqualTo("com.facebook.jni.FBJniTests$CustomException: getMessages: 1"); } private native String nativeTestMoveConstructorWithPopulatedWhat(); @DoNotStrip // Used in native code. protected void customExceptionThrower() throws CustomException { throw new CustomException(); } @DoNotStrip // Used in native code. protected void nullMessageThrower() throws NullPointerException { // just like Preconditions.checkNotNull() does throw new NullPointerException(); } @Test public void testHandleCppRuntimeError() { String message = "Sample runtime error."; thrown.expect(RuntimeException.class); thrown.expectMessage(message); nativeTestHandleCppRuntimeError(message); } private native void nativeTestHandleCppRuntimeError(String message); @Test(expected = IOException.class) public void testHandleCppIOBaseFailure() { nativeTestHandleCppIOBaseFailure(); } private native void nativeTestHandleCppIOBaseFailure(); @Test(expected = CppSystemErrorException.class) public void testHandleCppSystemError() { nativeTestHandleCppSystemError(); } private native void nativeTestHandleCppSystemError(); @Test(expected = RuntimeException.class) public void testInterDsoExceptionHandlingA() { nativeTestInterDsoExceptionHandlingA(); } private native void nativeTestInterDsoExceptionHandlingA(); @Test public void testInterDsoExceptionHandlingB() { assertThat(nativeTestInterDsoExceptionHandlingB()).isTrue(); } private native boolean nativeTestInterDsoExceptionHandlingB(); @Test public void testHandleNonStdExceptionThrow() { try { nativeTestHandleNonStdExceptionThrow(); Fail.failBecauseExceptionWasNotThrown(UnknownCppException.class); } catch (UnknownCppException ex) { if (System.getProperty("os.name").startsWith("Windows")) { // Unknown exception types not supported on Windows. assertThat(ex.getMessage()).isEqualTo("Unknown"); return; } // the actual string is implementation-defined and mangled, but in practice, // it has the name of the C++ type in it somewhere. assertThat(ex.getMessage()).startsWith("Unknown: ").contains("NonStdException"); } } private native void nativeTestHandleNonStdExceptionThrow(); @Test(expected = UnknownCppException.class) public void testHandleCppCharPointerThrow() { nativeTestHandleCppCharPointerThrow(); } private native void nativeTestHandleCppCharPointerThrow(); @Test(expected = IllegalArgumentException.class) public void testThrowJavaExceptionByName() { nativeTestThrowJavaExceptionByName(); } private native void nativeTestThrowJavaExceptionByName(); @Test(expected = UnknownCppException.class) public void testHandleCppIntThrow() { nativeTestHandleCppIntThrow(); } private native void nativeTestHandleCppIntThrow(); @Test public void testJThread() { assertThat(nativeTestJThread()).isEqualTo(1); } private native int nativeTestJThread(); @Test public void testThreadScopeGuard() { assertThat(nativeTestThreadScopeGuard(17)).isEqualTo(42); } private native int nativeTestThreadScopeGuard(double input); @Test public void testNestedThreadScopeGuard() { assertThat(nativeTestNestedThreadScopeGuard(17)).isEqualTo(42); } private native int nativeTestNestedThreadScopeGuard(double input); @Test public void testClassLoadInWorker() { assertThat(nativeTestClassLoadInWorker()).isEqualTo(1); } private native int nativeTestClassLoadInWorker(); @Test public void testClassLoadWorkerFastPath() { assertThat(nativeTestClassLoadWorkerFastPath()).isEqualTo(3); } private native int nativeTestClassLoadWorkerFastPath(); @Test public void testToString() { assertThat(nativeTestToString()).isTrue(); } private native boolean nativeTestToString(); // Casting alias_ref @Test public void testCorrectStaticCastAliasRef() { // Static cast can't fail at run time. If the object isn't actually // of that type, we just get undefined behaviour, which we can't // check for. So we only do a positive test. assertThat(nativeStaticCastAliasRefToString("hello")).isTrue(); } @Test public void testNullStaticCastAliasRef() { assertThat(nativeStaticCastAliasRefToString(null)).isTrue(); } private native boolean nativeStaticCastAliasRefToString(Object a); @Test public void testDynamicCastAliasRefToSame() { assertThat(nativeDynamicCastAliasRefToThrowable(new Throwable())).isTrue(); } public void testDynamicCastAliasRefToBase() { assertThat(nativeDynamicCastAliasRefToThrowable(new Exception())).isTrue(); } @Test(expected = ClassCastException.class) public void testDynamicCastAliasRefToDerived() { nativeDynamicCastAliasRefToThrowable(new Object()); } @Test(expected = ClassCastException.class) public void testDynamicCastAliasRefToUnrelated() { nativeDynamicCastAliasRefToThrowable(new Integer(23)); } @Test public void testNullDynamicCastAliasRef() { assertThat(nativeDynamicCastAliasRefToThrowable(null)).isTrue(); } private native boolean nativeDynamicCastAliasRefToThrowable(Object a); // Casting local_ref @Test public void testCorrectStaticCastLocalRef() { // Static cast can't fail at run time. If the object isn't actually // of that type, we just get undefined behaviour, which we can't // check for. So we only do a positive test. assertThat(nativeStaticCastLocalRefToString("hello")).isTrue(); } @Test public void testNullStaticCastLocalRef() { assertThat(nativeStaticCastLocalRefToString(null)).isTrue(); } private native boolean nativeStaticCastLocalRefToString(Object a); @Test public void testCorrectDynamicCastLocalRef() { assertThat(nativeDynamicCastLocalRefToString("hello")).isTrue(); } @Test(expected = ClassCastException.class) public void testIncorrectDynamicCastLocalRef() { nativeDynamicCastLocalRefToString(new Integer(23)); } @Test public void testNullDynamicCastLocalRef() { assertThat(nativeDynamicCastLocalRefToString(null)).isTrue(); } private native boolean nativeDynamicCastLocalRefToString(Object a); // Casting global_ref @Test public void testCorrectStaticCastGlobalRef() { // Static cast can't fail at run time. If the object isn't actually // of that type, we just get undefined behaviour, which we can't // check for. So we only do a positive test. assertThat(nativeStaticCastGlobalRefToString("hello")).isTrue(); } @Test public void testNullStaticCastGlobalRef() { assertThat(nativeStaticCastGlobalRefToString(null)).isTrue(); } private native boolean nativeStaticCastGlobalRefToString(Object a); @Test public void testCorrectDynamicCastGlobalRef() { assertThat(nativeDynamicCastGlobalRefToString("hello")).isTrue(); } @Test(expected = ClassCastException.class) public void testIncorrectDynamicCastGlobalRef() { nativeDynamicCastGlobalRefToString(new Integer(23)); } @Test public void testNullDynamicCastGlobalRef() { assertThat(nativeDynamicCastGlobalRefToString(null)).isTrue(); } private native boolean nativeDynamicCastGlobalRefToString(Object a); @Test public void testCriticalNativeMethodBindsAndCanBeInvoked() { assertThat(nativeCriticalNativeMethodBindsAndCanBeInvoked(12, 3.45f)).isTrue(); } private static native boolean nativeCriticalNativeMethodBindsAndCanBeInvoked(int a, float b); }