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