From b7d34d08640d219c14c0e7d8a1a042807e7afca0 Mon Sep 17 00:00:00 2001 From: tastybento Date: Sat, 30 May 2026 18:35:26 -0700 Subject: [PATCH 01/12] Rev version to 3.4.1 Co-Authored-By: Claude Opus 4.8 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7ed1ba8..89e3181 100644 --- a/pom.xml +++ b/pom.xml @@ -63,7 +63,7 @@ -LOCAL - 3.4.0 + 3.4.1 BentoBoxWorld_Boxed bentobox-world From d705d1261cdc10075ae646c48df0e4874ccb8e22 Mon Sep 17 00:00:00 2001 From: tastybento Date: Sat, 30 May 2026 18:36:42 -0700 Subject: [PATCH 02/12] Remove unused imports (Sonar java:S1128) Removes three unused imports flagged by SonarCloud: - TrialSpawnerConfiguration in BoxedBlockPopulator - AbstractMetaData in NewAreaListener - static anyInt in AdvancementListenerTest Co-Authored-By: Claude Opus 4.8 --- release-notes-3.4.0.md | 49 +++++++++++++++++++ .../chunks/BoxedBlockPopulator.java | 1 - .../boxed/listeners/NewAreaListener.java | 1 - .../listeners/AdvancementListenerTest.java | 1 - 4 files changed, 49 insertions(+), 3 deletions(-) create mode 100644 release-notes-3.4.0.md diff --git a/release-notes-3.4.0.md b/release-notes-3.4.0.md new file mode 100644 index 0000000..3a8bfff --- /dev/null +++ b/release-notes-3.4.0.md @@ -0,0 +1,49 @@ +Boxed 3.4.0 brings support for **Trial Chambers** and their trial spawners, fixes cross-game-mode advancement resets, and modernises the entire build and test stack for Paper 1.21.11 / BentoBox 3.13.0. + +## Highlights + +* **Trial Chambers support** — Boxed now captures and restores Trial Spawner state (including the normal *and* ominous configurations) when structures are pulled from the seed world into a player's box, and recognises `trial_chambers` as a tracked structure. +* **No more cross-game-mode progress loss** — Boxed no longer clears a player's advancements and statistics when an island is reset in a *different*, non-Boxed game mode. +* **Modernised build & test stack** — upgraded to Paper 1.21.11 and BentoBox 3.13.0, with the test suite migrated to JUnit 5 + Mockito + MockBukkit. + +## New Features + +### Trial Chambers & Trial Spawners + +Trial Chambers can now appear inside player boxes. When a box is generated from the seed world, Boxed copies the Trial Spawner tile-entity data so the spawners function correctly, preserving whether each spawner is in its **normal** or **ominous** configuration. Trial Chambers are also tracked for advancement-driven box growth. [[PR #123](https://github.com/BentoBoxWorld/Boxed/pull/123)] + +## Bug Fixes + +* **Advancement/statistic reset leaked across game modes** — `IslandNewIslandEvent` handling now no-ops unless the reset island belongs to a Boxed world, so resetting an island in another game mode no longer wipes Boxed progress. [[PR #125](https://github.com/BentoBoxWorld/Boxed/pull/125)] +* **Structure pasting into deleted islands** — pending structure pastes are now cancelled when an island is deleted, preventing structures being placed into a box that no longer exists. +* **Ominous trial spawners restored incorrectly** — trial spawners now restore the correct configuration rather than always applying the normal one. + +## Other Improvements + +* **Test suite migration** — replaced the PowerMock-based setup with JUnit 5 (Jupiter), Mockito `mockStatic`, and MockBukkit, and expanded coverage with new listener and placeholder tests (113 tests). +* **Reliable test dependency** — MockBukkit is now pinned to the stable Maven Central artifact `org.mockbukkit.mockbukkit:mockbukkit-v1.21:4.110.0` instead of a floating jitpack snapshot that could break the build without any code change. +* **Documentation** — the README now documents Flags, Placeholders, `structures.yml`, config options, and Regionerator usage. [[PR #124](https://github.com/BentoBoxWorld/Boxed/pull/124)] + +## Compatibility + +✔️ BentoBox API 3.13.0 +✔️ Minecraft 1.21.x – 26.1.x (Paper 1.21.11) +✔️ Java 21 + +## Upgrading + +1. Stop your server. +2. Replace the old `Boxed` jar in `plugins/BentoBox/addons` with this release. +3. Ensure BentoBox is **3.13.0 or newer**. +4. Start the server. + +> **Note:** Trial Chambers are captured from the seed world when a box is generated, so boxes created *before* 3.4.0 will not retroactively gain Trial Chambers. New boxes (and newly expanded regions) will include them. + +## What's Changed + +* Add trial chambers support: fix trial spawner tile entity copying and advancement tracking by @Copilot in https://github.com/BentoBoxWorld/Boxed/pull/123 +* Update Boxed README: add Flags, Placeholders, structures.yml, config options, and Regionerator docs by @Copilot in https://github.com/BentoBoxWorld/Boxed/pull/124 +* Prevent Boxed from resetting player progress on non-Boxed island resets by @Copilot in https://github.com/BentoBoxWorld/Boxed/pull/125 +* Release 3.4.0 (Paper 1.21.11 / BentoBox 3.13.0, JUnit 5 + MockBukkit test migration, build fixes) by @tastybento in https://github.com/BentoBoxWorld/Boxed/pull/126 + +**Full Changelog**: https://github.com/BentoBoxWorld/Boxed/compare/3.3.0...3.4.0 diff --git a/src/main/java/world/bentobox/boxed/generators/chunks/BoxedBlockPopulator.java b/src/main/java/world/bentobox/boxed/generators/chunks/BoxedBlockPopulator.java index c94e27d..3344f84 100644 --- a/src/main/java/world/bentobox/boxed/generators/chunks/BoxedBlockPopulator.java +++ b/src/main/java/world/bentobox/boxed/generators/chunks/BoxedBlockPopulator.java @@ -11,7 +11,6 @@ import org.bukkit.block.BlockState; import org.bukkit.block.CreatureSpawner; import org.bukkit.block.TrialSpawner; -import org.bukkit.spawner.TrialSpawnerConfiguration; import org.bukkit.entity.Entity; import org.bukkit.generator.BlockPopulator; import org.bukkit.generator.LimitedRegion; diff --git a/src/main/java/world/bentobox/boxed/listeners/NewAreaListener.java b/src/main/java/world/bentobox/boxed/listeners/NewAreaListener.java index a5d306b..da71223 100644 --- a/src/main/java/world/bentobox/boxed/listeners/NewAreaListener.java +++ b/src/main/java/world/bentobox/boxed/listeners/NewAreaListener.java @@ -53,7 +53,6 @@ import world.bentobox.bentobox.api.events.island.IslandResettedEvent; import world.bentobox.bentobox.database.Database; import world.bentobox.bentobox.database.objects.Island; -import world.bentobox.bentobox.nms.AbstractMetaData; import world.bentobox.bentobox.util.Pair; import world.bentobox.bentobox.util.Util; import world.bentobox.boxed.Boxed; diff --git a/src/test/java/world/bentobox/boxed/listeners/AdvancementListenerTest.java b/src/test/java/world/bentobox/boxed/listeners/AdvancementListenerTest.java index 5918ef6..d3a0114 100644 --- a/src/test/java/world/bentobox/boxed/listeners/AdvancementListenerTest.java +++ b/src/test/java/world/bentobox/boxed/listeners/AdvancementListenerTest.java @@ -4,7 +4,6 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; From 390d5487b82fafd83eb5e43083aff39bf767c39a Mon Sep 17 00:00:00 2001 From: tastybento Date: Sat, 30 May 2026 18:38:51 -0700 Subject: [PATCH 03/12] Remove redundant throws declarations in tests (Sonar java:S1130) Removes 15 thrown-exception declarations that the method bodies cannot actually throw, in AdvancementsManagerTest and EnderPearlListenerTest. CommonTestSetup.setUp() keeps 'throws Exception' because subclass overrides legitimately throw checked exceptions and the override contract requires it. Co-Authored-By: Claude Opus 4.8 --- .../world/bentobox/boxed/AdvancementsManagerTest.java | 10 +++++----- .../boxed/listeners/EnderPearlListenerTest.java | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/test/java/world/bentobox/boxed/AdvancementsManagerTest.java b/src/test/java/world/bentobox/boxed/AdvancementsManagerTest.java index 6bc561a..3bd23a7 100644 --- a/src/test/java/world/bentobox/boxed/AdvancementsManagerTest.java +++ b/src/test/java/world/bentobox/boxed/AdvancementsManagerTest.java @@ -142,7 +142,7 @@ public void testAdvancementsManagerNoFile() throws Exception { * @throws IOException */ @Test - public void testAdvancementsManager() throws IOException { + public void testAdvancementsManager() { verify(addon).saveResource("advancements.yml", false); verify(addon, never()).logError(anyString()); } @@ -165,7 +165,7 @@ public void testGetIsland() { * @throws IllegalAccessException */ @Test - public void testSaveIslandNotInCache() throws IllegalAccessException, InvocationTargetException, IntrospectionException { + public void testSaveIslandNotInCache() { am.removeFromCache(island); am.saveIsland(island); verify(island, times(2)).getUniqueId(); // 2x @@ -178,7 +178,7 @@ public void testSaveIslandNotInCache() throws IllegalAccessException, Invocation * @throws IllegalAccessException */ @Test - public void testSaveIslandInCache() throws IllegalAccessException, InvocationTargetException, IntrospectionException { + public void testSaveIslandInCache() { testGetIsland(); am.saveIsland(island); verify(island, times(3)).getUniqueId(); // 3x @@ -191,7 +191,7 @@ public void testSaveIslandInCache() throws IllegalAccessException, InvocationTar * @throws IllegalAccessException */ @Test - public void testSaveNothingToSave() throws IllegalAccessException, InvocationTargetException, IntrospectionException { + public void testSaveNothingToSave() { am.removeFromCache(island); am.save(); verify(island).getUniqueId(); @@ -204,7 +204,7 @@ public void testSaveNothingToSave() throws IllegalAccessException, InvocationTar * @throws IllegalAccessException */ @Test - public void testSave() throws IllegalAccessException, InvocationTargetException, IntrospectionException { + public void testSave() { testGetIsland(); am.save(); verify(island).getUniqueId(); diff --git a/src/test/java/world/bentobox/boxed/listeners/EnderPearlListenerTest.java b/src/test/java/world/bentobox/boxed/listeners/EnderPearlListenerTest.java index 17555ae..8a515cc 100644 --- a/src/test/java/world/bentobox/boxed/listeners/EnderPearlListenerTest.java +++ b/src/test/java/world/bentobox/boxed/listeners/EnderPearlListenerTest.java @@ -348,7 +348,7 @@ public void testOnEnderPearlLandHuman() throws IOException { * @throws IOException */ @Test - public void testOnEnderPearlThrewToDifferentIsland() throws IOException { + public void testOnEnderPearlThrewToDifferentIsland() { when(im.getIslandAt(eq(to))).thenReturn(Optional.of(anotherIsland)); ProjectileHitEvent e = new ProjectileHitEvent(projectile, null, hitBlock, BlockFace.UP); epl.onEnderPearlLand(e); @@ -361,7 +361,7 @@ public void testOnEnderPearlThrewToDifferentIsland() throws IOException { * @throws IOException */ @Test - public void testOnEnderPearlThrewToNonIsland() throws IOException { + public void testOnEnderPearlThrewToNonIsland() { when(im.getIslandAt(eq(to))).thenReturn(Optional.empty()); ProjectileHitEvent e = new ProjectileHitEvent(projectile, null, hitBlock, BlockFace.UP); epl.onEnderPearlLand(e); From bf071c022152ed3116bb0e797da9a241303182a5 Mon Sep 17 00:00:00 2001 From: tastybento Date: Sat, 30 May 2026 18:40:48 -0700 Subject: [PATCH 04/12] Give JUnit5 test classes/methods package visibility (Sonar java:S5786) Removes the redundant public modifier from the 5 test classes and their 118 @Test methods. setUp()/tearDown() keep public because they override the public lifecycle methods of CommonTestSetup across package boundaries. Co-Authored-By: Claude Opus 4.8 --- .../boxed/AdvancementsManagerTest.java | 46 +++++------ .../boxed/PlaceholdersManagerTest.java | 18 ++-- .../world/bentobox/boxed/SettingsTest.java | 82 +++++++++---------- .../listeners/AdvancementListenerTest.java | 54 ++++++------ .../listeners/EnderPearlListenerTest.java | 36 ++++---- 5 files changed, 118 insertions(+), 118 deletions(-) diff --git a/src/test/java/world/bentobox/boxed/AdvancementsManagerTest.java b/src/test/java/world/bentobox/boxed/AdvancementsManagerTest.java index 3bd23a7..a36eb7a 100644 --- a/src/test/java/world/bentobox/boxed/AdvancementsManagerTest.java +++ b/src/test/java/world/bentobox/boxed/AdvancementsManagerTest.java @@ -42,7 +42,7 @@ * @author tastybento * */ -public class AdvancementsManagerTest extends CommonTestSetup { +class AdvancementsManagerTest extends CommonTestSetup { @Mock private world.bentobox.bentobox.Settings pluginSettings; @@ -129,7 +129,7 @@ public void tearDown() throws Exception { * @throws Exception */ @Test - public void testAdvancementsManagerNoFile() throws Exception { + void testAdvancementsManagerNoFile() throws Exception { // Delete the advancements.yml file so the constructor logs an error. Do NOT tear // down the full mock infrastructure — we still need it for the second manager. deleteAll(dataFolder); @@ -142,7 +142,7 @@ public void testAdvancementsManagerNoFile() throws Exception { * @throws IOException */ @Test - public void testAdvancementsManager() { + void testAdvancementsManager() { verify(addon).saveResource("advancements.yml", false); verify(addon, never()).logError(anyString()); } @@ -151,7 +151,7 @@ public void testAdvancementsManager() { * Test method for {@link world.bentobox.boxed.AdvancementsManager#getIsland(world.bentobox.bentobox.database.objects.Island)}. */ @Test - public void testGetIsland() { + void testGetIsland() { @NonNull IslandAdvancements adv = am.getIsland(island); assertEquals("uniqueId", adv.getUniqueId()); @@ -165,7 +165,7 @@ public void testGetIsland() { * @throws IllegalAccessException */ @Test - public void testSaveIslandNotInCache() { + void testSaveIslandNotInCache() { am.removeFromCache(island); am.saveIsland(island); verify(island, times(2)).getUniqueId(); // 2x @@ -178,7 +178,7 @@ public void testSaveIslandNotInCache() { * @throws IllegalAccessException */ @Test - public void testSaveIslandInCache() { + void testSaveIslandInCache() { testGetIsland(); am.saveIsland(island); verify(island, times(3)).getUniqueId(); // 3x @@ -191,7 +191,7 @@ public void testSaveIslandInCache() { * @throws IllegalAccessException */ @Test - public void testSaveNothingToSave() { + void testSaveNothingToSave() { am.removeFromCache(island); am.save(); verify(island).getUniqueId(); @@ -204,7 +204,7 @@ public void testSaveNothingToSave() { * @throws IllegalAccessException */ @Test - public void testSave() { + void testSave() { testGetIsland(); am.save(); verify(island).getUniqueId(); @@ -214,7 +214,7 @@ public void testSave() { * Test method for {@link world.bentobox.boxed.AdvancementsManager#addAdvancement(world.bentobox.bentobox.database.objects.Island, java.lang.String)}. */ @Test - public void testAddAdvancementIslandString() { + void testAddAdvancementIslandString() { assertTrue(am.addAdvancement(island, "advancement")); assertFalse(am.addAdvancement(island, "advancement")); // Second time should fail } @@ -223,7 +223,7 @@ public void testAddAdvancementIslandString() { * Test method for {@link world.bentobox.boxed.AdvancementsManager#removeAdvancement(world.bentobox.bentobox.database.objects.Island, java.lang.String)}. */ @Test - public void testRemoveAdvancement() { + void testRemoveAdvancement() { assertTrue(am.addAdvancement(island, "advancement")); am.removeAdvancement(island, "advancement"); assertTrue(am.addAdvancement(island, "advancement")); // Should work because it was removed @@ -233,7 +233,7 @@ public void testRemoveAdvancement() { * Test method for {@link world.bentobox.boxed.AdvancementsManager#hasAdvancement(world.bentobox.bentobox.database.objects.Island, java.lang.String)}. */ @Test - public void testHasAdvancement() { + void testHasAdvancement() { assertFalse(am.hasAdvancement(island, "advancement")); am.addAdvancement(island, "advancement"); assertTrue(am.hasAdvancement(island, "advancement")); @@ -243,7 +243,7 @@ public void testHasAdvancement() { * Test method for {@link world.bentobox.boxed.AdvancementsManager#checkIslandSize(world.bentobox.bentobox.database.objects.Island)}. */ @Test - public void testCheckIslandSize() { + void testCheckIslandSize() { // Island protection size is set to 5, but after checking, the size is reduced by 4 assertEquals(-4, am.checkIslandSize(island)); } @@ -252,7 +252,7 @@ public void testCheckIslandSize() { * Test method for {@link world.bentobox.boxed.AdvancementsManager#addAdvancement(org.bukkit.entity.Player, org.bukkit.advancement.Advancement)}. */ @Test - public void testAddAdvancementPlayerAdvancementWrongWorld() { + void testAddAdvancementPlayerAdvancementWrongWorld() { when(addon.inWorld(world)).thenReturn(false); assertEquals(0, am.addAdvancement(player, advancement)); } @@ -261,7 +261,7 @@ public void testAddAdvancementPlayerAdvancementWrongWorld() { * Test method for {@link world.bentobox.boxed.AdvancementsManager#addAdvancement(org.bukkit.entity.Player, org.bukkit.advancement.Advancement)}. */ @Test - public void testAddAdvancementPlayerAdvancement() { + void testAddAdvancementPlayerAdvancement() { assertEquals(9, am.addAdvancement(player, advancement)); verify(island).setProtectionRange(14); // (9 + 5) } @@ -271,7 +271,7 @@ public void testAddAdvancementPlayerAdvancement() { * A null display means the advancement cannot be scored automatically. */ @Test - public void testAddAdvancementPlayerAdvancementZeroScore() { + void testAddAdvancementPlayerAdvancementZeroScore() { when(advancement.getDisplay()).thenReturn(null); assertEquals(0, am.addAdvancement(player, advancement)); verify(island, never()).setProtectionRange(org.mockito.ArgumentMatchers.anyInt()); @@ -281,7 +281,7 @@ public void testAddAdvancementPlayerAdvancementZeroScore() { * Test method for {@link world.bentobox.boxed.AdvancementsManager#getScore(java.lang.String)}. */ @Test - public void testGetScoreString() { + void testGetScoreString() { assertEquals(9, am.getScore("adventure/lightning_rod_with_villager_no_fire")); } @@ -289,7 +289,7 @@ public void testGetScoreString() { * Test method for {@link world.bentobox.boxed.AdvancementsManager#getScore(org.bukkit.advancement.Advancement)}. */ @Test - public void testGetScoreAdvancement() { + void testGetScoreAdvancement() { assertEquals(9, am.getScore(advancement)); } @@ -298,7 +298,7 @@ public void testGetScoreAdvancement() { * Root advancements fall back to settings.default-root-increase (0 in the shipped config). */ @Test - public void testGetScoreAdvancementRoot() { + void testGetScoreAdvancementRoot() { when(advancement.getKey()).thenReturn(NamespacedKey.fromString("story/root")); assertEquals(0, am.getScore(advancement)); } @@ -308,7 +308,7 @@ public void testGetScoreAdvancementRoot() { * Recipe advancements always score settings.unknown-recipe-increase (0 in the shipped config). */ @Test - public void testGetScoreAdvancementRecipe() { + void testGetScoreAdvancementRecipe() { when(advancement.getKey()).thenReturn(NamespacedKey.fromString("recipes/brewing/blaze_powder")); assertEquals(0, am.getScore(advancement)); } @@ -318,7 +318,7 @@ public void testGetScoreAdvancementRecipe() { * No island for this player means no expansion and a zero score. */ @Test - public void testAddAdvancementPlayerAdvancementNullIsland() { + void testAddAdvancementPlayerAdvancementNullIsland() { when(im.getIsland(world, player.getUniqueId())).thenReturn(null); assertEquals(0, am.addAdvancement(player, advancement)); verify(island, never()).setProtectionRange(org.mockito.ArgumentMatchers.anyInt()); @@ -329,7 +329,7 @@ public void testAddAdvancementPlayerAdvancementNullIsland() { * Visitors (rank below MEMBER_RANK) cannot expand the island. */ @Test - public void testAddAdvancementPlayerAdvancementVisitorRank() { + void testAddAdvancementPlayerAdvancementVisitorRank() { when(island.getRank(player.getUniqueId())).thenReturn(RanksManager.VISITOR_RANK); assertEquals(0, am.addAdvancement(player, advancement)); verify(island, never()).setProtectionRange(org.mockito.ArgumentMatchers.anyInt()); @@ -340,7 +340,7 @@ public void testAddAdvancementPlayerAdvancementVisitorRank() { * An advancement that's already been recorded on the island cannot grant a second expansion. */ @Test - public void testAddAdvancementPlayerAdvancementAlreadyHas() { + void testAddAdvancementPlayerAdvancementAlreadyHas() { // Seed the island with the exact same namespaced key the manager will try to record. am.addAdvancement(island, advancement.getKey().toString()); assertEquals(0, am.addAdvancement(player, advancement)); @@ -351,7 +351,7 @@ public void testAddAdvancementPlayerAdvancementAlreadyHas() { * Positive diff case: one scoring advancement grows a size-1 island to size 10. */ @Test - public void testCheckIslandSizePositiveDiff() { + void testCheckIslandSizePositiveDiff() { when(island.getProtectionRange()).thenReturn(1); am.addAdvancement(island, "adventure/honey_block_slide"); assertEquals(9, am.checkIslandSize(island)); diff --git a/src/test/java/world/bentobox/boxed/PlaceholdersManagerTest.java b/src/test/java/world/bentobox/boxed/PlaceholdersManagerTest.java index e6815b3..3eb15ec 100644 --- a/src/test/java/world/bentobox/boxed/PlaceholdersManagerTest.java +++ b/src/test/java/world/bentobox/boxed/PlaceholdersManagerTest.java @@ -19,7 +19,7 @@ /** * @author tastybento */ -public class PlaceholdersManagerTest extends CommonTestSetup { +class PlaceholdersManagerTest extends CommonTestSetup { @Mock private Boxed addon; @@ -61,47 +61,47 @@ public void tearDown() throws Exception { } @Test - public void testGetCountNullUser() { + void testGetCountNullUser() { assertEquals("", phm.getCount(null)); } @Test - public void testGetCountNullUuid() { + void testGetCountNullUuid() { when(user.getUniqueId()).thenReturn(null); assertEquals("", phm.getCount(user)); } @Test - public void testGetCountNoIsland() { + void testGetCountNoIsland() { when(im.getIsland(world, user)).thenReturn(null); assertEquals("", phm.getCount(user)); } @Test - public void testGetCountReturnsAdvancementCount() { + void testGetCountReturnsAdvancementCount() { when(im.getIsland(world, user)).thenReturn(island); assertEquals("3", phm.getCount(user)); } @Test - public void testGetCountByLocationNullUser() { + void testGetCountByLocationNullUser() { assertEquals("", phm.getCountByLocation(null)); } @Test - public void testGetCountByLocationNullLocation() { + void testGetCountByLocationNullLocation() { when(user.getLocation()).thenReturn(null); assertEquals("", phm.getCountByLocation(user)); } @Test - public void testGetCountByLocationNoIslandAtLocation() { + void testGetCountByLocationNoIslandAtLocation() { when(im.getIslandAt(userLocation)).thenReturn(Optional.empty()); assertEquals("", phm.getCountByLocation(user)); } @Test - public void testGetCountByLocationReturnsAdvancementCount() { + void testGetCountByLocationReturnsAdvancementCount() { when(im.getIslandAt(userLocation)).thenReturn(Optional.of(island)); assertEquals("3", phm.getCountByLocation(user)); } diff --git a/src/test/java/world/bentobox/boxed/SettingsTest.java b/src/test/java/world/bentobox/boxed/SettingsTest.java index 482ac69..fb8aca4 100644 --- a/src/test/java/world/bentobox/boxed/SettingsTest.java +++ b/src/test/java/world/bentobox/boxed/SettingsTest.java @@ -20,7 +20,7 @@ * @author tastybento * */ -public class SettingsTest extends CommonTestSetup { +class SettingsTest extends CommonTestSetup { Settings s; @@ -40,7 +40,7 @@ public void tearDown() throws Exception { * Test method for {@link world.bentobox.boxed.Settings#setFriendlyName(java.lang.String)}. */ @Test - public void testSetFriendlyName() { + void testSetFriendlyName() { s.setFriendlyName("name"); assertEquals("name", s.getFriendlyName()); } @@ -49,7 +49,7 @@ public void testSetFriendlyName() { * Test method for {@link world.bentobox.boxed.Settings#setWorldName(java.lang.String)}. */ @Test - public void testSetWorldName() { + void testSetWorldName() { s.setWorldName("name"); assertEquals("name", s.getWorldName()); } @@ -58,7 +58,7 @@ public void testSetWorldName() { * Test method for {@link world.bentobox.boxed.Settings#setDifficulty(org.bukkit.Difficulty)}. */ @Test - public void testSetDifficulty() { + void testSetDifficulty() { s.setDifficulty(Difficulty.PEACEFUL); assertEquals(Difficulty.PEACEFUL, s.getDifficulty()); } @@ -67,7 +67,7 @@ public void testSetDifficulty() { * Test method for {@link world.bentobox.boxed.Settings#setIslandDistance(int)}. */ @Test - public void testSetIslandDistance() { + void testSetIslandDistance() { s.setIslandDistance(123); assertEquals(112, s.getIslandDistance()); verify(plugin).logWarning("Boxed: Area radius is not a factor of 16. Rounding to 112"); @@ -77,7 +77,7 @@ public void testSetIslandDistance() { * Test method for {@link world.bentobox.boxed.Settings#setIslandProtectionRange(int)}. */ @Test - public void testSetIslandProtectionRange() { + void testSetIslandProtectionRange() { s.setIslandProtectionRange(123); assertEquals(123, s.getIslandProtectionRange()); } @@ -86,7 +86,7 @@ public void testSetIslandProtectionRange() { * Test method for {@link world.bentobox.boxed.Settings#setIslandStartX(int)}. */ @Test - public void testSetIslandStartX() { + void testSetIslandStartX() { s.setIslandStartX(123); assertEquals(123, s.getIslandStartX()); } @@ -95,7 +95,7 @@ public void testSetIslandStartX() { * Test method for {@link world.bentobox.boxed.Settings#setIslandStartZ(int)}. */ @Test - public void testSetIslandStartZ() { + void testSetIslandStartZ() { s.setIslandStartZ(123); assertEquals(123, s.getIslandStartZ()); } @@ -104,7 +104,7 @@ public void testSetIslandStartZ() { * Test method for {@link world.bentobox.boxed.Settings#setMaxIslands(int)}. */ @Test - public void testSetMaxIslands() { + void testSetMaxIslands() { s.setMaxIslands(123); assertEquals(123, s.getMaxIslands()); } @@ -113,7 +113,7 @@ public void testSetMaxIslands() { * Test method for {@link world.bentobox.boxed.Settings#setNetherGenerate(boolean)}. */ @Test - public void testSetNetherGenerate() { + void testSetNetherGenerate() { s.setNetherGenerate(true); assertTrue(s.isNetherGenerate()); } @@ -122,7 +122,7 @@ public void testSetNetherGenerate() { * Test method for {@link world.bentobox.boxed.Settings#setEndGenerate(boolean)}. */ @Test - public void testSetEndGenerate() { + void testSetEndGenerate() { s.setEndGenerate(true); assertTrue(s.isEndGenerate()); } @@ -131,7 +131,7 @@ public void testSetEndGenerate() { * Test method for {@link world.bentobox.boxed.Settings#setRemoveMobsWhitelist(java.util.Set)}. */ @Test - public void testSetRemoveMobsWhitelist() { + void testSetRemoveMobsWhitelist() { Set wl = Collections.emptySet(); s.setRemoveMobsWhitelist(wl); assertEquals(wl, s.getRemoveMobsWhitelist()); @@ -141,7 +141,7 @@ public void testSetRemoveMobsWhitelist() { * Test method for {@link world.bentobox.boxed.Settings#setWorldFlags(java.util.Map)}. */ @Test - public void testSetWorldFlags() { + void testSetWorldFlags() { Map worldFlags = Collections.emptyMap(); s.setWorldFlags(worldFlags); assertEquals(worldFlags, s.getWorldFlags()); @@ -151,7 +151,7 @@ public void testSetWorldFlags() { * Test method for {@link world.bentobox.boxed.Settings#setHiddenFlags(java.util.List)}. */ @Test - public void testSetVisibleSettings() { + void testSetVisibleSettings() { List visibleSettings = Collections.emptyList(); s.setHiddenFlags(visibleSettings); assertEquals(visibleSettings, s.getHiddenFlags()); @@ -161,7 +161,7 @@ public void testSetVisibleSettings() { * Test method for {@link world.bentobox.boxed.Settings#setVisitorBannedCommands(java.util.List)}. */ @Test - public void testSetVisitorBannedCommands() { + void testSetVisitorBannedCommands() { List visitorBannedCommands = Collections.emptyList(); s.setVisitorBannedCommands(visitorBannedCommands); assertEquals(visitorBannedCommands, s.getVisitorBannedCommands()); @@ -171,7 +171,7 @@ public void testSetVisitorBannedCommands() { * Test method for {@link world.bentobox.boxed.Settings#setMaxTeamSize(int)}. */ @Test - public void testSetMaxTeamSize() { + void testSetMaxTeamSize() { s.setMaxTeamSize(123); assertEquals(123, s.getMaxTeamSize()); } @@ -180,7 +180,7 @@ public void testSetMaxTeamSize() { * Test method for {@link world.bentobox.boxed.Settings#setMaxHomes(int)}. */ @Test - public void testSetMaxHomes() { + void testSetMaxHomes() { s.setMaxHomes(123); assertEquals(123, s.getMaxHomes()); } @@ -189,7 +189,7 @@ public void testSetMaxHomes() { * Test method for {@link world.bentobox.boxed.Settings#setResetLimit(int)}. */ @Test - public void testSetResetLimit() { + void testSetResetLimit() { s.setResetLimit(123); assertEquals(123, s.getResetLimit()); } @@ -198,7 +198,7 @@ public void testSetResetLimit() { * Test method for {@link world.bentobox.boxed.Settings#setLeaversLoseReset(boolean)}. */ @Test - public void testSetLeaversLoseReset() { + void testSetLeaversLoseReset() { s.setLeaversLoseReset(true); assertTrue(s.isLeaversLoseReset()); } @@ -207,7 +207,7 @@ public void testSetLeaversLoseReset() { * Test method for {@link world.bentobox.boxed.Settings#setKickedKeepInventory(boolean)}. */ @Test - public void testSetKickedKeepInventory() { + void testSetKickedKeepInventory() { s.setKickedKeepInventory(true); assertTrue(s.isKickedKeepInventory()); } @@ -216,7 +216,7 @@ public void testSetKickedKeepInventory() { * Test method for {@link world.bentobox.boxed.Settings#setOnJoinResetMoney(boolean)}. */ @Test - public void testSetOnJoinResetMoney() { + void testSetOnJoinResetMoney() { s.setOnJoinResetMoney(true); assertTrue(s.isOnJoinResetMoney()); } @@ -225,7 +225,7 @@ public void testSetOnJoinResetMoney() { * Test method for {@link world.bentobox.boxed.Settings#setOnJoinResetInventory(boolean)}. */ @Test - public void testSetOnJoinResetInventory() { + void testSetOnJoinResetInventory() { s.setOnJoinResetInventory(true); assertTrue(s.isOnJoinResetInventory()); } @@ -234,7 +234,7 @@ public void testSetOnJoinResetInventory() { * Test method for {@link world.bentobox.boxed.Settings#setOnJoinResetEnderChest(boolean)}. */ @Test - public void testSetOnJoinResetEnderChest() { + void testSetOnJoinResetEnderChest() { s.setOnJoinResetEnderChest(true); assertTrue(s.isOnJoinResetEnderChest()); } @@ -243,7 +243,7 @@ public void testSetOnJoinResetEnderChest() { * Test method for {@link world.bentobox.boxed.Settings#setOnLeaveResetMoney(boolean)}. */ @Test - public void testSetOnLeaveResetMoney() { + void testSetOnLeaveResetMoney() { s.setOnLeaveResetMoney(true); assertTrue(s.isOnLeaveResetMoney()); } @@ -252,7 +252,7 @@ public void testSetOnLeaveResetMoney() { * Test method for {@link world.bentobox.boxed.Settings#setOnLeaveResetInventory(boolean)}. */ @Test - public void testSetOnLeaveResetInventory() { + void testSetOnLeaveResetInventory() { s.setOnLeaveResetInventory(true); assertTrue(s.isOnLeaveResetInventory()); } @@ -261,7 +261,7 @@ public void testSetOnLeaveResetInventory() { * Test method for {@link world.bentobox.boxed.Settings#setOnLeaveResetEnderChest(boolean)}. */ @Test - public void testSetOnLeaveResetEnderChest() { + void testSetOnLeaveResetEnderChest() { s.setOnLeaveResetEnderChest(true); assertTrue(s.isOnLeaveResetEnderChest()); } @@ -270,7 +270,7 @@ public void testSetOnLeaveResetEnderChest() { * Test method for {@link world.bentobox.boxed.Settings#setDeathsCounted(boolean)}. */ @Test - public void testSetDeathsCounted() { + void testSetDeathsCounted() { s.setDeathsCounted(true); assertTrue(s.isDeathsCounted()); } @@ -279,7 +279,7 @@ public void testSetDeathsCounted() { * Test method for {@link world.bentobox.boxed.Settings#setDeathsMax(int)}. */ @Test - public void testSetDeathsMax() { + void testSetDeathsMax() { s.setDeathsMax(123); assertEquals(123, s.getDeathsMax()); } @@ -288,7 +288,7 @@ public void testSetDeathsMax() { * Test method for {@link world.bentobox.boxed.Settings#setTeamJoinDeathReset(boolean)}. */ @Test - public void testSetTeamJoinDeathReset() { + void testSetTeamJoinDeathReset() { s.setTeamJoinDeathReset(true); assertTrue(s.isTeamJoinDeathReset()); } @@ -297,7 +297,7 @@ public void testSetTeamJoinDeathReset() { * Test method for {@link world.bentobox.boxed.Settings#setGeoLimitSettings(java.util.List)}. */ @Test - public void testSetGeoLimitSettings() { + void testSetGeoLimitSettings() { List geoLimitSettings = Collections.emptyList(); s.setGeoLimitSettings(geoLimitSettings); assertEquals(geoLimitSettings, s.getGeoLimitSettings()); @@ -307,7 +307,7 @@ public void testSetGeoLimitSettings() { * Test method for {@link world.bentobox.boxed.Settings#setIvSettings(java.util.List)}. */ @Test - public void testSetIvSettings() { + void testSetIvSettings() { List ivSettings = Collections.emptyList(); s.setIvSettings(ivSettings); assertEquals(ivSettings, s.getIvSettings()); @@ -317,7 +317,7 @@ public void testSetIvSettings() { * Test method for {@link world.bentobox.boxed.Settings#setAllowSetHomeInNether(boolean)}. */ @Test - public void testSetAllowSetHomeInNether() { + void testSetAllowSetHomeInNether() { s.setAllowSetHomeInNether(true); assertTrue(s.isAllowSetHomeInNether()); } @@ -326,7 +326,7 @@ public void testSetAllowSetHomeInNether() { * Test method for {@link world.bentobox.boxed.Settings#setAllowSetHomeInTheEnd(boolean)}. */ @Test - public void testSetAllowSetHomeInTheEnd() { + void testSetAllowSetHomeInTheEnd() { s.setAllowSetHomeInTheEnd(true); assertTrue(s.isAllowSetHomeInTheEnd()); } @@ -335,7 +335,7 @@ public void testSetAllowSetHomeInTheEnd() { * Test method for {@link world.bentobox.boxed.Settings#setRequireConfirmationToSetHomeInNether(boolean)}. */ @Test - public void testSetRequireConfirmationToSetHomeInNether() { + void testSetRequireConfirmationToSetHomeInNether() { s.setRequireConfirmationToSetHomeInNether(true); assertTrue(s.isRequireConfirmationToSetHomeInNether()); } @@ -344,7 +344,7 @@ public void testSetRequireConfirmationToSetHomeInNether() { * Test method for {@link world.bentobox.boxed.Settings#setRequireConfirmationToSetHomeInTheEnd(boolean)}. */ @Test - public void testSetRequireConfirmationToSetHomeInTheEnd() { + void testSetRequireConfirmationToSetHomeInTheEnd() { s.setRequireConfirmationToSetHomeInTheEnd(true); assertTrue(s.isRequireConfirmationToSetHomeInTheEnd()); } @@ -353,7 +353,7 @@ public void testSetRequireConfirmationToSetHomeInTheEnd() { * Test method for {@link world.bentobox.boxed.Settings#setResetEpoch(long)}. */ @Test - public void testSetResetEpoch() { + void testSetResetEpoch() { s.setResetEpoch(123); assertEquals(123, s.getResetEpoch()); } @@ -362,7 +362,7 @@ public void testSetResetEpoch() { * Test method for {@link world.bentobox.boxed.Settings#getPermissionPrefix()}. */ @Test - public void testGetPermissionPrefix() { + void testGetPermissionPrefix() { assertEquals("boxed", s.getPermissionPrefix()); } @@ -370,7 +370,7 @@ public void testGetPermissionPrefix() { * Test method for {@link world.bentobox.boxed.Settings#isWaterUnsafe()}. */ @Test - public void testIsWaterUnsafe() { + void testIsWaterUnsafe() { assertFalse(s.isWaterUnsafe()); } @@ -378,7 +378,7 @@ public void testIsWaterUnsafe() { * Test method for {@link world.bentobox.boxed.Settings#setBanLimit(int)}. */ @Test - public void testSetBanLimit() { + void testSetBanLimit() { s.setBanLimit(123); assertEquals(123, s.getBanLimit()); } @@ -387,7 +387,7 @@ public void testSetBanLimit() { * Test method for {@link Settings#getPlayerCommandAliases()} Command()}. */ @Test - public void testGetIslandCommand() { + void testGetIslandCommand() { s.setPlayerCommandAliases("island"); assertEquals("island", s.getPlayerCommandAliases()); } @@ -396,7 +396,7 @@ public void testGetIslandCommand() { * Test method for {@link Settings#getAdminCommandAliases()}. */ @Test - public void testGetAdminCommand() { + void testGetAdminCommand() { s.setAdminCommandAliases("admin"); assertEquals("admin", s.getAdminCommandAliases()); } diff --git a/src/test/java/world/bentobox/boxed/listeners/AdvancementListenerTest.java b/src/test/java/world/bentobox/boxed/listeners/AdvancementListenerTest.java index d3a0114..d91bc3c 100644 --- a/src/test/java/world/bentobox/boxed/listeners/AdvancementListenerTest.java +++ b/src/test/java/world/bentobox/boxed/listeners/AdvancementListenerTest.java @@ -50,7 +50,7 @@ /** * @author tastybento */ -public class AdvancementListenerTest extends CommonTestSetup { +class AdvancementListenerTest extends CommonTestSetup { @Mock private Boxed addon; @@ -144,7 +144,7 @@ public void tearDown() throws Exception { // ---------- constructor ---------- @Test - public void testConstructor() { + void testConstructor() { assertNotNull(listener); } @@ -158,28 +158,28 @@ private PlayerAdvancementDoneEvent advancementDoneEvent() { } @Test - public void testOnAdvancementNotSurvival() { + void testOnAdvancementNotSurvival() { when(player.getGameMode()).thenReturn(GameMode.CREATIVE); listener.onAdvancement(advancementDoneEvent()); verify(advManager, never()).addAdvancement(any(Player.class), any(Advancement.class)); } @Test - public void testOnAdvancementIgnoreSetting() { + void testOnAdvancementIgnoreSetting() { settings.setIgnoreAdvancements(true); listener.onAdvancement(advancementDoneEvent()); verify(advManager, never()).addAdvancement(any(Player.class), any(Advancement.class)); } @Test - public void testOnAdvancementNotInWorld() { + void testOnAdvancementNotInWorld() { when(addon.inWorld(world)).thenReturn(false); listener.onAdvancement(advancementDoneEvent()); verify(advManager, never()).addAdvancement(any(Player.class), any(Advancement.class)); } @Test - public void testOnAdvancementVisitorDenied() { + void testOnAdvancementVisitorDenied() { settings.setDenyVisitorAdvancements(true); // player is NOT in the member set when(island.getMemberSet()).thenReturn(com.google.common.collect.ImmutableSet.of(UUID.randomUUID())); @@ -194,7 +194,7 @@ public void testOnAdvancementVisitorDenied() { } @Test - public void testOnAdvancementMemberGrantsAndSchedulesTellTeam() { + void testOnAdvancementMemberGrantsAndSchedulesTellTeam() { listener.onAdvancement(advancementDoneEvent()); verify(advManager).addAdvancement(eq(player), eq(advancement)); // tellTeam is scheduled one tick later @@ -202,7 +202,7 @@ public void testOnAdvancementMemberGrantsAndSchedulesTellTeam() { } @Test - public void testOnAdvancementZeroScoreDoesNotSchedule() { + void testOnAdvancementZeroScoreDoesNotSchedule() { when(advManager.addAdvancement(any(Player.class), any(Advancement.class))).thenReturn(0); listener.onAdvancement(advancementDoneEvent()); verify(sch, never()).runTask(eq(plugin), any(Runnable.class)); @@ -218,25 +218,25 @@ private PlayerPortalEvent portalEvent(TeleportCause cause) { } @Test - public void testOnPortalNetherNoException() { + void testOnPortalNetherNoException() { // With null netherAdvancement fields, giveAdv short-circuits — we just assert no throw. listener.onPortal(portalEvent(TeleportCause.NETHER_PORTAL)); } @Test - public void testOnPortalEndNoException() { + void testOnPortalEndNoException() { listener.onPortal(portalEvent(TeleportCause.END_PORTAL)); } @Test - public void testOnPortalNotSurvival() { + void testOnPortalNotSurvival() { when(player.getGameMode()).thenReturn(GameMode.CREATIVE); // should early return — no NPE even though we don't stub the cause listener.onPortal(portalEvent(TeleportCause.NETHER_PORTAL)); } @Test - public void testOnPortalNotInWorld() { + void testOnPortalNotInWorld() { when(addon.inWorld(world)).thenReturn(false); listener.onPortal(portalEvent(TeleportCause.NETHER_PORTAL)); } @@ -244,21 +244,21 @@ public void testOnPortalNotInWorld() { // ---------- syncAdvancements ---------- @Test - public void testSyncAdvancementsIgnoreSetting() { + void testSyncAdvancementsIgnoreSetting() { settings.setIgnoreAdvancements(true); listener.syncAdvancements(user); verify(user, never()).sendMessage(anyString(), any()); } @Test - public void testSyncAdvancementsNoIsland() { + void testSyncAdvancementsNoIsland() { when(im.getIsland(world, user)).thenReturn(null); listener.syncAdvancements(user); verify(user, never()).sendMessage(anyString(), any()); } @Test - public void testSyncAdvancementsSizeIncreased() { + void testSyncAdvancementsSizeIncreased() { when(advManager.checkIslandSize(island)).thenReturn(3); // Return a non-null IslandAdvancements stub for grantAdv's iteration when(advManager.getIsland(island)) @@ -269,7 +269,7 @@ public void testSyncAdvancementsSizeIncreased() { } @Test - public void testSyncAdvancementsSizeDecreased() { + void testSyncAdvancementsSizeDecreased() { when(advManager.checkIslandSize(island)).thenReturn(-2); when(advManager.getIsland(island)) .thenReturn(mock(world.bentobox.boxed.objects.IslandAdvancements.class)); @@ -280,7 +280,7 @@ public void testSyncAdvancementsSizeDecreased() { // ---------- onPlayerJoin / onPlayerEnterWorld ---------- @Test - public void testOnPlayerJoinNotInWorld() { + void testOnPlayerJoinNotInWorld() { when(addon.inWorld(world)).thenReturn(false); PlayerJoinEvent e = mock(PlayerJoinEvent.class); when(e.getPlayer()).thenReturn(player); @@ -290,7 +290,7 @@ public void testOnPlayerJoinNotInWorld() { } @Test - public void testOnPlayerEnterWorldDifferentWorld() { + void testOnPlayerEnterWorldDifferentWorld() { World otherWorld = mock(World.class); when(otherWorld.getName()).thenReturn("other_world"); when(world.getName()).thenReturn("boxed_world"); @@ -306,7 +306,7 @@ public void testOnPlayerEnterWorldDifferentWorld() { // ---------- onTeamJoinTime / onTeamLeaveTime / onFirstTime ---------- @Test - public void testOnTeamJoinTimeSettingDisabled() { + void testOnTeamJoinTimeSettingDisabled() { // isOnJoinResetAdvancements defaults to false TeamJoinedEvent e = mock(TeamJoinedEvent.class); when(e.getPlayerUUID()).thenReturn(playerUuid); @@ -315,7 +315,7 @@ public void testOnTeamJoinTimeSettingDisabled() { } @Test - public void testOnTeamLeaveTimeIgnoreAdvancements() { + void testOnTeamLeaveTimeIgnoreAdvancements() { settings.setIgnoreAdvancements(true); TeamLeaveEvent e = mock(TeamLeaveEvent.class); listener.onTeamLeaveTime(e); @@ -324,7 +324,7 @@ public void testOnTeamLeaveTimeIgnoreAdvancements() { } @Test - public void testOnFirstTimeIgnoreAdvancements() { + void testOnFirstTimeIgnoreAdvancements() { settings.setIgnoreAdvancements(true); IslandNewIslandEvent e = mock(IslandNewIslandEvent.class); listener.onFirstTime(e); @@ -332,7 +332,7 @@ public void testOnFirstTimeIgnoreAdvancements() { } @Test - public void testOnFirstTimeNotInBoxedWorld() { + void testOnFirstTimeNotInBoxedWorld() { IslandNewIslandEvent e = mock(IslandNewIslandEvent.class); when(e.getIsland()).thenReturn(island); when(island.getWorld()).thenReturn(world); @@ -346,12 +346,12 @@ public void testOnFirstTimeNotInBoxedWorld() { // ---------- static helpers ---------- @Test - public void testGetAdvancementNotFound() { + void testGetAdvancementNotFound() { assertNull(AdvancementListener.getAdvancement("minecraft:story/nonexistent")); } @Test - public void testGetAdvancementFound() { + void testGetAdvancementFound() { Advancement a = mock(Advancement.class); when(a.getKey()).thenReturn(NamespacedKey.fromString("minecraft:story/root")); mockedBukkit.when(Bukkit::advancementIterator).thenAnswer(inv -> List.of(a).iterator()); @@ -359,21 +359,21 @@ public void testGetAdvancementFound() { } @Test - public void testGiveAdvNullAdvancement() { + void testGiveAdvNullAdvancement() { // No throw, no interaction AdvancementListener.giveAdv(player, null); verify(player, never()).getAdvancementProgress(any(Advancement.class)); } @Test - public void testGiveAdvNotDoneAwardsCriteria() { + void testGiveAdvNotDoneAwardsCriteria() { AdvancementListener.giveAdv(player, advancement); verify(progress).awardCriteria("crit1"); verify(progress).awardCriteria("crit2"); } @Test - public void testGiveAdvAlreadyDoneNoOp() { + void testGiveAdvAlreadyDoneNoOp() { when(progress.isDone()).thenReturn(true); AdvancementListener.giveAdv(player, advancement); verify(progress, never()).awardCriteria(anyString()); diff --git a/src/test/java/world/bentobox/boxed/listeners/EnderPearlListenerTest.java b/src/test/java/world/bentobox/boxed/listeners/EnderPearlListenerTest.java index 8a515cc..32002e6 100644 --- a/src/test/java/world/bentobox/boxed/listeners/EnderPearlListenerTest.java +++ b/src/test/java/world/bentobox/boxed/listeners/EnderPearlListenerTest.java @@ -53,7 +53,7 @@ * @author tastybento * */ -public class EnderPearlListenerTest extends CommonTestSetup { +class EnderPearlListenerTest extends CommonTestSetup { @Mock private Boxed addon; @@ -158,7 +158,7 @@ public void tearDown() throws Exception { * Test method for {@link world.bentobox.boxed.listeners.EnderPearlListener#EnderPearlListener(world.bentobox.boxed.Boxed)}. */ @Test - public void testEnderPearlListener() { + void testEnderPearlListener() { assertNotNull(epl); } @@ -166,7 +166,7 @@ public void testEnderPearlListener() { * Test method for {@link world.bentobox.boxed.listeners.EnderPearlListener#onPlayerTeleport(org.bukkit.event.player.PlayerTeleportEvent)}. */ @Test - public void testOnPlayerTeleportNotAllowed() { + void testOnPlayerTeleportNotAllowed() { PlayerTeleportEvent e = new PlayerTeleportEvent(player, from, to, TeleportCause.CHORUS_FRUIT); epl.onPlayerTeleport(e); assertTrue(e.isCancelled()); @@ -177,7 +177,7 @@ public void testOnPlayerTeleportNotAllowed() { * Test method for {@link world.bentobox.boxed.listeners.EnderPearlListener#onPlayerTeleport(org.bukkit.event.player.PlayerTeleportEvent)}. */ @Test - public void testOnPlayerTeleportNotSurvival() { + void testOnPlayerTeleportNotSurvival() { when(player.getGameMode()).thenReturn(GameMode.CREATIVE); PlayerTeleportEvent e = new PlayerTeleportEvent(player, from, to, TeleportCause.CHORUS_FRUIT); epl.onPlayerTeleport(e); @@ -189,7 +189,7 @@ public void testOnPlayerTeleportNotSurvival() { * Test method for {@link world.bentobox.boxed.listeners.EnderPearlListener#onPlayerTeleport(org.bukkit.event.player.PlayerTeleportEvent)}. */ @Test - public void testOnPlayerTeleportNullTo() { + void testOnPlayerTeleportNullTo() { when(player.getGameMode()).thenReturn(GameMode.CREATIVE); PlayerTeleportEvent e = new PlayerTeleportEvent(player, from, null, TeleportCause.CHORUS_FRUIT); epl.onPlayerTeleport(e); @@ -201,7 +201,7 @@ public void testOnPlayerTeleportNullTo() { * Test method for {@link world.bentobox.boxed.listeners.EnderPearlListener#onPlayerTeleport(org.bukkit.event.player.PlayerTeleportEvent)}. */ @Test - public void testOnPlayerTeleportToSpawn() { + void testOnPlayerTeleportToSpawn() { when(spawn.onIsland(any())).thenReturn(true); PlayerTeleportEvent e = new PlayerTeleportEvent(player, from, to, TeleportCause.CHORUS_FRUIT); epl.onPlayerTeleport(e); @@ -213,7 +213,7 @@ public void testOnPlayerTeleportToSpawn() { * Test method for {@link world.bentobox.boxed.listeners.EnderPearlListener#onPlayerTeleport(org.bukkit.event.player.PlayerTeleportEvent)}. */ @Test - public void testOnPlayerTeleportNotInWorldAllowed() { + void testOnPlayerTeleportNotInWorldAllowed() { when(addon.inWorld(any(World.class))).thenReturn(false); when(addon.inWorld(any(Location.class))).thenReturn(false); PlayerTeleportEvent e = new PlayerTeleportEvent(player, from, to, TeleportCause.CHORUS_FRUIT); @@ -227,7 +227,7 @@ public void testOnPlayerTeleportNotInWorldAllowed() { * @throws IOException */ @Test - public void testOnEnderPearlLandNotEnderPearl() throws IOException { + void testOnEnderPearlLandNotEnderPearl() throws IOException { when(projectile.getType()).thenReturn(EntityType.ARROW); ProjectileHitEvent e = new ProjectileHitEvent(projectile, null, hitBlock, BlockFace.UP); epl.onEnderPearlLand(e); @@ -241,7 +241,7 @@ public void testOnEnderPearlLandNotEnderPearl() throws IOException { * @throws IOException */ @Test - public void testOnEnderPearlLandNullHitBlock() throws IOException { + void testOnEnderPearlLandNullHitBlock() throws IOException { ProjectileHitEvent e = new ProjectileHitEvent(projectile, null, null, BlockFace.UP); epl.onEnderPearlLand(e); assertFalse(e.isCancelled()); @@ -254,7 +254,7 @@ public void testOnEnderPearlLandNullHitBlock() throws IOException { * @throws IOException */ @Test - public void testOnEnderPearlLandNotInWorld() throws IOException { + void testOnEnderPearlLandNotInWorld() throws IOException { when(addon.inWorld(to)).thenReturn(false); ProjectileHitEvent e = new ProjectileHitEvent(projectile, null, hitBlock, BlockFace.UP); epl.onEnderPearlLand(e); @@ -268,7 +268,7 @@ public void testOnEnderPearlLandNotInWorld() throws IOException { * @throws IOException */ @Test - public void testOnEnderPearlLandNotMovingBox() throws IOException { + void testOnEnderPearlLandNotMovingBox() throws IOException { Boxed.ALLOW_MOVE_BOX.setSetting(world, false); ProjectileHitEvent e = new ProjectileHitEvent(projectile, null, hitBlock, BlockFace.UP); epl.onEnderPearlLand(e); @@ -282,7 +282,7 @@ public void testOnEnderPearlLandNotMovingBox() throws IOException { * @throws IOException */ @Test - public void testOnEnderPearlLandNonHuman() throws IOException { + void testOnEnderPearlLandNonHuman() throws IOException { Creeper creeper = mock(Creeper.class); when(projectile.getShooter()).thenReturn(creeper); ProjectileHitEvent e = new ProjectileHitEvent(projectile, null, hitBlock, BlockFace.UP); @@ -297,7 +297,7 @@ public void testOnEnderPearlLandNonHuman() throws IOException { * @throws IOException */ @Test - public void testOnEnderPearlLandUserHasNoIsland() throws IOException { + void testOnEnderPearlLandUserHasNoIsland() throws IOException { when(im.getIsland(world, user)).thenReturn(null); ProjectileHitEvent e = new ProjectileHitEvent(projectile, null, hitBlock, BlockFace.UP); epl.onEnderPearlLand(e); @@ -311,7 +311,7 @@ public void testOnEnderPearlLandUserHasNoIsland() throws IOException { * @throws IOException */ @Test - public void testOnEnderPearlNotOnIslandWhenThrowing() throws IOException { + void testOnEnderPearlNotOnIslandWhenThrowing() throws IOException { when(im.getIslandAt(any())).thenReturn(Optional.empty()); ProjectileHitEvent e = new ProjectileHitEvent(projectile, null, hitBlock, BlockFace.UP); epl.onEnderPearlLand(e); @@ -332,7 +332,7 @@ private void verifyFailure() throws IOException { * @throws IOException */ @Test - public void testOnEnderPearlLandHuman() throws IOException { + void testOnEnderPearlLandHuman() throws IOException { ProjectileHitEvent e = new ProjectileHitEvent(projectile, null, hitBlock, BlockFace.UP); epl.onEnderPearlLand(e); assertFalse(e.isCancelled()); @@ -348,7 +348,7 @@ public void testOnEnderPearlLandHuman() throws IOException { * @throws IOException */ @Test - public void testOnEnderPearlThrewToDifferentIsland() { + void testOnEnderPearlThrewToDifferentIsland() { when(im.getIslandAt(eq(to))).thenReturn(Optional.of(anotherIsland)); ProjectileHitEvent e = new ProjectileHitEvent(projectile, null, hitBlock, BlockFace.UP); epl.onEnderPearlLand(e); @@ -361,7 +361,7 @@ public void testOnEnderPearlThrewToDifferentIsland() { * @throws IOException */ @Test - public void testOnEnderPearlThrewToNonIsland() { + void testOnEnderPearlThrewToNonIsland() { when(im.getIslandAt(eq(to))).thenReturn(Optional.empty()); ProjectileHitEvent e = new ProjectileHitEvent(projectile, null, hitBlock, BlockFace.UP); epl.onEnderPearlLand(e); @@ -374,7 +374,7 @@ public void testOnEnderPearlThrewToNonIsland() { * @throws IOException */ @Test - public void testOnEnderPearlCannotSetProtectionCenter() throws IOException { + void testOnEnderPearlCannotSetProtectionCenter() throws IOException { doThrow(IOException.class).when(island).setProtectionCenter(to); ProjectileHitEvent e = new ProjectileHitEvent(projectile, null, hitBlock, BlockFace.UP); epl.onEnderPearlLand(e); From bcde0d290b2a6d7d495e802f6292e884754a74d5 Mon Sep 17 00:00:00 2001 From: tastybento Date: Sat, 30 May 2026 18:41:35 -0700 Subject: [PATCH 05/12] Remove useless Mockito eq() matchers in tests (Sonar java:S6068) Where every argument was wrapped in eq(), pass the raw values directly: addAdvancement() in AdvancementListenerTest and two getIslandAt() stubs in EnderPearlListenerTest. Mixed-matcher calls keep eq() as required. Co-Authored-By: Claude Opus 4.8 --- .../bentobox/boxed/listeners/AdvancementListenerTest.java | 2 +- .../bentobox/boxed/listeners/EnderPearlListenerTest.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/test/java/world/bentobox/boxed/listeners/AdvancementListenerTest.java b/src/test/java/world/bentobox/boxed/listeners/AdvancementListenerTest.java index d91bc3c..e36dc70 100644 --- a/src/test/java/world/bentobox/boxed/listeners/AdvancementListenerTest.java +++ b/src/test/java/world/bentobox/boxed/listeners/AdvancementListenerTest.java @@ -196,7 +196,7 @@ void testOnAdvancementVisitorDenied() { @Test void testOnAdvancementMemberGrantsAndSchedulesTellTeam() { listener.onAdvancement(advancementDoneEvent()); - verify(advManager).addAdvancement(eq(player), eq(advancement)); + verify(advManager).addAdvancement(player, advancement); // tellTeam is scheduled one tick later verify(sch).runTask(eq(plugin), any(Runnable.class)); } diff --git a/src/test/java/world/bentobox/boxed/listeners/EnderPearlListenerTest.java b/src/test/java/world/bentobox/boxed/listeners/EnderPearlListenerTest.java index 32002e6..90ce454 100644 --- a/src/test/java/world/bentobox/boxed/listeners/EnderPearlListenerTest.java +++ b/src/test/java/world/bentobox/boxed/listeners/EnderPearlListenerTest.java @@ -349,7 +349,7 @@ void testOnEnderPearlLandHuman() throws IOException { */ @Test void testOnEnderPearlThrewToDifferentIsland() { - when(im.getIslandAt(eq(to))).thenReturn(Optional.of(anotherIsland)); + when(im.getIslandAt(to)).thenReturn(Optional.of(anotherIsland)); ProjectileHitEvent e = new ProjectileHitEvent(projectile, null, hitBlock, BlockFace.UP); epl.onEnderPearlLand(e); assertTrue(e.isCancelled()); @@ -362,7 +362,7 @@ void testOnEnderPearlThrewToDifferentIsland() { */ @Test void testOnEnderPearlThrewToNonIsland() { - when(im.getIslandAt(eq(to))).thenReturn(Optional.empty()); + when(im.getIslandAt(to)).thenReturn(Optional.empty()); ProjectileHitEvent e = new ProjectileHitEvent(projectile, null, hitBlock, BlockFace.UP); epl.onEnderPearlLand(e); assertTrue(e.isCancelled()); From ea59ac6a65261554201ab5e45481777f910f45f9 Mon Sep 17 00:00:00 2001 From: tastybento Date: Sat, 30 May 2026 18:43:16 -0700 Subject: [PATCH 06/12] Replace lambdas with method references (Sonar java:S1612) - BoxedChunkGenerator: LivingEntity.class::isInstance - NewAreaListener: List::isEmpty in removeIf The third flagged lambda (CommonTestSetup toLegacyText) is left as-is: BaseComponent has both an instance toLegacyText() and a static toLegacyText(BaseComponent...), so a method reference is ambiguous. Co-Authored-By: Claude Opus 4.8 --- .../bentobox/boxed/generators/chunks/BoxedChunkGenerator.java | 2 +- .../java/world/bentobox/boxed/listeners/NewAreaListener.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/world/bentobox/boxed/generators/chunks/BoxedChunkGenerator.java b/src/main/java/world/bentobox/boxed/generators/chunks/BoxedChunkGenerator.java index fd6dd38..296a0f3 100644 --- a/src/main/java/world/bentobox/boxed/generators/chunks/BoxedChunkGenerator.java +++ b/src/main/java/world/bentobox/boxed/generators/chunks/BoxedChunkGenerator.java @@ -73,7 +73,7 @@ protected List getEnts(Chunk chunk) { return this.setEntities(Arrays.stream(chunk.getEntities()) .filter(Objects::nonNull) .filter(e -> !(e instanceof Player)) - .filter(e -> e instanceof LivingEntity) + .filter(LivingEntity.class::isInstance) .map(LivingEntity.class::cast) .toList()); } diff --git a/src/main/java/world/bentobox/boxed/listeners/NewAreaListener.java b/src/main/java/world/bentobox/boxed/listeners/NewAreaListener.java index da71223..ff7a8b1 100644 --- a/src/main/java/world/bentobox/boxed/listeners/NewAreaListener.java +++ b/src/main/java/world/bentobox/boxed/listeners/NewAreaListener.java @@ -367,7 +367,7 @@ public void onIslandDeleted(IslandDeleteEvent event) { for (List records : pending.values()) { records.removeIf(record -> event.getIsland().inIslandSpace(record.location())); } - pending.values().removeIf(list -> list.isEmpty()); + pending.values().removeIf(List::isEmpty); // Remove from pending structures in database Map, List> readyToBuild = loadToDos().getReadyToBuild(); From 979ada003649035d55b8a18bfdee0677b07e6ca5 Mon Sep 17 00:00:00 2001 From: tastybento Date: Sat, 30 May 2026 18:43:45 -0700 Subject: [PATCH 07/12] Use instanceof pattern matching in BoxedChunkGenerator (Sonar java:S6201) Replaces three instanceof-and-cast pairs (Tameable, ChestedHorse, Ageable) with pattern variables. Behaviour is unchanged. Co-Authored-By: Claude Opus 4.8 --- .../boxed/generators/chunks/BoxedChunkGenerator.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/world/bentobox/boxed/generators/chunks/BoxedChunkGenerator.java b/src/main/java/world/bentobox/boxed/generators/chunks/BoxedChunkGenerator.java index 296a0f3..48547ed 100644 --- a/src/main/java/world/bentobox/boxed/generators/chunks/BoxedChunkGenerator.java +++ b/src/main/java/world/bentobox/boxed/generators/chunks/BoxedChunkGenerator.java @@ -98,14 +98,14 @@ private List setEntities(Collection entities) { bpe.setColor(c.getColor()); } } - if (entity instanceof Tameable) { - bpe.setTamed(((Tameable)entity).isTamed()); + if (entity instanceof Tameable tameable) { + bpe.setTamed(tameable.isTamed()); } - if (entity instanceof ChestedHorse) { - bpe.setChest(((ChestedHorse)entity).isCarryingChest()); + if (entity instanceof ChestedHorse chestedHorse) { + bpe.setChest(chestedHorse.isCarryingChest()); } // Only set if child. Most animals are adults - if (entity instanceof Ageable && !((Ageable)entity).isAdult()) { + if (entity instanceof Ageable ageable && !ageable.isAdult()) { bpe.setAdult(false); } if (entity instanceof AbstractHorse horse) { From 3447530bd29dba1c800670ea3f8f657948112365 Mon Sep 17 00:00:00 2001 From: tastybento Date: Sat, 30 May 2026 18:47:25 -0700 Subject: [PATCH 08/12] Fix assorted minor code smells (Sonar S1488/S2293/S116/S100) - AdminPlaceStructureCommand: return the air block state directly (S1488) - NewAreaListener: use the diamond operator for the Pair (S2293) - BoxedJigsawBlock: rename final_state -> finalState and getFinal_state -> getFinalState (S116/S100), pinning the GSON key with @SerializedName so the Minecraft jigsaw 'final_state' data still deserialises. Caller in NewAreaListener updated. Added BoxedJigsawBlockTest covering the mapping. S2637 (line 96) is left as-is: it flags getIslandAt(getLocation()), but Location from getLocation() is @NonNull, so it is a false positive. Co-Authored-By: Claude Opus 4.8 --- .../commands/AdminPlaceStructureCommand.java | 3 +- .../boxed/listeners/NewAreaListener.java | 4 +- .../boxed/objects/BoxedJigsawBlock.java | 12 ++-- .../boxed/objects/BoxedJigsawBlockTest.java | 58 +++++++++++++++++++ 4 files changed, 68 insertions(+), 9 deletions(-) create mode 100644 src/test/java/world/bentobox/boxed/objects/BoxedJigsawBlockTest.java diff --git a/src/main/java/world/bentobox/boxed/commands/AdminPlaceStructureCommand.java b/src/main/java/world/bentobox/boxed/commands/AdminPlaceStructureCommand.java index 4e4d7b2..acc8e58 100644 --- a/src/main/java/world/bentobox/boxed/commands/AdminPlaceStructureCommand.java +++ b/src/main/java/world/bentobox/boxed/commands/AdminPlaceStructureCommand.java @@ -246,8 +246,7 @@ private boolean undoLastPlacement(User user) { if (lastRecord.removedBlocks().containsKey(v)) { return lastRecord.removedBlocks().get(v).createBlockState(); } - BlockState airState = Material.AIR.createBlockData().createBlockState(); - return airState; + return Material.AIR.createBlockData().createBlockState(); }; s.place( diff --git a/src/main/java/world/bentobox/boxed/listeners/NewAreaListener.java b/src/main/java/world/bentobox/boxed/listeners/NewAreaListener.java index ff7a8b1..18577da 100644 --- a/src/main/java/world/bentobox/boxed/listeners/NewAreaListener.java +++ b/src/main/java/world/bentobox/boxed/listeners/NewAreaListener.java @@ -235,7 +235,7 @@ public void onChunkLoad(ChunkLoadEvent e) { if (!(addon.inWorld(chunk.getWorld()))) { return; } - Pair chunkCoords = new Pair(chunk.getX(), chunk.getZ()); + Pair chunkCoords = new Pair<>(chunk.getX(), chunk.getZ()); if (pending.containsKey(chunkCoords)) { Iterator it = pending.get(chunkCoords).iterator(); while (it.hasNext()) { @@ -577,7 +577,7 @@ private static void processJigsaw(Block b, StructureRotation structureRotation, return; } BoxedJigsawBlock bjb = gson.fromJson(data, BoxedJigsawBlock.class); - String finalState = correctDirection(bjb.getFinal_state(), structureRotation); + String finalState = correctDirection(bjb.getFinalState(), structureRotation); BlockData bd = Bukkit.createBlockData(finalState); b.setBlockData(bd); if (!bjb.getPool().equalsIgnoreCase("minecraft:empty") && pasteMobs) { diff --git a/src/main/java/world/bentobox/boxed/objects/BoxedJigsawBlock.java b/src/main/java/world/bentobox/boxed/objects/BoxedJigsawBlock.java index b06e892..48240bc 100644 --- a/src/main/java/world/bentobox/boxed/objects/BoxedJigsawBlock.java +++ b/src/main/java/world/bentobox/boxed/objects/BoxedJigsawBlock.java @@ -1,6 +1,7 @@ package world.bentobox.boxed.objects; import com.google.gson.annotations.Expose; +import com.google.gson.annotations.SerializedName; /** * This replicates a jigsaw block. @@ -10,7 +11,8 @@ public class BoxedJigsawBlock { // final_state:"minecraft:polished_blackstone_bricks",joint:"aligned",name:"minecraft:empty",pool:"minecraft:bastion/bridge/legs",target:"minecraft:leg_connector" @Expose - private String final_state; + @SerializedName("final_state") + private String finalState; @Expose private String joint; @Expose @@ -20,10 +22,10 @@ public class BoxedJigsawBlock { @Expose private String target; /** - * @return the final_state + * @return the finalState */ - public String getFinal_state() { - return final_state; + public String getFinalState() { + return finalState; } /** * @return the joint @@ -51,7 +53,7 @@ public String getTarget() { } @Override public String toString() { - return "BoxedJigsawBlock [" + (final_state != null ? "final_state=" + final_state + ", " : "") + return "BoxedJigsawBlock [" + (finalState != null ? "finalState=" + finalState + ", " : "") + (joint != null ? "joint=" + joint + ", " : "") + (name != null ? "name=" + name + ", " : "") + (pool != null ? "pool=" + pool + ", " : "") + (target != null ? "target=" + target : "") + "]"; } diff --git a/src/test/java/world/bentobox/boxed/objects/BoxedJigsawBlockTest.java b/src/test/java/world/bentobox/boxed/objects/BoxedJigsawBlockTest.java new file mode 100644 index 0000000..cae90f4 --- /dev/null +++ b/src/test/java/world/bentobox/boxed/objects/BoxedJigsawBlockTest.java @@ -0,0 +1,58 @@ +package world.bentobox.boxed.objects; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +import com.google.gson.Gson; + +/** + * Tests {@link BoxedJigsawBlock} GSON (de)serialization. + * + *

The {@code finalState} field is mapped to the Minecraft jigsaw JSON key + * {@code final_state} via {@link com.google.gson.annotations.SerializedName}. + * These tests guard that mapping so the field can keep a Java-friendly name + * without breaking the structure data that {@code NewAreaListener} reads with a + * plain {@code new Gson()}.

+ */ +class BoxedJigsawBlockTest { + + // Production (NewAreaListener) deserialises with a plain Gson instance. + private final Gson gson = new Gson(); + + private static final String JSON = "{\"final_state\":\"minecraft:polished_blackstone_bricks\"," + + "\"joint\":\"aligned\",\"name\":\"minecraft:empty\"," + + "\"pool\":\"minecraft:bastion/bridge/legs\",\"target\":\"minecraft:leg_connector\"}"; + + /** + * The key in the source data is {@code final_state}; it must still populate the + * renamed {@code finalState} field. + */ + @Test + void testDeserialiseFinalStateKey() { + BoxedJigsawBlock bjb = gson.fromJson(JSON, BoxedJigsawBlock.class); + assertEquals("minecraft:polished_blackstone_bricks", bjb.getFinalState()); + assertEquals("aligned", bjb.getJoint()); + assertEquals("minecraft:empty", bjb.getName()); + assertEquals("minecraft:bastion/bridge/legs", bjb.getPool()); + assertEquals("minecraft:leg_connector", bjb.getTarget()); + } + + /** + * Serialisation must emit the {@code final_state} key, not {@code finalState}. + */ + @Test + void testSerialiseUsesFinalStateKey() { + BoxedJigsawBlock bjb = gson.fromJson(JSON, BoxedJigsawBlock.class); + String json = gson.toJson(bjb); + assertTrue(json.contains("\"final_state\""), "Expected final_state key in: " + json); + assertTrue(json.contains("minecraft:polished_blackstone_bricks")); + } + + @Test + void testToStringUsesFinalState() { + BoxedJigsawBlock bjb = gson.fromJson(JSON, BoxedJigsawBlock.class); + assertTrue(bjb.toString().contains("finalState=minecraft:polished_blackstone_bricks")); + } +} From 60b8ecb8af722ce2d3b23b6c4329592295e631db Mon Sep 17 00:00:00 2001 From: tastybento Date: Sun, 31 May 2026 18:15:35 -0700 Subject: [PATCH 09/12] Add tests for data objects (coverage) Covers the previously 0%-covered data objects: - BoxedStructureBlock: GSON deserialisation of all field types + toString - IslandStructures: structure/nether maps, add helpers, lazy null re-init - ToBePlacedStructures: uniqueId, lazy map, StructureRecord accessors/equality - IslandAdvancements: uniqueId and advancements list Co-Authored-By: Claude Opus 4.8 --- .../objects/BoxedStructureBlockTest.java | 79 +++++++++++++++ .../boxed/objects/IslandAdvancementsTest.java | 46 +++++++++ .../boxed/objects/IslandStructuresTest.java | 69 +++++++++++++ .../objects/ToBePlacedStructuresTest.java | 96 +++++++++++++++++++ 4 files changed, 290 insertions(+) create mode 100644 src/test/java/world/bentobox/boxed/objects/BoxedStructureBlockTest.java create mode 100644 src/test/java/world/bentobox/boxed/objects/IslandAdvancementsTest.java create mode 100644 src/test/java/world/bentobox/boxed/objects/IslandStructuresTest.java create mode 100644 src/test/java/world/bentobox/boxed/objects/ToBePlacedStructuresTest.java diff --git a/src/test/java/world/bentobox/boxed/objects/BoxedStructureBlockTest.java b/src/test/java/world/bentobox/boxed/objects/BoxedStructureBlockTest.java new file mode 100644 index 0000000..f9250c9 --- /dev/null +++ b/src/test/java/world/bentobox/boxed/objects/BoxedStructureBlockTest.java @@ -0,0 +1,79 @@ +package world.bentobox.boxed.objects; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.bukkit.block.data.type.StructureBlock.Mode; +import org.bukkit.block.structure.Mirror; +import org.bukkit.block.structure.StructureRotation; +import org.junit.jupiter.api.Test; + +import com.google.gson.Gson; + +/** + * Tests {@link BoxedStructureBlock} GSON deserialization (this is how the class + * is populated from captured structure-block data) and its {@code toString()}. + */ +class BoxedStructureBlockTest { + + // NewAreaListener reads structure data with a plain Gson instance. + private final Gson gson = new Gson(); + + private static final String JSON = "{\"author\":\"LadyAgnes\",\"ignoreEntities\":true,\"integrity\":1.0," + + "\"metadata\":\"drowned\",\"mirror\":\"NONE\",\"mode\":\"DATA\",\"name\":\"house\"," + + "\"posX\":3,\"posY\":1,\"posZ\":-7,\"powered\":false,\"rotation\":\"CLOCKWISE_90\"," + + "\"seed\":\"0\",\"showair\":false,\"showboundingbox\":true," + + "\"sizeX\":5,\"sizeY\":6,\"sizeZ\":7}"; + + private BoxedStructureBlock deserialise() { + return gson.fromJson(JSON, BoxedStructureBlock.class); + } + + @Test + void testStringFields() { + BoxedStructureBlock b = deserialise(); + assertEquals("LadyAgnes", b.getAuthor()); + assertEquals("drowned", b.getMetadata()); + assertEquals("house", b.getName()); + assertEquals("0", b.getSeed()); + } + + @Test + void testEnumFields() { + BoxedStructureBlock b = deserialise(); + assertEquals(Mirror.NONE, b.getMirror()); + assertEquals(Mode.DATA, b.getMode()); + assertEquals(StructureRotation.CLOCKWISE_90, b.getRotation()); + } + + @Test + void testNumericFields() { + BoxedStructureBlock b = deserialise(); + assertEquals(1.0f, b.getIntegrity()); + assertEquals(3, b.getPosX()); + assertEquals(1, b.getPosY()); + assertEquals(-7, b.getPosZ()); + assertEquals(5, b.getSizeX()); + assertEquals(6, b.getSizeY()); + assertEquals(7, b.getSizeZ()); + } + + @Test + void testBooleanFields() { + BoxedStructureBlock b = deserialise(); + assertTrue(b.isIgnoreEntities()); + assertFalse(b.isPowered()); + assertFalse(b.isShowair()); + assertTrue(b.isShowboundingbox()); + } + + @Test + void testToString() { + String s = deserialise().toString(); + assertTrue(s.startsWith("BoxedStructureBlock [")); + assertTrue(s.contains("author=LadyAgnes")); + assertTrue(s.contains("mode=DATA")); + assertTrue(s.contains("sizeZ=7")); + } +} diff --git a/src/test/java/world/bentobox/boxed/objects/IslandAdvancementsTest.java b/src/test/java/world/bentobox/boxed/objects/IslandAdvancementsTest.java new file mode 100644 index 0000000..32edd27 --- /dev/null +++ b/src/test/java/world/bentobox/boxed/objects/IslandAdvancementsTest.java @@ -0,0 +1,46 @@ +package world.bentobox.boxed.objects; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.List; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +/** + * Tests {@link IslandAdvancements} - the per-island list of earned advancements. + */ +class IslandAdvancementsTest { + + private IslandAdvancements ia; + + @BeforeEach + void setUp() { + ia = new IslandAdvancements("island-1"); + } + + @Test + void testConstructorSetsUniqueId() { + assertEquals("island-1", ia.getUniqueId()); + } + + @Test + void testSetUniqueId() { + ia.setUniqueId("island-2"); + assertEquals("island-2", ia.getUniqueId()); + } + + @Test + void testAdvancementsStartEmpty() { + assertTrue(ia.getAdvancements().isEmpty()); + } + + @Test + void testSetAndGetAdvancements() { + List advs = List.of("minecraft:story/mine_stone", "minecraft:story/upgrade_tools"); + ia.setAdvancements(advs); + assertEquals(2, ia.getAdvancements().size()); + assertTrue(ia.getAdvancements().contains("minecraft:story/mine_stone")); + } +} diff --git a/src/test/java/world/bentobox/boxed/objects/IslandStructuresTest.java b/src/test/java/world/bentobox/boxed/objects/IslandStructuresTest.java new file mode 100644 index 0000000..fad5d06 --- /dev/null +++ b/src/test/java/world/bentobox/boxed/objects/IslandStructuresTest.java @@ -0,0 +1,69 @@ +package world.bentobox.boxed.objects; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.bukkit.util.BoundingBox; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +/** + * Tests {@link IslandStructures} - the per-island record of placed structures. + */ +class IslandStructuresTest { + + private IslandStructures is; + + @BeforeEach + void setUp() { + is = new IslandStructures("island-123"); + } + + @Test + void testConstructorSetsUniqueId() { + assertEquals("island-123", is.getUniqueId()); + } + + @Test + void testSetUniqueId() { + is.setUniqueId("other"); + assertEquals("other", is.getUniqueId()); + } + + @Test + void testMapsStartEmpty() { + assertTrue(is.getStructureBoundingBoxMap().isEmpty()); + assertTrue(is.getNetherStructureBoundingBoxMap().isEmpty()); + } + + @Test + void testAddStructure() { + BoundingBox bb = new BoundingBox(0, 0, 0, 16, 16, 16); + is.addStructure(bb, "minecraft:village"); + assertEquals(1, is.getStructureBoundingBoxMap().size()); + assertEquals("minecraft:village", is.getStructureBoundingBoxMap().get(bb)); + // The nether map is untouched + assertTrue(is.getNetherStructureBoundingBoxMap().isEmpty()); + } + + @Test + void testAddNetherStructure() { + BoundingBox bb = new BoundingBox(0, 0, 0, 8, 8, 8); + is.addNetherStructure(bb, "minecraft:fortress"); + assertEquals(1, is.getNetherStructureBoundingBoxMap().size()); + assertEquals("minecraft:fortress", is.getNetherStructureBoundingBoxMap().get(bb)); + assertTrue(is.getStructureBoundingBoxMap().isEmpty()); + } + + @Test + void testGettersLazilyRecreateNullMaps() { + is.setStructureBoundingBoxMap(null); + is.setNetherStructureBoundingBoxMap(null); + assertTrue(is.getStructureBoundingBoxMap().isEmpty()); + assertTrue(is.getNetherStructureBoundingBoxMap().isEmpty()); + // And adding after a null reset still works + BoundingBox bb = new BoundingBox(0, 0, 0, 1, 1, 1); + is.addStructure(bb, "minecraft:shipwreck"); + assertEquals("minecraft:shipwreck", is.getStructureBoundingBoxMap().get(bb)); + } +} diff --git a/src/test/java/world/bentobox/boxed/objects/ToBePlacedStructuresTest.java b/src/test/java/world/bentobox/boxed/objects/ToBePlacedStructuresTest.java new file mode 100644 index 0000000..b768110 --- /dev/null +++ b/src/test/java/world/bentobox/boxed/objects/ToBePlacedStructuresTest.java @@ -0,0 +1,96 @@ +package world.bentobox.boxed.objects; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.bukkit.Location; +import org.bukkit.block.structure.Mirror; +import org.bukkit.block.structure.StructureRotation; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import world.bentobox.bentobox.util.Pair; +import world.bentobox.boxed.objects.ToBePlacedStructures.StructureRecord; + +/** + * Tests {@link ToBePlacedStructures} and its {@link StructureRecord} component. + */ +class ToBePlacedStructuresTest { + + private ToBePlacedStructures tbps; + + @BeforeEach + void setUp() { + tbps = new ToBePlacedStructures(); + } + + @Test + void testDefaultUniqueId() { + assertEquals("ToDo", tbps.getUniqueId()); + } + + @Test + void testSetUniqueId() { + tbps.setUniqueId("changed"); + assertEquals("changed", tbps.getUniqueId()); + } + + @Test + void testReadyToBuildStartsEmpty() { + assertTrue(tbps.getReadyToBuild().isEmpty()); + } + + @Test + void testGetterLazilyRecreatesNullMap() { + tbps.setReadyToBuild(null); + assertTrue(tbps.getReadyToBuild().isEmpty()); + } + + @Test + void testSetAndGetReadyToBuild() { + Map, List> map = new HashMap<>(); + StructureRecord rec = new StructureRecord("village", "minecraft:village", + new Location(null, 1, 2, 3), StructureRotation.NONE, Mirror.NONE, false, new HashMap<>()); + map.put(new Pair<>(0, 0), new ArrayList<>(List.of(rec))); + tbps.setReadyToBuild(map); + assertSame(map, tbps.getReadyToBuild()); + assertEquals(1, tbps.getReadyToBuild().get(new Pair<>(0, 0)).size()); + } + + @Test + void testStructureRecordAccessors() { + Location loc = new Location(null, 10, 64, -20); + Map removed = new HashMap<>(); + StructureRecord rec = new StructureRecord("name", "minecraft:igloo", loc, + StructureRotation.CLOCKWISE_90, Mirror.LEFT_RIGHT, true, removed); + assertEquals("name", rec.name()); + assertEquals("minecraft:igloo", rec.structure()); + assertSame(loc, rec.location()); + assertEquals(StructureRotation.CLOCKWISE_90, rec.rot()); + assertEquals(Mirror.LEFT_RIGHT, rec.mirror()); + assertTrue(rec.noMobs()); + assertSame(removed, rec.removedBlocks()); + } + + @Test + void testStructureRecordEqualityAndToString() { + Location loc = new Location(null, 0, 0, 0); + Map removed = new HashMap<>(); + StructureRecord a = new StructureRecord("n", "s", loc, StructureRotation.NONE, Mirror.NONE, false, removed); + StructureRecord b = new StructureRecord("n", "s", loc, StructureRotation.NONE, Mirror.NONE, false, removed); + StructureRecord different = new StructureRecord("other", "s", loc, StructureRotation.NONE, Mirror.NONE, false, + removed); + // Records get value-based equals/hashCode for free + assertEquals(a, b); + assertEquals(a.hashCode(), b.hashCode()); + assertNotEquals(a, different); + assertTrue(a.toString().contains("name=n")); + } +} From 2e18a7eb70f4d648e70d6b78c98ad0097008bc14 Mon Sep 17 00:00:00 2001 From: tastybento Date: Sun, 31 May 2026 18:18:49 -0700 Subject: [PATCH 10/12] Add tests for chunk-coordinate wrapping and biome copying (coverage) - RepeatCalcTest: covers AbstractBoxedChunkGenerator.repeatCalc, the function that tiles the captured seed region across the game world (identity within [-size,size), wrapping, range invariant, varied sizes). - CopyBiomeProviderTest: covers AbstractCopyBiomeProvider.getBiome via the overworld BoxedBiomeGenerator - stored biome lookup, default fallback, missing-chunk warning, and coordinate wrapping into the seed region. Co-Authored-By: Claude Opus 4.8 --- .../biomes/CopyBiomeProviderTest.java | 96 +++++++++++++++++++ .../generators/chunks/RepeatCalcTest.java | 67 +++++++++++++ 2 files changed, 163 insertions(+) create mode 100644 src/test/java/world/bentobox/boxed/generators/biomes/CopyBiomeProviderTest.java create mode 100644 src/test/java/world/bentobox/boxed/generators/chunks/RepeatCalcTest.java diff --git a/src/test/java/world/bentobox/boxed/generators/biomes/CopyBiomeProviderTest.java b/src/test/java/world/bentobox/boxed/generators/biomes/CopyBiomeProviderTest.java new file mode 100644 index 0000000..2217520 --- /dev/null +++ b/src/test/java/world/bentobox/boxed/generators/biomes/CopyBiomeProviderTest.java @@ -0,0 +1,96 @@ +package world.bentobox.boxed.generators.biomes; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.HashMap; +import java.util.Map; + +import org.bukkit.World.Environment; +import org.bukkit.block.Biome; +import org.bukkit.generator.WorldInfo; +import org.bukkit.util.Vector; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import world.bentobox.boxed.Boxed; +import world.bentobox.boxed.CommonTestSetup; +import world.bentobox.boxed.Settings; +import world.bentobox.boxed.WhiteBox; +import world.bentobox.boxed.generators.chunks.AbstractBoxedChunkGenerator; +import world.bentobox.boxed.generators.chunks.AbstractBoxedChunkGenerator.ChunkStore; + +/** + * Tests {@link AbstractCopyBiomeProvider#getBiome(WorldInfo, int, int, int)} via the + * concrete overworld {@link BoxedBiomeGenerator}. The provider looks up the biome that + * was captured from the seed world for the (wrapped) chunk and the in-chunk position. + */ +class CopyBiomeProviderTest extends CommonTestSetup { + + private BoxedBiomeGenerator gen; + private AbstractBoxedChunkGenerator chunkGen; + private WorldInfo worldInfo; + private Boxed addon; + + @BeforeEach + public void setUpProvider() { + addon = mock(Boxed.class); + Settings settings = mock(Settings.class); + when(addon.getSettings()).thenReturn(settings); + when(settings.getIslandDistance()).thenReturn(400); + + chunkGen = mock(AbstractBoxedChunkGenerator.class); + when(addon.getChunkGenerator(Environment.NORMAL)).thenReturn(chunkGen); + + worldInfo = mock(WorldInfo.class); + when(worldInfo.getEnvironment()).thenReturn(Environment.NORMAL); + + // 400 / 16 = 25 chunks half-width. repeatCalc needs size > 0. + WhiteBox.setInternalState(AbstractBoxedChunkGenerator.class, "size", 25); + + gen = new BoxedBiomeGenerator(addon); + } + + @Test + void testReturnsStoredBiome() { + // x=3,z=5 -> chunk 0,0 ; in-chunk position (3, y, 5) + Map biomes = new HashMap<>(); + biomes.put(new Vector(3, 64, 5), Biome.PLAINS); + when(chunkGen.getChunk(0, 0)).thenReturn(new ChunkStore(null, null, null, biomes)); + + assertEquals(Biome.PLAINS, gen.getBiome(worldInfo, 3, 64, 5)); + } + + @Test + void testReturnsDefaultBiomeWhenPositionNotStored() { + // Chunk exists but the requested position is not in the map -> default (OCEAN) + when(chunkGen.getChunk(0, 0)).thenReturn(new ChunkStore(null, null, null, new HashMap<>())); + + assertEquals(Biome.OCEAN, gen.getBiome(worldInfo, 3, 64, 5)); + } + + @Test + void testReturnsDefaultBiomeAndWarnsWhenChunkMissing() { + when(chunkGen.getChunk(0, 0)).thenReturn(null); + + assertEquals(Biome.OCEAN, gen.getBiome(worldInfo, 3, 64, 5)); + // The missing snapshot is logged as a warning + verify(plugin).logWarning(eq("Snapshot at 0 0 is not stored")); + } + + @Test + void testCoordinatesAreWrappedIntoSeedRegion() { + // x in a far chunk still resolves back into the stored region. + // x = 3 + 16*25 = 403 -> chunkX 25 -> repeatCalc(25)= -25 ... but getChunk is + // stubbed on the wrapped coord, so verify the wrapped lookup is used. + Map biomes = new HashMap<>(); + biomes.put(new Vector(3, 64, 5), Biome.DESERT); + // 403 >> 4 = 25 ; repeatCalc(25) with size 25 = floorMod(50,50)-25 = -25 + when(chunkGen.getChunk(-25, 0)).thenReturn(new ChunkStore(null, null, null, biomes)); + + assertEquals(Biome.DESERT, gen.getBiome(worldInfo, 403, 64, 5)); + } +} diff --git a/src/test/java/world/bentobox/boxed/generators/chunks/RepeatCalcTest.java b/src/test/java/world/bentobox/boxed/generators/chunks/RepeatCalcTest.java new file mode 100644 index 0000000..a665aa7 --- /dev/null +++ b/src/test/java/world/bentobox/boxed/generators/chunks/RepeatCalcTest.java @@ -0,0 +1,67 @@ +package world.bentobox.boxed.generators.chunks; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +import world.bentobox.boxed.WhiteBox; + +/** + * Tests {@link AbstractBoxedChunkGenerator#repeatCalc(int)} - the function that + * maps an arbitrary chunk coordinate back into the repeating seed region + * {@code [-size, size)}. This is what makes the small captured seed area tile + * infinitely across the game world. + */ +class RepeatCalcTest { + + private void setSize(int size) { + WhiteBox.setInternalState(AbstractBoxedChunkGenerator.class, "size", size); + } + + @Test + void testIdentityWithinRange() { + setSize(5); + // Coordinates already inside [-size, size) are returned unchanged + for (int c = -5; c < 5; c++) { + assertEquals(c, AbstractBoxedChunkGenerator.repeatCalc(c), "coord " + c); + } + } + + @Test + void testWrapsAboveRange() { + setSize(5); + // size maps back to -size, and it keeps wrapping with period 2*size + assertEquals(-5, AbstractBoxedChunkGenerator.repeatCalc(5)); + assertEquals(-4, AbstractBoxedChunkGenerator.repeatCalc(6)); + assertEquals(0, AbstractBoxedChunkGenerator.repeatCalc(10)); + assertEquals(4, AbstractBoxedChunkGenerator.repeatCalc(14)); + assertEquals(-1, AbstractBoxedChunkGenerator.repeatCalc(19)); + } + + @Test + void testWrapsBelowRange() { + setSize(5); + assertEquals(-5, AbstractBoxedChunkGenerator.repeatCalc(-5)); + assertEquals(0, AbstractBoxedChunkGenerator.repeatCalc(-10)); + assertEquals(-1, AbstractBoxedChunkGenerator.repeatCalc(-11)); + } + + @Test + void testResultAlwaysWithinRange() { + setSize(8); + for (int c = -100; c <= 100; c++) { + int r = AbstractBoxedChunkGenerator.repeatCalc(c); + assertEquals(true, r >= -8 && r < 8, "coord " + c + " mapped out of range to " + r); + } + } + + @Test + void testDifferentSize() { + setSize(1); + // With size 1 the region is just {-1, 0} + assertEquals(0, AbstractBoxedChunkGenerator.repeatCalc(0)); + assertEquals(-1, AbstractBoxedChunkGenerator.repeatCalc(-1)); + assertEquals(-1, AbstractBoxedChunkGenerator.repeatCalc(1)); + assertEquals(0, AbstractBoxedChunkGenerator.repeatCalc(2)); + } +} From d23bda8160c4066f33ed645124d11df1d182ceaf Mon Sep 17 00:00:00 2001 From: tastybento Date: Sun, 31 May 2026 18:22:24 -0700 Subject: [PATCH 11/12] Add tests for AdminPlaceStructureCommand (coverage) Covers the previously 0%-covered admin /place command: - canExecute argument validation: undo shortcut, wrong-world, unknown structure, coordinate parsing (~, integers, non-integers), rotation and mirror parsing, and the size>6 rejection guard. - tabComplete suggestions for each argument position. - setup() permission/label/only-player. Documents that the 7-arg NO_MOBS form is unreachable (the size>6 guard rejects it before the NO_MOBS block) by asserting the actual behaviour. Co-Authored-By: Claude Opus 4.8 --- .../AdminPlaceStructureCommandTest.java | 173 ++++++++++++++++++ 1 file changed, 173 insertions(+) create mode 100644 src/test/java/world/bentobox/boxed/commands/AdminPlaceStructureCommandTest.java diff --git a/src/test/java/world/bentobox/boxed/commands/AdminPlaceStructureCommandTest.java b/src/test/java/world/bentobox/boxed/commands/AdminPlaceStructureCommandTest.java new file mode 100644 index 0000000..6be8442 --- /dev/null +++ b/src/test/java/world/bentobox/boxed/commands/AdminPlaceStructureCommandTest.java @@ -0,0 +1,173 @@ +package world.bentobox.boxed.commands; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import org.bukkit.Bukkit; +import org.bukkit.NamespacedKey; +import org.bukkit.structure.Structure; +import org.bukkit.structure.StructureManager; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import world.bentobox.bentobox.api.commands.CompositeCommand; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.boxed.Boxed; +import world.bentobox.boxed.CommonTestSetup; + +/** + * Tests the argument validation ({@code canExecute}) and tab completion of + * {@link AdminPlaceStructureCommand}. + */ +class AdminPlaceStructureCommandTest extends CommonTestSetup { + + private AdminPlaceStructureCommand cmd; + private Boxed addon; + private User user; + + @BeforeEach + public void setUpCommand() { + addon = mock(Boxed.class); + + CompositeCommand parent = mock(CompositeCommand.class); + when(parent.getAddon()).thenReturn(addon); + when(parent.getTopLabel()).thenReturn("boxadmin"); + when(parent.getPermissionPrefix()).thenReturn(""); + when(parent.getWorld()).thenReturn(world); + when(parent.getSubCommands()).thenReturn(new HashMap<>()); + + cmd = new AdminPlaceStructureCommand(parent); + + // In the Boxed world by default + when(addon.inWorld(world)).thenReturn(true); + + // One known structure called "igloo" + StructureManager sm = Bukkit.getStructureManager(); + Map structures = new HashMap<>(); + structures.put(NamespacedKey.minecraft("igloo"), mock(Structure.class)); + when(sm.getStructures()).thenReturn(structures); + + user = mock(User.class); + when(user.getLocation()).thenReturn(location); + } + + @Test + void testSetup() { + assertEquals("place", cmd.getLabel()); + assertEquals("boxed.commands.boxadmin.place", cmd.getPermission()); + assertFalse(cmd.isOnlyPlayer()); + } + + @Test + void testUndoAlwaysAllowed() { + assertTrue(cmd.canExecute(user, "place", List.of("undo"))); + } + + @Test + void testWrongWorld() { + when(addon.inWorld(world)).thenReturn(false); + assertFalse(cmd.canExecute(user, "place", List.of("igloo"))); + verify(user).sendMessage("boxed.commands.boxadmin.place.wrong-world"); + } + + @Test + void testUnknownStructure() { + assertFalse(cmd.canExecute(user, "place", List.of("mansion"))); + verify(user).sendMessage("boxed.commands.boxadmin.place.unknown-structure"); + } + + @Test + void testStructureNameOnly() { + assertTrue(cmd.canExecute(user, "place", List.of("igloo"))); + } + + @Test + void testNonIntegerCoordinates() { + assertFalse(cmd.canExecute(user, "place", List.of("igloo", "x", "0", "0"))); + verify(user).sendMessage("boxed.commands.boxadmin.place.use-integers"); + } + + @Test + void testTildeCoordinates() { + assertTrue(cmd.canExecute(user, "place", List.of("igloo", "~", "~", "~"))); + } + + @Test + void testIntegerCoordinates() { + assertTrue(cmd.canExecute(user, "place", List.of("igloo", "10", "64", "-20"))); + } + + @Test + void testUnknownRotation() { + assertFalse(cmd.canExecute(user, "place", List.of("igloo", "~", "~", "~", "SPIN"))); + verify(user).sendMessage("boxed.commands.boxadmin.place.unknown-rotation"); + } + + @Test + void testValidRotation() { + assertTrue(cmd.canExecute(user, "place", List.of("igloo", "~", "~", "~", "CLOCKWISE_90"))); + } + + @Test + void testUnknownMirror() { + assertFalse(cmd.canExecute(user, "place", List.of("igloo", "~", "~", "~", "NONE", "FLIP"))); + verify(user).sendMessage("boxed.commands.boxadmin.place.unknown-mirror"); + } + + @Test + void testValidMirror() { + assertTrue(cmd.canExecute(user, "place", List.of("igloo", "~", "~", "~", "NONE", "LEFT_RIGHT"))); + } + + @Test + void testTooManyArgsRejected() { + // The size > 6 guard rejects a 7th argument (the NO_MOBS form is unreachable). + assertFalse(cmd.canExecute(user, "place", List.of("igloo", "~", "~", "~", "NONE", "LEFT_RIGHT", "NO_MOBS"))); + } + + @Test + void testTabCompleteFirstArgOffersUndo() { + Optional> opt = cmd.tabComplete(user, "place", List.of("")); + assertTrue(opt.isPresent()); + assertTrue(opt.get().contains("undo")); + } + + @Test + void testTabCompleteSecondArgOffersStructures() { + Optional> opt = cmd.tabComplete(user, "place", List.of("place", "")); + assertTrue(opt.isPresent()); + assertTrue(opt.get().contains("igloo")); + } + + @Test + void testTabCompleteRotation() { + Optional> opt = cmd.tabComplete(user, "place", List.of("place", "igloo", "~", "~", "~", "")); + assertTrue(opt.isPresent()); + assertTrue(opt.get().contains("CLOCKWISE_90")); + } + + @Test + void testTabCompleteNoMobs() { + Optional> opt = cmd.tabComplete(user, "place", + List.of("place", "igloo", "~", "~", "~", "NONE", "LEFT_RIGHT", "")); + assertTrue(opt.isPresent()); + assertEquals(List.of("NO_MOBS"), opt.get()); + } + + @Test + void testNeverMessagesWhenValid() { + cmd.canExecute(user, "place", List.of("igloo")); + verify(user, never()).sendMessage(anyString()); + } +} From 9e16faa0e48ea1553b8736cba049bbe82abbe415 Mon Sep 17 00:00:00 2001 From: tastybento Date: Sun, 31 May 2026 18:58:27 -0700 Subject: [PATCH 12/12] Enable the NO_MOBS option for /boxadmin place The 7-argument NO_MOBS form was unreachable: the argument-count guard rejected any 7th argument before the NO_MOBS branch could run, so admins could not place a structure with its built-in mobs suppressed. - Relax the guard from size > 6 to size > 7 so the NO_MOBS argument is accepted (the downstream place()/processJigsaw pipeline already honours the flag via structures.yml). - Reset the noMobs field at the start of canExecute alongside rotation and mirror so the setting does not leak into subsequent placements. Tests updated: NO_MOBS is now accepted, an unknown 7th arg is rejected with the 'unknown' message, and 8 args is the new over-limit case. Co-Authored-By: Claude Opus 4.8 --- .../commands/AdminPlaceStructureCommand.java | 3 ++- .../AdminPlaceStructureCommandTest.java | 17 +++++++++++++++-- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/main/java/world/bentobox/boxed/commands/AdminPlaceStructureCommand.java b/src/main/java/world/bentobox/boxed/commands/AdminPlaceStructureCommand.java index acc8e58..a6a492e 100644 --- a/src/main/java/world/bentobox/boxed/commands/AdminPlaceStructureCommand.java +++ b/src/main/java/world/bentobox/boxed/commands/AdminPlaceStructureCommand.java @@ -85,6 +85,7 @@ public boolean canExecute(User user, String label, List args) { // Initialize sr = StructureRotation.NONE; mirror = Mirror.NONE; + noMobs = false; // Check world if (!((Boxed) getAddon()).inWorld(getWorld())) { @@ -100,7 +101,7 @@ public boolean canExecute(User user, String label, List args) { * 6. place ~ ~ ~ ROTATION MIRROR * 7. place ~ ~ ~ ROTATION MIRROR NO_MOBS */ - if (args.isEmpty() || args.size() == 2 || args.size() == 3 || args.size() > 6) { + if (args.isEmpty() || args.size() == 2 || args.size() == 3 || args.size() > 7) { this.showHelp(this, user); return false; } diff --git a/src/test/java/world/bentobox/boxed/commands/AdminPlaceStructureCommandTest.java b/src/test/java/world/bentobox/boxed/commands/AdminPlaceStructureCommandTest.java index 6be8442..1a64b38 100644 --- a/src/test/java/world/bentobox/boxed/commands/AdminPlaceStructureCommandTest.java +++ b/src/test/java/world/bentobox/boxed/commands/AdminPlaceStructureCommandTest.java @@ -4,6 +4,7 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; @@ -130,10 +131,22 @@ void testValidMirror() { assertTrue(cmd.canExecute(user, "place", List.of("igloo", "~", "~", "~", "NONE", "LEFT_RIGHT"))); } + @Test + void testValidNoMobs() { + assertTrue(cmd.canExecute(user, "place", List.of("igloo", "~", "~", "~", "NONE", "LEFT_RIGHT", "NO_MOBS"))); + } + + @Test + void testUnknownTrailingArg() { + assertFalse(cmd.canExecute(user, "place", List.of("igloo", "~", "~", "~", "NONE", "LEFT_RIGHT", "MAYBE"))); + verify(user).sendMessage(eq("boxed.commands.boxadmin.place.unknown"), anyString(), anyString()); + } + @Test void testTooManyArgsRejected() { - // The size > 6 guard rejects a 7th argument (the NO_MOBS form is unreachable). - assertFalse(cmd.canExecute(user, "place", List.of("igloo", "~", "~", "~", "NONE", "LEFT_RIGHT", "NO_MOBS"))); + // The size > 7 guard rejects an 8th argument. + assertFalse(cmd.canExecute(user, "place", + List.of("igloo", "~", "~", "~", "NONE", "LEFT_RIGHT", "NO_MOBS", "EXTRA"))); } @Test