1 /*
2  * Copyright 2021 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 XCTest
18 @testable import FlatBuffers
19 
20 final class FlatBuffersUnionTests: XCTestCase {
21 
testCreateMonstornull22   func testCreateMonstor() {
23 
24     var b = FlatBufferBuilder(initialSize: 20)
25     let dmg: Int16 = 5
26     let str = "Axe"
27     let axe = b.create(string: str)
28     let weapon = Weapon.createWeapon(builder: &b, offset: axe, dmg: dmg)
29     let weapons = b.createVector(ofOffsets: [weapon])
30     let root = LocalMonster.createMonster(
31       builder: &b,
32       offset: weapons,
33       equipment: .Weapon,
34       equippedOffset: weapon.o)
35     b.finish(offset: root)
36     let buffer = b.sizedByteArray
37     // swiftformat:disable all
38     XCTAssertEqual(buffer, [16, 0, 0, 0, 0, 0, 10, 0, 16, 0, 8, 0, 7, 0, 12, 0, 10, 0, 0, 0, 0, 0, 0, 1, 8, 0, 0, 0, 20, 0, 0, 0, 1, 0, 0, 0, 12, 0, 0, 0, 8, 0, 12, 0, 8, 0, 6, 0, 8, 0, 0, 0, 0, 0, 5, 0, 4, 0, 0, 0, 3, 0, 0, 0, 65, 120, 101, 0])
39     // swiftformat:enable all
40     let monster = LocalMonster.getRootAsMonster(bb: ByteBuffer(bytes: buffer))
41     XCTAssertEqual(monster.weapon(at: 0)?.dmg, dmg)
42     XCTAssertEqual(monster.weapon(at: 0)?.name, str)
43     XCTAssertEqual(monster.weapon(at: 0)?.nameVector, [65, 120, 101])
44     let p: Weapon? = monster.equiped()
45     XCTAssertEqual(p?.dmg, dmg)
46     XCTAssertEqual(p?.name, str)
47     XCTAssertEqual(p?.nameVector, [65, 120, 101])
48   }
49 
testEndTableFinishnull50   func testEndTableFinish() {
51     var builder = FlatBufferBuilder(initialSize: 20)
52     let sword = builder.create(string: "Sword")
53     let axe = builder.create(string: "Axe")
54     let weaponOne = Weapon.createWeapon(
55       builder: &builder,
56       offset: sword,
57       dmg: 3)
58     let weaponTwo = Weapon.createWeapon(builder: &builder, offset: axe, dmg: 5)
59     let name = builder.create(string: "Orc")
60     let inventory: [UInt8] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
61     let inv = builder.createVector(inventory, size: 10)
62     let weapons = builder.createVector(ofOffsets: [weaponOne, weaponTwo])
63     let path = builder.createVector(ofStructs: [
64       Vec(x: 4.0, y: 5.0, z: 6.0),
65       Vec(x: 1.0, y: 2.0, z: 3.0),
66     ])
67     let orc = FinalMonster.createMonster(
68       builder: &builder,
69       position: Vec(x: 1, y: 2, z: 3),
70       hp: 300,
71       name: name,
72       inventory: inv,
73       color: .red,
74       weapons: weapons,
75       equipment: .Weapon,
76       equippedOffset: weaponTwo,
77       path: path)
78     builder.finish(offset: orc)
79     // swiftformat:disable all
80     XCTAssertEqual(builder.sizedByteArray, [32, 0, 0, 0, 0, 0, 26, 0, 48, 0, 36, 0, 0, 0, 34, 0, 28, 0, 0, 0, 24, 0, 23, 0, 16, 0, 15, 0, 8, 0, 4, 0, 26, 0, 0, 0, 44, 0, 0, 0, 104, 0, 0, 0, 0, 0, 0, 1, 60, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 76, 0, 0, 0, 0, 0, 44, 1, 0, 0, 128, 63, 0, 0, 0, 64, 0, 0, 64, 64, 2, 0, 0, 0, 0, 0, 128, 64, 0, 0, 160, 64, 0, 0, 192, 64, 0, 0, 128, 63, 0, 0, 0, 64, 0, 0, 64, 64, 2, 0, 0, 0, 52, 0, 0, 0, 28, 0, 0, 0, 10, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 3, 0, 0, 0, 79, 114, 99, 0, 244, 255, 255, 255, 0, 0, 5, 0, 24, 0, 0, 0, 8, 0, 12, 0, 8, 0, 6, 0, 8, 0, 0, 0, 0, 0, 3, 0, 12, 0, 0, 0, 3, 0, 0, 0, 65, 120, 101, 0, 5, 0, 0, 0, 83, 119, 111, 114, 100, 0, 0, 0])
81     // swiftformat:enable all
82   }
83 
testEnumVectornull84   func testEnumVector() {
85     let vectorOfEnums: [ColorsNameSpace.RGB] = [.blue, .green]
86 
87     var builder = FlatBufferBuilder(initialSize: 1)
88     let off = builder.createVector(vectorOfEnums)
89     let start = ColorsNameSpace.Monster.startMonster(&builder)
90     ColorsNameSpace.Monster.add(colors: off, &builder)
91     let end = ColorsNameSpace.Monster.endMonster(&builder, start: start)
92     builder.finish(offset: end)
93     // swiftformat:disable all
94     XCTAssertEqual(builder.sizedByteArray, [12, 0, 0, 0, 0, 0, 6, 0, 8, 0, 4, 0, 6, 0, 0, 0, 4, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 1, 0, 0, 0])
95     // swiftformat:enable all
96     let monster = ColorsNameSpace.Monster.getRootAsMonster(bb: builder.buffer)
97     XCTAssertEqual(monster.colorsCount, 2)
98     XCTAssertEqual(monster.colors(at: 0), .blue)
99     XCTAssertEqual(monster.colors(at: 1), .green)
100   }
101 
testUnionVectornull102   func testUnionVector() {
103     var fb = FlatBufferBuilder()
104 
105     let swordDmg: Int32 = 8
106     let attackStart = Attacker.startAttacker(&fb)
107     Attacker.add(swordAttackDamage: swordDmg, &fb)
108     let attack = Attacker.endAttacker(&fb, start: attackStart)
109 
110     let characterType: [Character] = [.belle, .mulan, .bookfan]
111 
112     let characters = [
113       fb.create(struct: BookReader(booksRead: 7)),
114       attack,
115       fb.create(struct: BookReader(booksRead: 2)),
116     ]
117     let types = fb.createVector(characterType)
118     let characterVector = fb.createVector(ofOffsets: characters)
119     let end = Movie.createMovie(
120       &fb,
121       charactersTypeVectorOffset: types,
122       charactersVectorOffset: characterVector)
123     Movie.finish(&fb, end: end)
124 
125     var movie = Movie.getRootAsMovie(bb: fb.buffer)
126     XCTAssertEqual(movie.charactersTypeCount, Int32(characterType.count))
127     XCTAssertEqual(movie.charactersCount, Int32(characters.count))
128 
129     for i in 0..<movie.charactersTypeCount {
130       XCTAssertEqual(movie.charactersType(at: i), characterType[Int(i)])
131     }
132 
133     XCTAssertEqual(
134       movie.characters(at: 0, type: BookReader_Mutable.self)?.booksRead,
135       7)
136     XCTAssertEqual(
137       movie.characters(at: 1, type: Attacker.self)?.swordAttackDamage,
138       swordDmg)
139     XCTAssertEqual(
140       movie.characters(at: 2, type: BookReader_Mutable.self)?.booksRead,
141       2)
142 
143     var objc: MovieT? = movie.unpack()
144     XCTAssertEqual(
145       movie.charactersTypeCount,
146       Int32(objc?.characters.count ?? 0))
147     XCTAssertEqual(
148       movie.characters(at: 0, type: BookReader_Mutable.self)?.booksRead,
149       (objc?.characters[0]?.value as? BookReader)?.booksRead)
150     fb.clear()
151     let newMovie = Movie.pack(&fb, obj: &objc)
152     fb.finish(offset: newMovie)
153 
154     let packedMovie = Movie.getRootAsMovie(bb: fb.buffer)
155 
156     XCTAssertEqual(
157       packedMovie.characters(at: 0, type: BookReader_Mutable.self)?.booksRead,
158       movie.characters(at: 0, type: BookReader_Mutable.self)?.booksRead)
159     XCTAssertEqual(
160       packedMovie.characters(at: 1, type: Attacker.self)?.swordAttackDamage,
161       movie.characters(at: 1, type: Attacker.self)?.swordAttackDamage)
162     XCTAssertEqual(
163       packedMovie.characters(at: 2, type: BookReader_Mutable.self)?.booksRead,
164       movie.characters(at: 2, type: BookReader_Mutable.self)?.booksRead)
165   }
166 
testStringUnionnull167   func testStringUnion() {
168     let string = "Awesome \\\\t\t\nstring!"
169     var fb = FlatBufferBuilder()
170     let stringOffset = fb.create(string: string)
171     let characterType: [Character] = [.bookfan, .other]
172 
173     let characters = [
174       fb.create(struct: BookReader(booksRead: 7)),
175       stringOffset,
176     ]
177     let types = fb.createVector(characterType)
178     let characterVector = fb.createVector(ofOffsets: characters)
179 
180     let end = Movie.createMovie(
181       &fb,
182       mainCharacterType: .other,
183       mainCharacterOffset: Offset(offset: stringOffset.o),
184       charactersTypeVectorOffset: types,
185       charactersVectorOffset: characterVector)
186     Movie.finish(&fb, end: end)
187 
188     var movie = Movie.getRootAsMovie(bb: fb.sizedBuffer)
189     XCTAssertEqual(movie.mainCharacter(type: String.self), string)
190     XCTAssertEqual(
191       movie.characters(at: 0, type: BookReader_Mutable.self)?.booksRead,
192       7)
193     XCTAssertEqual(movie.characters(at: 1, type: String.self), string)
194 
195     var objc: MovieT? = movie.unpack()
196     XCTAssertEqual(objc?.mainCharacter?.value as? String, string)
197     XCTAssertEqual((objc?.characters[0]?.value as? BookReader)?.booksRead, 7)
198     XCTAssertEqual(objc?.characters[1]?.value as? String, string)
199     fb.clear()
200     let newMovie = Movie.pack(&fb, obj: &objc)
201     fb.finish(offset: newMovie)
202 
203     let packedMovie = Movie.getRootAsMovie(bb: fb.buffer)
204     XCTAssertEqual(packedMovie.mainCharacter(type: String.self), string)
205     XCTAssertEqual(
206       packedMovie.characters(at: 0, type: BookReader_Mutable.self)?.booksRead,
207       7)
208     XCTAssertEqual(packedMovie.characters(at: 1, type: String.self), string)
209   }
210 
testEncodingnull211   func testEncoding() {
212     let string = "Awesome \\\\t\t\nstring!"
213     var fb = FlatBufferBuilder()
214 
215     let stringOffset = fb.create(string: string)
216 
217     let swordDmg: Int32 = 8
218     let attackStart = Attacker.startAttacker(&fb)
219     Attacker.add(swordAttackDamage: swordDmg, &fb)
220     let attack = Attacker.endAttacker(&fb, start: attackStart)
221 
222     let characterType: [Character] = [.belle, .mulan, .bookfan, .other]
223 
224     let characters = [
225       fb.create(struct: BookReader(booksRead: 7)),
226       attack,
227       fb.create(struct: BookReader(booksRead: 2)),
228       stringOffset,
229     ]
230     let types = fb.createVector(characterType)
231     let characterVector = fb.createVector(ofOffsets: characters)
232     let end = Movie.createMovie(
233       &fb,
234       charactersTypeVectorOffset: types,
235       charactersVectorOffset: characterVector)
236     Movie.finish(&fb, end: end)
237 
238     var sizedBuffer = fb.sizedBuffer
239     do {
240       let reader: Movie = try getCheckedRoot(byteBuffer: &sizedBuffer)
241       let encoder = JSONEncoder()
242       encoder.keyEncodingStrategy = .convertToSnakeCase
243       let data = try encoder.encode(reader)
244       XCTAssertEqual(data, jsonData.data(using: .utf8))
245     } catch {
246       XCTFail(error.localizedDescription)
247     }
248   }
249 
250   var jsonData: String {
251     "{\"characters_type\":[\"Belle\",\"MuLan\",\"BookFan\",\"Other\"],\"characters\":[{\"books_read\":7},{\"sword_attack_damage\":8},{\"books_read\":2},\"Awesome \\\\\\\\t\\t\\nstring!\"]}"
252   }
253 }
254 
255 public enum ColorsNameSpace {
256 
257   enum RGB: Int32, Enum {
258     typealias T = Int32
259     static var byteSize: Int { MemoryLayout<Int32>.size }
260     var value: Int32 { rawValue }
261     case red = 0, green = 1, blue = 2
262   }
263 
264   struct Monster: FlatBufferObject {
265     var __buffer: ByteBuffer! { _accessor.bb }
266 
267     private var _accessor: Table
getRootAsMonsternull268     static func getRootAsMonster(bb: ByteBuffer) -> Monster { Monster(Table(
269       bb: bb,
270       position: Int32(bb.read(def: UOffset.self, position: bb.reader)) +
271         Int32(bb.reader))) }
272 
273     init(_ t: Table) { _accessor = t }
274     init(_ bb: ByteBuffer, o: Int32) { _accessor = Table(bb: bb, position: o) }
275 
276     public var colorsCount: Int32 {
277       let o = _accessor.offset(4); return o == 0 ? 0 : _accessor
278         .vector(count: o) }
colorsnull279     public func colors(at index: Int32) -> ColorsNameSpace
280       .RGB?
281     { let o = _accessor.offset(4); return o == 0 ? ColorsNameSpace
282       .RGB(rawValue: 0)! : ColorsNameSpace.RGB(rawValue: _accessor.directRead(
283         of: Int32.self,
284         offset: _accessor.vector(at: o) + index * 4)) }
startMonsternull285     static func startMonster(_ fbb: inout FlatBufferBuilder) -> UOffset { fbb
286       .startTable(with: 1) }
addnull287     static func add(colors: Offset, _ fbb: inout FlatBufferBuilder) { fbb.add(
288       offset: colors,
289       at: 4)  }
290     static func endMonster(
291       _ fbb: inout FlatBufferBuilder,
292       start: UOffset)
293       -> Offset
294     { let end = Offset(offset: fbb.endTable(at: start)); return end
295     }
296   }
297 }
298 
299 
300 enum Equipment: Byte { case none, Weapon }
301 
302 enum Color3: Int8 { case red = 0, green, blue }
303 
304 struct FinalMonster {
305 
306   @inlinable
307   static func createMonster(
308     builder: inout FlatBufferBuilder,
309     position: Vec,
310     hp: Int16,
311     name: Offset,
312     inventory: Offset,
313     color: Color3,
314     weapons: Offset,
315     equipment: Equipment = .none,
316     equippedOffset: Offset,
317     path: Offset) -> Offset
318   {
319     let start = builder.startTable(with: 11)
320     builder.create(struct: position, position: 4)
321     builder.add(element: hp, def: 100, at: 8)
322     builder.add(offset: name, at: 10)
323     builder.add(offset: inventory, at: 14)
324     builder.add(element: color.rawValue, def: Color3.green.rawValue, at: 16)
325     builder.add(offset: weapons, at: 18)
326     builder.add(
327       element: equipment.rawValue,
328       def: Equipment.none.rawValue,
329       at: 20)
330     builder.add(offset: equippedOffset, at: 22)
331     builder.add(offset: path, at: 24)
332     return Offset(offset: builder.endTable(at: start))
333   }
334 }
335 
336 struct LocalMonster {
337 
338   private var __t: Table
339 
340   init(_ fb: ByteBuffer, o: Int32) { __t = Table(bb: fb, position: o) }
341   init(_ t: Table) { __t = t }
342 
weaponnull343   func weapon(at index: Int32) -> Weapon? { let o = __t
344     .offset(4); return o == 0 ? nil : Weapon.assign(
345       __t.indirect(__t.vector(at: o) + (index * 4)),
346       __t.bb) }
347 
equiped<T: FlatBufferObject>null348   func equiped<T: FlatBufferObject>() -> T? {
349     let o = __t.offset(8); return o == 0 ? nil : __t.union(o)
350   }
351 
getRootAsMonsternull352   static func getRootAsMonster(bb: ByteBuffer) -> LocalMonster {
353     LocalMonster(Table(
354       bb: bb,
355       position: Int32(bb.read(def: UOffset.self, position: 0))))
356   }
357 
358   @inlinable
359   static func createMonster(
360     builder: inout FlatBufferBuilder,
361     offset: Offset,
362     equipment: Equipment = .none,
363     equippedOffset: UOffset) -> Offset
364   {
365     let start = builder.startTable(with: 3)
366     builder.add(element: equippedOffset, def: 0, at: 8)
367     builder.add(offset: offset, at: 4)
368     builder.add(
369       element: equipment.rawValue,
370       def: Equipment.none.rawValue,
371       at: 6)
372     return Offset(offset: builder.endTable(at: start))
373   }
374 }
375 
376 struct Weapon: FlatBufferObject {
377 
378   var __buffer: ByteBuffer! { __t.bb }
379 
380   static let offsets: (name: VOffset, dmg: VOffset) = (4, 6)
381   private var __t: Table
382 
383   init(_ t: Table) { __t = t }
384   init(_ fb: ByteBuffer, o: Int32) { __t = Table(bb: fb, position: o)}
385 
386   var dmg: Int16 { let o = __t.offset(6); return o == 0 ? 0 : __t.readBuffer(
387     of: Int16.self,
388     at: o) }
389   var nameVector: [UInt8]? { __t.getVector(at: 4) }
390   var name: String? {
391     let o = __t.offset(4); return o == 0 ? nil : __t.string(at: o) }
392 
assignnull393   static func assign(_ i: Int32, _ bb: ByteBuffer) -> Weapon { Weapon(Table(
394     bb: bb,
395     position: i)) }
396 
397   @inlinable
398   static func createWeapon(
399     builder: inout FlatBufferBuilder,
400     offset: Offset,
401     dmg: Int16) -> Offset
402   {
403     let _start = builder.startTable(with: 2)
404     Weapon.add(builder: &builder, name: offset)
405     Weapon.add(builder: &builder, dmg: dmg)
406     return Weapon.end(builder: &builder, startOffset: _start)
407   }
408 
409   @inlinable
410   static func end(
411     builder: inout FlatBufferBuilder,
412     startOffset: UOffset) -> Offset
413   {
414     Offset(offset: builder.endTable(at: startOffset))
415   }
416 
417   @inlinable
addnull418   static func add(builder: inout FlatBufferBuilder, name: Offset) {
419     builder.add(offset: name, at: Weapon.offsets.name)
420   }
421 
422   @inlinable
addnull423   static func add(builder: inout FlatBufferBuilder, dmg: Int16) {
424     builder.add(element: dmg, def: 0, at: Weapon.offsets.dmg)
425   }
426 }
427