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