~bigbes/tarantool

tarantool-protobuf

ref: b1d9c7581bcc6b4f299155746dc04acc2330e78c tarantool-protobuf/cmd/conformance/core.lua -rw-r--r-- 5.4 KiB
b1d9c758 — Eugene Blikh test: pin unknown-fields text-format conformance regressions 3 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
-- Core dispatch for the Google protobuf conformance protocol.
--
-- Decodes a `conformance.ConformanceRequest` (wire bytes), runs it through
-- our codec/JSON in the requested format, and returns the encoded
-- `conformance.ConformanceResponse` bytes. The stdin/stdout framing is
-- handled by the thin wrapper in `cmd/conformance-runner.lua`.
--
-- Split out as a module so the luatest suite can drive `handle_request`
-- directly without spawning a subprocess.

local pb           = require('pb')
local conformance  = require('full.conformance.conformance_pb')
local proto3_tests = require('full.protobuf_test_messages.proto3.test_messages_proto3_pb')

local M = {}

-- Map of supported `message_type` -> descriptor. Any other message type
-- yields a `skipped` response so we don't claim conformance for protos we
-- don't actually support yet (proto2, editions).
local MESSAGE_REGISTRY = {
    ['protobuf_test_messages.proto3.TestAllTypesProto3'] =
        proto3_tests.TestAllTypesProto3_descriptor,
    ['conformance.FailureSet'] =
        conformance.FailureSet_descriptor,
}

-- Register the test message in the runtime's WKT registry so Any fields
-- with `@type` pointing at a non-WKT user type (the conformance suite uses
-- TestAllTypesProto3 itself inside some Any tests) resolve in JSON decode.
pb.register(proto3_tests.TestAllTypesProto3_descriptor)

local WIRE_FORMAT = conformance.WireFormat
local PROTOBUF    = WIRE_FORMAT.PROTOBUF
local JSON        = WIRE_FORMAT.JSON
local JSPB        = WIRE_FORMAT.JSPB
local TEXT_FORMAT = WIRE_FORMAT.TEXT_FORMAT

local function dispatch(req)
    local desc = MESSAGE_REGISTRY[req.message_type]
    if desc == nil then
        return {skipped = 'unsupported message type: ' ..
            tostring(req.message_type)}
    end

    -- 1. Decode the input payload into a Lua table.
    local msg
    if req.protobuf_payload ~= nil then
        local ok, decoded = pcall(pb.decode, desc, req.protobuf_payload)
        if not ok then
            return {parse_error = 'protobuf decode failed: ' ..
                tostring(decoded)}
        end
        msg = decoded
    elseif req.json_payload ~= nil then
        local ok, decoded = pcall(pb.json.decode, desc, req.json_payload)
        if not ok then
            return {parse_error = 'json decode failed: ' .. tostring(decoded)}
        end
        msg = decoded
    elseif req.jspb_payload ~= nil then
        return {skipped = 'jspb input not supported'}
    elseif req.text_payload ~= nil then
        -- pb.text is encode-only; text-format input parsing is deferred.
        return {skipped = 'text-format input not supported'}
    else
        return {runtime_error = 'no payload set in ConformanceRequest'}
    end

    -- 2. Serialize in the requested output format.
    local out_fmt = req.requested_output_format
    if out_fmt == PROTOBUF then
        local ok, bytes = pcall(pb.encode, desc, msg)
        if not ok then
            return {serialize_error = 'protobuf encode failed: ' ..
                tostring(bytes)}
        end
        return {protobuf_payload = bytes}
    elseif out_fmt == JSON then
        -- Optional opt-out: the Google harness crashes inside jsoncpp
        -- when comparing our currently-imperfect JSON output (enum
        -- numerics, map<K,V> shape, oneof object form). Setting
        -- PB_CONFORMANCE_SKIP_JSON=1 in the container short-circuits to
        -- `skipped` so the suite completes and PROTOBUF coverage stays
        -- measurable; the host tests run without the flag and still
        -- exercise the JSON output path end-to-end.
        -- Check explicitly for "1" so docker `-e PB_CONFORMANCE_SKIP_JSON=`
        -- (empty string) can disable the gate without rebuilding the image.
        if os.getenv('PB_CONFORMANCE_SKIP_JSON') == '1' then
            return {skipped =
                'JSON output deferred (see test/conformance/known_failures.txt)'}
        end
        local ok, jbytes = pcall(pb.json.encode, desc, msg)
        if not ok then
            return {serialize_error = 'json encode failed: ' ..
                tostring(jbytes)}
        end
        return {json_payload = jbytes}
    elseif out_fmt == TEXT_FORMAT then
        local ok, tbytes = pcall(pb.text.encode, desc, msg)
        if not ok then
            return {serialize_error = 'text encode failed: ' ..
                tostring(tbytes)}
        end
        return {text_payload = tbytes}
    elseif out_fmt == JSPB then
        return {skipped = 'jspb output not supported'}
    else
        return {runtime_error = 'unknown requested_output_format: ' ..
            tostring(out_fmt)}
    end
end

-- Takes the raw bytes of a ConformanceRequest, returns the raw bytes of
-- a ConformanceResponse. Never throws — every failure path produces a
-- well-formed ConformanceResponse so the conformance runner stays synced.
function M.handle_request(req_bytes)
    local ok, req = pcall(conformance.ConformanceRequest_decode, req_bytes)
    if not ok then
        return conformance.ConformanceResponse_encode(
            {runtime_error = 'failed to decode ConformanceRequest: ' ..
                tostring(req)})
    end
    local resp = dispatch(req)
    local ok2, bytes = pcall(conformance.ConformanceResponse_encode, resp)
    if not ok2 then
        return conformance.ConformanceResponse_encode(
            {runtime_error = 'failed to encode response: ' .. tostring(bytes)})
    end
    return bytes
end

-- Expose for introspection / extension.
M.MESSAGE_REGISTRY = MESSAGE_REGISTRY

return M