OLD | NEW |
| (Empty) |
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 <stdarg.h> | |
32 #include <string> | |
33 | |
34 #include "conformance.pb.h" | |
35 #include "conformance_test.h" | |
36 #include <google/protobuf/stubs/common.h> | |
37 #include <google/protobuf/stubs/stringprintf.h> | |
38 #include <google/protobuf/text_format.h> | |
39 #include <google/protobuf/util/json_util.h> | |
40 #include <google/protobuf/util/message_differencer.h> | |
41 #include <google/protobuf/util/type_resolver_util.h> | |
42 #include <google/protobuf/wire_format_lite.h> | |
43 | |
44 using conformance::ConformanceRequest; | |
45 using conformance::ConformanceResponse; | |
46 using conformance::TestAllTypes; | |
47 using conformance::WireFormat; | |
48 using google::protobuf::Descriptor; | |
49 using google::protobuf::FieldDescriptor; | |
50 using google::protobuf::internal::WireFormatLite; | |
51 using google::protobuf::TextFormat; | |
52 using google::protobuf::util::JsonToBinaryString; | |
53 using google::protobuf::util::MessageDifferencer; | |
54 using google::protobuf::util::NewTypeResolverForDescriptorPool; | |
55 using google::protobuf::util::Status; | |
56 using std::string; | |
57 | |
58 namespace { | |
59 | |
60 static const char kTypeUrlPrefix[] = "type.googleapis.com"; | |
61 | |
62 static string GetTypeUrl(const Descriptor* message) { | |
63 return string(kTypeUrlPrefix) + "/" + message->full_name(); | |
64 } | |
65 | |
66 /* Routines for building arbitrary protos *************************************/ | |
67 | |
68 // We would use CodedOutputStream except that we want more freedom to build | |
69 // arbitrary protos (even invalid ones). | |
70 | |
71 const string empty; | |
72 | |
73 string cat(const string& a, const string& b, | |
74 const string& c = empty, | |
75 const string& d = empty, | |
76 const string& e = empty, | |
77 const string& f = empty, | |
78 const string& g = empty, | |
79 const string& h = empty, | |
80 const string& i = empty, | |
81 const string& j = empty, | |
82 const string& k = empty, | |
83 const string& l = empty) { | |
84 string ret; | |
85 ret.reserve(a.size() + b.size() + c.size() + d.size() + e.size() + f.size() + | |
86 g.size() + h.size() + i.size() + j.size() + k.size() + l.size()); | |
87 ret.append(a); | |
88 ret.append(b); | |
89 ret.append(c); | |
90 ret.append(d); | |
91 ret.append(e); | |
92 ret.append(f); | |
93 ret.append(g); | |
94 ret.append(h); | |
95 ret.append(i); | |
96 ret.append(j); | |
97 ret.append(k); | |
98 ret.append(l); | |
99 return ret; | |
100 } | |
101 | |
102 // The maximum number of bytes that it takes to encode a 64-bit varint. | |
103 #define VARINT_MAX_LEN 10 | |
104 | |
105 size_t vencode64(uint64_t val, char *buf) { | |
106 if (val == 0) { buf[0] = 0; return 1; } | |
107 size_t i = 0; | |
108 while (val) { | |
109 uint8_t byte = val & 0x7fU; | |
110 val >>= 7; | |
111 if (val) byte |= 0x80U; | |
112 buf[i++] = byte; | |
113 } | |
114 return i; | |
115 } | |
116 | |
117 string varint(uint64_t x) { | |
118 char buf[VARINT_MAX_LEN]; | |
119 size_t len = vencode64(x, buf); | |
120 return string(buf, len); | |
121 } | |
122 | |
123 // TODO: proper byte-swapping for big-endian machines. | |
124 string fixed32(void *data) { return string(static_cast<char*>(data), 4); } | |
125 string fixed64(void *data) { return string(static_cast<char*>(data), 8); } | |
126 | |
127 string delim(const string& buf) { return cat(varint(buf.size()), buf); } | |
128 string uint32(uint32_t u32) { return fixed32(&u32); } | |
129 string uint64(uint64_t u64) { return fixed64(&u64); } | |
130 string flt(float f) { return fixed32(&f); } | |
131 string dbl(double d) { return fixed64(&d); } | |
132 string zz32(int32_t x) { return varint(WireFormatLite::ZigZagEncode32(x)); } | |
133 string zz64(int64_t x) { return varint(WireFormatLite::ZigZagEncode64(x)); } | |
134 | |
135 string tag(uint32_t fieldnum, char wire_type) { | |
136 return varint((fieldnum << 3) | wire_type); | |
137 } | |
138 | |
139 string submsg(uint32_t fn, const string& buf) { | |
140 return cat( tag(fn, WireFormatLite::WIRETYPE_LENGTH_DELIMITED), delim(buf) ); | |
141 } | |
142 | |
143 #define UNKNOWN_FIELD 666 | |
144 | |
145 uint32_t GetFieldNumberForType(FieldDescriptor::Type type, bool repeated) { | |
146 const Descriptor* d = TestAllTypes().GetDescriptor(); | |
147 for (int i = 0; i < d->field_count(); i++) { | |
148 const FieldDescriptor* f = d->field(i); | |
149 if (f->type() == type && f->is_repeated() == repeated) { | |
150 return f->number(); | |
151 } | |
152 } | |
153 GOOGLE_LOG(FATAL) << "Couldn't find field with type " << (int)type; | |
154 return 0; | |
155 } | |
156 | |
157 string UpperCase(string str) { | |
158 for (int i = 0; i < str.size(); i++) { | |
159 str[i] = toupper(str[i]); | |
160 } | |
161 return str; | |
162 } | |
163 | |
164 } // anonymous namespace | |
165 | |
166 namespace google { | |
167 namespace protobuf { | |
168 | |
169 void ConformanceTestSuite::ReportSuccess(const string& test_name) { | |
170 if (expected_to_fail_.erase(test_name) != 0) { | |
171 StringAppendF(&output_, | |
172 "ERROR: test %s is in the failure list, but test succeeded. " | |
173 "Remove it from the failure list.\n", | |
174 test_name.c_str()); | |
175 unexpected_succeeding_tests_.insert(test_name); | |
176 } | |
177 successes_++; | |
178 } | |
179 | |
180 void ConformanceTestSuite::ReportFailure(const string& test_name, | |
181 const ConformanceRequest& request, | |
182 const ConformanceResponse& response, | |
183 const char* fmt, ...) { | |
184 if (expected_to_fail_.erase(test_name) == 1) { | |
185 expected_failures_++; | |
186 if (!verbose_) | |
187 return; | |
188 } else { | |
189 StringAppendF(&output_, "ERROR, test=%s: ", test_name.c_str()); | |
190 unexpected_failing_tests_.insert(test_name); | |
191 } | |
192 va_list args; | |
193 va_start(args, fmt); | |
194 StringAppendV(&output_, fmt, args); | |
195 va_end(args); | |
196 StringAppendF(&output_, " request=%s, response=%s\n", | |
197 request.ShortDebugString().c_str(), | |
198 response.ShortDebugString().c_str()); | |
199 } | |
200 | |
201 void ConformanceTestSuite::ReportSkip(const string& test_name, | |
202 const ConformanceRequest& request, | |
203 const ConformanceResponse& response) { | |
204 if (verbose_) { | |
205 StringAppendF(&output_, "SKIPPED, test=%s request=%s, response=%s\n", | |
206 test_name.c_str(), request.ShortDebugString().c_str(), | |
207 response.ShortDebugString().c_str()); | |
208 } | |
209 skipped_.insert(test_name); | |
210 } | |
211 | |
212 void ConformanceTestSuite::RunTest(const string& test_name, | |
213 const ConformanceRequest& request, | |
214 ConformanceResponse* response) { | |
215 if (test_names_.insert(test_name).second == false) { | |
216 GOOGLE_LOG(FATAL) << "Duplicated test name: " << test_name; | |
217 } | |
218 | |
219 string serialized_request; | |
220 string serialized_response; | |
221 request.SerializeToString(&serialized_request); | |
222 | |
223 runner_->RunTest(serialized_request, &serialized_response); | |
224 | |
225 if (!response->ParseFromString(serialized_response)) { | |
226 response->Clear(); | |
227 response->set_runtime_error("response proto could not be parsed."); | |
228 } | |
229 | |
230 if (verbose_) { | |
231 StringAppendF(&output_, "conformance test: name=%s, request=%s, response=%s\
n", | |
232 test_name.c_str(), | |
233 request.ShortDebugString().c_str(), | |
234 response->ShortDebugString().c_str()); | |
235 } | |
236 } | |
237 | |
238 void ConformanceTestSuite::RunValidInputTest( | |
239 const string& test_name, const string& input, WireFormat input_format, | |
240 const string& equivalent_text_format, WireFormat requested_output) { | |
241 TestAllTypes reference_message; | |
242 GOOGLE_CHECK( | |
243 TextFormat::ParseFromString(equivalent_text_format, &reference_message)); | |
244 | |
245 ConformanceRequest request; | |
246 ConformanceResponse response; | |
247 | |
248 switch (input_format) { | |
249 case conformance::PROTOBUF: | |
250 request.set_protobuf_payload(input); | |
251 break; | |
252 | |
253 case conformance::JSON: | |
254 request.set_json_payload(input); | |
255 break; | |
256 | |
257 case conformance::UNSPECIFIED: | |
258 GOOGLE_LOG(FATAL) << "Unspecified input format"; | |
259 | |
260 } | |
261 | |
262 request.set_requested_output_format(requested_output); | |
263 | |
264 RunTest(test_name, request, &response); | |
265 | |
266 TestAllTypes test_message; | |
267 | |
268 switch (response.result_case()) { | |
269 case ConformanceResponse::kParseError: | |
270 case ConformanceResponse::kRuntimeError: | |
271 ReportFailure(test_name, request, response, | |
272 "Failed to parse valid JSON input."); | |
273 return; | |
274 | |
275 case ConformanceResponse::kSkipped: | |
276 ReportSkip(test_name, request, response); | |
277 return; | |
278 | |
279 case ConformanceResponse::kJsonPayload: { | |
280 if (requested_output != conformance::JSON) { | |
281 ReportFailure( | |
282 test_name, request, response, | |
283 "Test was asked for protobuf output but provided JSON instead."); | |
284 return; | |
285 } | |
286 string binary_protobuf; | |
287 Status status = | |
288 JsonToBinaryString(type_resolver_.get(), type_url_, | |
289 response.json_payload(), &binary_protobuf); | |
290 if (!status.ok()) { | |
291 ReportFailure(test_name, request, response, | |
292 "JSON output we received from test was unparseable."); | |
293 return; | |
294 } | |
295 | |
296 GOOGLE_CHECK(test_message.ParseFromString(binary_protobuf)); | |
297 break; | |
298 } | |
299 | |
300 case ConformanceResponse::kProtobufPayload: { | |
301 if (requested_output != conformance::PROTOBUF) { | |
302 ReportFailure( | |
303 test_name, request, response, | |
304 "Test was asked for JSON output but provided protobuf instead."); | |
305 return; | |
306 } | |
307 | |
308 if (!test_message.ParseFromString(response.protobuf_payload())) { | |
309 ReportFailure(test_name, request, response, | |
310 "Protobuf output we received from test was unparseable."); | |
311 return; | |
312 } | |
313 | |
314 break; | |
315 } | |
316 } | |
317 | |
318 MessageDifferencer differencer; | |
319 string differences; | |
320 differencer.ReportDifferencesToString(&differences); | |
321 | |
322 if (differencer.Equals(reference_message, test_message)) { | |
323 ReportSuccess(test_name); | |
324 } else { | |
325 ReportFailure(test_name, request, response, | |
326 "Output was not equivalent to reference message: %s.", | |
327 differences.c_str()); | |
328 } | |
329 } | |
330 | |
331 // Expect that this precise protobuf will cause a parse error. | |
332 void ConformanceTestSuite::ExpectParseFailureForProto( | |
333 const string& proto, const string& test_name) { | |
334 ConformanceRequest request; | |
335 ConformanceResponse response; | |
336 request.set_protobuf_payload(proto); | |
337 string effective_test_name = "ProtobufInput." + test_name; | |
338 | |
339 // We don't expect output, but if the program erroneously accepts the protobuf | |
340 // we let it send its response as this. We must not leave it unspecified. | |
341 request.set_requested_output_format(conformance::PROTOBUF); | |
342 | |
343 RunTest(effective_test_name, request, &response); | |
344 if (response.result_case() == ConformanceResponse::kParseError) { | |
345 ReportSuccess(effective_test_name); | |
346 } else { | |
347 ReportFailure(effective_test_name, request, response, | |
348 "Should have failed to parse, but didn't."); | |
349 } | |
350 } | |
351 | |
352 // Expect that this protobuf will cause a parse error, even if it is followed | |
353 // by valid protobuf data. We can try running this twice: once with this | |
354 // data verbatim and once with this data followed by some valid data. | |
355 // | |
356 // TODO(haberman): implement the second of these. | |
357 void ConformanceTestSuite::ExpectHardParseFailureForProto( | |
358 const string& proto, const string& test_name) { | |
359 return ExpectParseFailureForProto(proto, test_name); | |
360 } | |
361 | |
362 void ConformanceTestSuite::RunValidJsonTest( | |
363 const string& test_name, const string& input_json, | |
364 const string& equivalent_text_format) { | |
365 RunValidInputTest("JsonInput." + test_name + ".JsonOutput", input_json, | |
366 conformance::JSON, equivalent_text_format, | |
367 conformance::PROTOBUF); | |
368 RunValidInputTest("JsonInput." + test_name + ".ProtobufOutput", input_json, co
nformance::JSON, | |
369 equivalent_text_format, conformance::JSON); | |
370 } | |
371 | |
372 void ConformanceTestSuite::TestPrematureEOFForType(FieldDescriptor::Type type) { | |
373 // Incomplete values for each wire type. | |
374 static const string incompletes[6] = { | |
375 string("\x80"), // VARINT | |
376 string("abcdefg"), // 64BIT | |
377 string("\x80"), // DELIMITED (partial length) | |
378 string(), // START_GROUP (no value required) | |
379 string(), // END_GROUP (no value required) | |
380 string("abc") // 32BIT | |
381 }; | |
382 | |
383 uint32_t fieldnum = GetFieldNumberForType(type, false); | |
384 uint32_t rep_fieldnum = GetFieldNumberForType(type, true); | |
385 WireFormatLite::WireType wire_type = WireFormatLite::WireTypeForFieldType( | |
386 static_cast<WireFormatLite::FieldType>(type)); | |
387 const string& incomplete = incompletes[wire_type]; | |
388 const string type_name = | |
389 UpperCase(string(".") + FieldDescriptor::TypeName(type)); | |
390 | |
391 ExpectParseFailureForProto( | |
392 tag(fieldnum, wire_type), | |
393 "PrematureEofBeforeKnownNonRepeatedValue" + type_name); | |
394 | |
395 ExpectParseFailureForProto( | |
396 tag(rep_fieldnum, wire_type), | |
397 "PrematureEofBeforeKnownRepeatedValue" + type_name); | |
398 | |
399 ExpectParseFailureForProto( | |
400 tag(UNKNOWN_FIELD, wire_type), | |
401 "PrematureEofBeforeUnknownValue" + type_name); | |
402 | |
403 ExpectParseFailureForProto( | |
404 cat( tag(fieldnum, wire_type), incomplete ), | |
405 "PrematureEofInsideKnownNonRepeatedValue" + type_name); | |
406 | |
407 ExpectParseFailureForProto( | |
408 cat( tag(rep_fieldnum, wire_type), incomplete ), | |
409 "PrematureEofInsideKnownRepeatedValue" + type_name); | |
410 | |
411 ExpectParseFailureForProto( | |
412 cat( tag(UNKNOWN_FIELD, wire_type), incomplete ), | |
413 "PrematureEofInsideUnknownValue" + type_name); | |
414 | |
415 if (wire_type == WireFormatLite::WIRETYPE_LENGTH_DELIMITED) { | |
416 ExpectParseFailureForProto( | |
417 cat( tag(fieldnum, wire_type), varint(1) ), | |
418 "PrematureEofInDelimitedDataForKnownNonRepeatedValue" + type_name); | |
419 | |
420 ExpectParseFailureForProto( | |
421 cat( tag(rep_fieldnum, wire_type), varint(1) ), | |
422 "PrematureEofInDelimitedDataForKnownRepeatedValue" + type_name); | |
423 | |
424 // EOF in the middle of delimited data for unknown value. | |
425 ExpectParseFailureForProto( | |
426 cat( tag(UNKNOWN_FIELD, wire_type), varint(1) ), | |
427 "PrematureEofInDelimitedDataForUnknownValue" + type_name); | |
428 | |
429 if (type == FieldDescriptor::TYPE_MESSAGE) { | |
430 // Submessage ends in the middle of a value. | |
431 string incomplete_submsg = | |
432 cat( tag(WireFormatLite::TYPE_INT32, WireFormatLite::WIRETYPE_VARINT), | |
433 incompletes[WireFormatLite::WIRETYPE_VARINT] ); | |
434 ExpectHardParseFailureForProto( | |
435 cat( tag(fieldnum, WireFormatLite::WIRETYPE_LENGTH_DELIMITED), | |
436 varint(incomplete_submsg.size()), | |
437 incomplete_submsg ), | |
438 "PrematureEofInSubmessageValue" + type_name); | |
439 } | |
440 } else if (type != FieldDescriptor::TYPE_GROUP) { | |
441 // Non-delimited, non-group: eligible for packing. | |
442 | |
443 // Packed region ends in the middle of a value. | |
444 ExpectHardParseFailureForProto( | |
445 cat( tag(rep_fieldnum, WireFormatLite::WIRETYPE_LENGTH_DELIMITED), | |
446 varint(incomplete.size()), | |
447 incomplete ), | |
448 "PrematureEofInPackedFieldValue" + type_name); | |
449 | |
450 // EOF in the middle of packed region. | |
451 ExpectParseFailureForProto( | |
452 cat( tag(rep_fieldnum, WireFormatLite::WIRETYPE_LENGTH_DELIMITED), | |
453 varint(1) ), | |
454 "PrematureEofInPackedField" + type_name); | |
455 } | |
456 } | |
457 | |
458 void ConformanceTestSuite::SetFailureList(const vector<string>& failure_list) { | |
459 expected_to_fail_.clear(); | |
460 std::copy(failure_list.begin(), failure_list.end(), | |
461 std::inserter(expected_to_fail_, expected_to_fail_.end())); | |
462 } | |
463 | |
464 bool ConformanceTestSuite::CheckSetEmpty(const set<string>& set_to_check, | |
465 const char* msg) { | |
466 if (set_to_check.empty()) { | |
467 return true; | |
468 } else { | |
469 StringAppendF(&output_, "\n"); | |
470 StringAppendF(&output_, "%s:\n", msg); | |
471 for (set<string>::const_iterator iter = set_to_check.begin(); | |
472 iter != set_to_check.end(); ++iter) { | |
473 StringAppendF(&output_, " %s\n", iter->c_str()); | |
474 } | |
475 StringAppendF(&output_, "\n"); | |
476 return false; | |
477 } | |
478 } | |
479 | |
480 bool ConformanceTestSuite::RunSuite(ConformanceTestRunner* runner, | |
481 std::string* output) { | |
482 runner_ = runner; | |
483 successes_ = 0; | |
484 expected_failures_ = 0; | |
485 skipped_.clear(); | |
486 test_names_.clear(); | |
487 unexpected_failing_tests_.clear(); | |
488 unexpected_succeeding_tests_.clear(); | |
489 type_resolver_.reset(NewTypeResolverForDescriptorPool( | |
490 kTypeUrlPrefix, DescriptorPool::generated_pool())); | |
491 type_url_ = GetTypeUrl(TestAllTypes::descriptor()); | |
492 | |
493 output_ = "\nCONFORMANCE TEST BEGIN ====================================\n\n"; | |
494 | |
495 for (int i = 1; i <= FieldDescriptor::MAX_TYPE; i++) { | |
496 if (i == FieldDescriptor::TYPE_GROUP) continue; | |
497 TestPrematureEOFForType(static_cast<FieldDescriptor::Type>(i)); | |
498 } | |
499 | |
500 RunValidJsonTest("HelloWorld", "{\"optionalString\":\"Hello, World!\"}", | |
501 "optional_string: 'Hello, World!'"); | |
502 | |
503 bool ok = | |
504 CheckSetEmpty(expected_to_fail_, | |
505 "These tests were listed in the failure list, but they " | |
506 "don't exist. Remove them from the failure list") && | |
507 | |
508 CheckSetEmpty(unexpected_failing_tests_, | |
509 "These tests failed. If they can't be fixed right now, " | |
510 "you can add them to the failure list so the overall " | |
511 "suite can succeed") && | |
512 | |
513 CheckSetEmpty(unexpected_succeeding_tests_, | |
514 "These tests succeeded, even though they were listed in " | |
515 "the failure list. Remove them from the failure list"); | |
516 | |
517 CheckSetEmpty(skipped_, | |
518 "These tests were skipped (probably because support for some " | |
519 "features is not implemented)"); | |
520 | |
521 StringAppendF(&output_, | |
522 "CONFORMANCE SUITE %s: %d successes, %d skipped, " | |
523 "%d expected failures, %d unexpected failures.\n", | |
524 ok ? "PASSED" : "FAILED", successes_, skipped_.size(), | |
525 expected_failures_, unexpected_failing_tests_.size()); | |
526 StringAppendF(&output_, "\n"); | |
527 | |
528 output->assign(output_); | |
529 | |
530 return ok; | |
531 } | |
532 | |
533 } // namespace protobuf | |
534 } // namespace google | |
OLD | NEW |