diff --git a/src/main/java/fr/xephi/authme/settings/domain/EnumPropertyType.java b/src/main/java/fr/xephi/authme/settings/domain/EnumPropertyType.java new file mode 100644 index 000000000..8455a893a --- /dev/null +++ b/src/main/java/fr/xephi/authme/settings/domain/EnumPropertyType.java @@ -0,0 +1,44 @@ +package fr.xephi.authme.settings.domain; + +import org.bukkit.configuration.file.YamlConfiguration; + +import java.util.List; + +import static java.util.Arrays.asList; + +/** + * Enum property type. + * @param The enum class + */ +class EnumPropertyType> extends PropertyType { + + private Class clazz; + + public EnumPropertyType(Class clazz) { + this.clazz = clazz; + } + + @Override + public E getFromFile(Property property, YamlConfiguration configuration) { + String textValue = configuration.getString(property.getPath()); + if (textValue == null) { + return property.getDefaultValue(); + } + E mappedValue = mapToEnum(textValue); + return mappedValue != null ? mappedValue : property.getDefaultValue(); + } + + @Override + protected List asYaml(E value) { + return asList("'" + value + "'"); + } + + private E mapToEnum(String value) { + for (E entry : clazz.getEnumConstants()) { + if (entry.name().equalsIgnoreCase(value)) { + return entry; + } + } + return null; + } +} diff --git a/src/main/java/fr/xephi/authme/settings/domain/Property.java b/src/main/java/fr/xephi/authme/settings/domain/Property.java index 71e111648..809f7a987 100644 --- a/src/main/java/fr/xephi/authme/settings/domain/Property.java +++ b/src/main/java/fr/xephi/authme/settings/domain/Property.java @@ -32,7 +32,7 @@ public class Property { } public static > Property newProperty(Class clazz, String path, E defaultValue) { - return new Property<>(new PropertyType.EnumProperty<>(clazz), path, defaultValue); + return new Property<>(new EnumPropertyType<>(clazz), path, defaultValue); } // ----- diff --git a/src/main/java/fr/xephi/authme/settings/domain/PropertyType.java b/src/main/java/fr/xephi/authme/settings/domain/PropertyType.java index dba0e9712..e41965640 100644 --- a/src/main/java/fr/xephi/authme/settings/domain/PropertyType.java +++ b/src/main/java/fr/xephi/authme/settings/domain/PropertyType.java @@ -143,40 +143,4 @@ public abstract class PropertyType { } } - /** - * Enum property. - * @param The enum class - */ - static final class EnumProperty> extends PropertyType { - private Class clazz; - - public EnumProperty(Class clazz) { - this.clazz = clazz; - } - - @Override - public E getFromFile(Property property, YamlConfiguration configuration) { - String textValue = configuration.getString(property.getPath()); - if (textValue == null) { - return property.getDefaultValue(); - } - E mappedValue = mapToEnum(textValue); - return mappedValue != null ? mappedValue : property.getDefaultValue(); - } - - @Override - protected List asYaml(E value) { - return asList("'" + value + "'"); - } - - private E mapToEnum(String value) { - for (E entry : clazz.getEnumConstants()) { - if (entry.name().equalsIgnoreCase(value)) { - return entry; - } - } - return null; - } - } - } diff --git a/src/test/java/fr/xephi/authme/settings/custom/SettingsClassConsistencyTest.java b/src/test/java/fr/xephi/authme/settings/custom/SettingsClassConsistencyTest.java new file mode 100644 index 000000000..c06ed11b1 --- /dev/null +++ b/src/test/java/fr/xephi/authme/settings/custom/SettingsClassConsistencyTest.java @@ -0,0 +1,106 @@ +package fr.xephi.authme.settings.custom; + +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.Field; +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.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/custom"; + 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()); + } + } + } + } + + private static boolean isValidConstantField(Field field) { + int modifiers = field.getModifiers(); + return Modifier.isPublic(modifiers) && Modifier.isStatic(modifiers) && Modifier.isFinal(modifiers); + } + + 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); + } + } + +} diff --git a/src/test/java/fr/xephi/authme/settings/domain/EnumPropertyTypeTest.java b/src/test/java/fr/xephi/authme/settings/domain/EnumPropertyTypeTest.java new file mode 100644 index 000000000..1b9d39091 --- /dev/null +++ b/src/test/java/fr/xephi/authme/settings/domain/EnumPropertyTypeTest.java @@ -0,0 +1,71 @@ +package fr.xephi.authme.settings.domain; + +import org.bukkit.configuration.file.YamlConfiguration; +import org.junit.Test; + +import static org.hamcrest.Matchers.equalTo; +import static org.junit.Assert.assertThat; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; + +/** + * Test for {@link EnumPropertyType}. + */ +public class EnumPropertyTypeTest { + + @Test + public void shouldReturnCorrectEnumValue() { + // given + PropertyType propertyType = new EnumPropertyType<>(TestEnum.class); + Property property = Property.newProperty(TestEnum.class, "enum.path", TestEnum.ENTRY_C); + YamlConfiguration configuration = mock(YamlConfiguration.class); + given(configuration.getString(property.getPath())).willReturn("Entry_B"); + + // when + TestEnum result = propertyType.getFromFile(property, configuration); + + // then + assertThat(result, equalTo(TestEnum.ENTRY_B)); + } + + @Test + public void shouldFallBackToDefaultForInvalidValue() { + // given + PropertyType propertyType = new EnumPropertyType<>(TestEnum.class); + Property property = Property.newProperty(TestEnum.class, "enum.path", TestEnum.ENTRY_C); + YamlConfiguration configuration = mock(YamlConfiguration.class); + given(configuration.getString(property.getPath())).willReturn("Bogus"); + + // when + TestEnum result = propertyType.getFromFile(property, configuration); + + // then + assertThat(result, equalTo(TestEnum.ENTRY_C)); + } + + @Test + public void shouldFallBackToDefaultForNonExistentValue() { + // given + PropertyType propertyType = new EnumPropertyType<>(TestEnum.class); + Property property = Property.newProperty(TestEnum.class, "enum.path", TestEnum.ENTRY_C); + YamlConfiguration configuration = mock(YamlConfiguration.class); + given(configuration.getString(property.getPath())).willReturn(null); + + // when + TestEnum result = propertyType.getFromFile(property, configuration); + + // then + assertThat(result, equalTo(TestEnum.ENTRY_C)); + } + + + private enum TestEnum { + + ENTRY_A, + + ENTRY_B, + + ENTRY_C + + } +}