package fr.xephi.authme.settings.properties; import fr.xephi.authme.ReflectionTestUtils; import fr.xephi.authme.settings.domain.Property; import fr.xephi.authme.settings.domain.SettingsClass; import org.junit.BeforeClass; import org.junit.Test; import java.io.File; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import static org.hamcrest.Matchers.arrayWithSize; import static org.hamcrest.Matchers.equalTo; import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; /** * Test for {@link SettingsClass} implementations. */ public class SettingsClassConsistencyTest { private static final String SETTINGS_FOLDER = "src/main/java/fr/xephi/authme/settings/properties"; private static List> classes; @BeforeClass public static void scanForSettingsClasses() { File settingsFolder = new File(SETTINGS_FOLDER); File[] filesInFolder = settingsFolder.listFiles(); if (filesInFolder == null || filesInFolder.length == 0) { throw new IllegalStateException("Could not read folder '" + SETTINGS_FOLDER + "'. Is it correct?"); } classes = new ArrayList<>(); for (File file : filesInFolder) { Class clazz = getSettingsClassFromFile(file); if (clazz != null) { classes.add(clazz); } } System.out.println("Found " + classes.size() + " SettingsClass implementations"); } /** * Make sure that all {@link Property} instances we define are in public, static, final fields. */ @Test public void shouldHavePublicStaticFinalFields() { for (Class clazz : classes) { Field[] fields = clazz.getDeclaredFields(); for (Field field : fields) { if (Property.class.isAssignableFrom(field.getType())) { String fieldName = "Field " + clazz.getSimpleName() + "#" + field.getName(); assertThat(fieldName + " should be public, static, and final", isValidConstantField(field), equalTo(true)); } } } } /** * Make sure that no properties use the same path. */ @Test public void shouldHaveUniquePaths() { Set paths = new HashSet<>(); for (Class clazz : classes) { Field[] fields = clazz.getDeclaredFields(); for (Field field : fields) { if (Property.class.isAssignableFrom(field.getType())) { Property property = (Property) ReflectionTestUtils.getFieldValue(clazz, null, field.getName()); if (paths.contains(property.getPath())) { fail("Path '" + property.getPath() + "' should be used by only one constant"); } paths.add(property.getPath()); } } } } @Test public void shouldHaveHiddenDefaultConstructorOnly() { for (Class clazz : classes) { Constructor[] constructors = clazz.getDeclaredConstructors(); assertThat(clazz + " should only have one constructor", constructors, arrayWithSize(1)); assertThat("Constructor of " + clazz + " is private", Modifier.isPrivate(constructors[0].getModifiers()), equalTo(true)); // Ugly hack to get coverage on the private constructors // http://stackoverflow.com/questions/14077842/how-to-test-a-private-constructor-in-java-application try { Constructor constructor = clazz.getDeclaredConstructor(); constructor.setAccessible(true); constructor.newInstance(); } catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) { e.printStackTrace(); } } } private static boolean isValidConstantField(Field field) { int modifiers = field.getModifiers(); return Modifier.isPublic(modifiers) && Modifier.isStatic(modifiers) && Modifier.isFinal(modifiers); } @SuppressWarnings("unchecked") private static Class getSettingsClassFromFile(File file) { String fileName = file.getPath(); String className = fileName .substring("src/main/java/".length(), fileName.length() - ".java".length()) .replace(File.separator, "."); try { Class clazz = SettingsClassConsistencyTest.class.getClassLoader().loadClass(className); if (SettingsClass.class.isAssignableFrom(clazz)) { return (Class) clazz; } return null; } catch (ClassNotFoundException e) { throw new IllegalStateException("Could not load class '" + className + "'", e); } } }