#1036 Add restoration options for Limbo allowFlight / fly speed / walk speed

- Introduce options to define how allow flight, fly & walk speed should be restored from LimboPlayer
- Create consistency tests for line length in SectionComments methods and to ensure that all SettingsHolder classes are part of the returned ConfigurationData
This commit is contained in:
ljacqu
2017-03-12 13:51:03 +01:00
parent c79ba49ca8
commit c766b5c259
13 changed files with 518 additions and 45 deletions
@@ -75,10 +75,11 @@ public final class ReflectionTestUtils {
}
}
public static Object invokeMethod(Method method, Object instance, Object... parameters) {
@SuppressWarnings("unchecked")
public static <V> V invokeMethod(Method method, Object instance, Object... parameters) {
method.setAccessible(true);
try {
return method.invoke(instance, parameters);
return (V) method.invoke(instance, parameters);
} catch (InvocationTargetException | IllegalAccessException e) {
throw new UnsupportedOperationException("Could not invoke method '" + method + "'", e);
}
@@ -0,0 +1,72 @@
package fr.xephi.authme.data.limbo;
import org.bukkit.entity.Player;
import org.junit.Test;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
/**
* Test for {@link AllowFlightRestoreType}.
*/
public class AllowFlightRestoreTypeTest {
@Test
public void shouldRestoreValue() {
// given
LimboPlayer limboWithFly = newLimboWithAllowFlight(true);
LimboPlayer limboWithoutFly = newLimboWithAllowFlight(false);
Player player1 = mock(Player.class);
Player player2 = mock(Player.class);
// when
AllowFlightRestoreType.RESTORE.restoreAllowFlight(player1, limboWithFly);
AllowFlightRestoreType.RESTORE.restoreAllowFlight(player2, limboWithoutFly);
// then
verify(player1).setAllowFlight(true);
verify(player2).setAllowFlight(false);
}
@Test
public void shouldEnableFlight() {
// given
LimboPlayer limboWithFly = newLimboWithAllowFlight(true);
LimboPlayer limboWithoutFly = newLimboWithAllowFlight(false);
Player player1 = mock(Player.class);
Player player2 = mock(Player.class);
// when
AllowFlightRestoreType.ENABLE.restoreAllowFlight(player1, limboWithFly);
AllowFlightRestoreType.ENABLE.restoreAllowFlight(player2, limboWithoutFly);
// then
verify(player1).setAllowFlight(true);
verify(player2).setAllowFlight(true);
}
@Test
public void shouldDisableFlight() {
// given
LimboPlayer limboWithFly = newLimboWithAllowFlight(true);
LimboPlayer limboWithoutFly = newLimboWithAllowFlight(false);
Player player1 = mock(Player.class);
Player player2 = mock(Player.class);
// when
AllowFlightRestoreType.DISABLE.restoreAllowFlight(player1, limboWithFly);
AllowFlightRestoreType.DISABLE.restoreAllowFlight(player2, limboWithoutFly);
// then
verify(player1).setAllowFlight(false);
verify(player2).setAllowFlight(false);
}
private static LimboPlayer newLimboWithAllowFlight(boolean allowFlight) {
LimboPlayer limbo = mock(LimboPlayer.class);
given(limbo.isCanFly()).willReturn(allowFlight);
return limbo;
}
}
@@ -5,6 +5,7 @@ import fr.xephi.authme.TestHelper;
import fr.xephi.authme.permission.PermissionsManager;
import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.SpawnLoader;
import fr.xephi.authme.settings.properties.LimboSettings;
import fr.xephi.authme.settings.properties.RestrictionSettings;
import org.bukkit.Location;
import org.bukkit.entity.Player;
@@ -19,8 +20,6 @@ import org.mockito.junit.MockitoJUnitRunner;
import java.util.Map;
import static org.hamcrest.Matchers.anEmptyMap;
import static org.hamcrest.Matchers.both;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.nullValue;
@@ -85,7 +84,8 @@ public class LimboServiceTest {
verify(player).setFlySpeed(0.0f);
verify(player).setWalkSpeed(0.0f);
LimboPlayer limbo = getLimboMap().get("bobby");
assertThat(limboService.hasLimboPlayer("Bobby"), equalTo(true));
LimboPlayer limbo = limboService.getLimboPlayer("Bobby");
assertThat(limbo, not(nullValue()));
assertThat(limbo.isOperator(), equalTo(true));
assertThat(limbo.getWalkSpeed(), equalTo(0.3f));
@@ -114,7 +114,7 @@ public class LimboServiceTest {
verify(player).setFlySpeed(0.0f);
verify(player).setWalkSpeed(0.0f);
LimboPlayer limbo = getLimboMap().get("charles");
LimboPlayer limbo = limboService.getLimboPlayer("charles");
assertThat(limbo, not(nullValue()));
assertThat(limbo.isOperator(), equalTo(false));
assertThat(limbo.getWalkSpeed(), equalTo(0.1f));
@@ -127,24 +127,31 @@ public class LimboServiceTest {
@Test
public void shouldClearTasksOnAlreadyExistingLimbo() {
// given
LimboPlayer limbo = mock(LimboPlayer.class);
getLimboMap().put("carlos", limbo);
LimboPlayer existingLimbo = mock(LimboPlayer.class);
getLimboMap().put("carlos", existingLimbo);
Player player = newPlayer("Carlos");
// when
limboService.createLimboPlayer(player, false);
// then
verify(limbo).clearTasks();
assertThat(getLimboMap().get("carlos"), both(not(sameInstance(limbo))).and(not(nullValue())));
verify(existingLimbo).clearTasks();
LimboPlayer newLimbo = limboService.getLimboPlayer("Carlos");
assertThat(newLimbo, not(nullValue()));
assertThat(newLimbo, not(sameInstance(existingLimbo)));
}
@Test
public void shouldRestoreData() {
// given
Player player = newPlayer("John", true, 0.4f, false, 0.2f);
LimboPlayer limbo = Mockito.spy(convertToLimboPlayer(player, null, ""));
LimboPlayer limbo = Mockito.spy(convertToLimboPlayer(
newPlayer("John", true, 0.4f, false, 0.0f), null, ""));
getLimboMap().put("john", limbo);
Player player = newPlayer("John", false, 0.2f, false, 0.7f);
given(settings.getProperty(LimboSettings.RESTORE_ALLOW_FLIGHT)).willReturn(AllowFlightRestoreType.ENABLE);
given(settings.getProperty(LimboSettings.RESTORE_WALK_SPEED)).willReturn(WalkFlySpeedRestoreType.RESTORE);
given(settings.getProperty(LimboSettings.RESTORE_FLY_SPEED)).willReturn(WalkFlySpeedRestoreType.RESTORE_NO_ZERO);
// when
limboService.restoreData(player);
@@ -152,10 +159,10 @@ public class LimboServiceTest {
// then
verify(player).setOp(true);
verify(player).setWalkSpeed(0.4f);
verify(player).setAllowFlight(false);
verify(player).setFlySpeed(0.2f);
verify(player).setAllowFlight(true);
verify(player).setFlySpeed(LimboPlayer.DEFAULT_FLY_SPEED);
verify(limbo).clearTasks();
assertThat(getLimboMap(), anEmptyMap());
assertThat(limboService.hasLimboPlayer("John"), equalTo(false));
}
@Test
@@ -0,0 +1,108 @@
package fr.xephi.authme.data.limbo;
import org.bukkit.entity.Player;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import static fr.xephi.authme.data.limbo.LimboPlayer.DEFAULT_FLY_SPEED;
import static fr.xephi.authme.data.limbo.LimboPlayer.DEFAULT_WALK_SPEED;
import static fr.xephi.authme.data.limbo.WalkFlySpeedRestoreType.DEFAULT;
import static fr.xephi.authme.data.limbo.WalkFlySpeedRestoreType.MAX_RESTORE;
import static fr.xephi.authme.data.limbo.WalkFlySpeedRestoreType.RESTORE;
import static fr.xephi.authme.data.limbo.WalkFlySpeedRestoreType.RESTORE_NO_ZERO;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
/**
* Test for {@link WalkFlySpeedRestoreType}.
*/
@RunWith(Parameterized.class)
public class WalkFlySpeedRestoreTypeTest {
private final TestParameters parameters;
public WalkFlySpeedRestoreTypeTest(TestParameters parameters) {
this.parameters = parameters;
}
@Test
public void shouldRestoreToExpectedValue() {
// given
LimboPlayer limbo = mock(LimboPlayer.class);
given(limbo.getWalkSpeed()).willReturn(parameters.givenLimboWalkSpeed);
given(limbo.getFlySpeed()).willReturn(parameters.givenLimboFlySpeed);
Player player = mock(Player.class);
given(player.getWalkSpeed()).willReturn(parameters.givenPlayerWalkSpeed);
given(player.getFlySpeed()).willReturn(parameters.givenPlayerFlySpeed);
// when
parameters.testedType.restoreWalkSpeed(player, limbo);
parameters.testedType.restoreFlySpeed(player, limbo);
// then
verify(player).setWalkSpeed(parameters.expectedWalkSpeed);
verify(player).setFlySpeed(parameters.expectedFlySpeed);
}
@Parameterized.Parameters(name = "{0}")
public static List<Object[]> buildParams() {
List<TestParameters> parameters = Arrays.asList(
create(RESTORE).withLimbo(0.1f, 0.4f).withPlayer(0.3f, 0.9f).expect(0.1f, 0.4f),
create(RESTORE).withLimbo(0.9f, 0.2f).withPlayer(0.3f, 0.0f).expect(0.9f, 0.2f),
create(MAX_RESTORE).withLimbo(0.3f, 0.8f).withPlayer(0.5f, 0.2f).expect(0.5f, 0.8f),
create(MAX_RESTORE).withLimbo(0.4f, 0.2f).withPlayer(0.1f, 0.4f).expect(0.4f, 0.4f),
create(RESTORE_NO_ZERO).withLimbo(0.1f, 0.2f).withPlayer(0.5f, 0.1f).expect(0.1f, 0.2f),
create(RESTORE_NO_ZERO).withLimbo(0.0f, 0.005f).withPlayer(0.4f, 0.8f).expect(DEFAULT_WALK_SPEED, DEFAULT_FLY_SPEED),
create(DEFAULT).withLimbo(0.1f, 0.7f).withPlayer(0.4f, 0.0f).expect(DEFAULT_WALK_SPEED, DEFAULT_FLY_SPEED)
);
// Convert List<TestParameters> to List<Object[]>
return parameters.stream().map(p -> new Object[]{p}).collect(Collectors.toList());
}
private static TestParameters create(WalkFlySpeedRestoreType testedType) {
TestParameters params = new TestParameters();
params.testedType = testedType;
return params;
}
private static final class TestParameters {
private WalkFlySpeedRestoreType testedType;
private float givenLimboWalkSpeed;
private float givenLimboFlySpeed;
private float givenPlayerWalkSpeed;
private float givenPlayerFlySpeed;
private float expectedWalkSpeed;
private float expectedFlySpeed;
TestParameters withLimbo(float walkSpeed, float flySpeed) {
this.givenLimboWalkSpeed = walkSpeed;
this.givenLimboFlySpeed = flySpeed;
return this;
}
TestParameters withPlayer(float walkSpeed, float flySpeed) {
this.givenPlayerWalkSpeed = walkSpeed;
this.givenPlayerFlySpeed = flySpeed;
return this;
}
TestParameters expect(float walkSpeed, float flySpeed) {
this.expectedWalkSpeed = walkSpeed;
this.expectedFlySpeed = flySpeed;
return this;
}
@Override
public String toString() {
return testedType + " {" + expectedWalkSpeed + ", " + expectedFlySpeed + "}";
}
}
}
@@ -1,15 +1,25 @@
package fr.xephi.authme.settings;
import ch.jalu.configme.SectionComments;
import ch.jalu.configme.SettingsHolder;
import ch.jalu.configme.configurationdata.ConfigurationData;
import ch.jalu.configme.properties.Property;
import fr.xephi.authme.ClassCollector;
import fr.xephi.authme.ReflectionTestUtils;
import fr.xephi.authme.settings.properties.AuthMeSettingsRetriever;
import org.junit.BeforeClass;
import org.junit.Test;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import static com.google.common.base.Preconditions.checkArgument;
import static org.junit.Assert.fail;
/**
@@ -66,4 +76,60 @@ public class SettingsConsistencyTest {
}
}
@Test
public void shouldNotHaveVeryLongSectionCommentLines() {
// given
List<Method> sectionCommentMethods = getSectionCommentMethods();
Set<Method> badMethods = new HashSet<>();
// when
for (Method method : sectionCommentMethods) {
boolean hasTooLongLine = getSectionComments(method).stream()
.anyMatch(line -> line.length() > MAX_COMMENT_LENGTH);
if (hasTooLongLine) {
badMethods.add(method);
}
}
// then
if (!badMethods.isEmpty()) {
String methodList = badMethods.stream()
.map(m -> m.getName() + " in " + m.getDeclaringClass().getSimpleName())
.collect(Collectors.joining("\n- "));
fail("Found SectionComments methods with too long comments:\n- " + methodList);
}
}
/**
* Gets all {@link SectionComments} methods from {@link SettingsHolder} implementations.
*/
@SuppressWarnings("unchecked")
private List<Method> getSectionCommentMethods() {
// Find all SettingsHolder classes
List<Class<? extends SettingsHolder>> settingsClasses =
new ClassCollector("src/main/java", "fr/xephi/authme/settings/properties/")
.collectClasses(SettingsHolder.class);
checkArgument(!settingsClasses.isEmpty(), "Could not find any SettingsHolder classes");
// Find all @SectionComments methods in these classes
return settingsClasses.stream()
.map(Class::getDeclaredMethods)
.flatMap(Arrays::stream)
.filter(method -> method.isAnnotationPresent(SectionComments.class))
.collect(Collectors.toList());
}
/**
* Returns all comments returned from the given SectionComments method, flattened into one list.
*
* @param sectionCommentsMethod the method whose comments should be retrieved
* @return flattened list of all comments provided by the method
*/
private static List<String> getSectionComments(Method sectionCommentsMethod) {
// @SectionComments methods are static
Map<String, String[]> comments = ReflectionTestUtils.invokeMethod(sectionCommentsMethod, null);
return comments.values().stream()
.flatMap(Arrays::stream)
.collect(Collectors.toList());
}
}
@@ -1,6 +1,7 @@
package fr.xephi.authme.settings.properties;
import ch.jalu.configme.SettingsHolder;
import ch.jalu.configme.configurationdata.ConfigurationData;
import ch.jalu.configme.properties.Property;
import fr.xephi.authme.ClassCollector;
import fr.xephi.authme.ReflectionTestUtils;
@@ -10,11 +11,13 @@ import org.junit.Test;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasSize;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
@@ -74,6 +77,27 @@ public class SettingsClassConsistencyTest {
}
}
/**
* Checks that {@link AuthMeSettingsRetriever} returns a ConfigurationData with all
* available SettingsHolder classes.
*/
@Test
public void shouldHaveAllClassesInConfigurationData() {
// given
long totalProperties = classes.stream()
.map(Class::getDeclaredFields)
.flatMap(Arrays::stream)
.filter(field -> Property.class.isAssignableFrom(field.getType()))
.count();
// when
ConfigurationData configData = AuthMeSettingsRetriever.buildConfigurationData();
// then
assertThat("ConfigurationData should have " + totalProperties + " properties (as found manually)",
configData.getProperties(), hasSize((int) totalProperties));
}
@Test
public void shouldHaveHiddenEmptyConstructorOnly() {
for (Class<?> clazz : classes) {