~bigbes/tarantool

tarantool-protobuf

ref: 48ef418028fc02aaa2348f6ba34c988c4fd21100 tarantool-protobuf/test/unknown_test.lua -rw-r--r-- 5.4 KiB
48ef4180 — Eugene Blikh beads: close a6n + memory note on ffi.cast cdata allocation 2 months ago
                                                                                
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
51
52
53
54
55
56
57
58
59
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
-- Unknown-field passthrough: a decoder that meets fields not in its schema
-- must capture their raw bytes into result._unknown_fields and a subsequent
-- encode must re-emit them verbatim. Mirrors Tarantool's built-in `protobuf`
-- module convention.
local t = require('luatest')

local function hex(s)
    local out = {}
    for i = 1, #s do out[i] = string.format('%02x', s:byte(i)) end
    return table.concat(out)
end

local MODES = {'full', 'runtime'}

-- Wire-tag for (field_id, wire_type). Single-byte for id <= 15; we always
-- produce the multi-byte varint here for safety.
local function tag_bytes(id, wt)
    local v = id * 8 + wt
    local out = {}
    while v >= 0x80 do
        out[#out + 1] = string.char(v % 0x80 + 0x80)
        v = math.floor(v / 0x80)
    end
    out[#out + 1] = string.char(v)
    return table.concat(out)
end

-- Construct one byte-slice per wire type, using field IDs not declared in
-- examples/proto/hello.proto's Address (fields 1..4). These should all be
-- treated as unknown by the Address decoder.
local UNK_VARINT  = tag_bytes(50, 0) .. '\x2a'                      -- value 42
local UNK_I32     = tag_bytes(51, 5) .. '\x01\x00\x00\x00'          -- value 1
local UNK_I64     = tag_bytes(52, 1) .. '\x02\x00\x00\x00\x00\x00\x00\x00'
local UNK_LEN     = tag_bytes(53, 2) .. '\x03foo'                   -- 3-byte string

local ALL_UNK     = UNK_VARINT .. UNK_I32 .. UNK_I64 .. UNK_LEN

for _, mode in ipairs(MODES) do
    local g = t.group('unknown.' .. mode)
    local hello = require(mode .. '.hello.hello_pb')

    g.test_no_unknown_means_field_absent = function()
        local dec = hello.Address_decode(hello.Address_encode({street = 'X'}))
        t.assert_equals(dec.street, 'X')
        t.assert_equals(dec._unknown_fields, nil,
            '_unknown_fields must be absent when input had only known fields')
    end

    g.test_varint_unknown_round_trips = function()
        local known = hello.Address_encode({street = 'X', zip = 7})
        local mixed = known .. UNK_VARINT
        local dec = hello.Address_decode(mixed)
        t.assert_equals(dec.street, 'X')
        t.assert_equals(dec.zip, 7)
        t.assert_equals(hex(dec._unknown_fields), hex(UNK_VARINT))
    end

    g.test_all_wire_types_captured_in_order = function()
        local known = hello.Address_encode({street = 'X'})
        local mixed = known .. ALL_UNK
        local dec = hello.Address_decode(mixed)
        t.assert_equals(dec.street, 'X')
        t.assert_equals(hex(dec._unknown_fields), hex(ALL_UNK),
            'all four wire types must be captured verbatim in source order')
    end

    g.test_unknowns_interleaved_with_knowns = function()
        -- Bytes ordering: unknown, known, unknown — capture must preserve the
        -- two unknown chunks in encounter order (the known field stays out).
        local known1 = hello.Address_encode({street = 'A'})
        local known2 = hello.Address_encode({zip = 99})
        local mixed = UNK_VARINT .. known1 .. UNK_I64 .. known2
        local dec = hello.Address_decode(mixed)
        t.assert_equals(dec.street, 'A')
        t.assert_equals(dec.zip, 99)
        t.assert_equals(hex(dec._unknown_fields), hex(UNK_VARINT .. UNK_I64))
    end

    g.test_re_encode_preserves_unknown_bytes = function()
        local mixed = hello.Address_encode({street = 'X'}) .. ALL_UNK
        local dec = hello.Address_decode(mixed)
        local re_enc = hello.Address_encode(dec)
        -- Knowns are re-encoded in field-declaration order; unknowns trail.
        local expected = hello.Address_encode({street = 'X'}) .. ALL_UNK
        t.assert_equals(hex(re_enc), hex(expected),
            'unknown bytes must be re-emitted verbatim at the tail')
    end

    g.test_re_encode_decode_idempotent = function()
        local mixed = hello.Address_encode({street = 'X', zip = 1}) .. UNK_LEN
        local dec1 = hello.Address_decode(mixed)
        local dec2 = hello.Address_decode(hello.Address_encode(dec1))
        t.assert_equals(dec2.street, 'X')
        t.assert_equals(dec2.zip, 1)
        t.assert_equals(hex(dec2._unknown_fields), hex(UNK_LEN))
    end

    g.test_empty_unknown_string_treated_as_absent = function()
        -- User explicitly sets _unknown_fields = '' on encode; should be a no-op.
        local enc = hello.Address_encode({street = 'X', _unknown_fields = ''})
        t.assert_equals(hex(enc), hex(hello.Address_encode({street = 'X'})))
    end
end

-- ---------------------------------------------------------------------------
-- Cross-mode parity: full and runtime must capture identical unknown bytes.
-- ---------------------------------------------------------------------------
local g_parity = t.group('unknown.parity')
local hello_full    = require('full.hello.hello_pb')
local hello_runtime = require('runtime.hello.hello_pb')

g_parity.test_capture_identical_across_modes = function()
    local mixed = hello_full.Address_encode({street = 'X'}) .. ALL_UNK
    local d1 = hello_full.Address_decode(mixed)
    local d2 = hello_runtime.Address_decode(mixed)
    t.assert_equals(hex(d1._unknown_fields), hex(d2._unknown_fields))
    t.assert_equals(hex(d1._unknown_fields), hex(ALL_UNK))
end

g_parity.test_reencode_identical_across_modes = function()
    local table_with_unknowns = {
        street = 'X', zip = 9, _unknown_fields = ALL_UNK,
    }
    t.assert_equals(
        hex(hello_full.Address_encode(table_with_unknowns)),
        hex(hello_runtime.Address_encode(table_with_unknowns)))
end