• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Run this using JavaScriptTest.sh
2var assert = require('assert');
3var fs = require('fs');
4
5var flatbuffers = require('../js/flatbuffers').flatbuffers;
6var MyGame = require(process.argv[2]).MyGame;
7
8function main() {
9
10  // First, let's test reading a FlatBuffer generated by C++ code:
11  // This file was generated from monsterdata_test.json
12  var data = new Uint8Array(fs.readFileSync('monsterdata_test.mon'));
13
14  // Now test it:
15
16  var bb = new flatbuffers.ByteBuffer(data);
17  testBuffer(bb);
18
19  // Second, let's create a FlatBuffer from scratch in JavaScript, and test it also.
20  // We use an initial size of 1 to exercise the reallocation algorithm,
21  // normally a size larger than the typical FlatBuffer you generate would be
22  // better for performance.
23  var fbb = new flatbuffers.Builder(1);
24
25  // We set up the same values as monsterdata.json:
26
27  var str = fbb.createString('MyMonster');
28
29  var inv = MyGame.Example.Monster.createInventoryVector(fbb, [0, 1, 2, 3, 4]);
30
31  var fred = fbb.createString('Fred');
32  MyGame.Example.Monster.startMonster(fbb);
33  MyGame.Example.Monster.addName(fbb, fred);
34  var mon2 = MyGame.Example.Monster.endMonster(fbb);
35
36  MyGame.Example.Monster.startTest4Vector(fbb, 2);
37  MyGame.Example.Test.createTest(fbb, 10, 20);
38  MyGame.Example.Test.createTest(fbb, 30, 40);
39  var test4 = fbb.endVector();
40
41  var testArrayOfString = MyGame.Example.Monster.createTestarrayofstringVector(fbb, [
42    fbb.createString('test1'),
43    fbb.createString('test2')
44  ]);
45
46  MyGame.Example.Monster.startMonster(fbb);
47  MyGame.Example.Monster.addPos(fbb, MyGame.Example.Vec3.createVec3(fbb, 1, 2, 3, 3, MyGame.Example.Color.Green, 5, 6));
48  MyGame.Example.Monster.addHp(fbb, 80);
49  MyGame.Example.Monster.addName(fbb, str);
50  MyGame.Example.Monster.addInventory(fbb, inv);
51  MyGame.Example.Monster.addTestType(fbb, MyGame.Example.Any.Monster);
52  MyGame.Example.Monster.addTest(fbb, mon2);
53  MyGame.Example.Monster.addTest4(fbb, test4);
54  MyGame.Example.Monster.addTestarrayofstring(fbb, testArrayOfString);
55  MyGame.Example.Monster.addTestbool(fbb, false);
56  var mon = MyGame.Example.Monster.endMonster(fbb);
57
58  MyGame.Example.Monster.finishMonsterBuffer(fbb, mon);
59
60  // Write the result to a file for debugging purposes:
61  // Note that the binaries are not necessarily identical, since the JSON
62  // parser may serialize in a slightly different order than the above
63  // JavaScript code. They are functionally equivalent though.
64
65  fs.writeFileSync('monsterdata_javascript_wire.mon', new Buffer(fbb.asUint8Array()));
66
67  // Tests mutation first.  This will verify that we did not trample any other
68  // part of the byte buffer.
69  testMutation(fbb.dataBuffer());
70
71  testBuffer(fbb.dataBuffer());
72
73  test64bit();
74  testUnicode();
75  fuzzTest1();
76
77  console.log('FlatBuffers test: completed successfully');
78}
79
80function testMutation(bb) {
81  var monster = MyGame.Example.Monster.getRootAsMonster(bb);
82
83  monster.mutate_hp(120);
84  assert.strictEqual(monster.hp(), 120);
85
86  monster.mutate_hp(80);
87  assert.strictEqual(monster.hp(), 80);
88
89  var manaRes = monster.mutate_mana(10);
90  assert.strictEqual(manaRes, false);  // Field was NOT present, because default value.
91
92  // TODO: There is not the availability to mutate structs or vectors.
93}
94
95function testBuffer(bb) {
96  assert.ok(MyGame.Example.Monster.bufferHasIdentifier(bb));
97
98  var monster = MyGame.Example.Monster.getRootAsMonster(bb);
99
100  assert.strictEqual(monster.hp(), 80);
101  assert.strictEqual(monster.mana(), 150); // default
102
103  assert.strictEqual(monster.name(), 'MyMonster');
104
105  var pos = monster.pos();
106  assert.strictEqual(pos.x(), 1);
107  assert.strictEqual(pos.y(), 2);
108  assert.strictEqual(pos.z(), 3);
109  assert.strictEqual(pos.test1(), 3);
110  assert.strictEqual(pos.test2(), MyGame.Example.Color.Green);
111  var t = pos.test3();
112  assert.strictEqual(t.a(), 5);
113  assert.strictEqual(t.b(), 6);
114
115  assert.strictEqual(monster.testType(), MyGame.Example.Any.Monster);
116  var monster2 = new MyGame.Example.Monster();
117  assert.strictEqual(monster.test(monster2) != null, true);
118  assert.strictEqual(monster2.name(), 'Fred');
119
120  assert.strictEqual(monster.inventoryLength(), 5);
121  var invsum = 0;
122  for (var i = 0; i < monster.inventoryLength(); i++) {
123    invsum += monster.inventory(i);
124  }
125  assert.strictEqual(invsum, 10);
126
127  var invsum2 = 0;
128  var invArr = monster.inventoryArray();
129  for (var i = 0; i < invArr.length; i++) {
130    invsum2 += invArr[i];
131  }
132  assert.strictEqual(invsum2, 10);
133
134  var test_0 = monster.test4(0);
135  var test_1 = monster.test4(1);
136  assert.strictEqual(monster.test4Length(), 2);
137  assert.strictEqual(test_0.a() + test_0.b() + test_1.a() + test_1.b(), 100);
138
139  assert.strictEqual(monster.testarrayofstringLength(), 2);
140  assert.strictEqual(monster.testarrayofstring(0), 'test1');
141  assert.strictEqual(monster.testarrayofstring(1), 'test2');
142
143  assert.strictEqual(monster.testbool(), false);
144}
145
146function test64bit() {
147  var fbb = new flatbuffers.Builder();
148  var required = fbb.createString('required');
149
150  MyGame.Example.Stat.startStat(fbb);
151  var stat2 = MyGame.Example.Stat.endStat(fbb);
152
153  MyGame.Example.Monster.startMonster(fbb);
154  MyGame.Example.Monster.addName(fbb, required);
155  MyGame.Example.Monster.addTestempty(fbb, stat2);
156  var mon2 = MyGame.Example.Monster.endMonster(fbb);
157
158  MyGame.Example.Stat.startStat(fbb);
159  // 2541551405100253985 = 0x87654321(low part) + 0x23456789 * 0x100000000(high part);
160  MyGame.Example.Stat.addVal(fbb, new flatbuffers.Long(0x87654321, 0x23456789));    // the low part is Uint32
161  var stat = MyGame.Example.Stat.endStat(fbb);
162
163  MyGame.Example.Monster.startMonster(fbb);
164  MyGame.Example.Monster.addName(fbb, required);
165  MyGame.Example.Monster.addEnemy(fbb, mon2);
166  MyGame.Example.Monster.addTestempty(fbb, stat);
167  var mon = MyGame.Example.Monster.endMonster(fbb);
168
169  MyGame.Example.Monster.finishMonsterBuffer(fbb, mon);
170  var bytes = fbb.asUint8Array();
171
172  ////////////////////////////////////////////////////////////////
173
174  var bb = new flatbuffers.ByteBuffer(bytes);
175  assert.ok(MyGame.Example.Monster.bufferHasIdentifier(bb));
176  var mon = MyGame.Example.Monster.getRootAsMonster(bb);
177
178  var stat = mon.testempty();
179  assert.strictEqual(stat != null, true);
180  assert.strictEqual(stat.val() != null, true);
181  assert.strictEqual(stat.val().toFloat64(), 2541551405100253985);
182
183  var mon2 = mon.enemy();
184  assert.strictEqual(mon2 != null, true);
185  stat = mon2.testempty();
186  assert.strictEqual(stat != null, true);
187  assert.strictEqual(stat.val() != null, true);
188  assert.strictEqual(stat.val().low, 0); // default value
189  assert.strictEqual(stat.val().high, 0);
190}
191
192function testUnicode() {
193  var correct = fs.readFileSync('unicode_test.mon');
194  var json = JSON.parse(fs.readFileSync('unicode_test.json', 'utf8'));
195
196  // Test reading
197  function testReadingUnicode(bb) {
198    var monster = MyGame.Example.Monster.getRootAsMonster(bb);
199    assert.strictEqual(monster.name(), json.name);
200    assert.deepEqual(new Buffer(monster.name(flatbuffers.Encoding.UTF8_BYTES)), new Buffer(json.name));
201    assert.strictEqual(monster.testarrayoftablesLength(), json.testarrayoftables.length);
202    json.testarrayoftables.forEach(function(table, i) {
203      var value = monster.testarrayoftables(i);
204      assert.strictEqual(value.name(), table.name);
205      assert.deepEqual(new Buffer(value.name(flatbuffers.Encoding.UTF8_BYTES)), new Buffer(table.name));
206    });
207    assert.strictEqual(monster.testarrayofstringLength(), json.testarrayofstring.length);
208    json.testarrayofstring.forEach(function(string, i) {
209      assert.strictEqual(monster.testarrayofstring(i), string);
210      assert.deepEqual(new Buffer(monster.testarrayofstring(i, flatbuffers.Encoding.UTF8_BYTES)), new Buffer(string));
211    });
212  }
213  testReadingUnicode(new flatbuffers.ByteBuffer(new Uint8Array(correct)));
214
215  // Test writing
216  var fbb = new flatbuffers.Builder();
217  var name = fbb.createString(json.name);
218  var testarrayoftablesOffsets = json.testarrayoftables.map(function(table) {
219    var name = fbb.createString(new Uint8Array(new Buffer(table.name)));
220    MyGame.Example.Monster.startMonster(fbb);
221    MyGame.Example.Monster.addName(fbb, name);
222    return MyGame.Example.Monster.endMonster(fbb);
223  });
224  var testarrayoftablesOffset = MyGame.Example.Monster.createTestarrayoftablesVector(fbb,
225    testarrayoftablesOffsets);
226  var testarrayofstringOffset = MyGame.Example.Monster.createTestarrayofstringVector(fbb,
227    json.testarrayofstring.map(function(string) { return fbb.createString(string); }));
228  MyGame.Example.Monster.startMonster(fbb);
229  MyGame.Example.Monster.addTestarrayofstring(fbb, testarrayofstringOffset);
230  MyGame.Example.Monster.addTestarrayoftables(fbb, testarrayoftablesOffset);
231  MyGame.Example.Monster.addName(fbb, name);
232  MyGame.Example.Monster.finishMonsterBuffer(fbb, MyGame.Example.Monster.endMonster(fbb));
233  testReadingUnicode(new flatbuffers.ByteBuffer(fbb.asUint8Array()));
234}
235
236var __imul = Math.imul ? Math.imul : function(a, b) {
237  var ah = a >> 16 & 65535;
238  var bh = b >> 16 & 65535;
239  var al = a & 65535;
240  var bl = b & 65535;
241  return al * bl + (ah * bl + al * bh << 16) | 0;
242};
243
244// Include simple random number generator to ensure results will be the
245// same cross platform.
246// http://en.wikipedia.org/wiki/Park%E2%80%93Miller_random_number_generator
247var lcg_seed = 48271;
248
249function lcg_rand() {
250  return lcg_seed = (__imul(lcg_seed, 279470273) >>> 0) % 4294967291;
251}
252
253function lcg_reset() {
254  lcg_seed = 48271;
255}
256
257// Converts a Field ID to a virtual table offset.
258function fieldIndexToOffset(field_id) {
259  // Should correspond to what EndTable() below builds up.
260  var fixed_fields = 2;  // Vtable size and Object Size.
261  return (field_id + fixed_fields) * 2;
262}
263
264// Low level stress/fuzz test: serialize/deserialize a variety of
265// different kinds of data in different combinations
266function fuzzTest1() {
267
268  // Values we're testing against: chosen to ensure no bits get chopped
269  // off anywhere, and also be different from eachother.
270  var bool_val   = true;
271  var char_val   = -127;  // 0x81
272  var uchar_val  = 0xFF;
273  var short_val  = -32222; // 0x8222;
274  var ushort_val = 0xFEEE;
275  var int_val    = 0x83333333 | 0;
276  var uint_val   = 0xFDDDDDDD;
277  var long_val   = new flatbuffers.Long(0x44444444, 0x84444444);
278  var ulong_val  = new flatbuffers.Long(0xCCCCCCCC, 0xFCCCCCCC);
279  var float_val  = new Float32Array([3.14159])[0];
280  var double_val = 3.14159265359;
281
282  var test_values_max = 11;
283  var fields_per_object = 4;
284  var num_fuzz_objects = 10000;  // The higher, the more thorough :)
285
286  var builder = new flatbuffers.Builder();
287
288  lcg_reset();  // Keep it deterministic.
289
290  var objects = [];
291
292  // Generate num_fuzz_objects random objects each consisting of
293  // fields_per_object fields, each of a random type.
294  for (var i = 0; i < num_fuzz_objects; i++) {
295    builder.startObject(fields_per_object);
296    for (var f = 0; f < fields_per_object; f++) {
297      var choice = lcg_rand() % test_values_max;
298      switch (choice) {
299        case 0:  builder.addFieldInt8(f, bool_val,   0); break;
300        case 1:  builder.addFieldInt8(f, char_val,   0); break;
301        case 2:  builder.addFieldInt8(f, uchar_val,  0); break;
302        case 3:  builder.addFieldInt16(f, short_val,  0); break;
303        case 4:  builder.addFieldInt16(f, ushort_val, 0); break;
304        case 5:  builder.addFieldInt32(f, int_val,    0); break;
305        case 6:  builder.addFieldInt32(f, uint_val,   0); break;
306        case 7:  builder.addFieldInt64(f, long_val,   flatbuffers.Long.ZERO); break;
307        case 8:  builder.addFieldInt64(f, ulong_val,  flatbuffers.Long.ZERO); break;
308        case 9:  builder.addFieldFloat32(f, float_val,  0); break;
309        case 10: builder.addFieldFloat64(f, double_val, 0); break;
310      }
311    }
312    objects.push(builder.endObject());
313  }
314  builder.prep(8, 0);  // Align whole buffer.
315
316  lcg_reset();  // Reset.
317
318  builder.finish(objects[objects.length - 1]);
319  var bytes = new Uint8Array(builder.asUint8Array());
320  var view = new DataView(bytes.buffer);
321
322  // Test that all objects we generated are readable and return the
323  // expected values. We generate random objects in the same order
324  // so this is deterministic.
325  for (var i = 0; i < num_fuzz_objects; i++) {
326    var offset = bytes.length - objects[i];
327    for (var f = 0; f < fields_per_object; f++) {
328      var choice = lcg_rand() % test_values_max;
329      var vtable_offset = fieldIndexToOffset(f);
330      var vtable = offset - view.getInt32(offset, true);
331      assert.ok(vtable_offset < view.getInt16(vtable, true));
332      var field_offset = offset + view.getInt16(vtable + vtable_offset, true);
333      switch (choice) {
334        case 0:  assert.strictEqual(!!view.getInt8(field_offset), bool_val); break;
335        case 1:  assert.strictEqual(view.getInt8(field_offset), char_val); break;
336        case 2:  assert.strictEqual(view.getUint8(field_offset), uchar_val); break;
337        case 3:  assert.strictEqual(view.getInt16(field_offset, true), short_val); break;
338        case 4:  assert.strictEqual(view.getUint16(field_offset, true), ushort_val); break;
339        case 5:  assert.strictEqual(view.getInt32(field_offset, true), int_val); break;
340        case 6:  assert.strictEqual(view.getUint32(field_offset, true), uint_val); break;
341        case 7:  assert.strictEqual(view.getInt32(field_offset, true), long_val.low); assert.strictEqual(view.getInt32(field_offset + 4, true), long_val.high); break;
342        case 8:  assert.strictEqual(view.getInt32(field_offset, true), ulong_val.low); assert.strictEqual(view.getInt32(field_offset + 4, true), ulong_val.high); break;
343        case 9:  assert.strictEqual(view.getFloat32(field_offset, true), float_val); break;
344        case 10: assert.strictEqual(view.getFloat64(field_offset, true), double_val); break;
345      }
346    }
347  }
348}
349
350main();
351