import 'dart:typed_data'; import 'dart:io' as io; import 'package:path/path.dart' as path; import 'package:flat_buffers/flat_buffers.dart'; import 'package:test/test.dart'; import 'package:test_reflective_loader/test_reflective_loader.dart'; import './monster_test_my_game.example_generated.dart' as example; import './monster_test_my_game.example2_generated.dart' as example2; import './list_of_enums_generated.dart' as example3; import './bool_structs_generated.dart' as example4; import './keyword_test_keyword_test_generated.dart' as keyword_test; main() { defineReflectiveSuite(() { defineReflectiveTests(BuilderTest); defineReflectiveTests(ObjectAPITest); defineReflectiveTests(CheckOtherLangaugesData); defineReflectiveTests(GeneratorTest); defineReflectiveTests(ListOfEnumsTest); }); } int indexToField(int index) { return (1 + 1 + index) * 2; } @reflectiveTest class CheckOtherLangaugesData { test_cppData() async { List data = await io.File(path.join( path.context.current, 'test', 'monsterdata_test.mon', )).readAsBytes(); example.Monster mon = example.Monster(data); expect(mon.hp, 80); expect(mon.mana, 150); expect(mon.name, 'MyMonster'); expect(mon.pos!.x, 1.0); expect(mon.pos!.y, 2.0); expect(mon.pos!.z, 3.0); expect(mon.pos!.test1, 3.0); expect(mon.pos!.test2.value, 2.0); expect(mon.pos!.test3.a, 5); expect(mon.pos!.test3.b, 6); expect(mon.testType!.value, example.AnyTypeId.Monster.value); expect(mon.test is example.Monster, true); final monster2 = mon.test as example.Monster; expect(monster2.name, "Fred"); expect(mon.inventory!.length, 5); expect(mon.inventory!.reduce((cur, next) => cur + next), 10); final test4 = mon.test4!; expect(test4.length, 2); expect(test4[0].a + test4[0].b + test4[1].a + test4[1].b, 100); expect(mon.testarrayofstring!.length, 2); expect(mon.testarrayofstring![0], "test1"); expect(mon.testarrayofstring![1], "test2"); // this will fail if accessing any field fails. expect( mon.toString(), 'Monster{' 'pos: Vec3{x: 1.0, y: 2.0, z: 3.0, test1: 3.0, test2: Color{value: 2}, test3: Test{a: 5, b: 6}}, ' 'mana: 150, hp: 80, name: MyMonster, inventory: [0, 1, 2, 3, 4], ' 'color: Color{value: 8}, testType: AnyTypeId{value: 1}, ' 'test: Monster{pos: null, mana: 150, hp: 100, name: Fred, ' 'inventory: null, color: Color{value: 8}, testType: null, ' 'test: null, test4: null, testarrayofstring: null, ' 'testarrayoftables: null, enemy: null, testnestedflatbuffer: null, ' 'testempty: null, testbool: false, testhashs32Fnv1: 0, ' 'testhashu32Fnv1: 0, testhashs64Fnv1: 0, testhashu64Fnv1: 0, ' 'testhashs32Fnv1a: 0, testhashu32Fnv1a: 0, testhashs64Fnv1a: 0, ' 'testhashu64Fnv1a: 0, testarrayofbools: null, testf: 3.14159, ' 'testf2: 3.0, testf3: 0.0, testarrayofstring2: null, ' 'testarrayofsortedstruct: null, flex: null, test5: null, ' 'vectorOfLongs: null, vectorOfDoubles: null, parentNamespaceTest: null, ' 'vectorOfReferrables: null, singleWeakReference: 0, ' 'vectorOfWeakReferences: null, vectorOfStrongReferrables: null, ' 'coOwningReference: 0, vectorOfCoOwningReferences: null, ' 'nonOwningReference: 0, vectorOfNonOwningReferences: null, ' 'anyUniqueType: null, anyUnique: null, anyAmbiguousType: null, ' 'anyAmbiguous: null, vectorOfEnums: null, signedEnum: Race{value: -1}, ' 'testrequirednestedflatbuffer: null, scalarKeySortedTables: null, ' 'nativeInline: null, ' 'longEnumNonEnumDefault: LongEnum{value: 0}, ' 'longEnumNormalDefault: LongEnum{value: 2}, nanDefault: NaN, ' 'infDefault: Infinity, positiveInfDefault: Infinity, infinityDefault: ' 'Infinity, positiveInfinityDefault: Infinity, negativeInfDefault: ' '-Infinity, negativeInfinityDefault: -Infinity, doubleInfDefault: Infinity}, ' 'test4: [Test{a: 10, b: 20}, Test{a: 30, b: 40}], ' 'testarrayofstring: [test1, test2], testarrayoftables: null, ' 'enemy: Monster{pos: null, mana: 150, hp: 100, name: Fred, ' 'inventory: null, color: Color{value: 8}, testType: null, ' 'test: null, test4: null, testarrayofstring: null, ' 'testarrayoftables: null, enemy: null, testnestedflatbuffer: null, ' 'testempty: null, testbool: false, testhashs32Fnv1: 0, ' 'testhashu32Fnv1: 0, testhashs64Fnv1: 0, testhashu64Fnv1: 0, ' 'testhashs32Fnv1a: 0, testhashu32Fnv1a: 0, testhashs64Fnv1a: 0, ' 'testhashu64Fnv1a: 0, testarrayofbools: null, testf: 3.14159, ' 'testf2: 3.0, testf3: 0.0, testarrayofstring2: null, ' 'testarrayofsortedstruct: null, flex: null, test5: null, ' 'vectorOfLongs: null, vectorOfDoubles: null, parentNamespaceTest: null, ' 'vectorOfReferrables: null, singleWeakReference: 0, ' 'vectorOfWeakReferences: null, vectorOfStrongReferrables: null, ' 'coOwningReference: 0, vectorOfCoOwningReferences: null, ' 'nonOwningReference: 0, vectorOfNonOwningReferences: null, ' 'anyUniqueType: null, anyUnique: null, anyAmbiguousType: null, ' 'anyAmbiguous: null, vectorOfEnums: null, signedEnum: Race{value: -1}, ' 'testrequirednestedflatbuffer: null, scalarKeySortedTables: null, ' 'nativeInline: null, ' 'longEnumNonEnumDefault: LongEnum{value: 0}, ' 'longEnumNormalDefault: LongEnum{value: 2}, nanDefault: NaN, ' 'infDefault: Infinity, positiveInfDefault: Infinity, infinityDefault: ' 'Infinity, positiveInfinityDefault: Infinity, negativeInfDefault: ' '-Infinity, negativeInfinityDefault: -Infinity, doubleInfDefault: Infinity}, ' 'testnestedflatbuffer: null, testempty: null, testbool: true, ' 'testhashs32Fnv1: -579221183, testhashu32Fnv1: 3715746113, ' 'testhashs64Fnv1: 7930699090847568257, ' 'testhashu64Fnv1: 7930699090847568257, ' 'testhashs32Fnv1a: -1904106383, testhashu32Fnv1a: 2390860913, ' 'testhashs64Fnv1a: 4898026182817603057, ' 'testhashu64Fnv1a: 4898026182817603057, ' 'testarrayofbools: [true, false, true], testf: 3.14159, testf2: 3.0, ' 'testf3: 0.0, testarrayofstring2: null, testarrayofsortedstruct: [' 'Ability{id: 0, distance: 45}, Ability{id: 1, distance: 21}, ' 'Ability{id: 5, distance: 12}], ' 'flex: null, test5: [Test{a: 10, b: 20}, Test{a: 30, b: 40}], ' 'vectorOfLongs: [1, 100, 10000, 1000000, 100000000], ' 'vectorOfDoubles: [-1.7976931348623157e+308, 0.0, 1.7976931348623157e+308], ' 'parentNamespaceTest: null, vectorOfReferrables: null, ' 'singleWeakReference: 0, vectorOfWeakReferences: null, ' 'vectorOfStrongReferrables: null, coOwningReference: 0, ' 'vectorOfCoOwningReferences: null, nonOwningReference: 0, ' 'vectorOfNonOwningReferences: null, ' 'anyUniqueType: null, anyUnique: null, ' 'anyAmbiguousType: null, ' 'anyAmbiguous: null, vectorOfEnums: null, signedEnum: Race{value: -1}, ' 'testrequirednestedflatbuffer: null, scalarKeySortedTables: [Stat{id: ' 'miss, val: 0, count: 0}, Stat{id: hit, val: 10, count: 1}], ' 'nativeInline: Test{a: 1, b: 2}, ' 'longEnumNonEnumDefault: LongEnum{value: 0}, ' 'longEnumNormalDefault: LongEnum{value: 2}, nanDefault: NaN, ' 'infDefault: Infinity, positiveInfDefault: Infinity, infinityDefault: ' 'Infinity, positiveInfinityDefault: Infinity, negativeInfDefault: ' '-Infinity, negativeInfinityDefault: -Infinity, doubleInfDefault: Infinity}' ); } } /// Test a custom, fixed-memory allocator (no actual allocations performed) class CustomAllocator extends Allocator { final _memory = ByteData(10 * 1024); int _used = 0; Uint8List buffer(int size) => _memory.buffer.asUint8List(_used - size, size); @override ByteData allocate(int size) { if (size > _memory.lengthInBytes) { throw UnsupportedError('Trying to allocate too much'); } _used = size; return ByteData.sublistView(_memory, 0, size); } @override void deallocate(ByteData _) {} } @reflectiveTest class BuilderTest { void test_monsterBuilder([Builder? builder]) { final fbBuilder = builder ?? Builder(); final str = fbBuilder.writeString('MyMonster'); fbBuilder.writeString('test1'); fbBuilder.writeString('test2', asciiOptimization: true); final testArrayOfString = fbBuilder.endStructVector(2); final fred = fbBuilder.writeString('Fred'); final List treasure = [0, 1, 2, 3, 4]; final inventory = fbBuilder.writeListUint8(treasure); final monBuilder = example.MonsterBuilder(fbBuilder) ..begin() ..addNameOffset(fred); final mon2 = monBuilder.finish(); final testBuilder = example.TestBuilder(fbBuilder); testBuilder.finish(10, 20); testBuilder.finish(30, 40); final test4 = fbBuilder.endStructVector(2); monBuilder ..begin() ..addPos( example.Vec3Builder(fbBuilder).finish( 1.0, 2.0, 3.0, 3.0, example.Color.Green, () => testBuilder.finish(5, 6), ), ) ..addHp(80) ..addNameOffset(str) ..addInventoryOffset(inventory) ..addTestType(example.AnyTypeId.Monster) ..addTestOffset(mon2) ..addTest4Offset(test4) ..addTestarrayofstringOffset(testArrayOfString); final mon = monBuilder.finish(); fbBuilder.finish(mon); } void test_error_addInt32_withoutStartTable([Builder? builder]) { builder ??= Builder(); expect(() { builder!.addInt32(0, 0); }, throwsA(isA())); } void test_error_addOffset_withoutStartTable() { Builder builder = Builder(); expect(() { builder.addOffset(0, 0); }, throwsA(isA())); } void test_error_endTable_withoutStartTable() { Builder builder = Builder(); expect(() { builder.endTable(); }, throwsA(isA())); } void test_error_startTable_duringTable() { Builder builder = Builder(); builder.startTable(0); expect(() { builder.startTable(0); }, throwsA(isA())); } void test_error_writeString_duringTable() { Builder builder = Builder(); builder.startTable(1); expect(() { builder.writeString('12345'); }, throwsA(isA())); } void test_file_identifier() { Uint8List byteList; { Builder builder = Builder(initialSize: 0); builder.startTable(0); int offset = builder.endTable(); builder.finish(offset, 'Az~ÿ'); byteList = builder.buffer; } // Convert byteList to a ByteData so that we can read data from it. ByteData byteData = byteList.buffer.asByteData(byteList.offsetInBytes); // First 4 bytes are an offset to the table data. int tableDataLoc = byteData.getUint32(0, Endian.little); // Next 4 bytes are the file identifier. expect(byteData.getUint8(4), 65); // 'a' expect(byteData.getUint8(5), 122); // 'z' expect(byteData.getUint8(6), 126); // '~' expect(byteData.getUint8(7), 255); // 'ÿ' // First 4 bytes of the table data are a backwards offset to the vtable. int vTableLoc = tableDataLoc - byteData.getInt32(tableDataLoc, Endian.little); // First 2 bytes of the vtable are the size of the vtable in bytes, which // should be 4. expect(byteData.getUint16(vTableLoc, Endian.little), 4); // Next 2 bytes are the size of the object in bytes (including the vtable // pointer), which should be 4. expect(byteData.getUint16(vTableLoc + 2, Endian.little), 4); } void test_low() { final allocator = CustomAllocator(); final builder = Builder(initialSize: 0, allocator: allocator); builder.putUint8(1); expect(allocator.buffer(builder.size()), [1]); builder.putUint32(2); expect(allocator.buffer(builder.size()), [2, 0, 0, 0, 0, 0, 0, 1]); builder.putUint8(3); expect( allocator.buffer(builder.size()), [0, 0, 0, 3, 2, 0, 0, 0, 0, 0, 0, 1]); builder.putUint8(4); expect( allocator.buffer(builder.size()), [0, 0, 4, 3, 2, 0, 0, 0, 0, 0, 0, 1]); builder.putUint8(5); expect( allocator.buffer(builder.size()), [0, 5, 4, 3, 2, 0, 0, 0, 0, 0, 0, 1]); builder.putUint32(6); expect(allocator.buffer(builder.size()), [6, 0, 0, 0, 0, 5, 4, 3, 2, 0, 0, 0, 0, 0, 0, 1]); } void test_table_default() { List byteList; { final builder = Builder(initialSize: 0, allocator: CustomAllocator()); builder.startTable(2); builder.addInt32(0, 10, 10); builder.addInt32(1, 20, 10); int offset = builder.endTable(); builder.finish(offset); byteList = builder.buffer; expect(builder.size(), byteList.length); } // read and verify BufferContext buffer = BufferContext.fromBytes(byteList); int objectOffset = buffer.derefObject(0); // was not written, so uses the new default value expect( const Int32Reader() .vTableGet(buffer, objectOffset, indexToField(0), 15), 15); // has the written value expect( const Int32Reader() .vTableGet(buffer, objectOffset, indexToField(1), 15), 20); } void test_table_format([Builder? builder]) { Uint8List byteList; { builder ??= Builder(initialSize: 0); builder.startTable(3); builder.addInt32(0, 10); builder.addInt32(1, 20); builder.addInt32(2, 30); builder.finish(builder.endTable()); byteList = builder.buffer; } // Convert byteList to a ByteData so that we can read data from it. ByteData byteData = byteList.buffer.asByteData(byteList.offsetInBytes); // First 4 bytes are an offset to the table data. int tableDataLoc = byteData.getUint32(0, Endian.little); // First 4 bytes of the table data are a backwards offset to the vtable. int vTableLoc = tableDataLoc - byteData.getInt32(tableDataLoc, Endian.little); // First 2 bytes of the vtable are the size of the vtable in bytes, which // should be 10. expect(byteData.getUint16(vTableLoc, Endian.little), 10); // Next 2 bytes are the size of the object in bytes (including the vtable // pointer), which should be 16. expect(byteData.getUint16(vTableLoc + 2, Endian.little), 16); // Remaining 6 bytes are the offsets within the object where the ints are // located. for (int i = 0; i < 3; i++) { int offset = byteData.getUint16(vTableLoc + 4 + 2 * i, Endian.little); expect( byteData.getInt32(tableDataLoc + offset, Endian.little), 10 + 10 * i); } } void test_table_string() { String latinString = 'test'; String unicodeString = 'Проба пера'; List byteList; { Builder builder = Builder(initialSize: 0); int? latinStringOffset = builder.writeString(latinString, asciiOptimization: true); int? unicodeStringOffset = builder.writeString(unicodeString, asciiOptimization: true); builder.startTable(2); builder.addOffset(0, latinStringOffset); builder.addOffset(1, unicodeStringOffset); int offset = builder.endTable(); builder.finish(offset); byteList = builder.buffer; } // read and verify BufferContext buf = BufferContext.fromBytes(byteList); int objectOffset = buf.derefObject(0); expect( const StringReader() .vTableGetNullable(buf, objectOffset, indexToField(0)), latinString); expect( const StringReader(asciiOptimization: true) .vTableGetNullable(buf, objectOffset, indexToField(1)), unicodeString); } void test_table_types([Builder? builder]) { List byteList; { builder ??= Builder(initialSize: 0); int? stringOffset = builder.writeString('12345'); builder.startTable(7); builder.addBool(0, true); builder.addInt8(1, 10); builder.addInt32(2, 20); builder.addOffset(3, stringOffset); builder.addInt32(4, 40); builder.addUint32(5, 0x9ABCDEF0); builder.addUint8(6, 0x9A); int offset = builder.endTable(); builder.finish(offset); byteList = builder.buffer; } // read and verify BufferContext buf = BufferContext.fromBytes(byteList); int objectOffset = buf.derefObject(0); expect( const BoolReader() .vTableGetNullable(buf, objectOffset, indexToField(0)), true); expect( const Int8Reader() .vTableGetNullable(buf, objectOffset, indexToField(1)), 10); expect( const Int32Reader() .vTableGetNullable(buf, objectOffset, indexToField(2)), 20); expect( const StringReader() .vTableGetNullable(buf, objectOffset, indexToField(3)), '12345'); expect( const Int32Reader() .vTableGetNullable(buf, objectOffset, indexToField(4)), 40); expect( const Uint32Reader() .vTableGetNullable(buf, objectOffset, indexToField(5)), 0x9ABCDEF0); expect( const Uint8Reader() .vTableGetNullable(buf, objectOffset, indexToField(6)), 0x9A); } void test_writeList_of_Uint32() { List values = [10, 100, 12345, 0x9abcdef0]; // write List byteList; { Builder builder = Builder(initialSize: 0); int offset = builder.writeListUint32(values); builder.finish(offset); byteList = builder.buffer; } // read and verify BufferContext buf = BufferContext.fromBytes(byteList); List items = const Uint32ListReader().read(buf, 0); expect(items, hasLength(4)); expect(items, orderedEquals(values)); } void test_writeList_ofBool() { void verifyListBooleans(int len, List trueBits) { // write List byteList; { Builder builder = Builder(initialSize: 0); List values = List.filled(len, false); for (int bit in trueBits) { values[bit] = true; } int offset = builder.writeListBool(values); builder.finish(offset); byteList = builder.buffer; } // read and verify BufferContext buf = BufferContext.fromBytes(byteList); List items = const BoolListReader().read(buf, 0); expect(items, hasLength(len)); for (int i = 0; i < items.length; i++) { expect(items[i], trueBits.contains(i), reason: 'bit $i of $len'); } } verifyListBooleans(0, []); verifyListBooleans(1, []); verifyListBooleans(1, [0]); verifyListBooleans(31, [0, 1]); verifyListBooleans(31, [1, 2, 24, 25, 30]); verifyListBooleans(31, [0, 30]); verifyListBooleans(32, [1, 2, 24, 25, 31]); verifyListBooleans(33, [1, 2, 24, 25, 32]); verifyListBooleans(33, [1, 2, 24, 25, 31, 32]); verifyListBooleans(63, []); verifyListBooleans(63, [0, 1, 2, 61, 62]); verifyListBooleans(63, List.generate(63, (i) => i)); verifyListBooleans(64, []); verifyListBooleans(64, [0, 1, 2, 61, 62, 63]); verifyListBooleans(64, [1, 2, 62]); verifyListBooleans(64, [0, 1, 2, 63]); verifyListBooleans(64, List.generate(64, (i) => i)); verifyListBooleans(100, [0, 3, 30, 60, 90, 99]); } void test_writeList_ofInt32() { List byteList; { Builder builder = Builder(initialSize: 0); int offset = builder.writeListInt32([1, 2, 3, 4, 5]); builder.finish(offset); byteList = builder.buffer; } // read and verify BufferContext buf = BufferContext.fromBytes(byteList); List items = const ListReader(Int32Reader()).read(buf, 0); expect(items, hasLength(5)); expect(items, orderedEquals([1, 2, 3, 4, 5])); } void test_writeList_ofFloat64() { List values = [-1.234567, 3.4E+9, -5.6E-13, 7.8, 12.13]; // write List byteList; { Builder builder = Builder(initialSize: 0); int offset = builder.writeListFloat64(values); builder.finish(offset); byteList = builder.buffer; } // read and verify BufferContext buf = BufferContext.fromBytes(byteList); List items = const Float64ListReader().read(buf, 0); expect(items, hasLength(values.length)); for (int i = 0; i < values.length; i++) { expect(values[i], closeTo(items[i], .001)); } } void test_writeList_ofFloat32() { List values = [1.0, 2.23, -3.213, 7.8, 12.13]; // write List byteList; { Builder builder = Builder(initialSize: 0); int offset = builder.writeListFloat32(values); builder.finish(offset); byteList = builder.buffer; } // read and verify BufferContext buf = BufferContext.fromBytes(byteList); List items = const Float32ListReader().read(buf, 0); expect(items, hasLength(5)); for (int i = 0; i < values.length; i++) { expect(values[i], closeTo(items[i], .001)); } } void test_writeList_ofObjects([Builder? builder]) { List byteList; { builder ??= Builder(initialSize: 0); // write the object #1 int object1; { builder.startTable(2); builder.addInt32(0, 10); builder.addInt32(1, 20); object1 = builder.endTable(); } // write the object #1 int object2; { builder.startTable(2); builder.addInt32(0, 100); builder.addInt32(1, 200); object2 = builder.endTable(); } // write the list int offset = builder.writeList([object1, object2]); builder.finish(offset); byteList = builder.buffer; } // read and verify BufferContext buf = BufferContext.fromBytes(byteList); List items = const ListReader(TestPointReader()).read(buf, 0); expect(items, hasLength(2)); expect(items[0].x, 10); expect(items[0].y, 20); expect(items[1].x, 100); expect(items[1].y, 200); } void test_writeList_ofStrings_asRoot() { List byteList; { Builder builder = Builder(initialSize: 0); int? str1 = builder.writeString('12345'); int? str2 = builder.writeString('ABC'); int offset = builder.writeList([str1, str2]); builder.finish(offset); byteList = builder.buffer; } // read and verify BufferContext buf = BufferContext.fromBytes(byteList); List items = const ListReader(StringReader()).read(buf, 0); expect(items, hasLength(2)); expect(items, contains('12345')); expect(items, contains('ABC')); } void test_writeList_ofStrings_inObject([Builder? builder]) { List byteList; { builder ??= Builder(initialSize: 0); int listOffset = builder.writeList( [builder.writeString('12345'), builder.writeString('ABC')]); builder.startTable(1); builder.addOffset(0, listOffset); int offset = builder.endTable(); builder.finish(offset); byteList = builder.buffer; } // read and verify BufferContext buf = BufferContext.fromBytes(byteList); StringListWrapperImpl reader = StringListWrapperReader().read(buf, 0); List? items = reader.items; expect(items, hasLength(2)); expect(items, contains('12345')); expect(items, contains('ABC')); } void test_writeList_ofUint32() { List byteList; { Builder builder = Builder(initialSize: 0); int offset = builder.writeListUint32([1, 2, 0x9ABCDEF0]); builder.finish(offset); byteList = builder.buffer; } // read and verify BufferContext buf = BufferContext.fromBytes(byteList); List items = const Uint32ListReader().read(buf, 0); expect(items, hasLength(3)); expect(items, orderedEquals([1, 2, 0x9ABCDEF0])); } void test_writeList_ofUint16() { List byteList; { Builder builder = Builder(initialSize: 0); int offset = builder.writeListUint16([1, 2, 60000]); builder.finish(offset); byteList = builder.buffer; } // read and verify BufferContext buf = BufferContext.fromBytes(byteList); List items = const Uint16ListReader().read(buf, 0); expect(items, hasLength(3)); expect(items, orderedEquals([1, 2, 60000])); } void test_writeList_ofUint8() { List byteList; { Builder builder = Builder(initialSize: 0); int offset = builder.writeListUint8([1, 2, 3, 4, 0x9A, 0xFA]); builder.finish(offset); byteList = builder.buffer; } // read and verify BufferContext buf = BufferContext.fromBytes(byteList); const buffOffset = 8; // 32-bit offset to the list, + 32-bit length for (final lazy in [true, false]) { List items = Uint8ListReader(lazy: lazy).read(buf, 0); expect(items, hasLength(6)); expect(items, orderedEquals([1, 2, 3, 4, 0x9A, 0xFA])); // overwrite the buffer to verify the laziness buf.buffer.setUint8(buffOffset + 1, 99); expect(items, orderedEquals([1, lazy ? 99 : 2, 3, 4, 0x9A, 0xFA])); // restore the previous value for the next loop buf.buffer.setUint8(buffOffset + 1, 2); } } void test_reset() { // We'll run a selection of tests , reusing the builder between them. final testCases = [ test_monsterBuilder, test_error_addInt32_withoutStartTable, test_table_format, test_table_types, test_writeList_ofObjects, test_writeList_ofStrings_inObject ]; // Execute all test cases in all permutations of their order. // To do that, we generate permutations of test case indexes. final testCasesPermutations = _permutationsOf(List.generate(testCases.length, (index) => index)); expect(testCasesPermutations.length, _factorial(testCases.length)); for (var indexes in testCasesPermutations) { // print the order so failures are reproducible printOnFailure('Running reset() test cases in order: $indexes'); Builder? builder; for (var index in indexes) { if (builder == null) { // Initial size small enough so at least one test case increases it. // On the other hand, it's large enough so that some test cases don't. builder = Builder(initialSize: 32); } else { builder.reset(); } testCases[index](builder); } } } // Generate permutations of the given list List> _permutationsOf(List source) { final result = >[]; void permutate(List items, int startAt) { for (var i = startAt; i < items.length; i++) { List permutation = items.toList(growable: false); permutation[i] = items[startAt]; permutation[startAt] = items[i]; // add the current list upon reaching the end if (startAt == items.length - 1) { result.add(items); } else { permutate(permutation, startAt + 1); } } } permutate(source, 0); return result; } // a very simple implementation of n! int _factorial(int n) { var result = 1; for (var i = 2; i <= n; i++) { result *= i; } return result; } } @reflectiveTest class ObjectAPITest { void test_tableStat() { final object1 = example.StatT(count: 3, id: "foo", val: 4); expect(object1 is Packable, isTrue); final fbb = Builder(); fbb.finish(object1.pack(fbb)); final object2 = example.Stat(fbb.buffer).unpack(); expect(object2.count, object1.count); expect(object2.id, object1.id); expect(object2.val, object1.val); expect(object2.toString(), object1.toString()); } void test_tableMonster() { final monster = example.MonsterT() ..pos = example.Vec3T( x: 1, y: 2, z: 3, test1: 4.0, test2: example.Color.Red, test3: example.TestT(a: 1, b: 2)) ..mana = 2 ..name = 'Monstrous' ..inventory = [24, 42] ..color = example.Color.Green // TODO be smarter for unions and automatically set the `type` field? ..testType = example.AnyTypeId.MyGame_Example2_Monster ..test = example2.MonsterT() ..test4 = [example.TestT(a: 3, b: 4), example.TestT(a: 5, b: 6)] ..testarrayofstring = ["foo", "bar"] ..testarrayoftables = [example.MonsterT(name: 'Oof')] ..enemy = example.MonsterT(name: 'Enemy') ..testarrayofbools = [false, true, false] ..testf = 42.24 ..testarrayofsortedstruct = [ example.AbilityT(id: 1, distance: 5), example.AbilityT(id: 3, distance: 7) ] ..vectorOfLongs = [5, 6, 7] ..vectorOfDoubles = [8.9, 9.0, 10.1, 11.2] ..anyAmbiguousType = example.AnyAmbiguousAliasesTypeId.M2 ..anyAmbiguous = null ..vectorOfEnums = [example.Color.Blue, example.Color.Green] ..signedEnum = example.Race.None; final fbBuilder = Builder(); final offset = monster.pack(fbBuilder); expect(offset, isNonZero); fbBuilder.finish(offset); final data = fbBuilder.buffer; // TODO currently broken because of struct builder issue, see #6688 // final monster2 = example.Monster(data); // Monster (reader) // expect( // // map Monster => MonsterT, Vec3 => Vec3T, ... // monster2.toString().replaceAllMapped( // RegExp('([a-zA-z0-9]+){'), (match) => match.group(1) + 'T{'), // monster.toString()); // // final monster3 = monster2.unpack(); // MonsterT // expect(monster3.toString(), monster.toString()); } void test_Lists() { // Ensure unpack() reads lists eagerly by reusing the same builder and // overwriting data. Why: because standard reader reads lists lazily... final fbb = Builder(); final object1 = example.TypeAliasesT(v8: [1, 2, 3], vf64: [5, 6]); fbb.finish(object1.pack(fbb)); final object1Read = example.TypeAliases(fbb.buffer).unpack(); // overwrite the original buffer by writing to the same builder fbb.reset(); final object2 = example.TypeAliasesT(v8: [7, 8, 9], vf64: [10, 11]); fbb.finish(object2.pack(fbb)); final object2Read = example.TypeAliases(fbb.buffer).unpack(); // this is fine even with lazy lists: expect(object2.toString(), object2Read.toString()); // this fails with lazy lists: expect(object1.toString(), object1Read.toString()); // empty list must be serialized as such (were stored NULL before v2.0) fbb.reset(); final object3 = example.TypeAliasesT(v8: [], vf64: null); fbb.finish(object3.pack(fbb)); final object3Read = example.TypeAliases(fbb.buffer).unpack(); expect(object3.toString(), object3Read.toString()); } } class StringListWrapperImpl { final BufferContext bp; final int offset; StringListWrapperImpl(this.bp, this.offset); List? get items => const ListReader(StringReader()) .vTableGetNullable(bp, offset, indexToField(0)); } class StringListWrapperReader extends TableReader { const StringListWrapperReader(); @override StringListWrapperImpl createObject(BufferContext object, int offset) { return StringListWrapperImpl(object, offset); } } class TestPointImpl { final BufferContext bp; final int offset; TestPointImpl(this.bp, this.offset); int get x => const Int32Reader().vTableGet(bp, offset, indexToField(0), 0); int get y => const Int32Reader().vTableGet(bp, offset, indexToField(1), 0); } class TestPointReader extends TableReader { const TestPointReader(); @override TestPointImpl createObject(BufferContext object, int offset) { return TestPointImpl(object, offset); } } @reflectiveTest class GeneratorTest { void test_constantEnumValues() async { expect(example.Color.values, same(example.Color.values)); expect(example.Race.values, same(example.Race.values)); expect(example.AnyTypeId.values, same(example.AnyTypeId.values)); expect(example.AnyUniqueAliasesTypeId.values, same(example.AnyUniqueAliasesTypeId.values)); expect(example.AnyAmbiguousAliasesTypeId.values, same(example.AnyAmbiguousAliasesTypeId.values)); } } // See #6869 @reflectiveTest class ListOfEnumsTest { void test_listOfEnums() async { var mytable = example3.MyTableObjectBuilder(options: [ example3.OptionsEnum.A, example3.OptionsEnum.B, example3.OptionsEnum.C ]); var bytes = mytable.toBytes(); var mytable_read = example3.MyTable(bytes); expect(mytable_read.options![0].value, example3.OptionsEnum.A.value); expect(mytable_read.options![1].value, example3.OptionsEnum.B.value); expect(mytable_read.options![2].value, example3.OptionsEnum.C.value); } } @reflectiveTest class BoolInStructTest { void test_boolInStruct() async { var mystruct = example4.FooObjectBuilder( myFoo: example4.FooPropertiesObjectBuilder(a: true, b: false)); var bytes = mystruct.toBytes(); var mystruct_read = example4.Foo(bytes); expect(mystruct_read.myFoo!.a, true); expect(mystruct_read.myFoo!.b, false); } }