From d6bba7ff46a9807bf4009aa77c33bcc57cff2038 Mon Sep 17 00:00:00 2001 From: Algent Date: Sat, 15 Aug 2026 20:02:42 +0200 Subject: [PATCH] test: characterize the package objects Each holds one member, a logger alias for its proxy's logger. Everything goes through reflection because 'package' is a Java keyword and cannot appear as a type name in Java source, which is also why these two classes have no Java form at all. The plain-JVM half pins the shape and that the alias reads the proxy's var rather than holding anything, so it reports null before preInit. The Forge server half pins the part that matters for what comes next: after preInit each alias returns the very instance its proxy holds, adding nothing. 123 plain-JVM tests, 23 Forge server tests. --- .../test/ProxyLoggerFunctionalTest.java | 37 +++++++++++++ .../PackageObjectCharacterizationTest.java | 55 +++++++++++++++++++ 2 files changed, 92 insertions(+) create mode 100644 src/functionalTest/java/codechicken/multipart/test/ProxyLoggerFunctionalTest.java create mode 100644 src/test/java/codechicken/multipart/PackageObjectCharacterizationTest.java diff --git a/src/functionalTest/java/codechicken/multipart/test/ProxyLoggerFunctionalTest.java b/src/functionalTest/java/codechicken/multipart/test/ProxyLoggerFunctionalTest.java new file mode 100644 index 0000000..53f54e3 --- /dev/null +++ b/src/functionalTest/java/codechicken/multipart/test/ProxyLoggerFunctionalTest.java @@ -0,0 +1,37 @@ +package codechicken.multipart.test; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertSame; + +import org.junit.jupiter.api.Test; + +import codechicken.microblock.handler.MicroblockProxy; +import codechicken.multipart.handler.MultipartProxy; + +/** + * Both proxies publish a logger during preInit, and code across both packages logs through them. A plain JVM test only + * ever sees the uninitialised null, so this is the one place the real instances are observable. + */ +class ProxyLoggerFunctionalTest { + + @Test + void bothProxiesPublishALoggerDuringPreInit() { + assertNotNull(MultipartProxy.logger(), "MultipartProxy.logger is set in preInit"); + assertNotNull(MicroblockProxy.logger(), "MicroblockProxy.logger is set in preInit"); + } + + /** + * The package objects are pure aliases: each returns the very instance its proxy holds, adding nothing. That is + * what makes them safe to remove in favour of naming the proxy directly. + */ + @Test + void thePackageObjectAliasesAreTheProxyLoggersThemselves() throws Exception { + assertSame(MultipartProxy.logger(), invokeLogger("codechicken.multipart.package")); + assertSame(MicroblockProxy.logger(), invokeLogger("codechicken.microblock.package")); + } + + /** Reflective because {@code package} is a Java keyword and cannot appear as a type name in Java source. */ + private static Object invokeLogger(String name) throws Exception { + return Class.forName(name).getDeclaredMethod("logger").invoke(null); + } +} diff --git a/src/test/java/codechicken/multipart/PackageObjectCharacterizationTest.java b/src/test/java/codechicken/multipart/PackageObjectCharacterizationTest.java new file mode 100644 index 0000000..5c967e6 --- /dev/null +++ b/src/test/java/codechicken/multipart/PackageObjectCharacterizationTest.java @@ -0,0 +1,55 @@ +package codechicken.multipart; + +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.Method; +import java.lang.reflect.Modifier; + +import org.apache.logging.log4j.Logger; +import org.junit.jupiter.api.Test; + +/** + * Both package objects hold one member, a {@code logger} alias for their proxy's logger. + *

+ * Everything here goes through reflection because {@code package} is a Java keyword: + * {@code codechicken.multipart.package} cannot be written as a type name in Java source at all. That is not a style + * choice, it is the reason these two classes have no Java form. + */ +class PackageObjectCharacterizationTest { + + @Test + void bothPackageObjectsExposeAStaticLoggerAccessor() throws Exception { + for (String name : new String[] { "codechicken.multipart.package", "codechicken.microblock.package" }) { + Method logger = Class.forName(name).getDeclaredMethod("logger"); + + assertSame(Logger.class, logger.getReturnType(), name); + assertTrue(Modifier.isPublic(logger.getModifiers()), name); + assertTrue(Modifier.isStatic(logger.getModifiers()), name); + } + } + + @Test + void bothCarryACompanionSingleton() throws Exception { + for (String name : new String[] { "codechicken.multipart.package$", "codechicken.microblock.package$" }) { + Class companion = Class.forName(name); + + assertSame(companion, companion.getField("MODULE$").getType(), name); + } + } + + /** + * The alias reads the proxy's var rather than holding anything, so before preInit has run it reports null instead + * of failing. The Forge server suite asserts the other half, that after preInit it is the very same instance. + */ + @Test + void theAliasReportsWhateverTheProxyHoldsAndIsNullBeforePreInit() throws Exception { + assertNull(invokeLogger("codechicken.multipart.package")); + assertNull(invokeLogger("codechicken.microblock.package")); + } + + private static Object invokeLogger(String name) throws Exception { + return Class.forName(name).getDeclaredMethod("logger").invoke(null); + } +}