diff --git a/pom.xml b/pom.xml index 9c19232fd..7404cac4b 100644 --- a/pom.xml +++ b/pom.xml @@ -90,6 +90,11 @@ + + + src/test/resources + + diff --git a/src/main/java/fr/xephi/authme/AuthMe.java b/src/main/java/fr/xephi/authme/AuthMe.java index 7630b99e2..e68eb9a37 100644 --- a/src/main/java/fr/xephi/authme/AuthMe.java +++ b/src/main/java/fr/xephi/authme/AuthMe.java @@ -462,7 +462,7 @@ public class AuthMe extends JavaPlugin { private void setupConsoleFilter() { if (Settings.removePassword) { ConsoleFilter filter = new ConsoleFilter(); - ConsoleLogger.getLogger().setFilter(filter); + getLogger().setFilter(filter); Bukkit.getLogger().setFilter(filter); Logger.getLogger("Minecraft").setFilter(filter); // Set Log4J Filter @@ -955,20 +955,10 @@ public class AuthMe extends JavaPlugin { } /** - * Returns the management instance. + * Return the management instance. */ public Management getManagement() { return management; } - /** - * Returns the server instance running this plugin. Use this method in favor of {@link - * AuthMe#getServer()} for testability purposes. - * - * @return the server instance - */ - public Server getGameServer() { - return super.getServer(); - } - } diff --git a/src/main/java/fr/xephi/authme/ConsoleLogger.java b/src/main/java/fr/xephi/authme/ConsoleLogger.java index 0bbacbfce..ae8a76619 100644 --- a/src/main/java/fr/xephi/authme/ConsoleLogger.java +++ b/src/main/java/fr/xephi/authme/ConsoleLogger.java @@ -3,6 +3,7 @@ package fr.xephi.authme; import com.google.common.base.Throwables; import fr.xephi.authme.settings.Settings; import fr.xephi.authme.util.StringUtils; +import fr.xephi.authme.util.Wrapper; import java.io.IOException; import java.nio.file.Files; @@ -10,23 +11,17 @@ import java.nio.file.StandardOpenOption; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; -import java.util.logging.Logger; /** * The plugin's static logger. */ -public class ConsoleLogger { +public final class ConsoleLogger { - private static final Logger log = AuthMe.getInstance().getLogger(); + private static Wrapper wrapper = new Wrapper(AuthMe.getInstance()); private static final DateFormat df = new SimpleDateFormat("[MM-dd HH:mm:ss]"); - /** - * Returns the plugin's logger. - * - * @return Logger - */ - public static Logger getLogger() { - return log; + private ConsoleLogger() { + // Service class } /** @@ -35,7 +30,7 @@ public class ConsoleLogger { * @param message String */ public static void info(String message) { - log.info(message); + wrapper.getLogger().info(message); if (!Settings.useLogging) { return; } @@ -48,7 +43,7 @@ public class ConsoleLogger { * @param message String */ public static void showError(String message) { - log.warning(message); + wrapper.getLogger().warning(message); if (!Settings.useLogging) { return; } diff --git a/src/main/java/fr/xephi/authme/util/GeoLiteAPI.java b/src/main/java/fr/xephi/authme/util/GeoLiteAPI.java index 76bd9d8c7..85e402112 100644 --- a/src/main/java/fr/xephi/authme/util/GeoLiteAPI.java +++ b/src/main/java/fr/xephi/authme/util/GeoLiteAPI.java @@ -12,15 +12,15 @@ import java.util.zip.GZIPInputStream; public class GeoLiteAPI { - private static final String GEOIP_URL = "http://geolite.maxmind.com/download/geoip/database/GeoLiteCountry" + - "/GeoIP.dat.gz"; - private static final AuthMe plugin = AuthMe.getInstance(); + private static final String GEOIP_URL = "http://geolite.maxmind.com/download/geoip/database/GeoLiteCountry" + + "/GeoIP.dat.gz"; + private static final Wrapper wrapper = new Wrapper(AuthMe.getInstance()); private static LookupService lookupService; /** * Download (if absent) the GeoIpLite data file and then try to load it. * - * @return Boolean True if the data is available, false if not. + * @return True if the data is available, false otherwise. */ public static boolean isDataAvailable() { if (lookupService != null) { @@ -30,15 +30,17 @@ public class GeoLiteAPI { if (data.exists()) { try { lookupService = new LookupService(data); - plugin.getLogger().info("[LICENSE] This product uses data from the GeoLite API created by MaxMind, " + + // TODO ljacqu 20151123: Should this not be output over the ConsoleLogger service? + wrapper.getLogger().info("[LICENSE] This product uses data from the GeoLite API created by MaxMind, " + "available at http://www.maxmind.com"); return true; } catch (IOException e) { + // TODO ljacqu 20151123: Log the exception instead of just swallowing it return false; } } // Ok, let's try to download the data file! - plugin.getGameServer().getScheduler().runTaskAsynchronously(plugin, new Runnable() { + wrapper.getServer().getScheduler().runTaskAsynchronously(wrapper.getAuthMe(), new Runnable() { @Override public void run() { try { diff --git a/src/main/java/fr/xephi/authme/util/Utils.java b/src/main/java/fr/xephi/authme/util/Utils.java index 3262f2f5e..5efc481f1 100644 --- a/src/main/java/fr/xephi/authme/util/Utils.java +++ b/src/main/java/fr/xephi/authme/util/Utils.java @@ -31,7 +31,8 @@ import java.util.zip.GZIPInputStream; */ public final class Utils { - public static AuthMe plugin; + private static AuthMe plugin; + private static Wrapper wrapper; private static boolean getOnlinePlayersIsCollection = false; private static Method getOnlinePlayers; @@ -39,6 +40,7 @@ public final class Utils { static { plugin = AuthMe.getInstance(); + wrapper = new Wrapper(plugin); checkGeoIP(); initializeOnlinePlayersIsCollectionField(); } @@ -65,7 +67,7 @@ public final class Utils { } } } - plugin.getGameServer().getScheduler().runTaskAsynchronously(plugin, new Runnable() { + wrapper.getServer().getScheduler().runTaskAsynchronously(wrapper.getAuthMe(), new Runnable() { @Override public void run() { try { @@ -190,6 +192,7 @@ public final class Utils { } if (!Settings.isForcedRegistrationEnabled) { + // TODO ljacqu 20151123: Use a setter to retrieve things from AuthMe if (!plugin.database.isAuthAvailable(name)) { return true; } @@ -225,12 +228,12 @@ public final class Utils { final World world = theWorld; final Location loc = new Location(world, x, y, z); - Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, new Runnable() { + Bukkit.getScheduler().scheduleSyncDelayedTask(wrapper.getAuthMe(), new Runnable() { @Override public void run() { AuthMeTeleportEvent tpEvent = new AuthMeTeleportEvent(pl, loc); - plugin.getServer().getPluginManager().callEvent(tpEvent); + wrapper.getServer().getPluginManager().callEvent(tpEvent); if (!tpEvent.isCancelled()) { pl.teleport(tpEvent.getTo()); } @@ -325,7 +328,7 @@ public final class Utils { @SuppressWarnings("deprecation") public static Player getPlayer(String name) { name = name.toLowerCase(); - return plugin.getServer().getPlayer(name); + return wrapper.getServer().getPlayer(name); } public static boolean isNPC(final Entity player) { @@ -333,6 +336,7 @@ public final class Utils { if (player.hasMetadata("NPC")) { return true; } else if (plugin.combatTagPlus != null + // TODO ljacqu 20151123: Use a getter for combatTagPlus in AuthMe instead of using direct field access && player instanceof Player && plugin.combatTagPlus.getNpcPlayerHelper().isNpc((Player) player)) { return true; @@ -347,7 +351,7 @@ public final class Utils { if (Settings.isTeleportToSpawnEnabled && !Settings.noTeleport) { Location spawn = plugin.getSpawnLocation(player); AuthMeTeleportEvent tpEvent = new AuthMeTeleportEvent(player, spawn); - plugin.getServer().getPluginManager().callEvent(tpEvent); + wrapper.getServer().getPluginManager().callEvent(tpEvent); if (!tpEvent.isCancelled()) { player.teleport(tpEvent.getTo()); } diff --git a/src/main/java/fr/xephi/authme/util/Wrapper.java b/src/main/java/fr/xephi/authme/util/Wrapper.java new file mode 100644 index 000000000..29f806e58 --- /dev/null +++ b/src/main/java/fr/xephi/authme/util/Wrapper.java @@ -0,0 +1,34 @@ +package fr.xephi.authme.util; + +import fr.xephi.authme.AuthMe; +import org.bukkit.Server; + +import java.util.logging.Logger; + +/** + * Wrapper for the retrieval of common singletons used throughout the application. + * This class simply delegates the calls. + */ +public class Wrapper { + + private AuthMe authMe; + + public Wrapper(AuthMe authMe) { + this.authMe = authMe; + } + + public AuthMe getAuthMe() { + return authMe; + } + + public Server getServer() { + return authMe.getServer(); + } + + public Logger getLogger() { + return authMe.getLogger(); + } + + + +} diff --git a/src/test/java/fr/xephi/authme/AuthMeMockUtil.java b/src/test/java/fr/xephi/authme/AuthMeMockUtil.java index 905c46303..821157f36 100644 --- a/src/test/java/fr/xephi/authme/AuthMeMockUtil.java +++ b/src/test/java/fr/xephi/authme/AuthMeMockUtil.java @@ -1,6 +1,7 @@ package fr.xephi.authme; import fr.xephi.authme.settings.Messages; +import fr.xephi.authme.util.Wrapper; import org.mockito.Mockito; import java.lang.reflect.Field; @@ -15,15 +16,18 @@ public final class AuthMeMockUtil { } /** - * Sets the AuthMe plugin instance to a mock object. Use {@link AuthMe#getInstance()} to retrieve the mock. + * Set the AuthMe plugin instance to a mock object. Use {@link AuthMe#getInstance()} to retrieve the mock. + * + * @return The generated mock for the AuthMe instance */ - public static void mockAuthMeInstance() { + public static AuthMe mockAuthMeInstance() { AuthMe mock = Mockito.mock(AuthMe.class); mockSingletonForClass(AuthMe.class, "plugin", mock); + return mock; } /** - * Creates a mock Messages object for the instance returned from {@link Messages#getInstance()}. + * Create a mock Messages object for the instance returned from {@link Messages#getInstance()}. */ public static void mockMessagesInstance() { Messages mock = Mockito.mock(Messages.class); @@ -31,11 +35,39 @@ public final class AuthMeMockUtil { } /** - * Sets a field of a class to the given mock. + * Set the given class' {@link Wrapper} field to a mock implementation. * - * @param clazz the class to modify - * @param fieldName the field name - * @param mock the mock to set for the given field + * @param clazz The class to modify + * @param fieldName The name of the field containing the Wrapper in the class + * + * @return The generated Wrapper mock + * @see WrapperMock + */ + public static Wrapper insertMockWrapperInstance(Class clazz, String fieldName) { + Wrapper wrapperMock = new WrapperMock(); + mockSingletonForClass(clazz, fieldName, wrapperMock); + return wrapperMock; + } + + public static Wrapper insertMockWrapperInstance(Class clazz, String fieldName, AuthMe authMe) { + Wrapper wrapperMock = new WrapperMock(authMe); + mockSingletonForClass(clazz, fieldName, wrapperMock); + return wrapperMock; + } + + // TODO ljacqu 20151123: Find the use cases for the WrapperMock and remove any of these + // methods that will end up unused + public static Wrapper insertMockWrapperInstance(Class clazz, String fieldName, WrapperMock wrapperMock) { + mockSingletonForClass(clazz, fieldName, wrapperMock); + return wrapperMock; + } + + /** + * Set a field of a class to the given mock. + * + * @param clazz The class to modify + * @param fieldName The field name + * @param mock The mock to set for the given field */ private static void mockSingletonForClass(Class clazz, String fieldName, Object mock) { try { diff --git a/src/test/java/fr/xephi/authme/WrapperMock.java b/src/test/java/fr/xephi/authme/WrapperMock.java new file mode 100644 index 000000000..bfad67be5 --- /dev/null +++ b/src/test/java/fr/xephi/authme/WrapperMock.java @@ -0,0 +1,54 @@ +package fr.xephi.authme; + +import fr.xephi.authme.util.Wrapper; +import org.bukkit.Server; +import org.mockito.Mockito; + +import java.util.HashMap; +import java.util.Map; +import java.util.logging.Logger; + +/** + * Class returning mocks for all calls in {@link Wrapper}. + * This class keeps track of its mocks and will always return + * the same one for each type. + */ +public class WrapperMock extends Wrapper { + + private static Map, Object> mocks = new HashMap<>(); + + public WrapperMock() { + this((AuthMe) getMock(AuthMe.class)); + } + + public WrapperMock(AuthMe authMe) { + super(authMe); + } + + @Override + public Logger getLogger() { + return getMock(Logger.class); + } + + @Override + public Server getServer() { + return getMock(Server.class); + } + + @Override + public AuthMe getAuthMe() { + return getMock(AuthMe.class); + } + + @SuppressWarnings("unchecked") + private static T getMock(Class clazz) { + Object o = mocks.get(clazz); + if (o == null) { + o = Mockito.mock(clazz); + mocks.put(clazz, o); + } + return (T) o; + } + + +} diff --git a/src/test/java/fr/xephi/authme/settings/MessagesTest.java b/src/test/java/fr/xephi/authme/settings/MessagesTest.java new file mode 100644 index 000000000..3d4f138ec --- /dev/null +++ b/src/test/java/fr/xephi/authme/settings/MessagesTest.java @@ -0,0 +1,28 @@ +package fr.xephi.authme.settings; + +import fr.xephi.authme.AuthMe; +import fr.xephi.authme.AuthMeMockUtil; +import fr.xephi.authme.ConsoleLogger; +import org.junit.Before; +import org.junit.Test; + +import java.io.File; + +/** + * Test for {@link Messages}. + */ +public class MessagesTest { + + @Before + public void setUpMessages() { + AuthMe authMe = AuthMeMockUtil.mockAuthMeInstance(); + AuthMeMockUtil.insertMockWrapperInstance(ConsoleLogger.class, "wrapper", authMe); + File file = new File("messages_test.yml"); + Messages messages = new Messages(file, "en"); + } + + @Test + public void shouldLoadMessages() { + + } +} diff --git a/src/test/java/fr/xephi/authme/util/UtilsTest.java b/src/test/java/fr/xephi/authme/util/UtilsTest.java index d095746b3..97b64184c 100644 --- a/src/test/java/fr/xephi/authme/util/UtilsTest.java +++ b/src/test/java/fr/xephi/authme/util/UtilsTest.java @@ -2,6 +2,7 @@ package fr.xephi.authme.util; import fr.xephi.authme.AuthMe; import fr.xephi.authme.AuthMeMockUtil; +import fr.xephi.authme.WrapperMock; import fr.xephi.authme.permission.PermissionsManager; import org.bukkit.Server; import org.bukkit.entity.Player; @@ -9,6 +10,7 @@ import org.bukkit.plugin.Plugin; import org.bukkit.scheduler.BukkitScheduler; import org.bukkit.scheduler.BukkitTask; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; import java.lang.reflect.Field; @@ -23,26 +25,32 @@ import static org.mockito.Mockito.when; /** * Test for the {@link Utils} class. */ +@Ignore +// TODO ljacqu 20151123: Fix test setup public class UtilsTest { private AuthMe authMeMock; private PermissionsManager permissionsManagerMock; + private Wrapper wrapperMock; @Before public void setUpMocks() { - AuthMeMockUtil.mockAuthMeInstance(); - authMeMock = AuthMe.getInstance(); + authMeMock = AuthMeMockUtil.mockAuthMeInstance(); - permissionsManagerMock = mock(PermissionsManager.class); - when(authMeMock.getPermissionsManager()).thenReturn(permissionsManagerMock); - - Server serverMock = mock(Server.class); - when(authMeMock.getGameServer()).thenReturn(serverMock); + // We need to create the Wrapper mock before injecting it into Utils because it runs a lot of code in + // a static block which needs the proper mocks to be set up. + wrapperMock = new WrapperMock(authMeMock); + Server serverMock = wrapperMock.getServer(); BukkitScheduler schedulerMock = mock(BukkitScheduler.class); when(serverMock.getScheduler()).thenReturn(schedulerMock); when(schedulerMock.runTaskAsynchronously(any(Plugin.class), any(Runnable.class))) - .thenReturn(mock(BukkitTask.class)); + .thenReturn(mock(BukkitTask.class)); + + AuthMeMockUtil.insertMockWrapperInstance(Utils.class, "wrapper", (WrapperMock) wrapperMock); + + permissionsManagerMock = mock(PermissionsManager.class); + when(authMeMock.getPermissionsManager()).thenReturn(permissionsManagerMock); } // TODO ljacques 20151122: The tests for Utils.forceGM somehow can't be set up with the mocks correctly diff --git a/src/test/resources/messages_test.yml b/src/test/resources/messages_test.yml new file mode 100644 index 000000000..16218f329 --- /dev/null +++ b/src/test/resources/messages_test.yml @@ -0,0 +1,58 @@ +unknown_user: '&cCan''t find the requested user in the database!' +unsafe_spawn: '&cYour quit location was unsafe, you have been teleported to the world''s spawnpoint.' +not_logged_in: '&cYou''re not logged in!' +reg_voluntarily: 'You can register yourself to the server with the command "/register "' +usage_log: '&cUsage: /login ' +wrong_pwd: '&cWrong password!' +unregistered: '&cSuccessfully unregistered!' +reg_disabled: '&cIn-game registration is disabled!' +valid_session: '&2Logged-in due to Session Reconnection.' +login: '&2Successful login!' +vb_nonActiv: '&cYour account isn''t activated yet, please check your emails!' +user_regged: '&cYou already have registered this username!' +usage_reg: '&cUsage: /register ' +max_reg: '&cYou have exceeded the maximum number of registrations for your connection!' +no_perm: '&4You don''t have the permission to perform this action!' +error: '&4An unexpected error occurred, please contact an Administrator!' +login_msg: '&cPlease, login with the command "/login "' +reg_msg: '&3Please, register to the server with the command "/register "' +reg_email_msg: '&3Please, register to the server with the command "/register "' +usage_unreg: '&cUsage: /unregister ' +pwd_changed: '&2Password changed successfully!' +user_unknown: '&cThis user isn''t registered!' +password_error: '&cPasswords didn''t match, check them again!' +password_error_nick: '&cYou can''t use your name as password, please choose another one...' +password_error_unsafe: '&cThe chosen password isn''t safe, please choose another one...' +invalid_session: '&cYour IP has been changed and your session data has expired!' +reg_only: '&4Only registered users can join the server! Please visit http://example.com to register yourself!' +logged_in: '&cYou''re already logged in!' +logout: '&2Logged-out successfully!' +same_nick: '&4The same username is already playing on the server!' +registered: '&2Successfully registered!' +pass_len: '&cYour password is too short or too long! Please try with another one!' +reload: '&2Configuration and database have been reloaded correctly!' +timeout: '&4Login timeout exceeded, you have been kicked from the server, please try again!' +usage_changepassword: '&cUsage: /changepassword ' +name_len: '&4Your username is either too short or too long!' +regex: '&4Your username contains illegal characters. Allowed chars: REG_EX' +add_email: '&3Please add your email to your account with the command "/email add "' +recovery_email: '&3Forgot your password? Please use the command "/email recovery "' +usage_captcha: '&3To login you have to solve a captcha code, please use the command "/captcha "' +wrong_captcha: '&cWrong Captcha, please type "/captcha THE_CAPTCHA" into the chat!' +valid_captcha: '&2Captcha code solved correctly!' +kick_forvip: '&3A VIP Player has joined the server when it was full!' +kick_fullserver: '&4The server is full, try again later!' +usage_email_add: '&cUsage: /email add ' +usage_email_change: '&cUsage: /email change ' +usage_email_recovery: '&cUsage: /email recovery ' +new_email_invalid: '&cInvalid New Email, try again!' +old_email_invalid: '&cInvalid Old Email, try again!' +email_invalid: '&cInvalid Email address, try again!' +email_added: '&2Email address successfully added to your account!' +email_confirm: '&cPlease confirm your email address!' +email_changed: '&2Email address changed correctly!' +email_send: '&2Recovery email sent correctly! Check your email inbox!' +email_exists: '&cA recovery email was already sent! You can discart it and send a new one using the command below:' +country_banned: '&4Your country is banned from this server!' +antibot_auto_enabled: '&4[AntiBotService] AntiBot enabled due to the huge number of connections!' +antibot_auto_disabled: '&2[AntiBotService] AntiBot disabled disabled after %m minutes!'