diff --git a/.gitignore b/.gitignore index 5e2fb46cf..b31942ce5 100644 --- a/.gitignore +++ b/.gitignore @@ -84,4 +84,5 @@ dist/ nbdist/ nbactions.xml nb-configuration.xml -.nb-gradle/ \ No newline at end of file +.nb-gradle/ +/BentoBox/ diff --git a/src/main/java/world/bentobox/bentobox/util/teleport/SafeSpotTeleport.java b/src/main/java/world/bentobox/bentobox/util/teleport/SafeSpotTeleport.java index a41dcbfd6..3963b8b4c 100644 --- a/src/main/java/world/bentobox/bentobox/util/teleport/SafeSpotTeleport.java +++ b/src/main/java/world/bentobox/bentobox/util/teleport/SafeSpotTeleport.java @@ -71,7 +71,7 @@ public class SafeSpotTeleport { Util.getChunkAtAsync(location).thenRun(() -> tryToGo(builder.getFailureMessage())); } - private void tryToGo(String failureMessage) { + void tryToGo(String failureMessage) { if (plugin.getIslands().isSafeLocation(location)) { if (portal) { // If the desired location is safe, then that's where you'll go if there's no portal @@ -92,15 +92,16 @@ public class SafeSpotTeleport { task = Bukkit.getScheduler().runTaskTimer(plugin, () -> gatherChunks(failureMessage), 0L, SPEED); } - private void gatherChunks(String failureMessage) { + boolean gatherChunks(String failureMessage) { + // Set a flag so this is only run if it's not already in progress if (checking.get()) { - return; + return false; } checking.set(true); if (checkedChunks > MAX_CHUNKS || !chunksToScanIterator.hasNext()) { // Nothing left tidyUp(entity, failureMessage); - return; + return false; } // Get the chunk @@ -109,22 +110,23 @@ public class SafeSpotTeleport { checkedChunks++; if (checkedChunks >= MAX_CHUNKS) { checking.set(false); - return; + return false; } // Get the chunk snapshot and scan it Util.getChunkAtAsync(world, chunkPair.x, chunkPair.z) - .thenApply(Chunk::getChunkSnapshot) - .whenCompleteAsync((snapshot, e) -> { - if (snapshot != null && scanChunk(snapshot)) { - task.cancel(); - } else { - checking.set(false); - } - }); + .thenApply(Chunk::getChunkSnapshot) + .whenCompleteAsync((snapshot, e) -> { + if (snapshot != null && scanChunk(snapshot)) { + task.cancel(); + } else { + checking.set(false); + } + }); + return true; } - private void tidyUp(Entity entity, String failureMessage) { + void tidyUp(Entity entity, String failureMessage) { // Still Async! // Nothing left to check and still not canceled task.cancel(); @@ -165,7 +167,7 @@ public class SafeSpotTeleport { } } - private void makeAndTeleport(Material m) { + void makeAndTeleport(Material m) { location.getBlock().getRelative(BlockFace.DOWN).setType(m, false); location.getBlock().setType(Material.AIR, false); location.getBlock().getRelative(BlockFace.UP).setType(Material.AIR, false); @@ -181,7 +183,7 @@ public class SafeSpotTeleport { * * @return - list of chunk coords to be scanned */ - private List> getChunksToScan() { + List> getChunksToScan() { List> chunksToScan = new ArrayList<>(); int maxRadius = plugin.getIslands().getIslandAt(location).map(Island::getProtectionRange).orElseGet(() -> plugin.getIWM().getIslandProtectionRange(world)); maxRadius = Math.min(MAX_RADIUS, maxRadius); @@ -210,7 +212,7 @@ public class SafeSpotTeleport { * @param chunk - chunk snapshot * @return true if a safe spot was found */ - private boolean scanChunk(ChunkSnapshot chunk) { + boolean scanChunk(ChunkSnapshot chunk) { int startY = location.getBlockY(); int minY = world.getMinHeight(); int maxY = 60; // Just a dummy value @@ -265,7 +267,7 @@ public class SafeSpotTeleport { /** * Teleports entity to the safe spot */ - private void teleportEntity(final Location loc) { + void teleportEntity(final Location loc) { task.cancel(); // Return to main thread and teleport the player Bukkit.getScheduler().runTask(plugin, () -> { @@ -303,7 +305,7 @@ public class SafeSpotTeleport { return false; } - private boolean safe(ChunkSnapshot chunk, int x, int y, int z, World world) { + boolean safe(ChunkSnapshot chunk, int x, int y, int z, World world) { Vector newSpot = new Vector((chunk.getX() << 4) + x + 0.5D, y + 1.0D, (chunk.getZ() << 4) + z + 0.5D); if (portal) { if (bestSpot == null) { diff --git a/src/test/java/world/bentobox/bentobox/util/teleport/SafeSpotTeleportBuilderTest.java b/src/test/java/world/bentobox/bentobox/util/teleport/SafeSpotTeleportBuilderTest.java deleted file mode 100644 index cd425159e..000000000 --- a/src/test/java/world/bentobox/bentobox/util/teleport/SafeSpotTeleportBuilderTest.java +++ /dev/null @@ -1,149 +0,0 @@ -package world.bentobox.bentobox.util.teleport; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import java.util.Optional; - -import org.bukkit.Location; -import org.bukkit.entity.Player; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.Mockito; -import org.powermock.api.mockito.PowerMockito; -import org.powermock.core.classloader.annotations.PrepareForTest; -import org.powermock.modules.junit4.PowerMockRunner; - -import world.bentobox.bentobox.BentoBox; -import world.bentobox.bentobox.api.user.User; -import world.bentobox.bentobox.database.objects.Island; -import world.bentobox.bentobox.managers.IslandWorldManager; -import world.bentobox.bentobox.managers.LocalesManager; - -@RunWith(PowerMockRunner.class) -@PrepareForTest(SafeSpotTeleport.Builder.class) -public class SafeSpotTeleportBuilderTest { - - @Mock - private SafeSpotTeleport sst; - @Mock - private BentoBox plugin; - @Mock - private Player player; - @Mock - private Location loc; - - @InjectMocks - private SafeSpotTeleport.Builder sstb; - - @Before - public void setUp() throws Exception { - PowerMockito.whenNew(SafeSpotTeleport.class).withAnyArguments().thenReturn(sst); - // Users - User.setPlugin(plugin); - // Locales - final - LocalesManager lm = mock(LocalesManager.class); - when(plugin.getLocalesManager()).thenReturn(lm); - when(lm.get(any(), any())).thenReturn("mock translation"); - // Addon - IslandWorldManager iwm = mock(IslandWorldManager.class); - when(iwm.getAddon(Mockito.any())).thenReturn(Optional.empty()); - when(plugin.getIWM()).thenReturn(iwm); - - } - - @After - public void tearDown() { - Mockito.framework().clearInlineMocks(); - } - - @Test - public void testBuilder() { - sstb = new SafeSpotTeleport.Builder(plugin); - // Should fail because no data - assertNull(sstb.build()); - } - - @Test - public void testEntity() throws Exception { - // Start builder - sstb = new SafeSpotTeleport.Builder(plugin); - // Add entity - sstb.entity(player); - // Test for error - assertNull(sstb.build()); - // Add location - sstb.location(loc); - // Build - expect success - SafeSpotTeleport result = sstb.build(); - assertEquals(sst, result); - } - - @Test - public void testIsland() { - // Start builder - SafeSpotTeleport.Builder sstb = new SafeSpotTeleport.Builder(plugin); - // Add entity - sstb.entity(player); - // Add island - Island island = mock(Island.class); - when(island.getProtectionCenter()).thenReturn(loc); - sstb.island(island); - // Build - expect success - SafeSpotTeleport result = sstb.build(); - assertEquals(sst, result); - } - - @Test - public void testHomeNumber() { - // Start builder - SafeSpotTeleport.Builder sstb = new SafeSpotTeleport.Builder(plugin); - // Add entity - sstb.entity(player); - // Add location - sstb.location(loc); - // Add home - sstb.homeName("my name"); - // Build - expect success - SafeSpotTeleport result = sstb.build(); - assertEquals(sst, result); - - } - - @Test - public void testPortal() { - // Start builder - SafeSpotTeleport.Builder sstb = new SafeSpotTeleport.Builder(plugin); - // Add entity - sstb.entity(player); - // Add location - sstb.location(loc); - // Portal - sstb.portal(); - // Build - expect success - SafeSpotTeleport result = sstb.build(); - assertEquals(sst, result); - } - - @Test - public void testFailureMessage() { - // Start builder - SafeSpotTeleport.Builder sstb = new SafeSpotTeleport.Builder(plugin); - // Add entity - sstb.entity(player); - // Add location - sstb.location(loc); - // Add failure - sstb.failureMessage("testing 123"); - // Build - expect success - SafeSpotTeleport result = sstb.build(); - assertEquals(sst, result); - } -} diff --git a/src/test/java/world/bentobox/bentobox/util/teleport/SafeSpotTeleportTest.java b/src/test/java/world/bentobox/bentobox/util/teleport/SafeSpotTeleportTest.java new file mode 100644 index 000000000..412dab93d --- /dev/null +++ b/src/test/java/world/bentobox/bentobox/util/teleport/SafeSpotTeleportTest.java @@ -0,0 +1,281 @@ +package world.bentobox.bentobox.util.teleport; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.List; +import java.util.Optional; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; + +import org.bukkit.Bukkit; +import org.bukkit.Chunk; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.entity.Entity; +import org.bukkit.scheduler.BukkitScheduler; +import org.bukkit.scheduler.BukkitTask; +import org.eclipse.jdt.annotation.NonNull; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.reflect.Whitebox; + +import world.bentobox.bentobox.BentoBox; +import world.bentobox.bentobox.database.objects.Island; +import world.bentobox.bentobox.managers.IslandWorldManager; +import world.bentobox.bentobox.managers.IslandsManager; +import world.bentobox.bentobox.util.Pair; +import world.bentobox.bentobox.util.Util; + +/** + * Test class for safe teleporting + * @author tastybento + * + */ +@RunWith(PowerMockRunner.class) +@PrepareForTest({Util.class, Bukkit.class}) +public class SafeSpotTeleportTest { + + // Class under test + private SafeSpotTeleport sst; + + @Mock + private SafeSpotTeleport.Builder builder; + @Mock + private BentoBox plugin; + @Mock + private Location location; + @Mock + private World world; + @Mock + private Entity entity; + + private boolean portal; + + private int num; + + private String name; + @Mock + private Runnable runnable; + @Mock + private Runnable failRunnable; + @Mock + private CompletableFuture result; + @Mock + private @NonNull CompletableFuture cfChunk; + @Mock + private IslandsManager im; + @Mock + private BukkitScheduler scheduler; + + private Island island; + @Mock + private IslandWorldManager iwm; + + @Mock + private BukkitTask task; + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + // Setup instance + Whitebox.setInternalState(BentoBox.class, "instance", plugin); + // IWM + when(iwm.getIslandProtectionRange(any())).thenReturn(100); + when(iwm.getIslandDistance(any())).thenReturn(400); + when(plugin.getIWM()).thenReturn(iwm); + + // Mock static Util + PowerMockito.mockStatic(Util.class, Mockito.RETURNS_MOCKS); + when(Util.getChunkAtAsync(any(Location.class))).thenReturn(cfChunk); + // Same world + when(Util.sameWorld(any(), any())).thenReturn(true); + // Set up a mock builder + when(builder.getPlugin()).thenReturn(plugin); + when(builder.getEntity()).thenReturn(entity); + when(builder.getLocation()).thenReturn(location); + when(builder.isPortal()).thenReturn(portal); + when(builder.getHomeNumber()).thenReturn(num); + when(builder.getHomeName()).thenReturn(name); + when(builder.getRunnable()).thenReturn(runnable); + when(builder.getFailRunnable()).thenReturn(failRunnable); + when(builder.getResult()).thenReturn(result); + // Set the default world + when(location.getWorld()).thenReturn(world); + + // Island + island = new Island(location, UUID.randomUUID(), 50); + + // Plugin Island Manager + // Default that locations are safe + when(im.isSafeLocation(any(Location.class))).thenReturn(true); + // Provide an island + when(im.getIslandAt(any(Location.class))).thenReturn(Optional.of(island)); + + + when(plugin.getIslands()).thenReturn(im); + + // Bukkit scheduler + when(scheduler.runTaskTimer(eq(plugin), any(Runnable.class), anyLong(), anyLong())).thenReturn(task); + PowerMockito.mockStatic(Bukkit.class, Mockito.RETURNS_MOCKS); + when(Bukkit.getScheduler()).thenReturn(scheduler); + } + + /** + * @throws java.lang.Exception + */ + @After + public void tearDown() throws Exception { + } + + /** + * Test method for {@link world.bentobox.bentobox.util.teleport.SafeSpotTeleport#SafeSpotTeleport(world.bentobox.bentobox.util.teleport.SafeSpotTeleport.Builder)}. + */ + @Test(expected = AssertionError.class) + public void testSafeSpotTeleportNullWorld() { + when(location.getWorld()).thenReturn(null); + sst = new SafeSpotTeleport(builder); + } + + /** + * Test method for {@link world.bentobox.bentobox.util.teleport.SafeSpotTeleport#SafeSpotTeleport(world.bentobox.bentobox.util.teleport.SafeSpotTeleport.Builder)}. + */ + @Test + public void testSafeSpotTeleport() { + sst = new SafeSpotTeleport(builder); + verify(cfChunk).thenRun(any(Runnable.class)); + } + + /** + * Test method for {@link world.bentobox.bentobox.util.teleport.SafeSpotTeleport#tryToGo(java.lang.String)}. + */ + @Test + public void testTryToGoSafeNotPortal() { + portal = false; + testSafeSpotTeleport(); + sst.tryToGo("failure message"); + PowerMockito.verifyStatic(Util.class); + // Verify that the teleport is done immediately + Util.teleportAsync(entity, location); + + } + + /** + * Test method for {@link world.bentobox.bentobox.util.teleport.SafeSpotTeleport#tryToGo(java.lang.String)}. + */ + @Test + public void testTryToGoUnsafe() { + when(im.isSafeLocation(any(Location.class))).thenReturn(false); + // Set up fields + testSafeSpotTeleport(); + sst.tryToGo("failure message"); + verify(scheduler).runTaskTimer(eq(plugin), any(Runnable.class), eq(0L), eq(1L)); + } + + /** + * Test method for {@link world.bentobox.bentobox.util.teleport.SafeSpotTeleport#gatherChunks(java.lang.String)}. + */ + @Test + public void testGatherChunks() { + // Setup fields + testTryToGoUnsafe(); + // run test + assertTrue(sst.gatherChunks("failure message")); + PowerMockito.verifyStatic(Util.class); + Util.getChunkAtAsync(eq(world), anyInt(), anyInt()); + // run test again - should be blocked because of atomic boolean + assertFalse(sst.gatherChunks("failure message")); + } + + /** + * Test method for {@link world.bentobox.bentobox.util.teleport.SafeSpotTeleport#tidyUp(org.bukkit.entity.Entity, java.lang.String)}. + */ + @Test + public void testTidyUpNoPlayerFailRunnable() { + when(im.isSafeLocation(any(Location.class))).thenReturn(false); + sst = new SafeSpotTeleport(builder); + sst.tryToGo("failure message"); + sst.tidyUp(entity, "failure note"); + verify(task).cancel(); + verify(scheduler).runTask(plugin, failRunnable); + } + + /** + * Test method for {@link world.bentobox.bentobox.util.teleport.SafeSpotTeleport#tidyUp(org.bukkit.entity.Entity, java.lang.String)}. + */ + @Test + public void testTidyUpPlayer() { + when(im.isSafeLocation(any(Location.class))).thenReturn(false); + sst = new SafeSpotTeleport(builder); + sst.tryToGo("failure message"); + sst.tidyUp(entity, "failure note"); + verify(task).cancel(); + verify(scheduler).runTask(plugin, failRunnable); + } + + /** + * Test method for {@link world.bentobox.bentobox.util.teleport.SafeSpotTeleport#makeAndTeleport(org.bukkit.Material)}. + */ + @Test + public void testMakeAndTeleport() { + //fail("Not yet implemented"); // TODO + } + + /** + * Test method for {@link world.bentobox.bentobox.util.teleport.SafeSpotTeleport#getChunksToScan()}. + */ + @Test + public void testGetChunksToScan() { + testSafeSpotTeleport(); + List> pairs = sst.getChunksToScan(); + assertEquals(62, pairs.size()); + } + + /** + * Test method for {@link world.bentobox.bentobox.util.teleport.SafeSpotTeleport#scanChunk(org.bukkit.ChunkSnapshot)}. + */ + @Test + public void testScanChunk() { + //fail("Not yet implemented"); // TODO + } + + /** + * Test method for {@link world.bentobox.bentobox.util.teleport.SafeSpotTeleport#teleportEntity(org.bukkit.Location)}. + */ + @Test + public void testTeleportEntity() { + //fail("Not yet implemented"); // TODO + } + + /** + * Test method for {@link world.bentobox.bentobox.util.teleport.SafeSpotTeleport#checkBlock(org.bukkit.ChunkSnapshot, int, int, int)}. + */ + @Test + public void testCheckBlock() { + //fail("Not yet implemented"); // TODO + } + + /** + * Test method for {@link world.bentobox.bentobox.util.teleport.SafeSpotTeleport#safe(org.bukkit.ChunkSnapshot, int, int, int, org.bukkit.World)}. + */ + @Test + public void testSafe() { + //fail("Not yet implemented"); // TODO + } + +}