From e589401ea74d515b149ed66fa1867d80d6452b8f Mon Sep 17 00:00:00 2001 From: Bukkit/Spigot Date: Mon, 11 Sep 2023 18:20:41 +1000 Subject: [PATCH] #990: Use Mockito instead of InvocationHandler for test mocking By: DerFrZocker --- paper-api/pom.xml | 6 + .../java/org/bukkit/LastChatColorTest.java | 2 +- .../test/java/org/bukkit/LocationTest.java | 5 +- .../src/test/java/org/bukkit/TestServer.java | 146 ------------------ .../src/test/java/org/bukkit/TestWorld.java | 66 -------- .../event/PlayerChatTabCompleteEventTest.java | 4 +- .../org/bukkit/event/SyntheticEventTest.java | 9 +- .../org/bukkit/plugin/PluginManagerTest.java | 7 +- .../messaging/StandardMessengerTest.java | 7 +- .../bukkit/plugin/messaging/TestPlayer.java | 50 ------ .../org/bukkit/scoreboard/CriteriaTest.java | 6 +- .../bukkit/support/AbstractTestingBase.java | 13 ++ .../java/org/bukkit/support/TestServer.java | 72 +++++++++ 13 files changed, 113 insertions(+), 280 deletions(-) delete mode 100644 paper-api/src/test/java/org/bukkit/TestServer.java delete mode 100644 paper-api/src/test/java/org/bukkit/TestWorld.java delete mode 100644 paper-api/src/test/java/org/bukkit/plugin/messaging/TestPlayer.java create mode 100644 paper-api/src/test/java/org/bukkit/support/AbstractTestingBase.java create mode 100644 paper-api/src/test/java/org/bukkit/support/TestServer.java diff --git a/paper-api/pom.xml b/paper-api/pom.xml index 35913cd02c..53905a9204 100644 --- a/paper-api/pom.xml +++ b/paper-api/pom.xml @@ -97,6 +97,12 @@ 1.3 test + + org.mockito + mockito-core + 5.5.0 + test + org.ow2.asm asm-tree diff --git a/paper-api/src/test/java/org/bukkit/LastChatColorTest.java b/paper-api/src/test/java/org/bukkit/LastChatColorTest.java index 0a4fe1cced..64e164ea79 100644 --- a/paper-api/src/test/java/org/bukkit/LastChatColorTest.java +++ b/paper-api/src/test/java/org/bukkit/LastChatColorTest.java @@ -1,6 +1,6 @@ package org.bukkit; -import static org.junit.Assert.assertEquals; +import static org.junit.Assert.*; import java.util.Arrays; import java.util.Collection; import org.junit.Test; diff --git a/paper-api/src/test/java/org/bukkit/LocationTest.java b/paper-api/src/test/java/org/bukkit/LocationTest.java index 887305c05c..02b97fa743 100644 --- a/paper-api/src/test/java/org/bukkit/LocationTest.java +++ b/paper-api/src/test/java/org/bukkit/LocationTest.java @@ -2,6 +2,7 @@ package org.bukkit; import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; +import static org.mockito.Mockito.*; import com.google.common.collect.ImmutableList; import java.util.List; import java.util.Random; @@ -189,8 +190,10 @@ public class LocationTest { return new Vector(x, y, z); } + private static final World TEST_WORLD = mock(); + private static Location getEmptyLocation() { - return new Location(TestWorld.INSTANCE, 0, 0, 0); + return new Location(TEST_WORLD, 0, 0, 0); } private Location getLocation() { diff --git a/paper-api/src/test/java/org/bukkit/TestServer.java b/paper-api/src/test/java/org/bukkit/TestServer.java deleted file mode 100644 index d20c29fc6f..0000000000 --- a/paper-api/src/test/java/org/bukkit/TestServer.java +++ /dev/null @@ -1,146 +0,0 @@ -package org.bukkit; - -import com.google.common.collect.ImmutableMap; -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.Method; -import java.lang.reflect.Proxy; -import java.util.Iterator; -import java.util.Map; -import java.util.logging.Logger; -import java.util.stream.Stream; -import org.bukkit.command.SimpleCommandMap; -import org.bukkit.plugin.PluginManager; -import org.bukkit.plugin.SimplePluginManager; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -public final class TestServer implements InvocationHandler { - private static interface MethodHandler { - Object handle(TestServer server, Object[] args); - } - - private static final Map methods; - - static { - try { - ImmutableMap.Builder methodMap = ImmutableMap.builder(); - methodMap.put( - Server.class.getMethod("isPrimaryThread"), - new MethodHandler() { - @Override - public Object handle(TestServer server, Object[] args) { - return Thread.currentThread().equals(server.creatingThread); - } - } - ); - methodMap.put( - Server.class.getMethod("getPluginManager"), - new MethodHandler() { - @Override - public Object handle(TestServer server, Object[] args) { - return server.pluginManager; - } - } - ); - methodMap.put( - Server.class.getMethod("getLogger"), - new MethodHandler() { - final Logger logger = Logger.getLogger(TestServer.class.getCanonicalName()); - @Override - public Object handle(TestServer server, Object[] args) { - return logger; - } - } - ); - methodMap.put( - Server.class.getMethod("getName"), - new MethodHandler() { - @Override - public Object handle(TestServer server, Object[] args) { - return TestServer.class.getSimpleName(); - } - } - ); - methodMap.put( - Server.class.getMethod("getVersion"), - new MethodHandler() { - @Override - public Object handle(TestServer server, Object[] args) { - return "Version_" + TestServer.class.getPackage().getImplementationVersion(); - } - } - ); - methodMap.put( - Server.class.getMethod("getBukkitVersion"), - new MethodHandler() { - @Override - public Object handle(TestServer server, Object[] args) { - return "BukkitVersion_" + TestServer.class.getPackage().getImplementationVersion(); - } - } - ); - methodMap.put( - Server.class.getMethod("getRegistry", Class.class), - new MethodHandler() { - @Override - public Object handle(TestServer server, Object[] args) { - return new Registry() { - @NotNull - @Override - public Iterator iterator() { - throw new UnsupportedOperationException(); - } - - @Nullable - @Override - public Keyed get(@NotNull NamespacedKey key) { - return null; - } - - @NotNull - @Override - public Stream stream() { - throw new UnsupportedOperationException(); - } - }; - } - } - ); - methodMap.put( - Server.class.getMethod("getScoreboardCriteria", String.class), - new MethodHandler() { - @Override - public Object handle(TestServer server, Object[] args) { - // Does not need to return anything. Exists solely to test CriteriaTest which has static init fields - return null; - } - } - ); - methods = methodMap.build(); - - TestServer server = new TestServer(); - Server instance = Proxy.getProxyClass(Server.class.getClassLoader(), Server.class).asSubclass(Server.class).getConstructor(InvocationHandler.class).newInstance(server); - Bukkit.setServer(instance); - server.pluginManager = new SimplePluginManager(instance, new SimpleCommandMap(instance)); - } catch (Throwable t) { - throw new Error(t); - } - } - - private Thread creatingThread = Thread.currentThread(); - private PluginManager pluginManager; - private TestServer() {}; - - public static Server getInstance() { - return Bukkit.getServer(); - } - - @Override - public Object invoke(Object proxy, Method method, Object[] args) { - MethodHandler handler = methods.get(method); - if (handler != null) { - return handler.handle(this, args); - } - throw new UnsupportedOperationException(String.valueOf(method)); - } -} diff --git a/paper-api/src/test/java/org/bukkit/TestWorld.java b/paper-api/src/test/java/org/bukkit/TestWorld.java deleted file mode 100644 index ab34f11999..0000000000 --- a/paper-api/src/test/java/org/bukkit/TestWorld.java +++ /dev/null @@ -1,66 +0,0 @@ -package org.bukkit; - -import com.google.common.collect.ImmutableMap; -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.Method; -import java.lang.reflect.Proxy; -import java.util.Map; - -public final class TestWorld implements InvocationHandler { - - private static interface MethodHandler { - - Object handle(TestWorld server, Object[] args); - } - - private static final Map methods; - public static final World INSTANCE; - - static { - try { - TestServer.getInstance(); - - ImmutableMap.Builder methodMap = ImmutableMap.builder(); - methodMap.put( - Object.class.getMethod("equals", Object.class), - new MethodHandler() { - @Override - public Object handle(TestWorld server, Object[] args) { - return this == args[0]; - } - } - ); - methodMap.put( - Object.class.getMethod("hashCode"), - new MethodHandler() { - @Override - public Object handle(TestWorld server, Object[] args) { - return this.hashCode(); - } - } - ); - methods = methodMap.build(); - - TestWorld world = new TestWorld(); - INSTANCE = Proxy.getProxyClass(World.class.getClassLoader(), World.class).asSubclass(World.class).getConstructor(InvocationHandler.class).newInstance(world); - } catch (Throwable t) { - throw new Error(t); - } - } - - private TestWorld() { - } - - public static Server getInstance() { - return Bukkit.getServer(); - } - - @Override - public Object invoke(Object proxy, Method method, Object[] args) { - MethodHandler handler = methods.get(method); - if (handler != null) { - return handler.handle(this, args); - } - throw new UnsupportedOperationException(String.valueOf(method)); - } -} diff --git a/paper-api/src/test/java/org/bukkit/event/PlayerChatTabCompleteEventTest.java b/paper-api/src/test/java/org/bukkit/event/PlayerChatTabCompleteEventTest.java index dd5fb243b7..80c0bfbd2a 100644 --- a/paper-api/src/test/java/org/bukkit/event/PlayerChatTabCompleteEventTest.java +++ b/paper-api/src/test/java/org/bukkit/event/PlayerChatTabCompleteEventTest.java @@ -2,9 +2,9 @@ package org.bukkit.event; import static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.*; +import static org.mockito.Mockito.*; import com.google.common.collect.ImmutableList; import org.bukkit.event.player.PlayerChatTabCompleteEvent; -import org.bukkit.plugin.messaging.TestPlayer; import org.junit.Test; public class PlayerChatTabCompleteEventTest { @@ -21,6 +21,6 @@ public class PlayerChatTabCompleteEventTest { } private String getToken(String message) { - return new PlayerChatTabCompleteEvent(TestPlayer.getInstance(), message, ImmutableList.of()).getLastToken(); + return new PlayerChatTabCompleteEvent(mock(), message, ImmutableList.of()).getLastToken(); } } diff --git a/paper-api/src/test/java/org/bukkit/event/SyntheticEventTest.java b/paper-api/src/test/java/org/bukkit/event/SyntheticEventTest.java index d402cb59f5..ab53cf1438 100644 --- a/paper-api/src/test/java/org/bukkit/event/SyntheticEventTest.java +++ b/paper-api/src/test/java/org/bukkit/event/SyntheticEventTest.java @@ -1,25 +1,26 @@ package org.bukkit.event; -import org.bukkit.TestServer; +import org.bukkit.Bukkit; import org.bukkit.plugin.PluginLoader; import org.bukkit.plugin.SimplePluginManager; import org.bukkit.plugin.TestPlugin; import org.bukkit.plugin.java.JavaPluginLoader; +import org.bukkit.support.AbstractTestingBase; import org.junit.Assert; import org.junit.Test; -public class SyntheticEventTest { +public class SyntheticEventTest extends AbstractTestingBase { @SuppressWarnings("deprecation") @Test public void test() { - final JavaPluginLoader loader = new JavaPluginLoader(TestServer.getInstance()); + final JavaPluginLoader loader = new JavaPluginLoader(Bukkit.getServer()); TestPlugin plugin = new TestPlugin(getClass().getName()) { @Override public PluginLoader getPluginLoader() { return loader; } }; - SimplePluginManager pluginManager = new SimplePluginManager(TestServer.getInstance(), null); + SimplePluginManager pluginManager = new SimplePluginManager(Bukkit.getServer(), null); TestEvent event = new TestEvent(false); Impl impl = new Impl(); diff --git a/paper-api/src/test/java/org/bukkit/plugin/PluginManagerTest.java b/paper-api/src/test/java/org/bukkit/plugin/PluginManagerTest.java index f188cd4f3b..4c61b180c2 100644 --- a/paper-api/src/test/java/org/bukkit/plugin/PluginManagerTest.java +++ b/paper-api/src/test/java/org/bukkit/plugin/PluginManagerTest.java @@ -2,19 +2,20 @@ package org.bukkit.plugin; import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; -import org.bukkit.TestServer; +import org.bukkit.Bukkit; import org.bukkit.event.Event; import org.bukkit.event.TestEvent; import org.bukkit.permissions.Permission; +import org.bukkit.support.AbstractTestingBase; import org.junit.After; import org.junit.Test; -public class PluginManagerTest { +public class PluginManagerTest extends AbstractTestingBase { private class MutableObject { volatile Object value = null; } - private static final PluginManager pm = TestServer.getInstance().getPluginManager(); + private static final PluginManager pm = Bukkit.getServer().getPluginManager(); private final MutableObject store = new MutableObject(); diff --git a/paper-api/src/test/java/org/bukkit/plugin/messaging/StandardMessengerTest.java b/paper-api/src/test/java/org/bukkit/plugin/messaging/StandardMessengerTest.java index dce3d619a6..bf00b7a9d8 100644 --- a/paper-api/src/test/java/org/bukkit/plugin/messaging/StandardMessengerTest.java +++ b/paper-api/src/test/java/org/bukkit/plugin/messaging/StandardMessengerTest.java @@ -2,6 +2,7 @@ package org.bukkit.plugin.messaging; import static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.*; +import static org.mockito.Mockito.*; import java.util.Collection; import org.bukkit.entity.Player; import org.bukkit.plugin.TestPlugin; @@ -73,7 +74,7 @@ public class StandardMessengerTest { Messenger messenger = getMessenger(); TestPlugin plugin = getPlugin(); TestMessageListener listener = new TestMessageListener("test:foo", "test:bar".getBytes()); - Player player = TestPlayer.getInstance(); + Player player = mock(); PluginMessageListenerRegistration registration = messenger.registerIncomingPluginChannel(plugin, "test:foo", listener); assertTrue(registration.isValid()); @@ -114,7 +115,7 @@ public class StandardMessengerTest { TestPlugin plugin = getPlugin(); TestMessageListener listener1 = new TestMessageListener("test:foo", "test:bar".getBytes()); TestMessageListener listener2 = new TestMessageListener("test:baz", "test:qux".getBytes()); - Player player = TestPlayer.getInstance(); + Player player = mock(); PluginMessageListenerRegistration registration1 = messenger.registerIncomingPluginChannel(plugin, "test:foo", listener1); PluginMessageListenerRegistration registration2 = messenger.registerIncomingPluginChannel(plugin, "test:baz", listener2); @@ -143,7 +144,7 @@ public class StandardMessengerTest { TestPlugin plugin = getPlugin(); TestMessageListener listener1 = new TestMessageListener("test:foo", "test:bar".getBytes()); TestMessageListener listener2 = new TestMessageListener("test:baz", "test:qux".getBytes()); - Player player = TestPlayer.getInstance(); + Player player = mock(); PluginMessageListenerRegistration registration1 = messenger.registerIncomingPluginChannel(plugin, "test:foo", listener1); PluginMessageListenerRegistration registration2 = messenger.registerIncomingPluginChannel(plugin, "test:baz", listener2); diff --git a/paper-api/src/test/java/org/bukkit/plugin/messaging/TestPlayer.java b/paper-api/src/test/java/org/bukkit/plugin/messaging/TestPlayer.java deleted file mode 100644 index 1b7642dc1c..0000000000 --- a/paper-api/src/test/java/org/bukkit/plugin/messaging/TestPlayer.java +++ /dev/null @@ -1,50 +0,0 @@ -package org.bukkit.plugin.messaging; - -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.Method; -import java.lang.reflect.Proxy; -import java.util.HashMap; -import org.bukkit.entity.Player; - - -public final class TestPlayer implements InvocationHandler { - private static interface MethodHandler { - Object handle(TestPlayer server, Object[] args); - } - private static final Constructor constructor; - private static final HashMap methods = new HashMap(); - static { - try { - /* - methods.put(Player.class.getMethod("methodName"), - new MethodHandler() { - public Object handle(TestPlayer server, Object[] args) { - } - }); - */ - constructor = Proxy.getProxyClass(Player.class.getClassLoader(), Player.class).asSubclass(Player.class).getConstructor(InvocationHandler.class); - } catch (Throwable t) { - throw new Error(t); - } - } - - private TestPlayer() {}; - - public static Player getInstance() { - try { - return constructor.newInstance(new TestPlayer()); - } catch (Throwable t) { - throw new RuntimeException(t); - } - } - - @Override - public Object invoke(Object proxy, Method method, Object[] args) { - MethodHandler handler = methods.get(method); - if (handler != null) { - return handler.handle(this, args); - } - throw new UnsupportedOperationException(String.valueOf(method)); - } -} diff --git a/paper-api/src/test/java/org/bukkit/scoreboard/CriteriaTest.java b/paper-api/src/test/java/org/bukkit/scoreboard/CriteriaTest.java index eb94b6f4d5..025ec8d637 100644 --- a/paper-api/src/test/java/org/bukkit/scoreboard/CriteriaTest.java +++ b/paper-api/src/test/java/org/bukkit/scoreboard/CriteriaTest.java @@ -2,17 +2,15 @@ package org.bukkit.scoreboard; import org.bukkit.Material; import org.bukkit.Statistic; -import org.bukkit.TestServer; import org.bukkit.entity.EntityType; +import org.bukkit.support.AbstractTestingBase; import org.junit.Assert; import org.junit.Test; -public class CriteriaTest { +public class CriteriaTest extends AbstractTestingBase { @Test public void testStatistic() { - TestServer.getInstance(); - Assert.assertThrows(IllegalArgumentException.class, () -> Criteria.statistic(Statistic.AVIATE_ONE_CM, Material.STONE)); // Generic statistic with block Assert.assertThrows(IllegalArgumentException.class, () -> Criteria.statistic(Statistic.AVIATE_ONE_CM, EntityType.CREEPER)); // Generic statistic with entity type diff --git a/paper-api/src/test/java/org/bukkit/support/AbstractTestingBase.java b/paper-api/src/test/java/org/bukkit/support/AbstractTestingBase.java new file mode 100644 index 0000000000..759e1ced83 --- /dev/null +++ b/paper-api/src/test/java/org/bukkit/support/AbstractTestingBase.java @@ -0,0 +1,13 @@ +package org.bukkit.support; + +/** + * If you are getting: java.lang.ExceptionInInitializerError + * + * extend this class to solve it. + */ +public abstract class AbstractTestingBase { + + static { + TestServer.setup(); + } +} diff --git a/paper-api/src/test/java/org/bukkit/support/TestServer.java b/paper-api/src/test/java/org/bukkit/support/TestServer.java new file mode 100644 index 0000000000..79173d6ed8 --- /dev/null +++ b/paper-api/src/test/java/org/bukkit/support/TestServer.java @@ -0,0 +1,72 @@ +package org.bukkit.support; + +import static org.mockito.Mockito.*; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.logging.Logger; +import java.util.stream.Stream; +import org.bukkit.Bukkit; +import org.bukkit.Keyed; +import org.bukkit.NamespacedKey; +import org.bukkit.Registry; +import org.bukkit.Server; +import org.bukkit.UnsafeValues; +import org.bukkit.command.SimpleCommandMap; +import org.bukkit.plugin.PluginManager; +import org.bukkit.plugin.SimplePluginManager; +import org.jetbrains.annotations.NotNull; + +public final class TestServer { + + static { + Server instance = mock(withSettings().stubOnly()); + + Thread creatingThread = Thread.currentThread(); + when(instance.isPrimaryThread()).then(mock -> Thread.currentThread().equals(creatingThread)); + + PluginManager pluginManager = new SimplePluginManager(instance, new SimpleCommandMap(instance)); + when(instance.getPluginManager()).thenReturn(pluginManager); + + Logger logger = Logger.getLogger(TestServer.class.getCanonicalName()); + when(instance.getLogger()).thenReturn(logger); + + when(instance.getName()).thenReturn(TestServer.class.getSimpleName()); + + when(instance.getVersion()).thenReturn("Version_" + TestServer.class.getPackage().getImplementationVersion()); + + when(instance.getBukkitVersion()).thenReturn("BukkitVersion_" + TestServer.class.getPackage().getImplementationVersion()); + + Map, Registry> registers = new HashMap<>(); + when(instance.getRegistry(any())).then(invocationOnMock -> registers.computeIfAbsent(invocationOnMock.getArgument(0), aClass -> new Registry() { + private final Map cache = new HashMap<>(); + + @Override + public Keyed get(NamespacedKey key) { + return cache.computeIfAbsent(key, key2 -> mock(aClass, withSettings().stubOnly())); + } + + @NotNull + @Override + public Stream stream() { + throw new UnsupportedOperationException("Not supported"); + } + + @Override + public Iterator iterator() { + throw new UnsupportedOperationException("Not supported"); + } + })); + + UnsafeValues unsafeValues = mock(withSettings().stubOnly()); + when(instance.getUnsafe()).thenReturn(unsafeValues); + + Bukkit.setServer(instance); + } + + private TestServer() { + } + + public static void setup() { + } +}