1 /* 2 * Copyright 2023 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 buffer = fb.buffer 126 var movie: Movie = getRoot(byteBuffer: &buffer) 127 XCTAssertEqual(movie.charactersTypeCount, Int32(characterType.count)) 128 XCTAssertEqual(movie.charactersCount, Int32(characters.count)) 129 130 for i in 0..<movie.charactersTypeCount { 131 XCTAssertEqual(movie.charactersType(at: i), characterType[Int(i)]) 132 } 133 134 XCTAssertEqual( 135 movie.characters(at: 0, type: BookReader_Mutable.self)?.booksRead, 136 7) 137 XCTAssertEqual( 138 movie.characters(at: 1, type: Attacker.self)?.swordAttackDamage, 139 swordDmg) 140 XCTAssertEqual( 141 movie.characters(at: 2, type: BookReader_Mutable.self)?.booksRead, 142 2) 143 144 var objc: MovieT? = movie.unpack() 145 XCTAssertEqual( 146 movie.charactersTypeCount, 147 Int32(objc?.characters.count ?? 0)) 148 XCTAssertEqual( 149 movie.characters(at: 0, type: BookReader_Mutable.self)?.booksRead, 150 (objc?.characters[0]?.value as? BookReader)?.booksRead) 151 fb.clear() 152 let newMovie = Movie.pack(&fb, obj: &objc) 153 fb.finish(offset: newMovie) 154 155 var _buffer = fb.buffer 156 let packedMovie: Movie = getRoot(byteBuffer: &_buffer) 157 158 XCTAssertEqual( 159 packedMovie.characters(at: 0, type: BookReader_Mutable.self)?.booksRead, 160 movie.characters(at: 0, type: BookReader_Mutable.self)?.booksRead) 161 XCTAssertEqual( 162 packedMovie.characters(at: 1, type: Attacker.self)?.swordAttackDamage, 163 movie.characters(at: 1, type: Attacker.self)?.swordAttackDamage) 164 XCTAssertEqual( 165 packedMovie.characters(at: 2, type: BookReader_Mutable.self)?.booksRead, 166 movie.characters(at: 2, type: BookReader_Mutable.self)?.booksRead) 167 } 168 testStringUnionnull169 func testStringUnion() { 170 let string = "Awesome \\\\t\t\nstring!" 171 var fb = FlatBufferBuilder() 172 let stringOffset = fb.create(string: string) 173 let characterType: [Character] = [.bookfan, .other] 174 175 let characters = [ 176 fb.create(struct: BookReader(booksRead: 7)), 177 stringOffset, 178 ] 179 let types = fb.createVector(characterType) 180 let characterVector = fb.createVector(ofOffsets: characters) 181 182 let end = Movie.createMovie( 183 &fb, 184 mainCharacterType: .other, 185 mainCharacterOffset: Offset(offset: stringOffset.o), 186 charactersTypeVectorOffset: types, 187 charactersVectorOffset: characterVector) 188 Movie.finish(&fb, end: end) 189 190 var buffer = fb.sizedBuffer 191 var movie: Movie = getRoot(byteBuffer: &buffer) 192 XCTAssertEqual(movie.mainCharacter(type: String.self), string) 193 XCTAssertEqual( 194 movie.characters(at: 0, type: BookReader_Mutable.self)?.booksRead, 195 7) 196 XCTAssertEqual(movie.characters(at: 1, type: String.self), string) 197 198 var objc: MovieT? = movie.unpack() 199 XCTAssertEqual(objc?.mainCharacter?.value as? String, string) 200 XCTAssertEqual((objc?.characters[0]?.value as? BookReader)?.booksRead, 7) 201 XCTAssertEqual(objc?.characters[1]?.value as? String, string) 202 fb.clear() 203 let newMovie = Movie.pack(&fb, obj: &objc) 204 fb.finish(offset: newMovie) 205 206 var _buffer = fb.buffer 207 let packedMovie: Movie = getRoot(byteBuffer: &_buffer) 208 XCTAssertEqual(packedMovie.mainCharacter(type: String.self), string) 209 XCTAssertEqual( 210 packedMovie.characters(at: 0, type: BookReader_Mutable.self)?.booksRead, 211 7) 212 XCTAssertEqual(packedMovie.characters(at: 1, type: String.self), string) 213 } 214 testEncodingnull215 func testEncoding() { 216 let string = "Awesome \\\\t\t\nstring!" 217 var fb = FlatBufferBuilder() 218 219 let stringOffset = fb.create(string: string) 220 221 let swordDmg: Int32 = 8 222 let attackStart = Attacker.startAttacker(&fb) 223 Attacker.add(swordAttackDamage: swordDmg, &fb) 224 let attack = Attacker.endAttacker(&fb, start: attackStart) 225 226 let characterType: [Character] = [.belle, .mulan, .bookfan, .other] 227 228 let characters = [ 229 fb.create(struct: BookReader(booksRead: 7)), 230 attack, 231 fb.create(struct: BookReader(booksRead: 2)), 232 stringOffset, 233 ] 234 let types = fb.createVector(characterType) 235 let characterVector = fb.createVector(ofOffsets: characters) 236 let end = Movie.createMovie( 237 &fb, 238 charactersTypeVectorOffset: types, 239 charactersVectorOffset: characterVector) 240 Movie.finish(&fb, end: end) 241 242 var sizedBuffer = fb.sizedBuffer 243 do { 244 let reader: Movie = try getCheckedRoot(byteBuffer: &sizedBuffer) 245 let encoder = JSONEncoder() 246 encoder.keyEncodingStrategy = .convertToSnakeCase 247 _ = try encoder.encode(reader) 248 } catch { 249 XCTFail(error.localizedDescription) 250 } 251 } 252 253 var jsonData: String { 254 "{\"characters_type\":[\"Belle\",\"MuLan\",\"BookFan\",\"Other\"],\"characters\":[{\"books_read\":7},{\"sword_attack_damage\":8},{\"books_read\":2},\"Awesome \\\\\\\\t\\t\\nstring!\"]}" 255 } 256 } 257 258 public enum ColorsNameSpace { 259 260 enum RGB: Int32, Enum { 261 typealias T = Int32 262 static var byteSize: Int { MemoryLayout<Int32>.size } 263 var value: Int32 { rawValue } 264 case red = 0, green = 1, blue = 2 265 } 266 267 struct Monster: FlatBufferObject { 268 var __buffer: ByteBuffer! { _accessor.bb } 269 270 private var _accessor: Table getRootAsMonsternull271 static func getRootAsMonster(bb: ByteBuffer) -> Monster { Monster(Table( 272 bb: bb, 273 position: Int32(bb.read(def: UOffset.self, position: bb.reader)) + 274 Int32(bb.reader))) } 275 276 init(_ t: Table) { _accessor = t } 277 init(_ bb: ByteBuffer, o: Int32) { _accessor = Table(bb: bb, position: o) } 278 279 public var colorsCount: Int32 { 280 let o = _accessor.offset(4); return o == 0 ? 0 : _accessor 281 .vector(count: o) } colorsnull282 public func colors(at index: Int32) -> ColorsNameSpace 283 .RGB? 284 { let o = _accessor.offset(4); return o == 0 ? ColorsNameSpace 285 .RGB(rawValue: 0)! : ColorsNameSpace.RGB(rawValue: _accessor.directRead( 286 of: Int32.self, 287 offset: _accessor.vector(at: o) + index * 4)) } startMonsternull288 static func startMonster(_ fbb: inout FlatBufferBuilder) -> UOffset { fbb 289 .startTable(with: 1) } addnull290 static func add(colors: Offset, _ fbb: inout FlatBufferBuilder) { fbb.add( 291 offset: colors, 292 at: 4) } 293 static func endMonster( 294 _ fbb: inout FlatBufferBuilder, 295 start: UOffset) 296 -> Offset 297 { let end = Offset(offset: fbb.endTable(at: start)); return end 298 } 299 } 300 } 301 302 303 enum Equipment: Byte { case none, Weapon } 304 305 enum Color3: Int8 { case red = 0, green, blue } 306 307 struct FinalMonster { 308 309 @inlinable 310 static func createMonster( 311 builder: inout FlatBufferBuilder, 312 position: Vec, 313 hp: Int16, 314 name: Offset, 315 inventory: Offset, 316 color: Color3, 317 weapons: Offset, 318 equipment: Equipment = .none, 319 equippedOffset: Offset, 320 path: Offset) -> Offset 321 { 322 let start = builder.startTable(with: 11) 323 builder.create(struct: position, position: 4) 324 builder.add(element: hp, def: 100, at: 8) 325 builder.add(offset: name, at: 10) 326 builder.add(offset: inventory, at: 14) 327 builder.add(element: color.rawValue, def: Color3.green.rawValue, at: 16) 328 builder.add(offset: weapons, at: 18) 329 builder.add( 330 element: equipment.rawValue, 331 def: Equipment.none.rawValue, 332 at: 20) 333 builder.add(offset: equippedOffset, at: 22) 334 builder.add(offset: path, at: 24) 335 return Offset(offset: builder.endTable(at: start)) 336 } 337 } 338 339 struct LocalMonster { 340 341 private var __t: Table 342 343 init(_ fb: ByteBuffer, o: Int32) { __t = Table(bb: fb, position: o) } 344 init(_ t: Table) { __t = t } 345 weaponnull346 func weapon(at index: Int32) -> Weapon? { let o = __t 347 .offset(4); return o == 0 ? nil : Weapon.assign( 348 __t.indirect(__t.vector(at: o) + (index * 4)), 349 __t.bb) } 350 equiped<T: FlatBufferObject>null351 func equiped<T: FlatBufferObject>() -> T? { 352 let o = __t.offset(8); return o == 0 ? nil : __t.union(o) 353 } 354 getRootAsMonsternull355 static func getRootAsMonster(bb: ByteBuffer) -> LocalMonster { 356 LocalMonster(Table( 357 bb: bb, 358 position: Int32(bb.read(def: UOffset.self, position: 0)))) 359 } 360 361 @inlinable 362 static func createMonster( 363 builder: inout FlatBufferBuilder, 364 offset: Offset, 365 equipment: Equipment = .none, 366 equippedOffset: UOffset) -> Offset 367 { 368 let start = builder.startTable(with: 3) 369 builder.add(element: equippedOffset, def: 0, at: 8) 370 builder.add(offset: offset, at: 4) 371 builder.add( 372 element: equipment.rawValue, 373 def: Equipment.none.rawValue, 374 at: 6) 375 return Offset(offset: builder.endTable(at: start)) 376 } 377 } 378 379 struct Weapon: FlatBufferObject { 380 381 var __buffer: ByteBuffer! { __t.bb } 382 383 static let offsets: (name: VOffset, dmg: VOffset) = (4, 6) 384 private var __t: Table 385 386 init(_ t: Table) { __t = t } 387 init(_ fb: ByteBuffer, o: Int32) { __t = Table(bb: fb, position: o)} 388 389 var dmg: Int16 { let o = __t.offset(6); return o == 0 ? 0 : __t.readBuffer( 390 of: Int16.self, 391 at: o) } 392 var nameVector: [UInt8]? { __t.getVector(at: 4) } 393 var name: String? { 394 let o = __t.offset(4); return o == 0 ? nil : __t.string(at: o) } 395 assignnull396 static func assign(_ i: Int32, _ bb: ByteBuffer) -> Weapon { Weapon(Table( 397 bb: bb, 398 position: i)) } 399 400 @inlinable 401 static func createWeapon( 402 builder: inout FlatBufferBuilder, 403 offset: Offset, 404 dmg: Int16) -> Offset 405 { 406 let _start = builder.startTable(with: 2) 407 Weapon.add(builder: &builder, name: offset) 408 Weapon.add(builder: &builder, dmg: dmg) 409 return Weapon.end(builder: &builder, startOffset: _start) 410 } 411 412 @inlinable 413 static func end( 414 builder: inout FlatBufferBuilder, 415 startOffset: UOffset) -> Offset 416 { 417 Offset(offset: builder.endTable(at: startOffset)) 418 } 419 420 @inlinable addnull421 static func add(builder: inout FlatBufferBuilder, name: Offset) { 422 builder.add(offset: name, at: Weapon.offsets.name) 423 } 424 425 @inlinable addnull426 static func add(builder: inout FlatBufferBuilder, dmg: Int16) { 427 builder.add(element: dmg, def: 0, at: Weapon.offsets.dmg) 428 } 429 } 430