From cd84c140ae082b1bd9e21665d6c3bdc2c70eb430 Mon Sep 17 00:00:00 2001 From: Algent Date: Sun, 30 Aug 2026 21:55:54 +0200 Subject: [PATCH] test: characterize microblock event handler Pin the client-only ABI and dedicated-server side stripping before the Java port. --- .../MicroblockEventHandlerFunctionalTest.java | 31 +++++++ ...blockEventHandlerCharacterizationTest.java | 90 +++++++++++++++++++ 2 files changed, 121 insertions(+) create mode 100644 src/functionalTest/java/codechicken/multipart/test/MicroblockEventHandlerFunctionalTest.java create mode 100644 src/test/java/codechicken/microblock/handler/MicroblockEventHandlerCharacterizationTest.java diff --git a/src/functionalTest/java/codechicken/multipart/test/MicroblockEventHandlerFunctionalTest.java b/src/functionalTest/java/codechicken/multipart/test/MicroblockEventHandlerFunctionalTest.java new file mode 100644 index 0000000..1081755 --- /dev/null +++ b/src/functionalTest/java/codechicken/multipart/test/MicroblockEventHandlerFunctionalTest.java @@ -0,0 +1,31 @@ +package codechicken.multipart.test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.lang.reflect.Field; +import java.util.Map; + +import net.minecraftforge.common.MinecraftForge; + +import org.junit.jupiter.api.Test; + +import codechicken.microblock.handler.MicroblockEventHandler; +import codechicken.microblock.handler.MicroblockEventHandler$; +import cpw.mods.fml.common.eventhandler.EventBus; + +class MicroblockEventHandlerFunctionalTest { + + @Test + void dedicatedServerRegistersTheCompanionAfterStrippingClientHandlers() throws Exception { + assertEquals(0, MicroblockEventHandler.class.getDeclaredMethods().length); + assertEquals(0, MicroblockEventHandler$.class.getDeclaredMethods().length); + assertTrue(listenerOwners(MinecraftForge.EVENT_BUS).containsKey(MicroblockEventHandler$.MODULE$)); + } + + private static Map listenerOwners(EventBus bus) throws Exception { + Field ownersField = EventBus.class.getDeclaredField("listenerOwners"); + ownersField.setAccessible(true); + return (Map) ownersField.get(bus); + } +} diff --git a/src/test/java/codechicken/microblock/handler/MicroblockEventHandlerCharacterizationTest.java b/src/test/java/codechicken/microblock/handler/MicroblockEventHandlerCharacterizationTest.java new file mode 100644 index 0000000..beb8b6b --- /dev/null +++ b/src/test/java/codechicken/microblock/handler/MicroblockEventHandlerCharacterizationTest.java @@ -0,0 +1,90 @@ +package codechicken.microblock.handler; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.Arrays; +import java.util.Set; +import java.util.TreeSet; + +import org.junit.jupiter.api.Test; +import org.objectweb.asm.Type; + +import cpw.mods.fml.common.eventhandler.EventPriority; +import cpw.mods.fml.common.eventhandler.SubscribeEvent; +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; + +class MicroblockEventHandlerCharacterizationTest { + + private static final Set EVENT_METHODS = new TreeSet<>( + Arrays.asList( + "drawBlockHighlight(Lnet/minecraftforge/client/event/DrawBlockHighlightEvent;)V", + "postTextureStitch(Lnet/minecraftforge/client/event/TextureStitchEvent$Post;)V")); + + @Test + void keepsBothSingletonTypesAndEventDescriptors() throws Exception { + assertHandlerType(MicroblockEventHandler.class, true); + assertHandlerType(MicroblockEventHandler$.class, false); + + assertEquals(0, MicroblockEventHandler.class.getFields().length); + Field module = MicroblockEventHandler$.class.getField("MODULE$"); + assertSame(MicroblockEventHandler$.class, module.getType()); + assertTrue(Modifier.isStatic(module.getModifiers())); + assertTrue(Modifier.isFinal(module.getModifiers())); + assertSame(MicroblockEventHandler$.MODULE$, module.get(null)); + } + + @Test + void keepsBothClientOnlyEventAnnotations() { + assertEventAnnotations(MicroblockEventHandler.class); + assertEventAnnotations(MicroblockEventHandler$.class); + } + + private static void assertHandlerType(Class type, boolean staticMethods) { + assertTrue(Modifier.isPublic(type.getModifiers())); + assertTrue(Modifier.isFinal(type.getModifiers())); + assertNull(type.getAnnotation(SideOnly.class)); + assertEquals(EVENT_METHODS, publicMethodSignatures(type)); + + for (Method method : type.getDeclaredMethods()) { + if (Modifier.isPublic(method.getModifiers())) { + assertEquals(staticMethods, Modifier.isStatic(method.getModifiers())); + } + } + } + + private static void assertEventAnnotations(Class type) { + for (Method method : type.getDeclaredMethods()) { + if (!Modifier.isPublic(method.getModifiers())) { + continue; + } + + SubscribeEvent subscribe = method.getAnnotation(SubscribeEvent.class); + assertNotNull(subscribe, method.toString()); + assertEquals(EventPriority.NORMAL, subscribe.priority(), method.toString()); + assertFalse(subscribe.receiveCanceled(), method.toString()); + + SideOnly sideOnly = method.getAnnotation(SideOnly.class); + assertNotNull(sideOnly, method.toString()); + assertEquals(Side.CLIENT, sideOnly.value(), method.toString()); + } + } + + private static Set publicMethodSignatures(Class type) { + Set signatures = new TreeSet<>(); + for (Method method : type.getDeclaredMethods()) { + if (Modifier.isPublic(method.getModifiers())) { + signatures.add(method.getName() + Type.getMethodDescriptor(method)); + } + } + return signatures; + } +}