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