~bigbes/tarantool

tarantool-protobuf

ref: 7b3d69010b4ebad701bb98c0dda9e785823857d0 tarantool-protobuf/test/c_runtime_plan_test.lua -rw-r--r-- 9.0 KiB
7b3d6901 — Eugene Blikh bench: apply mcode arena hardening across all bench scripts (3qu) 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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
-- Smoke test for bd-mq7: descriptor → C plan compiler.
--
-- Only runs when PB_ENABLE_C=1 is set in the environment AND the C
-- runtime module is loadable. Otherwise the group is skipped, which
-- keeps `just test` green on hosts without the C module built.
--
-- Acceptance per bd-mq7:
--   (1) pb.c_runtime.compile_plan(desc) returns a userdata
--   (2) plan->n_fields and plan->fields[i].tag are readable
--   (3) Plans for hello.Person and hello.Address build without error

local t = require('luatest')

local pb = require('pb')
local c_runtime = pb.c_runtime

local function skip_if_no_c()
    if c_runtime == nil then
        t.skip('PB_ENABLE_C not set or pb.c_runtime not available')
    end
end

-- Run each test against both codegen modes — c_plan is attached to the
-- descriptor regardless of mode, so both pick up the same compile path.
for _, mode in ipairs({'full', 'runtime'}) do
    local g = t.group('c_runtime_plan.' .. mode)
    local hello

    g.before_all(function()
        skip_if_no_c()
        hello = require(mode .. '.hello.hello_pb')
    end)

    g.before_each(skip_if_no_c)

    function g.test_module_surface()
        t.assert_equals(type(c_runtime.compile_plan), 'function')
        t.assert_equals(type(c_runtime._abi_version), 'string')
        t.assert(c_runtime.KIND ~= nil, 'KIND table exposed')
        t.assert(c_runtime.WIRE ~= nil, 'WIRE table exposed')
        t.assert_equals(c_runtime.WIRE.LEN, 2)
        t.assert_equals(c_runtime.WIRE.VARINT, 0)
    end

    function g.test_compile_address_returns_userdata()
        local plan = c_runtime.compile_plan(hello.Address_descriptor)
        t.assert_equals(type(plan), 'userdata')
        t.assert_equals(c_runtime.plan_name(plan), 'hello.Address')
        t.assert_equals(c_runtime.plan_n_fields(plan), 4)
    end

    function g.test_address_field_shapes()
        local plan = c_runtime.compile_plan(hello.Address_descriptor)
        -- {name="street", id=1, kind=scalar/string}
        local f1 = c_runtime.plan_field_info(plan, 1)
        t.assert_equals(f1.name, 'street')
        t.assert_equals(f1.field_number, 1)
        t.assert_equals(f1.wire_type, c_runtime.WIRE.LEN)
        t.assert_equals(f1.kind, c_runtime.KIND.STRING)
        t.assert_equals(f1.tag_bytes:byte(1, 1), 0x0A) -- (1<<3)|2
        -- {name="apartment", id=4, kind=scalar/string, optional=true}
        local f4 = c_runtime.plan_field_info(plan, 4)
        t.assert_equals(f4.name, 'apartment')
        t.assert_equals(f4.optional, true)
        t.assert_equals(f4.field_number, 4)
    end

    function g.test_compile_person_returns_userdata()
        local plan = c_runtime.compile_plan(hello.Person_descriptor)
        t.assert_equals(type(plan), 'userdata')
        t.assert_equals(c_runtime.plan_name(plan), 'hello.Person')
        -- Person has 14 fields per examples/proto/hello.proto
        t.assert_equals(c_runtime.plan_n_fields(plan), 14)
    end

    function g.test_person_scalar_fields()
        local plan = c_runtime.compile_plan(hello.Person_descriptor)
        -- name = string @1
        local f = c_runtime.plan_field_info(plan, 1)
        t.assert_equals(f.name, 'name')
        t.assert_equals(f.kind, c_runtime.KIND.STRING)
        t.assert_equals(f.wire_type, c_runtime.WIRE.LEN)
        -- age = int32 @2
        f = c_runtime.plan_field_info(plan, 2)
        t.assert_equals(f.name, 'age')
        t.assert_equals(f.kind, c_runtime.KIND.INT32)
        t.assert_equals(f.wire_type, c_runtime.WIRE.VARINT)
        -- user_id = fixed64 @9
        f = c_runtime.plan_field_info(plan, 9)
        t.assert_equals(f.name, 'user_id')
        t.assert_equals(f.kind, c_runtime.KIND.FIXED64)
        t.assert_equals(f.wire_type, c_runtime.WIRE.I64)
        -- balance = sint32 @10
        f = c_runtime.plan_field_info(plan, 10)
        t.assert_equals(f.kind, c_runtime.KIND.SINT32)
        -- weight_kg = double @11
        f = c_runtime.plan_field_info(plan, 11)
        t.assert_equals(f.kind, c_runtime.KIND.DOUBLE)
        t.assert_equals(f.wire_type, c_runtime.WIRE.I64)
    end

    function g.test_person_repeated_packed()
        local plan = c_runtime.compile_plan(hello.Person_descriptor)
        -- emails = repeated string @3 (not packed)
        local f = c_runtime.plan_field_info(plan, 3)
        t.assert_equals(f.name, 'emails')
        t.assert_equals(f.repeated, true)
        t.assert_equals(f.packed, false)
        t.assert_equals(f.wire_type, c_runtime.WIRE.LEN)
        -- lucky_numbers = repeated int32, packed @7
        f = c_runtime.plan_field_info(plan, 7)
        t.assert_equals(f.name, 'lucky_numbers')
        t.assert_equals(f.repeated, true)
        t.assert_equals(f.packed, true)
        -- packed flips wire type to LEN regardless of element type
        t.assert_equals(f.wire_type, c_runtime.WIRE.LEN)
        t.assert_equals(f.kind, c_runtime.KIND.INT32)
    end

    function g.test_person_enum_field()
        local plan = c_runtime.compile_plan(hello.Person_descriptor)
        -- status = enum @4
        local f = c_runtime.plan_field_info(plan, 4)
        t.assert_equals(f.name, 'status')
        t.assert_equals(f.kind, c_runtime.KIND.ENUM)
        t.assert_equals(f.wire_type, c_runtime.WIRE.VARINT)
    end

    function g.test_person_message_field_resolves_sub_plan()
        local plan = c_runtime.compile_plan(hello.Person_descriptor)
        -- address = message Address @5
        local f = c_runtime.plan_field_info(plan, 5)
        t.assert_equals(f.name, 'address')
        t.assert_equals(f.kind, c_runtime.KIND.MESSAGE)
        t.assert(f.sub_plan_idx > 0, 'sub_plan_idx populated')
        local sub = c_runtime.plan_sub_plan(plan, f.sub_plan_idx)
        t.assert_equals(type(sub), 'userdata')
        t.assert_equals(c_runtime.plan_name(sub), 'hello.Address')
    end

    function g.test_self_reference_cycle()
        local plan = c_runtime.compile_plan(hello.Person_descriptor)
        -- friends = repeated Person @6 (self-reference)
        local f = c_runtime.plan_field_info(plan, 6)
        t.assert_equals(f.name, 'friends')
        t.assert_equals(f.kind, c_runtime.KIND.MESSAGE)
        t.assert_equals(f.repeated, true)
        t.assert(f.sub_plan_idx > 0)
        local sub = c_runtime.plan_sub_plan(plan, f.sub_plan_idx)
        t.assert_equals(c_runtime.plan_name(sub), 'hello.Person')
        -- Cycle resolves to the same userdata, not a fresh one
        t.assert(sub == plan,
                 'self-reference returns the same plan userdata (cycle broken)')
    end

    function g.test_person_map_field()
        local plan = c_runtime.compile_plan(hello.Person_descriptor)
        -- ages_by_nickname: map<string, int32> @13
        local f = c_runtime.plan_field_info(plan, 12)  -- 12th field
        t.assert_equals(f.name, 'ages_by_nickname')
        t.assert_equals(f.kind, c_runtime.KIND.MAP)
        t.assert_equals(f.map_key_kind, c_runtime.KIND.STRING)
        t.assert_equals(f.map_value_kind, c_runtime.KIND.INT32)
        -- addresses_by_label: map<string, Address> @15
        f = c_runtime.plan_field_info(plan, 14)
        t.assert_equals(f.name, 'addresses_by_label')
        t.assert_equals(f.map_value_kind, c_runtime.KIND.MESSAGE)
        t.assert(f.map_value_sub_plan_idx > 0)
        local sub = c_runtime.plan_sub_plan(plan, f.map_value_sub_plan_idx)
        t.assert_equals(c_runtime.plan_name(sub), 'hello.Address')
    end

    function g.test_idempotent_compile()
        local p1 = c_runtime.compile_plan(hello.Person_descriptor)
        local p2 = c_runtime.compile_plan(hello.Person_descriptor)
        t.assert(p1 == p2, 'second compile returns cached plan')
    end

    function g.test_result_oneof()
        local plan = c_runtime.compile_plan(hello.Result_descriptor)
        t.assert_equals(c_runtime.plan_n_fields(plan), 4)
        t.assert_equals(c_runtime.plan_n_oneofs(plan), 1)
        local o = c_runtime.plan_oneof_info(plan, 1)
        t.assert_equals(o.name, 'outcome')
        t.assert_equals(#o.member_indices, 3)
        -- Members are 0-based indices into plan->fields[]; text/code/details
        -- are fields 2, 3, 4 in Result (id-ordered) → indices 1, 2, 3.
        local idxs = {}
        for _, i in ipairs(o.member_indices) do idxs[i] = true end
        t.assert(idxs[1] and idxs[2] and idxs[3],
                 'oneof members map to text/code/details indices')
        -- Each member field gets oneof_idx = 0 (the only oneof in Result).
        for _, fi in ipairs({2, 3, 4}) do
            local f = c_runtime.plan_field_info(plan, fi)
            t.assert_equals(f.oneof_idx, 0,
                            ('field %d oneof_idx'):format(fi))
        end
        -- id (field 1) is NOT in a oneof.
        local f1 = c_runtime.plan_field_info(plan, 1)
        t.assert_equals(f1.oneof_idx, -1)
    end

    function g.test_wkt_override_detection()
        local wkt = pb.wkt
        local plan = c_runtime.compile_plan(wkt.Timestamp_descriptor)
        t.assert_equals(c_runtime.plan_has_override(plan), true)
        -- has_override means the plan does not walk fields; field count is 0.
        t.assert_equals(c_runtime.plan_n_fields(plan), 0)
    end
end