Java tutorial
/* * Copyright 2015-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.hellojavaer.testcase.generator; import java.beans.PropertyDescriptor; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.Collections; import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Random; import java.util.Set; import java.util.Stack; import org.springframework.beans.BeanUtils; /** * * @author <a href="mailto:hellojavaer@gmail.com">zoukaiming</a> */ public class TestCaseGenerator { private static long RANDOM_SEED = 175934207561840L; private static Date START_TIME; private static Character[] CHAR_RANGE = null; private static Character[] STRING_RANGE = null; static { List<Character> charRange = new ArrayList<Character>(); for (int i = 0; i < 10; i++) { charRange.add((char) (i + '0')); } for (int i = 0; i < 26; i++) { charRange.add((char) (i + 'A')); } for (int i = 0; i < 26; i++) { charRange.add((char) (i + 'a')); } CHAR_RANGE = charRange.toArray(new Character[charRange.size()]); List<Character> stringRange = new ArrayList<Character>(); for (int i = 0; i < 10; i++) { stringRange.add((char) (i + '0')); } for (int i = 0; i < 26; i++) { stringRange.add((char) (i + 'A')); } STRING_RANGE = stringRange.toArray(new Character[stringRange.size()]); DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS"); try { START_TIME = df.parse("2015-01-01 00:00:00:000"); } catch (ParseException e) { System.err.print(e.getMessage()); } Random random = new Random(RANDOM_SEED); List<Character> list = Arrays.asList(STRING_RANGE); Collections.shuffle(list, random); list.toArray(STRING_RANGE); } public static <T> List<T> generate(Class<T> clazz, String[] uniqueFields, int count) { return generate(clazz, uniqueFields, count, null, 0); } public static <T> List<T> generate(Class<T> clazz, String[] uniqueFields, int count, String[] excludeFields) { return generate(clazz, uniqueFields, count, excludeFields, 0); } @SuppressWarnings("rawtypes") public static <T> List<T> generate(Class<T> clazz, String[] uniqueFields, int count, String[] excludeFields, int recursiveCycleLimit) { List<String> excludeFieldList = null; if (excludeFields != null && excludeFields.length > 0) { excludeFieldList = Arrays.asList(excludeFields); } if (recursiveCycleLimit <= 0) { recursiveCycleLimit = 0; } checkBeanValidity(clazz, excludeFieldList, recursiveCycleLimit); List<T> list = new ArrayList<T>(count); ControlParam cp = new ControlParam(); cp.setExcludeFieldList(excludeFieldList); cp.setNumIndex(1); cp.setRandom(new Random(RANDOM_SEED)); cp.setStrIndex(100000); cp.setStrStep(1111L); cp.setCharIndex(0); cp.setRecursiveCycleLimit(recursiveCycleLimit); Calendar time = Calendar.getInstance(); time.setTime(START_TIME); cp.setTime(time); if (uniqueFields != null && uniqueFields.length > 0) { cp.setUniqueFieldList(Arrays.asList(uniqueFields)); } for (int i = 0; i < count; i++) { Stack<Class> stack = new Stack<Class>(); stack.push(clazz); list.add(produceBean(clazz, cp, stack)); } return list; } @SuppressWarnings({ "rawtypes", "unchecked" }) private static <T> void checkBeanValidity(Class<T> clazz, List<String> excludeFieldList, int recursiveCycleLimit) { PropertyDescriptor[] pds = BeanUtils.getPropertyDescriptors(clazz); boolean validBean = false; for (PropertyDescriptor pd : pds) { if (pd.getWriteMethod() != null && pd.getReadMethod() != null && // (excludeFieldList != null && !excludeFieldList.contains(pd.getName()) || excludeFieldList == null) // ) { validBean = true; // just set write ,read for user control if (!Modifier.isPublic(pd.getWriteMethod().getDeclaringClass().getModifiers())) { pd.getWriteMethod().setAccessible(true); } Class fieldClazz = pd.getPropertyType(); if (!TypeUtil.isBaseType(fieldClazz)) { try { // ???? if (recursiveCycleLimit == 0 && fieldClazz == clazz) { throw new RuntimeException("recursive cycle limit is 0! field[" + pd.getName() + "] may cause recursive, please add this field[" + pd.getName() + "] to exclude list or set recursiveCycleLimit more than 0 ."); } else if (!fieldClazz.isAssignableFrom(clazz)) { checkBeanValidity(fieldClazz, null, 999999999); } } catch (Exception e) { throw new RuntimeException("Unknown Class " + fieldClazz.getName() + " for field " + pd.getName() + " ! please add this field[" + pd.getName() + "] to exclude list.", e); } } } } if (!validBean) { throw new RuntimeException( "Invalid Bean Class[" + clazz.getName() + "], for it has not getter setter methods!"); } } private static class ControlParam { private long numIndex; private Random random; private long strIndex; private long strStep; private int charIndex; private Calendar time; private List<String> excludeFieldList; private int recursiveCycleLimit; private List<String> uniqueFieldList; public long getNumIndex() { return numIndex; } public void setNumIndex(long numIndex) { this.numIndex = numIndex; } public Random getRandom() { return random; } public void setRandom(Random random) { this.random = random; } public long getStrIndex() { return strIndex; } public void setStrIndex(long strIndex) { this.strIndex = strIndex; } public int getCharIndex() { return charIndex; } public void setCharIndex(int charIndex) { this.charIndex = charIndex; } public Calendar getTime() { return time; } public void setTime(Calendar time) { this.time = time; } public List<String> getExcludeFieldList() { return excludeFieldList; } public void setExcludeFieldList(List<String> excludeFieldList) { this.excludeFieldList = excludeFieldList; } public int getRecursiveCycleLimit() { return recursiveCycleLimit; } public void setRecursiveCycleLimit(int recursiveCycleLimit) { this.recursiveCycleLimit = recursiveCycleLimit; } public List<String> getUniqueFieldList() { return uniqueFieldList; } public void setUniqueFieldList(List<String> uniqueFieldList) { this.uniqueFieldList = uniqueFieldList; } public long getStrStep() { return strStep; } public void setStrStep(long strStep) { this.strStep = strStep; } } @SuppressWarnings("rawtypes") private static class TypeUtil { private static final Set<Class> baseTypeCache = new HashSet<Class>(); private static final Set<Class> numTypeCache = new HashSet<Class>(); static { baseTypeCache.add(byte.class); baseTypeCache.add(short.class); baseTypeCache.add(int.class); baseTypeCache.add(long.class); baseTypeCache.add(float.class); baseTypeCache.add(double.class); baseTypeCache.add(char.class); baseTypeCache.add(boolean.class); baseTypeCache.add(Byte.class); baseTypeCache.add(Short.class); baseTypeCache.add(Integer.class); baseTypeCache.add(Long.class); baseTypeCache.add(Float.class); baseTypeCache.add(Double.class); baseTypeCache.add(Character.class); baseTypeCache.add(Boolean.class); baseTypeCache.add(String.class); baseTypeCache.add(Date.class); numTypeCache.add(byte.class); numTypeCache.add(short.class); numTypeCache.add(int.class); numTypeCache.add(long.class); numTypeCache.add(float.class); numTypeCache.add(double.class); numTypeCache.add(Byte.class); numTypeCache.add(Short.class); numTypeCache.add(Integer.class); numTypeCache.add(Long.class); numTypeCache.add(Float.class); numTypeCache.add(Double.class); } public static boolean isBaseType(Class clazz) { // if (fieldClazz.isPrimitive() // 8 ?? // || fieldClazz == Byte.class || fieldClazz == Short.class // || fieldClazz == Integer.class // || fieldClazz == Long.class || fieldClazz == Float.class // || fieldClazz == Double.class // || fieldClazz == Boolean.class || fieldClazz == Character.class// 8 ?? // || fieldClazz == Date.class || fieldClazz == String.class // // || fieldClazz.isEnum() // // ) return clazz.isEnum() || baseTypeCache.contains(clazz); } public static boolean isNumberType(Class clazz) { return numTypeCache.contains(clazz); } } @SuppressWarnings({ "unchecked", "rawtypes" }) private static <T> T produceBean(Class<T> clazz, ControlParam countrolParam, Stack<Class> parseClassList) { try { T item = clazz.newInstance(); for (PropertyDescriptor pd : BeanUtils.getPropertyDescriptors(clazz)) { Method writeMethod = pd.getWriteMethod(); if (writeMethod == null || pd.getReadMethod() == null || // countrolParam.getExcludeFieldList() != null && countrolParam.getExcludeFieldList().contains(pd.getName())// ) {// continue; } Class fieldClazz = pd.getPropertyType(); Long numIndex = countrolParam.getNumIndex(); // int enumIndex = countrolParam.getEnumIndex(); Random random = countrolParam.getRandom(); long strIndex = countrolParam.getStrIndex(); int charIndex = countrolParam.getCharIndex(); Calendar time = countrolParam.getTime(); if (TypeUtil.isBaseType(fieldClazz)) { if (TypeUtil.isNumberType(fieldClazz)) { if (fieldClazz == Byte.class) { writeMethod.invoke(item, Byte.valueOf((byte) (numIndex & 0x7F))); } else if (fieldClazz == Short.class) { writeMethod.invoke(item, Short.valueOf((short) (numIndex & 0x7FFF))); } else if (fieldClazz == Integer.class) { writeMethod.invoke(item, Integer.valueOf((int) (numIndex & 0x7FFFFFFF))); } else if (fieldClazz == Long.class) { writeMethod.invoke(item, Long.valueOf((long) numIndex)); } else if (fieldClazz == Float.class) { writeMethod.invoke(item, Float.valueOf((float) numIndex)); } else if (fieldClazz == Double.class) { writeMethod.invoke(item, Double.valueOf((double) numIndex)); } else if (fieldClazz == byte.class) {// writeMethod.invoke(item, (byte) (numIndex & 0x7F)); } else if (fieldClazz == short.class) { writeMethod.invoke(item, (short) (numIndex & 0x7FFF)); } else if (fieldClazz == int.class) { writeMethod.invoke(item, (int) (numIndex & 0x7FFFFFFF)); } else if (fieldClazz == long.class) { writeMethod.invoke(item, (long) numIndex); } else if (fieldClazz == float.class) { writeMethod.invoke(item, (float) numIndex); } else if (fieldClazz == double.class) { writeMethod.invoke(item, (double) numIndex); } numIndex++; if (numIndex < 0) { numIndex &= 0x7FFFFFFFFFFFFFFFL; } countrolParam.setNumIndex(numIndex); } else if (fieldClazz == boolean.class) { writeMethod.invoke(item, random.nextBoolean()); } else if (fieldClazz == Boolean.class) { writeMethod.invoke(item, Boolean.valueOf(random.nextBoolean())); } else if (fieldClazz == char.class) { writeMethod.invoke(item, CHAR_RANGE[charIndex]); charIndex++; if (charIndex >= CHAR_RANGE.length) { charIndex = 0; } countrolParam.setCharIndex(charIndex); } else if (fieldClazz == Character.class) { writeMethod.invoke(item, Character.valueOf(CHAR_RANGE[charIndex])); charIndex++; if (charIndex >= CHAR_RANGE.length) { charIndex = 0; } countrolParam.setCharIndex(charIndex); } else if (fieldClazz == String.class) { if (countrolParam.getUniqueFieldList() != null && countrolParam.getUniqueFieldList().contains(pd.getName())) { StringBuilder sb = new StringBuilder(); convertNum(strIndex, STRING_RANGE, countrolParam.getRandom(), sb); writeMethod.invoke(item, sb.toString()); strIndex += countrolParam.getStrStep(); if (strIndex < 0) { strIndex &= 0x7FFFFFFFFFFFFFFFL; } countrolParam.setStrIndex(strIndex); } else { writeMethod.invoke(item, String.valueOf(CHAR_RANGE[charIndex])); charIndex++; if (charIndex >= CHAR_RANGE.length) { charIndex = 0; } countrolParam.setCharIndex(charIndex); } } else if (fieldClazz == Date.class) { writeMethod.invoke(item, time.getTime()); time.add(Calendar.DAY_OF_YEAR, 1); } else if (fieldClazz.isEnum()) { int index = random.nextInt(fieldClazz.getEnumConstants().length); writeMethod.invoke(item, fieldClazz.getEnumConstants()[index]); } else { // throw new RuntimeException("out of countrol Class " + fieldClazz.getName()); } } else { parseClassList.push(fieldClazz); // TODO ? Set<Class> set = new HashSet<Class>(parseClassList); if (parseClassList.size() - set.size() <= countrolParam.getRecursiveCycleLimit()) { Object bean = produceBean(fieldClazz, countrolParam, parseClassList); writeMethod.invoke(item, bean); } parseClassList.pop(); } } return item; } catch (Exception e) { throw new RuntimeException(e); } } private static void convertNum(long num, Character[] baseChar, Random random, StringBuilder result) { if (num <= 0 && result.length() == 0) { result.insert(0, baseChar[0]); } else if (num > 0) { int i = (int) (num % baseChar.length); Character ch = random.nextBoolean() ? baseChar[i] : Character.toLowerCase(baseChar[i]); result.insert(0, ch); convertNum(num / baseChar.length, baseChar, random, result); } } }