From 64444aae92237a85229086f86fdddbe083fb314e Mon Sep 17 00:00:00 2001 From: JanHolger Date: Sun, 10 Mar 2024 15:55:23 +0100 Subject: [PATCH] Implemented test for JsonParser --- .../abstractdata/json/JsonParser.java | 25 +-- .../abstractdata/json/JsonParserTest.java | 169 ++++++++++++++++++ 2 files changed, 182 insertions(+), 12 deletions(-) create mode 100644 src/test/java/org/javawebstack/abstractdata/json/JsonParserTest.java diff --git a/src/main/java/org/javawebstack/abstractdata/json/JsonParser.java b/src/main/java/org/javawebstack/abstractdata/json/JsonParser.java index 1afedd2..980a7f0 100644 --- a/src/main/java/org/javawebstack/abstractdata/json/JsonParser.java +++ b/src/main/java/org/javawebstack/abstractdata/json/JsonParser.java @@ -3,10 +3,7 @@ import org.javawebstack.abstractdata.*; import java.text.ParseException; -import java.util.ArrayDeque; -import java.util.ArrayList; -import java.util.Deque; -import java.util.List; +import java.util.*; public class JsonParser { @@ -19,7 +16,7 @@ public AbstractElement parse(String json) throws ParseException { AbstractElement parsed; try { parsed = parse(stack); - } catch (NullPointerException ex) { + } catch (NoSuchElementException | NullPointerException ex) { throw new ParseException("Unexpected character ", primChars.length); } if (parsed == null) { @@ -28,11 +25,11 @@ public AbstractElement parse(String json) throws ParseException { for (int i = 0; i < primChars.length - stack.size(); i++) { if (primChars[i] == '\n') { line++; - pos = 1; + pos = 0; } pos++; } - throw new ParseException("Unexpected character '" + stack.pop() + "' at line " + line + " pos " + pos, primChars.length - stack.size()); + throw new ParseException("Unexpected character '" + stack.pop() + "' at line " + line + " pos " + pos, primChars.length - stack.size() - 1); } return parsed; } @@ -114,7 +111,7 @@ private void popWhitespace(Deque stack) { private AbstractPrimitive parseNumber(Deque stack) { StringBuilder sb = new StringBuilder(); - while (Character.isDigit(stack.peek()) || stack.peek() == '.' || stack.peek() == '-' || stack.peek() == 'E' || stack.peek() == 'e') + while (stack.peek() != null && (Character.isDigit(stack.peek()) || stack.peek() == '.' || stack.peek() == '+' || stack.peek() == '-' || stack.peek() == 'E' || stack.peek() == 'e')) sb.append(stack.pop()); String s = sb.toString(); if (s.contains(".")) { @@ -181,8 +178,6 @@ private AbstractObject parseObject(Deque stack) { break; } AbstractPrimitive key = parseString(stack); - if (key == null) - return null; popWhitespace(stack); if (stack.peek() != ':') return null; @@ -193,8 +188,11 @@ private AbstractObject parseObject(Deque stack) { return null; object.set(key.string(), value); popWhitespace(stack); - if (stack.peek() == ',') + if (stack.peek() == ',') { stack.pop(); + } else if(stack.peek() != '}') { + return null; + } } return object; } @@ -213,8 +211,11 @@ private AbstractArray parseArray(Deque stack) { return null; array.add(value); popWhitespace(stack); - if (stack.peek() == ',') + if (stack.peek() == ',') { stack.pop(); + } else if(stack.peek() != ']') { + return null; + } } return array; } diff --git a/src/test/java/org/javawebstack/abstractdata/json/JsonParserTest.java b/src/test/java/org/javawebstack/abstractdata/json/JsonParserTest.java new file mode 100644 index 0000000..5c96faa --- /dev/null +++ b/src/test/java/org/javawebstack/abstractdata/json/JsonParserTest.java @@ -0,0 +1,169 @@ +package org.javawebstack.abstractdata.json; + +import org.javawebstack.abstractdata.AbstractElement; +import org.javawebstack.abstractdata.AbstractPrimitive; +import org.junit.jupiter.api.Test; + +import java.text.ParseException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.*; + +public class JsonParserTest { + + @Test + public void testParseEmptyString() { + ParseException e = assertThrows(ParseException.class, () -> new JsonParser().parse("")); + assertEquals("Unexpected character ", e.getMessage()); + } + + @Test + public void testParseUnexpectedEOF() { + ParseException e = assertThrows(ParseException.class, () -> new JsonParser().parse("{")); + assertEquals("Unexpected character ", e.getMessage()); + e = assertThrows(ParseException.class, () -> new JsonParser().parse("[")); + assertEquals("Unexpected character ", e.getMessage()); + e = assertThrows(ParseException.class, () -> new JsonParser().parse("\"")); + assertEquals("Unexpected character ", e.getMessage()); + e = assertThrows(ParseException.class, () -> new JsonParser().parse("t")); + assertEquals("Unexpected character ", e.getMessage()); + } + + @Test + public void testParseUnexpectedCharacter() { + ParseException e = assertThrows(ParseException.class, () -> new JsonParser().parse("{\n \"abc\":x\n}")); + assertEquals("Unexpected character 'x' at line 2 pos 11", e.getMessage()); + assertEquals(12, e.getErrorOffset()); + } + + @Test + public void testParseBooleanTrue() { + AbstractElement trueElement = assertDoesNotThrow(() -> new JsonParser().parse("true")); + assertTrue(trueElement.isBoolean()); + assertTrue(trueElement.bool()); + assertThrows(ParseException.class, () -> new JsonParser().parse("trux")); + assertThrows(ParseException.class, () -> new JsonParser().parse("trxx")); + assertThrows(ParseException.class, () -> new JsonParser().parse("txxx")); + } + + @Test + public void testParseBooleanFalse() { + AbstractElement falseElement = assertDoesNotThrow(() -> new JsonParser().parse("false")); + assertTrue(falseElement.isBoolean()); + assertFalse(falseElement.bool()); + assertThrows(ParseException.class, () -> new JsonParser().parse("falsx")); + assertThrows(ParseException.class, () -> new JsonParser().parse("falxx")); + assertThrows(ParseException.class, () -> new JsonParser().parse("faxxx")); + assertThrows(ParseException.class, () -> new JsonParser().parse("fxxxx")); + } + + @Test + public void testParseBooleanNull() { + AbstractElement nullElement = assertDoesNotThrow(() -> new JsonParser().parse("null")); + assertTrue(nullElement.isNull()); + assertThrows(ParseException.class, () -> new JsonParser().parse("nulx")); + assertThrows(ParseException.class, () -> new JsonParser().parse("nuxx")); + assertThrows(ParseException.class, () -> new JsonParser().parse("nxxx")); + } + + @Test + public void testParseInteger() { + AbstractElement e = assertDoesNotThrow(() -> new JsonParser().parse("123")); + assertTrue(e.isNumber()); + assertEquals(123, e.number()); + } + + @Test + public void testParseLong() { + long expected = 1L + Integer.MAX_VALUE; + AbstractElement e = assertDoesNotThrow(() -> new JsonParser().parse(String.valueOf(expected))); + assertTrue(e.isNumber()); + assertEquals(expected, e.number()); + } + + @Test + public void testParseDouble() { + AbstractElement e = assertDoesNotThrow(() -> new JsonParser().parse("123.456")); + assertTrue(e.isNumber()); + assertEquals(123.456, e.number()); + } + + @Test + public void testParseStringEscapeSeq() { + Map escapes = new HashMap<>(); + escapes.put("\\\"", "\""); + escapes.put("\\\\", "\\"); + escapes.put("\\b", "\b"); + escapes.put("\\f", "\f"); + escapes.put("\\n", "\n"); + escapes.put("\\r", "\r"); + escapes.put("\\t", "\t"); + escapes.put("\\0", "\0"); + escapes.put("\\/", "/"); + for(String c : escapes.keySet()) { + AbstractElement e = assertDoesNotThrow(() -> new JsonParser().parse("\"" + c + "\"")); + assertTrue(e.isString()); + assertEquals(escapes.get(c), e.string()); + } + } + + @Test + public void testParseStringUnicode() { + AbstractElement e = assertDoesNotThrow(() -> new JsonParser().parse("\"\\u001F\"")); + assertTrue(e.isString()); + assertEquals("\u001F", e.string()); + } + + @Test + public void testParseValidObject() { + AbstractElement e = assertDoesNotThrow(() -> new JsonParser().parse("{\"a\":1,\"b\":2}")); + assertTrue(e.isObject()); + assertEquals(2, e.object().size()); + List keys = new ArrayList<>(e.object().keys()); + assertEquals("a", keys.get(0)); + assertEquals("b", keys.get(1)); + AbstractElement aElement = e.object().get("a"); + assertTrue(aElement.isNumber()); + assertEquals(1, aElement.number()); + AbstractElement bElement = e.object().get("b"); + assertTrue(bElement.isNumber()); + assertEquals(2, bElement.number()); + } + + @Test + public void testParseInvalidObjectWithMissingColon() { + assertThrows(ParseException.class, () -> new JsonParser().parse("{\"a\"1}")); + } + + @Test + public void testParseInvalidObjectWithMissingComma() { + assertThrows(ParseException.class, () -> new JsonParser().parse("{\"a\":1\"b\":2}")); + } + + @Test + public void testParseValidArray() { + AbstractElement e = assertDoesNotThrow(() -> new JsonParser().parse("[1,2]")); + assertTrue(e.isArray()); + assertEquals(2, e.array().size()); + AbstractElement firstElement = e.array().get(0); + assertTrue(firstElement.isNumber()); + assertEquals(1, firstElement.number()); + AbstractElement secondElement = e.array().get(1); + assertTrue(secondElement.isNumber()); + assertEquals(2, secondElement.number()); + } + + @Test + public void testParseInvalidArrayWithInvalidValue() { + assertThrows(ParseException.class, () -> new JsonParser().parse("[talse]")); + } + + @Test + public void testParseInvalidArrayWithMissingComma() { + assertThrows(ParseException.class, () -> new JsonParser().parse("[\"a\"\"b\"]")); + } + +}