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