test/jdk/javax/management/remote/rest/JsonParserTest.java
author hb
Tue, 02 Jan 2018 13:33:08 +0530
branchjmx-rest-api
changeset 56004 da55d1429860
parent 56002 test/jdk/javax/management/remote/rest/json/JsonTester.java@60ab3b595a8e
child 56026 bd531f08d7c7
permissions -rw-r--r--
Fixed JSON parser tests


/* @test
 * @summary Test JSON parser for a random json node tree
 * @modules java.management.rest/com.oracle.jmx.remote.rest.json
 *          java.management.rest/com.oracle.jmx.remote.rest.json.parser
 * @build JsonParserTest
 * @run main JsonParserTest
 */

import com.oracle.jmx.remote.rest.json.parser.JSONParser;
import com.oracle.jmx.remote.rest.json.parser.ParseException;

import java.util.*;

/**
 * Below class tests JSON parser for a randomly generated JSON string.
 * The Json string is generated by converting a randomly generated tree into a string
 * Each node in the tree is either a json Object, array or a primitive.
 * Primitive node generates string, number, boolean or null as a string.
 * Json Number is generated according to the syntax graph as in json.org
 * Json string is generated along with all the control characters, and by escaping
 * only backslash and double quote characters.
 */
public class JsonParserTest {

    static final Random RANDOM = new Random(System.currentTimeMillis());
    static int maxChildrenPerNode;

    public static void main(String[] args) throws ParseException {

        for(int i=0; i<100; i++) {
            maxChildrenPerNode = RANDOM.nextInt(90) + 10;
            int totalNodes = RANDOM.nextInt(90000) + 10000;
            boolean isArray = RANDOM.nextBoolean();         // Generate either a Json Array or a Json Object
            JsonNode node;
            if (isArray) {
                node = JsonNodeGenerator.ArrayGenerator.generate(totalNodes);
            } else {
                node = JsonNodeGenerator.ObjectGenerator.generate(totalNodes);
            }
            String str = node.toJsonString();
            JSONParser parser = new JSONParser(str);
            com.oracle.jmx.remote.rest.json.JSONElement parse = parser.parse();
            parse.toJsonString();
            System.out.println("Finished iteration : " + i + ", Node count :" + totalNodes);
        }
    }
}


interface JsonNode {

    class ObjectNode extends LinkedHashMap<String, JsonNode> implements JsonNode {

        @Override
        public String toJsonString() {
            if (isEmpty()) {
                return null;
            }

            StringBuilder sbuild = new StringBuilder();
            sbuild.append("{");
            keySet().forEach((elem) -> sbuild.append(elem).append(": ").
                    append((get(elem) != null) ? get(elem).toJsonString() : "null").append(","));

            sbuild.deleteCharAt(sbuild.lastIndexOf(","));
            sbuild.append("}");
            return sbuild.toString();
        }
    }

    class ArrayNode extends ArrayList<JsonNode> implements JsonNode {

        @Override
        public String toJsonString() {
            if (isEmpty()) {
                return null;
            }
            StringBuilder sbuild = new StringBuilder();
            sbuild.append("[");
            for (JsonNode val : this) {
                if (val != null) {
                    sbuild.append(val.toJsonString()).append(", ");
                } else {
                    sbuild.append("null").append(", ");
                }
            }

            sbuild.deleteCharAt(sbuild.lastIndexOf(","));
            sbuild.append("]");
            return sbuild.toString();
        }
    }

    class PrimitiveNode implements JsonNode {

        private final String s;

        PrimitiveNode(String s) {
            this.s = s;
        }

        @Override
        public String toJsonString() {
            return s;
        }
    }

    String toJsonString();
}

interface JsonNodeGenerator {

    class NumberGenerator {

         // Node that returns the assigned label
        private static class Node {
            final private String label;

            Node(String label) {
                children = new LinkedList<>();
                this.label = label;
            }

            Node() {
                this("");
            }

            void add(Node node) {
                if (!children.contains(node)) {
                    children.add(node);
                }
            }

            String getLabel() {
                return label;
            }

            List<Node> children;
        }

        // Node that generates a random digit from 1-9
        private static class Digit19 extends Node {
            Digit19() {
                super();
            }

            @Override
            String getLabel() {
                return "" + (JsonParserTest.RANDOM.nextInt(9) + 1);
            }
        }

        // Node that generates a random digit from 0-9
        private static class Digits extends Node {
            Digits() {
                super();
            }

            @Override
            String getLabel() {
                return "" + (JsonParserTest.RANDOM.nextInt(10));
            }
        }

        private final static Node root;

        // Setup a graph for the grammar productions for JSON number as outlined in json.org
        // The graph below mimics the syntax diagram for JSON number.
        // Node "R" is the start node and "T" is the terminal node
        static {

            // Create all the nodes
            root = new Node("R");
            Node minus1 = new Node("-");
            Node zero = new Node("0");
            Node digit19 = new Digit19();
            Node digits1 = new Digits();
            Node dot = new Node(".");
            Node digits2 = new Digits();
            Node e = new Node("e");
            Node E = new Node("E");
            Node plus = new Node("+");
            Node minus2 = new Node("-");
            Node digits3 = new Digits();
            Node terminal = new Node("T");

            //set up graph
            root.add(zero);
            root.add(minus1);
            root.add(digit19);

            minus1.add(zero);
            minus1.add(digit19);

            zero.add(dot);
            zero.add(terminal);

            digit19.add(dot);
            digit19.add(digits1);
            digit19.add(terminal);

            digits1.add(dot);
            digits1.add(digits1);
            digits1.add(terminal);

            dot.add(digits2);

            digits2.add(digits2);
            digits2.add(e);
            digits2.add(E);
            digits2.add(terminal);

            e.add(plus);
            e.add(minus2);
            e.add(digits3);

            E.add(plus);
            E.add(minus2);
            E.add(digits3);

            plus.add(digits3);
            minus2.add(digits3);

            digits3.add(digits3);
            digits3.add(terminal);
        }

        static String generate() {
            // Get a random path from start to finish
            StringBuilder sbuf = new StringBuilder();
            Node parent = root;
            Node child = parent.children.get(JsonParserTest.RANDOM.nextInt(parent.children.size()));
            while (!child.getLabel().equals("T")) {
                sbuf.append(child.getLabel());
                parent = child;
                child = parent.children.get(JsonParserTest.RANDOM.nextInt(parent.children.size()));
            }
            return sbuf.toString();
        }
    }

    class StringGenerator {

        private static final int minStringLength = 0;
        private static final int maxStringLength = 50;

        private static final String controlChars = "\b" + "\f" + "\n" + "\r" + "\t" + "\\b";
//        private static final String escapedControls = "\\b" + "\\f" + "\\n" + "\\r" + "\\t" + "\\b";

        private static final String specials = "\\" + "\"" + controlChars;// + escapedControls;    // TODO: "\\uxxxx"
//        private static final String alphanums = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";

        static String generate() {
            char ch;
            StringBuilder sbuf = new StringBuilder();
            int len = minStringLength + JsonParserTest.RANDOM.nextInt(maxStringLength - minStringLength + 1);
            sbuf.append("\"");
            for (int i = 0; i < len; i++) {
                if (JsonParserTest.RANDOM.nextInt(10) == 1) { // 1/10 chances of a control character
                    ch = specials.charAt(JsonParserTest.RANDOM.nextInt(specials.length()));
                } else {
//                ch = alphanums.charAt(JsonParserTest.RANDOM.nextInt(alphanums.length()));
                    ch = (char) JsonParserTest.RANDOM.nextInt(Character.MAX_VALUE + 1);
                }
                switch (ch) {
                    case '\"':
                    case '\\':
                        sbuf.append('\\');
                }
                sbuf.append(ch);
            }
            sbuf.append("\"");
            return sbuf.toString();
        }
    }

    class ArrayGenerator {

         static JsonNode.ArrayNode generate(int size) {
            JsonNode.ArrayNode array = new JsonNode.ArrayNode();
            if (size <= JsonParserTest.maxChildrenPerNode) {
                for (int i = 0; i < size; i++) {
                    array.add(PrimtiveGenerator.generate());
                }
            } else if (size >= JsonParserTest.maxChildrenPerNode) {
                int newSize = size;
                do {
                    int childSize = JsonParserTest.RANDOM.nextInt(newSize);
                    if (JsonParserTest.RANDOM.nextBoolean()) {
                        array.add(ArrayGenerator.generate(childSize));
                    } else {
                        array.add(ObjectGenerator.generate(childSize));
                    }
                    newSize = newSize - childSize;
                } while (newSize > JsonParserTest.maxChildrenPerNode);
                if (JsonParserTest.RANDOM.nextBoolean()) {
                    array.add(ArrayGenerator.generate(newSize));
                } else {
                    array.add(ObjectGenerator.generate(newSize));
                }
            }
            return array;
        }
    }

    class PrimtiveGenerator {

        static JsonNode.PrimitiveNode generate() {
            int primitiveTypre = JsonParserTest.RANDOM.nextInt(10) + 1;
            switch (primitiveTypre) {
                case 1:
                case 2:
                case 3:
                case 4:
                    return new JsonNode.PrimitiveNode(StringGenerator.generate());
                case 5:
                case 6:
                case 7:
                case 8:
                    return new JsonNode.PrimitiveNode(NumberGenerator.generate());
                case 9:
                    return new JsonNode.PrimitiveNode(Boolean.toString(JsonParserTest.RANDOM.nextBoolean()));
                case 10:
                    return null;
            }
            return null;
        }
    }

    class ObjectGenerator {

        static JsonNode.ObjectNode generate(int size) {
            JsonNode.ObjectNode jobj = new JsonNode.ObjectNode();
            if (size <= JsonParserTest.maxChildrenPerNode) {
                for (int i = 0; i < size; i++) {
                    jobj.put(StringGenerator.generate(), PrimtiveGenerator.generate());
                }
            } else {
                int newSize = size;
                do {
                    int childSize = JsonParserTest.RANDOM.nextInt(newSize);
                    if (JsonParserTest.RANDOM.nextBoolean()) {
                        jobj.put(StringGenerator.generate(), ArrayGenerator.generate(childSize));
                    } else {
                        jobj.put(StringGenerator.generate(), ObjectGenerator.generate(childSize));
                    }
                    newSize = newSize - childSize;
                } while (newSize > JsonParserTest.maxChildrenPerNode);
                if (JsonParserTest.RANDOM.nextBoolean()) {
                    jobj.put(StringGenerator.generate(), ArrayGenerator.generate(newSize));
                } else {
                    jobj.put(StringGenerator.generate(), ObjectGenerator.generate(newSize));
                }
            }
            return jobj;
        }
    }
}