• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 }