package io.swagger; import io.swagger.models.Model; import org.powermock.api.mockito.PowerMockito; import org.powermock.reflect.Whitebox; import org.powermock.reflect.exceptions.FieldNotFoundException; import org.powermock.reflect.exceptions.MethodNotFoundException; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertNull; import static org.testng.Assert.assertTrue; public class TestUtils { private final static Logger LOGGER = Logger.getLogger(TestUtils.class.getName()); private TestUtils() { } private static <T> Map<Field, Object> getPropertiesAndValues(Class<T> clazz, Set<String> exclusions, boolean defaultValues, boolean includeInherited) throws InstantiationException, IllegalAccessException, ClassNotFoundException { Map<Field, Object> propertiesAndDefaultValues = new HashMap<Field, Object>(); Field[] fields = PowerMockito.fields(clazz); for (Field field : fields) { if (exclusions != null && exclusions.contains(field.getName())) continue; if (!includeInherited && field.getDeclaringClass() != clazz) continue; if (Modifier.isStatic(field.getModifiers())) continue; Class<?> type = field.getType(); Object value = null; if (defaultValues) { value = getTypeDefaultValue(type); } else { value = getTypeNonDefaultValue(type); } propertiesAndDefaultValues.put(field, value); } return propertiesAndDefaultValues; } private static boolean implementsMethod(Class<?> clazz, String methodName) { try { Method[] methods = PowerMockito.methods(clazz, new String[]{methodName}); for (Method method : methods) { if (method.getDeclaringClass().equals(clazz)) return true; } } catch (MethodNotFoundException ex) { LOGGER.log(Level.INFO, ex.getMessage(), ex); return false; } return false; } public static <T> void testEquals(Class<T> clazz, Set<String> exclusions, boolean useInheritedFields) throws InstantiationException, IllegalAccessException, ClassNotFoundException { T instance = Whitebox.newInstance(clazz); T other = Whitebox.newInstance(clazz); Map<Field, Object> propertiesAndDefaultValues = getPropertiesAndValues(clazz, exclusions, true, useInheritedFields); Map<Field, Object> propertiesAndNonDefaultValues = getPropertiesAndValues(clazz, exclusions, false, useInheritedFields); testEquals(instance, other, propertiesAndDefaultValues, propertiesAndNonDefaultValues); } private static <T> Map<Field, Object> getPropertiesAndValues(Class<T> clazz, Set<String> exclusions, boolean defaultValues) throws InstantiationException, IllegalAccessException, ClassNotFoundException { return getPropertiesAndValues(clazz, exclusions, defaultValues, false); } public static <T extends Model> void testClone(T instance, Object[] propertiesAndDefaultValues) { for (int i = 0; i < propertiesAndDefaultValues.length; i += 2) { String field = (String) propertiesAndDefaultValues[i]; Object value = propertiesAndDefaultValues[i + 1]; Whitebox.setInternalState(instance, field, value); } T cloned = (T) instance.clone(); for (int i = 0; i < propertiesAndDefaultValues.length; i += 2) { String field = (String) propertiesAndDefaultValues[i]; Object value = propertiesAndDefaultValues[i + 1]; assertEquals(Whitebox.getInternalState(cloned, field), value, "the value of the clone and the cloned instances must be se same for field " + field); } } public static <T> void testBuilders(Class<T> clazz, Set<String> exclusions) throws Exception { Map<Field, Object> propertiesAndNonDefaultValues = getPropertiesAndValues(clazz, exclusions, false, true); T instance = null; try { instance = clazz.newInstance(); } catch (InstantiationException ex) { instance = Whitebox.newInstance(clazz); } for (Field field : propertiesAndNonDefaultValues.keySet()) { Method[] methods = {}; try { methods = PowerMockito.methods(instance.getClass(), new String[]{field.getName()}); } catch (MethodNotFoundException ex) { continue; } for (Method method : methods) { if (method.getParameterTypes().length == 1) { Object value = propertiesAndNonDefaultValues.get(field); if (value != null && method.getParameterTypes()[0].isAssignableFrom(value.getClass())) { T res = Whitebox.invokeMethod(instance, field.getName(), value); assertEquals(Whitebox.getInternalState(res, field.getName()), value, "the value of the field must be the one that has just be set:" + field); } } } } } public static <T> void testCommonMethods(Class<T> clazz, Set<String> exclusions) throws Exception { T instance = null; Constructor<?>[] constructors = PowerMockito.constructorsDeclaredIn(clazz); for (Constructor<?> constructor : constructors) { Class<?>[] types = constructor.getParameterTypes(); List<Object> parameters = new ArrayList<Object>(); for (Class<?> type : types) { parameters.add(getTypeNonDefaultValue(type)); } try { instance = Whitebox.invokeConstructor(clazz, types, parameters.toArray()); } catch (Exception exx) { LOGGER.log(Level.INFO, exx.getMessage(), exx); continue; } } if (instance == null) { try { instance = clazz.newInstance(); } catch (InstantiationException ex) { LOGGER.log(Level.INFO, ex.getMessage(), ex); instance = Whitebox.newInstance(clazz); } } Method[] methods = {}; try { methods = PowerMockito.methodsDeclaredIn(instance.getClass()); } catch (MethodNotFoundException ex) { LOGGER.log(Level.INFO, ex.getMessage(), ex); } Set<String> commonMethodNames = new HashSet<String>( Arrays.asList("_default", "_enum", "example", "vendorExtension", "setEnum")); for (Method method : methods) { if (commonMethodNames.contains(method.getName()) && (exclusions == null || !exclusions.contains(method.getName()))) { System.out.println("testing common method: " + method); List<Object> parameters = new ArrayList<Object>(); Class<?>[] types = method.getParameterTypes(); if (types.length <= 2) { for (Class<?> type : types) { parameters.add(getTypeNonDefaultValue(type)); } } if (method.getName().equals("vendorExtension")) { parameters = Arrays.asList((Object) "x-vendor", "value"); } Object[] parametersArray = parameters.toArray(); String getterMethodName = method.getName(); if (method.getName().startsWith("set")) { getterMethodName = "g" + getterMethodName.substring(1); } else { if (getterMethodName.startsWith("_")) getterMethodName = getterMethodName.substring(1); getterMethodName = "get" + getterMethodName.substring(0, 1).toUpperCase() + getterMethodName.substring(1); if (parameters.size() > 1) getterMethodName = getterMethodName + "s"; } Object value = parametersArray[0]; boolean testWithUnknownString = false; if (parameters.size() == 1) { Method getMethod = null; try { getMethod = PowerMockito.method(clazz, getterMethodName); } catch (MethodNotFoundException ex) { LOGGER.log(Level.INFO, ex.getMessage()); continue; } Class<?> retType = getMethod.getReturnType(); if (value instanceof String && !retType.isAssignableFrom(String.class)) { value = getTypeNonDefaultValue(retType).toString(); parametersArray[0] = value; testWithUnknownString = true; } } try { Whitebox.setInternalState(instance, "_enum", (Object) null); } catch (FieldNotFoundException ex) { LOGGER.log(Level.INFO, ex.getMessage()); } Whitebox.invokeMethod(instance, method.getName(), parametersArray); Object res = Whitebox.invokeMethod(instance, getterMethodName); if (parameters.size() > 1 && res instanceof Map) { res = ((Map) res).get(parameters.get(0)); value = parameters.get(1); } if (value == null) { assertNull(res, "the value returned by " + getterMethodName + " must be null as we set it null through " + method); } else if (res instanceof Collection) { assertTrue(((Collection) res).contains(value) || res.equals(value), "the value returned by " + getterMethodName + " must contain the value set by " + method); } else { assertEquals(res.toString(), value.toString(), "the value returned by " + getterMethodName + " must be the same set by " + method); } if (testWithUnknownString) { // try to raise an xxxformatexception value = "unknown string"; parametersArray[0] = value; Object actual = Whitebox.invokeMethod(instance, getterMethodName); Whitebox.invokeMethod(instance, method.getName(), parametersArray); res = Whitebox.invokeMethod(instance, getterMethodName); assertEquals(actual, res, "the value must not change when passing an unknown value to " + method); } } } } public static <T> void testEquals(T instance, T other, Map<Field, Object> propertiesAndDefaultValues, Map<Field, Object> propertiesAndNonDefaultValues) { assertTrue(instance.equals(instance), "an instance must be equals to itself"); assertFalse(instance.equals(null), "an instance must not be equals to null"); assertFalse(instance.equals(new Object()), "an instance must not be equals to an object of another class"); boolean equals; for (Field field : propertiesAndDefaultValues.keySet()) { // given for (Field otherField : propertiesAndDefaultValues.keySet()) { if (field.equals(otherField)) continue; // make suer instance and other have the same values for the // other fields Whitebox.setInternalState(other, otherField.getName(), propertiesAndNonDefaultValues.get(otherField)); Whitebox.setInternalState(instance, otherField.getName(), propertiesAndNonDefaultValues.get(otherField)); } Object nonDefaultValue = propertiesAndNonDefaultValues.get(field); Whitebox.setInternalState(other, field.getName(), nonDefaultValue); Object defaultValue = propertiesAndDefaultValues.get(field); Whitebox.setInternalState(instance, field.getName(), defaultValue); // when equals = instance.equals(other); // then assertFalse(equals, "an instance can not be equals to another if they don't have the same values for the involved fields:" + field); assertFalse(other.equals(instance), "an instance can not be equals to another if they don't have the same values for the involved fields"); // test hashcode consistency assertEquals(instance.hashCode(), instance.hashCode(), "hashcode must be consinstent between calls"); Whitebox.setInternalState(instance, field.getName(), nonDefaultValue); } assertTrue(instance.equals(other), "if they have the same values, then the two instances must be equals: " + instance.getClass()); assertEquals(instance.hashCode(), other.hashCode(), "if two instances are equals, then they must have the same hashcode"); } public static final Object getTypeDefaultValue(Class<?> clazz) { if (clazz == byte.class) return 0; if (clazz == short.class) return 0; if (clazz == int.class) return 0; if (clazz == long.class) return 0L; if (clazz == char.class) return '\u0000'; if (clazz == float.class) return 0.0F; if (clazz == double.class) return 0.0; if (clazz == boolean.class) return false; return null; } public static final <T> Object getTypeNonDefaultValue(Class<T> clazz) { if (clazz == byte.class) return 1; if (clazz == short.class) return 1; if (clazz == int.class) return 1; if (clazz == long.class) return 1L; if (clazz == char.class) return 'z'; if (clazz == float.class) return 1.0F; if (clazz == double.class) return 1.0; if (clazz == boolean.class) return true; if (clazz == String.class) { return "non null string"; } if (clazz.equals(List.class)) { return new ArrayList(); } if (clazz.isEnum()) { try { T[] values = Whitebox.invokeMethod(clazz, "values"); return values[0]; } catch (Exception e) { LOGGER.log(Level.INFO, e.getMessage(), e); } } return PowerMockito.mock(clazz); } }