From b1d9c7581bcc6b4f299155746dc04acc2330e78c Mon Sep 17 00:00:00 2001 From: Eugene Blikh Date: Sat, 16 May 2026 10:01:21 +0300 Subject: [PATCH] test: pin unknown-fields text-format conformance regressions Adds eight tests under conformance.core mirroring the Recommended.Proto3.ProtobufInput.*UnknownFields_*.TextFormatOutput tests in the Google harness. Each sends the byte-exact upstream payload (field IDs 1001..1011 from UnknownToTestAllTypes) through cmd/conformance/core and pins current output, with the target assertion + needed runtime fix documented in each failure message. Two blockers surface: 1. wire.skip_field rejects SGROUP/EGROUP (wire 3/4), so the four Group/Repeated tests fail at decode. 2. pb.text.encode doesn't walk _unknown_fields, so the four *_Print tests serialize empty. Lets us iterate on those fixes locally without the Docker round-trip. --- test/conformance_test.lua | 140 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 140 insertions(+) diff --git a/test/conformance_test.lua b/test/conformance_test.lua index 60e1031259ed56f5cd64f52a58a8e9deb16db463..8972b1eceb44776e6e4c60272abcdae257e8beff 100644 --- a/test/conformance_test.lua +++ b/test/conformance_test.lua @@ -1166,6 +1166,146 @@ core_g.test_json_double_integer_valued_emitted_without_decimal = function() t.assert_str_contains(resp.json_payload, '"optionalDouble":1') end +-- ========================================================================= +-- Unknown-field text-format regressions. +-- +-- Mirrors the eight Recommended.Proto3.ProtobufInput.*UnknownFields_*. +-- TextFormatOutput tests in the Google harness so we can iterate on the +-- runtime without the Docker round-trip. Each test sends the *exact* +-- bytes the conformance suite sends, runs through cmd/conformance/core, +-- and asserts what we produce today plus the target our text encoder +-- should produce once unknown-field rendering / group skipping land. +-- +-- Field numbers (from src/google/protobuf/test_messages_proto2.proto +-- `UnknownToTestAllTypes`, used to build the input payloads): +-- 1001 optional_int32 1004 OptionalGroup (group) +-- 1002 optional_string 1006 optional_bool +-- 1003 nested_message (LEN) 1011 repeated_int32 (unpacked) +-- None of these IDs exist in TestAllTypesProto3 so the receiver sees +-- them as unknown fields. +-- +-- The Google harness compares semantically via MessageDifferencer after +-- re-parsing our text output with AllowFieldNumber(true). It accepts any +-- text that round-trips to a message with the same field set, so +-- numeric field IDs (e.g. `1001: 123`) are the canonical rendering for +-- types unknown to the testee. +-- ========================================================================= + +local function pb_to_text(input) + return decode_resp(core.handle_request(encode_req({ + protobuf_payload = input, + requested_output_format = TEXT, + message_type = PROTO3_NAME, + }))) +end + +local function pb_to_text_print_unknowns(input) + return decode_resp(core.handle_request(encode_req({ + protobuf_payload = input, + requested_output_format = TEXT, + message_type = PROTO3_NAME, + print_unknown_fields = true, + }))) +end + +-- Payload helpers — same bytes the upstream conformance suite produces. +local SCALAR_UNKNOWN = '\xc8\x3e\x7b' -- field 1001 varint 123 + .. '\xd2\x3e\x05hello' -- field 1002 LEN "hello" + .. '\xf0\x3e\x01' -- field 1006 varint 1 (bool) +local MESSAGE_UNKNOWN = '\xda\x3e\x02\x08\x6f' -- field 1003 LEN {1:111} +local GROUP_UNKNOWN = '\xe3\x3e\x08\xc1\x02\xe4\x3e' -- field 1004 SGROUP {a:321} EGROUP +-- Repeated builds on Group then appends three repeated_int32 entries. +local REPEATED_UNKNOWN = GROUP_UNKNOWN + .. '\xd8\x3e\x01\xd8\x3e\x02\xd8\x3e\x03' -- field 1011 varint 1,2,3 + +core_g.test_scalar_unknown_fields_drop = function() + -- ProtobufInput.ScalarUnknownFields_Drop.TextFormatOutput + -- print_unknown_fields=false → unknowns dropped → text empty. + local resp = pb_to_text(SCALAR_UNKNOWN) + t.assert_not(resp.parse_error, resp.parse_error) + t.assert_not(resp.serialize_error, resp.serialize_error) + t.assert_equals(resp.text_payload, '') +end + +core_g.test_scalar_unknown_fields_print = function() + -- ProtobufInput.ScalarUnknownFields_Print.TextFormatOutput + -- TARGET: text_payload must round-trip to a message containing the + -- three unknown fields. Canonical form is numeric IDs, e.g. + -- 1001: 123\n1002: "hello"\n1006: 1\n + -- CURRENT: pb.text doesn't walk _unknown_fields, so output is empty. + -- Pin current behavior; flip this assertion when the renderer lands. + local resp = pb_to_text_print_unknowns(SCALAR_UNKNOWN) + t.assert_not(resp.parse_error, resp.parse_error) + t.assert_not(resp.serialize_error, resp.serialize_error) + t.assert_equals(resp.text_payload, '', + 'current behavior pins empty output; ' .. + 'replace with assert_str_contains(...,"1001: 123") when ' .. + 'pb.text renders captured _unknown_fields') +end + +core_g.test_message_unknown_fields_drop = function() + -- ProtobufInput.MessageUnknownFields_Drop.TextFormatOutput + local resp = pb_to_text(MESSAGE_UNKNOWN) + t.assert_not(resp.parse_error, resp.parse_error) + t.assert_equals(resp.text_payload, '') +end + +core_g.test_message_unknown_fields_print = function() + -- ProtobufInput.MessageUnknownFields_Print.TextFormatOutput + -- TARGET text: 1003 {\n 1: 111\n}\n + -- CURRENT: pb.text drops _unknown_fields, so output is empty. + local resp = pb_to_text_print_unknowns(MESSAGE_UNKNOWN) + t.assert_not(resp.parse_error, resp.parse_error) + t.assert_equals(resp.text_payload, '', + 'replace with target "1003 { 1: 111 }" rendering when ' .. + 'pb.text grows submessage unknown-field support') +end + +core_g.test_group_unknown_fields_drop = function() + -- ProtobufInput.GroupUnknownFields_Drop.TextFormatOutput + -- Wire types 3/4 (SGROUP/EGROUP) — proto3 must tolerate them as + -- unknown groups. Our skip_field currently errors on wire 3, so the + -- decoder reports parse_error before reaching the text encoder. + -- TARGET: parse_error == nil, text_payload == ''. + local resp = pb_to_text(GROUP_UNKNOWN) + t.assert_not_equals(resp.parse_error, nil, + 'current: skip_field rejects SGROUP wire type 3. ' .. + 'Fix in runtime/pb/wire.lua to recursively skip until matching ' .. + 'EGROUP, then flip this to assert_not + text_payload == ""') +end + +core_g.test_group_unknown_fields_print = function() + -- ProtobufInput.GroupUnknownFields_Print.TextFormatOutput + -- TARGET text: 1004 {\n 1: 321\n}\n (groups render as submessages) + -- CURRENT: blocked by same SGROUP-skip issue as the Drop variant. + local resp = pb_to_text_print_unknowns(GROUP_UNKNOWN) + t.assert_not_equals(resp.parse_error, nil, + 'blocked on SGROUP support in wire.skip_field; once decoder ' .. + 'accepts groups and pb.text renders unknowns, expect ' .. + 'text_payload containing "1004 {" and "1: 321"') +end + +core_g.test_repeated_unknown_fields_drop = function() + -- ProtobufInput.RepeatedUnknownFields_Drop.TextFormatOutput + -- Payload prepends a group, so the same SGROUP issue blocks decode. + -- TARGET: parse_error == nil, text_payload == ''. + local resp = pb_to_text(REPEATED_UNKNOWN) + t.assert_not_equals(resp.parse_error, nil, + 'blocked on SGROUP-skip in wire.lua (group bytes come first)') +end + +core_g.test_repeated_unknown_fields_print = function() + -- ProtobufInput.RepeatedUnknownFields_Print.TextFormatOutput + -- TARGET text: + -- 1004 {\n 1: 321\n}\n1011: 1\n1011: 2\n1011: 3\n + -- CURRENT: blocked by SGROUP-skip, then by unknown-field rendering. + local resp = pb_to_text_print_unknowns(REPEATED_UNKNOWN) + t.assert_not_equals(resp.parse_error, nil, + 'two-stage fix: (1) accept SGROUP in wire.skip_field, ' .. + '(2) render captured _unknown_fields from pb.text. ' .. + 'Then assert_str_contains for "1011: 1", "1011: 2", "1011: 3"') +end + -- --------------------------------------------------------------------------- -- 2. Subprocess: stdin/stdout framing -- ---------------------------------------------------------------------------