-- Self-test for the conformance runner. -- -- The Google conformance suite is an external binary -- (`conformance_test_runner`) we can't reasonably bundle here, so this -- test stands in for it: drives our runner with crafted -- `ConformanceRequest` cases and asserts well-formed -- `ConformanceResponse` output. -- -- Two layers: -- 1. `core` group — calls `cmd.conformance.core.handle_request` directly. -- Covers every dispatch arm without paying subprocess cost. -- 2. `subprocess` group — actually pipes framed bytes through -- `tarantool cmd/conformance-runner.lua`. Covers the length-prefixed -- framing and the read-until-EOF loop. local t = require('luatest') local fio = require('fio') local core = require('cmd.conformance.core') local conformance = require('full.conformance.conformance_pb') local proto3 = require('full.protobuf_test_messages.proto3.test_messages_proto3_pb') local PROTOBUF = conformance.WireFormat.PROTOBUF local JSON = conformance.WireFormat.JSON local TEXT = conformance.WireFormat.TEXT_FORMAT local PROTO3_NAME = 'protobuf_test_messages.proto3.TestAllTypesProto3' local function encode_req(t_) return conformance.ConformanceRequest_encode(t_) end local function decode_resp(bytes) return conformance.ConformanceResponse_decode(bytes) end -- --------------------------------------------------------------------------- -- 1. Direct dispatch (no subprocess) -- --------------------------------------------------------------------------- local core_g = t.group('conformance.core') core_g.test_failureset_preflight = function() -- The conformance runner asks for a FailureSet up front. Empty payload, -- empty FailureSet response is the canonical answer. local req = encode_req({ protobuf_payload = '', requested_output_format = PROTOBUF, message_type = 'conformance.FailureSet', }) local resp = decode_resp(core.handle_request(req)) t.assert_equals(resp.protobuf_payload, '') t.assert_equals(resp.skipped, nil) t.assert_equals(resp.parse_error, nil) t.assert_equals(resp.runtime_error, nil) end core_g.test_pb_to_pb_roundtrip = function() -- Encode a TestAllTypesProto3 ourselves, ask the runner to round-trip -- it through pb->pb, assert byte-identical output. Since our encoder -- is deterministic for non-map fields, the output bytes must match -- input bytes exactly. local input = proto3.TestAllTypesProto3_encode({ optional_int32 = 42, optional_string = 'hello', repeated_int32 = {1, 2, 3, 4}, }) local resp = decode_resp(core.handle_request(encode_req({ protobuf_payload = input, requested_output_format = PROTOBUF, message_type = PROTO3_NAME, }))) t.assert_equals(resp.protobuf_payload, input) end core_g.test_pb_to_json = function() local input = proto3.TestAllTypesProto3_encode({ optional_int32 = 7, optional_string = 'world', }) local resp = decode_resp(core.handle_request(encode_req({ protobuf_payload = input, requested_output_format = JSON, message_type = PROTO3_NAME, }))) t.assert_str_contains(resp.json_payload, '"optionalInt32":7') t.assert_str_contains(resp.json_payload, '"optionalString":"world"') end core_g.test_json_to_pb = function() local resp = decode_resp(core.handle_request(encode_req({ json_payload = [[{"optionalInt32": 9, "optionalString": "abc"}]], requested_output_format = PROTOBUF, message_type = PROTO3_NAME, }))) t.assert_not(resp.parse_error, resp.parse_error) t.assert_not(resp.runtime_error, resp.runtime_error) local decoded = proto3.TestAllTypesProto3_decode(resp.protobuf_payload) t.assert_equals(decoded.optional_int32, 9) t.assert_equals(decoded.optional_string, 'abc') end core_g.test_parse_error_on_malformed_protobuf = function() -- A truncated varint should produce a parse_error, not a crash. local resp = decode_resp(core.handle_request(encode_req({ protobuf_payload = '\x08', -- tag(1, VARINT), no value requested_output_format = PROTOBUF, message_type = PROTO3_NAME, }))) t.assert_not_equals(resp.parse_error, nil) end core_g.test_parse_error_on_malformed_json = function() local resp = decode_resp(core.handle_request(encode_req({ json_payload = '{not valid json', requested_output_format = PROTOBUF, message_type = PROTO3_NAME, }))) t.assert_not_equals(resp.parse_error, nil) end core_g.test_unsupported_message_type_skipped = function() -- proto2 / editions test message types are intentionally unsupported. local resp = decode_resp(core.handle_request(encode_req({ protobuf_payload = '', requested_output_format = PROTOBUF, message_type = 'protobuf_test_messages.proto2.TestAllTypesProto2', }))) t.assert_str_contains(resp.skipped or '', 'unsupported message type') end core_g.test_text_format_skipped = function() local input = proto3.TestAllTypesProto3_encode({optional_int32 = 1}) local resp = decode_resp(core.handle_request(encode_req({ protobuf_payload = input, requested_output_format = TEXT, message_type = PROTO3_NAME, }))) t.assert_str_contains(resp.skipped or '', 'jspb/text') end core_g.test_empty_payload_decodes_as_empty_message = function() -- proto3 says empty bytes is a valid empty message. local resp = decode_resp(core.handle_request(encode_req({ protobuf_payload = '', requested_output_format = PROTOBUF, message_type = PROTO3_NAME, }))) t.assert_equals(resp.protobuf_payload, '') end core_g.test_wkt_timestamp_field_roundtrip = function() -- Exercise the WKT path: TestAllTypesProto3.optional_timestamp. -- We rely on our WKT Timestamp encoder/decoder. local input = proto3.TestAllTypesProto3_encode({ optional_timestamp = require('datetime').new({timestamp = 1700000000}), }) local resp = decode_resp(core.handle_request(encode_req({ protobuf_payload = input, requested_output_format = PROTOBUF, message_type = PROTO3_NAME, }))) t.assert_equals(resp.protobuf_payload, input) end -- --------------------------------------------------------------------------- -- 2. Subprocess: stdin/stdout framing -- --------------------------------------------------------------------------- -- -- These tests verify only the framing wrapper in cmd/conformance-runner.lua; -- the dispatch logic is fully covered by the `core` group above. local sub_g = t.group('conformance.subprocess') local REPO_ROOT = fio.abspath(fio.pathjoin( fio.dirname(debug.getinfo(1, 'S').source:sub(2)), '..')) local RUNNER = fio.pathjoin(REPO_ROOT, 'cmd', 'conformance-runner.lua') local function le32(n) return string.char( n % 256, math.floor(n / 256) % 256, math.floor(n / 65536) % 256, math.floor(n / 16777216) % 256) end local function read_le32(s, off) local b1, b2, b3, b4 = s:byte(off, off + 3) return b1 + b2 * 256 + b3 * 65536 + b4 * 16777216 end -- Run the runner with `input_bytes` piped to stdin, return the raw stdout. local function run_runner(input_bytes) local in_path = os.tmpname() local out_path = os.tmpname() local err_path = os.tmpname() local fin = assert(io.open(in_path, 'wb')) fin:write(input_bytes); fin:close() local lua_path = os.getenv('LUA_PATH') or '' local cmd = string.format( 'cd %q && LUA_PATH=%q tarantool cmd/conformance-runner.lua < %q > %q 2> %q', REPO_ROOT, lua_path, in_path, out_path, err_path) local rc = os.execute(cmd) local fout = assert(io.open(out_path, 'rb')) local out = fout:read('*a'); fout:close() local ferr = io.open(err_path, 'r') local err = ferr and ferr:read('*a') or '' if ferr then ferr:close() end os.remove(in_path); os.remove(out_path); os.remove(err_path) return rc, out, err end -- Parse a stream of length-prefixed responses out of `bytes`. local function parse_framed(bytes) local out, off = {}, 1 while off + 4 <= #bytes + 1 do local n = read_le32(bytes, off) off = off + 4 if off + n - 1 > #bytes then break end out[#out + 1] = bytes:sub(off, off + n - 1) off = off + n end return out, off end sub_g.test_single_request_round_trip = function() local req = encode_req({ protobuf_payload = '', requested_output_format = PROTOBUF, message_type = 'conformance.FailureSet', }) local framed = le32(#req) .. req local rc, out, err = run_runner(framed) t.assert_equals(rc, 0, 'runner exited non-zero, stderr=' .. err) local resps = parse_framed(out) t.assert_equals(#resps, 1, 'expected 1 framed response, stderr=' .. err) local resp = decode_resp(resps[1]) t.assert_equals(resp.protobuf_payload, '') end sub_g.test_multiple_requests_in_one_session = function() -- The conformance runner sends many requests over a single pipe; the -- script must loop until EOF rather than handle one and exit. local req1 = encode_req({ protobuf_payload = '', requested_output_format = PROTOBUF, message_type = 'conformance.FailureSet', }) local payload = proto3.TestAllTypesProto3_encode({optional_int32 = 17}) local req2 = encode_req({ protobuf_payload = payload, requested_output_format = PROTOBUF, message_type = PROTO3_NAME, }) local req3 = encode_req({ json_payload = [[{"optionalInt32": 99}]], requested_output_format = JSON, message_type = PROTO3_NAME, }) local framed = le32(#req1) .. req1 .. le32(#req2) .. req2 .. le32(#req3) .. req3 local rc, out, err = run_runner(framed) t.assert_equals(rc, 0, 'runner exited non-zero, stderr=' .. err) local resps = parse_framed(out) t.assert_equals(#resps, 3, 'expected 3 framed responses, stderr=' .. err) local r1 = decode_resp(resps[1]) local r2 = decode_resp(resps[2]) local r3 = decode_resp(resps[3]) t.assert_equals(r1.protobuf_payload, '') t.assert_equals(r2.protobuf_payload, payload) t.assert_str_contains(r3.json_payload, '"optionalInt32":99') end sub_g.test_empty_stdin_clean_exit = function() local rc, out, err = run_runner('') t.assert_equals(rc, 0, 'runner exited non-zero, stderr=' .. err) t.assert_equals(out, '', 'unexpected output on empty stdin') end