/** * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ package net.sourceforge.pmd.properties; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import org.junit.Assert; import org.junit.Test; import net.sourceforge.pmd.PropertyDescriptor; import net.sourceforge.pmd.PropertyDescriptorFactory; import net.sourceforge.pmd.PropertyDescriptorFields; import net.sourceforge.pmd.lang.rule.properties.factories.PropertyDescriptorUtil; import net.sourceforge.pmd.util.CollectionUtil; /** * Base functionality for all concrete subclasses that evaluate type-specific * property descriptors. Checks for error conditions during construction, error * value detection, serialization, etc. * * @author Brian Remedios */ public abstract class AbstractPropertyDescriptorTester { public AbstractPropertyDescriptorTester(String typeName) { this.typeName = typeName; } protected final String typeName; private static final int MULTI_VALUE_COUNT = 10; public static final String PUNCTUATION_CHARS = "!@#$%^&*()_-+=[]{}\\|;:'\",.<>/?`~"; public static final String WHITESPACE_CHARS = " \t\n"; public static final String DIGIST_CHARS = "0123456789"; public static final String ALPHA_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmniopqrstuvwxyz"; public static final String ALPHA_NUMERIC_CHARS = DIGIST_CHARS + ALPHA_CHARS; public static final String ALL_CHARS = PUNCTUATION_CHARS + WHITESPACE_CHARS + ALPHA_NUMERIC_CHARS; /** * Return a legal value(s) per the general scope of the descriptor. * * @param count * int * @return Object */ protected abstract Object createValue(int count); /** * Return a value(s) that is known to be faulty per the general scope of the * descriptor. * * @param count * int * @return Object */ protected abstract Object createBadValue(int count); /** * Creates and returns a properly configured property descriptor. * * @param multiValue * boolean * @return PropertyDescriptor */ protected abstract PropertyDescriptor createProperty(boolean multiValue); /** * Attempt to create a property with faulty configuration values. This * method should throw an IllegalArgumentException if done correctly. * * @param multiValue * boolean * @return PropertyDescriptor */ protected abstract PropertyDescriptor createBadProperty(boolean multiValue); protected final PropertyDescriptorFactory getSingleFactory() { return PropertyDescriptorUtil.factoryFor(typeName); } protected final PropertyDescriptorFactory getMultiFactory() { return PropertyDescriptorUtil.factoryFor(typeName + "[]"); } private Map<String, String> getPropertyDescriptorValues() { Map<String, String> valuesById = new HashMap<>(); valuesById.put(PropertyDescriptorFields.NAME, "test"); valuesById.put(PropertyDescriptorFields.DESCRIPTION, "desc"); valuesById.put(PropertyDescriptorFields.MIN, "0"); valuesById.put(PropertyDescriptorFields.MAX, "10"); return valuesById; } @Test public void testFactorySingleValue() { PropertyDescriptor prop = getSingleFactory().createWith(getPropertyDescriptorValues()); Object originalValue = createValue(1); Object value = prop.valueFrom( originalValue instanceof Class ? ((Class) originalValue).getName() : String.valueOf(originalValue)); String asDelimitedString = prop.asDelimitedString(value); Object value2 = prop.valueFrom(asDelimitedString); Assert.assertEquals(value, value2); } @Test public void testFactoryMultiValueDefaultDelimiter() { PropertyDescriptorFactory multiFactory = getMultiFactory(); PropertyDescriptor prop = multiFactory.createWith(getPropertyDescriptorValues()); Object originalValue = createValue(MULTI_VALUE_COUNT); String asDelimitedString = prop.asDelimitedString(originalValue); Object value2 = prop.valueFrom(asDelimitedString); Assert.assertArrayEquals((Object[]) originalValue, (Object[]) value2); } @Test public void testFactoryMultiValueCustomDelimiter() { PropertyDescriptorFactory multiFactory = getMultiFactory(); Map<String, String> valuesById = getPropertyDescriptorValues(); String customDelimiter = "รค"; Assert.assertFalse(ALL_CHARS.contains(customDelimiter)); valuesById.put(PropertyDescriptorFields.DELIMITER, customDelimiter); PropertyDescriptor prop = multiFactory.createWith(valuesById); Object originalValue = createValue(MULTI_VALUE_COUNT); String asDelimitedString = prop.asDelimitedString(originalValue); Object value2 = prop.valueFrom(asDelimitedString); Assert.assertEquals(Arrays.toString((Object[]) originalValue), Arrays.toString((Object[]) value2)); Assert.assertArrayEquals((Object[]) originalValue, (Object[]) value2); } @Test public void testConstructors() { PropertyDescriptor<?> desc = createProperty(false); assertNotNull(desc); try { createBadProperty(false); } catch (Exception ex) { return; // caught ok } Assert.fail("uncaught constructor exception"); } @Test public void testAsDelimitedString() { Object testValue = createValue(MULTI_VALUE_COUNT); PropertyDescriptor pmdProp = createProperty(true); String storeValue = pmdProp.asDelimitedString(testValue); Object returnedValue = pmdProp.valueFrom(storeValue); assertTrue(CollectionUtil.areEqual(returnedValue, testValue)); } @Test public void testValueFrom() { Object testValue = createValue(1); PropertyDescriptor pmdProp = createProperty(false); String storeValue = pmdProp.asDelimitedString(testValue); Object returnedValue = pmdProp.valueFrom(storeValue); assertTrue(CollectionUtil.areEqual(returnedValue, testValue)); } @Test public void testErrorFor() { Object testValue = createValue(1); PropertyDescriptor<?> pmdProp = createProperty(false); // plain vanilla // property & // valid test // value String errorMsg = pmdProp.errorFor(testValue); assertNull(errorMsg, errorMsg); testValue = createValue(MULTI_VALUE_COUNT); // multi-value property, all // valid test values pmdProp = createProperty(true); errorMsg = pmdProp.errorFor(testValue); assertNull(errorMsg, errorMsg); } @Test public void testErrorForBad() { PropertyDescriptor<?> pmdProp = createProperty(false); Object testValue = createBadValue(1); String errorMsg = pmdProp.errorFor(testValue); // bad value should // result in an error if (errorMsg == null) { Assert.fail("uncaught bad value: " + testValue); } testValue = createBadValue(MULTI_VALUE_COUNT); // multi-value prop, // several bad values pmdProp = createProperty(true); errorMsg = pmdProp.errorFor(testValue); if (errorMsg == null) { Assert.fail("uncaught bad value in: " + testValue); } } @Test public void testType() { PropertyDescriptor<?> pmdProp = createProperty(false); assertNotNull(pmdProp.type()); } public static boolean randomBool() { return ((Math.random() * 100) % 2) == 0; } /** * Method randomInt. * * @return int */ public static int randomInt() { int randomVal = (int) (Math.random() * 100 + 1D); return randomVal + (int) (Math.random() * 100000D); } /** * Method randomInt. * * @param min * int * @param max * int * @return int */ public static int randomInt(int min, int max) { if (max < min) { max = min; } int range = Math.abs(max - min); int x = (int) (range * Math.random()); return x + min; } public static String randomString(int length) { final char[] chars = ALPHA_CHARS.toCharArray(); StringBuilder sb = new StringBuilder(length); for (int i = 0; i < length; i++) { sb.append(randomChar(chars)); } return sb.toString(); } /** * Method randomFloat. * * @param min * float * @param max * float * @return float */ public static float randomFloat(float min, float max) { return (float) randomDouble(min, max); } /** * Method randomDouble. * * @param min * double * @param max * double * @return double */ public static double randomDouble(double min, double max) { if (max < min) { max = min; } double range = Math.abs(max - min); double x = range * Math.random(); return x + min; } /** * Method randomChar. * * @param characters * char[] * @return char */ public static char randomChar(char[] characters) { return characters[randomInt(0, characters.length - 1)]; } /** * Method randomChoice. * * @param items * Object[] * @return Object */ public static Object randomChoice(Object[] items) { return items[randomInt(0, items.length - 1)]; } /** * Method filter. * * @param chars * char[] * @param removeChar * char * @return char[] */ protected static final char[] filter(char[] chars, char removeChar) { int count = 0; for (int i = 0; i < chars.length; i++) { if (chars[i] == removeChar) { count++; } } char[] results = new char[chars.length - count]; int index = 0; for (int i = 0; i < chars.length; i++) { if (chars[i] != removeChar) { results[index++] = chars[i]; } } return results; } }