gen-testdata 5.15 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
#!/usr/bin/env python2
# -*- coding: utf-8 -*-
# Copyright (C) 2017  Nexedi SA and Contributors.
#                     Kirill Smelkov <kirr@nexedi.com>
#
# This program is free software: you can Use, Study, Modify and Redistribute
# it under the terms of the GNU General Public License version 3, or (at your
# option) any later version, as published by the Free Software Foundation.
#
# You can also Link and Combine this program with other software covered by
# the terms of any of the Free Software licenses or any of the Open Source
# Initiative approved licenses and Convey the resulting work. Corresponding
# source of such a combination shall include the source code for all other
# software used.
#
# This program is distributed WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#
# See COPYING file for full licensing terms.
# See https://www.nexedi.com/licensing for rationale and options.
"""generate reference fs1 database and index for tests"""

from ZODB.FileStorage import FileStorage
from zodbtools.test.gen_testdata import gen_testdb
from zodbtools.util import escapeqq

import struct

# convert numeric oid to/from str
def p64(num):
    return struct.pack('>Q', num)

def unpack64(packed):
    return struct.unpack('>Q', packed)[0]

def hex64(packed):
    return '0x%016x' % unpack64(packed)


def main():
    outfs = "testdata/1.fs"
    gen_testdb(outfs)

    # dump to go what to expect
    stor = FileStorage(outfs, read_only=True)
    with open("ztestdata_expect_test.go", "w") as f:
        def emit(v):
            print >>f, v
        emit("// Code generated by %s; DO NOT EDIT." % __file__)
        emit("package fs1\n")
51
        emit("import \"lab.nexedi.com/kirr/neo/go/zodb\"\n")
52 53 54 55 56 57 58 59

        # index
        emit("const _1fs_indexTopPos = %i" % stor._pos)
        emit("var _1fs_indexEntryv = [...]indexEntry{")
        for k, v in stor._index.iteritems():
            emit("\t{%8i, %8i}," % (unpack64(k), v))
        emit("}")

60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
        # database records
        emit("\nvar _1fs_dbEntryv = [...]dbEntry{")
        txnLenPrev = 0
        for txn in stor.iterator():     # txn is TransactionRecord
            # txn.extension is already depickled dict - we want to put raw data from file
            # also we need to access txn record length which is not provided by higher-level iterator
            # do deep-dive into FileStorage
            th = stor._read_txn_header(txn._tpos)
            assert th.tid == txn.tid
            assert th.tlen == txn._tend - txn._tpos

            # fs1/go keeps in RAM whole txn length, not len-8 as it is on disk
            txnLen = th.tlen + 8

            emit("\t{")

            # -> TxnHeader
            emit("\t\tTxnHeader{")
            emit("\t\t\tPos:\t  %i," % txn._tpos)
            emit("\t\t\tLenPrev:  %i," % txnLenPrev)
            emit("\t\t\tLen:\t  %i," % txnLen)
            emit("\t\t\tTxnInfo:\tzodb.TxnInfo{")
            emit("\t\t\t\tTid:\t%s," % hex64(txn.tid))
            emit("\t\t\t\tStatus:\t'%s'," % txn.status)
            emit("\t\t\t\tUser:\t\t[]byte(%s)," % escapeqq(txn.user))
            emit("\t\t\t\tDescription:\t[]byte(%s)," % escapeqq(txn.description))
            emit("\t\t\t\tExtension:\t[]byte(%s)," % escapeqq(th.ext))
            emit("\t\t\t},")
            emit("\t\t},")

            txnLenPrev = txnLen

            # -> DataHeader + payload
            emit("\n\t\t[]txnEntry{")

            for drec in txn:            # drec is itemof(TransactionRecordIterator) = Record
                # same as with txn - not everything is possible to get via
                # higher-level api
                dh = stor._read_data_header(drec.pos)
                assert dh.oid == drec.oid
                assert dh.tid == drec.tid
                assert dh.tloc == txn._tpos

                emit("\t\t\t{")
                emit("\t\t\t\tDataHeader{")
                emit("\t\t\t\t\tPos:\t%i," % drec.pos)
                emit("\t\t\t\t\tOid:\t%i," % unpack64(drec.oid))
                emit("\t\t\t\t\tTid:\t%s," % hex64(drec.tid))

                emit("\t\t\t\t\tPrevRevPos:\t%i," % dh.prev)
                emit("\t\t\t\t\tTxnPos:\t%i," % txn._tpos)
                assert drec.version == ''
                emit("\t\t\t\t\tDataLen:\t%i," % dh.plen)
                emit("\t\t\t\t},")
                plen = dh.plen
                if plen == 0:
                    rawdata = p64(dh.back) # back-pointer or 0 (= delete)
                    if drec.data is None:
                        data = "/* deleted */ nil"
                        datatid = "/* deleted */ 0"
                    else:
                        data = "[]byte(%s)" % escapeqq(drec.data)
                        datatid = "/* copy from */ " + hex64(drec.data_txn)
                else:
                    rawdata = drec.data
                    data = "/* same as ^^^ */ sameAsRaw"
                    datatid = "/* no copy */ 0"

                emit("\t\t\t\t[]byte(%s)," % escapeqq(rawdata))
                emit("\t\t\t\t%s," % data)
                emit("\t\t\t\t%s," % datatid)
                emit("\t\t\t},")

            emit("\t\t},")
            emit("\t},")
        emit("}")

137 138 139

if __name__ == '__main__':
    main()