Allow passing null to JsonArray.Add()
[mono.git] / mcs / class / System.Json / Test / System.Json / JsonValueTest.cs
1 //
2 // JsonValueTest.cs: Tests for JSonValue
3 //
4 // Copyright 2011 Xamarin, Inc.
5 //
6 // Authors:
7 //   Miguel de Icaza
8 //
9 using NUnit.Framework;
10 using System;
11 using System.IO;
12 using System.Text;
13 using System.Json;
14 using System.Globalization;
15 using System.Threading;
16
17 namespace MonoTests.System
18 {
19         [TestFixture]
20         public class JsonValueTests {
21                 // Tests that a trailing comma is allowed in dictionary definitions
22                 [Test]
23                 public void LoadWithTrailingComma ()
24                 {
25                         var j = JsonValue.Load (new StringReader ("{ \"a\": \"b\",}"));
26                         Assert.AreEqual (1, j.Count, "itemcount");
27                         Assert.AreEqual (JsonType.String, j ["a"].JsonType, "type");
28                         Assert.AreEqual ("b", (string) j ["a"], "value");
29
30                         JsonValue.Parse ("[{ \"a\": \"b\",}]");
31                 }
32
33                 [Test]
34                 public void LoadWithTrailingComma2 ()
35                 {
36                         JsonValue.Parse ("[{ \"a\": \"b\",}]");
37                 }
38
39                 // Test that we correctly serialize JsonArray with null elements.
40                 [Test]
41                 public void ToStringOnJsonArrayWithNulls () {
42                         var j = JsonValue.Load (new StringReader ("[1,2,3,null]"));
43                         Assert.AreEqual (4, j.Count, "itemcount");
44                         Assert.AreEqual (JsonType.Array, j.JsonType, "type");
45                         var str = j.ToString ();
46                         Assert.AreEqual (str, "[1, 2, 3, null]");
47                         ((JsonArray) j).Add (null);
48                         str = j.ToString ();
49                         Assert.AreEqual (str, "[1, 2, 3, null, null]");
50                 }
51
52                 // Test that we correctly serialize JsonObject with null elements.
53                 [Test]
54                 public void ToStringOnJsonObjectWithNulls () {
55                         var j = JsonValue.Load (new StringReader ("{\"a\":null,\"b\":2}"));
56                         Assert.AreEqual (2, j.Count, "itemcount");
57                         Assert.AreEqual (JsonType.Object, j.JsonType, "type");
58                         var str = j.ToString ();
59                         Assert.AreEqual (str, "{\"a\": null, \"b\": 2}");
60                 }
61
62                 [Test]
63                 public void JsonObjectOrder () {
64                         var obj = new JsonObject ();
65                         obj["a"] = 1;
66                         obj["c"] = 3;
67                         obj["b"] = 2;
68                         var str = obj.ToString ();
69                         Assert.AreEqual (str, "{\"a\": 1, \"b\": 2, \"c\": 3}");
70                 }
71
72                 [Test]
73                 public void QuoteEscapeBug_20869 () 
74                 {
75                         Assert.AreEqual ((new JsonPrimitive ("\"\"")).ToString (), "\"\\\"\\\"\"");
76                 }
77
78                 void ExpectError (string s)
79                 {
80                         try {
81                                 JsonValue.Parse (s);
82                                 Assert.Fail ("Expected ArgumentException for `" + s + "'");
83                         } catch (ArgumentException) {
84                         }
85                 }
86
87                 // Test whether an exception is thrown for invalid JSON
88                 [Test]
89                 public void CheckErrors () 
90                 {
91                         ExpectError (@"-");
92                         ExpectError (@"- ");
93                         ExpectError (@"1.");
94                         ExpectError (@"1. ");
95                         ExpectError (@"1e+");
96                         ExpectError (@"1 2");
97                         ExpectError (@"077");
98
99                         ExpectError (@"[1,]");
100
101                         //ExpectError (@"{""a"":1,}"); // Not valid JSON, allowed anyway
102                 }
103
104                 // Parse a json string and compare to the expected value
105                 void CheckDouble (double expected, string json)
106                 {
107                         double jvalue = (double) JsonValue.Parse (json);
108                         Assert.AreEqual (expected, jvalue);
109                 }
110
111                 // Convert a number to json and parse the string, then compare the result to the original value
112                 void CheckDouble (double number)
113                 {
114                         double jvalue = (double) JsonValue.Parse (new JsonPrimitive (number).ToString ());
115                         Assert.AreEqual (number, jvalue); // should be exactly the same
116                 }
117
118                 [Test]
119                 public void CheckIntegers ()
120                 {
121                         Assert.AreEqual (sbyte.MinValue, (sbyte) JsonValue.Parse (new JsonPrimitive (sbyte.MinValue).ToString ()));
122                         Assert.AreEqual (sbyte.MaxValue, (sbyte) JsonValue.Parse (new JsonPrimitive (sbyte.MaxValue).ToString ()));
123                         Assert.AreEqual (byte.MinValue, (byte) JsonValue.Parse (new JsonPrimitive (byte.MinValue).ToString ()));
124                         Assert.AreEqual (byte.MaxValue, (byte) JsonValue.Parse (new JsonPrimitive (byte.MaxValue).ToString ()));
125
126                         Assert.AreEqual (short.MinValue, (short) JsonValue.Parse (new JsonPrimitive (short.MinValue).ToString ()));
127                         Assert.AreEqual (short.MaxValue, (short) JsonValue.Parse (new JsonPrimitive (short.MaxValue).ToString ()));
128                         Assert.AreEqual (ushort.MinValue, (ushort) JsonValue.Parse (new JsonPrimitive (ushort.MinValue).ToString ()));
129                         Assert.AreEqual (ushort.MaxValue, (ushort) JsonValue.Parse (new JsonPrimitive (ushort.MaxValue).ToString ()));
130
131                         Assert.AreEqual (int.MinValue, (int) JsonValue.Parse (new JsonPrimitive (int.MinValue).ToString ()));
132                         Assert.AreEqual (int.MaxValue, (int) JsonValue.Parse (new JsonPrimitive (int.MaxValue).ToString ()));
133                         Assert.AreEqual (uint.MinValue, (uint) JsonValue.Parse (new JsonPrimitive (uint.MinValue).ToString ()));
134                         Assert.AreEqual (uint.MaxValue, (uint) JsonValue.Parse (new JsonPrimitive (uint.MaxValue).ToString ()));
135
136                         Assert.AreEqual (long.MinValue, (long) JsonValue.Parse (new JsonPrimitive (long.MinValue).ToString ()));
137                         Assert.AreEqual (long.MaxValue, (long) JsonValue.Parse (new JsonPrimitive (long.MaxValue).ToString ()));
138                         Assert.AreEqual (ulong.MinValue, (ulong) JsonValue.Parse (new JsonPrimitive (ulong.MinValue).ToString ()));
139                         Assert.AreEqual (ulong.MaxValue, (ulong) JsonValue.Parse (new JsonPrimitive (ulong.MaxValue).ToString ()));
140                 }
141
142                 [Test]
143                 public void CheckNumbers () 
144                 {
145                         CheckDouble (0, "0");
146                         CheckDouble (0, "-0");
147                         CheckDouble (0, "0.00");
148                         CheckDouble (0, "-0.00");
149                         CheckDouble (1, "1");
150                         CheckDouble (1.1, "1.1");
151                         CheckDouble (-1, "-1");
152                         CheckDouble (-1.1, "-1.1");
153                         CheckDouble (1e-10, "1e-10");
154                         CheckDouble (1e+10, "1e+10");
155                         CheckDouble (1e-30, "1e-30");
156                         CheckDouble (1e+30, "1e+30");
157
158                         CheckDouble (1, "\"1\"");
159                         CheckDouble (1.1, "\"1.1\"");
160                         CheckDouble (-1, "\"-1\"");
161                         CheckDouble (-1.1, "\"-1.1\"");
162
163                         CheckDouble (double.NaN, "\"NaN\"");
164                         CheckDouble (double.PositiveInfinity, "\"Infinity\"");
165                         CheckDouble (double.NegativeInfinity, "\"-Infinity\"");
166
167                         ExpectError ("NaN");
168                         ExpectError ("Infinity");
169                         ExpectError ("-Infinity");
170
171                         Assert.AreEqual ("1.1", new JsonPrimitive (1.1).ToString ());
172                         Assert.AreEqual ("-1.1", new JsonPrimitive (-1.1).ToString ());
173                         Assert.AreEqual ("1E-20", new JsonPrimitive (1e-20).ToString ());
174                         Assert.AreEqual ("1E+20", new JsonPrimitive (1e+20).ToString ());
175                         Assert.AreEqual ("1E-30", new JsonPrimitive (1e-30).ToString ());
176                         Assert.AreEqual ("1E+30", new JsonPrimitive (1e+30).ToString ());
177                         Assert.AreEqual ("\"NaN\"", new JsonPrimitive (double.NaN).ToString ());
178                         Assert.AreEqual ("\"Infinity\"", new JsonPrimitive (double.PositiveInfinity).ToString ());
179                         Assert.AreEqual ("\"-Infinity\"", new JsonPrimitive (double.NegativeInfinity).ToString ());
180
181                         Assert.AreEqual ("1E-30", JsonValue.Parse ("1e-30").ToString ());
182                         Assert.AreEqual ("1E+30", JsonValue.Parse ("1e+30").ToString ());
183
184                         CheckDouble (1);
185                         CheckDouble (1.1);
186                         CheckDouble (1.25);
187                         CheckDouble (-1);
188                         CheckDouble (-1.1);
189                         CheckDouble (-1.25);
190                         CheckDouble (1e-20);
191                         CheckDouble (1e+20);
192                         CheckDouble (1e-30);
193                         CheckDouble (1e+30);
194                         CheckDouble (3.1415926535897932384626433);
195                         CheckDouble (3.1415926535897932384626433e-20);
196                         CheckDouble (3.1415926535897932384626433e+20);
197                         CheckDouble (double.NaN);
198                         CheckDouble (double.PositiveInfinity);
199                         CheckDouble (double.NegativeInfinity);
200                         CheckDouble (double.MinValue);
201                         CheckDouble (double.MaxValue);
202
203                         // A number which needs 17 digits (see http://stackoverflow.com/questions/6118231/why-do-i-need-17-significant-digits-and-not-16-to-represent-a-double)
204                         CheckDouble (18014398509481982.0);
205
206                         // Values around the smallest positive decimal value
207                         CheckDouble (1.123456789e-29);
208                         CheckDouble (1.123456789e-28);
209
210                         CheckDouble (1.1E-29, "0.000000000000000000000000000011");
211                         // This is being parsed as a decimal and rounded to 1e-28, even though it can be more accurately be represented by a double
212                         //CheckDouble (1.1E-28, "0.00000000000000000000000000011");
213                 }
214
215                 // Retry the test with different locales
216                 [Test]
217                 public void CheckNumbersCulture () 
218                 {
219                         CultureInfo old = Thread.CurrentThread.CurrentCulture;
220                         try {
221                                 Thread.CurrentThread.CurrentCulture = new CultureInfo ("en");
222                                 CheckNumbers ();
223                                 Thread.CurrentThread.CurrentCulture = new CultureInfo ("fr");
224                                 CheckNumbers ();
225                                 Thread.CurrentThread.CurrentCulture = new CultureInfo ("de");
226                                 CheckNumbers ();
227                         } finally {
228                                 Thread.CurrentThread.CurrentCulture = old;
229                         }
230                 }
231
232                 // Convert a string to json and parse the string, then compare the result to the original value
233                 void CheckString (string str)
234                 {
235                         var json = new JsonPrimitive (str).ToString ();
236                         // Check whether the string is valid Unicode (will throw for broken surrogate pairs)
237                         new UTF8Encoding (false, true).GetBytes (json);
238                         string jvalue = (string) JsonValue.Parse (json);
239                         Assert.AreEqual (str, jvalue);
240                 }
241                 
242                 // String handling: http://tools.ietf.org/html/rfc7159#section-7
243                 [Test]
244                 public void CheckStrings () 
245                 {
246                         Assert.AreEqual ("\"test\"", new JsonPrimitive ("test").ToString ());
247                         // Handling of characters
248                         Assert.AreEqual ("\"f\"", new JsonPrimitive ('f').ToString ());
249                         Assert.AreEqual ('f', (char) JsonValue.Parse ("\"f\""));
250
251                         // Control characters with special escape sequence
252                         Assert.AreEqual ("\"\\b\\f\\n\\r\\t\"", new JsonPrimitive ("\b\f\n\r\t").ToString ());
253                         // Other characters which must be escaped
254                         Assert.AreEqual (@"""\""\\""", new JsonPrimitive ("\"\\").ToString ());
255                         // Control characters without special escape sequence
256                         for (int i = 0; i < 32; i++)
257                                 if (i != '\b' && i != '\f' && i != '\n' && i != '\r' && i != '\t')
258                                         Assert.AreEqual ("\"\\u" + i.ToString ("x04") + "\"", new JsonPrimitive ("" + (char) i).ToString ());
259
260                         // JSON does not require U+2028 and U+2029 to be escaped, but
261                         // JavaScript does require this:
262                         // http://stackoverflow.com/questions/2965293/javascript-parse-error-on-u2028-unicode-character/9168133#9168133
263                         Assert.AreEqual ("\"\\u2028\\u2029\"", new JsonPrimitive ("\u2028\u2029").ToString ());
264
265                         // '/' also does not have to be escaped, but escaping it when
266                         // preceeded by a '<' avoids problems with JSON in HTML <script> tags
267                         Assert.AreEqual ("\"<\\/\"", new JsonPrimitive ("</").ToString ());
268                         // Don't escape '/' in other cases as this makes the JSON hard to read
269                         Assert.AreEqual ("\"/bar\"", new JsonPrimitive ("/bar").ToString ());
270                         Assert.AreEqual ("\"foo/bar\"", new JsonPrimitive ("foo/bar").ToString ());
271
272                         CheckString ("test\b\f\n\r\t\"\\/</\0x");
273                         for (int i = 0; i < 65536; i++)
274                                 CheckString ("x" + ((char) i));
275
276                         // Check broken surrogate pairs
277                         CheckString ("\ud800");
278                         CheckString ("x\ud800");
279                         CheckString ("\udfff\ud800");
280                         CheckString ("\ude03\ud912");
281                         CheckString ("\uc000\ubfff");
282                         CheckString ("\udfffx");
283                         // Valid strings should not be escaped:
284                         Assert.AreEqual ("\"\ud7ff\"", new JsonPrimitive ("\ud7ff").ToString ());
285                         Assert.AreEqual ("\"\ue000\"", new JsonPrimitive ("\ue000").ToString ());
286                         Assert.AreEqual ("\"\ud800\udc00\"", new JsonPrimitive ("\ud800\udc00").ToString ());
287                         Assert.AreEqual ("\"\ud912\ude03\"", new JsonPrimitive ("\ud912\ude03").ToString ());
288                         Assert.AreEqual ("\"\udbff\udfff\"", new JsonPrimitive ("\udbff\udfff").ToString ());
289                 }
290         }
291 }
292
293 // vim: noexpandtab
294 // Local Variables:
295 // tab-width: 4
296 // c-basic-offset: 4
297 // indent-tabs-mode: t
298 // End: