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