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