|
|
|
/*
|
|
|
|
* 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 <errno.h>
|
|
|
|
#include <stdarg.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
|
|
#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/decode.h"
|
|
|
|
#include "upb/encode.h"
|
|
|
|
#include "upb/json/decode.h"
|
|
|
|
#include "upb/json/encode.h"
|
|
|
|
#include "upb/reflection/message.h"
|
|
|
|
#include "upb/text_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(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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|