943 lines
33 KiB
Dart
943 lines
33 KiB
Dart
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<int> 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<int> 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<AssertionError>()));
|
|
}
|
|
|
|
void test_error_addOffset_withoutStartTable() {
|
|
Builder builder = Builder();
|
|
expect(() {
|
|
builder.addOffset(0, 0);
|
|
}, throwsA(isA<AssertionError>()));
|
|
}
|
|
|
|
void test_error_endTable_withoutStartTable() {
|
|
Builder builder = Builder();
|
|
expect(() {
|
|
builder.endTable();
|
|
}, throwsA(isA<AssertionError>()));
|
|
}
|
|
|
|
void test_error_startTable_duringTable() {
|
|
Builder builder = Builder();
|
|
builder.startTable(0);
|
|
expect(() {
|
|
builder.startTable(0);
|
|
}, throwsA(isA<AssertionError>()));
|
|
}
|
|
|
|
void test_error_writeString_duringTable() {
|
|
Builder builder = Builder();
|
|
builder.startTable(1);
|
|
expect(() {
|
|
builder.writeString('12345');
|
|
}, throwsA(isA<AssertionError>()));
|
|
}
|
|
|
|
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<int> 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<int> 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<int> 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<int> values = <int>[10, 100, 12345, 0x9abcdef0];
|
|
// write
|
|
List<int> 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<int> items = const Uint32ListReader().read(buf, 0);
|
|
expect(items, hasLength(4));
|
|
expect(items, orderedEquals(values));
|
|
}
|
|
|
|
void test_writeList_ofBool() {
|
|
void verifyListBooleans(int len, List<int> trueBits) {
|
|
// write
|
|
List<int> byteList;
|
|
{
|
|
Builder builder = Builder(initialSize: 0);
|
|
List<bool> values = List<bool>.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<bool> 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, <int>[]);
|
|
verifyListBooleans(1, <int>[]);
|
|
verifyListBooleans(1, <int>[0]);
|
|
verifyListBooleans(31, <int>[0, 1]);
|
|
verifyListBooleans(31, <int>[1, 2, 24, 25, 30]);
|
|
verifyListBooleans(31, <int>[0, 30]);
|
|
verifyListBooleans(32, <int>[1, 2, 24, 25, 31]);
|
|
verifyListBooleans(33, <int>[1, 2, 24, 25, 32]);
|
|
verifyListBooleans(33, <int>[1, 2, 24, 25, 31, 32]);
|
|
verifyListBooleans(63, <int>[]);
|
|
verifyListBooleans(63, <int>[0, 1, 2, 61, 62]);
|
|
verifyListBooleans(63, List<int>.generate(63, (i) => i));
|
|
verifyListBooleans(64, <int>[]);
|
|
verifyListBooleans(64, <int>[0, 1, 2, 61, 62, 63]);
|
|
verifyListBooleans(64, <int>[1, 2, 62]);
|
|
verifyListBooleans(64, <int>[0, 1, 2, 63]);
|
|
verifyListBooleans(64, List<int>.generate(64, (i) => i));
|
|
verifyListBooleans(100, <int>[0, 3, 30, 60, 90, 99]);
|
|
}
|
|
|
|
void test_writeList_ofInt32() {
|
|
List<int> byteList;
|
|
{
|
|
Builder builder = Builder(initialSize: 0);
|
|
int offset = builder.writeListInt32(<int>[1, 2, 3, 4, 5]);
|
|
builder.finish(offset);
|
|
byteList = builder.buffer;
|
|
}
|
|
// read and verify
|
|
BufferContext buf = BufferContext.fromBytes(byteList);
|
|
List<int> items = const ListReader<int>(Int32Reader()).read(buf, 0);
|
|
expect(items, hasLength(5));
|
|
expect(items, orderedEquals(<int>[1, 2, 3, 4, 5]));
|
|
}
|
|
|
|
void test_writeList_ofFloat64() {
|
|
List<double> values = <double>[-1.234567, 3.4E+9, -5.6E-13, 7.8, 12.13];
|
|
// write
|
|
List<int> 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<double> 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<double> values = [1.0, 2.23, -3.213, 7.8, 12.13];
|
|
// write
|
|
List<int> 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<double> 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<int> 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<TestPointImpl> items =
|
|
const ListReader<TestPointImpl>(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<int> 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<String> items = const ListReader<String>(StringReader()).read(buf, 0);
|
|
expect(items, hasLength(2));
|
|
expect(items, contains('12345'));
|
|
expect(items, contains('ABC'));
|
|
}
|
|
|
|
void test_writeList_ofStrings_inObject([Builder? builder]) {
|
|
List<int> 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<String>? items = reader.items;
|
|
expect(items, hasLength(2));
|
|
expect(items, contains('12345'));
|
|
expect(items, contains('ABC'));
|
|
}
|
|
|
|
void test_writeList_ofUint32() {
|
|
List<int> byteList;
|
|
{
|
|
Builder builder = Builder(initialSize: 0);
|
|
int offset = builder.writeListUint32(<int>[1, 2, 0x9ABCDEF0]);
|
|
builder.finish(offset);
|
|
byteList = builder.buffer;
|
|
}
|
|
// read and verify
|
|
BufferContext buf = BufferContext.fromBytes(byteList);
|
|
List<int> items = const Uint32ListReader().read(buf, 0);
|
|
expect(items, hasLength(3));
|
|
expect(items, orderedEquals(<int>[1, 2, 0x9ABCDEF0]));
|
|
}
|
|
|
|
void test_writeList_ofUint16() {
|
|
List<int> byteList;
|
|
{
|
|
Builder builder = Builder(initialSize: 0);
|
|
int offset = builder.writeListUint16(<int>[1, 2, 60000]);
|
|
builder.finish(offset);
|
|
byteList = builder.buffer;
|
|
}
|
|
// read and verify
|
|
BufferContext buf = BufferContext.fromBytes(byteList);
|
|
List<int> items = const Uint16ListReader().read(buf, 0);
|
|
expect(items, hasLength(3));
|
|
expect(items, orderedEquals(<int>[1, 2, 60000]));
|
|
}
|
|
|
|
void test_writeList_ofUint8() {
|
|
List<int> byteList;
|
|
{
|
|
Builder builder = Builder(initialSize: 0);
|
|
int offset = builder.writeListUint8(<int>[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<int> items = Uint8ListReader(lazy: lazy).read(buf, 0);
|
|
expect(items, hasLength(6));
|
|
expect(items, orderedEquals(<int>[1, 2, 3, 4, 0x9A, 0xFA]));
|
|
|
|
// overwrite the buffer to verify the laziness
|
|
buf.buffer.setUint8(buffOffset + 1, 99);
|
|
expect(items, orderedEquals(<int>[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 = <void Function(Builder?)>[
|
|
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<List<T>> _permutationsOf<T>(List<T> source) {
|
|
final result = <List<T>>[];
|
|
|
|
void permutate(List<T> items, int startAt) {
|
|
for (var i = startAt; i < items.length; i++) {
|
|
List<T> 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<String>? get items => const ListReader<String>(StringReader())
|
|
.vTableGetNullable(bp, offset, indexToField(0));
|
|
}
|
|
|
|
class StringListWrapperReader extends TableReader<StringListWrapperImpl> {
|
|
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<TestPointImpl> {
|
|
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);
|
|
}
|
|
}
|