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