All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.deephacks.tools4j.config.test.JUnitUtils Maven / Gradle / Ivy

There is a newer version: 0.15.0
Show newest version
package org.deephacks.tools4j.config.test;

import com.google.common.base.Charsets;
import com.google.common.io.CharStreams;
import org.deephacks.tools4j.config.model.Bean;
import org.deephacks.tools4j.config.model.Bean.BeanId;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;

import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic;
import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;

public class JUnitUtils {
    /**
     * Compute the root directory of this maven project. This will result in the
     * same directory no matter if executed from Eclipse, this maven project root or
     * any parent maven pom directory.
     *
     * @param anyTestClass Any test class *local* to the maven project, i.e that
     * only exist in this maven project.
     *
     * @param anyTestClass The file that should be
     * @return The root directory of this maven project.
     */
    public static File computeMavenProjectRoot(Class anyTestClass) {
        final String clsUri = anyTestClass.getName().replace('.', '/') + ".class";
        final URL url = anyTestClass.getClassLoader().getResource(clsUri);
        final String clsPath = url.getPath();
        // located in ./target/test-classes or ./eclipse-out/target
        final File target_test_classes = new File(clsPath.substring(0,
                clsPath.length() - clsUri.length()));
        // get parent's parent
        return target_test_classes.getParentFile().getParentFile();
    }

    /**
     * Normalizes the root for reading a file to the maven project root directory.
     *
     * @param anyTestClass Any test class *local* to the maven project, i.e that
     * only exist in this maven project.
     *
     * @param child A child path.
     *
     * @return A file relative to the maven root.
     */
    public static File getMavenProjectChildFile(Class anyTestClass, String child) {
        return new File(computeMavenProjectRoot(anyTestClass), child);
    }

    public static File getMetaInfDir(Class context) {
        URL url = context.getResource("/META-INF/");
        try {
            return new File(url.toURI());
        } catch (URISyntaxException e) {
            throw new RuntimeException(e);
        }
    }

    public static List readMetaInfResource(Class context, String filepath) {
        InputStream in = context.getResourceAsStream("/META-INF/" + filepath);
        ArrayList list = new ArrayList();
        try {
            String content = CharStreams.toString(new InputStreamReader(in, Charsets.UTF_8));

            list.addAll(Arrays.asList(content.split("\n")));
            return list;
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * Mini-languge for generating classes and bean instances.
     *
     * Ex: A=1000, B=200$10, C=3$300; B=2000, C=200$10; C=300
     *
     *   - creates 1000 A, 2000 B and 300 C instances.
     *   - Assign 200 random A instances to 10 random B references
     *   - Assign 3 random A instances to 300 random C references
     *   - Assign 200 random B instances to 10 random C references
     */
    public static Set generate(String minilang, int numprops, int numvalues) {

        String[] cfgtypes = minilang.split(";");
        HashMap configs = new HashMap<>();
        // declared config types
        for (String cfgtype : cfgtypes) {
            String[] stmts = cfgtype.split(",");
            String stmt = stmts[0].trim();
            String[] assign = stmt.split("=");
            ConfigClass clazz = new ConfigClass(assign[0].trim(), ConfigClass.randomFieldName());
            clazz.addBeans(Integer.parseInt(assign[1].trim()));
            String[] refstmts = new String[cfgtype.split(",").length - 1];
            System.arraycopy(stmts, 1, refstmts, 0, refstmts.length);
            clazz.stmts = refstmts;
            configs.put(assign[0].trim(), clazz);

        }
        // reference assignment for each config type
        for (String parentname : configs.keySet()) {
            ConfigClass parent = configs.get(parentname);
            for (int i = 0; i < numprops; i++) {
                parent.addField(numvalues);
            }
            for (String stmt : parent.stmts) {
                String childname = stmt.split("=")[0].trim();
                String refs = stmt.split("=")[1].trim();
                ConfigClass child = configs.get(childname);
                int parentnum = Integer.parseInt(refs.split("\\$")[0].trim());
                int childnum = Integer.parseInt(refs.split("\\$")[1].trim());
                parent.addReferences(parentnum, childnum, child);
            }
        }
        return new HashSet<>(configs.values());
    }

    public static List generateBeans(int numBeans, int numProps) {
        ArrayList beans = new ArrayList<>();
        for (int i = 0; i < numBeans; i++) {
            String id = "beanId" + i;
            String type = "beanType" + i;
            Bean bean = Bean.create(BeanId.create(id, type));
            for (int j = 0; j < numProps; j++) {
                String _name = "propName" + j;
                String _value = "propFieldName" + j;
                bean.addProperty(_name, _value);
                List d = Arrays.asList("1", "2", "3");
                bean.addProperty(_name, d);
            }
            beans.add(bean);
        }
        return beans;
    }

    /**
     * Compile a generated config class to given directory.
     *
     * Classes will be loaded into current thread classloader.
     */
    public static Set> compile(ConfigClass config, File dir) {
        return compile(Arrays.asList(config), dir);
    }

    /**
     * Compile generated config classes to given directory.
     *
     * Classes will be loaded into current thread classloader.
     */
    public static Set> compile(Collection configs, File dir) {
        HashMap sources = new HashMap<>();
        for (ConfigClass config : configs) {
            sources.put(config.classname, config.toString());
        }
        return CompilerUtils.compile(sources, ConfigClass.class.getPackage().getName(), dir);
    }

    /**
     * A dynamically generated config class.
     */
    public static class ConfigClass {
        private final static Class[] types = new Class[] { Long.class, String.class };

        static final String CLASSNAME = "%classname%";

        static final String ID = "%idfield%";
        static final String ID_NAME = "%idname%";
        static final String ID_TYPE = "%idtype%";
        static final String ID_TEMPLATE = " @Id(desc=\"" + ID_NAME + "\") private " + ID_TYPE + " "
                + ID_NAME + ";";

        static final String FIELDS = "%fields%";
        static final String FIELD_NAME = "%fieldname%";
        static final String FIELD_TYPE = "%fieldtype%";
        static final String FIELD_TEMPLATE = " @Config(desc=\"" + FIELD_NAME + "\") private "
                + FIELD_TYPE + " " + FIELD_NAME + ";";

        static final String REFERENCES = "%references%";
        static final String REFERENCE_NAME = "%referencename%";
        static final String REFERENCE_TYPE = "%referencetype%";
        static final String REFERENCE_TEMPLATE = " @Config(desc=\"" + REFERENCE_NAME
                + "\") private " + REFERENCE_TYPE + " " + REFERENCE_NAME + ";";

        static final String CLASS_TEMPLATE = "package " + ConfigClass.class.getPackage().getName()
                + ";\n" + "import org.deephacks.tools4j.config.*;\nimport java.util.*;\n"
                + "@Config(name=\"" + CLASSNAME + "\", desc=\"" + CLASSNAME + "\")\n"
                + "public class %classname% {\n" + ID + "\n" + FIELDS + "\n" + REFERENCES + "\n}";

        private Map fields = new HashMap<>();
        private Map references = new HashMap<>();

        public String classname;
        public String idName;
        public String idType;
        public ArrayList beans = new ArrayList<>();
        String[] stmts = new String[0];

        public ConfigClass(String classname, String idName) {
            this.classname = classname;
            this.idName = idName;
            this.idType = "String";
        }

        public static Set generate(int numTypes, int minProps, int maxProps) {
            numTypes = numTypes == 0 ? 1 : numTypes;
            Set classes = new HashSet<>();
            for (int i = 0; i < numTypes; i++) {
                int numProps = randomInt(minProps, maxProps);
                ConfigClass config = new ConfigClass(randomClassname(), randomFieldName());
                for (int j = 0; j < numProps; j++) {
                    config.addField(10);
                }
                classes.add(config);
            }
            return classes;
        }

        public void addBeans(int num) {
            for (int i = 0; i < num; i++) {
                Bean b = Bean.create(BeanId.create(randomAlphanumeric(20), classname));
                beans.add(b);
            }
        }

        public List getBeans() {
            return beans;
        }

        public void addField(int numvalues) {
            Class clazz = types[new Random().nextInt(types.length)];
            boolean isList = new Random().nextBoolean();
            String name = randomFieldName();
            if (!isList) {
                fields.put(name, clazz.getName());
                for (Bean bean : beans) {
                    bean.addProperty(name, generateRandomValue(clazz));
                }
            } else {
                fields.put(name, "List<" + clazz.getName() + ">");
                for (Bean bean : beans) {
                    for (int i = 0; i < numvalues; i++) {
                        bean.addProperty(name, generateRandomValue(clazz));
                    }
                }
            }
        }

        public void addReferenceRandom(String type) {
            boolean isList = new Random().nextBoolean();
            if (!isList) {
                addReference(type);
            } else {
                addReferenceList(randomFieldName(), type);
            }
        }

        public void addReferences(int parentnum, int childnum, ConfigClass child) {
            addReferenceList(child.classname, child.classname);
            Bean[] b = beans.toArray(new Bean[0]);
            for (int i = 0; i < parentnum; i++) {
                List numbers = randomUniqueInt(0, beans.size() - 1);
                while (parentnum-- > 0) {
                    if (parentnum > b.length) {
                        throw new IllegalArgumentException(
                                "MINILANG: too few instances available [" + b.length
                                        + "] to reference [" + parentnum + "] instances.");
                    }
                    Bean parent = b[numbers.get(parentnum)];
                    Set children = child.getInstances(childnum);
                    for (Bean bean : children) {
                        // circular references to self is not allowed.
                        if (!bean.equals(parent)) {
                            parent.addReference(bean.getId().getSchemaName(), bean.getId());
                        }
                    }
                }
            }
        }

        private Set getInstances(int num) {
            Bean[] b = beans.toArray(new Bean[0]);
            Set result = new HashSet<>();
            List numbers = randomUniqueInt(0, num - 1);
            while (num-- > 0) {
                result.add(b[numbers.get(num)]);
            }
            return result;
        }

        public void addReference(String type) {
            references.put(randomFieldName(), type);
        }

        public void addReferenceList(String name, String type) {
            references.put(name, "List<" + type + ">");
        }

        public Collection getFields() {
            return fields.keySet();
        }

        public Collection getReferences() {
            return references.keySet();
        }

        /**
         * Class names are assumed to have between 8 - 40 alphabetic characters.
         */
        private static String randomClassname() {
            return randomAlphabeth(8, 20);
        }

        /**
         * Field names are assumed to have between 5 - 15 alphabetic characters.
         */
        private static String randomFieldName() {
            return randomAlphabeth(5, 15);
        }

        /**
         * generate a random number between min and max
         */
        private static int randomInt(int min, int max) {
            return min + (new Random()).nextInt(max - min);
        }

        private static List randomUniqueInt(int min, int max) {
            List numbers = new ArrayList<>();
            for (int i = min; i <= max; i++) {
                numbers.add(i);
            }
            Collections.shuffle(numbers);
            return numbers;
        }

        /**
         * generate a random number of alphabetic characters between min and max
         */
        private static String randomAlphabeth(int min, int max) {
            return randomAlphabetic(randomInt(min, max));
        }

        /**
         * generate a random number of alphanumeric characters between
         * min and max
         */
        private static String randomAlphanum(int min, int max) {
            return randomAlphanumeric(randomInt(min, max));
        }

        private final static List longValueCache = new ArrayList<>();
        private static int numLongs = 0;
        private final static List doubleValueCache = new ArrayList<>();
        private static int numDoubles = 0;
        private final static List stringValueCache = new ArrayList<>();
        private static int numStrings = 0;

        /**
         * This method will generate at most 1 million random values for each type
         * and then serve the same values again to save memory and time.
         */
        private static final String generateRandomValue(Class clazz) {
            String value = null;
            if (clazz.isAssignableFrom(Long.class)) {
                if (numLongs > 1000000) {
                    return longValueCache.get(numLongs++ % 1000000);
                }
                numLongs++;
                value = "" + new Random().nextLong();
                longValueCache.add(value);
            } else if (clazz.isAssignableFrom(Double.class)) {
                if (numDoubles > 1000000) {
                    return doubleValueCache.get(numDoubles++ % 1000000);
                }
                numDoubles++;
                value = "" + new Random().nextDouble();
                doubleValueCache.add(value);
            } else if (clazz.isAssignableFrom(String.class)) {
                if (numStrings > 1000000) {
                    return stringValueCache.get(numStrings++ % 1000000);
                }
                numStrings++;
                value = randomAlphanum(10, 40);
                stringValueCache.add(value);
            }
            return value;

        }

        /**
         * Add references between configurables with given probability.
         * 2 = 50%, 3 = 33%, 4 = 25 % and so on.
         */
        @SuppressWarnings("unused")
        private static void addReferences(Set all, int probability) {
            for (ConfigClass target : all) {
                for (ConfigClass source : all) {
                    if (new Random().nextInt(probability) == 0) {
                        target.addReference(source.classname);
                    }
                }
            }
        }

        @Override
        public String toString() {
            String clazz = CLASS_TEMPLATE.replaceAll(CLASSNAME, classname);

            String fieldStr = replace(fields, FIELD_TEMPLATE, FIELD_TYPE, FIELD_NAME);
            String referenceStr = replace(references, REFERENCE_TEMPLATE, REFERENCE_TYPE,
                    REFERENCE_NAME);

            String id = ID_TEMPLATE.replaceAll(ID_NAME, idName);
            id = id.replaceAll(ID_TYPE, idType);

            clazz = clazz.replaceAll(ID, id);
            clazz = clazz.replaceAll(FIELDS, fieldStr);
            clazz = clazz.replaceAll(REFERENCES, referenceStr);

            return clazz;
        }

        private String replace(Map content, String template, String type,
                               String name) {
            StringBuffer referenceStrs = new StringBuffer();
            for (String key : content.keySet()) {
                String reference = template.replaceAll(name, key);
                reference = reference.replaceAll(type, content.get(key));
                referenceStrs.append(reference).append("\n");
            }
            return referenceStrs.toString();
        }
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy