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