@@ 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
-- ---------------------------------------------------------------------------