1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2023 Google LLC. All rights reserved.
3 //
4 // Use of this source code is governed by a BSD-style
5 // license that can be found in the LICENSE file or at
6 // https://developers.google.com/open-source/licenses/bsd
7
8 /* This is a upb implementation of the upb conformance tests, see:
9 * https://github.com/google/protobuf/tree/master/conformance
10 */
11
12 #include <errno.h>
13 #include <stdarg.h>
14 #include <stdlib.h>
15 #include <unistd.h>
16
17 #include "conformance/conformance.upb.h"
18 #include "conformance/conformance.upbdefs.h"
19 #include "editions/golden/test_messages_proto2_editions.upbdefs.h"
20 #include "editions/golden/test_messages_proto3_editions.upbdefs.h"
21 #include "google/protobuf/test_messages_proto2.upbdefs.h"
22 #include "google/protobuf/test_messages_proto3.upbdefs.h"
23 #include "upb/base/upcast.h"
24 #include "upb/json/decode.h"
25 #include "upb/json/encode.h"
26 #include "upb/reflection/message.h"
27 #include "upb/text/encode.h"
28 #include "upb/wire/decode.h"
29 #include "upb/wire/encode.h"
30
31 // Must be last.
32 #include "upb/port/def.inc"
33
34 int test_count = 0;
35 bool verbose = false; /* Set to true to get req/resp printed on stderr. */
36
CheckedRead(int fd,void * buf,size_t len)37 bool CheckedRead(int fd, void* buf, size_t len) {
38 size_t ofs = 0;
39 while (len > 0) {
40 ssize_t bytes_read = read(fd, (char*)buf + ofs, len);
41
42 if (bytes_read == 0) return false;
43
44 if (bytes_read < 0) {
45 perror("reading from test runner");
46 exit(1);
47 }
48
49 len -= bytes_read;
50 ofs += bytes_read;
51 }
52
53 return true;
54 }
55
CheckedWrite(int fd,const void * buf,size_t len)56 void CheckedWrite(int fd, const void* buf, size_t len) {
57 if ((size_t)write(fd, buf, len) != len) {
58 perror("writing to test runner");
59 exit(1);
60 }
61 }
62
63 typedef struct {
64 const conformance_ConformanceRequest* request;
65 conformance_ConformanceResponse* response;
66 upb_Arena* arena;
67 const upb_DefPool* symtab;
68 } ctx;
69
parse_proto(upb_Message * msg,const upb_MessageDef * m,const ctx * c)70 bool parse_proto(upb_Message* msg, const upb_MessageDef* m, const ctx* c) {
71 upb_StringView proto =
72 conformance_ConformanceRequest_protobuf_payload(c->request);
73 if (upb_Decode(proto.data, proto.size, msg, upb_MessageDef_MiniTable(m), NULL,
74 0, c->arena) == kUpb_DecodeStatus_Ok) {
75 return true;
76 } else {
77 static const char msg[] = "Parse error";
78 conformance_ConformanceResponse_set_parse_error(
79 c->response, upb_StringView_FromString(msg));
80 return false;
81 }
82 }
83
serialize_proto(const upb_Message * msg,const upb_MessageDef * m,const ctx * c)84 void serialize_proto(const upb_Message* msg, const upb_MessageDef* m,
85 const ctx* c) {
86 size_t len;
87 char* data;
88 upb_EncodeStatus status =
89 upb_Encode(msg, upb_MessageDef_MiniTable(m), 0, c->arena, &data, &len);
90 if (status == kUpb_EncodeStatus_Ok) {
91 conformance_ConformanceResponse_set_protobuf_payload(
92 c->response, upb_StringView_FromDataAndSize(data, len));
93 } else {
94 static const char msg[] = "Error serializing.";
95 conformance_ConformanceResponse_set_serialize_error(
96 c->response, upb_StringView_FromString(msg));
97 }
98 }
99
serialize_text(const upb_Message * msg,const upb_MessageDef * m,const ctx * c)100 void serialize_text(const upb_Message* msg, const upb_MessageDef* m,
101 const ctx* c) {
102 size_t len;
103 size_t len2;
104 int opts = 0;
105 char* data;
106
107 if (!conformance_ConformanceRequest_print_unknown_fields(c->request)) {
108 opts |= UPB_TXTENC_SKIPUNKNOWN;
109 }
110
111 len = upb_TextEncode(msg, m, c->symtab, opts, NULL, 0);
112 data = upb_Arena_Malloc(c->arena, len + 1);
113 len2 = upb_TextEncode(msg, m, c->symtab, opts, data, len + 1);
114 UPB_ASSERT(len == len2);
115 conformance_ConformanceResponse_set_text_payload(
116 c->response, upb_StringView_FromDataAndSize(data, len));
117 }
118
parse_json(upb_Message * msg,const upb_MessageDef * m,const ctx * c)119 bool parse_json(upb_Message* msg, const upb_MessageDef* m, const ctx* c) {
120 upb_StringView json = conformance_ConformanceRequest_json_payload(c->request);
121 upb_Status status;
122 int opts = 0;
123
124 if (conformance_ConformanceRequest_test_category(c->request) ==
125 conformance_JSON_IGNORE_UNKNOWN_PARSING_TEST) {
126 opts |= upb_JsonDecode_IgnoreUnknown;
127 }
128
129 upb_Status_Clear(&status);
130 if (upb_JsonDecode(json.data, json.size, msg, m, c->symtab, opts, c->arena,
131 &status)) {
132 return true;
133 } else {
134 const char* inerr = upb_Status_ErrorMessage(&status);
135 size_t len = strlen(inerr);
136 char* err = upb_Arena_Malloc(c->arena, len + 1);
137 memcpy(err, inerr, strlen(inerr));
138 err[len] = '\0';
139 conformance_ConformanceResponse_set_parse_error(
140 c->response, upb_StringView_FromString(err));
141 return false;
142 }
143 }
144
serialize_json(const upb_Message * msg,const upb_MessageDef * m,const ctx * c)145 void serialize_json(const upb_Message* msg, const upb_MessageDef* m,
146 const ctx* c) {
147 size_t len;
148 size_t len2;
149 int opts = 0;
150 char* data;
151 upb_Status status;
152
153 upb_Status_Clear(&status);
154 len = upb_JsonEncode(msg, m, c->symtab, opts, NULL, 0, &status);
155
156 if (len == (size_t)-1) {
157 const char* inerr = upb_Status_ErrorMessage(&status);
158 size_t len = strlen(inerr);
159 char* err = upb_Arena_Malloc(c->arena, len + 1);
160 memcpy(err, inerr, strlen(inerr));
161 err[len] = '\0';
162 conformance_ConformanceResponse_set_serialize_error(
163 c->response, upb_StringView_FromString(err));
164 return;
165 }
166
167 data = upb_Arena_Malloc(c->arena, len + 1);
168 len2 = upb_JsonEncode(msg, m, c->symtab, opts, data, len + 1, &status);
169 UPB_ASSERT(len == len2);
170 conformance_ConformanceResponse_set_json_payload(
171 c->response, upb_StringView_FromDataAndSize(data, len));
172 }
173
parse_input(upb_Message * msg,const upb_MessageDef * m,const ctx * c)174 bool parse_input(upb_Message* msg, const upb_MessageDef* m, const ctx* c) {
175 switch (conformance_ConformanceRequest_payload_case(c->request)) {
176 case conformance_ConformanceRequest_payload_protobuf_payload:
177 return parse_proto(msg, m, c);
178 case conformance_ConformanceRequest_payload_json_payload:
179 return parse_json(msg, m, c);
180 case conformance_ConformanceRequest_payload_NOT_SET:
181 fprintf(stderr, "conformance_upb: Request didn't have payload.\n");
182 return false;
183 default: {
184 static const char msg[] = "Unsupported input format.";
185 conformance_ConformanceResponse_set_skipped(
186 c->response, upb_StringView_FromString(msg));
187 return false;
188 }
189 }
190 }
191
write_output(const upb_Message * msg,const upb_MessageDef * m,const ctx * c)192 void write_output(const upb_Message* msg, const upb_MessageDef* m,
193 const ctx* c) {
194 switch (conformance_ConformanceRequest_requested_output_format(c->request)) {
195 case conformance_UNSPECIFIED:
196 fprintf(stderr, "conformance_upb: Unspecified output format.\n");
197 exit(1);
198 case conformance_PROTOBUF:
199 serialize_proto(msg, m, c);
200 break;
201 case conformance_TEXT_FORMAT:
202 serialize_text(msg, m, c);
203 break;
204 case conformance_JSON:
205 serialize_json(msg, m, c);
206 break;
207 default: {
208 static const char msg[] = "Unsupported output format.";
209 conformance_ConformanceResponse_set_skipped(
210 c->response, upb_StringView_FromString(msg));
211 break;
212 }
213 }
214 }
215
DoTest(const ctx * c)216 void DoTest(const ctx* c) {
217 upb_Message* msg;
218 upb_StringView name = conformance_ConformanceRequest_message_type(c->request);
219 const upb_MessageDef* m =
220 upb_DefPool_FindMessageByNameWithSize(c->symtab, name.data, name.size);
221 #if 0
222 // Handy code for limiting conformance tests to a single input payload.
223 // This is a hack since the conformance runner doesn't give an easy way to
224 // specify what test should be run.
225 const char skip[] = "\343>\010\301\002\344>\230?\001\230?\002\230?\003";
226 upb_StringView skip_str = upb_StringView_FromDataAndSize(skip, sizeof(skip) - 1);
227 upb_StringView pb_payload =
228 conformance_ConformanceRequest_protobuf_payload(c->request);
229 if (!upb_StringView_IsEqual(pb_payload, skip_str)) m = NULL;
230 #endif
231
232 if (!m) {
233 static const char msg[] = "Unknown message type.";
234 conformance_ConformanceResponse_set_skipped(c->response,
235 upb_StringView_FromString(msg));
236 return;
237 }
238
239 msg = upb_Message_New(upb_MessageDef_MiniTable(m), c->arena);
240
241 if (parse_input(msg, m, c)) {
242 write_output(msg, m, c);
243 }
244 }
245
debug_print(const char * label,const upb_Message * msg,const upb_MessageDef * m,const ctx * c)246 void debug_print(const char* label, const upb_Message* msg,
247 const upb_MessageDef* m, const ctx* c) {
248 char buf[512];
249 upb_TextEncode(msg, m, c->symtab, UPB_TXTENC_SINGLELINE, buf, sizeof(buf));
250 fprintf(stderr, "%s: %s\n", label, buf);
251 }
252
DoTestIo(upb_DefPool * symtab)253 bool DoTestIo(upb_DefPool* symtab) {
254 upb_Status status;
255 char* input;
256 char* output;
257 uint32_t input_size;
258 size_t output_size;
259 ctx c;
260
261 if (!CheckedRead(STDIN_FILENO, &input_size, sizeof(uint32_t))) {
262 /* EOF. */
263 return false;
264 }
265
266 c.symtab = symtab;
267 c.arena = upb_Arena_New();
268 input = upb_Arena_Malloc(c.arena, input_size);
269
270 if (!CheckedRead(STDIN_FILENO, input, input_size)) {
271 fprintf(stderr, "conformance_upb: unexpected EOF on stdin.\n");
272 exit(1);
273 }
274
275 c.request = conformance_ConformanceRequest_parse(input, input_size, c.arena);
276 c.response = conformance_ConformanceResponse_new(c.arena);
277
278 if (c.request) {
279 DoTest(&c);
280 } else {
281 fprintf(stderr, "conformance_upb: parse of ConformanceRequest failed: %s\n",
282 upb_Status_ErrorMessage(&status));
283 }
284
285 output = conformance_ConformanceResponse_serialize(c.response, c.arena,
286 &output_size);
287
288 uint32_t network_out = (uint32_t)output_size;
289 CheckedWrite(STDOUT_FILENO, &network_out, sizeof(uint32_t));
290 CheckedWrite(STDOUT_FILENO, output, output_size);
291
292 test_count++;
293
294 if (verbose) {
295 debug_print("Request", UPB_UPCAST(c.request),
296 conformance_ConformanceRequest_getmsgdef(symtab), &c);
297 debug_print("Response", UPB_UPCAST(c.response),
298 conformance_ConformanceResponse_getmsgdef(symtab), &c);
299 fprintf(stderr, "\n");
300 }
301
302 upb_Arena_Free(c.arena);
303
304 return true;
305 }
306
main(void)307 int main(void) {
308 upb_DefPool* symtab = upb_DefPool_New();
309
310 #ifdef REBUILD_MINITABLES
311 _upb_DefPool_LoadDefInitEx(
312 symtab, &google_protobuf_test_messages_proto2_proto_upbdefinit, true);
313 _upb_DefPool_LoadDefInitEx(
314 symtab, &google_protobuf_test_messages_proto3_proto_upbdefinit, true);
315 _upb_DefPool_LoadDefInitEx(
316 symtab,
317 &editions_golden_test_messages_proto2_editions_proto_upbdefinit,
318 true);
319 _upb_DefPool_LoadDefInitEx(
320 symtab,
321 &editions_golden_test_messages_proto3_editions_proto_upbdefinit,
322 true);
323 #else
324 protobuf_test_messages_proto2_TestAllTypesProto2_getmsgdef(symtab);
325 protobuf_test_messages_editions_proto2_TestAllTypesProto2_getmsgdef(symtab);
326 protobuf_test_messages_proto3_TestAllTypesProto3_getmsgdef(symtab);
327 protobuf_test_messages_editions_proto3_TestAllTypesProto3_getmsgdef(symtab);
328 #endif
329
330 while (1) {
331 if (!DoTestIo(symtab)) {
332 fprintf(stderr,
333 "conformance_upb: received EOF from test runner "
334 "after %d tests, exiting\n",
335 test_count);
336 upb_DefPool_Free(symtab);
337 return 0;
338 }
339 }
340 }
341