• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# coding=utf-8
2# Copyright 2014 Google Inc. All rights reserved.
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8#     http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
16import os.path
17import sys
18PY_VERSION = sys.version_info[:2]
19
20import ctypes
21from collections import defaultdict
22import timeit
23import unittest
24
25
26from flatbuffers import compat
27from flatbuffers.compat import range_func as compat_range
28
29import flatbuffers
30from flatbuffers import number_types as N
31
32import MyGame  # refers to generated code
33import MyGame.Example  # refers to generated code
34import MyGame.Example.Any  # refers to generated code
35import MyGame.Example.Color  # refers to generated code
36import MyGame.Example.Monster  # refers to generated code
37import MyGame.Example.Test  # refers to generated code
38import MyGame.Example.Stat  # refers to generated code
39import MyGame.Example.Vec3  # refers to generated code
40
41
42def assertRaises(test_case, fn, exception_class):
43    ''' Backwards-compatible assertion for exceptions raised. '''
44
45    exc = None
46    try:
47        fn()
48    except Exception as e:
49        exc = e
50    test_case.assertTrue(exc is not None)
51    test_case.assertTrue(isinstance(exc, exception_class))
52
53
54class TestWireFormat(unittest.TestCase):
55    def test_wire_format(self):
56        # Verify that using the generated Python code builds a buffer without
57        # returning errors, and is interpreted correctly:
58        gen_buf, gen_off = make_monster_from_generated_code()
59        CheckReadBuffer(gen_buf, gen_off)
60
61        # Verify that the canonical flatbuffer file is readable by the
62        # generated Python code. Note that context managers are not part of
63        # Python 2.5, so we use the simpler open/close methods here:
64        f = open('monsterdata_test.mon', 'rb')
65        canonicalWireData = f.read()
66        f.close()
67        CheckReadBuffer(bytearray(canonicalWireData), 0)
68
69        # Write the generated buffer out to a file:
70        f = open('monsterdata_python_wire.mon', 'wb')
71        f.write(gen_buf[gen_off:])
72        f.close()
73
74
75def CheckReadBuffer(buf, offset):
76    ''' CheckReadBuffer checks that the given buffer is evaluated correctly
77        as the example Monster. '''
78
79    def asserter(stmt):
80        ''' An assertion helper that is separated from TestCase classes. '''
81        if not stmt:
82            raise AssertionError('CheckReadBuffer case failed')
83
84    monster = MyGame.Example.Monster.Monster.GetRootAsMonster(buf, offset)
85
86    asserter(monster.Hp() == 80)
87    asserter(monster.Mana() == 150)
88    asserter(monster.Name() == b'MyMonster')
89
90    # initialize a Vec3 from Pos()
91    vec = monster.Pos()
92    asserter(vec is not None)
93
94    # verify the properties of the Vec3
95    asserter(vec.X() == 1.0)
96    asserter(vec.Y() == 2.0)
97    asserter(vec.Z() == 3.0)
98    asserter(vec.Test1() == 3.0)
99    asserter(vec.Test2() == 2)
100
101    # initialize a Test from Test3(...)
102    t = MyGame.Example.Test.Test()
103    t = vec.Test3(t)
104    asserter(t is not None)
105
106    # verify the properties of the Test
107    asserter(t.A() == 5)
108    asserter(t.B() == 6)
109
110    # verify that the enum code matches the enum declaration:
111    union_type = MyGame.Example.Any.Any
112    asserter(monster.TestType() == union_type.Monster)
113
114    # initialize a Table from a union field Test(...)
115    table2 = monster.Test()
116    asserter(type(table2) is flatbuffers.table.Table)
117
118    # initialize a Monster from the Table from the union
119    monster2 = MyGame.Example.Monster.Monster()
120    monster2.Init(table2.Bytes, table2.Pos)
121
122    asserter(monster2.Name() == b"Fred")
123
124    # iterate through the first monster's inventory:
125    asserter(monster.InventoryLength() == 5)
126
127    invsum = 0
128    for i in compat_range(monster.InventoryLength()):
129        v = monster.Inventory(i)
130        invsum += int(v)
131    asserter(invsum == 10)
132
133    asserter(monster.Test4Length() == 2)
134
135    # create a 'Test' object and populate it:
136    test0 = monster.Test4(0)
137    asserter(type(test0) is MyGame.Example.Test.Test)
138
139    test1 = monster.Test4(1)
140    asserter(type(test1) is MyGame.Example.Test.Test)
141
142    # the position of test0 and test1 are swapped in monsterdata_java_wire
143    # and monsterdata_test_wire, so ignore ordering
144    v0 = test0.A()
145    v1 = test0.B()
146    v2 = test1.A()
147    v3 = test1.B()
148    sumtest12 = int(v0) + int(v1) + int(v2) + int(v3)
149
150    asserter(sumtest12 == 100)
151
152    asserter(monster.TestarrayofstringLength() == 2)
153    asserter(monster.Testarrayofstring(0) == b"test1")
154    asserter(monster.Testarrayofstring(1) == b"test2")
155
156    asserter(monster.TestarrayoftablesLength() == 0)
157    asserter(monster.TestnestedflatbufferLength() == 0)
158    asserter(monster.Testempty() is None)
159
160
161class TestFuzz(unittest.TestCase):
162    ''' Low level stress/fuzz test: serialize/deserialize a variety of
163        different kinds of data in different combinations '''
164
165    binary_type = compat.binary_types[0] # this will always exist
166    ofInt32Bytes = binary_type([0x83, 0x33, 0x33, 0x33])
167    ofInt64Bytes = binary_type([0x84, 0x44, 0x44, 0x44,
168                                0x44, 0x44, 0x44, 0x44])
169    overflowingInt32Val = flatbuffers.encode.Get(flatbuffers.packer.int32,
170                                                 ofInt32Bytes, 0)
171    overflowingInt64Val = flatbuffers.encode.Get(flatbuffers.packer.int64,
172                                                 ofInt64Bytes, 0)
173
174    # Values we're testing against: chosen to ensure no bits get chopped
175    # off anywhere, and also be different from eachother.
176    boolVal = True
177    int8Val = N.Int8Flags.py_type(-127) # 0x81
178    uint8Val = N.Uint8Flags.py_type(0xFF)
179    int16Val = N.Int16Flags.py_type(-32222) # 0x8222
180    uint16Val = N.Uint16Flags.py_type(0xFEEE)
181    int32Val = N.Int32Flags.py_type(overflowingInt32Val)
182    uint32Val = N.Uint32Flags.py_type(0xFDDDDDDD)
183    int64Val = N.Int64Flags.py_type(overflowingInt64Val)
184    uint64Val = N.Uint64Flags.py_type(0xFCCCCCCCCCCCCCCC)
185    # Python uses doubles, so force it here
186    float32Val = N.Float32Flags.py_type(ctypes.c_float(3.14159).value)
187    float64Val = N.Float64Flags.py_type(3.14159265359)
188
189    def test_fuzz(self):
190        return self.check_once(11, 100)
191
192    def check_once(self, fuzzFields, fuzzObjects):
193        testValuesMax = 11 # hardcoded to the number of scalar types
194
195        builder = flatbuffers.Builder(0)
196        l = LCG()
197
198        objects = [0 for _ in compat_range(fuzzObjects)]
199
200        # Generate fuzzObjects random objects each consisting of
201        # fuzzFields fields, each of a random type.
202        for i in compat_range(fuzzObjects):
203            builder.StartObject(fuzzFields)
204
205            for j in compat_range(fuzzFields):
206                choice = int(l.Next()) % testValuesMax
207                if choice == 0:
208                    builder.PrependBoolSlot(int(j), self.boolVal, False)
209                elif choice == 1:
210                    builder.PrependInt8Slot(int(j), self.int8Val, 0)
211                elif choice == 2:
212                    builder.PrependUint8Slot(int(j), self.uint8Val, 0)
213                elif choice == 3:
214                    builder.PrependInt16Slot(int(j), self.int16Val, 0)
215                elif choice == 4:
216                    builder.PrependUint16Slot(int(j), self.uint16Val, 0)
217                elif choice == 5:
218                    builder.PrependInt32Slot(int(j), self.int32Val, 0)
219                elif choice == 6:
220                    builder.PrependUint32Slot(int(j), self.uint32Val, 0)
221                elif choice == 7:
222                    builder.PrependInt64Slot(int(j), self.int64Val, 0)
223                elif choice == 8:
224                    builder.PrependUint64Slot(int(j), self.uint64Val, 0)
225                elif choice == 9:
226                    builder.PrependFloat32Slot(int(j), self.float32Val, 0)
227                elif choice == 10:
228                    builder.PrependFloat64Slot(int(j), self.float64Val, 0)
229                else:
230                    raise RuntimeError('unreachable')
231
232            off = builder.EndObject()
233
234            # store the offset from the end of the builder buffer,
235            # since it will keep growing:
236            objects[i] = off
237
238        # Do some bookkeeping to generate stats on fuzzes:
239        stats = defaultdict(int)
240        def check(table, desc, want, got):
241            stats[desc] += 1
242            self.assertEqual(want, got, "%s != %s, %s" % (want, got, desc))
243
244        l = LCG()  # Reset.
245
246        # Test that all objects we generated are readable and return the
247        # expected values. We generate random objects in the same order
248        # so this is deterministic.
249        for i in compat_range(fuzzObjects):
250
251            table = flatbuffers.table.Table(builder.Bytes,
252                                            len(builder.Bytes) - objects[i])
253
254            for j in compat_range(fuzzFields):
255                field_count = flatbuffers.builder.VtableMetadataFields + j
256                f = N.VOffsetTFlags.py_type(field_count *
257                                            N.VOffsetTFlags.bytewidth)
258                choice = int(l.Next()) % testValuesMax
259
260                if choice == 0:
261                    check(table, "bool", self.boolVal,
262                          table.GetSlot(f, False, N.BoolFlags))
263                elif choice == 1:
264                    check(table, "int8", self.int8Val,
265                          table.GetSlot(f, 0, N.Int8Flags))
266                elif choice == 2:
267                    check(table, "uint8", self.uint8Val,
268                          table.GetSlot(f, 0, N.Uint8Flags))
269                elif choice == 3:
270                    check(table, "int16", self.int16Val,
271                          table.GetSlot(f, 0, N.Int16Flags))
272                elif choice == 4:
273                    check(table, "uint16", self.uint16Val,
274                          table.GetSlot(f, 0, N.Uint16Flags))
275                elif choice == 5:
276                    check(table, "int32", self.int32Val,
277                          table.GetSlot(f, 0, N.Int32Flags))
278                elif choice == 6:
279                    check(table, "uint32", self.uint32Val,
280                          table.GetSlot(f, 0, N.Uint32Flags))
281                elif choice == 7:
282                    check(table, "int64", self.int64Val,
283                          table.GetSlot(f, 0, N.Int64Flags))
284                elif choice == 8:
285                    check(table, "uint64", self.uint64Val,
286                          table.GetSlot(f, 0, N.Uint64Flags))
287                elif choice == 9:
288                    check(table, "float32", self.float32Val,
289                          table.GetSlot(f, 0, N.Float32Flags))
290                elif choice == 10:
291                    check(table, "float64", self.float64Val,
292                          table.GetSlot(f, 0, N.Float64Flags))
293                else:
294                    raise RuntimeError('unreachable')
295
296        # If enough checks were made, verify that all scalar types were used:
297        self.assertEqual(testValuesMax, len(stats),
298                "fuzzing failed to test all scalar types: %s" % stats)
299
300
301class TestByteLayout(unittest.TestCase):
302    ''' TestByteLayout checks the bytes of a Builder in various scenarios. '''
303
304    def assertBuilderEquals(self, builder, want_chars_or_ints):
305        def integerize(x):
306            if isinstance(x, compat.string_types):
307                return ord(x)
308            return x
309
310        want_ints = list(map(integerize, want_chars_or_ints))
311        want = bytearray(want_ints)
312        got = builder.Bytes[builder.Head():] # use the buffer directly
313        self.assertEqual(want, got)
314
315    def test_numbers(self):
316        b = flatbuffers.Builder(0)
317        self.assertBuilderEquals(b, [])
318        b.PrependBool(True)
319        self.assertBuilderEquals(b, [1])
320        b.PrependInt8(-127)
321        self.assertBuilderEquals(b, [129, 1])
322        b.PrependUint8(255)
323        self.assertBuilderEquals(b, [255, 129, 1])
324        b.PrependInt16(-32222)
325        self.assertBuilderEquals(b, [0x22, 0x82, 0, 255, 129, 1]) # first pad
326        b.PrependUint16(0xFEEE)
327        # no pad this time:
328        self.assertBuilderEquals(b, [0xEE, 0xFE, 0x22, 0x82, 0, 255, 129, 1])
329        b.PrependInt32(-53687092)
330        self.assertBuilderEquals(b, [204, 204, 204, 252, 0xEE, 0xFE,
331                                     0x22, 0x82, 0, 255, 129, 1])
332        b.PrependUint32(0x98765432)
333        self.assertBuilderEquals(b, [0x32, 0x54, 0x76, 0x98,
334                                     204, 204, 204, 252,
335                                     0xEE, 0xFE, 0x22, 0x82,
336                                     0, 255, 129, 1])
337
338    def test_numbers64(self):
339        b = flatbuffers.Builder(0)
340        b.PrependUint64(0x1122334455667788)
341        self.assertBuilderEquals(b, [0x88, 0x77, 0x66, 0x55,
342                                     0x44, 0x33, 0x22, 0x11])
343
344        b = flatbuffers.Builder(0)
345        b.PrependInt64(0x1122334455667788)
346        self.assertBuilderEquals(b, [0x88, 0x77, 0x66, 0x55,
347                                     0x44, 0x33, 0x22, 0x11])
348
349    def test_1xbyte_vector(self):
350        b = flatbuffers.Builder(0)
351        self.assertBuilderEquals(b, [])
352        b.StartVector(flatbuffers.number_types.Uint8Flags.bytewidth, 1, 1)
353        self.assertBuilderEquals(b, [0, 0, 0]) # align to 4bytes
354        b.PrependByte(1)
355        self.assertBuilderEquals(b, [1, 0, 0, 0])
356        b.EndVector(1)
357        self.assertBuilderEquals(b, [1, 0, 0, 0, 1, 0, 0, 0]) # padding
358
359    def test_2xbyte_vector(self):
360        b = flatbuffers.Builder(0)
361        b.StartVector(flatbuffers.number_types.Uint8Flags.bytewidth, 2, 1)
362        self.assertBuilderEquals(b, [0, 0]) # align to 4bytes
363        b.PrependByte(1)
364        self.assertBuilderEquals(b, [1, 0, 0])
365        b.PrependByte(2)
366        self.assertBuilderEquals(b, [2, 1, 0, 0])
367        b.EndVector(2)
368        self.assertBuilderEquals(b, [2, 0, 0, 0, 2, 1, 0, 0]) # padding
369
370    def test_1xuint16_vector(self):
371        b = flatbuffers.Builder(0)
372        b.StartVector(flatbuffers.number_types.Uint16Flags.bytewidth, 1, 1)
373        self.assertBuilderEquals(b, [0, 0]) # align to 4bytes
374        b.PrependUint16(1)
375        self.assertBuilderEquals(b, [1, 0, 0, 0])
376        b.EndVector(1)
377        self.assertBuilderEquals(b, [1, 0, 0, 0, 1, 0, 0, 0]) # padding
378
379    def test_2xuint16_vector(self):
380        b = flatbuffers.Builder(0)
381        b.StartVector(flatbuffers.number_types.Uint16Flags.bytewidth, 2, 1)
382        self.assertBuilderEquals(b, []) # align to 4bytes
383        b.PrependUint16(0xABCD)
384        self.assertBuilderEquals(b, [0xCD, 0xAB])
385        b.PrependUint16(0xDCBA)
386        self.assertBuilderEquals(b, [0xBA, 0xDC, 0xCD, 0xAB])
387        b.EndVector(2)
388        self.assertBuilderEquals(b, [2, 0, 0, 0, 0xBA, 0xDC, 0xCD, 0xAB])
389
390    def test_create_ascii_string(self):
391        b = flatbuffers.Builder(0)
392        b.CreateString(u"foo", encoding='ascii')
393
394        # 0-terminated, no pad:
395        self.assertBuilderEquals(b, [3, 0, 0, 0, 'f', 'o', 'o', 0])
396        b.CreateString(u"moop", encoding='ascii')
397        # 0-terminated, 3-byte pad:
398        self.assertBuilderEquals(b, [4, 0, 0, 0, 'm', 'o', 'o', 'p',
399                                     0, 0, 0, 0,
400                                     3, 0, 0, 0, 'f', 'o', 'o', 0])
401
402    def test_create_utf8_string(self):
403        b = flatbuffers.Builder(0)
404        b.CreateString(u"Цлїςσδε")
405        self.assertBuilderEquals(b, "\x0e\x00\x00\x00\xd0\xa6\xd0\xbb\xd1\x97" \
406            "\xcf\x82\xcf\x83\xce\xb4\xce\xb5\x00\x00")
407
408        b.CreateString(u"フムアムカモケモ")
409        self.assertBuilderEquals(b, "\x18\x00\x00\x00\xef\xbe\x8c\xef\xbe\x91" \
410            "\xef\xbd\xb1\xef\xbe\x91\xef\xbd\xb6\xef\xbe\x93\xef\xbd\xb9\xef" \
411            "\xbe\x93\x00\x00\x00\x00\x0e\x00\x00\x00\xd0\xa6\xd0\xbb\xd1\x97" \
412            "\xcf\x82\xcf\x83\xce\xb4\xce\xb5\x00\x00")
413
414    def test_create_arbitrary_string(self):
415        b = flatbuffers.Builder(0)
416        s = "\x01\x02\x03"
417        b.CreateString(s) # Default encoding is utf-8.
418        # 0-terminated, no pad:
419        self.assertBuilderEquals(b, [3, 0, 0, 0, 1, 2, 3, 0])
420        s2 = "\x04\x05\x06\x07"
421        b.CreateString(s2) # Default encoding is utf-8.
422        # 0-terminated, 3-byte pad:
423        self.assertBuilderEquals(b, [4, 0, 0, 0, 4, 5, 6, 7, 0, 0, 0, 0,
424                                     3, 0, 0, 0, 1, 2, 3, 0])
425
426    def test_empty_vtable(self):
427        b = flatbuffers.Builder(0)
428        b.StartObject(0)
429        self.assertBuilderEquals(b, [])
430        b.EndObject()
431        self.assertBuilderEquals(b, [4, 0, 4, 0, 4, 0, 0, 0])
432
433    def test_vtable_with_one_true_bool(self):
434        b = flatbuffers.Builder(0)
435        self.assertBuilderEquals(b, [])
436        b.StartObject(1)
437        self.assertBuilderEquals(b, [])
438        b.PrependBoolSlot(0, True, False)
439        b.EndObject()
440        self.assertBuilderEquals(b, [
441            6, 0,  # vtable bytes
442            8, 0,  # length of object including vtable offset
443            7, 0,  # start of bool value
444            6, 0, 0, 0,  # offset for start of vtable (int32)
445            0, 0, 0,  # padded to 4 bytes
446            1,  # bool value
447        ])
448
449    def test_vtable_with_one_default_bool(self):
450        b = flatbuffers.Builder(0)
451        self.assertBuilderEquals(b, [])
452        b.StartObject(1)
453        self.assertBuilderEquals(b, [])
454        b.PrependBoolSlot(0, False, False)
455        b.EndObject()
456        self.assertBuilderEquals(b, [
457            6, 0,  # vtable bytes
458            4, 0,  # end of object from here
459            0, 0,  # entry 1 is zero
460            6, 0, 0, 0,  # offset for start of vtable (int32)
461        ])
462
463    def test_vtable_with_one_int16(self):
464        b = flatbuffers.Builder(0)
465        b.StartObject(1)
466        b.PrependInt16Slot(0, 0x789A, 0)
467        b.EndObject()
468        self.assertBuilderEquals(b, [
469            6, 0,  # vtable bytes
470            8, 0,  # end of object from here
471            6, 0,  # offset to value
472            6, 0, 0, 0,  # offset for start of vtable (int32)
473            0, 0,  # padding to 4 bytes
474            0x9A, 0x78,
475        ])
476
477    def test_vtable_with_two_int16(self):
478        b = flatbuffers.Builder(0)
479        b.StartObject(2)
480        b.PrependInt16Slot(0, 0x3456, 0)
481        b.PrependInt16Slot(1, 0x789A, 0)
482        b.EndObject()
483        self.assertBuilderEquals(b, [
484            8, 0,  # vtable bytes
485            8, 0,  # end of object from here
486            6, 0,  # offset to value 0
487            4, 0,  # offset to value 1
488            8, 0, 0, 0,  # offset for start of vtable (int32)
489            0x9A, 0x78,  # value 1
490            0x56, 0x34,  # value 0
491        ])
492
493    def test_vtable_with_int16_and_bool(self):
494        b = flatbuffers.Builder(0)
495        b.StartObject(2)
496        b.PrependInt16Slot(0, 0x3456, 0)
497        b.PrependBoolSlot(1, True, False)
498        b.EndObject()
499        self.assertBuilderEquals(b, [
500            8, 0,  # vtable bytes
501            8, 0,  # end of object from here
502            6, 0,  # offset to value 0
503            5, 0,  # offset to value 1
504            8, 0, 0, 0,  # offset for start of vtable (int32)
505            0,          # padding
506            1,          # value 1
507            0x56, 0x34,  # value 0
508        ])
509
510    def test_vtable_with_empty_vector(self):
511        b = flatbuffers.Builder(0)
512        b.StartVector(flatbuffers.number_types.Uint8Flags.bytewidth, 0, 1)
513        vecend = b.EndVector(0)
514        b.StartObject(1)
515        b.PrependUOffsetTRelativeSlot(0, vecend, 0)
516        b.EndObject()
517        self.assertBuilderEquals(b, [
518            6, 0,  # vtable bytes
519            8, 0,
520            4, 0,  # offset to vector offset
521            6, 0, 0, 0,  # offset for start of vtable (int32)
522            4, 0, 0, 0,
523            0, 0, 0, 0,  # length of vector (not in struct)
524        ])
525
526    def test_vtable_with_empty_vector_of_byte_and_some_scalars(self):
527        b = flatbuffers.Builder(0)
528        b.StartVector(flatbuffers.number_types.Uint8Flags.bytewidth, 0, 1)
529        vecend = b.EndVector(0)
530        b.StartObject(2)
531        b.PrependInt16Slot(0, 55, 0)
532        b.PrependUOffsetTRelativeSlot(1, vecend, 0)
533        b.EndObject()
534        self.assertBuilderEquals(b, [
535            8, 0,  # vtable bytes
536            12, 0,
537            10, 0,  # offset to value 0
538            4, 0,  # offset to vector offset
539            8, 0, 0, 0,  # vtable loc
540            8, 0, 0, 0,  # value 1
541            0, 0, 55, 0,  # value 0
542
543            0, 0, 0, 0,  # length of vector (not in struct)
544        ])
545
546    def test_vtable_with_1_int16_and_2vector_of_int16(self):
547        b = flatbuffers.Builder(0)
548        b.StartVector(flatbuffers.number_types.Int16Flags.bytewidth, 2, 1)
549        b.PrependInt16(0x1234)
550        b.PrependInt16(0x5678)
551        vecend = b.EndVector(2)
552        b.StartObject(2)
553        b.PrependUOffsetTRelativeSlot(1, vecend, 0)
554        b.PrependInt16Slot(0, 55, 0)
555        b.EndObject()
556        self.assertBuilderEquals(b, [
557            8, 0,  # vtable bytes
558            12, 0,  # length of object
559            6, 0,  # start of value 0 from end of vtable
560            8, 0,  # start of value 1 from end of buffer
561            8, 0, 0, 0,  # offset for start of vtable (int32)
562            0, 0,  # padding
563            55, 0,  # value 0
564            4, 0, 0, 0,  # vector position from here
565            2, 0, 0, 0,  # length of vector (uint32)
566            0x78, 0x56,  # vector value 1
567            0x34, 0x12,  # vector value 0
568        ])
569
570    def test_vtable_with_1_struct_of_1_int8__1_int16__1_int32(self):
571        b = flatbuffers.Builder(0)
572        b.StartObject(1)
573        b.Prep(4+4+4, 0)
574        b.PrependInt8(55)
575        b.Pad(3)
576        b.PrependInt16(0x1234)
577        b.Pad(2)
578        b.PrependInt32(0x12345678)
579        structStart = b.Offset()
580        b.PrependStructSlot(0, structStart, 0)
581        b.EndObject()
582        self.assertBuilderEquals(b, [
583            6, 0,  # vtable bytes
584            16, 0,  # end of object from here
585            4, 0,  # start of struct from here
586            6, 0, 0, 0,  # offset for start of vtable (int32)
587            0x78, 0x56, 0x34, 0x12,  # value 2
588            0, 0,  # padding
589            0x34, 0x12,  # value 1
590            0, 0, 0,  # padding
591            55,  # value 0
592        ])
593
594    def test_vtable_with_1_vector_of_2_struct_of_2_int8(self):
595        b = flatbuffers.Builder(0)
596        b.StartVector(flatbuffers.number_types.Int8Flags.bytewidth*2, 2, 1)
597        b.PrependInt8(33)
598        b.PrependInt8(44)
599        b.PrependInt8(55)
600        b.PrependInt8(66)
601        vecend = b.EndVector(2)
602        b.StartObject(1)
603        b.PrependUOffsetTRelativeSlot(0, vecend, 0)
604        b.EndObject()
605        self.assertBuilderEquals(b, [
606            6, 0,  # vtable bytes
607            8, 0,
608            4, 0,  # offset of vector offset
609            6, 0, 0, 0,  # offset for start of vtable (int32)
610            4, 0, 0, 0,  # vector start offset
611
612            2, 0, 0, 0,  # vector length
613            66,  # vector value 1,1
614            55,  # vector value 1,0
615            44,  # vector value 0,1
616            33,  # vector value 0,0
617        ])
618
619    def test_table_with_some_elements(self):
620        b = flatbuffers.Builder(0)
621        b.StartObject(2)
622        b.PrependInt8Slot(0, 33, 0)
623        b.PrependInt16Slot(1, 66, 0)
624        off = b.EndObject()
625        b.Finish(off)
626
627        self.assertBuilderEquals(b, [
628            12, 0, 0, 0,  # root of table: points to vtable offset
629
630            8, 0,  # vtable bytes
631            8, 0,  # end of object from here
632            7, 0,  # start of value 0
633            4, 0,  # start of value 1
634
635            8, 0, 0, 0,  # offset for start of vtable (int32)
636
637            66, 0,  # value 1
638            0,  # padding
639            33,  # value 0
640        ])
641
642    def test__one_unfinished_table_and_one_finished_table(self):
643        b = flatbuffers.Builder(0)
644        b.StartObject(2)
645        b.PrependInt8Slot(0, 33, 0)
646        b.PrependInt8Slot(1, 44, 0)
647        off = b.EndObject()
648        b.Finish(off)
649
650        b.StartObject(3)
651        b.PrependInt8Slot(0, 55, 0)
652        b.PrependInt8Slot(1, 66, 0)
653        b.PrependInt8Slot(2, 77, 0)
654        off = b.EndObject()
655        b.Finish(off)
656
657        self.assertBuilderEquals(b, [
658            16, 0, 0, 0,  # root of table: points to object
659            0, 0,  # padding
660
661            10, 0,  # vtable bytes
662            8, 0,  # size of object
663            7, 0,  # start of value 0
664            6, 0,  # start of value 1
665            5, 0,  # start of value 2
666            10, 0, 0, 0,  # offset for start of vtable (int32)
667            0,  # padding
668            77,  # value 2
669            66,  # value 1
670            55,  # value 0
671
672            12, 0, 0, 0,  # root of table: points to object
673
674            8, 0,  # vtable bytes
675            8, 0,  # size of object
676            7, 0,  # start of value 0
677            6, 0,  # start of value 1
678            8, 0, 0, 0,  # offset for start of vtable (int32)
679            0, 0,  # padding
680            44,  # value 1
681            33,  # value 0
682        ])
683
684    def test_a_bunch_of_bools(self):
685        b = flatbuffers.Builder(0)
686        b.StartObject(8)
687        b.PrependBoolSlot(0, True, False)
688        b.PrependBoolSlot(1, True, False)
689        b.PrependBoolSlot(2, True, False)
690        b.PrependBoolSlot(3, True, False)
691        b.PrependBoolSlot(4, True, False)
692        b.PrependBoolSlot(5, True, False)
693        b.PrependBoolSlot(6, True, False)
694        b.PrependBoolSlot(7, True, False)
695        off = b.EndObject()
696        b.Finish(off)
697
698        self.assertBuilderEquals(b, [
699            24, 0, 0, 0,  # root of table: points to vtable offset
700
701            20, 0,  # vtable bytes
702            12, 0,  # size of object
703            11, 0,  # start of value 0
704            10, 0,  # start of value 1
705            9, 0,  # start of value 2
706            8, 0,  # start of value 3
707            7, 0,  # start of value 4
708            6, 0,  # start of value 5
709            5, 0,  # start of value 6
710            4, 0,  # start of value 7
711            20, 0, 0, 0,  # vtable offset
712
713            1,  # value 7
714            1,  # value 6
715            1,  # value 5
716            1,  # value 4
717            1,  # value 3
718            1,  # value 2
719            1,  # value 1
720            1,  # value 0
721        ])
722
723    def test_three_bools(self):
724        b = flatbuffers.Builder(0)
725        b.StartObject(3)
726        b.PrependBoolSlot(0, True, False)
727        b.PrependBoolSlot(1, True, False)
728        b.PrependBoolSlot(2, True, False)
729        off = b.EndObject()
730        b.Finish(off)
731
732        self.assertBuilderEquals(b, [
733            16, 0, 0, 0,  # root of table: points to vtable offset
734
735            0, 0,  # padding
736
737            10, 0,  # vtable bytes
738            8, 0,  # size of object
739            7, 0,  # start of value 0
740            6, 0,  # start of value 1
741            5, 0,  # start of value 2
742            10, 0, 0, 0,  # vtable offset from here
743
744            0,  # padding
745            1,  # value 2
746            1,  # value 1
747            1,  # value 0
748        ])
749
750    def test_some_floats(self):
751        b = flatbuffers.Builder(0)
752        b.StartObject(1)
753        b.PrependFloat32Slot(0, 1.0, 0.0)
754        off = b.EndObject()
755
756        self.assertBuilderEquals(b, [
757            6, 0,  # vtable bytes
758            8, 0,  # size of object
759            4, 0,  # start of value 0
760            6, 0, 0, 0,  # vtable offset
761
762            0, 0, 128, 63,  # value 0
763        ])
764
765
766def make_monster_from_generated_code():
767    ''' Use generated code to build the example Monster. '''
768
769    b = flatbuffers.Builder(0)
770    string = b.CreateString("MyMonster")
771    test1 = b.CreateString("test1")
772    test2 = b.CreateString("test2")
773    fred = b.CreateString("Fred")
774
775    MyGame.Example.Monster.MonsterStartInventoryVector(b, 5)
776    b.PrependByte(4)
777    b.PrependByte(3)
778    b.PrependByte(2)
779    b.PrependByte(1)
780    b.PrependByte(0)
781    inv = b.EndVector(5)
782
783    MyGame.Example.Monster.MonsterStart(b)
784    MyGame.Example.Monster.MonsterAddName(b, fred)
785    mon2 = MyGame.Example.Monster.MonsterEnd(b)
786
787    MyGame.Example.Monster.MonsterStartTest4Vector(b, 2)
788    MyGame.Example.Test.CreateTest(b, 10, 20)
789    MyGame.Example.Test.CreateTest(b, 30, 40)
790    test4 = b.EndVector(2)
791
792    MyGame.Example.Monster.MonsterStartTestarrayofstringVector(b, 2)
793    b.PrependUOffsetTRelative(test2)
794    b.PrependUOffsetTRelative(test1)
795    testArrayOfString = b.EndVector(2)
796
797    MyGame.Example.Monster.MonsterStart(b)
798
799    pos = MyGame.Example.Vec3.CreateVec3(b, 1.0, 2.0, 3.0, 3.0, 2, 5, 6)
800    MyGame.Example.Monster.MonsterAddPos(b, pos)
801
802    MyGame.Example.Monster.MonsterAddHp(b, 80)
803    MyGame.Example.Monster.MonsterAddName(b, string)
804    MyGame.Example.Monster.MonsterAddInventory(b, inv)
805    MyGame.Example.Monster.MonsterAddTestType(b, 1)
806    MyGame.Example.Monster.MonsterAddTest(b, mon2)
807    MyGame.Example.Monster.MonsterAddTest4(b, test4)
808    MyGame.Example.Monster.MonsterAddTestarrayofstring(b, testArrayOfString)
809    mon = MyGame.Example.Monster.MonsterEnd(b)
810
811    b.Finish(mon)
812
813    return b.Bytes, b.Head()
814
815
816class TestAllCodePathsOfExampleSchema(unittest.TestCase):
817    def setUp(self, *args, **kwargs):
818        super(TestAllCodePathsOfExampleSchema, self).setUp(*args, **kwargs)
819
820        b = flatbuffers.Builder(0)
821        MyGame.Example.Monster.MonsterStart(b)
822        gen_mon = MyGame.Example.Monster.MonsterEnd(b)
823        b.Finish(gen_mon)
824
825        self.mon = MyGame.Example.Monster.Monster.GetRootAsMonster(b.Bytes,
826                                                                   b.Head())
827
828    def test_default_monster_pos(self):
829        self.assertTrue(self.mon.Pos() is None)
830
831    def test_nondefault_monster_mana(self):
832        b = flatbuffers.Builder(0)
833        MyGame.Example.Monster.MonsterStart(b)
834        MyGame.Example.Monster.MonsterAddMana(b, 50)
835        mon = MyGame.Example.Monster.MonsterEnd(b)
836        b.Finish(mon)
837
838        got_mon = MyGame.Example.Monster.Monster.GetRootAsMonster(b.Bytes,
839                                                                  b.Head())
840        self.assertEqual(50, got_mon.Mana())
841
842    def test_default_monster_hp(self):
843        self.assertEqual(100, self.mon.Hp())
844
845    def test_default_monster_name(self):
846        self.assertEqual('', self.mon.Name())
847
848    def test_default_monster_inventory_item(self):
849        self.assertEqual(0, self.mon.Inventory(0))
850
851    def test_default_monster_inventory_length(self):
852        self.assertEqual(0, self.mon.InventoryLength())
853
854    def test_default_monster_color(self):
855        self.assertEqual(MyGame.Example.Color.Color.Blue, self.mon.Color())
856
857    def test_nondefault_monster_color(self):
858        b = flatbuffers.Builder(0)
859        color = MyGame.Example.Color.Color.Red
860        MyGame.Example.Monster.MonsterStart(b)
861        MyGame.Example.Monster.MonsterAddColor(b, color)
862        mon = MyGame.Example.Monster.MonsterEnd(b)
863        b.Finish(mon)
864
865        mon2 = MyGame.Example.Monster.Monster.GetRootAsMonster(b.Bytes,
866                                                               b.Head())
867        self.assertEqual(MyGame.Example.Color.Color.Red, mon2.Color())
868
869    def test_default_monster_testtype(self):
870        self.assertEqual(0, self.mon.TestType())
871
872    def test_default_monster_test_field(self):
873        self.assertEqual(None, self.mon.Test())
874
875    def test_default_monster_test4_item(self):
876        self.assertEqual(None, self.mon.Test4(0))
877
878    def test_default_monster_test4_length(self):
879        self.assertEqual(0, self.mon.Test4Length())
880
881    def test_default_monster_testarrayofstring(self):
882        self.assertEqual("", self.mon.Testarrayofstring(0))
883
884    def test_default_monster_testarrayofstring_length(self):
885        self.assertEqual(0, self.mon.TestarrayofstringLength())
886
887    def test_default_monster_testarrayoftables(self):
888        self.assertEqual(None, self.mon.Testarrayoftables(0))
889
890    def test_nondefault_monster_testarrayoftables(self):
891        b = flatbuffers.Builder(0)
892
893        # make a child Monster within a vector of Monsters:
894        MyGame.Example.Monster.MonsterStart(b)
895        MyGame.Example.Monster.MonsterAddHp(b, 99)
896        sub_monster = MyGame.Example.Monster.MonsterEnd(b)
897
898        # build the vector:
899        MyGame.Example.Monster.MonsterStartTestarrayoftablesVector(b, 1)
900        b.PrependUOffsetTRelative(sub_monster)
901        vec = b.EndVector(1)
902
903        # make the parent monster and include the vector of Monster:
904        MyGame.Example.Monster.MonsterStart(b)
905        MyGame.Example.Monster.MonsterAddTestarrayoftables(b, vec)
906        mon = MyGame.Example.Monster.MonsterEnd(b)
907        b.Finish(mon)
908
909        # inspect the resulting data:
910        mon2 = MyGame.Example.Monster.Monster.GetRootAsMonster(b.Output(), 0)
911        self.assertEqual(99, mon2.Testarrayoftables(0).Hp())
912        self.assertEqual(1, mon2.TestarrayoftablesLength())
913
914    def test_default_monster_testarrayoftables_length(self):
915        self.assertEqual(0, self.mon.TestarrayoftablesLength())
916
917    def test_nondefault_monster_enemy(self):
918        b = flatbuffers.Builder(0)
919
920        # make an Enemy object:
921        MyGame.Example.Monster.MonsterStart(b)
922        MyGame.Example.Monster.MonsterAddHp(b, 88)
923        enemy = MyGame.Example.Monster.MonsterEnd(b)
924        b.Finish(enemy)
925
926        # make the parent monster and include the vector of Monster:
927        MyGame.Example.Monster.MonsterStart(b)
928        MyGame.Example.Monster.MonsterAddEnemy(b, enemy)
929        mon = MyGame.Example.Monster.MonsterEnd(b)
930        b.Finish(mon)
931
932        # inspect the resulting data:
933        mon2 = MyGame.Example.Monster.Monster.GetRootAsMonster(b.Bytes,
934                                                               b.Head())
935        self.assertEqual(88, mon2.Enemy().Hp())
936
937    def test_default_monster_testnestedflatbuffer(self):
938        self.assertEqual(0, self.mon.Testnestedflatbuffer(0))
939
940    def test_default_monster_testnestedflatbuffer_length(self):
941        self.assertEqual(0, self.mon.TestnestedflatbufferLength())
942
943    def test_nondefault_monster_testnestedflatbuffer(self):
944        b = flatbuffers.Builder(0)
945
946        MyGame.Example.Monster.MonsterStartTestnestedflatbufferVector(b, 3)
947        b.PrependByte(4)
948        b.PrependByte(2)
949        b.PrependByte(0)
950        sub_buf = b.EndVector(3)
951
952        # make the parent monster and include the vector of Monster:
953        MyGame.Example.Monster.MonsterStart(b)
954        MyGame.Example.Monster.MonsterAddTestnestedflatbuffer(b, sub_buf)
955        mon = MyGame.Example.Monster.MonsterEnd(b)
956        b.Finish(mon)
957
958        # inspect the resulting data:
959        mon2 = MyGame.Example.Monster.Monster.GetRootAsMonster(b.Bytes,
960                                                               b.Head())
961        self.assertEqual(3, mon2.TestnestedflatbufferLength())
962        self.assertEqual(0, mon2.Testnestedflatbuffer(0))
963        self.assertEqual(2, mon2.Testnestedflatbuffer(1))
964        self.assertEqual(4, mon2.Testnestedflatbuffer(2))
965
966    def test_nondefault_monster_testempty(self):
967        b = flatbuffers.Builder(0)
968
969        # make a Stat object:
970        MyGame.Example.Stat.StatStart(b)
971        MyGame.Example.Stat.StatAddVal(b, 123)
972        my_stat = MyGame.Example.Stat.StatEnd(b)
973        b.Finish(my_stat)
974
975        # include the stat object in a monster:
976        MyGame.Example.Monster.MonsterStart(b)
977        MyGame.Example.Monster.MonsterAddTestempty(b, my_stat)
978        mon = MyGame.Example.Monster.MonsterEnd(b)
979        b.Finish(mon)
980
981        # inspect the resulting data:
982        mon2 = MyGame.Example.Monster.Monster.GetRootAsMonster(b.Bytes,
983                                                               b.Head())
984        self.assertEqual(123, mon2.Testempty().Val())
985
986    def test_default_monster_testbool(self):
987        self.assertFalse(self.mon.Testbool())
988
989    def test_nondefault_monster_testbool(self):
990        b = flatbuffers.Builder(0)
991        MyGame.Example.Monster.MonsterStart(b)
992        MyGame.Example.Monster.MonsterAddTestbool(b, True)
993        mon = MyGame.Example.Monster.MonsterEnd(b)
994        b.Finish(mon)
995
996        # inspect the resulting data:
997        mon2 = MyGame.Example.Monster.Monster.GetRootAsMonster(b.Bytes,
998                                                               b.Head())
999        self.assertTrue(mon2.Testbool())
1000
1001    def test_default_monster_testhashes(self):
1002        self.assertEqual(0, self.mon.Testhashs32Fnv1())
1003        self.assertEqual(0, self.mon.Testhashu32Fnv1())
1004        self.assertEqual(0, self.mon.Testhashs64Fnv1())
1005        self.assertEqual(0, self.mon.Testhashu64Fnv1())
1006        self.assertEqual(0, self.mon.Testhashs32Fnv1a())
1007        self.assertEqual(0, self.mon.Testhashu32Fnv1a())
1008        self.assertEqual(0, self.mon.Testhashs64Fnv1a())
1009        self.assertEqual(0, self.mon.Testhashu64Fnv1a())
1010
1011    def test_nondefault_monster_testhashes(self):
1012        b = flatbuffers.Builder(0)
1013        MyGame.Example.Monster.MonsterStart(b)
1014        MyGame.Example.Monster.MonsterAddTesthashs32Fnv1(b, 1)
1015        MyGame.Example.Monster.MonsterAddTesthashu32Fnv1(b, 2)
1016        MyGame.Example.Monster.MonsterAddTesthashs64Fnv1(b, 3)
1017        MyGame.Example.Monster.MonsterAddTesthashu64Fnv1(b, 4)
1018        MyGame.Example.Monster.MonsterAddTesthashs32Fnv1a(b, 5)
1019        MyGame.Example.Monster.MonsterAddTesthashu32Fnv1a(b, 6)
1020        MyGame.Example.Monster.MonsterAddTesthashs64Fnv1a(b, 7)
1021        MyGame.Example.Monster.MonsterAddTesthashu64Fnv1a(b, 8)
1022        mon = MyGame.Example.Monster.MonsterEnd(b)
1023        b.Finish(mon)
1024
1025        # inspect the resulting data:
1026        mon2 = MyGame.Example.Monster.Monster.GetRootAsMonster(b.Bytes,
1027                                                               b.Head())
1028        self.assertEqual(1, mon2.Testhashs32Fnv1())
1029        self.assertEqual(2, mon2.Testhashu32Fnv1())
1030        self.assertEqual(3, mon2.Testhashs64Fnv1())
1031        self.assertEqual(4, mon2.Testhashu64Fnv1())
1032        self.assertEqual(5, mon2.Testhashs32Fnv1a())
1033        self.assertEqual(6, mon2.Testhashu32Fnv1a())
1034        self.assertEqual(7, mon2.Testhashs64Fnv1a())
1035        self.assertEqual(8, mon2.Testhashu64Fnv1a())
1036
1037    def test_getrootas_for_nonroot_table(self):
1038        b = flatbuffers.Builder(0)
1039        string = b.CreateString("MyStat")
1040
1041        MyGame.Example.Stat.StatStart(b)
1042        MyGame.Example.Stat.StatAddId(b, string)
1043        MyGame.Example.Stat.StatAddVal(b, 12345678)
1044        MyGame.Example.Stat.StatAddCount(b, 12345)
1045        stat = MyGame.Example.Stat.StatEnd(b)
1046        b.Finish(stat)
1047
1048        stat2 = MyGame.Example.Stat.Stat.GetRootAsStat(b.Bytes, b.Head())
1049
1050        self.assertEqual(b"MyStat", stat2.Id())
1051        self.assertEqual(12345678, stat2.Val())
1052        self.assertEqual(12345, stat2.Count())
1053
1054
1055class TestVtableDeduplication(unittest.TestCase):
1056    ''' TestVtableDeduplication verifies that vtables are deduplicated. '''
1057
1058    def test_vtable_deduplication(self):
1059        b = flatbuffers.Builder(0)
1060
1061        b.StartObject(4)
1062        b.PrependByteSlot(0, 0, 0)
1063        b.PrependByteSlot(1, 11, 0)
1064        b.PrependByteSlot(2, 22, 0)
1065        b.PrependInt16Slot(3, 33, 0)
1066        obj0 = b.EndObject()
1067
1068        b.StartObject(4)
1069        b.PrependByteSlot(0, 0, 0)
1070        b.PrependByteSlot(1, 44, 0)
1071        b.PrependByteSlot(2, 55, 0)
1072        b.PrependInt16Slot(3, 66, 0)
1073        obj1 = b.EndObject()
1074
1075        b.StartObject(4)
1076        b.PrependByteSlot(0, 0, 0)
1077        b.PrependByteSlot(1, 77, 0)
1078        b.PrependByteSlot(2, 88, 0)
1079        b.PrependInt16Slot(3, 99, 0)
1080        obj2 = b.EndObject()
1081
1082        got = b.Bytes[b.Head():]
1083
1084        want = bytearray([
1085            240, 255, 255, 255,  # == -12. offset to dedupped vtable.
1086            99, 0,
1087            88,
1088            77,
1089            248, 255, 255, 255,  # == -8. offset to dedupped vtable.
1090            66, 0,
1091            55,
1092            44,
1093            12, 0,
1094            8, 0,
1095            0, 0,
1096            7, 0,
1097            6, 0,
1098            4, 0,
1099            12, 0, 0, 0,
1100            33, 0,
1101            22,
1102            11,
1103        ])
1104
1105        self.assertEqual((len(want), want), (len(got), got))
1106
1107        table0 = flatbuffers.table.Table(b.Bytes, len(b.Bytes) - obj0)
1108        table1 = flatbuffers.table.Table(b.Bytes, len(b.Bytes) - obj1)
1109        table2 = flatbuffers.table.Table(b.Bytes, len(b.Bytes) - obj2)
1110
1111        def _checkTable(tab, voffsett_value, b, c, d):
1112            # vtable size
1113            got = tab.GetVOffsetTSlot(0, 0)
1114            self.assertEqual(12, got, 'case 0, 0')
1115
1116            # object size
1117            got = tab.GetVOffsetTSlot(2, 0)
1118            self.assertEqual(8, got, 'case 2, 0')
1119
1120            # default value
1121            got = tab.GetVOffsetTSlot(4, 0)
1122            self.assertEqual(voffsett_value, got, 'case 4, 0')
1123
1124            got = tab.GetSlot(6, 0, N.Uint8Flags)
1125            self.assertEqual(b, got, 'case 6, 0')
1126
1127            val = tab.GetSlot(8, 0, N.Uint8Flags)
1128            self.assertEqual(c, val, 'failed 8, 0')
1129
1130            got = tab.GetSlot(10, 0, N.Uint8Flags)
1131            self.assertEqual(d, got, 'failed 10, 0')
1132
1133        _checkTable(table0, 0, 11, 22, 33)
1134        _checkTable(table1, 0, 44, 55, 66)
1135        _checkTable(table2, 0, 77, 88, 99)
1136
1137
1138class TestExceptions(unittest.TestCase):
1139    def test_object_is_nested_error(self):
1140        b = flatbuffers.Builder(0)
1141        b.StartObject(0)
1142        assertRaises(self, lambda: b.StartObject(0),
1143                     flatbuffers.builder.IsNestedError)
1144
1145    def test_object_is_not_nested_error(self):
1146        b = flatbuffers.Builder(0)
1147        assertRaises(self, lambda: b.EndObject(),
1148                     flatbuffers.builder.IsNotNestedError)
1149
1150    def test_struct_is_not_inline_error(self):
1151        b = flatbuffers.Builder(0)
1152        b.StartObject(0)
1153        assertRaises(self, lambda: b.PrependStructSlot(0, 1, 0),
1154                     flatbuffers.builder.StructIsNotInlineError)
1155
1156    def test_unreachable_error(self):
1157        b = flatbuffers.Builder(0)
1158        assertRaises(self, lambda: b.PrependUOffsetTRelative(1),
1159                     flatbuffers.builder.OffsetArithmeticError)
1160
1161    def test_create_string_is_nested_error(self):
1162        b = flatbuffers.Builder(0)
1163        b.StartObject(0)
1164        s = 'test1'
1165        assertRaises(self, lambda: b.CreateString(s),
1166                     flatbuffers.builder.IsNestedError)
1167
1168    def test_finished_bytes_error(self):
1169        b = flatbuffers.Builder(0)
1170        assertRaises(self, lambda: b.Output(),
1171                     flatbuffers.builder.BuilderNotFinishedError)
1172
1173
1174def CheckAgainstGoldDataGo():
1175    try:
1176        gen_buf, gen_off = make_monster_from_generated_code()
1177        fn = 'monsterdata_go_wire.mon'
1178        if not os.path.exists(fn):
1179            print('Go-generated data does not exist, failed.')
1180            return False
1181
1182        # would like to use a context manager here, but it's less
1183        # backwards-compatible:
1184        f = open(fn, 'rb')
1185        go_wire_data = f.read()
1186        f.close()
1187
1188        CheckReadBuffer(bytearray(go_wire_data), 0)
1189        if not bytearray(gen_buf[gen_off:]) == bytearray(go_wire_data):
1190            raise AssertionError('CheckAgainstGoldDataGo failed')
1191    except:
1192        print('Failed to test against Go-generated test data.')
1193        return False
1194
1195    print('Can read Go-generated test data, and Python generates bytewise identical data.')
1196    return True
1197
1198
1199def CheckAgainstGoldDataJava():
1200    try:
1201        gen_buf, gen_off = make_monster_from_generated_code()
1202        fn = 'monsterdata_java_wire.mon'
1203        if not os.path.exists(fn):
1204            print('Java-generated data does not exist, failed.')
1205            return False
1206        f = open(fn, 'rb')
1207        java_wire_data = f.read()
1208        f.close()
1209
1210        CheckReadBuffer(bytearray(java_wire_data), 0)
1211    except:
1212        print('Failed to read Java-generated test data.')
1213        return False
1214
1215    print('Can read Java-generated test data.')
1216    return True
1217
1218
1219class LCG(object):
1220    ''' Include simple random number generator to ensure results will be the
1221        same cross platform.
1222        http://en.wikipedia.org/wiki/Park%E2%80%93Miller_random_number_generator '''
1223
1224    __slots__ = ['n']
1225
1226    InitialLCGSeed = 48271
1227
1228    def __init__(self):
1229        self.n = self.InitialLCGSeed
1230
1231    def Reset(self):
1232        self.n = self.InitialLCGSeed
1233
1234    def Next(self):
1235        self.n = ((self.n * 279470273) % 4294967291) & 0xFFFFFFFF
1236        return self.n
1237
1238
1239def BenchmarkVtableDeduplication(count):
1240    '''
1241    BenchmarkVtableDeduplication measures the speed of vtable deduplication
1242    by creating `prePop` vtables, then populating `count` objects with a
1243    different single vtable.
1244
1245    When count is large (as in long benchmarks), memory usage may be high.
1246    '''
1247
1248    prePop = 10
1249    builder = flatbuffers.Builder(0)
1250
1251    # pre-populate some vtables:
1252    for i in compat_range(prePop):
1253        builder.StartObject(i)
1254        for j in compat_range(i):
1255            builder.PrependInt16Slot(j, j, 0)
1256        builder.EndObject()
1257
1258    # benchmark deduplication of a new vtable:
1259    def f():
1260        builder.StartObject(prePop)
1261        for j in compat_range(prePop):
1262            builder.PrependInt16Slot(j, j, 0)
1263        builder.EndObject()
1264
1265    duration = timeit.timeit(stmt=f, number=count)
1266    rate = float(count) / duration
1267    print(('vtable deduplication rate: %.2f/sec' % rate))
1268
1269
1270def BenchmarkCheckReadBuffer(count, buf, off):
1271    '''
1272    BenchmarkCheckReadBuffer measures the speed of flatbuffer reading
1273    by re-using the CheckReadBuffer function with the gold data.
1274    '''
1275
1276    def f():
1277        CheckReadBuffer(buf, off)
1278
1279    duration = timeit.timeit(stmt=f, number=count)
1280    rate = float(count) / duration
1281    data = float(len(buf) * count) / float(1024 * 1024)
1282    data_rate = data / float(duration)
1283
1284    print(('traversed %d %d-byte flatbuffers in %.2fsec: %.2f/sec, %.2fMB/sec')
1285          % (count, len(buf), duration, rate, data_rate))
1286
1287
1288def BenchmarkMakeMonsterFromGeneratedCode(count, length):
1289    '''
1290    BenchmarkMakeMonsterFromGeneratedCode measures the speed of flatbuffer
1291    creation by re-using the make_monster_from_generated_code function for
1292    generating gold data examples.
1293    '''
1294
1295    duration = timeit.timeit(stmt=make_monster_from_generated_code,
1296                             number=count)
1297    rate = float(count) / duration
1298    data = float(length * count) / float(1024 * 1024)
1299    data_rate = data / float(duration)
1300
1301    print(('built %d %d-byte flatbuffers in %.2fsec: %.2f/sec, %.2fMB/sec' % \
1302           (count, length, duration, rate, data_rate)))
1303
1304
1305def backward_compatible_run_tests(**kwargs):
1306    if PY_VERSION < (2, 6):
1307        sys.stderr.write("Python version less than 2.6 are not supported")
1308        sys.stderr.flush()
1309        return False
1310
1311    # python2.6 has a reduced-functionality unittest.main function:
1312    if PY_VERSION == (2, 6):
1313        try:
1314            unittest.main(**kwargs)
1315        except SystemExit as e:
1316            if not e.code == 0:
1317                return False
1318        return True
1319
1320    # python2.7 and above let us not exit once unittest.main is run:
1321    kwargs['exit'] = False
1322    kwargs['verbosity'] = 0
1323    ret = unittest.main(**kwargs)
1324    if ret.result.errors or ret.result.failures:
1325        return False
1326
1327    return True
1328
1329def main():
1330    import os
1331    import sys
1332    if not len(sys.argv) == 4:
1333       sys.stderr.write('Usage: %s <benchmark vtable count>'
1334                        '<benchmark read count> <benchmark build count>\n'
1335                        % sys.argv[0])
1336       sys.stderr.write('       Provide COMPARE_GENERATED_TO_GO=1   to check'
1337                        'for bytewise comparison to Go data.\n')
1338       sys.stderr.write('       Provide COMPARE_GENERATED_TO_JAVA=1 to check'
1339                        'for bytewise comparison to Java data.\n')
1340       sys.stderr.flush()
1341       sys.exit(1)
1342
1343    kwargs = dict(argv=sys.argv[:-3])
1344
1345    # run tests, and run some language comparison checks if needed:
1346    success = backward_compatible_run_tests(**kwargs)
1347    if success and os.environ.get('COMPARE_GENERATED_TO_GO', 0) == "1":
1348        success = success and CheckAgainstGoldDataGo()
1349    if success and os.environ.get('COMPARE_GENERATED_TO_JAVA', 0) == "1":
1350        success = success and CheckAgainstGoldDataJava()
1351
1352    if not success:
1353        sys.stderr.write('Tests failed, skipping benchmarks.\n')
1354        sys.stderr.flush()
1355        sys.exit(1)
1356
1357    # run benchmarks (if 0, they will be a noop):
1358    bench_vtable = int(sys.argv[1])
1359    bench_traverse = int(sys.argv[2])
1360    bench_build = int(sys.argv[3])
1361    if bench_vtable:
1362        BenchmarkVtableDeduplication(bench_vtable)
1363    if bench_traverse:
1364        buf, off = make_monster_from_generated_code()
1365        BenchmarkCheckReadBuffer(bench_traverse, buf, off)
1366    if bench_build:
1367        buf, off = make_monster_from_generated_code()
1368        BenchmarkMakeMonsterFromGeneratedCode(bench_build, len(buf))
1369
1370if __name__ == '__main__':
1371    main()
1372