• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* This is a upb implementation of the upb conformance tests, see:
2  *   https://github.com/google/protobuf/tree/master/conformance
3  */
4 
5 #include <errno.h>
6 #include <stdarg.h>
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <unistd.h>
10 
11 #include "conformance/conformance.upb.h"
12 #include "conformance/conformance.upbdefs.h"
13 #include "src/google/protobuf/test_messages_proto2.upbdefs.h"
14 #include "src/google/protobuf/test_messages_proto3.upbdefs.h"
15 #include "upb/decode.h"
16 #include "upb/encode.h"
17 #include "upb/reflection.h"
18 #include "upb/json_decode.h"
19 #include "upb/json_encode.h"
20 #include "upb/text_encode.h"
21 
22 #include "upb/port_def.inc"
23 
24 int test_count = 0;
25 bool verbose = false;  /* Set to true to get req/resp printed on stderr. */
26 
CheckedRead(int fd,void * buf,size_t len)27 bool CheckedRead(int fd, void *buf, size_t len) {
28   size_t ofs = 0;
29   while (len > 0) {
30     ssize_t bytes_read = read(fd, (char*)buf + ofs, len);
31 
32     if (bytes_read == 0) return false;
33 
34     if (bytes_read < 0) {
35       perror("reading from test runner");
36       exit(1);
37     }
38 
39     len -= bytes_read;
40     ofs += bytes_read;
41   }
42 
43   return true;
44 }
45 
CheckedWrite(int fd,const void * buf,size_t len)46 void CheckedWrite(int fd, const void *buf, size_t len) {
47   if ((size_t)write(fd, buf, len) != len) {
48     perror("writing to test runner");
49     exit(1);
50   }
51 }
52 
53 typedef struct {
54   const conformance_ConformanceRequest *request;
55   conformance_ConformanceResponse *response;
56   upb_arena *arena;
57   const upb_symtab *symtab;
58 } ctx;
59 
parse_proto(upb_msg * msg,const upb_msgdef * m,const ctx * c)60 bool parse_proto(upb_msg *msg, const upb_msgdef *m, const ctx* c) {
61   upb_strview proto =
62       conformance_ConformanceRequest_protobuf_payload(c->request);
63   if (upb_decode(proto.data, proto.size, msg, upb_msgdef_layout(m), c->arena)) {
64     return true;
65   } else {
66     static const char msg[] = "Parse error";
67     conformance_ConformanceResponse_set_parse_error(
68         c->response, upb_strview_make(msg, strlen(msg)));
69     return false;
70   }
71 }
72 
serialize_proto(const upb_msg * msg,const upb_msgdef * m,const ctx * c)73 void serialize_proto(const upb_msg *msg, const upb_msgdef *m, const ctx *c) {
74   size_t len;
75   char *data = upb_encode(msg, upb_msgdef_layout(m), c->arena, &len);
76   if (data) {
77     conformance_ConformanceResponse_set_protobuf_payload(
78         c->response, upb_strview_make(data, len));
79   } else {
80     static const char msg[] = "Error serializing.";
81     conformance_ConformanceResponse_set_serialize_error(
82         c->response, upb_strview_make(msg, strlen(msg)));
83   }
84 }
85 
serialize_text(const upb_msg * msg,const upb_msgdef * m,const ctx * c)86 void serialize_text(const upb_msg *msg, const upb_msgdef *m, const ctx *c) {
87   size_t len;
88   size_t len2;
89   int opts = 0;
90   char *data;
91 
92   if (!conformance_ConformanceRequest_print_unknown_fields(c->request)) {
93     opts |= UPB_TXTENC_SKIPUNKNOWN;
94   }
95 
96   len = upb_text_encode(msg, m, c->symtab, opts, NULL, 0);
97   data = upb_arena_malloc(c->arena, len + 1);
98   len2 = upb_text_encode(msg, m, c->symtab, opts, data, len + 1);
99   UPB_ASSERT(len == len2);
100   conformance_ConformanceResponse_set_text_payload(
101       c->response, upb_strview_make(data, len));
102 }
103 
parse_json(upb_msg * msg,const upb_msgdef * m,const ctx * c)104 bool parse_json(upb_msg *msg, const upb_msgdef *m, const ctx* c) {
105   upb_strview json =
106       conformance_ConformanceRequest_json_payload(c->request);
107   upb_status status;
108   int opts = 0;
109 
110   if (conformance_ConformanceRequest_test_category(c->request) ==
111       conformance_JSON_IGNORE_UNKNOWN_PARSING_TEST) {
112     opts |= UPB_JSONDEC_IGNOREUNKNOWN;
113   }
114 
115   upb_status_clear(&status);
116   if (upb_json_decode(json.data, json.size, msg, m, c->symtab, opts, c->arena,
117                       &status)) {
118     return true;
119   } else {
120     const char *inerr = upb_status_errmsg(&status);
121     size_t len = strlen(inerr);
122     char *err = upb_arena_malloc(c->arena, len + 1);
123     memcpy(err, inerr, strlen(inerr));
124     err[len] = '\0';
125     conformance_ConformanceResponse_set_parse_error(c->response,
126                                                     upb_strview_makez(err));
127     return false;
128   }
129 }
130 
serialize_json(const upb_msg * msg,const upb_msgdef * m,const ctx * c)131 void serialize_json(const upb_msg *msg, const upb_msgdef *m, const ctx *c) {
132   size_t len;
133   size_t len2;
134   int opts = 0;
135   char *data;
136   upb_status status;
137 
138   upb_status_clear(&status);
139   len = upb_json_encode(msg, m, c->symtab, opts, NULL, 0, &status);
140 
141   if (len == (size_t)-1) {
142     const char *inerr = upb_status_errmsg(&status);
143     size_t len = strlen(inerr);
144     char *err = upb_arena_malloc(c->arena, len + 1);
145     memcpy(err, inerr, strlen(inerr));
146     err[len] = '\0';
147     conformance_ConformanceResponse_set_serialize_error(c->response,
148                                                         upb_strview_makez(err));
149     return;
150   }
151 
152   data = upb_arena_malloc(c->arena, len + 1);
153   len2 = upb_json_encode(msg, m, c->symtab, opts, data, len + 1, &status);
154   UPB_ASSERT(len == len2);
155   conformance_ConformanceResponse_set_json_payload(
156       c->response, upb_strview_make(data, len));
157 }
158 
parse_input(upb_msg * msg,const upb_msgdef * m,const ctx * c)159 bool parse_input(upb_msg *msg, const upb_msgdef *m, const ctx* c) {
160   switch (conformance_ConformanceRequest_payload_case(c->request)) {
161     case conformance_ConformanceRequest_payload_protobuf_payload:
162       return parse_proto(msg, m, c);
163     case conformance_ConformanceRequest_payload_json_payload:
164       return parse_json(msg, m, c);
165     case conformance_ConformanceRequest_payload_NOT_SET:
166       fprintf(stderr, "conformance_upb: Request didn't have payload.\n");
167       return false;
168     default: {
169       static const char msg[] = "Unsupported input format.";
170       conformance_ConformanceResponse_set_skipped(
171           c->response, upb_strview_make(msg, strlen(msg)));
172       return false;
173     }
174   }
175 }
176 
write_output(const upb_msg * msg,const upb_msgdef * m,const ctx * c)177 void write_output(const upb_msg *msg, const upb_msgdef *m, const ctx* c) {
178   switch (conformance_ConformanceRequest_requested_output_format(c->request)) {
179     case conformance_UNSPECIFIED:
180       fprintf(stderr, "conformance_upb: Unspecified output format.\n");
181       exit(1);
182     case conformance_PROTOBUF:
183       serialize_proto(msg, m, c);
184       break;
185     case conformance_TEXT_FORMAT:
186       serialize_text(msg, m, c);
187       break;
188     case conformance_JSON:
189       serialize_json(msg, m, c);
190       break;
191     default: {
192       static const char msg[] = "Unsupported output format.";
193       conformance_ConformanceResponse_set_skipped(
194           c->response, upb_strview_make(msg, strlen(msg)));
195       break;
196     }
197   }
198 }
199 
DoTest(const ctx * c)200 void DoTest(const ctx* c) {
201   upb_msg *msg;
202   upb_strview name = conformance_ConformanceRequest_message_type(c->request);
203   const upb_msgdef *m = upb_symtab_lookupmsg2(c->symtab, name.data, name.size);
204 #if 0
205   // Handy code for limiting conformance tests to a single input payload.
206   // This is a hack since the conformance runner doesn't give an easy way to
207   // specify what test should be run.
208   const char skip[] = "\343>\010\301\002\344>\230?\001\230?\002\230?\003";
209   upb_strview skip_str = upb_strview_make(skip, sizeof(skip) - 1);
210   upb_strview pb_payload =
211       conformance_ConformanceRequest_protobuf_payload(c->request);
212   if (!upb_strview_eql(pb_payload, skip_str)) m = NULL;
213 #endif
214 
215   if (!m) {
216     static const char msg[] = "Unknown message type.";
217     conformance_ConformanceResponse_set_skipped(
218         c->response, upb_strview_make(msg, strlen(msg)));
219     return;
220   }
221 
222   msg = upb_msg_new(m, c->arena);
223 
224   if (parse_input(msg, m, c)) {
225     write_output(msg, m, c);
226   }
227 }
228 
debug_print(const char * label,const upb_msg * msg,const upb_msgdef * m,const ctx * c)229 void debug_print(const char *label, const upb_msg *msg, const upb_msgdef *m,
230                  const ctx *c) {
231   char buf[512];
232   upb_text_encode(msg, m, c->symtab, UPB_TXTENC_SINGLELINE, buf, sizeof(buf));
233   fprintf(stderr, "%s: %s\n", label, buf);
234 }
235 
DoTestIo(upb_symtab * symtab)236 bool DoTestIo(upb_symtab *symtab) {
237   upb_status status;
238   char *input;
239   char *output;
240   uint32_t input_size;
241   size_t output_size;
242   ctx c;
243 
244   if (!CheckedRead(STDIN_FILENO, &input_size, sizeof(uint32_t))) {
245     /* EOF. */
246     return false;
247   }
248 
249   c.symtab = symtab;
250   c.arena = upb_arena_new();
251   input = upb_arena_malloc(c.arena, input_size);
252 
253   if (!CheckedRead(STDIN_FILENO, input, input_size)) {
254     fprintf(stderr, "conformance_upb: unexpected EOF on stdin.\n");
255     exit(1);
256   }
257 
258   c.request = conformance_ConformanceRequest_parse(input, input_size, c.arena);
259   c.response = conformance_ConformanceResponse_new(c.arena);
260 
261   if (c.request) {
262     DoTest(&c);
263   } else {
264     fprintf(stderr, "conformance_upb: parse of ConformanceRequest failed: %s\n",
265             upb_status_errmsg(&status));
266   }
267 
268   output = conformance_ConformanceResponse_serialize(c.response, c.arena,
269                                                      &output_size);
270 
271   CheckedWrite(STDOUT_FILENO, &output_size, sizeof(uint32_t));
272   CheckedWrite(STDOUT_FILENO, output, output_size);
273 
274   test_count++;
275 
276   if (verbose) {
277     debug_print("Request", c.request,
278                 conformance_ConformanceRequest_getmsgdef(symtab), &c);
279     debug_print("Response", c.response,
280                 conformance_ConformanceResponse_getmsgdef(symtab), &c);
281     fprintf(stderr, "\n");
282   }
283 
284   upb_arena_free(c.arena);
285 
286   return true;
287 }
288 
main(void)289 int main(void) {
290   upb_symtab *symtab = upb_symtab_new();
291 
292   protobuf_test_messages_proto2_TestAllTypesProto2_getmsgdef(symtab);
293   protobuf_test_messages_proto3_TestAllTypesProto3_getmsgdef(symtab);
294 
295   while (1) {
296     if (!DoTestIo(symtab)) {
297       fprintf(stderr, "conformance_upb: received EOF from test runner "
298                       "after %d tests, exiting\n", test_count);
299       upb_symtab_free(symtab);
300       return 0;
301     }
302   }
303 }
304