blob: a6cf04ab7af220f8f38c0ab4606f2668c14ae8c7 [file] [log] [blame]
Brian Silverman9c614bc2016-02-15 20:20:02 -05001#region Copyright notice and license
2// Protocol Buffers - Google's data interchange format
3// Copyright 2008 Google Inc. All rights reserved.
4// https://developers.google.com/protocol-buffers/
5//
6// Redistribution and use in source and binary forms, with or without
7// modification, are permitted provided that the following conditions are
8// met:
9//
10// * Redistributions of source code must retain the above copyright
11// notice, this list of conditions and the following disclaimer.
12// * Redistributions in binary form must reproduce the above
13// copyright notice, this list of conditions and the following disclaimer
14// in the documentation and/or other materials provided with the
15// distribution.
16// * Neither the name of Google Inc. nor the names of its
17// contributors may be used to endorse or promote products derived from
18// this software without specific prior written permission.
19//
20// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31#endregion
32
33using Google.Protobuf.Reflection;
34using Google.Protobuf.TestProtos;
35using Google.Protobuf.WellKnownTypes;
36using NUnit.Framework;
37using System;
38
39namespace Google.Protobuf
40{
41 /// <summary>
42 /// Unit tests for JSON parsing.
43 /// </summary>
44 public class JsonParserTest
45 {
46 // Sanity smoke test
47 [Test]
48 public void AllTypesRoundtrip()
49 {
50 AssertRoundtrip(SampleMessages.CreateFullTestAllTypes());
51 }
52
53 [Test]
54 public void Maps()
55 {
56 AssertRoundtrip(new TestMap { MapStringString = { { "with spaces", "bar" }, { "a", "b" } } });
57 AssertRoundtrip(new TestMap { MapInt32Int32 = { { 0, 1 }, { 2, 3 } } });
58 AssertRoundtrip(new TestMap { MapBoolBool = { { false, true }, { true, false } } });
59 }
60
61 [Test]
62 [TestCase(" 1 ")]
63 [TestCase("+1")]
64 [TestCase("1,000")]
65 [TestCase("1.5")]
66 public void IntegerMapKeysAreStrict(string keyText)
67 {
68 // Test that integer parsing is strict. We assume that if this is correct for int32,
69 // it's correct for other numeric key types.
70 var json = "{ \"mapInt32Int32\": { \"" + keyText + "\" : \"1\" } }";
71 Assert.Throws<InvalidProtocolBufferException>(() => JsonParser.Default.Parse<TestMap>(json));
72 }
73
74 [Test]
75 public void OriginalFieldNameAccepted()
76 {
77 var json = "{ \"single_int32\": 10 }";
78 var expected = new TestAllTypes { SingleInt32 = 10 };
79 Assert.AreEqual(expected, TestAllTypes.Parser.ParseJson(json));
80 }
81
82 [Test]
83 public void SourceContextRoundtrip()
84 {
85 AssertRoundtrip(new SourceContext { FileName = "foo.proto" });
86 }
87
88 [Test]
89 public void SingularWrappers_DefaultNonNullValues()
90 {
91 var message = new TestWellKnownTypes
92 {
93 StringField = "",
94 BytesField = ByteString.Empty,
95 BoolField = false,
96 FloatField = 0f,
97 DoubleField = 0d,
98 Int32Field = 0,
99 Int64Field = 0,
100 Uint32Field = 0,
101 Uint64Field = 0
102 };
103 AssertRoundtrip(message);
104 }
105
106 [Test]
107 public void SingularWrappers_NonDefaultValues()
108 {
109 var message = new TestWellKnownTypes
110 {
111 StringField = "x",
112 BytesField = ByteString.CopyFrom(1, 2, 3),
113 BoolField = true,
114 FloatField = 12.5f,
115 DoubleField = 12.25d,
116 Int32Field = 1,
117 Int64Field = 2,
118 Uint32Field = 3,
119 Uint64Field = 4
120 };
121 AssertRoundtrip(message);
122 }
123
124 [Test]
125 public void SingularWrappers_ExplicitNulls()
126 {
127 // When we parse the "valueField": null part, we remember it... basically, it's one case
128 // where explicit default values don't fully roundtrip.
129 var message = new TestWellKnownTypes { ValueField = Value.ForNull() };
130 var json = new JsonFormatter(new JsonFormatter.Settings(true)).Format(message);
131 var parsed = JsonParser.Default.Parse<TestWellKnownTypes>(json);
132 Assert.AreEqual(message, parsed);
133 }
134
135 [Test]
Austin Schuh40c16522018-10-28 20:27:54 -0700136 [TestCase(typeof(BoolValue), "true", true)]
Brian Silverman9c614bc2016-02-15 20:20:02 -0500137 [TestCase(typeof(Int32Value), "32", 32)]
138 [TestCase(typeof(Int64Value), "32", 32L)]
Austin Schuh40c16522018-10-28 20:27:54 -0700139 [TestCase(typeof(Int64Value), "\"32\"", 32L)]
Brian Silverman9c614bc2016-02-15 20:20:02 -0500140 [TestCase(typeof(UInt32Value), "32", 32U)]
Austin Schuh40c16522018-10-28 20:27:54 -0700141 [TestCase(typeof(UInt64Value), "\"32\"", 32UL)]
Brian Silverman9c614bc2016-02-15 20:20:02 -0500142 [TestCase(typeof(UInt64Value), "32", 32UL)]
143 [TestCase(typeof(StringValue), "\"foo\"", "foo")]
144 [TestCase(typeof(FloatValue), "1.5", 1.5f)]
145 [TestCase(typeof(DoubleValue), "1.5", 1.5d)]
146 public void Wrappers_Standalone(System.Type wrapperType, string json, object expectedValue)
147 {
Austin Schuh40c16522018-10-28 20:27:54 -0700148 IMessage parsed = (IMessage)Activator.CreateInstance(wrapperType);
149 IMessage expected = (IMessage)Activator.CreateInstance(wrapperType);
Brian Silverman9c614bc2016-02-15 20:20:02 -0500150 JsonParser.Default.Merge(parsed, "null");
151 Assert.AreEqual(expected, parsed);
152
153 JsonParser.Default.Merge(parsed, json);
154 expected.Descriptor.Fields[WrappersReflection.WrapperValueFieldNumber].Accessor.SetValue(expected, expectedValue);
155 Assert.AreEqual(expected, parsed);
156 }
157
158 [Test]
159 public void ExplicitNullValue()
160 {
161 string json = "{\"valueField\": null}";
162 var message = JsonParser.Default.Parse<TestWellKnownTypes>(json);
163 Assert.AreEqual(new TestWellKnownTypes { ValueField = Value.ForNull() }, message);
164 }
165
166 [Test]
167 public void BytesWrapper_Standalone()
168 {
169 ByteString data = ByteString.CopyFrom(1, 2, 3);
170 // Can't do this with attributes...
171 var parsed = JsonParser.Default.Parse<BytesValue>(WrapInQuotes(data.ToBase64()));
172 var expected = new BytesValue { Value = data };
173 Assert.AreEqual(expected, parsed);
174 }
175
176 [Test]
177 public void RepeatedWrappers()
178 {
179 var message = new RepeatedWellKnownTypes
180 {
181 BoolField = { true, false },
182 BytesField = { ByteString.CopyFrom(1, 2, 3), ByteString.CopyFrom(4, 5, 6), ByteString.Empty },
183 DoubleField = { 12.5, -1.5, 0d },
184 FloatField = { 123.25f, -20f, 0f },
185 Int32Field = { int.MaxValue, int.MinValue, 0 },
186 Int64Field = { long.MaxValue, long.MinValue, 0L },
187 StringField = { "First", "Second", "" },
188 Uint32Field = { uint.MaxValue, uint.MinValue, 0U },
189 Uint64Field = { ulong.MaxValue, ulong.MinValue, 0UL },
190 };
191 AssertRoundtrip(message);
192 }
193
194 [Test]
195 public void RepeatedField_NullElementProhibited()
196 {
197 string json = "{ \"repeated_foreign_message\": [null] }";
198 Assert.Throws<InvalidProtocolBufferException>(() => TestAllTypes.Parser.ParseJson(json));
199 }
200
201 [Test]
202 public void RepeatedField_NullOverallValueAllowed()
203 {
204 string json = "{ \"repeated_foreign_message\": null }";
205 Assert.AreEqual(new TestAllTypes(), TestAllTypes.Parser.ParseJson(json));
206 }
207
208 [Test]
209 [TestCase("{ \"mapInt32Int32\": { \"10\": null }")]
210 [TestCase("{ \"mapStringString\": { \"abc\": null }")]
211 [TestCase("{ \"mapInt32ForeignMessage\": { \"10\": null }")]
212 public void MapField_NullValueProhibited(string json)
213 {
214 Assert.Throws<InvalidProtocolBufferException>(() => TestMap.Parser.ParseJson(json));
215 }
216
217 [Test]
218 public void MapField_NullOverallValueAllowed()
219 {
220 string json = "{ \"mapInt32Int32\": null }";
221 Assert.AreEqual(new TestMap(), TestMap.Parser.ParseJson(json));
222 }
223
224 [Test]
225 public void IndividualWrapperTypes()
226 {
227 Assert.AreEqual(new StringValue { Value = "foo" }, StringValue.Parser.ParseJson("\"foo\""));
228 Assert.AreEqual(new Int32Value { Value = 1 }, Int32Value.Parser.ParseJson("1"));
229 // Can parse strings directly too
230 Assert.AreEqual(new Int32Value { Value = 1 }, Int32Value.Parser.ParseJson("\"1\""));
231 }
232
233 private static void AssertRoundtrip<T>(T message) where T : IMessage<T>, new()
234 {
235 var clone = message.Clone();
236 var json = JsonFormatter.Default.Format(message);
237 var parsed = JsonParser.Default.Parse<T>(json);
238 Assert.AreEqual(clone, parsed);
239 }
240
241 [Test]
242 [TestCase("0", 0)]
243 [TestCase("-0", 0)] // Not entirely clear whether we intend to allow this...
244 [TestCase("1", 1)]
245 [TestCase("-1", -1)]
246 [TestCase("2147483647", 2147483647)]
247 [TestCase("-2147483648", -2147483648)]
248 public void StringToInt32_Valid(string jsonValue, int expectedParsedValue)
249 {
250 string json = "{ \"singleInt32\": \"" + jsonValue + "\"}";
251 var parsed = TestAllTypes.Parser.ParseJson(json);
252 Assert.AreEqual(expectedParsedValue, parsed.SingleInt32);
253 }
254
255 [Test]
256 [TestCase("+0")]
257 [TestCase(" 1")]
258 [TestCase("1 ")]
259 [TestCase("00")]
260 [TestCase("-00")]
261 [TestCase("--1")]
262 [TestCase("+1")]
263 [TestCase("1.5")]
264 [TestCase("1e10")]
265 [TestCase("2147483648")]
266 [TestCase("-2147483649")]
267 public void StringToInt32_Invalid(string jsonValue)
268 {
269 string json = "{ \"singleInt32\": \"" + jsonValue + "\"}";
270 Assert.Throws<InvalidProtocolBufferException>(() => TestAllTypes.Parser.ParseJson(json));
271 }
272
273 [Test]
274 [TestCase("0", 0U)]
275 [TestCase("1", 1U)]
276 [TestCase("4294967295", 4294967295U)]
277 public void StringToUInt32_Valid(string jsonValue, uint expectedParsedValue)
278 {
279 string json = "{ \"singleUint32\": \"" + jsonValue + "\"}";
280 var parsed = TestAllTypes.Parser.ParseJson(json);
281 Assert.AreEqual(expectedParsedValue, parsed.SingleUint32);
282 }
283
284 // Assume that anything non-bounds-related is covered in the Int32 case
285 [Test]
286 [TestCase("-1")]
287 [TestCase("4294967296")]
288 public void StringToUInt32_Invalid(string jsonValue)
289 {
290 string json = "{ \"singleUint32\": \"" + jsonValue + "\"}";
291 Assert.Throws<InvalidProtocolBufferException>(() => TestAllTypes.Parser.ParseJson(json));
292 }
293
294 [Test]
295 [TestCase("0", 0L)]
296 [TestCase("1", 1L)]
297 [TestCase("-1", -1L)]
298 [TestCase("9223372036854775807", 9223372036854775807)]
299 [TestCase("-9223372036854775808", -9223372036854775808)]
300 public void StringToInt64_Valid(string jsonValue, long expectedParsedValue)
301 {
302 string json = "{ \"singleInt64\": \"" + jsonValue + "\"}";
303 var parsed = TestAllTypes.Parser.ParseJson(json);
304 Assert.AreEqual(expectedParsedValue, parsed.SingleInt64);
305 }
306
307 // Assume that anything non-bounds-related is covered in the Int32 case
308 [Test]
309 [TestCase("-9223372036854775809")]
310 [TestCase("9223372036854775808")]
311 public void StringToInt64_Invalid(string jsonValue)
312 {
313 string json = "{ \"singleInt64\": \"" + jsonValue + "\"}";
314 Assert.Throws<InvalidProtocolBufferException>(() => TestAllTypes.Parser.ParseJson(json));
315 }
316
317 [Test]
318 [TestCase("0", 0UL)]
319 [TestCase("1", 1UL)]
320 [TestCase("18446744073709551615", 18446744073709551615)]
321 public void StringToUInt64_Valid(string jsonValue, ulong expectedParsedValue)
322 {
323 string json = "{ \"singleUint64\": \"" + jsonValue + "\"}";
324 var parsed = TestAllTypes.Parser.ParseJson(json);
325 Assert.AreEqual(expectedParsedValue, parsed.SingleUint64);
326 }
327
328 // Assume that anything non-bounds-related is covered in the Int32 case
329 [Test]
330 [TestCase("-1")]
331 [TestCase("18446744073709551616")]
332 public void StringToUInt64_Invalid(string jsonValue)
333 {
334 string json = "{ \"singleUint64\": \"" + jsonValue + "\"}";
335 Assert.Throws<InvalidProtocolBufferException>(() => TestAllTypes.Parser.ParseJson(json));
336 }
337
338 [Test]
339 [TestCase("0", 0d)]
340 [TestCase("1", 1d)]
341 [TestCase("1.000000", 1d)]
342 [TestCase("1.0000000000000000000000001", 1d)] // We don't notice that we haven't preserved the exact value
343 [TestCase("-1", -1d)]
344 [TestCase("1e1", 10d)]
345 [TestCase("1e01", 10d)] // Leading decimals are allowed in exponents
346 [TestCase("1E1", 10d)] // Either case is fine
347 [TestCase("-1e1", -10d)]
348 [TestCase("1.5e1", 15d)]
349 [TestCase("-1.5e1", -15d)]
350 [TestCase("15e-1", 1.5d)]
351 [TestCase("-15e-1", -1.5d)]
352 [TestCase("1.79769e308", 1.79769e308)]
353 [TestCase("-1.79769e308", -1.79769e308)]
354 [TestCase("Infinity", double.PositiveInfinity)]
355 [TestCase("-Infinity", double.NegativeInfinity)]
356 [TestCase("NaN", double.NaN)]
357 public void StringToDouble_Valid(string jsonValue, double expectedParsedValue)
358 {
359 string json = "{ \"singleDouble\": \"" + jsonValue + "\"}";
360 var parsed = TestAllTypes.Parser.ParseJson(json);
361 Assert.AreEqual(expectedParsedValue, parsed.SingleDouble);
362 }
363
364 [Test]
365 [TestCase("1.7977e308")]
366 [TestCase("-1.7977e308")]
367 [TestCase("1e309")]
368 [TestCase("1,0")]
369 [TestCase("1.0.0")]
370 [TestCase("+1")]
371 [TestCase("00")]
372 [TestCase("01")]
373 [TestCase("-00")]
374 [TestCase("-01")]
375 [TestCase("--1")]
376 [TestCase(" Infinity")]
377 [TestCase(" -Infinity")]
378 [TestCase("NaN ")]
379 [TestCase("Infinity ")]
380 [TestCase("-Infinity ")]
381 [TestCase(" NaN")]
382 [TestCase("INFINITY")]
383 [TestCase("nan")]
384 [TestCase("\u00BD")] // 1/2 as a single Unicode character. Just sanity checking...
385 public void StringToDouble_Invalid(string jsonValue)
386 {
387 string json = "{ \"singleDouble\": \"" + jsonValue + "\"}";
388 Assert.Throws<InvalidProtocolBufferException>(() => TestAllTypes.Parser.ParseJson(json));
389 }
390
391 [Test]
392 [TestCase("0", 0f)]
393 [TestCase("1", 1f)]
394 [TestCase("1.000000", 1f)]
395 [TestCase("-1", -1f)]
396 [TestCase("3.402823e38", 3.402823e38f)]
397 [TestCase("-3.402823e38", -3.402823e38f)]
398 [TestCase("1.5e1", 15f)]
399 [TestCase("15e-1", 1.5f)]
400 public void StringToFloat_Valid(string jsonValue, float expectedParsedValue)
401 {
402 string json = "{ \"singleFloat\": \"" + jsonValue + "\"}";
403 var parsed = TestAllTypes.Parser.ParseJson(json);
404 Assert.AreEqual(expectedParsedValue, parsed.SingleFloat);
405 }
406
407 [Test]
408 [TestCase("3.402824e38")]
409 [TestCase("-3.402824e38")]
410 [TestCase("1,0")]
411 [TestCase("1.0.0")]
412 [TestCase("+1")]
413 [TestCase("00")]
414 [TestCase("--1")]
415 public void StringToFloat_Invalid(string jsonValue)
416 {
417 string json = "{ \"singleFloat\": \"" + jsonValue + "\"}";
418 Assert.Throws<InvalidProtocolBufferException>(() => TestAllTypes.Parser.ParseJson(json));
419 }
420
421 [Test]
422 [TestCase("0", 0)]
423 [TestCase("-0", 0)] // Not entirely clear whether we intend to allow this...
424 [TestCase("1", 1)]
425 [TestCase("-1", -1)]
426 [TestCase("2147483647", 2147483647)]
427 [TestCase("-2147483648", -2147483648)]
428 [TestCase("1e1", 10)]
429 [TestCase("-1e1", -10)]
430 [TestCase("10.00", 10)]
431 [TestCase("-10.00", -10)]
432 public void NumberToInt32_Valid(string jsonValue, int expectedParsedValue)
433 {
434 string json = "{ \"singleInt32\": " + jsonValue + "}";
435 var parsed = TestAllTypes.Parser.ParseJson(json);
436 Assert.AreEqual(expectedParsedValue, parsed.SingleInt32);
437 }
438
439 [Test]
440 [TestCase("+0", typeof(InvalidJsonException))]
441 [TestCase("00", typeof(InvalidJsonException))]
442 [TestCase("-00", typeof(InvalidJsonException))]
443 [TestCase("--1", typeof(InvalidJsonException))]
444 [TestCase("+1", typeof(InvalidJsonException))]
445 [TestCase("1.5", typeof(InvalidProtocolBufferException))]
446 // Value is out of range
447 [TestCase("1e10", typeof(InvalidProtocolBufferException))]
448 [TestCase("2147483648", typeof(InvalidProtocolBufferException))]
449 [TestCase("-2147483649", typeof(InvalidProtocolBufferException))]
450 public void NumberToInt32_Invalid(string jsonValue, System.Type expectedExceptionType)
451 {
452 string json = "{ \"singleInt32\": " + jsonValue + "}";
453 Assert.Throws(expectedExceptionType, () => TestAllTypes.Parser.ParseJson(json));
454 }
455
456 [Test]
457 [TestCase("0", 0U)]
458 [TestCase("1", 1U)]
459 [TestCase("4294967295", 4294967295U)]
460 public void NumberToUInt32_Valid(string jsonValue, uint expectedParsedValue)
461 {
462 string json = "{ \"singleUint32\": " + jsonValue + "}";
463 var parsed = TestAllTypes.Parser.ParseJson(json);
464 Assert.AreEqual(expectedParsedValue, parsed.SingleUint32);
465 }
466
467 // Assume that anything non-bounds-related is covered in the Int32 case
468 [Test]
469 [TestCase("-1")]
470 [TestCase("4294967296")]
471 public void NumberToUInt32_Invalid(string jsonValue)
472 {
473 string json = "{ \"singleUint32\": " + jsonValue + "}";
474 Assert.Throws<InvalidProtocolBufferException>(() => TestAllTypes.Parser.ParseJson(json));
475 }
476
477 [Test]
478 [TestCase("0", 0L)]
479 [TestCase("1", 1L)]
480 [TestCase("-1", -1L)]
481 // long.MaxValue isn't actually representable as a double. This string value is the highest
482 // representable value which isn't greater than long.MaxValue.
483 [TestCase("9223372036854774784", 9223372036854774784)]
484 [TestCase("-9223372036854775808", -9223372036854775808)]
485 public void NumberToInt64_Valid(string jsonValue, long expectedParsedValue)
486 {
487 string json = "{ \"singleInt64\": " + jsonValue + "}";
488 var parsed = TestAllTypes.Parser.ParseJson(json);
489 Assert.AreEqual(expectedParsedValue, parsed.SingleInt64);
490 }
491
492 // Assume that anything non-bounds-related is covered in the Int32 case
493 [Test]
494 [TestCase("9223372036854775808")]
495 // Theoretical bound would be -9223372036854775809, but when that is parsed to a double
496 // we end up with the exact value of long.MinValue due to lack of precision. The value here
497 // is the "next double down".
498 [TestCase("-9223372036854780000")]
499 public void NumberToInt64_Invalid(string jsonValue)
500 {
501 string json = "{ \"singleInt64\": " + jsonValue + "}";
502 Assert.Throws<InvalidProtocolBufferException>(() => TestAllTypes.Parser.ParseJson(json));
503 }
504
505 [Test]
506 [TestCase("0", 0UL)]
507 [TestCase("1", 1UL)]
508 // ulong.MaxValue isn't representable as a double. This value is the largest double within
509 // the range of ulong.
510 [TestCase("18446744073709549568", 18446744073709549568UL)]
511 public void NumberToUInt64_Valid(string jsonValue, ulong expectedParsedValue)
512 {
513 string json = "{ \"singleUint64\": " + jsonValue + "}";
514 var parsed = TestAllTypes.Parser.ParseJson(json);
515 Assert.AreEqual(expectedParsedValue, parsed.SingleUint64);
516 }
517
518 // Assume that anything non-bounds-related is covered in the Int32 case
519 [Test]
520 [TestCase("-1")]
521 [TestCase("18446744073709551616")]
522 public void NumberToUInt64_Invalid(string jsonValue)
523 {
524 string json = "{ \"singleUint64\": " + jsonValue + "}";
525 Assert.Throws<InvalidProtocolBufferException>(() => TestAllTypes.Parser.ParseJson(json));
526 }
527
528 [Test]
529 [TestCase("0", 0d)]
530 [TestCase("1", 1d)]
531 [TestCase("1.000000", 1d)]
532 [TestCase("1.0000000000000000000000001", 1d)] // We don't notice that we haven't preserved the exact value
533 [TestCase("-1", -1d)]
534 [TestCase("1e1", 10d)]
535 [TestCase("1e01", 10d)] // Leading decimals are allowed in exponents
536 [TestCase("1E1", 10d)] // Either case is fine
537 [TestCase("-1e1", -10d)]
538 [TestCase("1.5e1", 15d)]
539 [TestCase("-1.5e1", -15d)]
540 [TestCase("15e-1", 1.5d)]
541 [TestCase("-15e-1", -1.5d)]
542 [TestCase("1.79769e308", 1.79769e308)]
543 [TestCase("-1.79769e308", -1.79769e308)]
544 public void NumberToDouble_Valid(string jsonValue, double expectedParsedValue)
545 {
546 string json = "{ \"singleDouble\": " + jsonValue + "}";
547 var parsed = TestAllTypes.Parser.ParseJson(json);
548 Assert.AreEqual(expectedParsedValue, parsed.SingleDouble);
549 }
550
551 [Test]
552 [TestCase("1.7977e308")]
553 [TestCase("-1.7977e308")]
554 [TestCase("1e309")]
555 [TestCase("1,0")]
556 [TestCase("1.0.0")]
557 [TestCase("+1")]
558 [TestCase("00")]
559 [TestCase("--1")]
560 [TestCase("\u00BD")] // 1/2 as a single Unicode character. Just sanity checking...
561 public void NumberToDouble_Invalid(string jsonValue)
562 {
563 string json = "{ \"singleDouble\": " + jsonValue + "}";
564 Assert.Throws<InvalidJsonException>(() => TestAllTypes.Parser.ParseJson(json));
565 }
566
567 [Test]
568 [TestCase("0", 0f)]
569 [TestCase("1", 1f)]
570 [TestCase("1.000000", 1f)]
571 [TestCase("-1", -1f)]
572 [TestCase("3.402823e38", 3.402823e38f)]
573 [TestCase("-3.402823e38", -3.402823e38f)]
574 [TestCase("1.5e1", 15f)]
575 [TestCase("15e-1", 1.5f)]
576 public void NumberToFloat_Valid(string jsonValue, float expectedParsedValue)
577 {
578 string json = "{ \"singleFloat\": " + jsonValue + "}";
579 var parsed = TestAllTypes.Parser.ParseJson(json);
580 Assert.AreEqual(expectedParsedValue, parsed.SingleFloat);
581 }
582
583 [Test]
584 [TestCase("3.402824e38", typeof(InvalidProtocolBufferException))]
585 [TestCase("-3.402824e38", typeof(InvalidProtocolBufferException))]
586 [TestCase("1,0", typeof(InvalidJsonException))]
587 [TestCase("1.0.0", typeof(InvalidJsonException))]
588 [TestCase("+1", typeof(InvalidJsonException))]
589 [TestCase("00", typeof(InvalidJsonException))]
590 [TestCase("--1", typeof(InvalidJsonException))]
591 public void NumberToFloat_Invalid(string jsonValue, System.Type expectedExceptionType)
592 {
593 string json = "{ \"singleFloat\": " + jsonValue + "}";
594 Assert.Throws(expectedExceptionType, () => TestAllTypes.Parser.ParseJson(json));
595 }
596
597 // The simplest way of testing that the value has parsed correctly is to reformat it,
598 // as we trust the formatting. In many cases that will give the same result as the input,
599 // so in those cases we accept an expectedFormatted value of null. Sometimes the results
600 // will be different though, due to a different number of digits being provided.
601 [Test]
602 // Z offset
603 [TestCase("2015-10-09T14:46:23.123456789Z", null)]
604 [TestCase("2015-10-09T14:46:23.123456Z", null)]
605 [TestCase("2015-10-09T14:46:23.123Z", null)]
606 [TestCase("2015-10-09T14:46:23Z", null)]
607 [TestCase("2015-10-09T14:46:23.123456000Z", "2015-10-09T14:46:23.123456Z")]
608 [TestCase("2015-10-09T14:46:23.1234560Z", "2015-10-09T14:46:23.123456Z")]
609 [TestCase("2015-10-09T14:46:23.123000000Z", "2015-10-09T14:46:23.123Z")]
610 [TestCase("2015-10-09T14:46:23.1230Z", "2015-10-09T14:46:23.123Z")]
611 [TestCase("2015-10-09T14:46:23.00Z", "2015-10-09T14:46:23Z")]
612
613 // +00:00 offset
614 [TestCase("2015-10-09T14:46:23.123456789+00:00", "2015-10-09T14:46:23.123456789Z")]
615 [TestCase("2015-10-09T14:46:23.123456+00:00", "2015-10-09T14:46:23.123456Z")]
616 [TestCase("2015-10-09T14:46:23.123+00:00", "2015-10-09T14:46:23.123Z")]
617 [TestCase("2015-10-09T14:46:23+00:00", "2015-10-09T14:46:23Z")]
618 [TestCase("2015-10-09T14:46:23.123456000+00:00", "2015-10-09T14:46:23.123456Z")]
619 [TestCase("2015-10-09T14:46:23.1234560+00:00", "2015-10-09T14:46:23.123456Z")]
620 [TestCase("2015-10-09T14:46:23.123000000+00:00", "2015-10-09T14:46:23.123Z")]
621 [TestCase("2015-10-09T14:46:23.1230+00:00", "2015-10-09T14:46:23.123Z")]
622 [TestCase("2015-10-09T14:46:23.00+00:00", "2015-10-09T14:46:23Z")]
623
624 // Other offsets (assume by now that the subsecond handling is okay)
625 [TestCase("2015-10-09T15:46:23.123456789+01:00", "2015-10-09T14:46:23.123456789Z")]
626 [TestCase("2015-10-09T13:46:23.123456789-01:00", "2015-10-09T14:46:23.123456789Z")]
627 [TestCase("2015-10-09T15:16:23.123456789+00:30", "2015-10-09T14:46:23.123456789Z")]
628 [TestCase("2015-10-09T14:16:23.123456789-00:30", "2015-10-09T14:46:23.123456789Z")]
629 [TestCase("2015-10-09T16:31:23.123456789+01:45", "2015-10-09T14:46:23.123456789Z")]
630 [TestCase("2015-10-09T13:01:23.123456789-01:45", "2015-10-09T14:46:23.123456789Z")]
631 [TestCase("2015-10-10T08:46:23.123456789+18:00", "2015-10-09T14:46:23.123456789Z")]
632 [TestCase("2015-10-08T20:46:23.123456789-18:00", "2015-10-09T14:46:23.123456789Z")]
633
634 // Leap years and min/max
635 [TestCase("2016-02-29T14:46:23.123456789Z", null)]
636 [TestCase("2000-02-29T14:46:23.123456789Z", null)]
637 [TestCase("0001-01-01T00:00:00Z", null)]
638 [TestCase("9999-12-31T23:59:59.999999999Z", null)]
639 public void Timestamp_Valid(string jsonValue, string expectedFormatted)
640 {
641 expectedFormatted = expectedFormatted ?? jsonValue;
642 string json = WrapInQuotes(jsonValue);
643 var parsed = Timestamp.Parser.ParseJson(json);
644 Assert.AreEqual(WrapInQuotes(expectedFormatted), parsed.ToString());
645 }
Austin Schuh40c16522018-10-28 20:27:54 -0700646
Brian Silverman9c614bc2016-02-15 20:20:02 -0500647 [Test]
648 [TestCase("2015-10-09 14:46:23.123456789Z", Description = "No T between date and time")]
649 [TestCase("2015/10/09T14:46:23.123456789Z", Description = "Wrong date separators")]
650 [TestCase("2015-10-09T14.46.23.123456789Z", Description = "Wrong time separators")]
651 [TestCase("2015-10-09T14:46:23,123456789Z", Description = "Wrong fractional second separators (valid ISO-8601 though)")]
652 [TestCase(" 2015-10-09T14:46:23.123456789Z", Description = "Whitespace at start")]
653 [TestCase("2015-10-09T14:46:23.123456789Z ", Description = "Whitespace at end")]
654 [TestCase("2015-10-09T14:46:23.1234567890", Description = "Too many digits")]
655 [TestCase("2015-10-09T14:46:23.123456789", Description = "No offset")]
656 [TestCase("2015-13-09T14:46:23.123456789Z", Description = "Invalid month")]
657 [TestCase("2015-10-32T14:46:23.123456789Z", Description = "Invalid day")]
658 [TestCase("2015-10-09T24:00:00.000000000Z", Description = "Invalid hour (valid ISO-8601 though)")]
659 [TestCase("2015-10-09T14:60:23.123456789Z", Description = "Invalid minutes")]
660 [TestCase("2015-10-09T14:46:60.123456789Z", Description = "Invalid seconds")]
661 [TestCase("2015-10-09T14:46:23.123456789+18:01", Description = "Offset too large (positive)")]
662 [TestCase("2015-10-09T14:46:23.123456789-18:01", Description = "Offset too large (negative)")]
663 [TestCase("2015-10-09T14:46:23.123456789-00:00", Description = "Local offset (-00:00) makes no sense here")]
664 [TestCase("0001-01-01T00:00:00+00:01", Description = "Value before earliest when offset applied")]
665 [TestCase("9999-12-31T23:59:59.999999999-00:01", Description = "Value after latest when offset applied")]
666 [TestCase("2100-02-29T14:46:23.123456789Z", Description = "Feb 29th on a non-leap-year")]
667 public void Timestamp_Invalid(string jsonValue)
668 {
669 string json = WrapInQuotes(jsonValue);
670 Assert.Throws<InvalidProtocolBufferException>(() => Timestamp.Parser.ParseJson(json));
671 }
672
673 [Test]
674 public void StructValue_Null()
675 {
676 Assert.AreEqual(new Value { NullValue = 0 }, Value.Parser.ParseJson("null"));
677 }
678
679 [Test]
680 public void StructValue_String()
681 {
682 Assert.AreEqual(new Value { StringValue = "hi" }, Value.Parser.ParseJson("\"hi\""));
683 }
684
685 [Test]
686 public void StructValue_Bool()
687 {
688 Assert.AreEqual(new Value { BoolValue = true }, Value.Parser.ParseJson("true"));
689 Assert.AreEqual(new Value { BoolValue = false }, Value.Parser.ParseJson("false"));
690 }
691
692 [Test]
693 public void StructValue_List()
694 {
695 Assert.AreEqual(Value.ForList(Value.ForNumber(1), Value.ForString("x")), Value.Parser.ParseJson("[1, \"x\"]"));
696 }
697
698 [Test]
Austin Schuh40c16522018-10-28 20:27:54 -0700699 public void Value_List_WithNullElement()
700 {
701 var expected = Value.ForList(Value.ForString("x"), Value.ForNull(), Value.ForString("y"));
702 var actual = Value.Parser.ParseJson("[\"x\", null, \"y\"]");
703 Assert.AreEqual(expected, actual);
704 }
705
706 [Test]
707 public void StructValue_NullElement()
708 {
709 var expected = Value.ForStruct(new Struct { Fields = { { "x", Value.ForNull() } } });
710 var actual = Value.Parser.ParseJson("{ \"x\": null }");
711 Assert.AreEqual(expected, actual);
712 }
713
714 [Test]
Brian Silverman9c614bc2016-02-15 20:20:02 -0500715 public void ParseListValue()
716 {
717 Assert.AreEqual(new ListValue { Values = { Value.ForNumber(1), Value.ForString("x") } }, ListValue.Parser.ParseJson("[1, \"x\"]"));
718 }
719
720 [Test]
721 public void StructValue_Struct()
722 {
723 Assert.AreEqual(
724 Value.ForStruct(new Struct { Fields = { { "x", Value.ForNumber(1) }, { "y", Value.ForString("z") } } }),
725 Value.Parser.ParseJson("{ \"x\": 1, \"y\": \"z\" }"));
726 }
727
728 [Test]
729 public void ParseStruct()
730 {
731 Assert.AreEqual(new Struct { Fields = { { "x", Value.ForNumber(1) }, { "y", Value.ForString("z") } } },
732 Struct.Parser.ParseJson("{ \"x\": 1, \"y\": \"z\" }"));
733 }
734
735 // TODO for duration parsing: upper and lower bounds.
736 // +/- 315576000000 seconds
737
738 [Test]
739 [TestCase("1.123456789s", null)]
740 [TestCase("1.123456s", null)]
741 [TestCase("1.123s", null)]
742 [TestCase("1.12300s", "1.123s")]
743 [TestCase("1.12345s", "1.123450s")]
744 [TestCase("1s", null)]
745 [TestCase("-1.123456789s", null)]
746 [TestCase("-1.123456s", null)]
747 [TestCase("-1.123s", null)]
748 [TestCase("-1s", null)]
749 [TestCase("0.123s", null)]
750 [TestCase("-0.123s", null)]
751 [TestCase("123456.123s", null)]
752 [TestCase("-123456.123s", null)]
753 // Upper and lower bounds
754 [TestCase("315576000000s", null)]
755 [TestCase("-315576000000s", null)]
756 public void Duration_Valid(string jsonValue, string expectedFormatted)
757 {
758 expectedFormatted = expectedFormatted ?? jsonValue;
759 string json = WrapInQuotes(jsonValue);
760 var parsed = Duration.Parser.ParseJson(json);
761 Assert.AreEqual(WrapInQuotes(expectedFormatted), parsed.ToString());
762 }
763
764 // The simplest way of testing that the value has parsed correctly is to reformat it,
765 // as we trust the formatting. In many cases that will give the same result as the input,
766 // so in those cases we accept an expectedFormatted value of null. Sometimes the results
767 // will be different though, due to a different number of digits being provided.
768 [Test]
769 [TestCase("1.1234567890s", Description = "Too many digits")]
770 [TestCase("1.123456789", Description = "No suffix")]
771 [TestCase("1.123456789ss", Description = "Too much suffix")]
772 [TestCase("1.123456789S", Description = "Upper case suffix")]
773 [TestCase("+1.123456789s", Description = "Leading +")]
774 [TestCase(".123456789s", Description = "No integer before the fraction")]
775 [TestCase("1,123456789s", Description = "Comma as decimal separator")]
776 [TestCase("1x1.123456789s", Description = "Non-digit in integer part")]
777 [TestCase("1.1x3456789s", Description = "Non-digit in fractional part")]
778 [TestCase(" 1.123456789s", Description = "Whitespace before fraction")]
779 [TestCase("1.123456789s ", Description = "Whitespace after value")]
780 [TestCase("01.123456789s", Description = "Leading zero (positive)")]
781 [TestCase("-01.123456789s", Description = "Leading zero (negative)")]
782 [TestCase("--0.123456789s", Description = "Double minus sign")]
783 // Violate upper/lower bounds in various ways
784 [TestCase("315576000001s", Description = "Integer part too large")]
785 [TestCase("3155760000000s", Description = "Integer part too long (positive)")]
786 [TestCase("-3155760000000s", Description = "Integer part too long (negative)")]
787 public void Duration_Invalid(string jsonValue)
788 {
789 string json = WrapInQuotes(jsonValue);
790 Assert.Throws<InvalidProtocolBufferException>(() => Duration.Parser.ParseJson(json));
791 }
792
793 // Not as many tests for field masks as I'd like; more to be added when we have more
794 // detailed specifications.
795
796 [Test]
797 [TestCase("")]
798 [TestCase("foo", "foo")]
799 [TestCase("foo,bar", "foo", "bar")]
800 [TestCase("foo.bar", "foo.bar")]
801 [TestCase("fooBar", "foo_bar")]
802 [TestCase("fooBar.bazQux", "foo_bar.baz_qux")]
803 public void FieldMask_Valid(string jsonValue, params string[] expectedPaths)
804 {
805 string json = WrapInQuotes(jsonValue);
806 var parsed = FieldMask.Parser.ParseJson(json);
807 CollectionAssert.AreEqual(expectedPaths, parsed.Paths);
808 }
809
810 [Test]
811 [TestCase("foo_bar")]
812 public void FieldMask_Invalid(string jsonValue)
813 {
814 string json = WrapInQuotes(jsonValue);
815 Assert.Throws<InvalidProtocolBufferException>(() => FieldMask.Parser.ParseJson(json));
816 }
817
818 [Test]
819 public void Any_RegularMessage()
820 {
821 var registry = TypeRegistry.FromMessages(TestAllTypes.Descriptor);
822 var formatter = new JsonFormatter(new JsonFormatter.Settings(false, TypeRegistry.FromMessages(TestAllTypes.Descriptor)));
823 var message = new TestAllTypes { SingleInt32 = 10, SingleNestedMessage = new TestAllTypes.Types.NestedMessage { Bb = 20 } };
824 var original = Any.Pack(message);
825 var json = formatter.Format(original); // This is tested in JsonFormatterTest
826 var parser = new JsonParser(new JsonParser.Settings(10, registry));
827 Assert.AreEqual(original, parser.Parse<Any>(json));
Austin Schuh40c16522018-10-28 20:27:54 -0700828 string valueFirstJson = "{ \"singleInt32\": 10, \"singleNestedMessage\": { \"bb\": 20 }, \"@type\": \"type.googleapis.com/protobuf_unittest3.TestAllTypes\" }";
Brian Silverman9c614bc2016-02-15 20:20:02 -0500829 Assert.AreEqual(original, parser.Parse<Any>(valueFirstJson));
830 }
831
832 [Test]
Austin Schuh40c16522018-10-28 20:27:54 -0700833 public void Any_CustomPrefix()
834 {
835 var registry = TypeRegistry.FromMessages(TestAllTypes.Descriptor);
836 var message = new TestAllTypes { SingleInt32 = 10 };
837 var original = Any.Pack(message, "custom.prefix/middle-part");
838 var parser = new JsonParser(new JsonParser.Settings(10, registry));
839 string json = "{ \"@type\": \"custom.prefix/middle-part/protobuf_unittest3.TestAllTypes\", \"singleInt32\": 10 }";
840 Assert.AreEqual(original, parser.Parse<Any>(json));
841 }
842
843 [Test]
Brian Silverman9c614bc2016-02-15 20:20:02 -0500844 public void Any_UnknownType()
845 {
846 string json = "{ \"@type\": \"type.googleapis.com/bogus\" }";
847 Assert.Throws<InvalidOperationException>(() => Any.Parser.ParseJson(json));
848 }
849
850 [Test]
851 public void Any_NoTypeUrl()
852 {
853 string json = "{ \"foo\": \"bar\" }";
854 Assert.Throws<InvalidProtocolBufferException>(() => Any.Parser.ParseJson(json));
855 }
856
857 [Test]
858 public void Any_WellKnownType()
859 {
860 var registry = TypeRegistry.FromMessages(Timestamp.Descriptor);
861 var formatter = new JsonFormatter(new JsonFormatter.Settings(false, registry));
862 var timestamp = new DateTime(1673, 6, 19, 12, 34, 56, DateTimeKind.Utc).ToTimestamp();
863 var original = Any.Pack(timestamp);
864 var json = formatter.Format(original); // This is tested in JsonFormatterTest
865 var parser = new JsonParser(new JsonParser.Settings(10, registry));
866 Assert.AreEqual(original, parser.Parse<Any>(json));
867 string valueFirstJson = "{ \"value\": \"1673-06-19T12:34:56Z\", \"@type\": \"type.googleapis.com/google.protobuf.Timestamp\" }";
868 Assert.AreEqual(original, parser.Parse<Any>(valueFirstJson));
869 }
870
871 [Test]
872 public void Any_Nested()
873 {
874 var registry = TypeRegistry.FromMessages(TestWellKnownTypes.Descriptor, TestAllTypes.Descriptor);
875 var formatter = new JsonFormatter(new JsonFormatter.Settings(false, registry));
876 var parser = new JsonParser(new JsonParser.Settings(10, registry));
877 var doubleNestedMessage = new TestAllTypes { SingleInt32 = 20 };
878 var nestedMessage = Any.Pack(doubleNestedMessage);
879 var message = new TestWellKnownTypes { AnyField = Any.Pack(nestedMessage) };
880 var json = formatter.Format(message);
881 // Use the descriptor-based parser just for a change.
882 Assert.AreEqual(message, parser.Parse(json, TestWellKnownTypes.Descriptor));
883 }
884
885 [Test]
886 public void DataAfterObject()
887 {
888 string json = "{} 10";
889 Assert.Throws<InvalidJsonException>(() => TestAllTypes.Parser.ParseJson(json));
890 }
891
892 /// <summary>
893 /// JSON equivalent to <see cref="CodedInputStreamTest.MaliciousRecursion"/>
894 /// </summary>
895 [Test]
896 public void MaliciousRecursion()
897 {
898 string data64 = CodedInputStreamTest.MakeRecursiveMessage(64).ToString();
899 string data65 = CodedInputStreamTest.MakeRecursiveMessage(65).ToString();
900
901 var parser64 = new JsonParser(new JsonParser.Settings(64));
902 CodedInputStreamTest.AssertMessageDepth(parser64.Parse<TestRecursiveMessage>(data64), 64);
903 Assert.Throws<InvalidProtocolBufferException>(() => parser64.Parse<TestRecursiveMessage>(data65));
904
905 var parser63 = new JsonParser(new JsonParser.Settings(63));
906 Assert.Throws<InvalidProtocolBufferException>(() => parser63.Parse<TestRecursiveMessage>(data64));
907 }
908
909 [Test]
910 [TestCase("AQI")]
911 [TestCase("_-==")]
912 public void Bytes_InvalidBase64(string badBase64)
913 {
914 string json = "{ \"singleBytes\": \"" + badBase64 + "\" }";
915 Assert.Throws<InvalidProtocolBufferException>(() => TestAllTypes.Parser.ParseJson(json));
916 }
917
918 [Test]
Austin Schuh40c16522018-10-28 20:27:54 -0700919 [TestCase("\"FOREIGN_BAR\"", ForeignEnum.ForeignBar)]
920 [TestCase("5", ForeignEnum.ForeignBar)]
921 [TestCase("100", (ForeignEnum)100)]
Brian Silverman9c614bc2016-02-15 20:20:02 -0500922 public void EnumValid(string value, ForeignEnum expectedValue)
923 {
924 string json = "{ \"singleForeignEnum\": " + value + " }";
925 var parsed = TestAllTypes.Parser.ParseJson(json);
926 Assert.AreEqual(new TestAllTypes { SingleForeignEnum = expectedValue }, parsed);
927 }
928
929 [Test]
930 [TestCase("\"NOT_A_VALID_VALUE\"")]
931 [TestCase("5.5")]
932 public void Enum_Invalid(string value)
933 {
934 string json = "{ \"singleForeignEnum\": " + value + " }";
935 Assert.Throws<InvalidProtocolBufferException>(() => TestAllTypes.Parser.ParseJson(json));
936 }
937
938 [Test]
939 public void OneofDuplicate_Invalid()
940 {
941 string json = "{ \"oneofString\": \"x\", \"oneofUint32\": 10 }";
942 Assert.Throws<InvalidProtocolBufferException>(() => TestAllTypes.Parser.ParseJson(json));
943 }
944
Austin Schuh40c16522018-10-28 20:27:54 -0700945 [Test]
946 public void UnknownField_NotIgnored()
947 {
948 string json = "{ \"unknownField\": 10, \"singleString\": \"x\" }";
949 Assert.Throws<InvalidProtocolBufferException>(() => TestAllTypes.Parser.ParseJson(json));
950 }
951
952 [Test]
953 [TestCase("5")]
954 [TestCase("\"text\"")]
955 [TestCase("[0, 1, 2]")]
956 [TestCase("{ \"a\": { \"b\": 10 } }")]
957 public void UnknownField_Ignored(string value)
958 {
959 var parser = new JsonParser(JsonParser.Settings.Default.WithIgnoreUnknownFields(true));
960 string json = "{ \"unknownField\": " + value + ", \"singleString\": \"x\" }";
961 var actual = parser.Parse<TestAllTypes>(json);
962 var expected = new TestAllTypes { SingleString = "x" };
963 Assert.AreEqual(expected, actual);
964 }
965
Brian Silverman9c614bc2016-02-15 20:20:02 -0500966 /// <summary>
967 /// Various tests use strings which have quotes round them for parsing or as the result
968 /// of formatting, but without those quotes being specified in the tests (for the sake of readability).
969 /// This method simply returns the input, wrapped in double quotes.
970 /// </summary>
971 internal static string WrapInQuotes(string text)
972 {
973 return '"' + text + '"';
974 }
975 }
Austin Schuh40c16522018-10-28 20:27:54 -0700976}