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