310 lines
9.0 KiB
Java
310 lines
9.0 KiB
Java
/*
|
|
* 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 com.facebook.jni.annotations.DoNotStrip;
|
|
import org.junit.Test;
|
|
|
|
public class HybridTests extends BaseFBJniTests {
|
|
static class TestHybridClass {
|
|
// Hybrid classes must include a member which manages the C++ object. It
|
|
// will be initialized from C++. It must be declared exactly with this
|
|
// type and name, so JNI can find it, and initialized once in the ctor.
|
|
// The annotation is necessary to keep proguard from renaming it, or else JNI
|
|
// won't be able to find it.
|
|
@DoNotStrip private final HybridData mHybridData;
|
|
|
|
// This is the method which creates the C++ instance and initializes
|
|
// mHybridData. Conventionally, it should be named initHybrid, and invoked
|
|
// from the constructor. This must be called only once. If the C++
|
|
// instance is referenced before this is called, a NullPointerException
|
|
// will be thrown.
|
|
private native HybridData initHybrid(int i, String s, boolean b);
|
|
|
|
// You can have more than one, which may be useful if the ctor is
|
|
// overloaded. This will call the default C++ ctor.
|
|
private native HybridData initHybrid();
|
|
|
|
// Implements factory-style initialization. You shouldn't usually
|
|
// need both styles in one class. Here we do it for testing and
|
|
// demo purposes.
|
|
private native HybridData initHybrid(String s, int i, boolean b);
|
|
|
|
// Java ctor must invoke initHybrid(). This just passes arguments through,
|
|
// but the ctor can do whatever work it wants, as long as it calls
|
|
// initHybrid() before any native methods.
|
|
public TestHybridClass(int i, String s, boolean b) {
|
|
mHybridData = initHybrid(i, s, b);
|
|
}
|
|
|
|
// This behaves the same as the ctor above, I just wanted a different
|
|
// signature to demonstrate factory-style initialization.
|
|
public TestHybridClass(String s, int i, boolean b) {
|
|
mHybridData = initHybrid(s, i, b);
|
|
}
|
|
|
|
// This is the simplest case. Even if everything is default, initHybrid()
|
|
// must still be called.
|
|
public TestHybridClass() {
|
|
mHybridData = initHybrid();
|
|
}
|
|
|
|
// Java ctor used by C++ newObjectCxxArgs. Note this is private.
|
|
private TestHybridClass(HybridData hd) {
|
|
mHybridData = hd;
|
|
}
|
|
|
|
public void doneUsingIt() {
|
|
mHybridData.resetNative();
|
|
}
|
|
|
|
// Some C++ methods.
|
|
public native void setBoth(int i, String s);
|
|
|
|
public native int getInt();
|
|
|
|
public native String getString();
|
|
|
|
public native String getCharString();
|
|
|
|
public native boolean copy1(TestHybridClass other);
|
|
|
|
public native boolean copy2(TestHybridClass other);
|
|
|
|
public native void oops();
|
|
|
|
public native void setGlobal(String s);
|
|
|
|
public native String getGlobal1();
|
|
|
|
public native String getGlobal2();
|
|
|
|
public static native TestHybridClass makeWithTwo();
|
|
|
|
public static native TestHybridClass makeWithThree();
|
|
|
|
public static native void autoconvertMany();
|
|
}
|
|
|
|
@Test
|
|
public void testHybridClass() {
|
|
TestHybridClass thc1 = new TestHybridClass();
|
|
assertThat(thc1.getInt()).isEqualTo(0);
|
|
assertThat(thc1.getString()).isEqualTo("");
|
|
|
|
thc1.setBoth(1, "one");
|
|
assertThat(thc1.getInt()).isEqualTo(1);
|
|
assertThat(thc1.getString()).isEqualTo("one");
|
|
|
|
TestHybridClass thc2 = TestHybridClass.makeWithTwo();
|
|
assertThat(thc2.getInt()).isEqualTo(2);
|
|
assertThat(thc2.getString()).isEqualTo("two");
|
|
|
|
thc2.doneUsingIt();
|
|
|
|
thrown.expect(NullPointerException.class);
|
|
thc2.getInt();
|
|
}
|
|
|
|
@Test
|
|
public void testHybridAutoconversion() {
|
|
TestHybridClass thc3 = TestHybridClass.makeWithThree();
|
|
assertThat(thc3.copy1(new TestHybridClass(3, "three", false))).isTrue();
|
|
assertThat(thc3.getInt()).isEqualTo(3);
|
|
assertThat(thc3.getString()).isEqualTo("three");
|
|
|
|
TestHybridClass thc4 = new TestHybridClass();
|
|
thc4.copy1(new TestHybridClass("four", 4, false));
|
|
assertThat(thc4.getInt()).isEqualTo(4);
|
|
assertThat(thc4.getString()).isEqualTo("four");
|
|
assertThat(thc4.getCharString()).isEqualTo("four");
|
|
|
|
TestHybridClass thc5 = new TestHybridClass();
|
|
assertThat(thc5.copy2(new TestHybridClass(5, "five", false))).isTrue();
|
|
assertThat(thc5.getInt()).isEqualTo(5);
|
|
assertThat(thc5.getString()).isEqualTo("five");
|
|
}
|
|
|
|
@Test
|
|
public void testReturnGlobalRef() {
|
|
TestHybridClass thc = new TestHybridClass();
|
|
thc.setGlobal("global_ref");
|
|
assertThat(thc.getGlobal1()).isEqualTo("global_ref");
|
|
assertThat(thc.getGlobal2()).isEqualTo("global_ref");
|
|
}
|
|
|
|
@Test
|
|
public void testLocalLeak() {
|
|
TestHybridClass.autoconvertMany();
|
|
}
|
|
|
|
@Test
|
|
public void testExceptionMapping() {
|
|
TestHybridClass thc1 = new TestHybridClass();
|
|
thrown.expect(ArrayStoreException.class);
|
|
thc1.oops();
|
|
}
|
|
|
|
abstract static class AbstractTestHybrid {
|
|
@DoNotStrip private final HybridData mHybridData;
|
|
|
|
private int mAbstractNum;
|
|
|
|
protected AbstractTestHybrid(HybridData hybridData, int an) {
|
|
mHybridData = hybridData;
|
|
mAbstractNum = an;
|
|
}
|
|
|
|
public int abstractNum() {
|
|
return mAbstractNum;
|
|
}
|
|
|
|
public native int nativeNum();
|
|
|
|
public abstract int concreteNum();
|
|
|
|
public abstract int sum();
|
|
}
|
|
|
|
static class ConcreteTestHybrid extends AbstractTestHybrid {
|
|
public ConcreteTestHybrid(int an, int nn, int cn) {
|
|
super(initHybrid(nn, cn), an);
|
|
}
|
|
|
|
private static native HybridData initHybrid(int nn, int cn);
|
|
|
|
// overrides can be native
|
|
@Override
|
|
public native int concreteNum();
|
|
|
|
// overrides can be java
|
|
@Override
|
|
public int sum() {
|
|
return nativeNum() + abstractNum() + concreteNum();
|
|
}
|
|
}
|
|
|
|
@Test
|
|
public void testHybridInheritance() {
|
|
AbstractTestHybrid ath = new ConcreteTestHybrid(1, 2, 3);
|
|
assertThat(ath.abstractNum()).isEqualTo(1);
|
|
assertThat(ath.nativeNum()).isEqualTo(2);
|
|
assertThat(ath.concreteNum()).isEqualTo(3);
|
|
assertThat(ath.sum()).isEqualTo(6);
|
|
}
|
|
|
|
public static native boolean cxxTestInheritance(AbstractTestHybrid ath);
|
|
|
|
public static native AbstractTestHybrid makeAbstractHybrid();
|
|
|
|
@Test
|
|
public void testHybridCxx() {
|
|
AbstractTestHybrid ath = new ConcreteTestHybrid(4, 5, 6);
|
|
assertThat(cxxTestInheritance(ath)).isTrue();
|
|
|
|
AbstractTestHybrid ath2 = makeAbstractHybrid();
|
|
assertThat(ath2 instanceof ConcreteTestHybrid).isTrue();
|
|
assertThat(ath2.abstractNum()).isEqualTo(7);
|
|
assertThat(ath2.nativeNum()).isEqualTo(8);
|
|
assertThat(ath2.concreteNum()).isEqualTo(9);
|
|
assertThat(ath2.sum()).isEqualTo(24);
|
|
}
|
|
|
|
static class Base {}
|
|
|
|
static class Derived extends Base {
|
|
@DoNotStrip private final HybridData mHybridData;
|
|
|
|
private Derived(HybridData hybridData) {
|
|
mHybridData = hybridData;
|
|
}
|
|
}
|
|
|
|
public static native boolean cxxTestDerivedJavaClass();
|
|
|
|
@Test
|
|
public void testDerivedJavaClassCxx() {
|
|
assertThat(cxxTestDerivedJavaClass()).isTrue();
|
|
}
|
|
|
|
static class TestHybridClassBase extends HybridClassBase {
|
|
protected native void initHybrid();
|
|
|
|
private native void initHybrid(int i);
|
|
|
|
protected TestHybridClassBase() {
|
|
// No initHybrid() here!
|
|
// Otherwise factory construction will set native pointer twice and process will crash.
|
|
}
|
|
|
|
public TestHybridClassBase(int i) {
|
|
initHybrid(i);
|
|
}
|
|
|
|
// Some C++ methods.
|
|
public native void setInt(int i);
|
|
|
|
public native int getInt();
|
|
|
|
public static native TestHybridClassBase makeWithThree();
|
|
}
|
|
|
|
static class TestHybridClassBaseDefaultCtor extends TestHybridClassBase {
|
|
public TestHybridClassBaseDefaultCtor() {
|
|
initHybrid();
|
|
}
|
|
}
|
|
|
|
@Test
|
|
public void testHybridBaseDefaultCtor() {
|
|
TestHybridClassBaseDefaultCtor base = new TestHybridClassBaseDefaultCtor();
|
|
assertThat(base.getInt()).isZero();
|
|
|
|
base.setInt(58);
|
|
assertThat(base.getInt()).isEqualTo(58);
|
|
}
|
|
|
|
@Test
|
|
public void testHybridBaseConstructorArgs() {
|
|
TestHybridClassBase base = new TestHybridClassBase(42);
|
|
assertThat(base.getInt()).isEqualTo(42);
|
|
}
|
|
|
|
@Test
|
|
public void testHybridBaseFactoryConstruction() {
|
|
TestHybridClassBase base = TestHybridClassBase.makeWithThree();
|
|
assertThat(base.getInt()).isEqualTo(3);
|
|
}
|
|
|
|
static class Destroyable {
|
|
@DoNotStrip private final HybridData mHybridData;
|
|
|
|
private Destroyable(HybridData hybridData) {
|
|
mHybridData = hybridData;
|
|
}
|
|
}
|
|
|
|
public static native boolean cxxTestHybridDestruction();
|
|
|
|
@Test
|
|
public void testHybridDestuction() {
|
|
assertThat(cxxTestHybridDestruction()).isTrue();
|
|
}
|
|
}
|