blob: 8d30b96ccfce9d05eb3891eb6d11ff3c0c99db95 [file] [log] [blame]
Jon Skeet68036862008-10-22 13:30:34 +01001// Protocol Buffers - Google's data interchange format
2// Copyright 2008 Google Inc.
3// http://code.google.com/p/protobuf/
4//
5// Licensed under the Apache License, Version 2.0 (the "License");
6// you may not use this file except in compliance with the License.
7// You may obtain a copy of the License at
8//
9// http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing, software
12// distributed under the License is distributed on an "AS IS" BASIS,
13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14// See the License for the specific language governing permissions and
15// limitations under the License.
16using System;
17using System.IO;
18using System.Text;
19using Google.ProtocolBuffers.TestProtos;
20using NUnit.Framework;
21
22namespace Google.ProtocolBuffers {
23 [TestFixture]
24 public class TextFormatTest {
25
26 private static readonly string AllFieldsSetText = TestUtil.ReadTextFromFile("text_format_unittest_data.txt");
27 private static readonly string AllExtensionsSetText = TestUtil.ReadTextFromFile("text_format_unittest_extensions_data.txt");
28
29 /// <summary>
30 /// Note that this is slightly different to the Java - 123.0 becomes 123, and 1.23E17 becomes 1.23E+17.
31 /// Both of these differences can be parsed by the Java and the C++, and we can parse their output too.
32 /// </summary>
33 private const string ExoticText =
34 "repeated_int32: -1\n" +
35 "repeated_int32: -2147483648\n" +
36 "repeated_int64: -1\n" +
37 "repeated_int64: -9223372036854775808\n" +
38 "repeated_uint32: 4294967295\n" +
39 "repeated_uint32: 2147483648\n" +
40 "repeated_uint64: 18446744073709551615\n" +
41 "repeated_uint64: 9223372036854775808\n" +
42 "repeated_double: 123\n" +
43 "repeated_double: 123.5\n" +
44 "repeated_double: 0.125\n" +
45 "repeated_double: 1.23E+17\n" +
46 "repeated_double: 1.235E+22\n" +
47 "repeated_double: 1.235E-18\n" +
48 "repeated_double: 123.456789\n" +
49 "repeated_double: Infinity\n" +
50 "repeated_double: -Infinity\n" +
51 "repeated_double: NaN\n" +
52 "repeated_string: \"\\000\\001\\a\\b\\f\\n\\r\\t\\v\\\\\\'\\\"" +
53 "\\341\\210\\264\"\n" +
54 "repeated_bytes: \"\\000\\001\\a\\b\\f\\n\\r\\t\\v\\\\\\'\\\"\\376\"\n";
55
56 private const string MessageSetText =
57 "[protobuf_unittest.TestMessageSetExtension1] {\n" +
58 " i: 123\n" +
59 "}\n" +
60 "[protobuf_unittest.TestMessageSetExtension2] {\n" +
61 " str: \"foo\"\n" +
62 "}\n";
63
64 /// <summary>
65 /// Print TestAllTypes and compare with golden file.
66 /// </summary>
67 [Test]
68 public void PrintMessage() {
69 string text = TextFormat.PrintToString(TestUtil.GetAllSet());
70 Assert.AreEqual(AllFieldsSetText.Replace("\r\n", "\n"), text.Replace("\r\n", "\n"));
71 }
72
73 /// <summary>
74 /// Print TestAllExtensions and compare with golden file.
75 /// </summary>
76 [Test]
77 public void PrintExtensions() {
78 string text = TextFormat.PrintToString(TestUtil.GetAllExtensionsSet());
79
80 Assert.AreEqual(AllExtensionsSetText.Replace("\r\n", "\n"), text.Replace("\r\n", "\n"));
81 }
82
83 /// <summary>
84 /// Test printing of unknown fields in a message.
85 /// </summary>
86 [Test]
87 public void PrintUnknownFields() {
88 TestEmptyMessage message =
89 TestEmptyMessage.CreateBuilder()
90 .SetUnknownFields(
91 UnknownFieldSet.CreateBuilder()
92 .AddField(5,
93 UnknownField.CreateBuilder()
94 .AddVarint(1)
95 .AddFixed32(2)
96 .AddFixed64(3)
97 .AddLengthDelimited(ByteString.CopyFromUtf8("4"))
98 .AddGroup(
99 UnknownFieldSet.CreateBuilder()
100 .AddField(10,
101 UnknownField.CreateBuilder()
102 .AddVarint(5)
103 .Build())
104 .Build())
105 .Build())
106 .AddField(8,
107 UnknownField.CreateBuilder()
108 .AddVarint(1)
109 .AddVarint(2)
110 .AddVarint(3)
111 .Build())
112 .AddField(15,
113 UnknownField.CreateBuilder()
114 .AddVarint(0xABCDEF1234567890L)
115 .AddFixed32(0xABCD1234)
116 .AddFixed64(0xABCDEF1234567890L)
117 .Build())
118 .Build())
119 .Build();
120
121 Assert.AreEqual(
122 "5: 1\n" +
123 "5: 0x00000002\n" +
124 "5: 0x0000000000000003\n" +
125 "5: \"4\"\n" +
126 "5 {\n" +
127 " 10: 5\n" +
128 "}\n" +
129 "8: 1\n" +
130 "8: 2\n" +
131 "8: 3\n" +
132 "15: 12379813812177893520\n" +
133 "15: 0xabcd1234\n" +
134 "15: 0xabcdef1234567890\n",
135 TextFormat.PrintToString(message));
136 }
137
138 /// <summary>
139 /// Helper to construct a ByteString from a string containing only 8-bit
140 /// characters. The characters are converted directly to bytes, *not*
141 /// encoded using UTF-8.
142 /// </summary>
143 private static ByteString Bytes(string str) {
144 return ByteString.CopyFrom(Encoding.GetEncoding(28591).GetBytes(str));
145 }
146
147 [Test]
148 public void PrintExotic() {
149 IMessage message = TestAllTypes.CreateBuilder()
150 // Signed vs. unsigned numbers.
151 .AddRepeatedInt32 (-1)
152 .AddRepeatedUint32(uint.MaxValue)
153 .AddRepeatedInt64 (-1)
154 .AddRepeatedUint64(ulong.MaxValue)
155
156 .AddRepeatedInt32 (1 << 31)
157 .AddRepeatedUint32(1U << 31)
158 .AddRepeatedInt64 (1L << 63)
159 .AddRepeatedUint64(1UL << 63)
160
161 // Floats of various precisions and exponents.
162 .AddRepeatedDouble(123)
163 .AddRepeatedDouble(123.5)
164 .AddRepeatedDouble(0.125)
165 .AddRepeatedDouble(123e15)
166 .AddRepeatedDouble(123.5e20)
167 .AddRepeatedDouble(123.5e-20)
168 .AddRepeatedDouble(123.456789)
169 .AddRepeatedDouble(Double.PositiveInfinity)
170 .AddRepeatedDouble(Double.NegativeInfinity)
171 .AddRepeatedDouble(Double.NaN)
172
173 // Strings and bytes that needing escaping.
174 .AddRepeatedString("\0\u0001\u0007\b\f\n\r\t\v\\\'\"\u1234")
175 .AddRepeatedBytes(Bytes("\0\u0001\u0007\b\f\n\r\t\v\\\'\"\u00fe"))
176 .Build();
177
178 Assert.AreEqual(ExoticText, message.ToString());
179 }
180
181 [Test]
182 public void PrintMessageSet() {
183 TestMessageSet messageSet =
184 TestMessageSet.CreateBuilder()
185 .SetExtension(
186 TestMessageSetExtension1.MessageSetExtension,
187 TestMessageSetExtension1.CreateBuilder().SetI(123).Build())
188 .SetExtension(
189 TestMessageSetExtension2.MessageSetExtension,
190 TestMessageSetExtension2.CreateBuilder().SetStr("foo").Build())
191 .Build();
192
193 Assert.AreEqual(MessageSetText, messageSet.ToString());
194 }
195
196 // =================================================================
197
198 [Test]
199 public void Parse() {
200 TestAllTypes.Builder builder = TestAllTypes.CreateBuilder();
201 TextFormat.Merge(AllFieldsSetText, builder);
202 TestUtil.AssertAllFieldsSet(builder.Build());
203 }
204
205 [Test]
206 public void ParseReader() {
207 TestAllTypes.Builder builder = TestAllTypes.CreateBuilder();
208 TextFormat.Merge(new StringReader(AllFieldsSetText), builder);
209 TestUtil.AssertAllFieldsSet(builder.Build());
210 }
211
212 [Test]
213 public void ParseExtensions() {
214 TestAllExtensions.Builder builder = TestAllExtensions.CreateBuilder();
215 TextFormat.Merge(AllExtensionsSetText,
216 TestUtil.CreateExtensionRegistry(),
217 builder);
218 TestUtil.AssertAllExtensionsSet(builder.Build());
219 }
220
221 [Test]
222 public void ParseCompatibility() {
223 string original = "repeated_float: inf\n" +
224 "repeated_float: -inf\n" +
225 "repeated_float: nan\n" +
226 "repeated_float: inff\n" +
227 "repeated_float: -inff\n" +
228 "repeated_float: nanf\n" +
229 "repeated_float: 1.0f\n" +
230 "repeated_float: infinityf\n" +
231 "repeated_float: -Infinityf\n" +
232 "repeated_double: infinity\n" +
233 "repeated_double: -infinity\n" +
234 "repeated_double: nan\n";
235 string canonical = "repeated_float: Infinity\n" +
236 "repeated_float: -Infinity\n" +
237 "repeated_float: NaN\n" +
238 "repeated_float: Infinity\n" +
239 "repeated_float: -Infinity\n" +
240 "repeated_float: NaN\n" +
241 "repeated_float: 1\n" + // Java has 1.0; this is fine
242 "repeated_float: Infinity\n" +
243 "repeated_float: -Infinity\n" +
244 "repeated_double: Infinity\n" +
245 "repeated_double: -Infinity\n" +
246 "repeated_double: NaN\n";
247 TestAllTypes.Builder builder = TestAllTypes.CreateBuilder();
248 TextFormat.Merge(original, builder);
249 Assert.AreEqual(canonical, builder.Build().ToString());
250 }
251
252 [Test]
253 public void ParseExotic() {
254 TestAllTypes.Builder builder = TestAllTypes.CreateBuilder();
255 TextFormat.Merge(ExoticText, builder);
256
257 // Too lazy to check things individually. Don't try to debug this
258 // if testPrintExotic() is Assert.Failing.
259 Assert.AreEqual(ExoticText, builder.Build().ToString());
260 }
261
262 [Test]
263 public void ParseMessageSet() {
264 ExtensionRegistry extensionRegistry = ExtensionRegistry.CreateInstance();
265 extensionRegistry.Add(TestMessageSetExtension1.MessageSetExtension);
266 extensionRegistry.Add(TestMessageSetExtension2.MessageSetExtension);
267
268 TestMessageSet.Builder builder = TestMessageSet.CreateBuilder();
269 TextFormat.Merge(MessageSetText, extensionRegistry, builder);
270 TestMessageSet messageSet = builder.Build();
271
272 Assert.IsTrue(messageSet.HasExtension(TestMessageSetExtension1.MessageSetExtension));
273 Assert.AreEqual(123, messageSet.GetExtension(TestMessageSetExtension1.MessageSetExtension).I);
274 Assert.IsTrue(messageSet.HasExtension(TestMessageSetExtension2.MessageSetExtension));
275 Assert.AreEqual("foo", messageSet.GetExtension(TestMessageSetExtension2.MessageSetExtension).Str);
276 }
277
278 [Test]
279 public void ParseNumericEnum() {
280 TestAllTypes.Builder builder = TestAllTypes.CreateBuilder();
281 TextFormat.Merge("optional_nested_enum: 2", builder);
282 Assert.AreEqual(TestAllTypes.Types.NestedEnum.BAR, builder.OptionalNestedEnum);
283 }
284
285 [Test]
286 public void ParseAngleBrackets() {
287 TestAllTypes.Builder builder = TestAllTypes.CreateBuilder();
288 TextFormat.Merge("OptionalGroup: < a: 1 >", builder);
289 Assert.IsTrue(builder.HasOptionalGroup);
290 Assert.AreEqual(1, builder.OptionalGroup.A);
291 }
292
293 [Test]
294 public void ParseComment() {
295 TestAllTypes.Builder builder = TestAllTypes.CreateBuilder();
296 TextFormat.Merge(
297 "# this is a comment\n" +
298 "optional_int32: 1 # another comment\n" +
299 "optional_int64: 2\n" +
300 "# EOF comment", builder);
301 Assert.AreEqual(1, builder.OptionalInt32);
302 Assert.AreEqual(2, builder.OptionalInt64);
303 }
304
305
306 private static void AssertParseError(string error, string text) {
307 TestAllTypes.Builder builder = TestAllTypes.CreateBuilder();
308 try {
309 TextFormat.Merge(text, TestUtil.CreateExtensionRegistry(), builder);
310 Assert.Fail("Expected parse exception.");
311 } catch (FormatException e) {
312 Assert.AreEqual(error, e.Message);
313 }
314 }
315
316 [Test]
317 public void ParseErrors() {
318 AssertParseError(
319 "1:16: Expected \":\".",
320 "optional_int32 123");
321 AssertParseError(
322 "1:23: Expected identifier.",
323 "optional_nested_enum: ?");
324 AssertParseError(
325 "1:18: Couldn't parse integer: Number must be positive: -1",
326 "optional_uint32: -1");
327 AssertParseError(
328 "1:17: Couldn't parse integer: Number out of range for 32-bit signed " +
329 "integer: 82301481290849012385230157",
330 "optional_int32: 82301481290849012385230157");
331 AssertParseError(
332 "1:16: Expected \"true\" or \"false\".",
333 "optional_bool: maybe");
334 AssertParseError(
335 "1:18: Expected string.",
336 "optional_string: 123");
337 AssertParseError(
338 "1:18: String missing ending quote.",
339 "optional_string: \"ueoauaoe");
340 AssertParseError(
341 "1:18: String missing ending quote.",
342 "optional_string: \"ueoauaoe\n" +
343 "optional_int32: 123");
344 AssertParseError(
345 "1:18: Invalid escape sequence: '\\z'",
346 "optional_string: \"\\z\"");
347 AssertParseError(
348 "1:18: String missing ending quote.",
349 "optional_string: \"ueoauaoe\n" +
350 "optional_int32: 123");
351 AssertParseError(
352 "1:2: Extension \"nosuchext\" not found in the ExtensionRegistry.",
353 "[nosuchext]: 123");
354 AssertParseError(
355 "1:20: Extension \"protobuf_unittest.optional_int32_extension\" does " +
356 "not extend message type \"protobuf_unittest.TestAllTypes\".",
357 "[protobuf_unittest.optional_int32_extension]: 123");
358 AssertParseError(
359 "1:1: Message type \"protobuf_unittest.TestAllTypes\" has no field " +
360 "named \"nosuchfield\".",
361 "nosuchfield: 123");
362 AssertParseError(
363 "1:21: Expected \">\".",
364 "OptionalGroup < a: 1");
365 AssertParseError(
366 "1:23: Enum type \"protobuf_unittest.TestAllTypes.NestedEnum\" has no " +
367 "value named \"NO_SUCH_VALUE\".",
368 "optional_nested_enum: NO_SUCH_VALUE");
369 AssertParseError(
370 "1:23: Enum type \"protobuf_unittest.TestAllTypes.NestedEnum\" has no " +
371 "value with number 123.",
372 "optional_nested_enum: 123");
373
374 // Delimiters must match.
375 AssertParseError(
376 "1:22: Expected identifier.",
377 "OptionalGroup < a: 1 }");
378 AssertParseError(
379 "1:22: Expected identifier.",
380 "OptionalGroup { a: 1 >");
381 }
382
383 // =================================================================
384
385 private static ByteString Bytes(params byte[] bytes) {
386 return ByteString.CopyFrom(bytes);
387 }
388
389 private delegate void FormattingAction();
390
391 private static void AssertFormatException(FormattingAction action) {
392 try {
393 action();
394 Assert.Fail("Should have thrown an exception.");
395 } catch (FormatException) {
396 // success
397 }
398 }
399
400 [Test]
401 public void Escape() {
402 // Escape sequences.
403 Assert.AreEqual("\\000\\001\\a\\b\\f\\n\\r\\t\\v\\\\\\'\\\"",
404 TextFormat.EscapeBytes(Bytes("\0\u0001\u0007\b\f\n\r\t\v\\\'\"")));
405 Assert.AreEqual("\\000\\001\\a\\b\\f\\n\\r\\t\\v\\\\\\'\\\"",
406 TextFormat.EscapeText("\0\u0001\u0007\b\f\n\r\t\v\\\'\""));
407 Assert.AreEqual(Bytes("\0\u0001\u0007\b\f\n\r\t\v\\\'\""),
408 TextFormat.UnescapeBytes("\\000\\001\\a\\b\\f\\n\\r\\t\\v\\\\\\'\\\""));
409 Assert.AreEqual("\0\u0001\u0007\b\f\n\r\t\v\\\'\"",
410 TextFormat.UnescapeText("\\000\\001\\a\\b\\f\\n\\r\\t\\v\\\\\\'\\\""));
411
412 // Unicode handling.
413 Assert.AreEqual("\\341\\210\\264", TextFormat.EscapeText("\u1234"));
414 Assert.AreEqual("\\341\\210\\264", TextFormat.EscapeBytes(Bytes(0xe1, 0x88, 0xb4)));
415 Assert.AreEqual("\u1234", TextFormat.UnescapeText("\\341\\210\\264"));
416 Assert.AreEqual(Bytes(0xe1, 0x88, 0xb4), TextFormat.UnescapeBytes("\\341\\210\\264"));
417 Assert.AreEqual("\u1234", TextFormat.UnescapeText("\\xe1\\x88\\xb4"));
418 Assert.AreEqual(Bytes(0xe1, 0x88, 0xb4), TextFormat.UnescapeBytes("\\xe1\\x88\\xb4"));
419
420 // Errors.
421 AssertFormatException(() => TextFormat.UnescapeText("\\x"));
422 AssertFormatException(() => TextFormat.UnescapeText("\\z"));
423 AssertFormatException(() => TextFormat.UnescapeText("\\"));
424 }
425
426 [Test]
427 public void ParseInteger() {
428 Assert.AreEqual( 0, TextFormat.ParseInt32( "0"));
429 Assert.AreEqual( 1, TextFormat.ParseInt32( "1"));
430 Assert.AreEqual( -1, TextFormat.ParseInt32( "-1"));
431 Assert.AreEqual( 12345, TextFormat.ParseInt32( "12345"));
432 Assert.AreEqual( -12345, TextFormat.ParseInt32( "-12345"));
433 Assert.AreEqual( 2147483647, TextFormat.ParseInt32( "2147483647"));
434 Assert.AreEqual(-2147483648, TextFormat.ParseInt32("-2147483648"));
435
436 Assert.AreEqual( 0, TextFormat.ParseUInt32( "0"));
437 Assert.AreEqual( 1, TextFormat.ParseUInt32( "1"));
438 Assert.AreEqual( 12345, TextFormat.ParseUInt32( "12345"));
439 Assert.AreEqual( 2147483647, TextFormat.ParseUInt32("2147483647"));
440 Assert.AreEqual(2147483648U, TextFormat.ParseUInt32("2147483648"));
441 Assert.AreEqual(4294967295U, TextFormat.ParseUInt32("4294967295"));
442
443 Assert.AreEqual( 0L, TextFormat.ParseInt64( "0"));
444 Assert.AreEqual( 1L, TextFormat.ParseInt64( "1"));
445 Assert.AreEqual( -1L, TextFormat.ParseInt64( "-1"));
446 Assert.AreEqual( 12345L, TextFormat.ParseInt64( "12345"));
447 Assert.AreEqual( -12345L, TextFormat.ParseInt64( "-12345"));
448 Assert.AreEqual( 2147483647L, TextFormat.ParseInt64( "2147483647"));
449 Assert.AreEqual(-2147483648L, TextFormat.ParseInt64("-2147483648"));
450 Assert.AreEqual( 4294967295L, TextFormat.ParseInt64( "4294967295"));
451 Assert.AreEqual( 4294967296L, TextFormat.ParseInt64( "4294967296"));
452 Assert.AreEqual(9223372036854775807L, TextFormat.ParseInt64("9223372036854775807"));
453 Assert.AreEqual(-9223372036854775808L, TextFormat.ParseInt64("-9223372036854775808"));
454
455 Assert.AreEqual( 0L, TextFormat.ParseUInt64( "0"));
456 Assert.AreEqual( 1L, TextFormat.ParseUInt64( "1"));
457 Assert.AreEqual( 12345L, TextFormat.ParseUInt64( "12345"));
458 Assert.AreEqual( 2147483647L, TextFormat.ParseUInt64( "2147483647"));
459 Assert.AreEqual( 4294967295L, TextFormat.ParseUInt64( "4294967295"));
460 Assert.AreEqual( 4294967296L, TextFormat.ParseUInt64( "4294967296"));
461 Assert.AreEqual(9223372036854775807UL, TextFormat.ParseUInt64("9223372036854775807"));
462 Assert.AreEqual(9223372036854775808UL, TextFormat.ParseUInt64("9223372036854775808"));
463 Assert.AreEqual(18446744073709551615UL, TextFormat.ParseUInt64("18446744073709551615"));
464
465 // Hex
466 Assert.AreEqual(0x1234abcd, TextFormat.ParseInt32("0x1234abcd"));
467 Assert.AreEqual(-0x1234abcd, TextFormat.ParseInt32("-0x1234abcd"));
468 Assert.AreEqual(0xffffffffffffffffUL, TextFormat.ParseUInt64("0xffffffffffffffff"));
469 Assert.AreEqual(0x7fffffffffffffffL,
470 TextFormat.ParseInt64("0x7fffffffffffffff"));
471
472 // Octal
473 Assert.AreEqual(342391, TextFormat.ParseInt32("01234567"));
474
475 // Out-of-range
476 AssertFormatException(() => TextFormat.ParseInt32("2147483648"));
477 AssertFormatException(() => TextFormat.ParseInt32("-2147483649"));
478 AssertFormatException(() => TextFormat.ParseUInt32("4294967296"));
479 AssertFormatException(() => TextFormat.ParseUInt32("-1"));
480 AssertFormatException(() => TextFormat.ParseInt64("9223372036854775808"));
481 AssertFormatException(() => TextFormat.ParseInt64("-9223372036854775809"));
482 AssertFormatException(() => TextFormat.ParseUInt64("18446744073709551616"));
483 AssertFormatException(() => TextFormat.ParseUInt64("-1"));
484 AssertFormatException(() => TextFormat.ParseInt32("abcd"));
485 }
486 }
487}