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 <google/protobuf/message.h>
36 #include <google/protobuf/text_format.h>
37 #include <google/protobuf/util/json_util.h>
38 #include <google/protobuf/util/type_resolver_util.h>
39 #include <google/protobuf/stubs/status.h>
40 #include "conformance.pb.h"
41 #include <google/protobuf/test_messages_proto2.pb.h>
42 #include <google/protobuf/test_messages_proto3.pb.h>
43 #include <google/protobuf/stubs/status.h>
44
45 using conformance::ConformanceRequest;
46 using conformance::ConformanceResponse;
47 using google::protobuf::Descriptor;
48 using google::protobuf::DescriptorPool;
49 using google::protobuf::Message;
50 using google::protobuf::MessageFactory;
51 using google::protobuf::TextFormat;
52 using google::protobuf::util::BinaryToJsonString;
53 using google::protobuf::util::JsonParseOptions;
54 using google::protobuf::util::JsonToBinaryString;
55 using google::protobuf::util::NewTypeResolverForDescriptorPool;
56 using google::protobuf::util::TypeResolver;
57 using protobuf_test_messages::proto3::TestAllTypesProto3;
58 using protobuf_test_messages::proto2::TestAllTypesProto2;
59 using std::string;
60
61 static const char kTypeUrlPrefix[] = "type.googleapis.com";
62
63 const char* kFailures[] = {
64 };
65
GetTypeUrl(const Descriptor * message)66 static string GetTypeUrl(const Descriptor* message) {
67 return string(kTypeUrlPrefix) + "/" + message->full_name();
68 }
69
70 int test_count = 0;
71 bool verbose = false;
72 TypeResolver* type_resolver;
73 string* type_url;
74
75 namespace google {
76 namespace protobuf {
77
78 using util::Status;
79
CheckedRead(int fd,void * buf,size_t len)80 bool CheckedRead(int fd, void *buf, size_t len) {
81 size_t ofs = 0;
82 while (len > 0) {
83 ssize_t bytes_read = read(fd, (char*)buf + ofs, len);
84
85 if (bytes_read == 0) return false;
86
87 if (bytes_read < 0) {
88 GOOGLE_LOG(FATAL) << "Error reading from test runner: " << strerror(errno);
89 }
90
91 len -= bytes_read;
92 ofs += bytes_read;
93 }
94
95 return true;
96 }
97
CheckedWrite(int fd,const void * buf,size_t len)98 void CheckedWrite(int fd, const void *buf, size_t len) {
99 if (write(fd, buf, len) != len) {
100 GOOGLE_LOG(FATAL) << "Error writing to test runner: " << strerror(errno);
101 }
102 }
103
DoTest(const ConformanceRequest & request,ConformanceResponse * response)104 void DoTest(const ConformanceRequest& request, ConformanceResponse* response) {
105 Message *test_message;
106 google::protobuf::LinkMessageReflection<TestAllTypesProto2>();
107 google::protobuf::LinkMessageReflection<TestAllTypesProto3>();
108 const Descriptor *descriptor = DescriptorPool::generated_pool()->FindMessageTypeByName(
109 request.message_type());
110 if (!descriptor) {
111 GOOGLE_LOG(FATAL) << "No such message type: " << request.message_type();
112 }
113 test_message = MessageFactory::generated_factory()->GetPrototype(descriptor)->New();
114
115 switch (request.payload_case()) {
116 case ConformanceRequest::kProtobufPayload: {
117 if (!test_message->ParseFromString(request.protobuf_payload())) {
118 // Getting parse details would involve something like:
119 // http://stackoverflow.com/questions/22121922/how-can-i-get-more-details-about-errors-generated-during-protobuf-parsing-c
120 response->set_parse_error("Parse error (no more details available).");
121 return;
122 }
123 break;
124 }
125
126 case ConformanceRequest::kJsonPayload: {
127 string proto_binary;
128 JsonParseOptions options;
129 options.ignore_unknown_fields =
130 (request.test_category() ==
131 conformance::JSON_IGNORE_UNKNOWN_PARSING_TEST);
132 util::Status status =
133 JsonToBinaryString(type_resolver, *type_url, request.json_payload(),
134 &proto_binary, options);
135 if (!status.ok()) {
136 response->set_parse_error(string("Parse error: ") +
137 std::string(status.message()));
138 return;
139 }
140
141 if (!test_message->ParseFromString(proto_binary)) {
142 response->set_runtime_error(
143 "Parsing JSON generates invalid proto output.");
144 return;
145 }
146 break;
147 }
148
149 case ConformanceRequest::kTextPayload: {
150 if (!TextFormat::ParseFromString(request.text_payload(), test_message)) {
151 response->set_parse_error("Parse error");
152 return;
153 }
154 break;
155 }
156
157 case ConformanceRequest::PAYLOAD_NOT_SET:
158 GOOGLE_LOG(FATAL) << "Request didn't have payload.";
159 break;
160
161 default:
162 GOOGLE_LOG(FATAL) << "unknown payload type: " << request.payload_case();
163 break;
164 }
165
166 conformance::FailureSet failures;
167 if (descriptor == failures.GetDescriptor()) {
168 for (const char* s : kFailures) failures.add_failure(s);
169 test_message = &failures;
170 }
171
172 switch (request.requested_output_format()) {
173 case conformance::UNSPECIFIED:
174 GOOGLE_LOG(FATAL) << "Unspecified output format";
175 break;
176
177 case conformance::PROTOBUF: {
178 GOOGLE_CHECK(test_message->SerializeToString(
179 response->mutable_protobuf_payload()));
180 break;
181 }
182
183 case conformance::JSON: {
184 string proto_binary;
185 GOOGLE_CHECK(test_message->SerializeToString(&proto_binary));
186 util::Status status =
187 BinaryToJsonString(type_resolver, *type_url, proto_binary,
188 response->mutable_json_payload());
189 if (!status.ok()) {
190 response->set_serialize_error(
191 string("Failed to serialize JSON output: ") +
192 std::string(status.message()));
193 return;
194 }
195 break;
196 }
197
198 case conformance::TEXT_FORMAT: {
199 TextFormat::Printer printer;
200 printer.SetHideUnknownFields(!request.print_unknown_fields());
201 GOOGLE_CHECK(printer.PrintToString(*test_message,
202 response->mutable_text_payload()));
203 break;
204 }
205
206 default:
207 GOOGLE_LOG(FATAL) << "Unknown output format: "
208 << request.requested_output_format();
209 }
210 }
211
DoTestIo()212 bool DoTestIo() {
213 string serialized_input;
214 string serialized_output;
215 ConformanceRequest request;
216 ConformanceResponse response;
217 uint32_t bytes;
218
219 if (!CheckedRead(STDIN_FILENO, &bytes, sizeof(uint32_t))) {
220 // EOF.
221 return false;
222 }
223
224 serialized_input.resize(bytes);
225
226 if (!CheckedRead(STDIN_FILENO, (char*)serialized_input.c_str(), bytes)) {
227 GOOGLE_LOG(ERROR) << "Unexpected EOF on stdin. " << strerror(errno);
228 }
229
230 if (!request.ParseFromString(serialized_input)) {
231 GOOGLE_LOG(FATAL) << "Parse of ConformanceRequest proto failed.";
232 return false;
233 }
234
235 DoTest(request, &response);
236
237 response.SerializeToString(&serialized_output);
238
239 bytes = serialized_output.size();
240 CheckedWrite(STDOUT_FILENO, &bytes, sizeof(uint32_t));
241 CheckedWrite(STDOUT_FILENO, serialized_output.c_str(), bytes);
242
243 if (verbose) {
244 fprintf(stderr, "conformance-cpp: request=%s, response=%s\n",
245 request.ShortDebugString().c_str(),
246 response.ShortDebugString().c_str());
247 }
248
249 test_count++;
250
251 return true;
252 }
253
254 } // namespace protobuf
255 } // namespace google
256
main()257 int main() {
258 type_resolver = NewTypeResolverForDescriptorPool(
259 kTypeUrlPrefix, DescriptorPool::generated_pool());
260 type_url = new string(GetTypeUrl(TestAllTypesProto3::descriptor()));
261 while (1) {
262 if (!google::protobuf::DoTestIo()) {
263 fprintf(stderr, "conformance-cpp: received EOF from test runner "
264 "after %d tests, exiting\n", test_count);
265 return 0;
266 }
267 }
268 }
269