Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(183)

Side by Side Diff: third_party/protobuf/conformance/conformance_test.cc

Issue 1322483002: Revert https://codereview.chromium.org/1291903002 (protobuf roll). (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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
OLDNEW
« no previous file with comments | « third_party/protobuf/conformance/conformance_test.h ('k') | third_party/protobuf/conformance/conformance_test_runner.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698