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