diff --git a/build.gradle.kts b/build.gradle.kts index d6bb01459..cf1373896 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -84,20 +84,21 @@ version = finalRevision val javaVersion = "25" val junitVersion = "5.10.2" val mockitoVersion = "5.11.0" -// MockBukkit's modern, per-Minecraft-version artifacts are published to Paper's Maven repo -// under org.mockbukkit.mockbukkit (see the testImplementation coordinate below). The closest -// available to Paper 26.2 is the 26.1.2 line (no 26.2 build exists yet); 4.113.2 is the latest. +// TODO(blocked): switch the testImplementation coordinate below to mockbukkit-v26.2 once it is +// published. Until then the test suite cannot run against the 26.2 API — MockBukkit's registry +// mock throws on 26.2's new `minecraft:sulfur_cube_archetype` registry. The code still compiles +// against mockbukkit-v26.1.2; only the test *run* is blocked. This is the sole remaining change. val mockBukkitVersion = "4.113.2" val mongodbVersion = "3.12.12" val mariadbVersion = "3.0.5" val mysqlVersion = "8.0.27" val postgresqlVersion = "42.2.18" val hikaricpVersion = "5.0.1" -// Compile against the latest stable 26.1.2 dev bundle. This is the newest Paper API that has a -// matching MockBukkit release (mockbukkit-v26.1.2); MockBukkit does not yet support 26.2's new -// registries. Minecraft 26.2 is still fully supported at runtime (see ServerCompatibility and -// the Modrinth game-versions list); 26.2-only blocks/entities are accessed via Enums.getIfPresent. -val paperVersion = "26.1.2.build.72-stable" +// Compile against the literal 26.2 dev bundle so EntityType.SULFUR_CUBE and the new 26.2 +// materials are available at compile time. Pairs with mockbukkit-v26.2 (see above) once that +// MockBukkit build exists; until then the test suite is red (registry mismatch), which is why +// this PR is a draft. +val paperVersion = "26.2.build.25-alpha" val bstatsVersion = "3.0.0" val vaultVersion = "1.7.1" val levelVersion = "2.21.3" diff --git a/src/main/java/world/bentobox/bentobox/listeners/flags/protection/BucketListener.java b/src/main/java/world/bentobox/bentobox/listeners/flags/protection/BucketListener.java index d2c954d1f..01ba3a172 100644 --- a/src/main/java/world/bentobox/bentobox/listeners/flags/protection/BucketListener.java +++ b/src/main/java/world/bentobox/bentobox/listeners/flags/protection/BucketListener.java @@ -29,11 +29,9 @@ public class BucketListener extends FlagListener { /** * The Sulfur Cube entity type (Minecraft 26.2), resolved at runtime so the code still - * compiles against earlier API versions. {@code null} when absent. Non-final so tests can - * inject a stand-in type (the JVM constant-folds {@code static final} fields). + * compiles against earlier API versions. {@code null} when absent. */ - @SuppressWarnings("java:S3008") // non-final by design; see Javadoc (test injection) - private static EntityType SULFUR_CUBE = Enums.getIfPresent(EntityType.class, "SULFUR_CUBE") + private static final EntityType SULFUR_CUBE = Enums.getIfPresent(EntityType.class, "SULFUR_CUBE") .orNull(); /** diff --git a/src/main/java/world/bentobox/bentobox/listeners/flags/protection/EntityInteractListener.java b/src/main/java/world/bentobox/bentobox/listeners/flags/protection/EntityInteractListener.java index 55d104c92..8f28c7991 100644 --- a/src/main/java/world/bentobox/bentobox/listeners/flags/protection/EntityInteractListener.java +++ b/src/main/java/world/bentobox/bentobox/listeners/flags/protection/EntityInteractListener.java @@ -37,11 +37,9 @@ public class EntityInteractListener extends FlagListener { /** * The Sulfur Cube entity type (Minecraft 26.2), resolved at runtime so the code still - * compiles against earlier API versions. {@code null} when absent. Non-final so tests can - * inject a stand-in type (the JVM constant-folds {@code static final} fields). + * compiles against earlier API versions. {@code null} when absent. */ - @SuppressWarnings("java:S3008") // non-final by design; see Javadoc (test injection) - private static EntityType SULFUR_CUBE = Enums.getIfPresent(EntityType.class, "SULFUR_CUBE") + private static final EntityType SULFUR_CUBE = Enums.getIfPresent(EntityType.class, "SULFUR_CUBE") .orNull(); @EventHandler(priority = EventPriority.LOW, ignoreCancelled=true) diff --git a/src/main/java/world/bentobox/bentobox/util/Util.java b/src/main/java/world/bentobox/bentobox/util/Util.java index 385861f4c..62f00d909 100644 --- a/src/main/java/world/bentobox/bentobox/util/Util.java +++ b/src/main/java/world/bentobox/bentobox/util/Util.java @@ -90,12 +90,8 @@ public class Util { * The Sulfur Cube entity type (Minecraft 26.2), resolved at runtime so the code still * compiles against earlier API versions where the constant does not exist. {@code null} * when absent, in which case the {@code ==} comparisons against it are simply false. - * Intentionally non-final: assigned once at class load, but left non-final so tests can - * inject a stand-in type (the JVM constant-folds {@code static final} fields, defeating - * reflective injection). */ - @SuppressWarnings("java:S3008") // non-final by design; see Javadoc (test injection) - private static EntityType SULFUR_CUBE = Enums.getIfPresent(EntityType.class, "SULFUR_CUBE") + private static final EntityType SULFUR_CUBE = Enums.getIfPresent(EntityType.class, "SULFUR_CUBE") .orNull(); /** diff --git a/src/test/java/world/bentobox/bentobox/listeners/flags/protection/BucketListenerTest.java b/src/test/java/world/bentobox/bentobox/listeners/flags/protection/BucketListenerTest.java index 6a04f7fcb..8535cf808 100644 --- a/src/test/java/world/bentobox/bentobox/listeners/flags/protection/BucketListenerTest.java +++ b/src/test/java/world/bentobox/bentobox/listeners/flags/protection/BucketListenerTest.java @@ -252,68 +252,40 @@ void testOnTropicalFishScoopingFishWaterBucketNotAllowed() { /** * A Sulfur Cube (Minecraft 26.2) is picked up with an empty bucket; this is blocked by the - * BUCKET flag when not allowed. The 26.2 EntityType constant is absent in the test API, so - * MAGMA_CUBE is injected as a stand-in for SULFUR_CUBE. + * BUCKET flag when not allowed. */ @Test - void testOnSulfurCubeBucketingNotAllowed() throws Exception { + void testOnSulfurCubeBucketingNotAllowed() { when(island.isAllowed(any(), any())).thenReturn(false); - EntityType standIn = EntityType.MAGMA_CUBE; - Object previous = getStaticField(BucketListener.class, "SULFUR_CUBE"); - setStaticField(BucketListener.class, "SULFUR_CUBE", standIn); - try { - Entity cube = mock(Entity.class); - when(cube.getLocation()).thenReturn(location); - when(cube.getType()).thenReturn(standIn); - PlayerInteractEntityEvent e = new PlayerInteractEntityEvent(mockPlayer, cube); - PlayerInventory inv = mock(PlayerInventory.class); - ItemStack item = mock(ItemStack.class); - when(item.getType()).thenReturn(Material.BUCKET); - when(inv.getItemInMainHand()).thenReturn(item); - when(mockPlayer.getInventory()).thenReturn(inv); - l.onTropicalFishScooping(e); - assertTrue(e.isCancelled()); - verify(notifier).notify(any(), eq("protection.protected")); - } finally { - setStaticField(BucketListener.class, "SULFUR_CUBE", previous); - } + Entity cube = mock(Entity.class); + when(cube.getLocation()).thenReturn(location); + when(cube.getType()).thenReturn(EntityType.SULFUR_CUBE); + PlayerInteractEntityEvent e = new PlayerInteractEntityEvent(mockPlayer, cube); + PlayerInventory inv = mock(PlayerInventory.class); + ItemStack item = mock(ItemStack.class); + when(item.getType()).thenReturn(Material.BUCKET); + when(inv.getItemInMainHand()).thenReturn(item); + when(mockPlayer.getInventory()).thenReturn(inv); + l.onTropicalFishScooping(e); + assertTrue(e.isCancelled()); + verify(notifier).notify(any(), eq("protection.protected")); } /** * Bucketing a Sulfur Cube is allowed when the island permits the BUCKET flag. */ @Test - void testOnSulfurCubeBucketingAllowed() throws Exception { - EntityType standIn = EntityType.MAGMA_CUBE; - Object previous = getStaticField(BucketListener.class, "SULFUR_CUBE"); - setStaticField(BucketListener.class, "SULFUR_CUBE", standIn); - try { - Entity cube = mock(Entity.class); - when(cube.getLocation()).thenReturn(location); - when(cube.getType()).thenReturn(standIn); - PlayerInteractEntityEvent e = new PlayerInteractEntityEvent(mockPlayer, cube); - PlayerInventory inv = mock(PlayerInventory.class); - ItemStack item = mock(ItemStack.class); - when(item.getType()).thenReturn(Material.BUCKET); - when(inv.getItemInMainHand()).thenReturn(item); - when(mockPlayer.getInventory()).thenReturn(inv); - l.onTropicalFishScooping(e); - assertFalse(e.isCancelled()); - } finally { - setStaticField(BucketListener.class, "SULFUR_CUBE", previous); - } - } - - private static Object getStaticField(Class clazz, String name) throws Exception { - java.lang.reflect.Field f = clazz.getDeclaredField(name); - f.setAccessible(true); - return f.get(null); - } - - @SuppressWarnings("java:S3011") - private static void setStaticField(Class clazz, String name, Object value) throws Exception { - java.lang.reflect.Field f = clazz.getDeclaredField(name); - f.setAccessible(true); - f.set(null, value); + void testOnSulfurCubeBucketingAllowed() { + Entity cube = mock(Entity.class); + when(cube.getLocation()).thenReturn(location); + when(cube.getType()).thenReturn(EntityType.SULFUR_CUBE); + PlayerInteractEntityEvent e = new PlayerInteractEntityEvent(mockPlayer, cube); + PlayerInventory inv = mock(PlayerInventory.class); + ItemStack item = mock(ItemStack.class); + when(item.getType()).thenReturn(Material.BUCKET); + when(inv.getItemInMainHand()).thenReturn(item); + when(mockPlayer.getInventory()).thenReturn(inv); + l.onTropicalFishScooping(e); + assertFalse(e.isCancelled()); } } diff --git a/src/test/java/world/bentobox/bentobox/listeners/flags/protection/EntityInteractListenerTest.java b/src/test/java/world/bentobox/bentobox/listeners/flags/protection/EntityInteractListenerTest.java index 2cd33e60b..0c6470887 100644 --- a/src/test/java/world/bentobox/bentobox/listeners/flags/protection/EntityInteractListenerTest.java +++ b/src/test/java/world/bentobox/bentobox/listeners/flags/protection/EntityInteractListenerTest.java @@ -399,66 +399,38 @@ void testOnPlayerInteractEntityCopperGolemNameTagNoInteraction() { /** * Giving a block to a Sulfur Cube (Minecraft 26.2) for it to absorb is treated as placing a - * block and must be blocked by the PLACE_BLOCKS flag when not allowed. The 26.2 EntityType - * constant is absent in the test API, so MAGMA_CUBE is injected as a stand-in for SULFUR_CUBE. + * block and must be blocked by the PLACE_BLOCKS flag when not allowed. */ @Test - void testOnPlayerInteractEntitySulfurCubeBlockAbsorptionNotAllowed() throws Exception { - EntityType standIn = EntityType.MAGMA_CUBE; - Object previous = getStaticField(EntityInteractListener.class, "SULFUR_CUBE"); - setStaticField(EntityInteractListener.class, "SULFUR_CUBE", standIn); - try { - clickedEntity = mock(Entity.class); - when(clickedEntity.getLocation()).thenReturn(location); - when(clickedEntity.getType()).thenReturn(standIn); - ItemStack block = mock(ItemStack.class); - when(block.getType()).thenReturn(Material.STONE); - when(inv.getItemInMainHand()).thenReturn(block); - PlayerInteractEntityEvent e = new PlayerInteractEntityEvent(mockPlayer, clickedEntity, hand); - eil.onPlayerInteractEntity(e); - verify(notifier).notify(any(), eq("protection.protected")); - assertTrue(e.isCancelled()); - } finally { - setStaticField(EntityInteractListener.class, "SULFUR_CUBE", previous); - } + void testOnPlayerInteractEntitySulfurCubeBlockAbsorptionNotAllowed() { + clickedEntity = mock(Entity.class); + when(clickedEntity.getLocation()).thenReturn(location); + when(clickedEntity.getType()).thenReturn(EntityType.SULFUR_CUBE); + ItemStack block = mock(ItemStack.class); + when(block.getType()).thenReturn(Material.STONE); + when(inv.getItemInMainHand()).thenReturn(block); + PlayerInteractEntityEvent e = new PlayerInteractEntityEvent(mockPlayer, clickedEntity, hand); + eil.onPlayerInteractEntity(e); + verify(notifier).notify(any(), eq("protection.protected")); + assertTrue(e.isCancelled()); } /** * Giving a block to a Sulfur Cube is allowed when the island permits PLACE_BLOCKS. */ @Test - void testOnPlayerInteractEntitySulfurCubeBlockAbsorptionAllowed() throws Exception { + void testOnPlayerInteractEntitySulfurCubeBlockAbsorptionAllowed() { when(island.isAllowed(any(User.class), any())).thenReturn(true); - EntityType standIn = EntityType.MAGMA_CUBE; - Object previous = getStaticField(EntityInteractListener.class, "SULFUR_CUBE"); - setStaticField(EntityInteractListener.class, "SULFUR_CUBE", standIn); - try { - clickedEntity = mock(Entity.class); - when(clickedEntity.getLocation()).thenReturn(location); - when(clickedEntity.getType()).thenReturn(standIn); - ItemStack block = mock(ItemStack.class); - when(block.getType()).thenReturn(Material.STONE); - when(inv.getItemInMainHand()).thenReturn(block); - PlayerInteractEntityEvent e = new PlayerInteractEntityEvent(mockPlayer, clickedEntity, hand); - eil.onPlayerInteractEntity(e); - verify(notifier, never()).notify(any(), eq("protection.protected")); - assertFalse(e.isCancelled()); - } finally { - setStaticField(EntityInteractListener.class, "SULFUR_CUBE", previous); - } - } - - private static Object getStaticField(Class clazz, String name) throws Exception { - java.lang.reflect.Field f = clazz.getDeclaredField(name); - f.setAccessible(true); - return f.get(null); - } - - @SuppressWarnings("java:S3011") - private static void setStaticField(Class clazz, String name, Object value) throws Exception { - java.lang.reflect.Field f = clazz.getDeclaredField(name); - f.setAccessible(true); - f.set(null, value); + clickedEntity = mock(Entity.class); + when(clickedEntity.getLocation()).thenReturn(location); + when(clickedEntity.getType()).thenReturn(EntityType.SULFUR_CUBE); + ItemStack block = mock(ItemStack.class); + when(block.getType()).thenReturn(Material.STONE); + when(inv.getItemInMainHand()).thenReturn(block); + PlayerInteractEntityEvent e = new PlayerInteractEntityEvent(mockPlayer, clickedEntity, hand); + eil.onPlayerInteractEntity(e); + verify(notifier, never()).notify(any(), eq("protection.protected")); + assertFalse(e.isCancelled()); } } diff --git a/src/test/java/world/bentobox/bentobox/util/UtilTest.java b/src/test/java/world/bentobox/bentobox/util/UtilTest.java index adac7d9d9..c98dd5a4b 100644 --- a/src/test/java/world/bentobox/bentobox/util/UtilTest.java +++ b/src/test/java/world/bentobox/bentobox/util/UtilTest.java @@ -49,40 +49,17 @@ public void tearDown() throws Exception { } /** - * Sulfur Cube (Minecraft 26.2) is slime-like but passive. When the SULFUR_CUBE entity type is - * present, {@link Util#isPassiveEntity(org.bukkit.entity.Entity)} must classify it as passive - * and {@link Util#isHostileEntity(org.bukkit.entity.Entity)} must not treat it as hostile, - * even though it implements {@link Slime}. - *

- * The 26.2 EntityType constant does not exist in the test API, so an existing slime-like type - * (MAGMA_CUBE) is injected into Util's resolved SULFUR_CUBE field as a stand-in. + * Sulfur Cube (Minecraft 26.2) is slime-like but passive: + * {@link Util#isPassiveEntity(org.bukkit.entity.Entity)} must classify it as passive and + * {@link Util#isHostileEntity(org.bukkit.entity.Entity)} must not treat it as hostile, even + * though it implements {@link Slime}. */ @Test - void testSulfurCubeClassifiedAsPassiveNotHostile() throws Exception { - EntityType standIn = EntityType.MAGMA_CUBE; - Object previous = getStaticField(Util.class, "SULFUR_CUBE"); - setStaticField(Util.class, "SULFUR_CUBE", standIn); - try { - Slime sulfurCube = mock(Slime.class); - when(sulfurCube.getType()).thenReturn(standIn); - assertTrue(Util.isPassiveEntity(sulfurCube)); - assertFalse(Util.isHostileEntity(sulfurCube)); - } finally { - setStaticField(Util.class, "SULFUR_CUBE", previous); - } - } - - private static Object getStaticField(Class clazz, String name) throws Exception { - java.lang.reflect.Field f = clazz.getDeclaredField(name); - f.setAccessible(true); - return f.get(null); - } - - @SuppressWarnings("java:S3011") - private static void setStaticField(Class clazz, String name, Object value) throws Exception { - java.lang.reflect.Field f = clazz.getDeclaredField(name); - f.setAccessible(true); - f.set(null, value); + void testSulfurCubeClassifiedAsPassiveNotHostile() { + Slime sulfurCube = mock(Slime.class); + when(sulfurCube.getType()).thenReturn(EntityType.SULFUR_CUBE); + assertTrue(Util.isPassiveEntity(sulfurCube)); + assertFalse(Util.isHostileEntity(sulfurCube)); } // ---- blockFaceToFloat ----