Fix handling of strings in System.Json
[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                 }
48
49                 [Test]
50                 public void QuoteEscapeBug_20869 () 
51                 {
52                         Assert.AreEqual ((new JsonPrimitive ("\"\"")).ToString (), "\"\\\"\\\"\"");
53                 }
54
55                 void ExpectError (string s)
56                 {
57                         try {
58                                 JsonValue.Parse (s);
59                                 Assert.Fail ("Expected ArgumentException for `" + s + "'");
60                         } catch (ArgumentException) {
61                         }
62                 }
63
64                 // Test whether an exception is thrown for invalid JSON
65                 [Test]
66                 public void CheckErrors () 
67                 {
68                         ExpectError (@"-");
69                         ExpectError (@"- ");
70                         ExpectError (@"1.");
71                         ExpectError (@"1. ");
72                         ExpectError (@"1e+");
73                         ExpectError (@"1 2");
74                         ExpectError (@"077");
75
76                         ExpectError (@"[1,]");
77
78                         //ExpectError (@"{""a"":1,}"); // Not valid JSON, allowed anyway
79                 }
80
81                 // Parse a json string and compare to the expected value
82                 void CheckDouble (double expected, string json)
83                 {
84                         double jvalue = (double) JsonValue.Parse (json);
85                         Assert.AreEqual (expected, jvalue);
86                 }
87
88                 // Convert a number to json and parse the string, then compare the result to the original value
89                 void CheckDouble (double number)
90                 {
91                         double jvalue = (double) JsonValue.Parse (new JsonPrimitive (number).ToString ());
92                         Assert.AreEqual (number, jvalue); // should be exactly the same
93                 }
94
95                 [Test]
96                 public void CheckNumbers () 
97                 {
98                         CheckDouble (0, "0");
99                         CheckDouble (0, "-0");
100                         CheckDouble (0, "0.00");
101                         CheckDouble (0, "-0.00");
102                         CheckDouble (1, "1");
103                         CheckDouble (1.1, "1.1");
104                         CheckDouble (-1, "-1");
105                         CheckDouble (-1.1, "-1.1");
106                         CheckDouble (1e-10, "1e-10");
107                         CheckDouble (1e+10, "1e+10");
108                         CheckDouble (1e-30, "1e-30");
109                         CheckDouble (1e+30, "1e+30");
110
111                         CheckDouble (1, "\"1\"");
112                         CheckDouble (1.1, "\"1.1\"");
113                         CheckDouble (-1, "\"-1\"");
114                         CheckDouble (-1.1, "\"-1.1\"");
115
116                         CheckDouble (double.NaN, "\"NaN\"");
117                         CheckDouble (double.PositiveInfinity, "\"Infinity\"");
118                         CheckDouble (double.NegativeInfinity, "\"-Infinity\"");
119
120                         ExpectError ("NaN");
121                         ExpectError ("Infinity");
122                         ExpectError ("-Infinity");
123
124                         Assert.AreEqual ("1.1", new JsonPrimitive (1.1).ToString ());
125                         Assert.AreEqual ("-1.1", new JsonPrimitive (-1.1).ToString ());
126                         Assert.AreEqual ("1E-20", new JsonPrimitive (1e-20).ToString ());
127                         Assert.AreEqual ("1E+20", new JsonPrimitive (1e+20).ToString ());
128                         Assert.AreEqual ("1E-30", new JsonPrimitive (1e-30).ToString ());
129                         Assert.AreEqual ("1E+30", new JsonPrimitive (1e+30).ToString ());
130                         Assert.AreEqual ("\"NaN\"", new JsonPrimitive (double.NaN).ToString ());
131                         Assert.AreEqual ("\"Infinity\"", new JsonPrimitive (double.PositiveInfinity).ToString ());
132                         Assert.AreEqual ("\"-Infinity\"", new JsonPrimitive (double.NegativeInfinity).ToString ());
133
134                         Assert.AreEqual ("1E-30", JsonValue.Parse ("1e-30").ToString ());
135                         Assert.AreEqual ("1E+30", JsonValue.Parse ("1e+30").ToString ());
136
137                         CheckDouble (1);
138                         CheckDouble (1.1);
139                         CheckDouble (1.25);
140                         CheckDouble (-1);
141                         CheckDouble (-1.1);
142                         CheckDouble (-1.25);
143                         CheckDouble (1e-20);
144                         CheckDouble (1e+20);
145                         CheckDouble (1e-30);
146                         CheckDouble (1e+30);
147                         CheckDouble (3.1415926535897932384626433);
148                         CheckDouble (3.1415926535897932384626433e-20);
149                         CheckDouble (3.1415926535897932384626433e+20);
150                         CheckDouble (double.NaN);
151                         CheckDouble (double.PositiveInfinity);
152                         CheckDouble (double.NegativeInfinity);
153                         CheckDouble (double.MinValue);
154                         CheckDouble (double.MaxValue);
155
156                         // 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)
157                         CheckDouble (18014398509481982.0);
158
159                         // Values around the smallest positive decimal value
160                         CheckDouble (1.123456789e-29);
161                         CheckDouble (1.123456789e-28);
162
163                         CheckDouble (1.1E-29, "0.000000000000000000000000000011");
164                         // This is being parsed as a decimal and rounded to 1e-28, even though it can be more accurately be represented by a double
165                         //CheckDouble (1.1E-28, "0.00000000000000000000000000011");
166                 }
167
168                 // Retry the test with different locales
169                 [Test]
170                 public void CheckNumbersCulture () 
171                 {
172                         CultureInfo old = Thread.CurrentThread.CurrentCulture;
173                         try {
174                                 Thread.CurrentThread.CurrentCulture = new CultureInfo ("en");
175                                 CheckNumbers ();
176                                 Thread.CurrentThread.CurrentCulture = new CultureInfo ("fr");
177                                 CheckNumbers ();
178                                 Thread.CurrentThread.CurrentCulture = new CultureInfo ("de");
179                                 CheckNumbers ();
180                         } finally {
181                                 Thread.CurrentThread.CurrentCulture = old;
182                         }
183                 }
184
185                 // Convert a string to json and parse the string, then compare the result to the original value
186                 void CheckString (string str)
187                 {
188                         var json = new JsonPrimitive (str).ToString ();
189                         // Check whether the string is valid Unicode (will throw for broken surrogate pairs)
190                         new UTF8Encoding (false, true).GetBytes (json);
191                         string jvalue = (string) JsonValue.Parse (json);
192                         Assert.AreEqual (str, jvalue);
193                 }
194                 
195                 // String handling: http://tools.ietf.org/html/rfc7159#section-7
196                 [Test]
197                 public void CheckStrings () 
198                 {
199                         Assert.AreEqual ("\"test\"", new JsonPrimitive ("test").ToString ());
200                         // Handling of characters
201                         Assert.AreEqual ("\"f\"", new JsonPrimitive ('f').ToString ());
202                         Assert.AreEqual ('f', (char) JsonValue.Parse ("\"f\""));
203
204                         // Control characters with special escape sequence
205                         Assert.AreEqual ("\"\\b\\f\\n\\r\\t\"", new JsonPrimitive ("\b\f\n\r\t").ToString ());
206                         // Other characters which must be escaped
207                         Assert.AreEqual (@"""\""\\""", new JsonPrimitive ("\"\\").ToString ());
208                         // Control characters without special escape sequence
209                         for (int i = 0; i < 32; i++)
210                                 if (i != '\b' && i != '\f' && i != '\n' && i != '\r' && i != '\t')
211                                         Assert.AreEqual ("\"\\u" + i.ToString ("x04") + "\"", new JsonPrimitive ("" + (char) i).ToString ());
212
213                         // JSON does not require U+2028 and U+2029 to be escaped, but
214                         // JavaScript does require this:
215                         // http://stackoverflow.com/questions/2965293/javascript-parse-error-on-u2028-unicode-character/9168133#9168133
216                         Assert.AreEqual ("\"\\u2028\\u2029\"", new JsonPrimitive ("\u2028\u2029").ToString ());
217
218                         // '/' also does not have to be escaped, but escaping it when
219                         // preceeded by a '<' avoids problems with JSON in HTML <script> tags
220                         Assert.AreEqual ("\"<\\/\"", new JsonPrimitive ("</").ToString ());
221                         // Don't escape '/' in other cases as this makes the JSON hard to read
222                         Assert.AreEqual ("\"/bar\"", new JsonPrimitive ("/bar").ToString ());
223                         Assert.AreEqual ("\"foo/bar\"", new JsonPrimitive ("foo/bar").ToString ());
224
225                         CheckString ("test\b\f\n\r\t\"\\/</\0x");
226                         for (int i = 0; i < 65536; i++)
227                                 CheckString ("x" + ((char) i));
228
229                         // Check broken surrogate pairs
230                         CheckString ("\ud800");
231                         CheckString ("x\ud800");
232                         CheckString ("\udfff\ud800");
233                         CheckString ("\ude03\ud912");
234                         CheckString ("\uc000\ubfff");
235                         CheckString ("\udfffx");
236                         // Valid strings should not be escaped:
237                         Assert.AreEqual ("\"\ud7ff\"", new JsonPrimitive ("\ud7ff").ToString ());
238                         Assert.AreEqual ("\"\ue000\"", new JsonPrimitive ("\ue000").ToString ());
239                         Assert.AreEqual ("\"\ud800\udc00\"", new JsonPrimitive ("\ud800\udc00").ToString ());
240                         Assert.AreEqual ("\"\ud912\ude03\"", new JsonPrimitive ("\ud912\ude03").ToString ());
241                         Assert.AreEqual ("\"\udbff\udfff\"", new JsonPrimitive ("\udbff\udfff").ToString ());
242                 }
243         }
244 }