/* * Copyright (c) 2009-2021, Google LLC * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of Google LLC nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* This is a upb implementation of the upb conformance tests, see: * https://github.com/google/protobuf/tree/master/conformance */ #include #include #include #include #include "conformance/conformance.upb.h" #include "conformance/conformance.upbdefs.h" #include "google/protobuf/test_messages_proto2.upbdefs.h" #include "google/protobuf/test_messages_proto3.upbdefs.h" #include "upb/json/decode.h" #include "upb/json/encode.h" #include "upb/reflection/message.h" #include "upb/text_encode.h" #include "upb/wire/decode.h" #include "upb/wire/encode.h" // Must be last. #include "upb/port/def.inc" int test_count = 0; bool verbose = false; /* Set to true to get req/resp printed on stderr. */ bool CheckedRead(int fd, void* buf, size_t len) { size_t ofs = 0; while (len > 0) { ssize_t bytes_read = read(fd, (char*)buf + ofs, len); if (bytes_read == 0) return false; if (bytes_read < 0) { perror("reading from test runner"); exit(1); } len -= bytes_read; ofs += bytes_read; } return true; } void CheckedWrite(int fd, const void* buf, size_t len) { if ((size_t)write(fd, buf, len) != len) { perror("writing to test runner"); exit(1); } } typedef struct { const conformance_ConformanceRequest* request; conformance_ConformanceResponse* response; upb_Arena* arena; const upb_DefPool* symtab; } ctx; bool parse_proto(upb_Message* msg, const upb_MessageDef* m, const ctx* c) { upb_StringView proto = conformance_ConformanceRequest_protobuf_payload(c->request); if (upb_Decode(proto.data, proto.size, msg, upb_MessageDef_MiniTable(m), NULL, 0, c->arena) == kUpb_DecodeStatus_Ok) { return true; } else { static const char msg[] = "Parse error"; conformance_ConformanceResponse_set_parse_error( c->response, upb_StringView_FromString(msg)); return false; } } void serialize_proto(const upb_Message* msg, const upb_MessageDef* m, const ctx* c) { size_t len; char* data; upb_EncodeStatus status = upb_Encode(msg, upb_MessageDef_MiniTable(m), 0, c->arena, &data, &len); if (status == kUpb_EncodeStatus_Ok) { conformance_ConformanceResponse_set_protobuf_payload( c->response, upb_StringView_FromDataAndSize(data, len)); } else { static const char msg[] = "Error serializing."; conformance_ConformanceResponse_set_serialize_error( c->response, upb_StringView_FromString(msg)); } } void serialize_text(const upb_Message* msg, const upb_MessageDef* m, const ctx* c) { size_t len; size_t len2; int opts = 0; char* data; if (!conformance_ConformanceRequest_print_unknown_fields(c->request)) { opts |= UPB_TXTENC_SKIPUNKNOWN; } len = upb_TextEncode(msg, m, c->symtab, opts, NULL, 0); data = upb_Arena_Malloc(c->arena, len + 1); len2 = upb_TextEncode(msg, m, c->symtab, opts, data, len + 1); UPB_ASSERT(len == len2); conformance_ConformanceResponse_set_text_payload( c->response, upb_StringView_FromDataAndSize(data, len)); } bool parse_json(upb_Message* msg, const upb_MessageDef* m, const ctx* c) { upb_StringView json = conformance_ConformanceRequest_json_payload(c->request); upb_Status status; int opts = 0; if (conformance_ConformanceRequest_test_category(c->request) == conformance_JSON_IGNORE_UNKNOWN_PARSING_TEST) { opts |= upb_JsonDecode_IgnoreUnknown; } upb_Status_Clear(&status); if (upb_JsonDecode(json.data, json.size, msg, m, c->symtab, opts, c->arena, &status)) { return true; } else { const char* inerr = upb_Status_ErrorMessage(&status); size_t len = strlen(inerr); char* err = upb_Arena_Malloc(c->arena, len + 1); memcpy(err, inerr, strlen(inerr)); err[len] = '\0'; conformance_ConformanceResponse_set_parse_error( c->response, upb_StringView_FromString(err)); return false; } } void serialize_json(const upb_Message* msg, const upb_MessageDef* m, const ctx* c) { size_t len; size_t len2; int opts = 0; char* data; upb_Status status; upb_Status_Clear(&status); len = upb_JsonEncode(msg, m, c->symtab, opts, NULL, 0, &status); if (len == (size_t)-1) { const char* inerr = upb_Status_ErrorMessage(&status); size_t len = strlen(inerr); char* err = upb_Arena_Malloc(c->arena, len + 1); memcpy(err, inerr, strlen(inerr)); err[len] = '\0'; conformance_ConformanceResponse_set_serialize_error( c->response, upb_StringView_FromString(err)); return; } data = upb_Arena_Malloc(c->arena, len + 1); len2 = upb_JsonEncode(msg, m, c->symtab, opts, data, len + 1, &status); UPB_ASSERT(len == len2); conformance_ConformanceResponse_set_json_payload( c->response, upb_StringView_FromDataAndSize(data, len)); } bool parse_input(upb_Message* msg, const upb_MessageDef* m, const ctx* c) { switch (conformance_ConformanceRequest_payload_case(c->request)) { case conformance_ConformanceRequest_payload_protobuf_payload: return parse_proto(msg, m, c); case conformance_ConformanceRequest_payload_json_payload: return parse_json(msg, m, c); case conformance_ConformanceRequest_payload_NOT_SET: fprintf(stderr, "conformance_upb: Request didn't have payload.\n"); return false; default: { static const char msg[] = "Unsupported input format."; conformance_ConformanceResponse_set_skipped( c->response, upb_StringView_FromString(msg)); return false; } } } void write_output(const upb_Message* msg, const upb_MessageDef* m, const ctx* c) { switch (conformance_ConformanceRequest_requested_output_format(c->request)) { case conformance_UNSPECIFIED: fprintf(stderr, "conformance_upb: Unspecified output format.\n"); exit(1); case conformance_PROTOBUF: serialize_proto(msg, m, c); break; case conformance_TEXT_FORMAT: serialize_text(msg, m, c); break; case conformance_JSON: serialize_json(msg, m, c); break; default: { static const char msg[] = "Unsupported output format."; conformance_ConformanceResponse_set_skipped( c->response, upb_StringView_FromString(msg)); break; } } } void DoTest(const ctx* c) { upb_Message* msg; upb_StringView name = conformance_ConformanceRequest_message_type(c->request); const upb_MessageDef* m = upb_DefPool_FindMessageByNameWithSize(c->symtab, name.data, name.size); #if 0 // Handy code for limiting conformance tests to a single input payload. // This is a hack since the conformance runner doesn't give an easy way to // specify what test should be run. const char skip[] = "\343>\010\301\002\344>\230?\001\230?\002\230?\003"; upb_StringView skip_str = upb_StringView_FromDataAndSize(skip, sizeof(skip) - 1); upb_StringView pb_payload = conformance_ConformanceRequest_protobuf_payload(c->request); if (!upb_StringView_IsEqual(pb_payload, skip_str)) m = NULL; #endif if (!m) { static const char msg[] = "Unknown message type."; conformance_ConformanceResponse_set_skipped(c->response, upb_StringView_FromString(msg)); return; } msg = upb_Message_New(upb_MessageDef_MiniTable(m), c->arena); if (parse_input(msg, m, c)) { write_output(msg, m, c); } } void debug_print(const char* label, const upb_Message* msg, const upb_MessageDef* m, const ctx* c) { char buf[512]; upb_TextEncode(msg, m, c->symtab, UPB_TXTENC_SINGLELINE, buf, sizeof(buf)); fprintf(stderr, "%s: %s\n", label, buf); } bool DoTestIo(upb_DefPool* symtab) { upb_Status status; char* input; char* output; uint32_t input_size; size_t output_size; ctx c; if (!CheckedRead(STDIN_FILENO, &input_size, sizeof(uint32_t))) { /* EOF. */ return false; } c.symtab = symtab; c.arena = upb_Arena_New(); input = upb_Arena_Malloc(c.arena, input_size); if (!CheckedRead(STDIN_FILENO, input, input_size)) { fprintf(stderr, "conformance_upb: unexpected EOF on stdin.\n"); exit(1); } c.request = conformance_ConformanceRequest_parse(input, input_size, c.arena); c.response = conformance_ConformanceResponse_new(c.arena); if (c.request) { DoTest(&c); } else { fprintf(stderr, "conformance_upb: parse of ConformanceRequest failed: %s\n", upb_Status_ErrorMessage(&status)); } output = conformance_ConformanceResponse_serialize(c.response, c.arena, &output_size); uint32_t network_out = (uint32_t)output_size; CheckedWrite(STDOUT_FILENO, &network_out, sizeof(uint32_t)); CheckedWrite(STDOUT_FILENO, output, output_size); test_count++; if (verbose) { debug_print("Request", c.request, conformance_ConformanceRequest_getmsgdef(symtab), &c); debug_print("Response", c.response, conformance_ConformanceResponse_getmsgdef(symtab), &c); fprintf(stderr, "\n"); } upb_Arena_Free(c.arena); return true; } int main(void) { upb_DefPool* symtab = upb_DefPool_New(); #ifdef REBUILD_MINITABLES _upb_DefPool_LoadDefInitEx( symtab, &google_protobuf_test_messages_proto2_proto_upbdefinit, true); _upb_DefPool_LoadDefInitEx( symtab, &google_protobuf_test_messages_proto3_proto_upbdefinit, true); #else protobuf_test_messages_proto2_TestAllTypesProto2_getmsgdef(symtab); protobuf_test_messages_proto3_TestAllTypesProto3_getmsgdef(symtab); #endif while (1) { if (!DoTestIo(symtab)) { fprintf(stderr, "conformance_upb: received EOF from test runner " "after %d tests, exiting\n", test_count); upb_DefPool_Free(symtab); return 0; } } }