xref: /aosp_15_r20/external/fbjni/test/HybridTests.java (revision 65c59e023c5336bbd4a23be7af78407e3d80e7e7)
1 /*
2  * Copyright (c) Facebook, Inc. and its affiliates.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.facebook.jni;
18 
19 import static org.assertj.core.api.Assertions.assertThat;
20 
21 import com.facebook.jni.annotations.DoNotStrip;
22 import org.junit.Test;
23 
24 public class HybridTests extends BaseFBJniTests {
25   static class TestHybridClass {
26     // Hybrid classes must include a member which manages the C++ object.  It
27     // will be initialized from C++.  It must be declared exactly with this
28     // type and name, so JNI can find it, and initialized once in the ctor.
29     // The annotation is necessary to keep proguard from renaming it, or else JNI
30     // won't be able to find it.
31     @DoNotStrip private final HybridData mHybridData;
32 
33     // This is the method which creates the C++ instance and initializes
34     // mHybridData.  Conventionally, it should be named initHybrid, and invoked
35     // from the constructor.  This must be called only once.  If the C++
36     // instance is referenced before this is called, a NullPointerException
37     // will be thrown.
initHybrid(int i, String s, boolean b)38     private native HybridData initHybrid(int i, String s, boolean b);
39 
40     // You can have more than one, which may be useful if the ctor is
41     // overloaded.  This will call the default C++ ctor.
initHybrid()42     private native HybridData initHybrid();
43 
44     // Implements factory-style initialization.  You shouldn't usually
45     // need both styles in one class.  Here we do it for testing and
46     // demo purposes.
initHybrid(String s, int i, boolean b)47     private native HybridData initHybrid(String s, int i, boolean b);
48 
49     // Java ctor must invoke initHybrid().  This just passes arguments through,
50     // but the ctor can do whatever work it wants, as long as it calls
51     // initHybrid() before any native methods.
TestHybridClass(int i, String s, boolean b)52     public TestHybridClass(int i, String s, boolean b) {
53       mHybridData = initHybrid(i, s, b);
54     }
55 
56     // This behaves the same as the ctor above, I just wanted a different
57     // signature to demonstrate factory-style initialization.
TestHybridClass(String s, int i, boolean b)58     public TestHybridClass(String s, int i, boolean b) {
59       mHybridData = initHybrid(s, i, b);
60     }
61 
62     // This is the simplest case.  Even if everything is default, initHybrid()
63     // must still be called.
TestHybridClass()64     public TestHybridClass() {
65       mHybridData = initHybrid();
66     }
67 
68     // Java ctor used by C++ newObjectCxxArgs.  Note this is private.
TestHybridClass(HybridData hd)69     private TestHybridClass(HybridData hd) {
70       mHybridData = hd;
71     }
72 
doneUsingIt()73     public void doneUsingIt() {
74       mHybridData.resetNative();
75     }
76 
77     // Some C++ methods.
setBoth(int i, String s)78     public native void setBoth(int i, String s);
79 
getInt()80     public native int getInt();
81 
getString()82     public native String getString();
83 
getCharString()84     public native String getCharString();
85 
copy1(TestHybridClass other)86     public native boolean copy1(TestHybridClass other);
87 
copy2(TestHybridClass other)88     public native boolean copy2(TestHybridClass other);
89 
oops()90     public native void oops();
91 
setGlobal(String s)92     public native void setGlobal(String s);
93 
getGlobal1()94     public native String getGlobal1();
95 
getGlobal2()96     public native String getGlobal2();
97 
makeWithTwo()98     public static native TestHybridClass makeWithTwo();
99 
makeWithThree()100     public static native TestHybridClass makeWithThree();
101 
autoconvertMany()102     public static native void autoconvertMany();
103   }
104 
105   @Test
testHybridClass()106   public void testHybridClass() {
107     TestHybridClass thc1 = new TestHybridClass();
108     assertThat(thc1.getInt()).isEqualTo(0);
109     assertThat(thc1.getString()).isEqualTo("");
110 
111     thc1.setBoth(1, "one");
112     assertThat(thc1.getInt()).isEqualTo(1);
113     assertThat(thc1.getString()).isEqualTo("one");
114 
115     TestHybridClass thc2 = TestHybridClass.makeWithTwo();
116     assertThat(thc2.getInt()).isEqualTo(2);
117     assertThat(thc2.getString()).isEqualTo("two");
118 
119     thc2.doneUsingIt();
120 
121     thrown.expect(NullPointerException.class);
122     thc2.getInt();
123   }
124 
125   @Test
testHybridAutoconversion()126   public void testHybridAutoconversion() {
127     TestHybridClass thc3 = TestHybridClass.makeWithThree();
128     assertThat(thc3.copy1(new TestHybridClass(3, "three", false))).isTrue();
129     assertThat(thc3.getInt()).isEqualTo(3);
130     assertThat(thc3.getString()).isEqualTo("three");
131 
132     TestHybridClass thc4 = new TestHybridClass();
133     thc4.copy1(new TestHybridClass("four", 4, false));
134     assertThat(thc4.getInt()).isEqualTo(4);
135     assertThat(thc4.getString()).isEqualTo("four");
136     assertThat(thc4.getCharString()).isEqualTo("four");
137 
138     TestHybridClass thc5 = new TestHybridClass();
139     assertThat(thc5.copy2(new TestHybridClass(5, "five", false))).isTrue();
140     assertThat(thc5.getInt()).isEqualTo(5);
141     assertThat(thc5.getString()).isEqualTo("five");
142   }
143 
144   @Test
testReturnGlobalRef()145   public void testReturnGlobalRef() {
146     TestHybridClass thc = new TestHybridClass();
147     thc.setGlobal("global_ref");
148     assertThat(thc.getGlobal1()).isEqualTo("global_ref");
149     assertThat(thc.getGlobal2()).isEqualTo("global_ref");
150   }
151 
152   @Test
testLocalLeak()153   public void testLocalLeak() {
154     TestHybridClass.autoconvertMany();
155   }
156 
157   @Test
testExceptionMapping()158   public void testExceptionMapping() {
159     TestHybridClass thc1 = new TestHybridClass();
160     thrown.expect(ArrayStoreException.class);
161     thc1.oops();
162   }
163 
164   abstract static class AbstractTestHybrid {
165     @DoNotStrip private final HybridData mHybridData;
166 
167     private int mAbstractNum;
168 
AbstractTestHybrid(HybridData hybridData, int an)169     protected AbstractTestHybrid(HybridData hybridData, int an) {
170       mHybridData = hybridData;
171       mAbstractNum = an;
172     }
173 
abstractNum()174     public int abstractNum() {
175       return mAbstractNum;
176     }
177 
nativeNum()178     public native int nativeNum();
179 
concreteNum()180     public abstract int concreteNum();
181 
sum()182     public abstract int sum();
183   }
184 
185   static class ConcreteTestHybrid extends AbstractTestHybrid {
ConcreteTestHybrid(int an, int nn, int cn)186     public ConcreteTestHybrid(int an, int nn, int cn) {
187       super(initHybrid(nn, cn), an);
188     }
189 
initHybrid(int nn, int cn)190     private static native HybridData initHybrid(int nn, int cn);
191 
192     // overrides can be native
193     @Override
concreteNum()194     public native int concreteNum();
195 
196     // overrides can be java
197     @Override
sum()198     public int sum() {
199       return nativeNum() + abstractNum() + concreteNum();
200     }
201   }
202 
203   @Test
testHybridInheritance()204   public void testHybridInheritance() {
205     AbstractTestHybrid ath = new ConcreteTestHybrid(1, 2, 3);
206     assertThat(ath.abstractNum()).isEqualTo(1);
207     assertThat(ath.nativeNum()).isEqualTo(2);
208     assertThat(ath.concreteNum()).isEqualTo(3);
209     assertThat(ath.sum()).isEqualTo(6);
210   }
211 
cxxTestInheritance(AbstractTestHybrid ath)212   public static native boolean cxxTestInheritance(AbstractTestHybrid ath);
213 
makeAbstractHybrid()214   public static native AbstractTestHybrid makeAbstractHybrid();
215 
216   @Test
testHybridCxx()217   public void testHybridCxx() {
218     AbstractTestHybrid ath = new ConcreteTestHybrid(4, 5, 6);
219     assertThat(cxxTestInheritance(ath)).isTrue();
220 
221     AbstractTestHybrid ath2 = makeAbstractHybrid();
222     assertThat(ath2 instanceof ConcreteTestHybrid).isTrue();
223     assertThat(ath2.abstractNum()).isEqualTo(7);
224     assertThat(ath2.nativeNum()).isEqualTo(8);
225     assertThat(ath2.concreteNum()).isEqualTo(9);
226     assertThat(ath2.sum()).isEqualTo(24);
227   }
228 
229   static class Base {}
230 
231   static class Derived extends Base {
232     @DoNotStrip private final HybridData mHybridData;
233 
Derived(HybridData hybridData)234     private Derived(HybridData hybridData) {
235       mHybridData = hybridData;
236     }
237   }
238 
cxxTestDerivedJavaClass()239   public static native boolean cxxTestDerivedJavaClass();
240 
241   @Test
testDerivedJavaClassCxx()242   public void testDerivedJavaClassCxx() {
243     assertThat(cxxTestDerivedJavaClass()).isTrue();
244   }
245 
246   static class TestHybridClassBase extends HybridClassBase {
initHybrid()247     protected native void initHybrid();
248 
initHybrid(int i)249     private native void initHybrid(int i);
250 
TestHybridClassBase()251     protected TestHybridClassBase() {
252       // No initHybrid() here!
253       // Otherwise factory construction will set native pointer twice and process will crash.
254     }
255 
TestHybridClassBase(int i)256     public TestHybridClassBase(int i) {
257       initHybrid(i);
258     }
259 
260     // Some C++ methods.
setInt(int i)261     public native void setInt(int i);
262 
getInt()263     public native int getInt();
264 
makeWithThree()265     public static native TestHybridClassBase makeWithThree();
266   }
267 
268   static class TestHybridClassBaseDefaultCtor extends TestHybridClassBase {
TestHybridClassBaseDefaultCtor()269     public TestHybridClassBaseDefaultCtor() {
270       initHybrid();
271     }
272   }
273 
274   @Test
testHybridBaseDefaultCtor()275   public void testHybridBaseDefaultCtor() {
276     TestHybridClassBaseDefaultCtor base = new TestHybridClassBaseDefaultCtor();
277     assertThat(base.getInt()).isZero();
278 
279     base.setInt(58);
280     assertThat(base.getInt()).isEqualTo(58);
281   }
282 
283   @Test
testHybridBaseConstructorArgs()284   public void testHybridBaseConstructorArgs() {
285     TestHybridClassBase base = new TestHybridClassBase(42);
286     assertThat(base.getInt()).isEqualTo(42);
287   }
288 
289   @Test
testHybridBaseFactoryConstruction()290   public void testHybridBaseFactoryConstruction() {
291     TestHybridClassBase base = TestHybridClassBase.makeWithThree();
292     assertThat(base.getInt()).isEqualTo(3);
293   }
294 
295   static class Destroyable {
296     @DoNotStrip private final HybridData mHybridData;
297 
Destroyable(HybridData hybridData)298     private Destroyable(HybridData hybridData) {
299       mHybridData = hybridData;
300     }
301   }
302 
cxxTestHybridDestruction()303   public static native boolean cxxTestHybridDestruction();
304 
305   @Test
testHybridDestuction()306   public void testHybridDestuction() {
307     assertThat(cxxTestHybridDestruction()).isTrue();
308   }
309 }
310