blob: adb1fd52ad9f17238b721bf2a60e3dc9036f0c6c [file] [log] [blame]
// 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);
}
}