1 @file:OptIn(ExperimentalCoroutinesApi::class)
2 
3 package expo.modules.kotlin.jni
4 
5 import com.google.common.truth.Truth
6 import expo.modules.kotlin.typedarray.Float32Array
7 import expo.modules.kotlin.typedarray.Float64Array
8 import expo.modules.kotlin.typedarray.Int16Array
9 import expo.modules.kotlin.typedarray.Int32Array
10 import expo.modules.kotlin.typedarray.Int8Array
11 import expo.modules.kotlin.typedarray.Uint16Array
12 import expo.modules.kotlin.typedarray.Uint32Array
13 import expo.modules.kotlin.typedarray.Uint8Array
14 import kotlinx.coroutines.ExperimentalCoroutinesApi
15 import org.junit.Test
16 import java.nio.ByteBuffer
17 import java.nio.ByteOrder
18 
19 internal class JavaScriptTypedArrayTest {
20 
21   @Test
<lambda>null22   fun kind_should_return_correct_type() = withJSIInterop {
23     val uint16Result = evaluateScript("new Uint16Array(4)")
24     val float32Result = evaluateScript("new Float32Array([1.2, 3.4])")
25 
26     Truth.assertThat(uint16Result.isTypedArray()).isTrue()
27     Truth.assertThat(float32Result.isTypedArray()).isTrue()
28 
29     val uintTypedArray = uint16Result.getTypedArray()
30     Truth.assertThat(uintTypedArray.kind).isEqualTo(TypedArrayKind.Uint16Array)
31 
32     val float32TypedArray = float32Result.getTypedArray()
33     Truth.assertThat(float32TypedArray.kind).isEqualTo(TypedArrayKind.Float32Array)
34   }
35 
36   @Test
<lambda>null37   fun should_be_able_access_elements_via_object_api() = withJSIInterop {
38     val typedArray = evaluateScript("new Float32Array([1.2, 3.4])").getTypedArray()
39 
40     val first = typedArray.getProperty("0").getDouble()
41     val second = typedArray.getProperty("1").getDouble()
42 
43     Truth.assertThat(first).isWithin(1.0e-6).of(1.2)
44     Truth.assertThat(second).isWithin(1.0e-6).of(3.4)
45   }
46 
47   @Test
<lambda>null48   fun should_be_convertible_to_direct_buffer_float32() = withJSIInterop {
49     val typedArray = evaluateScript("new Float32Array([1.2, 3.4])").getTypedArray()
50 
51     val buffer = typedArray.toDirectBuffer()
52 
53     Truth.assertThat(buffer.isDirect).isTrue()
54     Truth.assertThat(buffer.isReadOnly).isFalse()
55 
56     val first = buffer.getFloat(0)
57     val second = buffer.getFloat(4)
58 
59     Truth.assertThat(first).isEqualTo(1.2f)
60     Truth.assertThat(second).isEqualTo(3.4f)
61   }
62 
63   @Test
<lambda>null64   fun should_be_convertible_to_direct_buffer_int32() = withJSIInterop {
65     val typedArray = evaluateScript("new Int32Array([21, 31])").getTypedArray()
66 
67     val buffer = typedArray.toDirectBuffer()
68 
69     Truth.assertThat(buffer.isDirect).isTrue()
70     Truth.assertThat(buffer.isReadOnly).isFalse()
71 
72     val first = buffer.getInt(0)
73     val second = buffer.getInt(4)
74 
75     Truth.assertThat(first).isEqualTo(21)
76     Truth.assertThat(second).isEqualTo(31)
77   }
78 
79   @Test
<lambda>null80   fun should_be_convertible_to_direct_buffer_int8() = withJSIInterop {
81     val typedArray = evaluateScript("new Int8Array([21, 31])").getTypedArray()
82 
83     val buffer = typedArray.toDirectBuffer()
84 
85     Truth.assertThat(buffer.isDirect).isTrue()
86     Truth.assertThat(buffer.isReadOnly).isFalse()
87 
88     val first = buffer.get(0)
89     val second = buffer.get(1)
90 
91     Truth.assertThat(first).isEqualTo(21)
92     Truth.assertThat(second).isEqualTo(31)
93   }
94 
95   @Test
<lambda>null96   fun raw_read() = withJSIInterop {
97     val typedArray = evaluateScript("new Int32Array([21, 31])").getTypedArray()
98 
99     val firstBytes = ByteArray(4)
100     val secondBytes = ByteArray(4)
101     typedArray.read(firstBytes, 0, 4)
102     typedArray.read(secondBytes, 4, 4)
103 
104     val first = wrapBytes(firstBytes).int
105     val second = wrapBytes(secondBytes).int
106 
107     Truth.assertThat(first).isEqualTo(21)
108     Truth.assertThat(second).isEqualTo(31)
109   }
110 
111   @Test
<lambda>null112   fun raw_write() = withJSIInterop {
113     val typedArray = evaluateScript("new Int32Array([21, 31])").getTypedArray()
114 
115     val bytes = ByteArray(4)
116     wrapBytes(bytes).putInt(21345)
117 
118     typedArray.write(bytes, 4, 4)
119 
120     val first = typedArray.getProperty("0").getDouble().toInt()
121     val second = typedArray.getProperty("1").getDouble().toInt()
122 
123     Truth.assertThat(first).isEqualTo(21)
124     Truth.assertThat(second).isEqualTo(21345)
125   }
126 
127   @Test
<lambda>null128   fun int8_read() = withJSIInterop {
129     val rawTypedArray = evaluateScript("new Int8Array([-128, 127])").getTypedArray()
130     val typedArray = Int8Array(rawTypedArray)
131 
132     val first = typedArray[0]
133     val second = typedArray[1]
134 
135     Truth.assertThat(first).isEqualTo(-128)
136     Truth.assertThat(second).isEqualTo(127)
137   }
138 
139   @Test
<lambda>null140   fun int8_write() = withJSIInterop {
141     val rawTypedArray = evaluateScript("new Int8Array(2)").getTypedArray()
142     val typedArray = Int8Array(rawTypedArray)
143 
144     typedArray[0] = -128
145     typedArray[1] = 127
146 
147     val first = typedArray[0]
148     val second = typedArray[1]
149 
150     Truth.assertThat(first).isEqualTo(-128)
151     Truth.assertThat(second).isEqualTo(127)
152   }
153 
154   @Test
<lambda>null155   fun int16_read() = withJSIInterop {
156     val rawTypedArray = evaluateScript("new Int16Array([-32768, 32767])").getTypedArray()
157     val typedArray = Int16Array(rawTypedArray)
158 
159     val first = typedArray[0]
160     val second = typedArray[1]
161 
162     Truth.assertThat(first).isEqualTo(-32768)
163     Truth.assertThat(second).isEqualTo(32767)
164   }
165 
166   @Test
<lambda>null167   fun int16_write() = withJSIInterop {
168     val rawTypedArray = evaluateScript("new Int16Array([0, 0])").getTypedArray()
169     val typedArray = Int16Array(rawTypedArray)
170 
171     typedArray[0] = -32768
172     typedArray[1] = 32767
173 
174     val first = typedArray[0]
175     val second = typedArray[1]
176 
177     Truth.assertThat(first).isEqualTo(-32768)
178     Truth.assertThat(second).isEqualTo(32767)
179   }
180 
181   @Test
<lambda>null182   fun int32_read() = withJSIInterop {
183     val rawTypedArray = evaluateScript("new Int32Array([-2147483648, 2147483647])").getTypedArray()
184     val typedArray = Int32Array(rawTypedArray)
185 
186     val first = typedArray[0]
187     val second = typedArray[1]
188 
189     Truth.assertThat(first).isEqualTo(-2147483648)
190     Truth.assertThat(second).isEqualTo(2147483647)
191   }
192 
193   @Test
<lambda>null194   fun int32_write() = withJSIInterop {
195     val rawTypedArray = evaluateScript("new Int32Array(2)").getTypedArray()
196     val typedArray = Int32Array(rawTypedArray)
197 
198     typedArray[0] = -2147483648
199     typedArray[1] = 2147483647
200 
201     val first = typedArray[0]
202     val second = typedArray[1]
203 
204     Truth.assertThat(first).isEqualTo(-2147483648)
205     Truth.assertThat(second).isEqualTo(2147483647)
206   }
207 
208   @Test
<lambda>null209   fun uint8_read() = withJSIInterop {
210     val rawTypedArray = evaluateScript("new Uint8Array([1, 255])").getTypedArray()
211     val typedArray = Uint8Array(rawTypedArray)
212 
213     val first = typedArray[0]
214     val second = typedArray[1]
215 
216     Truth.assertThat(first).isEqualTo(1.toUByte())
217     Truth.assertThat(second).isEqualTo(255.toUByte())
218   }
219 
220   @Test
<lambda>null221   fun uint8_write() = withJSIInterop {
222     val rawTypedArray = evaluateScript("new Uint8Array(2)").getTypedArray()
223     val typedArray = Uint8Array(rawTypedArray)
224 
225     typedArray[0] = 10u
226     typedArray[1] = 255u
227 
228     val first = typedArray[0]
229     val second = typedArray[1]
230 
231     Truth.assertThat(first).isEqualTo(10.toUByte())
232     Truth.assertThat(second).isEqualTo(255.toUByte())
233   }
234 
235   @Test
<lambda>null236   fun uint8clamped_read() = withJSIInterop {
237     val rawTypedArray = evaluateScript("new Uint8ClampedArray([1, 255])").getTypedArray()
238     val typedArray = Uint8Array(rawTypedArray)
239 
240     val first = typedArray[0]
241     val second = typedArray[1]
242 
243     Truth.assertThat(first).isEqualTo(1.toUByte())
244     Truth.assertThat(second).isEqualTo(255.toUByte())
245   }
246 
247   @Test
<lambda>null248   fun uint8clamped_write() = withJSIInterop {
249     val rawTypedArray = evaluateScript("new Uint8ClampedArray(2)").getTypedArray()
250     val typedArray = Uint8Array(rawTypedArray)
251 
252     typedArray[0] = 10u
253     typedArray[1] = 255u
254 
255     val first = typedArray[0]
256     val second = typedArray[1]
257 
258     Truth.assertThat(first).isEqualTo(10.toUByte())
259     Truth.assertThat(second).isEqualTo(255.toUByte())
260   }
261 
262   @Test
<lambda>null263   fun uint16_read() = withJSIInterop {
264     val rawTypedArray = evaluateScript("new Uint16Array([123, 65535])").getTypedArray()
265     val typedArray = Uint16Array(rawTypedArray)
266 
267     val first = typedArray[0]
268     val second = typedArray[1]
269 
270     Truth.assertThat(first).isEqualTo(123.toUShort())
271     Truth.assertThat(second).isEqualTo(65535.toUShort())
272   }
273 
274   @Test
<lambda>null275   fun uint16_write() = withJSIInterop {
276     val rawTypedArray = evaluateScript("new Uint16Array(2)").getTypedArray()
277     val typedArray = Uint16Array(rawTypedArray)
278 
279     typedArray[0] = 123.toUShort()
280     typedArray[1] = 65535.toUShort()
281 
282     val first = typedArray[0]
283     val second = typedArray[1]
284 
285     Truth.assertThat(first).isEqualTo(123.toUShort())
286     Truth.assertThat(second).isEqualTo(65535.toUShort())
287   }
288 
289   @Test
<lambda>null290   fun uint32_read() = withJSIInterop {
291     val rawTypedArray = evaluateScript("new Uint32Array([123, 4294967295])").getTypedArray()
292     val typedArray = Uint32Array(rawTypedArray)
293 
294     val first = typedArray[0]
295     val second = typedArray[1]
296 
297     Truth.assertThat(first).isEqualTo(123.toUInt())
298     Truth.assertThat(second).isEqualTo(4294967295.toUInt())
299   }
300 
301   @Test
<lambda>null302   fun uint32_write() = withJSIInterop {
303     val rawTypedArray = evaluateScript("new Uint32Array(2)").getTypedArray()
304     val typedArray = Uint32Array(rawTypedArray)
305 
306     typedArray[0] = 123.toUInt()
307     typedArray[1] = 4294967295.toUInt()
308 
309     val first = typedArray[0]
310     val second = typedArray[1]
311 
312     Truth.assertThat(first).isEqualTo(123.toUInt())
313     Truth.assertThat(second).isEqualTo(4294967295.toUInt())
314   }
315 
316   @Test
<lambda>null317   fun float32_read() = withJSIInterop {
318     val rawTypedArray = evaluateScript("new Float32Array([-1.12432, 987765.312])").getTypedArray()
319     val typedArray = Float32Array(rawTypedArray)
320 
321     val first = typedArray[0]
322     val second = typedArray[1]
323 
324     Truth.assertThat(first).isEqualTo(-1.12432f)
325     Truth.assertThat(second).isEqualTo(987765.312f)
326   }
327 
328   @Test
<lambda>null329   fun float32_write() = withJSIInterop {
330     val rawTypedArray = evaluateScript("new Float32Array(2)").getTypedArray()
331     val typedArray = Float32Array(rawTypedArray)
332 
333     typedArray[0] = -1.12432f
334     typedArray[1] = 987765.312f
335 
336     val first = typedArray[0]
337     val second = typedArray[1]
338 
339     Truth.assertThat(first).isEqualTo(-1.12432f)
340     Truth.assertThat(second).isEqualTo(987765.312f)
341   }
342 
343   @Test
<lambda>null344   fun float64_read() = withJSIInterop {
345     val rawTypedArray = evaluateScript("new Float64Array([-1.12432, 987765.312])").getTypedArray()
346     val typedArray = Float64Array(rawTypedArray)
347 
348     val first = typedArray[0]
349     val second = typedArray[1]
350 
351     Truth.assertThat(first).isEqualTo(-1.12432)
352     Truth.assertThat(second).isEqualTo(987765.312)
353   }
354 
355   @Test
<lambda>null356   fun float64_write() = withJSIInterop {
357     val rawTypedArray = evaluateScript("new Float64Array(2)").getTypedArray()
358     val typedArray = Float64Array(rawTypedArray)
359 
360     typedArray[0] = -1.12432
361     typedArray[1] = 987765.312
362 
363     val first = typedArray[0]
364     val second = typedArray[1]
365 
366     Truth.assertThat(first).isEqualTo(-1.12432)
367     Truth.assertThat(second).isEqualTo(987765.312)
368   }
369 
370   @Test
<lambda>null371   fun length_should_return_correct_value() = withJSIInterop {
372     val rawTypedArray = evaluateScript("new Float64Array(16)").getTypedArray()
373     val typedArray = Float64Array(rawTypedArray)
374 
375     Truth.assertThat(typedArray.length).isEqualTo(16)
376   }
377 
wrapBytesnull378   private fun wrapBytes(array: ByteArray): ByteBuffer = ByteBuffer.wrap(array).order(ByteOrder.nativeOrder())
379 }
380