-- Test for bd-w3u / ra6 3g: C-side oneof encode/decode.
--
-- Acceptance per bd-w3u:
-- hello.Result (oneof outcome { string text=2; int32 code=3;
-- Address details=4; }) round-trips byte-equal across all three
-- branches; decoded table includes the active branch and excludes
-- the others. Mirrors test/protobuf_test.lua g.test_oneof_* but
-- drives encode/decode through pb.c_runtime.
--
-- Gated on PB_ENABLE_C=1 + a loadable c_runtime module, same as the
-- sibling c_runtime_*_test.lua files.
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
local full_hello
for _, mode in ipairs({'full', 'runtime'}) do
local g = t.group('c_runtime_oneof.' .. mode)
local hello
local plan
g.before_all(function()
skip_if_no_c()
hello = require(mode .. '.hello.hello_pb')
full_hello = require('full.hello.hello_pb')
plan = c_runtime.compile_plan(hello.Result_descriptor)
end)
g.before_each(skip_if_no_c)
function g.test_oneof_text_branch_encode_byte_equal()
local msg = {id = 1, text = 'hello'}
local c_bytes = c_runtime.encode(plan, msg)
local lua_bytes = full_hello.Result_encode(msg)
t.assert_equals(c_bytes, lua_bytes)
end
function g.test_oneof_code_branch_encode_byte_equal()
local msg = {id = 2, code = 42}
local c_bytes = c_runtime.encode(plan, msg)
local lua_bytes = full_hello.Result_encode(msg)
t.assert_equals(c_bytes, lua_bytes)
end
function g.test_oneof_message_branch_encode_byte_equal()
local msg = {id = 3, details = {street = 'X', zip = 99}}
local c_bytes = c_runtime.encode(plan, msg)
local lua_bytes = full_hello.Result_encode(msg)
t.assert_equals(c_bytes, lua_bytes)
end
function g.test_oneof_text_branch_round_trip()
local msg = {id = 1, text = 'hello'}
local dec = c_runtime.decode(plan, c_runtime.encode(plan, msg))
t.assert_equals(dec.id, 1)
t.assert_equals(dec.text, 'hello')
t.assert_equals(dec.code, nil)
t.assert_equals(dec.details, nil)
end
function g.test_oneof_code_branch_round_trip()
local msg = {id = 2, code = 42}
local dec = c_runtime.decode(plan, c_runtime.encode(plan, msg))
t.assert_equals(dec.id, 2)
t.assert_equals(dec.code, 42)
t.assert_equals(dec.text, nil)
t.assert_equals(dec.details, nil)
end
function g.test_oneof_message_branch_round_trip()
local msg = {id = 3, details = {street = 'X', zip = 99}}
local dec = c_runtime.decode(plan, c_runtime.encode(plan, msg))
t.assert_equals(dec.id, 3)
t.assert_equals(dec.details.street, 'X')
t.assert_equals(dec.details.zip, 99)
t.assert_equals(dec.text, nil)
t.assert_equals(dec.code, nil)
end
function g.test_oneof_emits_default_value_when_active()
-- text='' is the proto3 string default. Outside a oneof it would
-- elide; inside, presence is meaningful. Active branch must emit.
local c_bytes = c_runtime.encode(plan, {text = ''})
t.assert_equals(c_bytes, '\x12\x00')
local dec = c_runtime.decode(plan, c_bytes)
t.assert_equals(dec.text, '')
end
function g.test_oneof_decode_clears_siblings()
-- Wire bytes carry text first, then code. Decoder must end with
-- code set and text cleared (last-wins per spec).
local raw = '\x12\x03foo' -- field 2 (text) LEN=3, "foo"
.. '\x18\x07' -- field 3 (code) varint 7
local dec = c_runtime.decode(plan, raw)
t.assert_equals(dec.code, 7)
t.assert_equals(dec.text, nil)
end
function g.test_oneof_last_set_wins_on_encode()
-- Caller sets multiple branches; encoder picks the last in
-- declaration order (details = field 4).
local msg = {text = 'first', code = 9, details = {street = 'last'}}
local c_bytes = c_runtime.encode(plan, msg)
-- Byte-equal to Lua reference encode, which also resolves to
-- details-only.
local lua_bytes = full_hello.Result_encode(msg)
t.assert_equals(c_bytes, lua_bytes)
local dec = c_runtime.decode(plan, c_bytes)
t.assert_equals(dec.details.street, 'last')
t.assert_equals(dec.text, nil)
t.assert_equals(dec.code, nil)
end
function g.test_oneof_message_branch_decode_clears_scalar_sibling()
-- Sibling-clear must also fire when the active branch is a
-- sub-message (exercises the PB_KIND_MESSAGE arm of the
-- singular dispatch).
local raw = '\x18\x07' -- code = 7
.. '\x22\x03\x0a\x01X' -- details {street="X"}
local dec = c_runtime.decode(plan, raw)
t.assert_equals(dec.details.street, 'X')
t.assert_equals(dec.code, nil)
t.assert_equals(dec.text, nil)
end
end