1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc. All rights reserved.
3 // https://developers.google.com/protocol-buffers/
4 //
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are
7 // met:
8 //
9 // * Redistributions of source code must retain the above copyright
10 // notice, this list of conditions and the following disclaimer.
11 // * Redistributions in binary form must reproduce the above
12 // copyright notice, this list of conditions and the following disclaimer
13 // in the documentation and/or other materials provided with the
14 // distribution.
15 // * Neither the name of Google Inc. nor the names of its
16 // contributors may be used to endorse or promote products derived from
17 // this software without specific prior written permission.
18 //
19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
31 #include <errno.h>
32 #include <stdarg.h>
33 #include <unistd.h>
34
35 #include "conformance.pb.h"
36 #include <google/protobuf/test_messages_proto3.pb.h>
37 #include <google/protobuf/test_messages_proto2.pb.h>
38 #include <google/protobuf/message.h>
39 #include <google/protobuf/text_format.h>
40 #include <google/protobuf/util/json_util.h>
41 #include <google/protobuf/util/type_resolver_util.h>
42
43 using conformance::ConformanceRequest;
44 using conformance::ConformanceResponse;
45 using google::protobuf::Descriptor;
46 using google::protobuf::DescriptorPool;
47 using google::protobuf::Message;
48 using google::protobuf::MessageFactory;
49 using google::protobuf::TextFormat;
50 using google::protobuf::util::BinaryToJsonString;
51 using google::protobuf::util::JsonParseOptions;
52 using google::protobuf::util::JsonToBinaryString;
53 using google::protobuf::util::NewTypeResolverForDescriptorPool;
54 using google::protobuf::util::Status;
55 using google::protobuf::util::TypeResolver;
56 using protobuf_test_messages::proto2::TestAllTypesProto2;
57 using protobuf_test_messages::proto3::TestAllTypesProto3;
58 using std::string;
59
60 static const char kTypeUrlPrefix[] = "type.googleapis.com";
61
62 const char* kFailures[] = {
63 #if !GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER
64 "Required.Proto2.ProtobufInput."
65 "PrematureEofInDelimitedDataForKnownNonRepeatedValue.MESSAGE",
66 "Required.Proto2.ProtobufInput."
67 "PrematureEofInDelimitedDataForKnownRepeatedValue.MESSAGE",
68 "Required.Proto2.ProtobufInput.PrematureEofInPackedField.BOOL",
69 "Required.Proto2.ProtobufInput.PrematureEofInPackedField.ENUM",
70 "Required.Proto2.ProtobufInput.PrematureEofInPackedField.INT32",
71 "Required.Proto2.ProtobufInput.PrematureEofInPackedField.INT64",
72 "Required.Proto2.ProtobufInput.PrematureEofInPackedField.SINT32",
73 "Required.Proto2.ProtobufInput.PrematureEofInPackedField.SINT64",
74 "Required.Proto2.ProtobufInput.PrematureEofInPackedField.UINT32",
75 "Required.Proto2.ProtobufInput.PrematureEofInPackedField.UINT64",
76 "Required.Proto3.ProtobufInput."
77 "PrematureEofInDelimitedDataForKnownNonRepeatedValue.MESSAGE",
78 "Required.Proto3.ProtobufInput."
79 "PrematureEofInDelimitedDataForKnownRepeatedValue.MESSAGE",
80 "Required.Proto3.ProtobufInput.PrematureEofInPackedField.BOOL",
81 "Required.Proto3.ProtobufInput.PrematureEofInPackedField.ENUM",
82 "Required.Proto3.ProtobufInput.PrematureEofInPackedField.INT32",
83 "Required.Proto3.ProtobufInput.PrematureEofInPackedField.INT64",
84 "Required.Proto3.ProtobufInput.PrematureEofInPackedField.SINT32",
85 "Required.Proto3.ProtobufInput.PrematureEofInPackedField.SINT64",
86 "Required.Proto3.ProtobufInput.PrematureEofInPackedField.UINT32",
87 "Required.Proto3.ProtobufInput.PrematureEofInPackedField.UINT64",
88 #endif
89 };
90
GetTypeUrl(const Descriptor * message)91 static string GetTypeUrl(const Descriptor* message) {
92 return string(kTypeUrlPrefix) + "/" + message->full_name();
93 }
94
95 int test_count = 0;
96 bool verbose = false;
97 TypeResolver* type_resolver;
98 string* type_url;
99
100
CheckedRead(int fd,void * buf,size_t len)101 bool CheckedRead(int fd, void *buf, size_t len) {
102 size_t ofs = 0;
103 while (len > 0) {
104 ssize_t bytes_read = read(fd, (char*)buf + ofs, len);
105
106 if (bytes_read == 0) return false;
107
108 if (bytes_read < 0) {
109 GOOGLE_LOG(FATAL) << "Error reading from test runner: " << strerror(errno);
110 }
111
112 len -= bytes_read;
113 ofs += bytes_read;
114 }
115
116 return true;
117 }
118
CheckedWrite(int fd,const void * buf,size_t len)119 void CheckedWrite(int fd, const void *buf, size_t len) {
120 if (write(fd, buf, len) != len) {
121 GOOGLE_LOG(FATAL) << "Error writing to test runner: " << strerror(errno);
122 }
123 }
124
DoTest(const ConformanceRequest & request,ConformanceResponse * response)125 void DoTest(const ConformanceRequest& request, ConformanceResponse* response) {
126 Message *test_message;
127 const Descriptor *descriptor = DescriptorPool::generated_pool()->FindMessageTypeByName(
128 request.message_type());
129 if (!descriptor) {
130 GOOGLE_LOG(FATAL) << "No such message type: " << request.message_type();
131 }
132 test_message = MessageFactory::generated_factory()->GetPrototype(descriptor)->New();
133
134 switch (request.payload_case()) {
135 case ConformanceRequest::kProtobufPayload: {
136 if (!test_message->ParseFromString(request.protobuf_payload())) {
137 // Getting parse details would involve something like:
138 // http://stackoverflow.com/questions/22121922/how-can-i-get-more-details-about-errors-generated-during-protobuf-parsing-c
139 response->set_parse_error("Parse error (no more details available).");
140 return;
141 }
142 break;
143 }
144
145 case ConformanceRequest::kJsonPayload: {
146 string proto_binary;
147 JsonParseOptions options;
148 options.ignore_unknown_fields =
149 (request.test_category() ==
150 conformance::JSON_IGNORE_UNKNOWN_PARSING_TEST);
151 Status status = JsonToBinaryString(type_resolver, *type_url,
152 request.json_payload(), &proto_binary,
153 options);
154 if (!status.ok()) {
155 response->set_parse_error(string("Parse error: ") +
156 status.error_message().as_string());
157 return;
158 }
159
160 if (!test_message->ParseFromString(proto_binary)) {
161 response->set_runtime_error(
162 "Parsing JSON generates invalid proto output.");
163 return;
164 }
165 break;
166 }
167
168 case ConformanceRequest::kTextPayload: {
169 if (!TextFormat::ParseFromString(request.text_payload(), test_message)) {
170 response->set_parse_error("Parse error");
171 return;
172 }
173 break;
174 }
175
176 case ConformanceRequest::PAYLOAD_NOT_SET:
177 GOOGLE_LOG(FATAL) << "Request didn't have payload.";
178 break;
179
180 default:
181 GOOGLE_LOG(FATAL) << "unknown payload type: "
182 << request.payload_case();
183 break;
184 }
185
186 conformance::FailureSet failures;
187 if (descriptor == failures.GetDescriptor()) {
188 for (const char* s : kFailures) failures.add_failure(s);
189 test_message = &failures;
190 }
191
192 switch (request.requested_output_format()) {
193 case conformance::UNSPECIFIED:
194 GOOGLE_LOG(FATAL) << "Unspecified output format";
195 break;
196
197 case conformance::PROTOBUF: {
198 GOOGLE_CHECK(test_message->SerializeToString(response->mutable_protobuf_payload()));
199 break;
200 }
201
202 case conformance::JSON: {
203 string proto_binary;
204 GOOGLE_CHECK(test_message->SerializeToString(&proto_binary));
205 Status status = BinaryToJsonString(type_resolver, *type_url, proto_binary,
206 response->mutable_json_payload());
207 if (!status.ok()) {
208 response->set_serialize_error(
209 string("Failed to serialize JSON output: ") +
210 status.error_message().as_string());
211 return;
212 }
213 break;
214 }
215
216 case conformance::TEXT_FORMAT: {
217 TextFormat::Printer printer;
218 printer.SetHideUnknownFields(!request.print_unknown_fields());
219 GOOGLE_CHECK(printer.PrintToString(*test_message,
220 response->mutable_text_payload()));
221 break;
222 }
223
224 default:
225 GOOGLE_LOG(FATAL) << "Unknown output format: "
226 << request.requested_output_format();
227 }
228 }
229
DoTestIo()230 bool DoTestIo() {
231 string serialized_input;
232 string serialized_output;
233 ConformanceRequest request;
234 ConformanceResponse response;
235 uint32_t bytes;
236
237 if (!CheckedRead(STDIN_FILENO, &bytes, sizeof(uint32_t))) {
238 // EOF.
239 return false;
240 }
241
242 serialized_input.resize(bytes);
243
244 if (!CheckedRead(STDIN_FILENO, (char*)serialized_input.c_str(), bytes)) {
245 GOOGLE_LOG(ERROR) << "Unexpected EOF on stdin. " << strerror(errno);
246 }
247
248 if (!request.ParseFromString(serialized_input)) {
249 GOOGLE_LOG(FATAL) << "Parse of ConformanceRequest proto failed.";
250 return false;
251 }
252
253 DoTest(request, &response);
254
255 response.SerializeToString(&serialized_output);
256
257 bytes = serialized_output.size();
258 CheckedWrite(STDOUT_FILENO, &bytes, sizeof(uint32_t));
259 CheckedWrite(STDOUT_FILENO, serialized_output.c_str(), bytes);
260
261 if (verbose) {
262 fprintf(stderr, "conformance-cpp: request=%s, response=%s\n",
263 request.ShortDebugString().c_str(),
264 response.ShortDebugString().c_str());
265 }
266
267 test_count++;
268
269 return true;
270 }
271
main()272 int main() {
273 type_resolver = NewTypeResolverForDescriptorPool(
274 kTypeUrlPrefix, DescriptorPool::generated_pool());
275 type_url = new string(GetTypeUrl(TestAllTypesProto3::descriptor()));
276 while (1) {
277 if (!DoTestIo()) {
278 fprintf(stderr, "conformance-cpp: received EOF from test runner "
279 "after %d tests, exiting\n", test_count);
280 return 0;
281 }
282 }
283 }
284