• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1package.path = string.format("../lua/?.lua;./?.lua;%s",package.path)
2local compat = require("flatbuffers.compat")
3
4local performBenchmarkTests = false
5
6if #arg > 1 then
7    print("usage: lua luatests [benchmark]");
8    return
9elseif #arg > 0 then
10    if(arg[1] == "benchmark") then
11        performBenchmarkTests = true
12    end
13end
14
15local function checkReadBuffer(buf, offset, sizePrefix)
16    offset = offset or 0
17
18    if type(buf) == "string" then
19        buf = flatbuffers.binaryArray.New(buf)
20    end
21
22    if sizePrefix then
23        local size = flatbuffers.N.Int32:Unpack(buf, offset)
24        assert(size == buf.size - offset - 4)
25        offset = offset + flatbuffers.N.Int32.bytewidth
26    end
27
28    local mon = monster.GetRootAsMonster(buf, offset)
29    assert(mon:Hp() == 80, "Monster Hp is not 80")
30    assert(mon:Mana() == 150, "Monster Mana is not 150")
31    assert(mon:Name() == "MyMonster", "Monster Name is not MyMonster")
32    assert(mon:Testbool() == true)
33
34    local vec = assert(mon:Pos(), "Monster Position is nil")
35    assert(vec:X() == 1.0)
36    assert(vec:Y() == 2.0)
37    assert(vec:Z() == 3.0)
38    assert(vec:Test1() == 3.0)
39    assert(vec:Test2() == 2)
40
41    local t = require("MyGame.Example.Test").New()
42    t = assert(vec:Test3(t))
43
44    assert(t:A() == 5)
45    assert(t:B() == 6)
46
47    local ut = require("MyGame.Example.Any")
48    assert(mon:TestType() == ut.Monster)
49
50    local table2 = mon:Test()
51    assert(getmetatable(table2) == "flatbuffers.view.mt")
52
53    local mon2 = monster.New()
54    mon2:Init(table2.bytes, table2.pos)
55
56    assert(mon2:Name() == "Fred")
57
58    assert(mon:InventoryLength() == 5)
59    local invsum = 0
60    for i=1,mon:InventoryLength() do
61        local v = mon:Inventory(i)
62        invsum = invsum + v
63    end
64    assert(invsum == 10)
65
66    for i=1,5 do
67        assert(mon:VectorOfLongs(i) == 10^((i-1)*2))
68    end
69
70    local dbls = { -1.7976931348623157e+308, 0, 1.7976931348623157e+308}
71    for i=1,mon:VectorOfDoublesLength() do
72        assert(mon:VectorOfDoubles(i) == dbls[i])
73    end
74
75    assert(mon:Test4Length() == 2)
76
77    local test0 = mon:Test4(1)
78    local test1 = mon:Test4(2)
79
80    local v0 = test0:A()
81    local v1 = test0:B()
82    local v2 = test1:A()
83    local v3 = test1:B()
84
85    local sumtest12 = v0 + v1 + v2 + v3
86    assert(sumtest12 == 100)
87
88    assert(mon:TestarrayofstringLength() == 2)
89    assert(mon:Testarrayofstring(1) == "test1")
90    assert(mon:Testarrayofstring(2) == "test2")
91
92    assert(mon:TestarrayoftablesLength() == 0)
93    assert(mon:TestnestedflatbufferLength() == 0)
94    assert(mon:Testempty() == nil)
95end
96
97local function generateMonster(sizePrefix, b)
98    if b then b:Clear() end
99    b = b or flatbuffers.Builder(0)
100    local str = b:CreateString("MyMonster")
101    local test1 = b:CreateString("test1")
102    local test2 = b:CreateString("test2")
103    local fred = b:CreateString("Fred")
104
105    monster.StartInventoryVector(b, 5)
106    b:PrependByte(4)
107    b:PrependByte(3)
108    b:PrependByte(2)
109    b:PrependByte(1)
110    b:PrependByte(0)
111    local inv = b:EndVector(5)
112
113    monster.Start(b)
114    monster.AddName(b, fred)
115    local mon2 = monster.End(b)
116
117    monster.StartTest4Vector(b, 2)
118    test.CreateTest(b, 10, 20)
119    test.CreateTest(b, 30, 40)
120    local test4 = b:EndVector(2)
121
122    monster.StartTestarrayofstringVector(b, 2)
123    b:PrependUOffsetTRelative(test2)
124    b:PrependUOffsetTRelative(test1)
125    local testArrayOfString = b:EndVector(2)
126
127    monster.StartVectorOfLongsVector(b, 5)
128    b:PrependInt64(100000000)
129    b:PrependInt64(1000000)
130    b:PrependInt64(10000)
131    b:PrependInt64(100)
132    b:PrependInt64(1)
133    local vectorOfLongs = b:EndVector(5)
134
135    monster.StartVectorOfDoublesVector(b, 3)
136    b:PrependFloat64(1.7976931348623157e+308)
137    b:PrependFloat64(0)
138    b:PrependFloat64(-1.7976931348623157e+308)
139    local vectorOfDoubles = b:EndVector(3)
140
141    monster.Start(b)
142    local pos = vec3.CreateVec3(b, 1.0, 2.0, 3.0, 3.0, 2, 5, 6)
143    monster.AddPos(b, pos)
144
145    monster.AddHp(b, 80)
146    monster.AddName(b, str)
147    monster.AddInventory(b, inv)
148    monster.AddTestType(b, 1)
149    monster.AddTest(b, mon2)
150    monster.AddTest4(b, test4)
151    monster.AddTestbool(b, true)
152    monster.AddTestbool(b, false)
153    monster.AddTestbool(b, null)
154    monster.AddTestbool(b,"true")
155    monster.AddTestarrayofstring(b, testArrayOfString)
156    monster.AddVectorOfLongs(b, vectorOfLongs)
157    monster.AddVectorOfDoubles(b, vectorOfDoubles)
158    local mon = monster.End(b)
159
160    if sizePrefix then
161        b:FinishSizePrefixed(mon)
162    else
163        b:Finish(mon)
164    end
165    return b:Output(true), b:Head()
166end
167
168local function sizePrefix(sizePrefix)
169    local buf,offset = generateMonster(sizePrefix)
170    checkReadBuffer(buf, offset, sizePrefix)
171end
172
173local function fbbClear()
174    -- Generate a builder that will be 'cleared' and reused to create two different objects.
175    local fbb = flatbuffers.Builder(0)
176
177    -- First use the builder to read the normal monster data and verify it works
178    local buf, offset = generateMonster(false, fbb)
179    checkReadBuffer(buf, offset, false)
180
181    -- Then clear the builder to be used again
182    fbb:Clear()
183
184    -- Storage for the built monsters
185    local monsters = {}
186    local lastBuf
187
188    -- Make another builder that will be use identically to the 'cleared' one so outputs can be compared. Build both the
189    -- Cleared builder and new builder in the exact same way, so we can compare their results
190    for i, builder in ipairs({fbb, flatbuffers.Builder(0)}) do
191        local strOffset = builder:CreateString("Hi there")
192        monster.Start(builder)
193        monster.AddPos(builder, vec3.CreateVec3(builder, 3.0, 2.0, 1.0, 17.0, 3, 100, 123))
194        monster.AddName(builder, strOffset)
195        monster.AddMana(builder, 123)
196        builder:Finish(monster.End(builder))
197        local buf = builder:Output(false)
198        if not lastBuf then
199            lastBuf = buf
200        else
201            -- the output, sized-buffer should be identical
202            assert(lastBuf == buf, "Monster output buffers are not identical")
203        end
204        monsters[i] = monster.GetRootAsMonster(flatbuffers.binaryArray.New(buf), 0)
205    end
206
207    -- Check that all the fields for the generated monsters are as we expect
208    for i, monster in ipairs(monsters) do
209        assert(monster:Name() == "Hi there", "Monster Name is not 'Hi There' for monster "..i)
210        -- HP is default to 100 in the schema, but we change it in generateMonster to 80, so this is a good test to
211        -- see if the cleared builder really clears the data.
212        assert(monster:Hp() == 100, "HP doesn't equal the default value for monster "..i)
213        assert(monster:Mana() == 123, "Monster Mana is not '123' for monster "..i)
214        assert(monster:Pos():X() == 3.0, "Monster vec3.X is not '3' for monster "..i)
215    end
216end
217
218local function testCanonicalData()
219    local f = assert(io.open('monsterdata_test.mon', 'rb'))
220    local wireData = f:read("*a")
221    f:close()
222    checkReadBuffer(wireData)
223end
224
225local function testCreateEmptyString()
226    local b = flatbuffers.Builder(0)
227    local str = b:CreateString("")
228    monster.Start(b)
229    monster.AddName(b, str)
230    b:Finish(monster.End(b))
231    local s = b:Output()
232    local data = flatbuffers.binaryArray.New(s)
233    local mon = monster.GetRootAsMonster(data, 0)
234    assert(mon:Name() == "")
235end
236
237local function benchmarkMakeMonster(count, reuseBuilder)
238    local fbb = reuseBuilder and flatbuffers.Builder(0)
239    local length = #(generateMonster(false, fbb))
240
241    local s = os.clock()
242    for i=1,count do
243        generateMonster(false, fbb)
244    end
245    local e = os.clock()
246
247    local dur = (e - s)
248    local rate = count / (dur * 1000)
249    local data = (length * count) / (1024 * 1024)
250    local dataRate = data / dur
251
252    print(string.format('built %d %d-byte flatbuffers in %.2fsec: %.2f/msec, %.2fMB/sec',
253        count, length, dur, rate, dataRate))
254end
255
256local function benchmarkReadBuffer(count)
257    local f = assert(io.open('monsterdata_test.mon', 'rb'))
258    local buf = f:read("*a")
259    f:close()
260
261    local s = os.clock()
262    for i=1,count do
263        checkReadBuffer(buf)
264    end
265    local e = os.clock()
266
267    local dur = (e - s)
268    local rate = count / (dur * 1000)
269    local data = (#buf * count) / (1024 * 1024)
270    local dataRate = data / dur
271
272    print(string.format('traversed %d %d-byte flatbuffers in %.2fsec: %.2f/msec, %.2fMB/sec',
273        count, #buf, dur, rate, dataRate))
274end
275
276local function getRootAs_canAcceptString()
277    local f = assert(io.open('monsterdata_test.mon', 'rb'))
278    local wireData = f:read("*a")
279    f:close()
280    assert(type(wireData) == "string", "Data is not a string");
281    local mon = monster.GetRootAsMonster(wireData, 0)
282    assert(mon:Hp() == 80, "Monster Hp is not 80")
283end
284
285local function testAccessByteVectorAsString()
286    local f = assert(io.open('monsterdata_test.mon', 'rb'))
287    local wireData = f:read("*a")
288    f:close()
289    local mon = monster.GetRootAsMonster(wireData, 0)
290    -- the data of byte array Inventory is [0, 1, 2, 3, 4]
291    local s = mon:InventoryAsString(1, 3)
292    assert(#s == 3)
293    for i = 1, #s do
294        assert(string.byte(s, i) == i - 1)
295    end
296
297    local s = mon:InventoryAsString(2, 5)
298    assert(#s == 4)
299    for i = 1, #s do
300        assert(string.byte(s, i) == i)
301    end
302
303    local s = mon:InventoryAsString(5, 5)
304    assert(#s == 1)
305    assert(string.byte(s, 1) == 4)
306
307    local s = mon:InventoryAsString(2)
308    assert(#s == 4)
309    for i = 1, #s do
310        assert(string.byte(s, i) == i)
311    end
312
313    local s = mon:InventoryAsString()
314    assert(#s == 5)
315    for i = 1, #s do
316        assert(string.byte(s, i) == i - 1)
317    end
318end
319
320local tests =
321{
322    {
323        f = sizePrefix,
324        d = "Test size prefix",
325        args = {{true}, {false}}
326    },
327    {
328        f = fbbClear,
329        d = "FlatBufferBuilder Clear",
330    },
331    {
332        f = testCanonicalData,
333        d = "Tests Canonical flatbuffer file included in repo"
334    },
335    {
336        f = testCreateEmptyString,
337        d = "Avoid infinite loop when creating empty string"
338    },
339    {
340        f = getRootAs_canAcceptString,
341        d = "Tests that GetRootAs<type>() generated methods accept strings"
342    },
343    {
344        f = testAccessByteVectorAsString,
345        d = "Access byte vector as string"
346    },
347}
348
349local benchmarks =
350{
351    {
352        f = benchmarkMakeMonster,
353        d = "Benchmark making monsters",
354        args = {
355            {100},
356            {1000},
357            {10000},
358            {10000, true}
359        }
360    },
361    {
362        f = benchmarkReadBuffer,
363        d = "Benchmark reading monsters",
364        args = {
365            {100},
366            {1000},
367            {10000},
368            -- uncomment following to run 1 million to compare.
369            -- Took ~141 seconds on my machine
370            --{1000000},
371        }
372    },
373}
374
375local result, err = xpcall(function()
376    flatbuffers = assert(require("flatbuffers"))
377    monster = assert(require("MyGame.Example.Monster"))
378    test = assert(require("MyGame.Example.Test"))
379    vec3 = assert(require("MyGame.Example.Vec3"))
380
381    local function buildArgList(tbl)
382        local s = ""
383        for _,item in ipairs(tbl) do
384            s = s .. tostring(item) .. ","
385        end
386        return s:sub(1,-2)
387    end
388
389    if performBenchmarkTests then
390        for _,benchmark in ipairs(benchmarks) do
391            table.insert(tests, benchmark)
392        end
393    end
394
395    local testsPassed, testsFailed = 0,0
396    for _,test in ipairs(tests) do
397        local allargs = test.args or {{}}
398        for _,args in ipairs(allargs) do
399            local results, err = xpcall(test.f,debug.traceback, table.unpack(args))
400            if results then
401                testsPassed = testsPassed + 1
402            else
403                testsFailed = testsFailed + 1
404                print(string.format(" Test [%s](%s) failed: \n\t%s",
405                        test.d or "",
406                        buildArgList(args),
407                        err))
408            end
409        end
410    end
411
412    local totalTests = testsPassed + testsFailed
413    print(string.format("# of test passed: %d / %d (%.2f%%)",
414        testsPassed,
415        totalTests,
416        totalTests ~= 0
417            and 100 * (testsPassed / totalTests)
418            or 0)
419        )
420
421    return 0
422end, debug.traceback)
423
424if not result then
425    print("Unable to run tests due to test framework error: ",err)
426end
427
428os.exit(result and 0 or -1)
429