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