• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2022 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 //
15 ////////////////////////////////////////////////////////////////////////////////
16 
17 package com.google.crypto.tink.internal;
18 
19 import static com.google.common.truth.Truth.assertThat;
20 import static org.junit.Assert.assertThrows;
21 
22 import com.google.gson.JsonArray;
23 import com.google.gson.JsonElement;
24 import com.google.gson.JsonNull;
25 import com.google.gson.JsonObject;
26 import com.google.gson.JsonPrimitive;
27 import java.io.ByteArrayOutputStream;
28 import java.io.IOException;
29 import java.io.NotSerializableException;
30 import java.io.ObjectOutputStream;
31 import java.math.BigInteger;
32 import org.junit.Test;
33 import org.junit.experimental.theories.DataPoints;
34 import org.junit.experimental.theories.FromDataPoints;
35 import org.junit.experimental.theories.Theories;
36 import org.junit.experimental.theories.Theory;
37 import org.junit.runner.RunWith;
38 
39 /**
40  * Tests for Tink's internal {@link JsonParser}.
41  */
42 @RunWith(Theories.class)
43 public final class JsonParserTest {
44 
45   public static final class TestCase {
46     public final String name;
47     public final String input;
48     public final JsonElement expected;
49 
TestCase(String name, String input, JsonElement expected)50     public TestCase(String name, String input, JsonElement expected) {
51       this.name = name;
52       this.input = input;
53       this.expected = expected;
54     }
55 
TestCase(String name, String input)56     public TestCase(String name, String input) {
57       this.name = name;
58       this.input = input;
59       this.expected = null;
60     }
61 
62     @Override
toString()63     public String toString() {
64       return name;
65     }
66   }
67 
jsonArray(JsonElement... elements)68   public static JsonArray jsonArray(JsonElement... elements) {
69     JsonArray output = new JsonArray();
70     for (JsonElement element : elements) {
71       output.add(element);
72     }
73     return output;
74   }
75 
jsonObject(String name, JsonElement value)76   public static JsonObject jsonObject(String name, JsonElement value) {
77     JsonObject output = new JsonObject();
78     output.add(name, value);
79     return output;
80   }
81 
82   @DataPoints("testCasesSuccess")
83   public static final TestCase[] TEST_CASES_SUCCESS = {
84     new TestCase("string", "\"xyz\"", new JsonPrimitive("xyz")),
85     new TestCase("number", "42", new JsonPrimitive(42)),
86     new TestCase("negative_number", "-42", new JsonPrimitive(-42)),
87     new TestCase("float", "42.42", new JsonPrimitive(42.42)),
88     new TestCase("negative_float", "-42.42", new JsonPrimitive(-42.42)),
89     new TestCase("true", "true", new JsonPrimitive(true)),
90     new TestCase("false", "false", new JsonPrimitive(false)),
91     new TestCase("null", "null", JsonNull.INSTANCE),
92     new TestCase(
93         "array", "[\"a\",\"b\"]", jsonArray(new JsonPrimitive("a"), new JsonPrimitive("b"))),
94     new TestCase("map", "{\"a\":\"b\"}", jsonObject("a", new JsonPrimitive("b"))),
95     new TestCase("empty_string", "\"\"", new JsonPrimitive("")),
96     new TestCase("empty_array", "[]", new JsonArray()),
97     new TestCase("array_with_newline", "[\n]", new JsonArray()),
98     new TestCase("array_with_tab", "[\t]", new JsonArray()),
99     new TestCase("empty_map", "{}", new JsonObject()),
100     new TestCase("map_with_empty_key", "{\"\":\"a\"}", jsonObject("", new JsonPrimitive("a"))),
101     new TestCase(
102         "nested_arrays",
103         "[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]",
104         jsonArray(
105             jsonArray(
106                 jsonArray(
107                     jsonArray(
108                         jsonArray(
109                             jsonArray(
110                                 jsonArray(
111                                     jsonArray(
112                                         jsonArray(
113                                             jsonArray(
114                                                 jsonArray(
115                                                     jsonArray(
116                                                         jsonArray(
117                                                             jsonArray(
118                                                                 jsonArray(
119                                                                     jsonArray())))))))))))))))),
120     new TestCase(
121         "nested_maps",
122         "{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{}}}}}}}}}}}",
123         jsonObject(
124             "a",
125             jsonObject(
126                 "a",
127                 jsonObject(
128                     "a",
129                     jsonObject(
130                         "a",
131                         jsonObject(
132                             "a",
133                             jsonObject(
134                                 "a",
135                                 jsonObject(
136                                     "a",
137                                     jsonObject(
138                                         "a",
139                                         jsonObject("a", jsonObject("a", new JsonObject()))))))))))),
140     new TestCase("tRuE", "tRuE", new JsonPrimitive(true)),
141     new TestCase("fAlSe", "fAlSe", new JsonPrimitive(false)),
142     new TestCase("nUlL", "nUlL", JsonNull.INSTANCE),
143     new TestCase(
144         "mixed_array",
145         "[\"a\", null, 1, 0.1, true, {\"a\":0}, [4]]",
146         jsonArray(
147             new JsonPrimitive("a"),
148             JsonNull.INSTANCE,
149             new JsonPrimitive(1),
150             new JsonPrimitive(0.1),
151             new JsonPrimitive(true),
152             jsonObject("a", new JsonPrimitive(0)),
153             jsonArray(new JsonPrimitive(4)))),
154     new TestCase("tailing_newline", "\"a\"\n", new JsonPrimitive("a")),
155     new TestCase(
156         "whitespace", " { \"a\"\n: \n\"b\" \n } \n ", jsonObject("a", new JsonPrimitive("b"))),
157     new TestCase("string_with_comment", "\"a/*b*/c\"", new JsonPrimitive("a/*b*/c")),
158     new TestCase("string_with_excaped_unicode", "\"\\uA66D\"", new JsonPrimitive("ꙭ")),
159     new TestCase("valid_utf16", "\"\\uD83D\\uDC69\"", new JsonPrimitive("��")),
160     new TestCase("valid_UTF8_1", "\"\\u002c\"", new JsonPrimitive(",")),
161     new TestCase("valid_UTF8_3", "\"\\u0123\"", new JsonPrimitive("ģ")),
162     new TestCase("escapes", "\"\\\"\\\\\\/\\b\\f\\n\\r\\t\"", new JsonPrimitive("\"\\/\b\f\n\r\t")),
163     new TestCase("newline", "\"a\\u000Ab\"", new JsonPrimitive("a\nb")),
164     new TestCase("backslash", "\"\\u005C\"", new JsonPrimitive("\\")),
165     new TestCase("double_quote", "\"\\u0022\"", new JsonPrimitive("\"")),
166     new TestCase(
167         "escaped_double_quote_in_key",
168         "{\"\\\"\\\"\": 42}",
169         jsonObject("\"\"", new JsonPrimitive(42))),
170     new TestCase("escaped_null", "\"\\u0000\"", new JsonPrimitive("" + (char) 0x00)),
171     new TestCase(
172         "escaped_null_in_key",
173         "{\"a\\u0000b\": 42}",
174         jsonObject("a\u0000b", new JsonPrimitive(42))),
175     new TestCase("invalid_UTF8", "\"日ш\"", new JsonPrimitive("日ш")),
176 
177     new TestCase("long_max_value", "9223372036854775807", new JsonPrimitive(9223372036854775807L)),
178     new TestCase("big_float", "60911552482230981.0", new JsonPrimitive(6.0911552482230984e16)),
179     new TestCase("exp", "4e+42", new JsonPrimitive(4e+42)),
180     new TestCase("exp2", "4e42", new JsonPrimitive(4e+42)),
181     new TestCase("Exp", "4E42", new JsonPrimitive(4e+42)),
182     new TestCase("-exp", "-4e-42", new JsonPrimitive(-4e-42)),
183     new TestCase("number_tailing_space", "42 ", new JsonPrimitive(42)),
184     new TestCase("number_tailing_newline", "42\n", new JsonPrimitive(42)),
185     new TestCase("number_tailing_formfeed", "42\f", new JsonPrimitive(42)),
186     new TestCase(
187         "float_close_to_zero",
188         "0.000000000000000000000000000000001",
189         new JsonPrimitive(0.000000000000000000000000000000001)),
190     new TestCase(
191         "-float_close_to_zero",
192         "-0.000000000000000000000000000000001",
193         new JsonPrimitive(-0.000000000000000000000000000000001)),
194     new TestCase(
195         "huge_number",
196         "999999999999999999999999999999999999999999999999999999999999999999999999999999999999999",
197         new JsonPrimitive(1e87)),
198     new TestCase(
199         "-huge_number",
200         "-999999999999999999999999999999999999999999999999999999999999999999999999999999999999999",
201         new JsonPrimitive(-1e87)),
202     new TestCase("string_with_tailing_comma", "\"a\",", new JsonPrimitive("a")),
203     new TestCase("number_with_tailing_comma", "42,", new JsonPrimitive(42)),
204     new TestCase("true_with_tailing_comma", "true,", new JsonPrimitive(true)),
205     new TestCase("string_with_tailing_comment", "\"a\"/*comment*/", new JsonPrimitive("a")),
206     new TestCase("map_with_tailing_comma", "{\"a\":1},", jsonObject("a", new JsonPrimitive(1))),
207     new TestCase(
208         "map_with_tailing_comment", "{\"a\":1}/*comment*/", jsonObject("a", new JsonPrimitive(1))),
209     new TestCase(
210         "map_with_tailing_open_comment",
211         "{\"a\":1}/*comment",
212         jsonObject("a", new JsonPrimitive(1))),
213     new TestCase("map_with_tailing_#", "{\"a\":1}#", jsonObject("a", new JsonPrimitive(1))),
214     new TestCase("map_with_tailing_]", "{\"a\":1}]", jsonObject("a", new JsonPrimitive(1))),
215     new TestCase("map_with_tailing_}", "{\"a\":1}}", jsonObject("a", new JsonPrimitive(1))),
216     new TestCase("array_with_tailing_comma", "[\"a\"],", jsonArray(new JsonPrimitive("a"))),
217     new TestCase(
218         "array_with_tailing_comment", "[\"a\"]/*comment*/", jsonArray(new JsonPrimitive("a"))),
219     new TestCase(
220         "array_with_tailing_open_comment", "[\"a\"]/*comment", jsonArray(new JsonPrimitive("a"))),
221     new TestCase("array_with_tailing_#", "[\"a\"]#", jsonArray(new JsonPrimitive("a"))),
222     new TestCase("array_with_tailing_]", "[\"a\"]]", jsonArray(new JsonPrimitive("a"))),
223     new TestCase("array_with_tailing_}", "[\"a\"]}", jsonArray(new JsonPrimitive("a"))),
224     new TestCase("double_array", "[][]", new JsonArray()),
225     new TestCase("number_with_space", "42 000", new JsonPrimitive(42)),
226     new TestCase("float_with_space", "42 000.0", new JsonPrimitive(42)),
227   };
228 
229   @Theory
parse_asExpected( @romDataPoints"testCasesSuccess") TestCase testCase)230   public void parse_asExpected(
231       @FromDataPoints("testCasesSuccess") TestCase testCase) throws Exception {
232     JsonElement output = JsonParser.parse(testCase.input);
233 
234     assertThat(output).isEqualTo(testCase.expected);
235   }
236 
237   @Test
parsedElementWithNumberToString_doesNotLoosePrecision()238   public void parsedElementWithNumberToString_doesNotLoosePrecision() throws Exception {
239     JsonElement element = JsonParser.parse("{ \"a\": 9223372036854775807 }");
240     assertThat(element.toString()).isEqualTo("{\"a\":9223372036854775807}");
241   }
242 
243   @Test
parsedNumberSerializeDeserialize_returnsBigDecimal()244   public void parsedNumberSerializeDeserialize_returnsBigDecimal() throws Exception {
245     JsonElement numElement = JsonParser.parse("42");
246     Number num = numElement.getAsNumber();
247     ByteArrayOutputStream bytes = new ByteArrayOutputStream();
248     ObjectOutputStream out = new ObjectOutputStream(bytes);
249     assertThrows(NotSerializableException.class, () -> out.writeObject(num));
250   }
251 
252   @Test
parsedNumberGetValue()253   public void parsedNumberGetValue() throws Exception {
254     JsonElement numElement = JsonParser.parse("42.42");
255     assertThat(numElement.getAsInt()).isEqualTo(42);
256     assertThat(numElement.getAsLong()).isEqualTo(42);
257     assertThat(numElement.getAsFloat()).isEqualTo(42.42f);
258     assertThat(numElement.getAsDouble()).isEqualTo(42.42);
259     Number number = numElement.getAsNumber();
260     assertThat(number.intValue()).isEqualTo(42);
261     assertThat(number.longValue()).isEqualTo(42);
262     assertThat(number.floatValue()).isEqualTo(42.42f);
263     assertThat(number.doubleValue()).isEqualTo(42.42);
264   }
265 
266   @Test
parsedNumberGetAsLong_discardsAllBut64LowestOrderBits()267   public void parsedNumberGetAsLong_discardsAllBut64LowestOrderBits() throws Exception {
268     // It would be preferable if JsonElement.getAsLong would throw a NumberFormatException exception
269     // if the number it contains does not fit into a long, similar to what Long.parseLong does.
270 
271     // Instead, the method never throws an exception, and follows the "narrowing primitive
272     // conversion" of the Java Language Specification section 5.1.3, which means that all but the 32
273     // lowest order bits are discarded.
274 
275     JsonElement numElement = JsonParser.parse("9223372036854775809"); // 2^63 + 1
276     assertThat(numElement.getAsLong()).isEqualTo(-9223372036854775807L);
277   }
278 
279   @Test
parsedNumberGetAsInt_discardsAllBut32LowestOrderBits()280   public void parsedNumberGetAsInt_discardsAllBut32LowestOrderBits() throws Exception {
281     // It would be preferable if JsonElement.getAsInt would throw a NumberFormatException exception
282     // if the number it contains does not fit into a long, similar to what Int.parseInt does.
283 
284     // Instead, the method never throws an exception, and follows the "narrowing primitive
285     // conversion" of the Java Language Specification section 5.1.3, which means that all but the 32
286     // lowest order bits are discarded.
287 
288     JsonElement numElement = JsonParser.parse("2147483649"); // 2^31 + 1
289     assertThat(numElement.getAsInt()).isEqualTo(-2147483647);
290   }
291 
292   @DataPoints("longs")
293   public static final String[] LONGS =
294       new String[] {
295         "0",
296         "42",
297         "-42",
298         "2147483647", // 2^31 - 1
299         "-2147483648", // - 2^31
300         "2147483649", // 2^31 + 1
301         "44444444444444444",
302         "9223372036854775807",  // 2^63 - 1
303         "-9223372036854775808",  // - 2^63
304       };
305 
306   @DataPoints("biggerThanLongs")
307   public static final String[] BIGGER_THAN_LONGS =
308       new String[] {
309         "9223372036854775809",  // 2^63 + 1
310         "18446744073709551658",  // 2^64 + 42
311         "9999999999999999999999999999999999999999999999999999999999999999",
312         "-9999999999999999999999999999999999999999999999999999999999999999",
313       };
314 
315   @Theory
parsedNumberGetAsLong_validLong_sameAsParseLong( @romDataPoints"longs") String numString)316   public void parsedNumberGetAsLong_validLong_sameAsParseLong(
317       @FromDataPoints("longs") String numString) throws Exception {
318     JsonElement parsed = JsonParser.parse(numString);
319     assertThat(parsed.getAsLong()).isEqualTo(Long.parseLong(numString));
320   }
321 
322   @Theory
parsedNumberGetAsLong_biggerThanLong_sameAsBigIntegerLongValue( @romDataPoints"biggerThanLongs") String numString)323   public void parsedNumberGetAsLong_biggerThanLong_sameAsBigIntegerLongValue(
324       @FromDataPoints("biggerThanLongs") String numString) throws Exception {
325     JsonElement parsed = JsonParser.parse(numString);
326     assertThat(parsed.getAsLong()).isEqualTo(new BigInteger(numString).longValue());
327   }
328 
329   @Theory
parsedNumberGetAsInt_validLong_sameAsBigIntegerIntValue( @romDataPoints"longs") String numString)330   public void parsedNumberGetAsInt_validLong_sameAsBigIntegerIntValue(
331       @FromDataPoints("longs") String numString) throws Exception {
332     JsonElement parsed = JsonParser.parse(numString);
333     assertThat(parsed.getAsInt()).isEqualTo(new BigInteger(numString).intValue());
334   }
335 
336   @Theory
parsedNumberGetAsInt_biggerThanLong_sameAsBigIntegerIntValue( @romDataPoints"biggerThanLongs") String numString)337   public void parsedNumberGetAsInt_biggerThanLong_sameAsBigIntegerIntValue(
338       @FromDataPoints("biggerThanLongs") String numString) throws Exception {
339     JsonElement parsed = JsonParser.parse(numString);
340     assertThat(parsed.getAsInt()).isEqualTo(new BigInteger(numString).intValue());
341   }
342 
343   @Theory
getParsedNumberAsLongOrThrow_validLong_sameAsParseLong( @romDataPoints"longs") String numString)344   public void getParsedNumberAsLongOrThrow_validLong_sameAsParseLong(
345       @FromDataPoints("longs") String numString) throws Exception {
346     Number parsed = JsonParser.parse(numString).getAsNumber();
347     assertThat(JsonParser.getParsedNumberAsLongOrThrow(parsed))
348         .isEqualTo(Long.parseLong(numString));
349   }
350 
351   @Theory
getParsedNumberAsLongOrThrow_validLong_sameAsLongValue( @romDataPoints"longs") String numString)352   public void getParsedNumberAsLongOrThrow_validLong_sameAsLongValue(
353       @FromDataPoints("longs") String numString) throws Exception {
354     Number parsed = JsonParser.parse(numString).getAsNumber();
355     assertThat(JsonParser.getParsedNumberAsLongOrThrow(parsed)).isEqualTo(parsed.longValue());
356   }
357 
358   @Theory
getParsedNumberAsLongOrThrow_biggerThanLong_throws( @romDataPoints"biggerThanLongs") String numString)359   public void getParsedNumberAsLongOrThrow_biggerThanLong_throws(
360       @FromDataPoints("biggerThanLongs") String numString) throws Exception {
361     Number parsed = JsonParser.parse(numString).getAsNumber();
362     assertThrows(
363         NumberFormatException.class, () -> JsonParser.getParsedNumberAsLongOrThrow(parsed));
364   }
365 
366   @Theory
getParsedNumberAsLongOrThrow_nestedValue_success()367   public void getParsedNumberAsLongOrThrow_nestedValue_success() throws Exception {
368     JsonElement parsed = JsonParser.parse("{\"a\":{\"b\":9223372036854775807}}");
369     Number parsedNumber =
370         parsed.getAsJsonObject().get("a").getAsJsonObject().get("b").getAsNumber();
371     long output = JsonParser.getParsedNumberAsLongOrThrow(parsedNumber);
372     assertThat(output).isEqualTo(9223372036854775807L);
373   }
374 
375   @Theory
getParsedNumberAsLongOrThrow_notParsed_throws()376   public void getParsedNumberAsLongOrThrow_notParsed_throws() throws Exception {
377     Number notParsedJsonElementWithNumber = new JsonPrimitive(42).getAsNumber();
378     assertThrows(
379         IllegalArgumentException.class,
380         () -> JsonParser.getParsedNumberAsLongOrThrow(notParsedJsonElementWithNumber));
381   }
382 
383   @Theory
floatNumbersGetAsLong_getsTruncated()384   public void floatNumbersGetAsLong_getsTruncated() throws Exception {
385     assertThat(JsonParser.parse("42.0").getAsLong()).isEqualTo(42);
386     assertThat(JsonParser.parse("2.1e1").getAsLong()).isEqualTo(21);
387 
388     assertThat(JsonParser.parse("42.1").getAsLong()).isEqualTo(42);
389     assertThat(JsonParser.parse("42.9999").getAsLong()).isEqualTo(42);
390 
391     // 2^63 - 1 as float
392     assertThat(JsonParser.parse("9.223372036854775807e18").getAsLong())
393         .isEqualTo(9223372036854775807L);
394 
395     // - 2^63 as float
396     assertThat(JsonParser.parse("-9.223372036854775808e18").getAsLong())
397         .isEqualTo(-9223372036854775808L);
398   }
399 
400   @Theory
floatNumbersGetAsInt_getsTruncated()401   public void floatNumbersGetAsInt_getsTruncated() throws Exception {
402     assertThat(JsonParser.parse("42.0").getAsInt()).isEqualTo(42);
403     assertThat(JsonParser.parse("2.1e1").getAsInt()).isEqualTo(21);
404 
405     assertThat(JsonParser.parse("42.1").getAsInt()).isEqualTo(42);
406     assertThat(JsonParser.parse("42.9999").getAsInt()).isEqualTo(42);
407 
408     // 2^31 - 1 as float
409     assertThat(JsonParser.parse("2.147483647e9").getAsInt()).isEqualTo(2147483647);
410 
411     // - 2^31 as float
412     assertThat(JsonParser.parse("-2.147483648e9").getAsInt()).isEqualTo(-2147483648);
413   }
414 
415   @Theory
testNumbersToDouble()416   public void testNumbersToDouble() throws Exception {
417     assertThat(JsonParser.parse("60911552482230981.0").getAsDouble())
418         .isEqualTo(6.0911552482230984e16);
419     assertThat(JsonParser.parse("4e+42").getAsDouble()).isEqualTo(4e42);
420     assertThat(JsonParser.parse("4e42").getAsDouble()).isEqualTo(4e42);
421     assertThat(JsonParser.parse("4E42").getAsDouble()).isEqualTo(4e42);
422     assertThat(JsonParser.parse("-4e-42").getAsDouble()).isEqualTo(-4e-42);
423     assertThat(
424             JsonParser.parse(
425                     "9999999999999999999999999999999999999999999999999999999999999999999999999999"
426                         + "99999999999")
427                 .getAsDouble())
428         .isEqualTo(1.0e87);
429     assertThat(
430             JsonParser.parse(
431                     "-999999999999999999999999999999999999999999999999999999999999999999999999999"
432                         + "999999999999")
433                 .getAsDouble())
434         .isEqualTo(-1.0e87);
435     assertThat(
436             JsonParser.parse("99999999999999999999999999.99e+99999999999999999999999999")
437                 .getAsDouble())
438         .isPositiveInfinity();
439     assertThat(
440             JsonParser.parse("-99999999999999999999999999.99e+99999999999999999999999999")
441                 .getAsDouble())
442         .isNegativeInfinity();
443     assertThat(
444             JsonParser.parse("99999999999999999999999999.99e-99999999999999999999999999")
445                 .getAsDouble())
446         .isEqualTo(0.0);
447     assertThat(JsonParser.parse("0.000000000000000000000000000000001").getAsDouble())
448         .isEqualTo(0.000000000000000000000000000000001);
449     assertThat(JsonParser.parse("-0.000000000000000000000000000000001").getAsDouble())
450         .isEqualTo(-0.000000000000000000000000000000001);
451     assertThat(JsonParser.parse("42").getAsInt()).isEqualTo(42);
452     assertThat(JsonParser.parse("42\n").getAsInt()).isEqualTo(42);
453     assertThat(JsonParser.parse("42\f").getAsInt()).isEqualTo(42);
454   }
455 
456   @DataPoints("testCasesFail")
457   public static final TestCase[] TEST_CASES_FAIL = {
458     new TestCase("nested_empty_maps", "{{}}"),
459     new TestCase("open_map", "{\"\":{\"\":{\"\":{\"\":{\"\":{\"\":{\"\":"),
460     new TestCase("open_array_map", "[{\"\":[{\"\":[{\"\":[{\"\":[{\"\":[{\"\":[{\"\":["),
461     new TestCase("open_array", "["),
462     new TestCase("open_array_1", "[1"),
463     new TestCase("open_array_2", "[1,"),
464     new TestCase("open_arrays", "[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[["),
465     new TestCase("open_array_with_huge_negative_int", "[-2374623746732768942798327498324234"),
466     new TestCase("map_missing_value", "{\"a\":"),
467     new TestCase("string_not_closed", "\"a"),
468     new TestCase("empty_string_not_closed", "\""),
469     new TestCase("string_with_backslash_not_closed", "\"\\"),
470     new TestCase("number_dot", "42."),
471     new TestCase("-number_dot", "-42."),
472     new TestCase("number_dot_with_e1", "42.e1"),
473     new TestCase("number_dot_with_+e1", "42.e+1"),
474     new TestCase("number_dot_with_-e1", "42.e-1"),
475     new TestCase("number_with_e", "42e"),
476     new TestCase("number_with_e+", "42e+"),
477     new TestCase("number_with_E", "42E"),
478     new TestCase("number_with_E+", "42E+"),
479     new TestCase("float_with_e", "42.42e"),
480     new TestCase("float_with_e+", "42.42e+"),
481     new TestCase("float_with_E", "42.42E"),
482     new TestCase("float_with_E+", "42.42E+"),
483     new TestCase("+number", "+42"),
484     new TestCase("++number", "++42"),
485     new TestCase("Inf", "Inf"),
486     new TestCase("+Inf", "+Inf"),
487     new TestCase("-Inf", "-Inf"),
488     new TestCase("Infinity", "Infinity"),
489     new TestCase("+Infinity", "+Infinity"),
490     new TestCase("-Infinity", "-Infinity"),
491     new TestCase("NaN", "NaN"),
492     new TestCase("+NaN", "+NaN"),
493     new TestCase("-NaN", "-NaN"),
494     new TestCase("number_dot_minus_number", "42.-42"),
495     new TestCase("dot_minus_number", ".-42"),
496     new TestCase("dot_number", ".42"),
497     new TestCase("minus_dot_number", "-.42"),
498     new TestCase("number_with_leading_zero", "042"),
499     new TestCase("-number_with_leading_zero", "-042"),
500     new TestCase("number_two_dots", "42.43.44"),
501     new TestCase("number_two_dots_2", ".42.43"),
502     new TestCase("number_two_dots_3", "42.43."),
503     new TestCase("number_ee", "42ee42"),
504     new TestCase("number_eE", "42eE42"),
505     new TestCase("number_e_plus_minus", "42e+-42"),
506     new TestCase("number_with_trailing_garbage", "2@"),
507     new TestCase("number_with_tailing_comment", "42/*comment*/"),
508     new TestCase("number_garbage_after_e", "1ea"),
509     new TestCase("number_with_a", "1.2a-3"),
510     new TestCase("number_with_h", "1.8011670033376514H-308"),
511     new TestCase("hex1", "0x1"),
512     new TestCase("hex2", "0x42"),
513     new TestCase("number_with_tailing_a", "42a"),
514     new TestCase("float_with_tailing_a", "42.42a"),
515     new TestCase("minus_number_with_tailing_a", "-42a"),
516     new TestCase("number_tailing_excaped_newline", "42\\n"),
517     new TestCase("minus_a", "-a"),
518     new TestCase("minus", "-"),
519     new TestCase("addition", "1+2"),
520     new TestCase("subtraction", "2-1"),
521     new TestCase("multiplication", "2*1"),
522     new TestCase("array_with_number_with_space", "[1 000]"),
523     new TestCase("array_with_float_with_space", "[1 000.0]"),
524     new TestCase("array_with_minus_space_number", "[- 42]"),
525     new TestCase("key_without_quotes", "{a:0}"),
526     new TestCase("key_single_quote", "{'a':0}"),
527     new TestCase("array_element_without_quotes", "[a,0]"),
528     new TestCase("array_element_single_quotes", "['a',0]"),
529     new TestCase("map_with_trailing_comma", "{\"a\":0,}"),
530     new TestCase("map_with_two_commas", "{\"a\":0,,\"b\":1}"),
531     new TestCase("array_with_trailing_comma", "[\"a\",]"),
532     new TestCase("map_with_comment", "{\"a\":/*comment*/\"b\"}"),
533     new TestCase("map_with_null_key", "{null:0}"),
534     new TestCase("map_with_number_key", "{1:1}"),
535     new TestCase("map_with_huge_float_key", "{9999E9999:1}"),
536     new TestCase("map_missing_colon", "{\"a\" \"b\"}"),
537     new TestCase("map_missing_key", "{:\"b\"}"),
538     new TestCase("map_with_comma", "{\"a\", \"b\"}"),
539     new TestCase("map_double_colon", "{\"x\"::\"b\"}"),
540     new TestCase("map_with_garbage", "{\"a\":\"b\" c}"),
541     new TestCase("map_with_single_string", "{ \"a\" : \"b\", \"c\" }"),
542     new TestCase("array_leading_comma", "[,1]"),
543     new TestCase("array_double_comma", "[1,,2]"),
544     new TestCase("array_double_tailing_comma", "[1,,]"),
545     new TestCase("array_comma", "[,]"),
546     new TestCase("nested_arrays_no_comma", "[3[4]]"),
547     new TestCase("array_without_comma", "[1 2]"),
548     new TestCase("array__with_colon", "[\"a\": 1]"),
549     new TestCase("incomplete_false", "fals"),
550     new TestCase("incomplete_null", "nul"),
551     new TestCase("incomplete_true", "tru"),
552     new TestCase("unquoted_string", "a"),
553     new TestCase("star", "*"),
554     new TestCase("angle_bracket_dot", "<.>"),
555     new TestCase("string_escape_x", "\"\\x00\""),
556     new TestCase("escaped_emoji", "\"\\��\""),
557     new TestCase("invalid_backslash_escape", "\"\\a\""),
558     new TestCase("unicode_with_capital_u", "\"\\UA66D\""),
559     new TestCase("invalid_unicode_escape", "\"\\uqqqq\""),
560     new TestCase("incomplete_surrogate", "\"\\uD834\\uDd\""),
561     new TestCase("1_surrogate_then_escape_u", "[\"\\uD800\\u\"]"),
562     new TestCase("2_incomplete_surrogate_escape_invalid", "[\"\\uD800\\uD800\\x\"]"),
563     new TestCase("array_with_formfeed", "[\f]"),
564     new TestCase("array_with_tailing_formfeed", "[\"a\"\f]"),
565     new TestCase("array_with_leading_uescaped_thinspace", "[\\u0020\"a\"]"),
566     new TestCase("array_with_escaped_new_line", "[\\n]"),
567     new TestCase("array_with_escaped_tab", "[\\t]"),
568 
569     new TestCase("duplicated_key", "{\"a\":\"b\",\"a\":\"c\"}"),
570     new TestCase("duplicated_key_and_value", "{\"a\":\"b\",\"a\":\"b\"}"),
571     new TestCase("empty", ""),
572     new TestCase("single_space", " "),
573     new TestCase("nested_with_duplicated_key", "{\"x\":{\"a\":\"b\",\"a\":\"c\"}}"),
574     new TestCase("split_array", "{ \"a\" : [1,2,3], \"b\" : 0, \"a\" : [4,5,6]}"),
575 
576     // invalid characters in strings
577     new TestCase("invalid_utf16", "\"\\uD834\"", null),
578     new TestCase("invalid_utf16_in_key", "{\"\\ud800\\ud800key\":\"value\"}", null),
579     new TestCase(
580          "invalid_utf16_in_key_2", "{\"key\":\"value1\",\"\\ud800\\ud800key\":\"value2\"}", null),
581     new TestCase("invalid_utf16_in_value", "{\"key\":\"value\\ud800\\ud800\"}", null),
582     new TestCase("invalid_surrogate_1", "\"\\uDADA\"", null),
583     new TestCase("invalid_surrogate_2", "\"\\ud800\"", null),
584     new TestCase("invalid_surrogate_3", "\"\\uDd1ea\"", null),
585     new TestCase("invalid_surrogate_4", "\"\\uDFAA\"", null),
586     new TestCase("invalid_surrogate_5", "\"\\uD888\\u1234\"", null),
587     new TestCase("invalid_surrogate_6", "\"\\uD800\\uD800\\n\"", null),
588     new TestCase("invalid_surrogate_7", "\"\\uDd1e\\uD834\"", null),
589     new TestCase("invalid_surrogate_in_map_key", "{\"\\uDFAA\":0}", null),
590     new TestCase("invalid_surrogate_in_map_value", "{\"a\": \"\\uDFAA\"}", null),
591   };
592 
593   @Test
tooManyRecursions_fail()594   public void tooManyRecursions_fail() throws Exception {
595     int recursionNum = 150;
596     StringBuilder sb = new StringBuilder();
597     for (int i = 0; i < recursionNum; i++) {
598       sb.append("{\"a\":");
599     }
600     sb.append("1");
601     for (int i = 0; i < recursionNum; i++) {
602       sb.append("}");
603     }
604     assertThrows(IOException.class, () -> JsonParser.parse(sb.toString()));
605   }
606 
607   @Theory
parse_fail( @romDataPoints"testCasesFail") TestCase testCase)608   public void parse_fail(
609       @FromDataPoints("testCasesFail") TestCase testCase) throws Exception {
610     assertThrows(IOException.class, () -> JsonParser.parse(testCase.input));
611   }
612 
613 }
614