using Test, FlatBuffers

const cppData = "monsterdata_test.mon"
const javaData = "monsterdata_java_wire.mon"
const outData = "out_data"
const fuzzFields = 4
const fuzzObjects = 10000

const overflowingInt32Val = read(IOBuffer(UInt8[0x83, 0x33, 0x33, 0x33]), Int32)
const overflowingInt64Val = read(IOBuffer(UInt8[0x84, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44]), Int64)

#   TestAll runs all checks, failing if any errors occur.
function TestAll(t)
    #   Verify that the Go FlatBuffers runtime library generates the
    #   expected bytes (does not use any schema):
    CheckByteLayout(t.Fatalf)
    CheckMutateMethods(t.Fatalf)

    #   Verify that panics are raised during exceptional conditions:
    CheckNotInObjectError(t.Fatalf)
    CheckStringIsNestedError(t.Fatalf)
    CheckByteStringIsNestedError(t.Fatalf)
    CheckStructIsNotInlineError(t.Fatalf)
    CheckFinishedBytesError(t.Fatalf)
    CheckSharedStrings(t.Fatalf)
    
    #   Verify that GetRootAs works for non-root tables
    CheckGetRootAsForNonRootTable(t.Fatalf)
    CheckTableAccessors(t.Fatalf)

    #   Verify that using the generated Go code builds a buffer without
    #   returning errors:
    generated, off = CheckGeneratedBuild(t.Fatalf)

    #   Verify that the buffer generated by Go code is readable by the
    #   generated Go code:
    CheckReadBuffer(generated, off, t.Fatalf)
    CheckMutateBuffer(generated, off, t.Fatalf)
    CheckObjectAPI(generated, off, t.Fatalf)

    #   Verify that the buffer generated by C++ code is readable by the
    #   generated Go code:
    monsterDataCpp = read(cppData)

    CheckReadBuffer(monsterDataCpp, 0, t.Fatalf)
    CheckMutateBuffer(monsterDataCpp, 0, t.Fatalf)
    CheckObjectAPI(monsterDataCpp, 0, t.Fatalf)

    #   Verify that vtables are deduplicated when written:
    CheckVtableDeduplication(t.Fatalf)

    #   Verify the enum names
    CheckEnumNames(t.Fatalf)

    #   Verify enum String methods
    CheckEnumString(t.Fatalf)

    #   Verify the enum values maps
    CheckEnumValues(t.Fatalf)

    #   Verify that the Go code used in FlatBuffers documentation passes
    #   some sanity checks:
    CheckDocExample(generated, off, t.Fatalf)

    #   Check Builder.CreateByteVector
    CheckCreateByteVector(t.Fatalf)

    #   Check a parent namespace import
    CheckParentNamespace(t.Fatalf)

    #   If the filename of the FlatBuffers file generated by the Java test
    #   is given, check that Go code can read it, and that Go code
    #   generates an identical buffer when used to create the example data:
    if javaData != ""
        monsterDataJava = read(javaData)
        CheckReadBuffer(monsterDataJava, 0, t.Fatalf)
        CheckByteEquality(generated[off:end], monsterDataJava, t.Fatalf)
    end

    #   Verify that various fuzzing scenarios produce a valid FlatBuffer.
    if fuzz
        checkFuzz(fuzzFields, fuzzObjects, t.Fatalf)
    end

    #   Write the generated buffer out to a file:
    open(outData, "w+") do io
        write(io, generated[off:end])
    end
end

#   CheckReadBuffer checks that the given buffer is evaluated correctly
#   as the example Monster.
function CheckReadBuffer(buf, offset)
    #   try the two ways of generating a monster
    #TODO
    monster1 = Example.GetRootAsMonster(buf, offset)
    monster2 = Monster()
    FlatBuffers.GetRootAs(buf, offset, monster2)
    for monster in (monster1, monster2)
        @test monster.hp == 80
        
        #   default
        @test monster.mana == 150
        @test monster.name == "MyMonster"
        @test monster.color == Example.ColorBlue
        @test monster.testbool

        #   initialize a Vec3 from Pos()
        @test !isempty(monster.pos)

        #   verify the properties of the Vec3
        @test monster.pos.x === Float32(1.0)
        @test monster.pos.y === Float32(2.0)
        @test monster.pos.z === Float32(3.0)
        @test monster.pos.test1 === Float64(3.0)
        @test monster.pos.test2 === Example.ColorGreen
        t = monster.pos.test3
        @test t !== nothing
        @test t.a === Int16(5)
        @test t.b === Int8(6)
        
        @test monster.testType == Example.AnyMonster
        
        table2 = monster.test
        @test table2 !== nothing
        monster2 = Monster(tabl2.bytes, table2.pos)
        @test monster2.name == "Fred"
        inventorySlice = monster.inventoryBytes
        @test length(inventorySlice) == monster.inventoryLength
        @test monster.inventoryLength == 5
        
        invsum = 0
        l = monster.inventoryLength
        for i = 1:l
            v = monster.inventory[i]
            @test v == inventorySlice[i]
            invsum += v
        end
        @test invsum == 10
        
        @test monster.test4Length == 2
        
        test0 = monster.test4[1]
        test1 = monster.test4[2]
        
        #   the position of test0 and test1 are swapped in monsterdata_java_wire
        #   and monsterdata_test_wire, so ignore ordering
        v0 = test0.a
        v1 = test0.b
        v2 = test1.a
        v3 = test1.b
        @test v0 + v1 + v2 + v3 == 100
        
        @test monster.testArrayOfStringLength == 2
        @test monster.testArrayOfString[1] == "test1"
        @test monster.testArrayOfString[2] == "test2"
    end
end

#   CheckMutateBuffer checks that the given buffer can be mutated correctly
#   as the example Monster. Only available scalar values are mutated.
# function CheckMutateBuffer(org, offset)
#     #   make a copy to mutate
#     buf = make([]byte, len(org))
#     copy(buf, org)

#     #   load monster data from the buffer
#     monster = Example.GetRootAsMonster(buf, offset)

#     #   test case struct
#     type testcase struct {
#         field  string
#         testfn func() bool
#     end

#     testForOriginalValues = []testcase{
#         testcase{"Hp", func() bool { return monster.Hp() == 80 }end,
#         testcase{"Mana", func() bool { return monster.Mana() == 150 }end,
#         testcase{"Testbool", func() bool { return monster.Testbool() == true }end,
#         testcase{"Pos.X'", func() bool { return monster.Pos(nil).X() == float32(1.0) }end,
#         testcase{"Pos.Y'", func() bool { return monster.Pos(nil).Y() == float32(2.0) }end,
#         testcase{"Pos.Z'", func() bool { return monster.Pos(nil).Z() == float32(3.0) }end,
#         testcase{"Pos.Test1'", func() bool { return monster.Pos(nil).Test1() == float64(3.0) }end,
#         testcase{"Pos.Test2'", func() bool { return monster.Pos(nil).Test2() == Example.ColorGreen }end,
#         testcase{"Pos.Test3.A", func() bool { return monster.Pos(nil).Test3(nil).A() == int16(5) }end,
#         testcase{"Pos.Test3.B", func() bool { return monster.Pos(nil).Test3(nil).B() == int8(6) }end,
#         testcase{"Inventory[2]", func() bool { return monster.Inventory(2) == byte(2) }end,
#     end

#     testMutability = []testcase{
#         testcase{"Hp", func() bool { return monster.MutateHp(70) }end,
#         testcase{"Mana", func() bool { return !monster.MutateMana(140) }end,
#         testcase{"Testbool", func() bool { return monster.MutateTestbool(false) }end,
#         testcase{"Pos.X", func() bool { return monster.Pos(nil).MutateX(10.0) }end,
#         testcase{"Pos.Y", func() bool { return monster.Pos(nil).MutateY(20.0) }end,
#         testcase{"Pos.Z", func() bool { return monster.Pos(nil).MutateZ(30.0) }end,
#         testcase{"Pos.Test1", func() bool { return monster.Pos(nil).MutateTest1(30.0) }end,
#         testcase{"Pos.Test2", func() bool { return monster.Pos(nil).MutateTest2(Example.ColorBlue) }end,
#         testcase{"Pos.Test3.A", func() bool { return monster.Pos(nil).Test3(nil).MutateA(50) }end,
#         testcase{"Pos.Test3.B", func() bool { return monster.Pos(nil).Test3(nil).MutateB(60) }end,
#         testcase{"Inventory[2]", func() bool { return monster.MutateInventory(2, 200) }end,
#     end

#     testForMutatedValues = []testcase{
#         testcase{"Hp", func() bool { return monster.Hp() == 70 }end,
#         testcase{"Mana", func() bool { return monster.Mana() == 150 }end,
#         testcase{"Testbool", func() bool { return monster.Testbool() == false }end,
#         testcase{"Pos.X'", func() bool { return monster.Pos(nil).X() == float32(10.0) }end,
#         testcase{"Pos.Y'", func() bool { return monster.Pos(nil).Y() == float32(20.0) }end,
#         testcase{"Pos.Z'", func() bool { return monster.Pos(nil).Z() == float32(30.0) }end,
#         testcase{"Pos.Test1'", func() bool { return monster.Pos(nil).Test1() == float64(30.0) }end,
#         testcase{"Pos.Test2'", func() bool { return monster.Pos(nil).Test2() == Example.ColorBlue }end,
#         testcase{"Pos.Test3.A", func() bool { return monster.Pos(nil).Test3(nil).A() == int16(50) }end,
#         testcase{"Pos.Test3.B", func() bool { return monster.Pos(nil).Test3(nil).B() == int8(60) }end,
#         testcase{"Inventory[2]", func() bool { return monster.Inventory(2) == byte(200) }end,
#     end

#     testInvalidEnumValues = []testcase{
#         testcase{"Pos.Test2", func() bool { return monster.Pos(nil).MutateTest2(Example.Color(20)) }end,
#         testcase{"Pos.Test2", func() bool { return monster.Pos(nil).Test2() == Example.Color(20) }end,
#     end

#     #   make sure original values are okay
#     for _, t = range testForOriginalValues {
#         if !t.testfn()
#             fail("field '" + t.field + "' doesn't have the expected original value")
#         end
#     end

#     #   try to mutate fields and check mutability
#     for _, t = range testMutability {
#         if !t.testfn()
#             error(string("field '"+t.field+"' failed mutability test", true, false))
#         end
#     end

#     #   test whether values have changed
#     for _, t = range testForMutatedValues {
#         if !t.testfn()
#             fail("field '" + t.field + "' doesn't have the expected mutated value")
#         end
#     end

#     #   make sure the buffer has changed
#     if reflect.DeepEqual(buf, org)
#         fail("mutate buffer failed")
#     end

#     #   To make sure the buffer has changed accordingly
#     #   Read data from the buffer and verify all fields
#     monster = Example.GetRootAsMonster(buf, offset)
#     for _, t = range testForMutatedValues {
#         if !t.testfn()
#             fail("field '" + t.field + "' doesn't have the expected mutated value")
#         end
#     end

#     #   a couple extra tests for "invalid" enum values, which don't correspond to
#     #   anything in the schema, but are allowed
#     for _, t = range testInvalidEnumValues {
#         if !t.testfn()
#             fail("field '" + t.field + "' doesn't work with an invalid enum value")
#         end
#     end

#     #   reverting all fields to original values should
#     #   re-create the original buffer. Mutate all fields
#     #   back to their original values and compare buffers.
#     #   This test is done to make sure mutations do not do
#     #   any unnecessary changes to the buffer.
#     monster = Example.GetRootAsMonster(buf, offset)
#     monster.MutateHp(80)
#     monster.MutateTestbool(true)
#     monster.Pos(nil).MutateX(1.0)
#     monster.Pos(nil).MutateY(2.0)
#     monster.Pos(nil).MutateZ(3.0)
#     monster.Pos(nil).MutateTest1(3.0)
#     monster.Pos(nil).MutateTest2(Example.ColorGreen)
#     monster.Pos(nil).Test3(nil).MutateA(5)
#     monster.Pos(nil).Test3(nil).MutateB(6)
#     monster.MutateInventory(2, 2)

#     for _, t = range testForOriginalValues {
#         if !t.testfn()
#             fail("field '" + t.field + "' doesn't have the expected original value")
#         end
#     end

#     #   buffer should have original values
#     if !reflect.DeepEqual(buf, org)
#         fail("revert changes failed")
#     end
# end

# function CheckObjectAPI(buf, offset)
#     #TODO
#     monster = Example.GetRootAsMonster(buf, offset).UnPack()

#     @test monster.hp == 80
    
#     #   default
#     @test monster.mana == 150
    
#     builder = FlatBuffers.Builder()
#     finish!(builder, monster.Pack(builder))
#     monster2 = Example.GetRootAsMonster(finishedbytes(builder), 0).UnPack()
#     if !reflect.DeepEqual(monster, monster2)
#         error(string("Pack/Unpack()", monster, monster2))
#     end
# end

#   Low level stress/fuzz test: serialize/deserialize a variety of
#   different kinds of data in different combinations
@testset "checkFuzz" begin

    #   Values we're testing against: chosen to ensure no bits get chopped
    #   off anywhere, and also be different from eachother.
    boolVal = true
    int8Val = Int8(-127) #   0x81
    uint8Val = UInt8(0xFF)
    int16Val = Int16(-32222) #   0x8222
    uint16Val = UInt16(0xFEEE)
    int32Val = Int32(overflowingInt32Val)
    uint32Val = UInt32(0xFDDDDDDD)
    int64Val = Int64(overflowingInt64Val)
    uint64Val = UInt64(0xFCCCCCCCCCCCCCCC)
    float32Val = Float32(3.14159)
    float64Val = Float64(3.14159265359)

    testValuesMax = 11 #   hardcoded to the number of scalar types

    builder = FlatBuffers.Builder()
    l = NewLCG()

    objects = Vector{FlatBuffers.UOffsetT}(undef, fuzzObjects)

    #   Generate fuzzObjects random objects each consisting of
    #   fuzzFields fields, each of a random type.
    for i = 1:fuzzObjects
        builder.startobject!(fuzzFields)

        for f = 1:fuzzFields
            choice = next!(l) % UInt32(testValuesMax)
            if choice == 0
                prependslot!(builder, Int(f), boolVal, false)
            elseif choice == 1
                prependslot!(builder, Int(f), int8Val, 0)
            elseif choice == 2
                prependslot!(builder, Int(f), uint8Val, 0)
            elseif choice == 3
                prependslot!(builder, Int(f), int16Val, 0)
            elseif choice == 4
                prependslot!(builder, Int(f), uint16Val, 0)
            elseif choice == 5
                prependslot!(builder, Int(f), int32Val, 0)
            elseif choice == 6
                prependslot!(builder, Int(f), uint32Val, 0)
            elseif choice == 7
                prependslot!(builder, Int(f), int64Val, 0)
            elseif choice == 8
                prependslot!(builder, Int(f), uint64Val, 0)
            elseif choice == 9
                prependslot!(builder, Int(f), float32Val, 0)
            elseif choice == 10
                prependslot!(builder, Int(f), float64Val, 0)
            end
        end

        off = endobject!(builder)

        #   store the offset from the end of the builder buffer,
        #   since it will keep growing:
        objects[i] = off
    end

    #   Do some bookkeeping to generate stats on fuzzes:
    stats = Dict{String, Int}()
    check = function(desc, want, got)
        stats[desc] += 1
        @test want == got
    end

    l = NewLCG() #   Reset.

    #   Test that all objects we generated are readable and return the
    #   expected values. We generate random objects in the same order
    #   so this is deterministic.
    for i = 1:fuzzObjects

        table = FlatBuffers.Table(
            builder.bytes,
            FlatBuffers.UOffsetT(length(builder.bytes)) - objects[i]
        )

        for j = 1:fuzzFields
            f = FlatBuffers.VOffsetT((FlatBuffers.VtableMetadataFields + j) * sizeof(FlatBuffers.VOffsetT))
            choice = next!(l) % UInt32(testValuesMax)

            if choice == 0
                check("bool", boolVal, getslot(table, f, false))
            elseif choice == 1
                check("int8", int8Val, getslot(table, f, 0))
            elseif choice == 2
                check("uint8", uint8Val, getslot(table, f, 0))
            elseif choice == 3
                check("int16", int16Val, getslot(table, f, 0))
            elseif choice == 4
                check("uint16", uint16Val, getslot(table, f, 0))
            elseif choice == 5
                check("int32", int32Val, getslot(table, f, 0))
            elseif choice == 6
                check("UInt32", uint32Val, getslot(table, f, 0))
            elseif choice == 7
                check("int64", int64Val, getslot(table, f, 0))
            elseif choice == 8
                check("uint64", uint64Val, getslot(table, f, 0))
            elseif choice == 9
                check("float32", float32Val, getslot(table, f, 0))
            elseif choice == 10
                check("float64", float64Val, getslot(table, f, 0))
            end
        end
    end

    #   If enough checks were made, verify that all scalar types were used:
    if fuzzFields * fuzzObjects >= testValuesMax
        @test length(stats) == testValuesMax
    end

    #   Print some counts, if needed:
    if fuzzFields == 0 || fuzzObjects == 0
        println("fuzz\tfields: $fuzzFields\tobjects: $fuzzObjects\t[none]\t0")
    else
        keys = collect(keys(stats))
        sort!(keys)
        for k in keys
            println("fuzz\tfields: $fuzzFields\tobjects: $fuzzObjects\t$k\t$(stats[k])\n")
        end
    end

    return
end

#   CheckByteLayout verifies the bytes of a Builder in various scenarios.
function CheckByteLayout()
    b::FlatBuffers.Builder
    check = function(want)
        i += 1
        @test want == b.bytes[b.head:end]
    end

    #   test 1: numbers

    b = FlatBuffers.Builder()
    check(UInt8[])
    prepend!(b, true)
    check(UInt8[1])
    prepend!(b, -127)
    check(UInt8[129, 1])
    prepend!(b, 255)
    check(UInt8[255, 129, 1])
    prepend!(b, -32222)
    check(UInt8[0x22, 0x82, 0, 255, 129, 1]) #   first pad
    prepend!(b, 0xFEEE)
    check(UInt8[0xEE, 0xFE, 0x22, 0x82, 0, 255, 129, 1]) #   no pad this time
    prepend!(b, -53687092)
    check(UInt8[204, 204, 204, 252, 0xEE, 0xFE, 0x22, 0x82, 0, 255, 129, 1])
    prepend!(b, 0x98765432)
    check(UInt8[0x32, 0x54, 0x76, 0x98, 204, 204, 204, 252, 0xEE, 0xFE, 0x22, 0x82, 0, 255, 129, 1])

    #   test 1b: numbers 2

    b = FlatBuffers.Builder()
    prepend!(b, 0x1122334455667788)
    check(UInt8[0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11])

    #   test 2: 1xbyte vector

    b = FlatBuffers.Builder()
    check(UInt8[])
    startvector!(b, sizeof(UInt8), 1, 1)
    check(UInt8[0, 0, 0]) #   align to 4bytes
    prepend!(b, UInt8(1))
    check(UInt8[1, 0, 0, 0])
    endvector!(b, 1)
    check(UInt8[1, 0, 0, 0, 1, 0, 0, 0]) #   padding

    #   test 3: 2xbyte vector

    b = FlatBuffers.Builder()
    startvector!(b, sizeof(UInt8), 2, 1)
    check(UInt8[0, 0]) #   align to 4bytes
    prepend!(b, UInt8(1))
    check(UInt8[1, 0, 0])
    prepend!(b, UInt8(2))
    check(UInt8[2, 1, 0, 0])
    endvector!(b, 2)
    check(UInt8[2, 0, 0, 0, 2, 1, 0, 0]) #   padding

    #   test 3b: 11xbyte vector matches builder size

    b = FlatBuffers.Builder(12)
    startvector!(b, sizeof(UInt8), 8, 1)
    start = UInt8[]
    check(start)
    for i = 1:11
        prepend!(b, UInt8(i))
        pushfirst!(start, UInt8(i))
        check(start)
    end
    endvector!(b, 8)
    check(append!(UInt8[8, 0, 0, 0], start))

    #   test 4: 1xuint16 vector

    b = FlatBuffers.Builder()
    startvector!(b, sizeof(UInt16), 1, 1)
    check(UInt8[0, 0]) #   align to 4bytes
    prepend!(b, 1)
    check(UInt8[1, 0, 0, 0])
    endvector!(b, 1)
    check(UInt8[1, 0, 0, 0, 1, 0, 0, 0]) #   padding

    #   test 5: 2xuint16 vector

    b = FlatBuffers.Builder()
    startvector!(b, sizeof(UInt16), 2, 1)
    check(UInt8[]) #   align to 4bytes
    prepend!(b, 0xABCD)
    check(UInt8[0xCD, 0xAB])
    prepend!(b, 0xDCBA)
    check(UInt8[0xBA, 0xDC, 0xCD, 0xAB])
    endvector!(b, 2)
    check(UInt8[2, 0, 0, 0, 0xBA, 0xDC, 0xCD, 0xAB])

    #   test 6: CreateString

    b = FlatBuffers.Builder()
    createstring!(b, "foo")
    check(UInt8[3, 0, 0, 0, 'f', 'o', 'o', 0]) #   0-terminated, no pad
    createstring!(b, "moop")
    check(UInt8[4, 0, 0, 0, 'm', 'o', 'o', 'p', 0, 0, 0, 0, #   0-terminated, 3-byte pad
        3, 0, 0, 0, 'f', 'o', 'o', 0])

    #   test 6b: CreateString unicode

    b = FlatBuffers.Builder()
    #   These characters are chinese from blog.golang.org/strings
    #   We use escape codes here so that editors without unicode support
    #   aren't bothered:
    uni_str = "\u65e5\u672c\u8a9e"
    createstring!(b, uni_str)
    check(UInt8[9, 0, 0, 0, 230, 151, 165, 230, 156, 172, 232, 170, 158, 0, #    null-terminated, 2-byte pad
        0, 0])

    #   test 6c: CreateByteString

    b = FlatBuffers.Builder()
    createstring!(b, "foo")
    check(UInt8[3, 0, 0, 0, 'f', 'o', 'o', 0]) #   0-terminated, no pad
    createstring!(b, "moop")
    check(UInt8[4, 0, 0, 0, 'm', 'o', 'o', 'p', 0, 0, 0, 0, #   0-terminated, 3-byte pad
        3, 0, 0, 0, 'f', 'o', 'o', 0])

    #   test 7: empty vtable
    b = FlatBuffers.Builder()
    startobject!(b, 0)
    check(UInt8[])
    endobject!(b)
    check(UInt8[4, 0, 4, 0, 4, 0, 0, 0])

    #   test 8: vtable with one true bool
    b = FlatBuffers.Builder()
    check(UInt8[])
    startobject!(b, 1)
    check(UInt8[])
    prependslot!(b, 0, true, false)
    endobject!(b)
    check(UInt8[
        6, 0, #   vtable bytes
        8, 0, #   length of object including vtable offset
        7, 0, #   start of bool value
        6, 0, 0, 0, #   offset for start of vtable (int32)
        0, 0, 0, #   padded to 4 bytes
        1, #   bool value
    ])

    #   test 9: vtable with one default bool
    b = FlatBuffers.Builder()
    check(UInt8[])
    startobject!(b, 1)
    check(UInt8[])
    prependslot!(b, 0, false, false)
    endobject!(b)
    check(UInt8[
        4, 0, #   vtable bytes
        4, 0, #   end of object from here
        #   entry 1 is zero and not stored.
        4, 0, 0, 0, #   offset for start of vtable (int32)
    ])

    #   test 10: vtable with one int16
    b = FlatBuffers.Builder()
    startobject!(b, 1)
    prependslot!(b, 0, 0x789A, 0)
    endobject!(b)
    check(UInt8[
        6, 0, #   vtable bytes
        8, 0, #   end of object from here
        6, 0, #   offset to value
        6, 0, 0, 0, #   offset for start of vtable (int32)
        0, 0, #   padding to 4 bytes
        0x9A, 0x78,
    ])

    #   test 11: vtable with two int16
    b = FlatBuffers.Builder()
    startobject!(b, 2)
    prependslot!(b, 0, 0x3456, 0)
    prependslot!(b, 1, 0x789A, 0)
    endobject!(b)
    check(UInt8[
        8, 0, #   vtable bytes
        8, 0, #   end of object from here
        6, 0, #   offset to value 0
        4, 0, #   offset to value 1
        8, 0, 0, 0, #   offset for start of vtable (int32)
        0x9A, 0x78, #   value 1
        0x56, 0x34, #   value 0
    ])

    #   test 12: vtable with int16 and bool
    b = FlatBuffers.Builder()
    startobject!(b, 2)
    prependslot!(b, 0, 0x3456, 0)
    prependslot!(b, 1, true, false)
    endobject!(b)
    check(UInt8[
        8, 0, #   vtable bytes
        8, 0, #   end of object from here
        6, 0, #   offset to value 0
        5, 0, #   offset to value 1
        8, 0, 0, 0, #   offset for start of vtable (int32)
        0,          #   padding
        1,          #   value 1
        0x56, 0x34, #   value 0
    ])

    #   test 12: vtable with empty vector
    b = FlatBuffers.Builder()
    startvector!(b, sizeof(UInt8), 0, 1)
    vecend = endvector!(b, 0)
    startobject!(b, 1)
    prependslot!(b, 0, vecend, 0)
    endobject!(b)
    check(UInt8[
        6, 0, #   vtable bytes
        8, 0,
        4, 0, #   offset to vector offset
        6, 0, 0, 0, #   offset for start of vtable (int32)
        4, 0, 0, 0,
        0, 0, 0, 0, #   length of vector (not in struct)
    ])

    #   test 12b: vtable with empty vector of byte and some scalars
    b = FlatBuffers.Builder()
    startvector!(b, sizeof(UInt8), 0, 1)
    vecend = endvector!(b, 0)
    startobject!(b, 2)
    prependslot!(b, 0, 55, 0)
    prependslot!(b, 1, vecend, 0)
    endobject!(b)
    check(UInt8[
        8, 0, #   vtable bytes
        12, 0,
        10, 0, #   offset to value 0
        4, 0, #   offset to vector offset
        8, 0, 0, 0, #   vtable loc
        8, 0, 0, 0, #   value 1
        0, 0, 55, 0, #   value 0

        0, 0, 0, 0, #   length of vector (not in struct)
    ])

    #   test 13: vtable with 1 int16 and 2-vector of int16
    b = FlatBuffers.Builder()
    startvector!(b, sizeof(Int16), 2, 1)
    prepend!(b, 0x1234)
    prepend!(b, 0x5678)
    vecend = endvector!(b, 2)
    startobject!(b, 2)
    prependslot!(b, 1, vecend, 0)
    prependslot!(b, 0, 55, 0)
    endobject!(b)
    check(UInt8[
        8, 0, #   vtable bytes
        12, 0, #   length of object
        6, 0, #   start of value 0 from end of vtable
        8, 0, #   start of value 1 from end of buffer
        8, 0, 0, 0, #   offset for start of vtable (int32)
        0, 0, #   padding
        55, 0, #   value 0
        4, 0, 0, 0, #   vector position from here
        2, 0, 0, 0, #   length of vector (UInt32)
        0x78, 0x56, #   vector value 1
        0x34, 0x12, #   vector value 0
    ])

    #   test 14: vtable with 1 struct of 1 int8, 1 int16, 1 int32
    b = FlatBuffers.Builder()
    startobject!(b, 1)
    prep!(b, 4+4+4, 0)
    prepend!(b, 55)
    pad!(b, 3)
    prepend!(b, 0x1234)
    pad!(b, 2)
    prepend!(b, 0x12345678)
    structStart = offset(b)
    prependstructslot!(b, 0, structStart, 0)
    endobject!(b)
    check(UInt8[
        6, 0, #   vtable bytes
        16, 0, #   end of object from here
        4, 0, #   start of struct from here
        6, 0, 0, 0, #   offset for start of vtable (int32)
        0x78, 0x56, 0x34, 0x12, #   value 2
        0, 0, #   padding
        0x34, 0x12, #   value 1
        0, 0, 0, #   padding
        55, #   value 0
    ])

    #   test 15: vtable with 1 vector of 2 struct of 2 int8
    b = FlatBuffers.Builder()
    startvector!(b, sizeof(Int8) * 2, 2, 1)
    prepend!(b, 33)
    prepend!(b, 44)
    prepend!(b, 55)
    prepend!(b, 66)
    vecend = endvector!(b, 2)
    startobject!(b, 1)
    prependslot!(b, 0, vecend, 0)
    endobject!(b)
    check(UInt8[
        6, 0, #   vtable bytes
        8, 0,
        4, 0, #   offset of vector offset
        6, 0, 0, 0, #   offset for start of vtable (int32)
        4, 0, 0, 0, #   vector start offset

        2, 0, 0, 0, #   vector length
        66, #   vector value 1,1
        55, #   vector value 1,0
        44, #   vector value 0,1
        33, #   vector value 0,0
    ])

    #   test 16: table with some elements
    b = FlatBuffers.Builder()
    startobject!(b, 2)
    prependslot!(b, 0, 33, 0)
    prependslot!(b, 1, 66, 0)
    off = endobject!(b)
    finish!(b, off)

    check(UInt8[
        12, 0, 0, 0, #   root of table: points to vtable offset

        8, 0, #   vtable bytes
        8, 0, #   end of object from here
        7, 0, #   start of value 0
        4, 0, #   start of value 1

        8, 0, 0, 0, #   offset for start of vtable (int32)

        66, 0, #   value 1
        0,  #   padding
        33, #   value 0
    ])

    #   test 17: one unfinished table and one finished table
    b = FlatBuffers.Builder()
    startobject!(b, 2)
    prependslot!(b, 0, 33, 0)
    prependslot!(b, 1, 44, 0)
    off = endobject!(b)
    finish!(b, off)

    startobject!(b, 3)
    prependslot!(b, 0, 55, 0)
    prependslot!(b, 1, 66, 0)
    prependslot!(b, 2, 77, 0)
    off = endobject!(b)
    finish!(b, off)

    check(UInt8[
        16, 0, 0, 0, #   root of table: points to object
        0, 0, #   padding

        10, 0, #   vtable bytes
        8, 0, #   size of object
        7, 0, #   start of value 0
        6, 0, #   start of value 1
        5, 0, #   start of value 2
        10, 0, 0, 0, #   offset for start of vtable (int32)
        0,  #   padding
        77, #   value 2
        66, #   value 1
        55, #   value 0

        12, 0, 0, 0, #   root of table: points to object

        8, 0, #   vtable bytes
        8, 0, #   size of object
        7, 0, #   start of value 0
        6, 0, #   start of value 1
        8, 0, 0, 0, #   offset for start of vtable (int32)
        0, 0, #   padding
        44, #   value 1
        33, #   value 0
    ])

    #   test 18: a bunch of bools
    b = FlatBuffers.Builder()
    startobject!(b, 8)
    prependslot!(b, 0, true, false)
    prependslot!(b, 1, true, false)
    prependslot!(b, 2, true, false)
    prependslot!(b, 3, true, false)
    prependslot!(b, 4, true, false)
    prependslot!(b, 5, true, false)
    prependslot!(b, 6, true, false)
    prependslot!(b, 7, true, false)
    off = endobject!(b)
    finish!(b, off)

    check(UInt8[
        24, 0, 0, 0, #   root of table: points to vtable offset

        20, 0, #   vtable bytes
        12, 0, #   size of object
        11, 0, #   start of value 0
        10, 0, #   start of value 1
        9, 0, #   start of value 2
        8, 0, #   start of value 3
        7, 0, #   start of value 4
        6, 0, #   start of value 5
        5, 0, #   start of value 6
        4, 0, #   start of value 7
        20, 0, 0, 0, #   vtable offset

        1, #   value 7
        1, #   value 6
        1, #   value 5
        1, #   value 4
        1, #   value 3
        1, #   value 2
        1, #   value 1
        1, #   value 0
    ])

    #   test 19: three bools
    b = FlatBuffers.Builder()
    startobject!(b, 3)
    prependslot!(b, 0, true, false)
    prependslot!(b, 1, true, false)
    prependslot!(b, 2, true, false)
    off = endobject!(b)
    finish!(b, off)

    check(UInt8[
        16, 0, 0, 0, #   root of table: points to vtable offset

        0, 0, #   padding

        10, 0, #   vtable bytes
        8, 0, #   size of object
        7, 0, #   start of value 0
        6, 0, #   start of value 1
        5, 0, #   start of value 2
        10, 0, 0, 0, #   vtable offset from here

        0, #   padding
        1, #   value 2
        1, #   value 1
        1, #   value 0
    ])

    #   test 20: some floats
    b = FlatBuffers.Builder()
    startobject!(b, 1)
    prependslot!(b, 0, 1.0, 0.0)
    off = endobject!(b)

    check(UInt8[
        6, 0, #   vtable bytes
        8, 0, #   size of object
        4, 0, #   start of value 0
        6, 0, 0, 0, #   vtable offset

        0, 0, 128, 63, #   value 0
    ])
end

#   CheckManualBuild builds a Monster manually.
function CheckManualBuild()
    b = FlatBuffers.Builder()
    str = createstring!(b, "MyMonster")

    startvector!(b, 1, 5, 1)
    prepend!(b, UInt8(4))
    prepend!(b, UInt8(3))
    prepend!(b, UInt8(2))
    prepend!(b, UInt8(1))
    prepend!(b, UInt8(0))
    inv = endvector!(b, 5)

    startobject!(b, 13)
    prependslot!(b, 2, 20, 100)
    mon2 = endobject!(b)

    #   Test4Vector
    startvector!(b, 4, 2, 1)

    #   Test 0
    prep!(b, 2, 4)
    pad!(b, 1)
    place!(b, Int8(20))
    place!(b, Int16(10)

    #   Test 1
    prep!(b, 2, 4)
    pad!(b, 1)
    place!(b, Int8(40))
    place!(b, Int16(30)

    #   end testvector
    test4 = endvector!(b, 2)

    startobject!(b, 13)

    #   a vec3
    prep!(b, 16, 32)
    pad!(b, 2)
    prep!(b, 2, 4)
    pad!(b, 1)
    place!(b, UInt8(6))
    place!(b, Int16(5)
    pad!(b, 1)
    place!(b, UInt8(4))
    place!(b, 3.0)
    pad!(b, 4)
    place!(b, Float32(3.0))
    place!(b, Float32(2.0))
    place!(b, Float32(1.0))
    vec3Loc = offset(b)
    #   end vec3

    prependstructslot!(b, 0, vec3Loc, 0) #   vec3. noop
    prependslot!(b, 2, 80, 100)     #   hp
    prependslot!(b, 3, str, 0)
    prependslot!(b, 5, inv, 0) #   inventory
    prependslot!(b, 7, UInt8(1), 0)
    prependslot!(b, 8, mon2, 0)
    prependslot!(b, 9, test4, 0)
    mon = endobject!(b)

    finish!(b, mon)

    return b.bytes, b.head
end

function CheckGetRootAsForNonRootTable()
    b = FlatBuffers.Builder()
    str = createstring!(b, "MyStat")
    Example.StatStart(b)
    Example.StatAddId(b, str)
    Example.StatAddVal(b, 12345678)
    Example.StatAddCount(b, 12345)
    stat_end = Example.StatEnd(b)
    finish!(b, stat_end)

    stat = Example.GetRootAsStat(b.bytes, b.head)

    @test stat.id == "MyStat"
    @test stat.val == 12345678
    @test stat.count == 12345
end

#   CheckGeneratedBuild uses generated code to build the example Monster.
function CheckGeneratedBuild()
    #TODO
    b = FlatBuffers.Builder()
    str = createstring!(b, "MyMonster")
    test1 = createstring!(b, "test1")
    test2 = createstring!(b, "test2")
    fred = createstring!(b, "Fred")

    Example.MonsterStartInventoryVector(b, 5)
    prepend!(b, UInt8(4))
    prepend!(b, UInt8(3))
    prepend!(b, UInt8(2))
    prepend!(b, UInt8(1))
    prepend!(b, UInt8(0))
    inv = endvector!(b, 5)

    Example.MonsterStart(b)
    Example.MonsterAddName(b, fred)
    mon2 = Example.MonsterEnd(b)

    Example.MonsterStartTest4Vector(b, 2)
    Example.CreateTest(b, 10, 20)
    Example.CreateTest(b, 30, 40)
    test4 = endvector!(b, 2)

    Example.MonsterStartTestarrayofstringVector(b, 2)
    prepend!(b, FlatBuffers.UOffsetT(test2))
    prepend!(b, FlatBuffers.UOffsetT(test1))
    testArrayOfString = endvector!(b, 2)

    Example.MonsterStart(b)

    pos = Example.CreateVec3(b, 1.0, 2.0, 3.0, 3.0, Example.ColorGreen, 5, 6)
    Example.MonsterAddPos(b, pos)

    Example.MonsterAddHp(b, 80)
    Example.MonsterAddName(b, str)
    Example.MonsterAddTestbool(b, true)
    Example.MonsterAddInventory(b, inv)
    Example.MonsterAddTestType(b, 1)
    Example.MonsterAddTest(b, mon2)
    Example.MonsterAddTest4(b, test4)
    Example.MonsterAddTestarrayofstring(b, testArrayOfString)
    mon = Example.MonsterEnd(b)

    finish!(b, mon)

    return b.bytes, b.head
end

#   CheckTableAccessors checks that the table accessors work as expected.
function CheckTableAccessors()
    #   test struct accessor
    b = FlatBuffers.Builder()
    pos = Example.CreateVec3(b, 1.0, 2.0, 3.0, 3.0, 4, 5, 6)
    finish!(b, pos)
    vec3Bytes = finishedbytes(b)
    vec3 = Example.Vec3()
    FlatBuffers.GetRootAs(vec3Bytes, 0, vec3)

    @test vec3Bytes == table(vec3).bytes
    
    #   test table accessor
    b = FlatBuffers.Builder()
    str = createstring!(b, "MyStat")
    Example.StatStart(b)
    Example.StatAddId(b, str)
    Example.StatAddVal(b, 12345678)
    Example.StatAddCount(b, 12345)
    pos = Example.StatEnd(b)
    finish!(b, pos)
    statBytes = finishedbytes(b)
    stat = Example.Stat()
    FlatBuffers.GetRootAs(statBytes, 0, stat)

    @test statBytes == table(stat).bytes
end

#   CheckVtableDeduplication verifies that vtables are deduplicated.
function CheckVtableDeduplication()
    b = FlatBuffers.Builder()

    startobject!(b, 4)
    prependslot!(b, 0, UInt8(0), 0)
    prependslot!(b, 1, UInt8(11), 0)
    prependslot!(b, 2, UInt8(22), 0)
    prependslot!(b, 3, Int16(33), 0)
    obj0 = endobject!(b)

    startobject!(b, 4)
    prependslot!(b, 0, UInt8(0), 0)
    prependslot!(b, 1, UInt8(44), 0)
    prependslot!(b, 2, UInt8(55), 0)
    prependslot!(b, 3, Int16(66), 0)
    obj1 = endobject!(b)

    startobject!(b, 4)
    prependslot!(b, 0, UInt8(0), 0)
    prependslot!(b, 1, UInt8(77), 0)
    prependslot!(b, 2, UInt8(88), 0)
    prependslot!(b, 3, Int16(99), 0)
    obj2 = endobject!(b)

    got = b.bytes[b.head:end]

    want = UInt8[
        240, 255, 255, 255, #   == -12. offset to dedupped vtable.
        99, 0,
        88,
        77,
        248, 255, 255, 255, #   == -8. offset to dedupped vtable.
        66, 0,
        55,
        44,
        12, 0,
        8, 0,
        0, 0,
        7, 0,
        6, 0,
        4, 0,
        12, 0, 0, 0,
        33, 0,
        22,
        11,
    ]

    @test want == got
    
    table0 = FlatBuffers.Table(b.bytes, FlatBuffers.UOffsetT(length(b.bytes)) - obj0)
    table1 = FlatBuffers.Table(b.bytes, FlatBuffers.UOffsetT(length(b.bytes)) - obj1)
    table2 = FlatBuffers.Table(b.bytes, FlatBuffers.UOffsetT(length(b.bytes)) - obj2)

    testTable = function(tab, a, b, c, d)
        #   vtable size
        @test got == 
        if got = tab.GetVOffsetTSlot(0, 0); 12 != got
            fail("failed 0, 0: %d", got)
        end
        #   object size
        if got = tab.GetVOffsetTSlot(2, 0); 8 != got
            fail("failed 2, 0: %d", got)
        end
        #   default value
        if got = tab.GetVOffsetTSlot(4, 0); a != got
            fail("failed 4, 0: %d", got)
        end
        if got = tab.GetByteSlot(6, 0); b != got
            fail("failed 6, 0: %d", got)
        end
        if val = tab.GetByteSlot(8, 0); c != val
            fail("failed 8, 0: %d", got)
        end
        if got = tab.GetByteSlot(10, 0); d != got
            fail("failed 10, 0: %d", got)
        end
    end

    testTable(table0, 0, 11, 22, 33)
    testTable(table1, 0, 44, 55, 66)
    testTable(table2, 0, 77, 88, 99)
end

#   CheckNotInObjectError verifies that `EndObject` fails if not inside an
#   object.
function CheckNotInObjectError(fail)
    b = FlatBuffers.Builder()

    defer func()
        r = recover()
        if r == nil
            fail("expected panic in CheckNotInObjectError")
        end
    end()
    endobject!(b)
end

#   CheckStringIsNestedError verifies that a string can not be created inside
#   another object.
function CheckStringIsNestedError(fail)
    b = FlatBuffers.Builder()
    startobject!(b, 0)
    defer func()
        r = recover()
        if r == nil
            fail("expected panic in CheckStringIsNestedError")
        end
    end()
    createstring!(b, "foo")
end

function CheckSharedStrings(fail)
    f = func(strings []string) bool {
        b = FlatBuffers.Builder()
        for _, s1 = range strings {
            for _, s2 = range strings {
                off1 = b.CreateSharedString(s1)
                off2 = b.CreateSharedString(s2)

                if (s1 == s2) && (off1 != off2)
                    return false
                end
                if (s1 != s2) && (off1 == off2)
                    return false
                end
            end
        end
        return true
    end
    if err = quick.Check(f, nil); err != nil
        fail("expected same offset")
    end
end

#   CheckByteStringIsNestedError verifies that a bytestring can not be created
#   inside another object.
function CheckByteStringIsNestedError(fail)
    b = FlatBuffers.Builder()
    startobject!(b, 0)
    defer func()
        r = recover()
        if r == nil
            fail("expected panic in CheckByteStringIsNestedError")
        end
    end()
    createstring!(b, []byte("foo"))
end

#   CheckStructIsNotInlineError verifies that writing a struct in a location
#   away from where it is used will cause a panic.
function CheckStructIsNotInlineError(fail)
    b = FlatBuffers.Builder()
    startobject!(b, 0)
    defer func()
        r = recover()
        if r == nil
            fail("expected panic in CheckStructIsNotInlineError")
        end
    end()
    prependstructslot!(b, 0, 1, 0)
end

#   CheckFinishedBytesError verifies that `FinishedBytes` panics if the table
#   is not finished.
function CheckFinishedBytesError(fail)
    b = FlatBuffers.Builder()

    defer func()
        r = recover()
        if r == nil
            fail("expected panic in CheckFinishedBytesError")
        end
    end()
    finishedbytes(b)
end

#   CheckEnumNames checks that the generated enum names are correct.
function CheckEnumNames(fail)
    {
        want = map[Example.Any]string{
            Example.AnyNONE:                    "NONE",
            Example.AnyMonster:                 "Monster",
            Example.AnyTestSimpleTableWithEnum: "TestSimpleTableWithEnum",
            Example.AnyMyGame_Example2_Monster: "MyGame_Example2_Monster",
        end
        got = Example.EnumNamesAny
        if !reflect.DeepEqual(got, want)
            fail("enum name is not equal")
        end
    end
    {
        want = map[Example.Color]string{
            Example.ColorRed:   "Red",
            Example.ColorGreen: "Green",
            Example.ColorBlue:  "Blue",
        end
        got = Example.EnumNamesColor
        if !reflect.DeepEqual(got, want)
            fail("enum name is not equal")
        end
    end
end

#   CheckEnumString checks the String method on generated enum types.
function CheckEnumString(fail)
    if got = Example.AnyMonster.String(); got != "Monster"
        fail("Monster.String: %q != %q", got, "Monster")
    end
    if got = fmt.Sprintf("color: %s", Example.ColorGreen); got != "color: Green"
        fail("color.String: %q != %q", got, "color: Green")
    end
end

#   CheckEnumValues checks that the generated enum values maps are correct.
function CheckEnumValues(fail)
    {
        want = map[string]Example.Any{
            "NONE":                    Example.AnyNONE,
            "Monster":                 Example.AnyMonster,
            "TestSimpleTableWithEnum": Example.AnyTestSimpleTableWithEnum,
            "MyGame_Example2_Monster": Example.AnyMyGame_Example2_Monster,
        end
        got = Example.EnumValuesAny
        if !reflect.DeepEqual(got, want)
            fail("enum name is not equal")
        end
    end
    {
        want = map[string]Example.Color{
            "Red":   Example.ColorRed,
            "Green": Example.ColorGreen,
            "Blue":  Example.ColorBlue,
        end
        got = Example.EnumValuesColor
        if !reflect.DeepEqual(got, want)
            fail("enum name is not equal")
        end
    end
end

#   CheckDocExample checks that the code given in FlatBuffers documentation
#   is syntactically correct.
function CheckDocExample(buf, off)
    monster = Example.GetRootAsMonster(buf, off)
    _ = monster.Hp()
    _ = monster.Pos(nil)
    for i = 0; i < monster.InventoryLength(); i++ {
        _ = monster.Inventory(i) #   do something here
    end

    builder = FlatBuffers.Builder()

    Example.MonsterStartInventoryVector(builder, 5)
    for i = 4; i >= 0; i-- {
        prepend!(b, UInt8(byte)(i))
    end
    inv = builder.EndVector(5)

    str = builder.CreateString("MyMonster")
    Example.MonsterStart(builder)
    Example.MonsterAddPos(builder, Example.CreateVec3(builder, 1.0, 2.0, 3.0, 3.0, Example.Color(4), 5, 6))
    Example.MonsterAddHp(builder, 80)
    Example.MonsterAddName(builder, str)
    Example.MonsterAddInventory(builder, inv)
    Example.MonsterAddTestType(builder, 1)
    Example.MonsterAddColor(builder, Example.ColorRed)
    #   Example.MonsterAddTest(builder, mon2)
    #   Example.MonsterAddTest4(builder, test4s)
    _ = Example.MonsterEnd(builder)
end

function CheckCreateByteVector(fail)
    raw = [30]byte{end
    for i = 0; i < len(raw); i++ {
        raw[i] = byte(i)
    end

    for size = 0; size < len(raw); size++ {
        b1 = FlatBuffers.Builder()
        b2 = FlatBuffers.Builder()
        b1.startvector!(1, size, 1)
        for i = size - 1; i >= 0; i-- {
            prepend!(b, UInt8(raw)[i])
        end
        b1.EndVector(size)
        b2.CreateByteVector(raw[:size])
        CheckByteEquality(b1.Bytes, b2.Bytes)
    end
end

function CheckParentNamespace(fail)
    var empty, nonempty []byte

    #   create monster with an empty parent namespace field
    {
        builder = FlatBuffers.Builder()

        Example.MonsterStart(builder)
        m = Example.MonsterEnd(builder)
        finish!(builder, m)

        empty = make([]byte, len(finishedbytes(builder)))
        copy(empty, finishedbytes(builder))
    end

    #   create monster with a non-empty parent namespace field
    {
        builder = FlatBuffers.Builder()
        mygame.InParentNamespaceStart(builder)
        pn = mygame.InParentNamespaceEnd(builder)

        Example.MonsterStart(builder)
        Example.MonsterAddParentNamespaceTest(builder, pn)
        m = Example.MonsterEnd(builder)

        finish!(builder, m)

        nonempty = make([]byte, len(finishedbytes(builder)))
        copy(nonempty, finishedbytes(builder))
    end

    #   read monster with empty parent namespace field
    {
        m = Example.GetRootAsMonster(empty, 0)
        if m.ParentNamespaceTest(nil) != nil
            fail("expected nil ParentNamespaceTest for empty field")
        end
    end

    #   read monster with non-empty parent namespace field
    {
        m = Example.GetRootAsMonster(nonempty, 0)
        if m.ParentNamespaceTest(nil) == nil
            fail("expected non-nil ParentNamespaceTest for non-empty field")
        end
    end
end

#   Include simple random number generator to ensure results will be the
#   same cross platform.
#   http://en.wikipedia.org/wiki/Park%E2%80%93Miller_random_number_generator
type LCG UInt32

const InitialLCGSeed = 48271

function NewLCG() *LCG {
    n = UInt32(InitialLCGSeed)
    l = LCG(n)
    return &l
end

function (lcg *LCG) Reset()
    *lcg = InitialLCGSeed
end

function (lcg *LCG) Next() UInt32 {
    n = UInt32((uint64(*lcg) * uint64(279470273)) % uint64(4294967291))
    *lcg = LCG(n)
    return n
end

#   CheckByteEquality verifies that two byte buffers are the same.
function CheckByteEquality(a, b)
    if !bytes.Equal(a, b)
        fail("objects are not byte-wise equal")
    end
end

#   CheckMutateMethods checks all mutate methods one by one
function CheckMutateMethods(fail)
    b = FlatBuffers.Builder()
    startobject!(b, 15)
    prependslot!(b, 0, true, false)
    prependslot!(b, 1, UInt8(1), 0)
    prependslot!(b, 2, 2, 0)
    prependslot!(b, 3, 3, 0)
    prependslot!(b, 4, 4, 0)
    prependslot!(b, 5, 5, 0)
    prependslot!(b, 6, 6, 0)
    prependslot!(b, 7, 7, 0)
    prependslot!(b, 8, 8, 0)
    prependslot!(b, 9, 9, 0)
    prependslot!(b, 10, 10, 0)
    prependslot!(b, 11, 11, 0)

    prependslot!(b, 12, 12, 0)
    uoVal = offset(b) - 12

    b.PrependVOffsetT(13)
    b.Slot(13)

    b.PrependSOffsetT(14)
    b.Slot(14)
    soVal = FlatBuffers.SOffsetT(offset(b) - 14)

    offset = endobject!(b)

    t = FlatBuffers.Table(
        Bytes: b.bytes,
        Pos:   FlatBuffers.UOffsetT(len(b.bytes)) - offset,
    end

    calcVOffsetT = func(slot int) (vtableOffset FlatBuffers.VOffsetT)
        return FlatBuffers.VOffsetT((FlatBuffers.VtableMetadataFields + slot) * FlatBuffers.SizeVOffsetT)
    end
    calcUOffsetT = func(vtableOffset FlatBuffers.VOffsetT) (valueOffset FlatBuffers.UOffsetT)
        return t.Pos + FlatBuffers.UOffsetT(t.Offset(vtableOffset))
    end

    type testcase struct {
        field  string
        testfn func() bool
    end

    testForOriginalValues = []testcase{
        testcase{"BoolSlot", func() bool { return t.getslot(calcVOffsetT(0), true) == true }end,
        testcase{"ByteSlot", func() bool { return t.GetByteSlot(calcVOffsetT(1), 1) == 1 }end,
        testcase{"Uint8Slot", func() bool { return t.getslot(calcVOffsetT(2), 2) == 2 }end,
        testcase{"Uint16Slot", func() bool { return t.getslot(calcVOffsetT(3), 3) == 3 }end,
        testcase{"Uint32Slot", func() bool { return t.getslot(calcVOffsetT(4), 4) == 4 }end,
        testcase{"Uint64Slot", func() bool { return t.getslot(calcVOffsetT(5), 5) == 5 }end,
        testcase{"Int8Slot", func() bool { return t.getslot(calcVOffsetT(6), 6) == 6 }end,
        testcase{"Int16Slot", func() bool { return t.getslot(calcVOffsetT(7), 7) == 7 }end,
        testcase{"Int32Slot", func() bool { return t.getslot(calcVOffsetT(8), 8) == 8 }end,
        testcase{"Int64Slot", func() bool { return t.getslot(calcVOffsetT(9), 9) == 9 }end,
        testcase{"Float32Slot", func() bool { return t.getslot(calcVOffsetT(10), 10) == 10 }end,
        testcase{"Float64Slot", func() bool { return t.getslot(calcVOffsetT(11), 11) == 11 }end,
        testcase{"UOffsetTSlot", func() bool { return t.GetUOffsetT(calcUOffsetT(calcVOffsetT(12))) == uoVal }end,
        testcase{"VOffsetTSlot", func() bool { return t.GetVOffsetT(calcUOffsetT(calcVOffsetT(13))) == 13 }end,
        testcase{"SOffsetTSlot", func() bool { return t.GetSOffsetT(calcUOffsetT(calcVOffsetT(14))) == soVal }end,
    end

    testMutability = []testcase{
        testcase{"BoolSlot", func() bool { return t.MutateBoolSlot(calcVOffsetT(0), false) }end,
        testcase{"ByteSlot", func() bool { return t.MutateByteSlot(calcVOffsetT(1), 2) }end,
        testcase{"Uint8Slot", func() bool { return t.MutateUint8Slot(calcVOffsetT(2), 4) }end,
        testcase{"Uint16Slot", func() bool { return t.MutateUint16Slot(calcVOffsetT(3), 6) }end,
        testcase{"Uint32Slot", func() bool { return t.MutateUint32Slot(calcVOffsetT(4), 8) }end,
        testcase{"Uint64Slot", func() bool { return t.MutateUint64Slot(calcVOffsetT(5), 10) }end,
        testcase{"Int8Slot", func() bool { return t.MutateInt8Slot(calcVOffsetT(6), 12) }end,
        testcase{"Int16Slot", func() bool { return t.MutateInt16Slot(calcVOffsetT(7), 14) }end,
        testcase{"Int32Slot", func() bool { return t.MutateInt32Slot(calcVOffsetT(8), 16) }end,
        testcase{"Int64Slot", func() bool { return t.MutateInt64Slot(calcVOffsetT(9), 18) }end,
        testcase{"Float32Slot", func() bool { return t.MutateFloat32Slot(calcVOffsetT(10), 20) }end,
        testcase{"Float64Slot", func() bool { return t.MutateFloat64Slot(calcVOffsetT(11), 22) }end,
        testcase{"UOffsetTSlot", func() bool { return t.MutateUOffsetT(calcUOffsetT(calcVOffsetT(12)), 24) }end,
        testcase{"VOffsetTSlot", func() bool { return t.MutateVOffsetT(calcUOffsetT(calcVOffsetT(13)), 26) }end,
        testcase{"SOffsetTSlot", func() bool { return t.MutateSOffsetT(calcUOffsetT(calcVOffsetT(14)), 28) }end,
    end

    testMutabilityWithoutSlot = []testcase{
        testcase{"BoolSlot", func() bool { return t.MutateBoolSlot(calcVOffsetT(16), false) }end,
        testcase{"ByteSlot", func() bool { return t.MutateByteSlot(calcVOffsetT(16), 2) }end,
        testcase{"Uint8Slot", func() bool { return t.MutateUint8Slot(calcVOffsetT(16), 2) }end,
        testcase{"Uint16Slot", func() bool { return t.MutateUint16Slot(calcVOffsetT(16), 2) }end,
        testcase{"Uint32Slot", func() bool { return t.MutateUint32Slot(calcVOffsetT(16), 2) }end,
        testcase{"Uint64Slot", func() bool { return t.MutateUint64Slot(calcVOffsetT(16), 2) }end,
        testcase{"Int8Slot", func() bool { return t.MutateInt8Slot(calcVOffsetT(16), 2) }end,
        testcase{"Int16Slot", func() bool { return t.MutateInt16Slot(calcVOffsetT(16), 2) }end,
        testcase{"Int32Slot", func() bool { return t.MutateInt32Slot(calcVOffsetT(16), 2) }end,
        testcase{"Int64Slot", func() bool { return t.MutateInt64Slot(calcVOffsetT(16), 2) }end,
        testcase{"Float32Slot", func() bool { return t.MutateFloat32Slot(calcVOffsetT(16), 2) }end,
        testcase{"Float64Slot", func() bool { return t.MutateFloat64Slot(calcVOffsetT(16), 2) }end,
    end

    testForMutatedValues = []testcase{
        testcase{"BoolSlot", func() bool { return t.getslot(calcVOffsetT(0), true) == false }end,
        testcase{"ByteSlot", func() bool { return t.GetByteSlot(calcVOffsetT(1), 1) == 2 }end,
        testcase{"Uint8Slot", func() bool { return t.getslot(calcVOffsetT(2), 1) == 4 }end,
        testcase{"Uint16Slot", func() bool { return t.getslot(calcVOffsetT(3), 1) == 6 }end,
        testcase{"Uint32Slot", func() bool { return t.getslot(calcVOffsetT(4), 1) == 8 }end,
        testcase{"Uint64Slot", func() bool { return t.getslot(calcVOffsetT(5), 1) == 10 }end,
        testcase{"Int8Slot", func() bool { return t.getslot(calcVOffsetT(6), 1) == 12 }end,
        testcase{"Int16Slot", func() bool { return t.getslot(calcVOffsetT(7), 1) == 14 }end,
        testcase{"Int32Slot", func() bool { return t.getslot(calcVOffsetT(8), 1) == 16 }end,
        testcase{"Int64Slot", func() bool { return t.getslot(calcVOffsetT(9), 1) == 18 }end,
        testcase{"Float32Slot", func() bool { return t.getslot(calcVOffsetT(10), 1) == 20 }end,
        testcase{"Float64Slot", func() bool { return t.getslot(calcVOffsetT(11), 1) == 22 }end,
        testcase{"UOffsetTSlot", func() bool { return t.GetUOffsetT(calcUOffsetT(calcVOffsetT(12))) == 24 }end,
        testcase{"VOffsetTSlot", func() bool { return t.GetVOffsetT(calcUOffsetT(calcVOffsetT(13))) == 26 }end,
        testcase{"SOffsetTSlot", func() bool { return t.GetSOffsetT(calcUOffsetT(calcVOffsetT(14))) == 28 }end,
    end

    #   make sure original values are okay
    for _, t = range testForOriginalValues {
        if !t.testfn()
            fail(t.field + "' field doesn't have the expected original value")
        end
    end

    #   try to mutate fields and check mutability
    for _, t = range testMutability {
        if !t.testfn()
            error(string(t.field+"' field failed mutability test", "passed", "failed"))
        end
    end

    #   try to mutate fields and check mutability
    #   these have wrong slots so should fail
    for _, t = range testMutabilityWithoutSlot {
        if t.testfn()
            error(string(t.field+"' field failed no slot mutability test", "failed", "passed"))
        end
    end

    #   test whether values have changed
    for _, t = range testForMutatedValues {
        if !t.testfn()
            fail(t.field + "' field doesn't have the expected mutated value")
        end
    end
end

#   BenchmarkVtableDeduplication measures the speed of vtable deduplication
#   by creating prePop vtables, then populating b.N objects with a
#   different single vtable.
//
#   When b.N is large (as in long benchmarks), memory usage may be high.
function BenchmarkVtableDeduplication(b *testing.B)
    prePop = 10
    builder = FlatBuffers.Builder()

    #   pre-populate some vtables:
    for i = 0; i < prePop; i++ {
        builder.startobject!(i)
        for j = 0; j < i; j++ {
            prependslot!(builder, j, int16(j), 0)
        end
        builder.EndObject()
    end

    #   benchmark deduplication of a new vtable:
    b.ResetTimer()
    for i = 0; i < b.N; i++ {
        lim = prePop

        builder.startobject!(lim)
        for j = 0; j < lim; j++ {
            prependslot!(builder, j, int16(j), 0)
        end
        builder.EndObject()
    end
end

#   BenchmarkParseGold measures the speed of parsing the 'gold' data
#   used throughout this test suite.
function BenchmarkParseGold(b *testing.B)
    buf, offset = CheckGeneratedBuild(b.Fatalf)
    monster = Example.GetRootAsMonster(buf, offset)

    #   use these to prevent allocations:
    reuse_pos = Example.Vec3{end
    reuse_test3 = Example.Test{end
    reuse_table2 = FlatBuffers.Table{end
    reuse_monster2 = Example.Monster{end
    reuse_test4_0 = Example.Test{end
    reuse_test4_1 = Example.Test{end

    b.SetBytes(int64(len(buf[offset:])))
    b.ReportAllocs()
    b.ResetTimer()
    for i = 0; i < b.N; i++ {
        monster.Hp()
        monster.Mana()
        name = monster.Name()
        _ = name[0]
        _ = name[len(name)-1]

        monster.Pos(&reuse_pos)
        reuse_pos.X()
        reuse_pos.Y()
        reuse_pos.Z()
        reuse_pos.Test1()
        reuse_pos.Test2()
        reuse_pos.Test3(&reuse_test3)
        reuse_test3.A()
        reuse_test3.B()
        monster.TestType()
        monster.Test(&reuse_table2)
        reuse_monster2.Init(reuse_table2.Bytes, reuse_table2.Pos)
        name2 = reuse_monster2.Name()
        _ = name2[0]
        _ = name2[len(name2)-1]
        monster.InventoryLength()
        l = monster.InventoryLength()
        for i = 0; i < l; i++ {
            monster.Inventory(i)
        end
        monster.Test4Length()
        monster.Test4(&reuse_test4_0, 0)
        monster.Test4(&reuse_test4_1, 1)

        reuse_test4_0.A()
        reuse_test4_0.B()
        reuse_test4_1.A()
        reuse_test4_1.B()

        monster.TestarrayofstringLength()
        str0 = monster.Testarrayofstring(0)
        _ = str0[0]
        _ = str0[len(str0)-1]
        str1 = monster.Testarrayofstring(1)
        _ = str1[0]
        _ = str1[len(str1)-1]
    end
end

#   BenchmarkBuildGold uses generated code to build the example Monster.
function BenchmarkBuildGold(b *testing.B)
    buf, offset = CheckGeneratedBuild(b.Fatalf)
    bytes_length = int64(len(buf[offset:]))

    reuse_str = "MyMonster"
    reuse_test1 = "test1"
    reuse_test2 = "test2"
    reuse_fred = "Fred"

    b.SetBytes(bytes_length)
    bldr = FlatBuffers.Builder()
    b.ResetTimer()
    b.ReportAllocs()
    for i = 0; i < b.N; i++ {
        bldr.Reset()

        str = bldr.CreateString(reuse_str)
        test1 = bldr.CreateString(reuse_test1)
        test2 = bldr.CreateString(reuse_test2)
        fred = bldr.CreateString(reuse_fred)

        Example.MonsterStartInventoryVector(bldr, 5)
        prepend!(b, UInt8(4))
        prepend!(b, UInt8(3))
        prepend!(b, UInt8(2))
        prepend!(b, UInt8(1))
        prepend!(b, UInt8(0))
        inv = bldr.EndVector(5)

        Example.MonsterStart(bldr)
        Example.MonsterAddName(bldr, fred)
        mon2 = Example.MonsterEnd(bldr)

        Example.MonsterStartTest4Vector(bldr, 2)
        Example.CreateTest(bldr, 10, 20)
        Example.CreateTest(bldr, 30, 40)
        test4 = bldr.EndVector(2)

        Example.MonsterStartTestarrayofstringVector(bldr, 2)
        bldr.PrependUOffsetT(test2)
        bldr.PrependUOffsetT(test1)
        testArrayOfString = bldr.EndVector(2)

        Example.MonsterStart(bldr)

        pos = Example.CreateVec3(bldr, 1.0, 2.0, 3.0, 3.0, Example.ColorGreen, 5, 6)
        Example.MonsterAddPos(bldr, pos)

        Example.MonsterAddHp(bldr, 80)
        Example.MonsterAddName(bldr, str)
        Example.MonsterAddInventory(bldr, inv)
        Example.MonsterAddTestType(bldr, 1)
        Example.MonsterAddTest(bldr, mon2)
        Example.MonsterAddTest4(bldr, test4)
        Example.MonsterAddTestarrayofstring(bldr, testArrayOfString)
        mon = Example.MonsterEnd(bldr)

        finish!(bldr, mon)
    end
end
