• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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