1import 'dart:typed_data'; 2import 'dart:io' as io; 3 4import 'package:path/path.dart' as path; 5 6import 'package:flat_buffers/flat_buffers.dart'; 7import 'package:test/test.dart'; 8import 'package:test_reflective_loader/test_reflective_loader.dart'; 9 10import './monster_test_my_game.example_generated.dart' as example; 11import './monster_test_my_game.example2_generated.dart' as example2; 12import './list_of_enums_generated.dart' as example3; 13import './bool_structs_generated.dart' as example4; 14 15main() { 16 defineReflectiveSuite(() { 17 defineReflectiveTests(BuilderTest); 18 defineReflectiveTests(ObjectAPITest); 19 defineReflectiveTests(CheckOtherLangaugesData); 20 defineReflectiveTests(GeneratorTest); 21 defineReflectiveTests(ListOfEnumsTest); 22 }); 23} 24 25int indexToField(int index) { 26 return (1 + 1 + index) * 2; 27} 28 29@reflectiveTest 30class CheckOtherLangaugesData { 31 test_cppData() async { 32 List<int> data = await io.File(path.join( 33 path.context.current, 34 'test', 35 'monsterdata_test.mon', 36 )).readAsBytes(); 37 example.Monster mon = example.Monster(data); 38 expect(mon.hp, 80); 39 expect(mon.mana, 150); 40 expect(mon.name, 'MyMonster'); 41 expect(mon.pos!.x, 1.0); 42 expect(mon.pos!.y, 2.0); 43 expect(mon.pos!.z, 3.0); 44 expect(mon.pos!.test1, 3.0); 45 expect(mon.pos!.test2.value, 2.0); 46 expect(mon.pos!.test3.a, 5); 47 expect(mon.pos!.test3.b, 6); 48 expect(mon.testType!.value, example.AnyTypeId.Monster.value); 49 expect(mon.test is example.Monster, true); 50 final monster2 = mon.test as example.Monster; 51 expect(monster2.name, "Fred"); 52 53 expect(mon.inventory!.length, 5); 54 expect(mon.inventory!.reduce((cur, next) => cur + next), 10); 55 final test4 = mon.test4!; 56 expect(test4.length, 2); 57 expect(test4[0].a + test4[0].b + test4[1].a + test4[1].b, 100); 58 expect(mon.testarrayofstring!.length, 2); 59 expect(mon.testarrayofstring![0], "test1"); 60 expect(mon.testarrayofstring![1], "test2"); 61 62 // this will fail if accessing any field fails. 63 expect( 64 mon.toString(), 65 'Monster{' 66 'pos: Vec3{x: 1.0, y: 2.0, z: 3.0, test1: 3.0, test2: Color{value: 2}, test3: Test{a: 5, b: 6}}, ' 67 'mana: 150, hp: 80, name: MyMonster, inventory: [0, 1, 2, 3, 4], ' 68 'color: Color{value: 8}, testType: AnyTypeId{value: 1}, ' 69 'test: Monster{pos: null, mana: 150, hp: 100, name: Fred, ' 70 'inventory: null, color: Color{value: 8}, testType: null, ' 71 'test: null, test4: null, testarrayofstring: null, ' 72 'testarrayoftables: null, enemy: null, testnestedflatbuffer: null, ' 73 'testempty: null, testbool: false, testhashs32Fnv1: 0, ' 74 'testhashu32Fnv1: 0, testhashs64Fnv1: 0, testhashu64Fnv1: 0, ' 75 'testhashs32Fnv1a: 0, testhashu32Fnv1a: 0, testhashs64Fnv1a: 0, ' 76 'testhashu64Fnv1a: 0, testarrayofbools: null, testf: 3.14159, ' 77 'testf2: 3.0, testf3: 0.0, testarrayofstring2: null, ' 78 'testarrayofsortedstruct: null, flex: null, test5: null, ' 79 'vectorOfLongs: null, vectorOfDoubles: null, parentNamespaceTest: null, ' 80 'vectorOfReferrables: null, singleWeakReference: 0, ' 81 'vectorOfWeakReferences: null, vectorOfStrongReferrables: null, ' 82 'coOwningReference: 0, vectorOfCoOwningReferences: null, ' 83 'nonOwningReference: 0, vectorOfNonOwningReferences: null, ' 84 'anyUniqueType: null, anyUnique: null, anyAmbiguousType: null, ' 85 'anyAmbiguous: null, vectorOfEnums: null, signedEnum: Race{value: -1}, ' 86 'testrequirednestedflatbuffer: null, scalarKeySortedTables: null, ' 87 'nativeInline: null, ' 88 'longEnumNonEnumDefault: LongEnum{value: 0}, ' 89 'longEnumNormalDefault: LongEnum{value: 2}, nanDefault: NaN, ' 90 'infDefault: Infinity, positiveInfDefault: Infinity, infinityDefault: ' 91 'Infinity, positiveInfinityDefault: Infinity, negativeInfDefault: ' 92 '-Infinity, negativeInfinityDefault: -Infinity, doubleInfDefault: Infinity}, ' 93 'test4: [Test{a: 10, b: 20}, Test{a: 30, b: 40}], ' 94 'testarrayofstring: [test1, test2], testarrayoftables: null, ' 95 'enemy: Monster{pos: null, mana: 150, hp: 100, name: Fred, ' 96 'inventory: null, color: Color{value: 8}, testType: null, ' 97 'test: null, test4: null, testarrayofstring: null, ' 98 'testarrayoftables: null, enemy: null, testnestedflatbuffer: null, ' 99 'testempty: null, testbool: false, testhashs32Fnv1: 0, ' 100 'testhashu32Fnv1: 0, testhashs64Fnv1: 0, testhashu64Fnv1: 0, ' 101 'testhashs32Fnv1a: 0, testhashu32Fnv1a: 0, testhashs64Fnv1a: 0, ' 102 'testhashu64Fnv1a: 0, testarrayofbools: null, testf: 3.14159, ' 103 'testf2: 3.0, testf3: 0.0, testarrayofstring2: null, ' 104 'testarrayofsortedstruct: null, flex: null, test5: null, ' 105 'vectorOfLongs: null, vectorOfDoubles: null, parentNamespaceTest: null, ' 106 'vectorOfReferrables: null, singleWeakReference: 0, ' 107 'vectorOfWeakReferences: null, vectorOfStrongReferrables: null, ' 108 'coOwningReference: 0, vectorOfCoOwningReferences: null, ' 109 'nonOwningReference: 0, vectorOfNonOwningReferences: null, ' 110 'anyUniqueType: null, anyUnique: null, anyAmbiguousType: null, ' 111 'anyAmbiguous: null, vectorOfEnums: null, signedEnum: Race{value: -1}, ' 112 'testrequirednestedflatbuffer: null, scalarKeySortedTables: null, ' 113 'nativeInline: null, ' 114 'longEnumNonEnumDefault: LongEnum{value: 0}, ' 115 'longEnumNormalDefault: LongEnum{value: 2}, nanDefault: NaN, ' 116 'infDefault: Infinity, positiveInfDefault: Infinity, infinityDefault: ' 117 'Infinity, positiveInfinityDefault: Infinity, negativeInfDefault: ' 118 '-Infinity, negativeInfinityDefault: -Infinity, doubleInfDefault: Infinity}, ' 119 'testnestedflatbuffer: null, testempty: null, testbool: true, ' 120 'testhashs32Fnv1: -579221183, testhashu32Fnv1: 3715746113, ' 121 'testhashs64Fnv1: 7930699090847568257, ' 122 'testhashu64Fnv1: 7930699090847568257, ' 123 'testhashs32Fnv1a: -1904106383, testhashu32Fnv1a: 2390860913, ' 124 'testhashs64Fnv1a: 4898026182817603057, ' 125 'testhashu64Fnv1a: 4898026182817603057, ' 126 'testarrayofbools: [true, false, true], testf: 3.14159, testf2: 3.0, ' 127 'testf3: 0.0, testarrayofstring2: null, testarrayofsortedstruct: [' 128 'Ability{id: 0, distance: 45}, Ability{id: 1, distance: 21}, ' 129 'Ability{id: 5, distance: 12}], ' 130 'flex: null, test5: [Test{a: 10, b: 20}, Test{a: 30, b: 40}], ' 131 'vectorOfLongs: [1, 100, 10000, 1000000, 100000000], ' 132 'vectorOfDoubles: [-1.7976931348623157e+308, 0.0, 1.7976931348623157e+308], ' 133 'parentNamespaceTest: null, vectorOfReferrables: null, ' 134 'singleWeakReference: 0, vectorOfWeakReferences: null, ' 135 'vectorOfStrongReferrables: null, coOwningReference: 0, ' 136 'vectorOfCoOwningReferences: null, nonOwningReference: 0, ' 137 'vectorOfNonOwningReferences: null, ' 138 'anyUniqueType: null, anyUnique: null, ' 139 'anyAmbiguousType: null, ' 140 'anyAmbiguous: null, vectorOfEnums: null, signedEnum: Race{value: -1}, ' 141 'testrequirednestedflatbuffer: null, scalarKeySortedTables: [Stat{id: ' 142 'miss, val: 0, count: 0}, Stat{id: hit, val: 10, count: 1}], ' 143 'nativeInline: Test{a: 1, b: 2}, ' 144 'longEnumNonEnumDefault: LongEnum{value: 0}, ' 145 'longEnumNormalDefault: LongEnum{value: 2}, nanDefault: NaN, ' 146 'infDefault: Infinity, positiveInfDefault: Infinity, infinityDefault: ' 147 'Infinity, positiveInfinityDefault: Infinity, negativeInfDefault: ' 148 '-Infinity, negativeInfinityDefault: -Infinity, doubleInfDefault: Infinity}'); 149 } 150} 151 152/// Test a custom, fixed-memory allocator (no actual allocations performed) 153class CustomAllocator extends Allocator { 154 final _memory = ByteData(10 * 1024); 155 int _used = 0; 156 157 Uint8List buffer(int size) => _memory.buffer.asUint8List(_used - size, size); 158 159 @override 160 ByteData allocate(int size) { 161 if (size > _memory.lengthInBytes) { 162 throw UnsupportedError('Trying to allocate too much'); 163 } 164 _used = size; 165 return ByteData.sublistView(_memory, 0, size); 166 } 167 168 @override 169 void deallocate(ByteData _) {} 170} 171 172@reflectiveTest 173class BuilderTest { 174 void test_monsterBuilder([Builder? builder]) { 175 final fbBuilder = builder ?? Builder(); 176 final str = fbBuilder.writeString('MyMonster'); 177 178 fbBuilder.writeString('test1'); 179 fbBuilder.writeString('test2', asciiOptimization: true); 180 final testArrayOfString = fbBuilder.endStructVector(2); 181 182 final fred = fbBuilder.writeString('Fred'); 183 184 final List<int> treasure = [0, 1, 2, 3, 4]; 185 final inventory = fbBuilder.writeListUint8(treasure); 186 187 final monBuilder = example.MonsterBuilder(fbBuilder) 188 ..begin() 189 ..addNameOffset(fred); 190 final mon2 = monBuilder.finish(); 191 192 final testBuilder = example.TestBuilder(fbBuilder); 193 testBuilder.finish(10, 20); 194 testBuilder.finish(30, 40); 195 final test4 = fbBuilder.endStructVector(2); 196 197 monBuilder 198 ..begin() 199 ..addPos( 200 example.Vec3Builder(fbBuilder).finish( 201 1.0, 202 2.0, 203 3.0, 204 3.0, 205 example.Color.Green, 206 () => testBuilder.finish(5, 6), 207 ), 208 ) 209 ..addHp(80) 210 ..addNameOffset(str) 211 ..addInventoryOffset(inventory) 212 ..addTestType(example.AnyTypeId.Monster) 213 ..addTestOffset(mon2) 214 ..addTest4Offset(test4) 215 ..addTestarrayofstringOffset(testArrayOfString); 216 final mon = monBuilder.finish(); 217 fbBuilder.finish(mon); 218 } 219 220 void test_error_addInt32_withoutStartTable([Builder? builder]) { 221 builder ??= Builder(); 222 expect(() { 223 builder!.addInt32(0, 0); 224 }, throwsA(isA<AssertionError>())); 225 } 226 227 void test_error_addOffset_withoutStartTable() { 228 Builder builder = Builder(); 229 expect(() { 230 builder.addOffset(0, 0); 231 }, throwsA(isA<AssertionError>())); 232 } 233 234 void test_error_endTable_withoutStartTable() { 235 Builder builder = Builder(); 236 expect(() { 237 builder.endTable(); 238 }, throwsA(isA<AssertionError>())); 239 } 240 241 void test_error_startTable_duringTable() { 242 Builder builder = Builder(); 243 builder.startTable(0); 244 expect(() { 245 builder.startTable(0); 246 }, throwsA(isA<AssertionError>())); 247 } 248 249 void test_error_writeString_duringTable() { 250 Builder builder = Builder(); 251 builder.startTable(1); 252 expect(() { 253 builder.writeString('12345'); 254 }, throwsA(isA<AssertionError>())); 255 } 256 257 void test_file_identifier() { 258 Uint8List byteList; 259 { 260 Builder builder = Builder(initialSize: 0); 261 builder.startTable(0); 262 int offset = builder.endTable(); 263 builder.finish(offset, 'Az~ÿ'); 264 byteList = builder.buffer; 265 } 266 // Convert byteList to a ByteData so that we can read data from it. 267 ByteData byteData = byteList.buffer.asByteData(byteList.offsetInBytes); 268 // First 4 bytes are an offset to the table data. 269 int tableDataLoc = byteData.getUint32(0, Endian.little); 270 // Next 4 bytes are the file identifier. 271 expect(byteData.getUint8(4), 65); // 'a' 272 expect(byteData.getUint8(5), 122); // 'z' 273 expect(byteData.getUint8(6), 126); // '~' 274 expect(byteData.getUint8(7), 255); // 'ÿ' 275 // First 4 bytes of the table data are a backwards offset to the vtable. 276 int vTableLoc = 277 tableDataLoc - byteData.getInt32(tableDataLoc, Endian.little); 278 // First 2 bytes of the vtable are the size of the vtable in bytes, which 279 // should be 4. 280 expect(byteData.getUint16(vTableLoc, Endian.little), 4); 281 // Next 2 bytes are the size of the object in bytes (including the vtable 282 // pointer), which should be 4. 283 expect(byteData.getUint16(vTableLoc + 2, Endian.little), 4); 284 } 285 286 void test_low() { 287 final allocator = CustomAllocator(); 288 final builder = Builder(initialSize: 0, allocator: allocator); 289 290 builder.putUint8(1); 291 expect(allocator.buffer(builder.size()), [1]); 292 293 builder.putUint32(2); 294 expect(allocator.buffer(builder.size()), [2, 0, 0, 0, 0, 0, 0, 1]); 295 296 builder.putUint8(3); 297 expect( 298 allocator.buffer(builder.size()), [0, 0, 0, 3, 2, 0, 0, 0, 0, 0, 0, 1]); 299 300 builder.putUint8(4); 301 expect( 302 allocator.buffer(builder.size()), [0, 0, 4, 3, 2, 0, 0, 0, 0, 0, 0, 1]); 303 304 builder.putUint8(5); 305 expect( 306 allocator.buffer(builder.size()), [0, 5, 4, 3, 2, 0, 0, 0, 0, 0, 0, 1]); 307 308 builder.putUint32(6); 309 expect(allocator.buffer(builder.size()), 310 [6, 0, 0, 0, 0, 5, 4, 3, 2, 0, 0, 0, 0, 0, 0, 1]); 311 } 312 313 void test_table_default() { 314 List<int> byteList; 315 { 316 final builder = Builder(initialSize: 0, allocator: CustomAllocator()); 317 builder.startTable(2); 318 builder.addInt32(0, 10, 10); 319 builder.addInt32(1, 20, 10); 320 int offset = builder.endTable(); 321 builder.finish(offset); 322 byteList = builder.buffer; 323 expect(builder.size(), byteList.length); 324 } 325 // read and verify 326 BufferContext buffer = BufferContext.fromBytes(byteList); 327 int objectOffset = buffer.derefObject(0); 328 // was not written, so uses the new default value 329 expect( 330 const Int32Reader() 331 .vTableGet(buffer, objectOffset, indexToField(0), 15), 332 15); 333 // has the written value 334 expect( 335 const Int32Reader() 336 .vTableGet(buffer, objectOffset, indexToField(1), 15), 337 20); 338 } 339 340 void test_table_format([Builder? builder]) { 341 Uint8List byteList; 342 { 343 builder ??= Builder(initialSize: 0); 344 builder.startTable(3); 345 builder.addInt32(0, 10); 346 builder.addInt32(1, 20); 347 builder.addInt32(2, 30); 348 builder.finish(builder.endTable()); 349 byteList = builder.buffer; 350 } 351 // Convert byteList to a ByteData so that we can read data from it. 352 ByteData byteData = byteList.buffer.asByteData(byteList.offsetInBytes); 353 // First 4 bytes are an offset to the table data. 354 int tableDataLoc = byteData.getUint32(0, Endian.little); 355 // First 4 bytes of the table data are a backwards offset to the vtable. 356 int vTableLoc = 357 tableDataLoc - byteData.getInt32(tableDataLoc, Endian.little); 358 // First 2 bytes of the vtable are the size of the vtable in bytes, which 359 // should be 10. 360 expect(byteData.getUint16(vTableLoc, Endian.little), 10); 361 // Next 2 bytes are the size of the object in bytes (including the vtable 362 // pointer), which should be 16. 363 expect(byteData.getUint16(vTableLoc + 2, Endian.little), 16); 364 // Remaining 6 bytes are the offsets within the object where the ints are 365 // located. 366 for (int i = 0; i < 3; i++) { 367 int offset = byteData.getUint16(vTableLoc + 4 + 2 * i, Endian.little); 368 expect( 369 byteData.getInt32(tableDataLoc + offset, Endian.little), 10 + 10 * i); 370 } 371 } 372 373 void test_table_string() { 374 String latinString = 'test'; 375 String unicodeString = 'Проба пера'; 376 List<int> byteList; 377 { 378 Builder builder = Builder(initialSize: 0); 379 int? latinStringOffset = 380 builder.writeString(latinString, asciiOptimization: true); 381 int? unicodeStringOffset = 382 builder.writeString(unicodeString, asciiOptimization: true); 383 builder.startTable(2); 384 builder.addOffset(0, latinStringOffset); 385 builder.addOffset(1, unicodeStringOffset); 386 int offset = builder.endTable(); 387 builder.finish(offset); 388 byteList = builder.buffer; 389 } 390 // read and verify 391 BufferContext buf = BufferContext.fromBytes(byteList); 392 int objectOffset = buf.derefObject(0); 393 expect( 394 const StringReader() 395 .vTableGetNullable(buf, objectOffset, indexToField(0)), 396 latinString); 397 expect( 398 const StringReader(asciiOptimization: true) 399 .vTableGetNullable(buf, objectOffset, indexToField(1)), 400 unicodeString); 401 } 402 403 void test_table_types([Builder? builder]) { 404 List<int> byteList; 405 { 406 builder ??= Builder(initialSize: 0); 407 int? stringOffset = builder.writeString('12345'); 408 builder.startTable(7); 409 builder.addBool(0, true); 410 builder.addInt8(1, 10); 411 builder.addInt32(2, 20); 412 builder.addOffset(3, stringOffset); 413 builder.addInt32(4, 40); 414 builder.addUint32(5, 0x9ABCDEF0); 415 builder.addUint8(6, 0x9A); 416 int offset = builder.endTable(); 417 builder.finish(offset); 418 byteList = builder.buffer; 419 } 420 // read and verify 421 BufferContext buf = BufferContext.fromBytes(byteList); 422 int objectOffset = buf.derefObject(0); 423 expect( 424 const BoolReader() 425 .vTableGetNullable(buf, objectOffset, indexToField(0)), 426 true); 427 expect( 428 const Int8Reader() 429 .vTableGetNullable(buf, objectOffset, indexToField(1)), 430 10); 431 expect( 432 const Int32Reader() 433 .vTableGetNullable(buf, objectOffset, indexToField(2)), 434 20); 435 expect( 436 const StringReader() 437 .vTableGetNullable(buf, objectOffset, indexToField(3)), 438 '12345'); 439 expect( 440 const Int32Reader() 441 .vTableGetNullable(buf, objectOffset, indexToField(4)), 442 40); 443 expect( 444 const Uint32Reader() 445 .vTableGetNullable(buf, objectOffset, indexToField(5)), 446 0x9ABCDEF0); 447 expect( 448 const Uint8Reader() 449 .vTableGetNullable(buf, objectOffset, indexToField(6)), 450 0x9A); 451 } 452 453 void test_writeList_of_Uint32() { 454 List<int> values = <int>[10, 100, 12345, 0x9abcdef0]; 455 // write 456 List<int> byteList; 457 { 458 Builder builder = Builder(initialSize: 0); 459 int offset = builder.writeListUint32(values); 460 builder.finish(offset); 461 byteList = builder.buffer; 462 } 463 // read and verify 464 BufferContext buf = BufferContext.fromBytes(byteList); 465 List<int> items = const Uint32ListReader().read(buf, 0); 466 expect(items, hasLength(4)); 467 expect(items, orderedEquals(values)); 468 } 469 470 void test_writeList_ofBool() { 471 void verifyListBooleans(int len, List<int> trueBits) { 472 // write 473 List<int> byteList; 474 { 475 Builder builder = Builder(initialSize: 0); 476 List<bool> values = List<bool>.filled(len, false); 477 for (int bit in trueBits) { 478 values[bit] = true; 479 } 480 int offset = builder.writeListBool(values); 481 builder.finish(offset); 482 byteList = builder.buffer; 483 } 484 // read and verify 485 BufferContext buf = BufferContext.fromBytes(byteList); 486 List<bool> items = const BoolListReader().read(buf, 0); 487 expect(items, hasLength(len)); 488 for (int i = 0; i < items.length; i++) { 489 expect(items[i], trueBits.contains(i), reason: 'bit $i of $len'); 490 } 491 } 492 493 verifyListBooleans(0, <int>[]); 494 verifyListBooleans(1, <int>[]); 495 verifyListBooleans(1, <int>[0]); 496 verifyListBooleans(31, <int>[0, 1]); 497 verifyListBooleans(31, <int>[1, 2, 24, 25, 30]); 498 verifyListBooleans(31, <int>[0, 30]); 499 verifyListBooleans(32, <int>[1, 2, 24, 25, 31]); 500 verifyListBooleans(33, <int>[1, 2, 24, 25, 32]); 501 verifyListBooleans(33, <int>[1, 2, 24, 25, 31, 32]); 502 verifyListBooleans(63, <int>[]); 503 verifyListBooleans(63, <int>[0, 1, 2, 61, 62]); 504 verifyListBooleans(63, List<int>.generate(63, (i) => i)); 505 verifyListBooleans(64, <int>[]); 506 verifyListBooleans(64, <int>[0, 1, 2, 61, 62, 63]); 507 verifyListBooleans(64, <int>[1, 2, 62]); 508 verifyListBooleans(64, <int>[0, 1, 2, 63]); 509 verifyListBooleans(64, List<int>.generate(64, (i) => i)); 510 verifyListBooleans(100, <int>[0, 3, 30, 60, 90, 99]); 511 } 512 513 void test_writeList_ofInt32() { 514 List<int> byteList; 515 { 516 Builder builder = Builder(initialSize: 0); 517 int offset = builder.writeListInt32(<int>[1, 2, 3, 4, 5]); 518 builder.finish(offset); 519 byteList = builder.buffer; 520 } 521 // read and verify 522 BufferContext buf = BufferContext.fromBytes(byteList); 523 List<int> items = const ListReader<int>(Int32Reader()).read(buf, 0); 524 expect(items, hasLength(5)); 525 expect(items, orderedEquals(<int>[1, 2, 3, 4, 5])); 526 } 527 528 void test_writeList_ofFloat64() { 529 List<double> values = <double>[-1.234567, 3.4E+9, -5.6E-13, 7.8, 12.13]; 530 // write 531 List<int> byteList; 532 { 533 Builder builder = Builder(initialSize: 0); 534 int offset = builder.writeListFloat64(values); 535 builder.finish(offset); 536 byteList = builder.buffer; 537 } 538 539 // read and verify 540 BufferContext buf = BufferContext.fromBytes(byteList); 541 List<double> items = const Float64ListReader().read(buf, 0); 542 543 expect(items, hasLength(values.length)); 544 for (int i = 0; i < values.length; i++) { 545 expect(values[i], closeTo(items[i], .001)); 546 } 547 } 548 549 void test_writeList_ofFloat32() { 550 List<double> values = [1.0, 2.23, -3.213, 7.8, 12.13]; 551 // write 552 List<int> byteList; 553 { 554 Builder builder = Builder(initialSize: 0); 555 int offset = builder.writeListFloat32(values); 556 builder.finish(offset); 557 byteList = builder.buffer; 558 } 559 // read and verify 560 BufferContext buf = BufferContext.fromBytes(byteList); 561 List<double> items = const Float32ListReader().read(buf, 0); 562 expect(items, hasLength(5)); 563 for (int i = 0; i < values.length; i++) { 564 expect(values[i], closeTo(items[i], .001)); 565 } 566 } 567 568 void test_writeList_ofObjects([Builder? builder]) { 569 List<int> byteList; 570 { 571 builder ??= Builder(initialSize: 0); 572 // write the object #1 573 int object1; 574 { 575 builder.startTable(2); 576 builder.addInt32(0, 10); 577 builder.addInt32(1, 20); 578 object1 = builder.endTable(); 579 } 580 // write the object #1 581 int object2; 582 { 583 builder.startTable(2); 584 builder.addInt32(0, 100); 585 builder.addInt32(1, 200); 586 object2 = builder.endTable(); 587 } 588 // write the list 589 int offset = builder.writeList([object1, object2]); 590 builder.finish(offset); 591 byteList = builder.buffer; 592 } 593 // read and verify 594 BufferContext buf = BufferContext.fromBytes(byteList); 595 List<TestPointImpl> items = 596 const ListReader<TestPointImpl>(TestPointReader()).read(buf, 0); 597 expect(items, hasLength(2)); 598 expect(items[0].x, 10); 599 expect(items[0].y, 20); 600 expect(items[1].x, 100); 601 expect(items[1].y, 200); 602 } 603 604 void test_writeList_ofStrings_asRoot() { 605 List<int> byteList; 606 { 607 Builder builder = Builder(initialSize: 0); 608 int? str1 = builder.writeString('12345'); 609 int? str2 = builder.writeString('ABC'); 610 int offset = builder.writeList([str1, str2]); 611 builder.finish(offset); 612 byteList = builder.buffer; 613 } 614 // read and verify 615 BufferContext buf = BufferContext.fromBytes(byteList); 616 List<String> items = const ListReader<String>(StringReader()).read(buf, 0); 617 expect(items, hasLength(2)); 618 expect(items, contains('12345')); 619 expect(items, contains('ABC')); 620 } 621 622 void test_writeList_ofStrings_inObject([Builder? builder]) { 623 List<int> byteList; 624 { 625 builder ??= Builder(initialSize: 0); 626 int listOffset = builder.writeList( 627 [builder.writeString('12345'), builder.writeString('ABC')]); 628 builder.startTable(1); 629 builder.addOffset(0, listOffset); 630 int offset = builder.endTable(); 631 builder.finish(offset); 632 byteList = builder.buffer; 633 } 634 // read and verify 635 BufferContext buf = BufferContext.fromBytes(byteList); 636 StringListWrapperImpl reader = StringListWrapperReader().read(buf, 0); 637 List<String>? items = reader.items; 638 expect(items, hasLength(2)); 639 expect(items, contains('12345')); 640 expect(items, contains('ABC')); 641 } 642 643 void test_writeList_ofUint32() { 644 List<int> byteList; 645 { 646 Builder builder = Builder(initialSize: 0); 647 int offset = builder.writeListUint32(<int>[1, 2, 0x9ABCDEF0]); 648 builder.finish(offset); 649 byteList = builder.buffer; 650 } 651 // read and verify 652 BufferContext buf = BufferContext.fromBytes(byteList); 653 List<int> items = const Uint32ListReader().read(buf, 0); 654 expect(items, hasLength(3)); 655 expect(items, orderedEquals(<int>[1, 2, 0x9ABCDEF0])); 656 } 657 658 void test_writeList_ofUint16() { 659 List<int> byteList; 660 { 661 Builder builder = Builder(initialSize: 0); 662 int offset = builder.writeListUint16(<int>[1, 2, 60000]); 663 builder.finish(offset); 664 byteList = builder.buffer; 665 } 666 // read and verify 667 BufferContext buf = BufferContext.fromBytes(byteList); 668 List<int> items = const Uint16ListReader().read(buf, 0); 669 expect(items, hasLength(3)); 670 expect(items, orderedEquals(<int>[1, 2, 60000])); 671 } 672 673 void test_writeList_ofUint8() { 674 List<int> byteList; 675 { 676 Builder builder = Builder(initialSize: 0); 677 int offset = builder.writeListUint8(<int>[1, 2, 3, 4, 0x9A, 0xFA]); 678 builder.finish(offset); 679 byteList = builder.buffer; 680 } 681 // read and verify 682 BufferContext buf = BufferContext.fromBytes(byteList); 683 const buffOffset = 8; // 32-bit offset to the list, + 32-bit length 684 for (final lazy in [true, false]) { 685 List<int> items = Uint8ListReader(lazy: lazy).read(buf, 0); 686 expect(items, hasLength(6)); 687 expect(items, orderedEquals(<int>[1, 2, 3, 4, 0x9A, 0xFA])); 688 689 // overwrite the buffer to verify the laziness 690 buf.buffer.setUint8(buffOffset + 1, 99); 691 expect(items, orderedEquals(<int>[1, lazy ? 99 : 2, 3, 4, 0x9A, 0xFA])); 692 693 // restore the previous value for the next loop 694 buf.buffer.setUint8(buffOffset + 1, 2); 695 } 696 } 697 698 void test_reset() { 699 // We'll run a selection of tests , reusing the builder between them. 700 final testCases = <void Function(Builder?)>[ 701 test_monsterBuilder, 702 test_error_addInt32_withoutStartTable, 703 test_table_format, 704 test_table_types, 705 test_writeList_ofObjects, 706 test_writeList_ofStrings_inObject 707 ]; 708 709 // Execute all test cases in all permutations of their order. 710 // To do that, we generate permutations of test case indexes. 711 final testCasesPermutations = 712 _permutationsOf(List.generate(testCases.length, (index) => index)); 713 expect(testCasesPermutations.length, _factorial(testCases.length)); 714 715 for (var indexes in testCasesPermutations) { 716 // print the order so failures are reproducible 717 printOnFailure('Running reset() test cases in order: $indexes'); 718 719 Builder? builder; 720 for (var index in indexes) { 721 if (builder == null) { 722 // Initial size small enough so at least one test case increases it. 723 // On the other hand, it's large enough so that some test cases don't. 724 builder = Builder(initialSize: 32); 725 } else { 726 builder.reset(); 727 } 728 testCases[index](builder); 729 } 730 } 731 } 732 733 // Generate permutations of the given list 734 List<List<T>> _permutationsOf<T>(List<T> source) { 735 final result = <List<T>>[]; 736 737 void permutate(List<T> items, int startAt) { 738 for (var i = startAt; i < items.length; i++) { 739 List<T> permutation = items.toList(growable: false); 740 permutation[i] = items[startAt]; 741 permutation[startAt] = items[i]; 742 743 // add the current list upon reaching the end 744 if (startAt == items.length - 1) { 745 result.add(items); 746 } else { 747 permutate(permutation, startAt + 1); 748 } 749 } 750 } 751 752 permutate(source, 0); 753 return result; 754 } 755 756 // a very simple implementation of n! 757 int _factorial(int n) { 758 var result = 1; 759 for (var i = 2; i <= n; i++) { 760 result *= i; 761 } 762 return result; 763 } 764} 765 766@reflectiveTest 767class ObjectAPITest { 768 void test_tableStat() { 769 final object1 = example.StatT(count: 3, id: "foo", val: 4); 770 final fbb = Builder(); 771 fbb.finish(object1.pack(fbb)); 772 final object2 = example.Stat(fbb.buffer).unpack(); 773 expect(object2.count, object1.count); 774 expect(object2.id, object1.id); 775 expect(object2.val, object1.val); 776 expect(object2.toString(), object1.toString()); 777 } 778 779 void test_tableMonster() { 780 final monster = example.MonsterT() 781 ..pos = example.Vec3T( 782 x: 1, 783 y: 2, 784 z: 3, 785 test1: 4.0, 786 test2: example.Color.Red, 787 test3: example.TestT(a: 1, b: 2)) 788 ..mana = 2 789 ..name = 'Monstrous' 790 ..inventory = [24, 42] 791 ..color = example.Color.Green 792 // TODO be smarter for unions and automatically set the `type` field? 793 ..testType = example.AnyTypeId.MyGame_Example2_Monster 794 ..test = example2.MonsterT() 795 ..test4 = [example.TestT(a: 3, b: 4), example.TestT(a: 5, b: 6)] 796 ..testarrayofstring = ["foo", "bar"] 797 ..testarrayoftables = [example.MonsterT(name: 'Oof')] 798 ..enemy = example.MonsterT(name: 'Enemy') 799 ..testarrayofbools = [false, true, false] 800 ..testf = 42.24 801 ..testarrayofsortedstruct = [ 802 example.AbilityT(id: 1, distance: 5), 803 example.AbilityT(id: 3, distance: 7) 804 ] 805 ..vectorOfLongs = [5, 6, 7] 806 ..vectorOfDoubles = [8.9, 9.0, 10.1, 11.2] 807 ..anyAmbiguousType = example.AnyAmbiguousAliasesTypeId.M2 808 ..anyAmbiguous = null 809 ..vectorOfEnums = [example.Color.Blue, example.Color.Green] 810 ..signedEnum = example.Race.None; 811 812 final fbBuilder = Builder(); 813 final offset = monster.pack(fbBuilder); 814 expect(offset, isNonZero); 815 fbBuilder.finish(offset); 816 final data = fbBuilder.buffer; 817 818 // TODO currently broken because of struct builder issue, see #6688 819 // final monster2 = example.Monster(data); // Monster (reader) 820 // expect( 821 // // map Monster => MonsterT, Vec3 => Vec3T, ... 822 // monster2.toString().replaceAllMapped( 823 // RegExp('([a-zA-z0-9]+){'), (match) => match.group(1) + 'T{'), 824 // monster.toString()); 825 // 826 // final monster3 = monster2.unpack(); // MonsterT 827 // expect(monster3.toString(), monster.toString()); 828 } 829 830 void test_Lists() { 831 // Ensure unpack() reads lists eagerly by reusing the same builder and 832 // overwriting data. Why: because standard reader reads lists lazily... 833 final fbb = Builder(); 834 835 final object1 = example.TypeAliasesT(v8: [1, 2, 3], vf64: [5, 6]); 836 fbb.finish(object1.pack(fbb)); 837 final object1Read = example.TypeAliases(fbb.buffer).unpack(); 838 839 // overwrite the original buffer by writing to the same builder 840 fbb.reset(); 841 final object2 = example.TypeAliasesT(v8: [7, 8, 9], vf64: [10, 11]); 842 fbb.finish(object2.pack(fbb)); 843 final object2Read = example.TypeAliases(fbb.buffer).unpack(); 844 845 // this is fine even with lazy lists: 846 expect(object2.toString(), object2Read.toString()); 847 848 // this fails with lazy lists: 849 expect(object1.toString(), object1Read.toString()); 850 851 // empty list must be serialized as such (were stored NULL before v2.0) 852 fbb.reset(); 853 final object3 = example.TypeAliasesT(v8: [], vf64: null); 854 fbb.finish(object3.pack(fbb)); 855 final object3Read = example.TypeAliases(fbb.buffer).unpack(); 856 expect(object3.toString(), object3Read.toString()); 857 } 858} 859 860class StringListWrapperImpl { 861 final BufferContext bp; 862 final int offset; 863 864 StringListWrapperImpl(this.bp, this.offset); 865 866 List<String>? get items => const ListReader<String>(StringReader()) 867 .vTableGetNullable(bp, offset, indexToField(0)); 868} 869 870class StringListWrapperReader extends TableReader<StringListWrapperImpl> { 871 const StringListWrapperReader(); 872 873 @override 874 StringListWrapperImpl createObject(BufferContext object, int offset) { 875 return StringListWrapperImpl(object, offset); 876 } 877} 878 879class TestPointImpl { 880 final BufferContext bp; 881 final int offset; 882 883 TestPointImpl(this.bp, this.offset); 884 885 int get x => const Int32Reader().vTableGet(bp, offset, indexToField(0), 0); 886 887 int get y => const Int32Reader().vTableGet(bp, offset, indexToField(1), 0); 888} 889 890class TestPointReader extends TableReader<TestPointImpl> { 891 const TestPointReader(); 892 893 @override 894 TestPointImpl createObject(BufferContext object, int offset) { 895 return TestPointImpl(object, offset); 896 } 897} 898 899@reflectiveTest 900class GeneratorTest { 901 void test_constantEnumValues() async { 902 expect(example.Color.values, same(example.Color.values)); 903 expect(example.Race.values, same(example.Race.values)); 904 expect(example.AnyTypeId.values, same(example.AnyTypeId.values)); 905 expect(example.AnyUniqueAliasesTypeId.values, 906 same(example.AnyUniqueAliasesTypeId.values)); 907 expect(example.AnyAmbiguousAliasesTypeId.values, 908 same(example.AnyAmbiguousAliasesTypeId.values)); 909 } 910} 911 912// See #6869 913@reflectiveTest 914class ListOfEnumsTest { 915 void test_listOfEnums() async { 916 var mytable = example3.MyTableObjectBuilder(options: [ 917 example3.OptionsEnum.A, 918 example3.OptionsEnum.B, 919 example3.OptionsEnum.C 920 ]); 921 var bytes = mytable.toBytes(); 922 var mytable_read = example3.MyTable(bytes); 923 expect(mytable_read.options![0].value, example3.OptionsEnum.A.value); 924 expect(mytable_read.options![1].value, example3.OptionsEnum.B.value); 925 expect(mytable_read.options![2].value, example3.OptionsEnum.C.value); 926 } 927} 928 929@reflectiveTest 930class BoolInStructTest { 931 void test_boolInStruct() async { 932 var mystruct = example4.FooObjectBuilder( 933 myFoo: example4.FooPropertiesObjectBuilder(a: true, b: false)); 934 var bytes = mystruct.toBytes(); 935 var mystruct_read = example4.Foo(bytes); 936 expect(mystruct_read.myFoo!.a, true); 937 expect(mystruct_read.myFoo!.b, false); 938 } 939} 940