| // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file | |
| // for details. All rights reserved. Use of this source code is governed by a | |
| // BSD-style license that can be found in the LICENSE file. | |
| import 'dart:typed_data'; | |
| import 'package:analyzer/src/summary/flat_buffers.dart'; | |
| import 'package:test/test.dart'; | |
| import 'package:test_reflective_loader/test_reflective_loader.dart'; | |
| main() { | |
| defineReflectiveSuite(() { | |
| defineReflectiveTests(BuilderTest); | |
| }); | |
| } | |
| @reflectiveTest | |
| class BuilderTest { | |
| void test_error_addInt32_withoutStartTable() { | |
| Builder builder = Builder(); | |
| expect(() { | |
| builder.addInt32(0, 0); | |
| }, throwsStateError); | |
| } | |
| void test_error_addOffset_withoutStartTable() { | |
| Builder builder = Builder(); | |
| expect(() { | |
| builder.addOffset(0, Offset(0)); | |
| }, throwsStateError); | |
| } | |
| void test_error_endTable_withoutStartTable() { | |
| Builder builder = Builder(); | |
| expect(() { | |
| builder.endTable(); | |
| }, throwsStateError); | |
| } | |
| void test_error_startTable_duringTable() { | |
| Builder builder = Builder(); | |
| builder.startTable(); | |
| expect(() { | |
| builder.startTable(); | |
| }, throwsStateError); | |
| } | |
| void test_error_writeString_duringTable() { | |
| Builder builder = Builder(); | |
| builder.startTable(); | |
| expect(() { | |
| builder.writeString('12345'); | |
| }, throwsStateError); | |
| } | |
| void test_file_identifier() { | |
| Uint8List byteList; | |
| { | |
| Builder builder = Builder(initialSize: 0); | |
| builder.startTable(); | |
| Offset offset = builder.endTable(); | |
| byteList = builder.finish(offset, 'Az~ÿ'); | |
| } | |
| // 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() { | |
| Builder builder = Builder(initialSize: 0); | |
| builder.lowReset(); | |
| expect((builder..lowWriteUint8(1)).lowFinish(), [1]); | |
| expect((builder..lowWriteUint32(2)).lowFinish(), [2, 0, 0, 0, 0, 0, 0, 1]); | |
| expect((builder..lowWriteUint8(3)).lowFinish(), [ | |
| 0, | |
| 0, | |
| 0, | |
| 3, | |
| 2, | |
| 0, | |
| 0, | |
| 0, | |
| 0, | |
| 0, | |
| 0, | |
| 1, | |
| ]); | |
| expect((builder..lowWriteUint8(4)).lowFinish(), [ | |
| 0, | |
| 0, | |
| 4, | |
| 3, | |
| 2, | |
| 0, | |
| 0, | |
| 0, | |
| 0, | |
| 0, | |
| 0, | |
| 1, | |
| ]); | |
| expect((builder..lowWriteUint8(5)).lowFinish(), [ | |
| 0, | |
| 5, | |
| 4, | |
| 3, | |
| 2, | |
| 0, | |
| 0, | |
| 0, | |
| 0, | |
| 0, | |
| 0, | |
| 1, | |
| ]); | |
| expect((builder..lowWriteUint32(6)).lowFinish(), [ | |
| 6, | |
| 0, | |
| 0, | |
| 0, | |
| 0, | |
| 5, | |
| 4, | |
| 3, | |
| 2, | |
| 0, | |
| 0, | |
| 0, | |
| 0, | |
| 0, | |
| 0, | |
| 1, | |
| ]); | |
| } | |
| void test_table_default() { | |
| List<int> byteList; | |
| { | |
| Builder builder = Builder(initialSize: 0); | |
| builder.startTable(); | |
| builder.addInt32(0, 10, 10); | |
| builder.addInt32(1, 20, 10); | |
| Offset offset = builder.endTable(); | |
| byteList = builder.finish(offset); | |
| } | |
| // 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, 0, 15), 15); | |
| // has the written value | |
| expect(const Int32Reader().vTableGet(buffer, objectOffset, 1, 15), 20); | |
| } | |
| void test_table_format() { | |
| Uint8List byteList; | |
| { | |
| Builder builder = Builder(initialSize: 0); | |
| builder.startTable(); | |
| builder.addInt32(0, 10); | |
| builder.addInt32(1, 20); | |
| builder.addInt32(2, 30); | |
| byteList = builder.finish(builder.endTable()); | |
| } | |
| // 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); | |
| Offset<String> latinStringOffset = builder.writeString(latinString); | |
| Offset<String> unicodeStringOffset = builder.writeString(unicodeString); | |
| builder.startTable(); | |
| builder.addOffset(0, latinStringOffset); | |
| builder.addOffset(1, unicodeStringOffset); | |
| Offset offset = builder.endTable(); | |
| byteList = builder.finish(offset); | |
| } | |
| // read and verify | |
| BufferContext buf = BufferContext.fromBytes(byteList); | |
| int objectOffset = buf.derefObject(0); | |
| expect( | |
| const StringReader().vTableGetOrNull(buf, objectOffset, 0), | |
| latinString, | |
| ); | |
| expect( | |
| const StringReader().vTableGetOrNull(buf, objectOffset, 1), | |
| unicodeString, | |
| ); | |
| } | |
| void test_table_types() { | |
| List<int> byteList; | |
| { | |
| Builder builder = Builder(initialSize: 0); | |
| Offset<String> stringOffset = builder.writeString('12345'); | |
| builder.startTable(); | |
| 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); | |
| builder.addFloat64(7, -12.34); | |
| Offset offset = builder.endTable(); | |
| byteList = builder.finish(offset); | |
| } | |
| // read and verify | |
| BufferContext buf = BufferContext.fromBytes(byteList); | |
| int objectOffset = buf.derefObject(0); | |
| expect(const BoolReader().vTableGetOrNull(buf, objectOffset, 0), true); | |
| expect(const Int8Reader().vTableGetOrNull(buf, objectOffset, 1), 10); | |
| expect(const Int32Reader().vTableGetOrNull(buf, objectOffset, 2), 20); | |
| expect(const StringReader().vTableGetOrNull(buf, objectOffset, 3), '12345'); | |
| expect(const Int32Reader().vTableGetOrNull(buf, objectOffset, 4), 40); | |
| expect( | |
| const Uint32Reader().vTableGetOrNull(buf, objectOffset, 5), | |
| 0x9ABCDEF0, | |
| ); | |
| expect(const Uint8Reader().vTableGetOrNull(buf, objectOffset, 6), 0x9A); | |
| expect(const Float64Reader().vTableGetOrNull(buf, objectOffset, 7), -12.34); | |
| } | |
| void test_writeList_of_Uint32() { | |
| List<int> values = <int>[10, 100, 12345, 0x9abcdef0]; | |
| // write | |
| List<int> byteList; | |
| { | |
| Builder builder = Builder(initialSize: 0); | |
| Offset offset = builder.writeListUint32(values); | |
| byteList = builder.finish(offset); | |
| } | |
| // 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; | |
| } | |
| Offset offset = builder.writeListBool(values); | |
| byteList = builder.finish(offset); | |
| } | |
| // 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_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); | |
| Offset offset = builder.writeListFloat64(values); | |
| byteList = builder.finish(offset); | |
| } | |
| // read and verify | |
| BufferContext buf = BufferContext.fromBytes(byteList); | |
| List<double> items = const Float64ListReader().read(buf, 0); | |
| expect(items, hasLength(5)); | |
| expect(items, orderedEquals(values)); | |
| } | |
| void test_writeList_ofInt32() { | |
| List<int> byteList; | |
| { | |
| Builder builder = Builder(initialSize: 0); | |
| Offset offset = builder.writeListInt32(<int>[1, 2, 3, 4, 5]); | |
| byteList = builder.finish(offset); | |
| } | |
| // 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_ofObjects() { | |
| List<int> byteList; | |
| { | |
| Builder builder = Builder(initialSize: 0); | |
| // write the object #1 | |
| Offset object1; | |
| { | |
| builder.startTable(); | |
| builder.addInt32(0, 10); | |
| builder.addInt32(1, 20); | |
| object1 = builder.endTable(); | |
| } | |
| // write the object #1 | |
| Offset object2; | |
| { | |
| builder.startTable(); | |
| builder.addInt32(0, 100); | |
| builder.addInt32(1, 200); | |
| object2 = builder.endTable(); | |
| } | |
| // write the list | |
| Offset offset = builder.writeList([object1, object2]); | |
| byteList = builder.finish(offset); | |
| } | |
| // 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); | |
| Offset<String> str1 = builder.writeString('12345'); | |
| Offset<String> str2 = builder.writeString('ABC'); | |
| Offset offset = builder.writeList([str1, str2]); | |
| byteList = builder.finish(offset); | |
| } | |
| // 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() { | |
| List<int> byteList; | |
| { | |
| Builder builder = Builder(initialSize: 0); | |
| Offset listOffset = builder.writeList([ | |
| builder.writeString('12345'), | |
| builder.writeString('ABC'), | |
| ]); | |
| builder.startTable(); | |
| builder.addOffset(0, listOffset); | |
| Offset offset = builder.endTable(); | |
| byteList = builder.finish(offset); | |
| } | |
| // 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); | |
| Offset offset = builder.writeListUint32(<int>[1, 2, 0x9ABCDEF0]); | |
| byteList = builder.finish(offset); | |
| } | |
| // 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_ofUint8() { | |
| List<int> byteList; | |
| { | |
| Builder builder = Builder(initialSize: 0); | |
| Offset offset = builder.writeListUint8(<int>[1, 2, 3, 4, 0x9A]); | |
| byteList = builder.finish(offset); | |
| } | |
| // read and verify | |
| BufferContext buf = BufferContext.fromBytes(byteList); | |
| List<int> items = const Uint8ListReader().read(buf, 0); | |
| expect(items, hasLength(5)); | |
| expect(items, orderedEquals(<int>[1, 2, 3, 4, 0x9A])); | |
| } | |
| } | |
| class StringListWrapperImpl { | |
| final BufferContext bp; | |
| final int offset; | |
| StringListWrapperImpl(this.bp, this.offset); | |
| List<String>? get items => | |
| const ListReader<String>(StringReader()).vTableGetOrNull(bp, offset, 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, 0, 0); | |
| int get y => const Int32Reader().vTableGet(bp, offset, 1, 0); | |
| } | |
| class TestPointReader extends TableReader<TestPointImpl> { | |
| const TestPointReader(); | |
| @override | |
| TestPointImpl createObject(BufferContext object, int offset) { | |
| return TestPointImpl(object, offset); | |
| } | |
| } |