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
33using Google.Protobuf.Reflection;
34using Google.Protobuf.TestProtos;
35using Google.Protobuf.WellKnownTypes;
36using NUnit.Framework;
37using ProtobufTestMessages.Proto2;
38using System;
39using UnitTest.Issues.TestProtos;
40
41namespace Google.Protobuf
42{
43    /// <summary>
44    /// Unit tests for JSON parsing.
45    /// </summary>
46    public class JsonParserTest
47    {
48        // Sanity smoke test
49        [Test]
50        public void AllTypesRoundtrip()
51        {
52            AssertRoundtrip(SampleMessages.CreateFullTestAllTypes());
53        }
54
55        [Test]
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")]
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]
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]
85        public void SourceContextRoundtrip()
86        {
87            AssertRoundtrip(new SourceContext { FileName = "foo.proto" });
88        }
89
90        [Test]
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]
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]
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)]
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]
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]
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]
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]
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]
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 }")]
214        public void MapField_NullValueProhibited(string json)
215        {
216            Assert.Throws<InvalidProtocolBufferException>(() => TestMap.Parser.ParseJson(json));
217        }
218
219        [Test]
220        public void MapField_NullOverallValueAllowed()
221        {
222            string json = "{ \"mapInt32Int32\": null }";
223            Assert.AreEqual(new TestMap(), TestMap.Parser.ParseJson(json));
224        }
225
226        [Test]
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)]
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")]
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)]
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")]
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)]
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")]
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)]
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")]
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)]
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...
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)]
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")]
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)]
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))]
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)]
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")]
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)]
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")]
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)]
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")]
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)]
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...
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)]
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))]
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)]
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")]
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]
676        public void StructValue_Null()
677        {
678            Assert.AreEqual(new Value { NullValue = 0 }, Value.Parser.ParseJson("null"));
679        }
680
681        [Test]
682        public void StructValue_String()
683        {
684            Assert.AreEqual(new Value { StringValue = "hi" }, Value.Parser.ParseJson("\"hi\""));
685        }
686
687        [Test]
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]
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]
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]
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]
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]
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]
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)]
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)")]
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")]
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")]
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]
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]
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]
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]
853        public void Any_NoTypeUrl()
854        {
855            string json = "{ \"foo\": \"bar\" }";
856            Assert.Throws<InvalidProtocolBufferException>(() => Any.Parser.ParseJson(json));
857        }
858
859        [Test]
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]
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]
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]
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("_-==")]
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)]
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")]
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]
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]
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]
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 } }")]
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]
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]
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]
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]
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>
1020        internal static string WrapInQuotes(string text)
1021        {
1022            return '"' + text + '"';
1023        }
1024    }
1025}