1 /*
2  * Copyright 2014 Google Inc. All rights reserved.
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 import MyGame.Example.*
18 import com.google.flatbuffers.ByteBufferUtil
19 import com.google.flatbuffers.FlatBufferBuilder
20 import NamespaceA.*
21 import NamespaceA.NamespaceB.*
22 import NamespaceA.NamespaceB.TableInNestedNS
23 import java.io.File
24 import java.io.FileOutputStream
25 import java.io.InputStream
26 import java.io.RandomAccessFile
27 import java.nio.ByteBuffer
28 import java.nio.ByteOrder
29 import java.nio.channels.FileChannel
30 
31 import com.google.flatbuffers.Constants.SIZE_PREFIX_LENGTH
32 
33 @kotlin.ExperimentalUnsignedTypes
34 class KotlinTest {
35 
36   companion object {
37     @JvmStatic
mainnull38     fun main(args: Array<String>) {
39 
40         // First, let's test reading a FlatBuffer generated by C++ code:
41         // This file was generated from monsterdata_test.json
42 
43         val data = RandomAccessFile(File("monsterdata_test.mon"), "r").use {
44             val temp = ByteArray(it.length().toInt())
45             it.readFully(temp)
46             temp
47         }
48 
49         // Now test it:
50 
51         val bb = ByteBuffer.wrap(data)
52         TestBuffer(bb)
53 
54         // Second, let's create a FlatBuffer from scratch in Java, and test it also.
55         // We use an initial size of 1 to exercise the reallocation algorithm,
56         // normally a size larger than the typical FlatBuffer you generate would be
57         // better for performance.
58         val fbb = FlatBufferBuilder(1)
59 
60         TestBuilderBasics(fbb, true)
61         TestBuilderBasics(fbb, false)
62 
63         TestExtendedBuffer(fbb.dataBuffer().asReadOnlyBuffer())
64 
65         TestNamespaceNesting()
66 
67         TestNestedFlatBuffer()
68 
69         TestCreateByteVector()
70 
71         TestCreateUninitializedVector()
72 
73         TestByteBufferFactory()
74 
75         TestSizedInputStream()
76 
77         TestVectorOfUnions()
78 
79         println("FlatBuffers test: completed successfully")
80     }
81 
TestEnumsnull82     fun TestEnums() {
83         assert(Color.name(Color.Red.toInt()) == "Red")
84         assert(Color.name(Color.Blue.toInt()) == "Blue")
85         assert(Any_.name(Any_.NONE.toInt()) == "NONE")
86         assert(Any_.name(Any_.Monster.toInt()) == "Monster")
87     }
88 
TestBuffernull89     fun TestBuffer(bb: ByteBuffer) {
90         assert(Monster.MonsterBufferHasIdentifier(bb) == true)
91 
92         val monster = Monster.getRootAsMonster(bb)
93 
94         assert(monster.hp == 80.toShort())
95         assert(monster.mana == 150.toShort())  // default
96 
97         assert(monster.name == "MyMonster")
98         // monster.friendly() // can't access, deprecated
99 
100         val pos = monster.pos!!
101         assert(pos.x == 1.0f)
102         assert(pos.y == 2.0f)
103         assert(pos.z == 3.0f)
104         assert(pos.test1 == 3.0)
105         // issue: int != byte
106         assert(pos.test2 == Color.Green)
107         val t = pos.test3!!
108         assert(t.a == 5.toShort())
109         assert(t.b == 6.toByte())
110 
111         assert(monster.testType == Any_.Monster)
112         val monster2 = Monster()
113         assert(monster.test(monster2) != null == true)
114         assert(monster2.name == "Fred")
115 
116         assert(monster.inventoryLength == 5)
117         var invsum = 0u
118         for (i in 0 until monster.inventoryLength)
119             invsum += monster.inventory(i)
120         assert(invsum == 10u)
121 
122         // Alternative way of accessing a vector:
123         val ibb = monster.inventoryAsByteBuffer
124         invsum = 0u
125         while (ibb.position() < ibb.limit())
126             invsum += ibb.get().toUInt()
127         assert(invsum == 10u)
128 
129 
130         val test_0 = monster.test4(0)!!
131         val test_1 = monster.test4(1)!!
132         assert(monster.test4Length == 2)
133         assert(test_0.a + test_0.b + test_1.a + test_1.b == 100)
134 
135         assert(monster.testarrayofstringLength == 2)
136         assert(monster.testarrayofstring(0) == "test1")
137         assert(monster.testarrayofstring(1) == "test2")
138 
139         assert(monster.testbool == true)
140     }
141 
142     // this method checks additional fields not present in the binary buffer read from file
143     // these new tests are performed on top of the regular tests
TestExtendedBuffernull144     fun TestExtendedBuffer(bb: ByteBuffer) {
145         TestBuffer(bb)
146 
147         val monster = Monster.getRootAsMonster(bb)
148 
149         assert(monster.testhashu32Fnv1 == (1u + Integer.MAX_VALUE.toUInt()))
150     }
151 
TestNamespaceNestingnull152     fun TestNamespaceNesting() {
153         // reference / manipulate these to verify compilation
154         val fbb = FlatBufferBuilder(1)
155 
156         TableInNestedNS.startTableInNestedNS(fbb)
157         TableInNestedNS.addFoo(fbb, 1234)
158         val nestedTableOff = TableInNestedNS.endTableInNestedNS(fbb)
159 
160         TableInFirstNS.startTableInFirstNS(fbb)
161         TableInFirstNS.addFooTable(fbb, nestedTableOff)
162     }
163 
TestNestedFlatBuffernull164     fun TestNestedFlatBuffer() {
165         val nestedMonsterName = "NestedMonsterName"
166         val nestedMonsterHp: Short = 600
167         val nestedMonsterMana: Short = 1024
168 
169         var fbb1: FlatBufferBuilder? = FlatBufferBuilder(16)
170         val str1 = fbb1!!.createString(nestedMonsterName)
171         Monster.startMonster(fbb1)
172         Monster.addName(fbb1, str1)
173         Monster.addHp(fbb1, nestedMonsterHp)
174         Monster.addMana(fbb1, nestedMonsterMana)
175         val monster1 = Monster.endMonster(fbb1)
176         Monster.finishMonsterBuffer(fbb1, monster1)
177         val fbb1Bytes = fbb1.sizedByteArray()
178 
179         val fbb2 = FlatBufferBuilder(16)
180         val str2 = fbb2.createString("My Monster")
181         val nestedBuffer = Monster.createTestnestedflatbufferVector(fbb2, fbb1Bytes.asUByteArray())
182         Monster.startMonster(fbb2)
183         Monster.addName(fbb2, str2)
184         Monster.addHp(fbb2, 50.toShort())
185         Monster.addMana(fbb2, 32.toShort())
186         Monster.addTestnestedflatbuffer(fbb2, nestedBuffer)
187         val monster = Monster.endMonster(fbb2)
188         Monster.finishMonsterBuffer(fbb2, monster)
189 
190         // Now test the data extracted from the nested buffer
191         val mons = Monster.getRootAsMonster(fbb2.dataBuffer())
192         val nestedMonster = mons.testnestedflatbufferAsMonster!!
193 
194         assert(nestedMonsterMana == nestedMonster.mana)
195         assert(nestedMonsterHp == nestedMonster.hp)
196         assert(nestedMonsterName == nestedMonster.name)
197     }
198 
TestCreateByteVectornull199     fun TestCreateByteVector() {
200         val fbb = FlatBufferBuilder(16)
201         val str = fbb.createString("MyMonster")
202         val inventory = byteArrayOf(0, 1, 2, 3, 4)
203         val vec = fbb.createByteVector(inventory)
204         Monster.startMonster(fbb)
205         Monster.addInventory(fbb, vec)
206         Monster.addName(fbb, str)
207         val monster1 = Monster.endMonster(fbb)
208         Monster.finishMonsterBuffer(fbb, monster1)
209         val monsterObject = Monster.getRootAsMonster(fbb.dataBuffer())
210 
211         assert(monsterObject.inventory(1) == inventory[1].toUByte())
212         assert(monsterObject.inventoryLength == inventory.size)
213         assert(ByteBuffer.wrap(inventory) == monsterObject.inventoryAsByteBuffer)
214     }
215 
TestCreateUninitializedVectornull216     fun TestCreateUninitializedVector() {
217         val fbb = FlatBufferBuilder(16)
218         val str = fbb.createString("MyMonster")
219         val inventory = byteArrayOf(0, 1, 2, 3, 4)
220         val bb = fbb.createUnintializedVector(1, inventory.size, 1)
221         for (i in inventory) {
222             bb.put(i)
223         }
224         val vec = fbb.endVector()
225         Monster.startMonster(fbb)
226         Monster.addInventory(fbb, vec)
227         Monster.addName(fbb, str)
228         val monster1 = Monster.endMonster(fbb)
229         Monster.finishMonsterBuffer(fbb, monster1)
230         val monsterObject = Monster.getRootAsMonster(fbb.dataBuffer())
231 
232         assert(monsterObject.inventory(1) == inventory[1].toUByte())
233         assert(monsterObject.inventoryLength == inventory.size)
234         assert(ByteBuffer.wrap(inventory) == monsterObject.inventoryAsByteBuffer)
235     }
236 
TestByteBufferFactorynull237     fun TestByteBufferFactory() {
238         class MappedByteBufferFactory : FlatBufferBuilder.ByteBufferFactory() {
239             override fun newByteBuffer(capacity: Int): ByteBuffer? {
240                 var bb: ByteBuffer?
241                 try {
242                     bb = RandomAccessFile("javatest.bin", "rw").channel.map(
243                         FileChannel.MapMode.READ_WRITE,
244                         0,
245                         capacity.toLong()
246                     ).order(ByteOrder.LITTLE_ENDIAN)
247                 } catch (e: Throwable) {
248                     println("FlatBuffers test: couldn't map ByteBuffer to a file")
249                     bb = null
250                 }
251 
252                 return bb
253             }
254         }
255 
256         val fbb = FlatBufferBuilder(1, MappedByteBufferFactory())
257 
258         TestBuilderBasics(fbb, false)
259     }
260 
TestSizedInputStreamnull261     fun TestSizedInputStream() {
262         // Test on default FlatBufferBuilder that uses HeapByteBuffer
263         val fbb = FlatBufferBuilder(1)
264 
265         TestBuilderBasics(fbb, false)
266 
267         val `in` = fbb.sizedInputStream()
268         val array = fbb.sizedByteArray()
269         var count = 0
270         var currentVal = 0
271 
272         while (currentVal != -1 && count < array.size) {
273             try {
274                 currentVal = `in`.read()
275             } catch (e: java.io.IOException) {
276                 println("FlatBuffers test: couldn't read from InputStream")
277                 return
278             }
279 
280             assert(currentVal.toByte() == array[count])
281             count++
282         }
283         assert(count == array.size)
284     }
285 
TestBuilderBasicsnull286     fun TestBuilderBasics(fbb: FlatBufferBuilder, sizePrefix: Boolean) {
287         val names = intArrayOf(fbb.createString("Frodo"), fbb.createString("Barney"), fbb.createString("Wilma"))
288         val off = IntArray(3)
289         Monster.startMonster(fbb)
290         Monster.addName(fbb, names[0])
291         off[0] = Monster.endMonster(fbb)
292         Monster.startMonster(fbb)
293         Monster.addName(fbb, names[1])
294         off[1] = Monster.endMonster(fbb)
295         Monster.startMonster(fbb)
296         Monster.addName(fbb, names[2])
297         off[2] = Monster.endMonster(fbb)
298         val sortMons = fbb.createSortedVectorOfTables(Monster(), off)
299 
300         // We set up the same values as monsterdata.json:
301 
302         val str = fbb.createString("MyMonster")
303 
304         val inv = Monster.createInventoryVector(fbb, byteArrayOf(0, 1, 2, 3, 4).asUByteArray())
305 
306         val fred = fbb.createString("Fred")
307         Monster.startMonster(fbb)
308         Monster.addName(fbb, fred)
309         val mon2 = Monster.endMonster(fbb)
310 
311         Monster.startTest4Vector(fbb, 2)
312         Test.createTest(fbb, 10.toShort(), 20.toByte())
313         Test.createTest(fbb, 30.toShort(), 40.toByte())
314         val test4 = fbb.endVector()
315 
316         val testArrayOfString =
317             Monster.createTestarrayofstringVector(fbb, intArrayOf(fbb.createString("test1"), fbb.createString("test2")))
318 
319         Monster.startMonster(fbb)
320         Monster.addPos(
321             fbb, Vec3.createVec3(
322                 fbb, 1.0f, 2.0f, 3.0f, 3.0,
323                 Color.Green, 5.toShort(), 6.toByte()
324             )
325         )
326         Monster.addHp(fbb, 80.toShort())
327         Monster.addName(fbb, str)
328         Monster.addInventory(fbb, inv)
329         Monster.addTestType(fbb, Any_.Monster)
330         Monster.addTest(fbb, mon2)
331         Monster.addTest4(fbb, test4)
332         Monster.addTestarrayofstring(fbb, testArrayOfString)
333         Monster.addTestbool(fbb, true)
334         Monster.addTesthashu32Fnv1(fbb, UInt.MAX_VALUE + 1u)
335         Monster.addTestarrayoftables(fbb, sortMons)
336         val mon = Monster.endMonster(fbb)
337 
338         if (sizePrefix) {
339             Monster.finishSizePrefixedMonsterBuffer(fbb, mon)
340         } else {
341             Monster.finishMonsterBuffer(fbb, mon)
342         }
343 
344         // Write the result to a file for debugging purposes:
345         // Note that the binaries are not necessarily identical, since the JSON
346         // parser may serialize in a slightly different order than the above
347         // Java code. They are functionally equivalent though.
348 
349         try {
350             val filename = "monsterdata_java_wire" + (if (sizePrefix) "_sp" else "") + ".mon"
351             val fc = FileOutputStream(filename).channel
352             fc.write(fbb.dataBuffer().duplicate())
353             fc.close()
354         } catch (e: java.io.IOException) {
355             println("FlatBuffers test: couldn't write file")
356             return
357         }
358 
359         // Test it:
360         var dataBuffer = fbb.dataBuffer()
361         if (sizePrefix) {
362             assert(
363                 ByteBufferUtil.getSizePrefix(dataBuffer) + SIZE_PREFIX_LENGTH ==
364                 dataBuffer.remaining()
365             )
366             dataBuffer = ByteBufferUtil.removeSizePrefix(dataBuffer)
367         }
368         TestExtendedBuffer(dataBuffer)
369 
370         // Make sure it also works with read only ByteBuffers. This is slower,
371         // since creating strings incurs an additional copy
372         // (see Table.__string).
373         TestExtendedBuffer(dataBuffer.asReadOnlyBuffer())
374 
375         TestEnums()
376 
377         //Attempt to mutate Monster fields and check whether the buffer has been mutated properly
378         // revert to original values after testing
379         val monster = Monster.getRootAsMonster(dataBuffer)
380 
381         // mana is optional and does not exist in the buffer so the mutation should fail
382         // the mana field should retain its default value
383         assert(monster.mutateMana(10.toShort()) == false)
384         assert(monster.mana == 150.toShort())
385 
386         // Accessing a vector of sorted by the key tables
387         assert(monster.testarrayoftables(0)!!.name == "Barney")
388         assert(monster.testarrayoftables(1)!!.name == "Frodo")
389         assert(monster.testarrayoftables(2)!!.name == "Wilma")
390 
391         // Example of searching for a table by the key
392         assert(monster.testarrayoftablesByKey("Frodo")!!.name == "Frodo")
393         assert(monster.testarrayoftablesByKey("Barney")!!.name == "Barney")
394         assert(monster.testarrayoftablesByKey("Wilma")!!.name == "Wilma")
395 
396         // testType is an existing field and mutating it should succeed
397         assert(monster.testType == Any_.Monster)
398         assert(monster.mutateTestType(Any_.NONE) == true)
399         assert(monster.testType == Any_.NONE)
400         assert(monster.mutateTestType(Any_.Monster) == true)
401         assert(monster.testType == Any_.Monster)
402 
403         //mutate the inventory vector
404         assert(monster.mutateInventory(0, 1u) == true)
405         assert(monster.mutateInventory(1, 2u) == true)
406         assert(monster.mutateInventory(2, 3u) == true)
407         assert(monster.mutateInventory(3, 4u) == true)
408         assert(monster.mutateInventory(4, 5u) == true)
409 
410         for (i in 0 until monster.inventoryLength) {
411             assert(monster.inventory(i) == (i.toUByte() + 1u).toUByte())
412         }
413 
414         //reverse mutation
415         assert(monster.mutateInventory(0, 0u) == true)
416         assert(monster.mutateInventory(1, 1u) == true)
417         assert(monster.mutateInventory(2, 2u) == true)
418         assert(monster.mutateInventory(3, 3u) == true)
419         assert(monster.mutateInventory(4, 4u) == true)
420 
421         // get a struct field and edit one of its fields
422         val pos = monster.pos!!
423         assert(pos.x == 1.0f)
424         pos.mutateX(55.0f)
425         assert(pos.x == 55.0f)
426         pos.mutateX(1.0f)
427         assert(pos.x == 1.0f)
428     }
429 
TestVectorOfUnionsnull430     fun TestVectorOfUnions() {
431         val fbb = FlatBufferBuilder()
432 
433         val swordAttackDamage = 1
434 
435         val characterVector = intArrayOf(Attacker.createAttacker(fbb, swordAttackDamage))
436 
437         val characterTypeVector = ubyteArrayOf(Character_.MuLan)
438 
439         Movie.finishMovieBuffer(
440             fbb,
441             Movie.createMovie(
442                 fbb,
443                 0u,
444                 0,
445                 Movie.createCharactersTypeVector(fbb, characterTypeVector),
446                 Movie.createCharactersVector(fbb, characterVector)
447             )
448         )
449 
450         val movie = Movie.getRootAsMovie(fbb.dataBuffer())
451 
452         assert(movie.charactersTypeLength == characterTypeVector.size)
453         assert(movie.charactersLength == characterVector.size)
454 
455         assert(movie.charactersType(0) == characterTypeVector[0])
456 
457         assert((movie.characters(Attacker(), 0) as Attacker).swordAttackDamage == swordAttackDamage)
458     }
459 }
460   }
461