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 "text_format_conformance_suite.h"
32
33 #include <google/protobuf/any.pb.h>
34 #include <google/protobuf/text_format.h>
35 #include "conformance_test.h"
36 #include <google/protobuf/test_messages_proto2.pb.h>
37 #include <google/protobuf/test_messages_proto3.pb.h>
38
39 namespace proto2_messages = protobuf_test_messages::proto2;
40
41 using conformance::ConformanceRequest;
42 using conformance::ConformanceResponse;
43 using conformance::WireFormat;
44 using google::protobuf::Message;
45 using google::protobuf::TextFormat;
46 using proto2_messages::TestAllTypesProto2;
47 using proto2_messages::UnknownToTestAllTypes;
48 using protobuf_test_messages::proto3::TestAllTypesProto3;
49 using std::string;
50
51 namespace google {
52 namespace protobuf {
53
TextFormatConformanceTestSuite()54 TextFormatConformanceTestSuite::TextFormatConformanceTestSuite() {
55 SetFailureListFlagName("--text_format_failure_list");
56 }
57
ParseTextFormatResponse(const ConformanceResponse & response,const ConformanceRequestSetting & setting,Message * test_message)58 bool TextFormatConformanceTestSuite::ParseTextFormatResponse(
59 const ConformanceResponse& response,
60 const ConformanceRequestSetting& setting, Message* test_message) {
61 TextFormat::Parser parser;
62 const ConformanceRequest& request = setting.GetRequest();
63 if (request.print_unknown_fields()) {
64 parser.AllowFieldNumber(true);
65 }
66 if (!parser.ParseFromString(response.text_payload(), test_message)) {
67 GOOGLE_LOG(ERROR) << "INTERNAL ERROR: internal text->protobuf transcode "
68 << "yielded unparseable proto. Text payload: "
69 << response.text_payload();
70 return false;
71 }
72
73 return true;
74 }
75
ParseResponse(const ConformanceResponse & response,const ConformanceRequestSetting & setting,Message * test_message)76 bool TextFormatConformanceTestSuite::ParseResponse(
77 const ConformanceResponse& response,
78 const ConformanceRequestSetting& setting, Message* test_message) {
79 const ConformanceRequest& request = setting.GetRequest();
80 WireFormat requested_output = request.requested_output_format();
81 const string& test_name = setting.GetTestName();
82 ConformanceLevel level = setting.GetLevel();
83
84 switch (response.result_case()) {
85 case ConformanceResponse::kProtobufPayload: {
86 if (requested_output != conformance::PROTOBUF) {
87 ReportFailure(test_name, level, request, response,
88 StrCat("Test was asked for ",
89 WireFormatToString(requested_output),
90 " output but provided PROTOBUF instead.")
91 .c_str());
92 return false;
93 }
94
95 if (!test_message->ParseFromString(response.protobuf_payload())) {
96 ReportFailure(test_name, level, request, response,
97 "Protobuf output we received from test was unparseable.");
98 return false;
99 }
100
101 break;
102 }
103
104 case ConformanceResponse::kTextPayload: {
105 if (requested_output != conformance::TEXT_FORMAT) {
106 ReportFailure(test_name, level, request, response,
107 StrCat("Test was asked for ",
108 WireFormatToString(requested_output),
109 " output but provided TEXT_FORMAT instead.")
110 .c_str());
111 return false;
112 }
113
114 if (!ParseTextFormatResponse(response, setting, test_message)) {
115 ReportFailure(
116 test_name, level, request, response,
117 "TEXT_FORMAT output we received from test was unparseable.");
118 return false;
119 }
120
121 break;
122 }
123
124 default:
125 GOOGLE_LOG(FATAL) << test_name
126 << ": unknown payload type: " << response.result_case();
127 }
128
129 return true;
130 }
131
ExpectParseFailure(const string & test_name,ConformanceLevel level,const string & input)132 void TextFormatConformanceTestSuite::ExpectParseFailure(const string& test_name,
133 ConformanceLevel level,
134 const string& input) {
135 TestAllTypesProto3 prototype;
136 // We don't expect output, but if the program erroneously accepts the protobuf
137 // we let it send its response as this. We must not leave it unspecified.
138 ConformanceRequestSetting setting(
139 level, conformance::TEXT_FORMAT, conformance::TEXT_FORMAT,
140 conformance::TEXT_FORMAT_TEST, prototype, test_name, input);
141 const ConformanceRequest& request = setting.GetRequest();
142 ConformanceResponse response;
143 string effective_test_name =
144 StrCat(setting.ConformanceLevelToString(level),
145 ".Proto3.TextFormatInput.", test_name);
146
147 RunTest(effective_test_name, request, &response);
148 if (response.result_case() == ConformanceResponse::kParseError) {
149 ReportSuccess(effective_test_name);
150 } else if (response.result_case() == ConformanceResponse::kSkipped) {
151 ReportSkip(effective_test_name, request, response);
152 } else {
153 ReportFailure(effective_test_name, level, request, response,
154 "Should have failed to parse, but didn't.");
155 }
156 }
157
RunValidTextFormatTest(const string & test_name,ConformanceLevel level,const string & input_text)158 void TextFormatConformanceTestSuite::RunValidTextFormatTest(
159 const string& test_name, ConformanceLevel level, const string& input_text) {
160 TestAllTypesProto3 prototype;
161 RunValidTextFormatTestWithMessage(test_name, level, input_text, prototype);
162 }
163
RunValidTextFormatTestProto2(const string & test_name,ConformanceLevel level,const string & input_text)164 void TextFormatConformanceTestSuite::RunValidTextFormatTestProto2(
165 const string& test_name, ConformanceLevel level, const string& input_text) {
166 TestAllTypesProto2 prototype;
167 RunValidTextFormatTestWithMessage(test_name, level, input_text, prototype);
168 }
169
RunValidTextFormatTestWithMessage(const string & test_name,ConformanceLevel level,const string & input_text,const Message & prototype)170 void TextFormatConformanceTestSuite::RunValidTextFormatTestWithMessage(
171 const string& test_name, ConformanceLevel level, const string& input_text,
172 const Message& prototype) {
173 ConformanceRequestSetting setting1(
174 level, conformance::TEXT_FORMAT, conformance::PROTOBUF,
175 conformance::TEXT_FORMAT_TEST, prototype, test_name, input_text);
176 RunValidInputTest(setting1, input_text);
177 ConformanceRequestSetting setting2(
178 level, conformance::TEXT_FORMAT, conformance::TEXT_FORMAT,
179 conformance::TEXT_FORMAT_TEST, prototype, test_name, input_text);
180 RunValidInputTest(setting2, input_text);
181 }
182
RunValidUnknownTextFormatTest(const string & test_name,const Message & message)183 void TextFormatConformanceTestSuite::RunValidUnknownTextFormatTest(
184 const string& test_name, const Message& message) {
185 string serialized_input;
186 message.SerializeToString(&serialized_input);
187 TestAllTypesProto3 prototype;
188 ConformanceRequestSetting setting1(
189 RECOMMENDED, conformance::PROTOBUF, conformance::TEXT_FORMAT,
190 conformance::TEXT_FORMAT_TEST, prototype, test_name + "_Drop",
191 serialized_input);
192 setting1.SetPrototypeMessageForCompare(message);
193 RunValidBinaryInputTest(setting1, "");
194
195 ConformanceRequestSetting setting2(
196 RECOMMENDED, conformance::PROTOBUF, conformance::TEXT_FORMAT,
197 conformance::TEXT_FORMAT_TEST, prototype, test_name + "_Print",
198 serialized_input);
199 setting2.SetPrototypeMessageForCompare(message);
200 setting2.SetPrintUnknownFields(true);
201 RunValidBinaryInputTest(setting2, serialized_input);
202 }
203
RunSuiteImpl()204 void TextFormatConformanceTestSuite::RunSuiteImpl() {
205 RunValidTextFormatTest("HelloWorld", REQUIRED,
206 "optional_string: 'Hello, World!'");
207 // Integer fields.
208 RunValidTextFormatTest("Int32FieldMaxValue", REQUIRED,
209 "optional_int32: 2147483647");
210 RunValidTextFormatTest("Int32FieldMinValue", REQUIRED,
211 "optional_int32: -2147483648");
212 RunValidTextFormatTest("Uint32FieldMaxValue", REQUIRED,
213 "optional_uint32: 4294967295");
214 RunValidTextFormatTest("Int64FieldMaxValue", REQUIRED,
215 "optional_int64: 9223372036854775807");
216 RunValidTextFormatTest("Int64FieldMinValue", REQUIRED,
217 "optional_int64: -9223372036854775808");
218 RunValidTextFormatTest("Uint64FieldMaxValue", REQUIRED,
219 "optional_uint64: 18446744073709551615");
220
221 // Parsers reject out-of-bound integer values.
222 ExpectParseFailure("Int32FieldTooLarge", REQUIRED,
223 "optional_int32: 2147483648");
224 ExpectParseFailure("Int32FieldTooSmall", REQUIRED,
225 "optional_int32: -2147483649");
226 ExpectParseFailure("Uint32FieldTooLarge", REQUIRED,
227 "optional_uint32: 4294967296");
228 ExpectParseFailure("Int64FieldTooLarge", REQUIRED,
229 "optional_int64: 9223372036854775808");
230 ExpectParseFailure("Int64FieldTooSmall", REQUIRED,
231 "optional_int64: -9223372036854775809");
232 ExpectParseFailure("Uint64FieldTooLarge", REQUIRED,
233 "optional_uint64: 18446744073709551616");
234
235 // Floating point fields
236 RunValidTextFormatTest("FloatField", REQUIRED,
237 "optional_float: 3.192837");
238 RunValidTextFormatTest("FloatFieldWithVeryPreciseNumber", REQUIRED,
239 "optional_float: 3.123456789123456789");
240 RunValidTextFormatTest("FloatFieldMaxValue", REQUIRED,
241 "optional_float: 3.4028235e+38");
242 RunValidTextFormatTest("FloatFieldMinValue", REQUIRED,
243 "optional_float: 1.17549e-38");
244 RunValidTextFormatTest("FloatFieldNaNValue", REQUIRED,
245 "optional_float: NaN");
246 RunValidTextFormatTest("FloatFieldPosInfValue", REQUIRED,
247 "optional_float: inf");
248 RunValidTextFormatTest("FloatFieldNegInfValue", REQUIRED,
249 "optional_float: -inf");
250 RunValidTextFormatTest("FloatFieldWithInt32Max", REQUIRED,
251 "optional_float: 4294967296");
252 RunValidTextFormatTest("FloatFieldLargerThanInt64", REQUIRED,
253 "optional_float: 9223372036854775808");
254 RunValidTextFormatTest("FloatFieldTooLarge", REQUIRED,
255 "optional_float: 3.4028235e+39");
256 RunValidTextFormatTest("FloatFieldTooSmall", REQUIRED,
257 "optional_float: 1.17549e-39");
258 RunValidTextFormatTest("FloatFieldLargerThanUint64", REQUIRED,
259 "optional_float: 18446744073709551616");
260
261 // Group fields
262 RunValidTextFormatTestProto2("GroupFieldNoColon", REQUIRED,
263 "Data { group_int32: 1 }");
264 RunValidTextFormatTestProto2("GroupFieldWithColon", REQUIRED,
265 "Data: { group_int32: 1 }");
266 RunValidTextFormatTestProto2("GroupFieldEmpty", REQUIRED,
267 "Data {}");
268
269
270 // Unknown Fields
271 UnknownToTestAllTypes message;
272 // Unable to print unknown Fixed32/Fixed64 fields as if they are known.
273 // Fixed32/Fixed64 fields are not added in the tests.
274 message.set_optional_int32(123);
275 message.set_optional_string("hello");
276 message.set_optional_bool(true);
277 RunValidUnknownTextFormatTest("ScalarUnknownFields", message);
278
279 message.Clear();
280 message.mutable_nested_message()->set_c(111);
281 RunValidUnknownTextFormatTest("MessageUnknownFields", message);
282
283 message.Clear();
284 message.mutable_optionalgroup()->set_a(321);
285 RunValidUnknownTextFormatTest("GroupUnknownFields", message);
286
287 message.add_repeated_int32(1);
288 message.add_repeated_int32(2);
289 message.add_repeated_int32(3);
290 RunValidUnknownTextFormatTest("RepeatedUnknownFields", message);
291
292 // Any fields
293 RunValidTextFormatTest("AnyField", REQUIRED,
294 R"(
295 optional_any: {
296 [type.googleapis.com/protobuf_test_messages.proto3.TestAllTypesProto3] {
297 optional_int32: 12345
298 }
299 }
300 )");
301 RunValidTextFormatTest("AnyFieldWithRawBytes", REQUIRED,
302 R"(
303 optional_any: {
304 type_url: "type.googleapis.com/protobuf_test_messages.proto3.TestAllTypesProto3"
305 value: "\b\271`"
306 }
307 )");
308 ExpectParseFailure("AnyFieldWithInvalidType", REQUIRED,
309 R"(
310 optional_any: {
311 [type.googleapis.com/unknown] {
312 optional_int32: 12345
313 }
314 }
315 )");
316
317 // Map fields
318 TestAllTypesProto3 prototype;
319 (*prototype.mutable_map_string_string())["c"] = "value";
320 (*prototype.mutable_map_string_string())["b"] = "value";
321 (*prototype.mutable_map_string_string())["a"] = "value";
322 RunValidTextFormatTestWithMessage("AlphabeticallySortedMapStringKeys",
323 REQUIRED,
324 R"(
325 map_string_string {
326 key: "a"
327 value: "value"
328 }
329 map_string_string {
330 key: "b"
331 value: "value"
332 }
333 map_string_string {
334 key: "c"
335 value: "value"
336 }
337 )",
338 prototype);
339
340 prototype.Clear();
341 (*prototype.mutable_map_int32_int32())[3] = 0;
342 (*prototype.mutable_map_int32_int32())[2] = 0;
343 (*prototype.mutable_map_int32_int32())[1] = 0;
344 RunValidTextFormatTestWithMessage("AlphabeticallySortedMapIntKeys", REQUIRED,
345 R"(
346 map_int32_int32 {
347 key: 1
348 value: 0
349 }
350 map_int32_int32 {
351 key: 2
352 value: 0
353 }
354 map_int32_int32 {
355 key: 3
356 value: 0
357 }
358 )",
359 prototype);
360
361 prototype.Clear();
362 (*prototype.mutable_map_bool_bool())[true] = false;
363 (*prototype.mutable_map_bool_bool())[false] = false;
364 RunValidTextFormatTestWithMessage("AlphabeticallySortedMapBoolKeys", REQUIRED,
365 R"(
366 map_bool_bool {
367 key: false
368 value: false
369 }
370 map_bool_bool {
371 key: true
372 value: false
373 }
374 )",
375 prototype);
376 }
377
378 } // namespace protobuf
379 } // namespace google
380