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