diff --git a/src/main/java/world/bentobox/level/Level.java b/src/main/java/world/bentobox/level/Level.java index 4710e11..a744427 100644 --- a/src/main/java/world/bentobox/level/Level.java +++ b/src/main/java/world/bentobox/level/Level.java @@ -6,6 +6,7 @@ import java.util.Map; import java.util.UUID; import org.bukkit.World; +import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; import world.bentobox.bentobox.api.addons.Addon; @@ -55,16 +56,17 @@ public class Level extends Addon { * @param user - the user who is asking, or null if none * @param playerUUID - the target island member's UUID */ - public void calculateIslandLevel(World world, @Nullable User user, UUID playerUUID) { + public void calculateIslandLevel(World world, @Nullable User user, @NonNull UUID playerUUID) { levelPresenter.calculateIslandLevel(world, user, playerUUID); } /** - * Get level from cache for a player - * @param targetPlayer - target player - * @return Level of player + * Get level from cache for a player. + * @param targetPlayer - target player UUID + * @return Level of player or zero if player is unknown or UUID is null */ - public long getIslandLevel(World world, UUID targetPlayer) { + public long getIslandLevel(World world, @Nullable UUID targetPlayer) { + if (targetPlayer == null) return 0L; LevelsData ld = getLevelsData(targetPlayer); return ld == null ? 0L : ld.getLevel(world); } @@ -74,7 +76,7 @@ public class Level extends Addon { * @param targetPlayer - UUID of target player * @return LevelsData object or null if not found */ - public LevelsData getLevelsData(UUID targetPlayer) { + public LevelsData getLevelsData(@NonNull UUID targetPlayer) { // Get from database if not in cache if (!levelsCache.containsKey(targetPlayer) && handler.objectExists(targetPlayer.toString())) { levelsCache.put(targetPlayer, handler.loadObject(targetPlayer.toString())); @@ -163,9 +165,9 @@ public class Level extends Addon { getPlugin().getPlaceholdersManager().registerPlaceholder(this, gm.getDescription().getName().toLowerCase() + "_visited_island_level", user -> getPlugin().getIslands().getIslandAt(user.getLocation()) - .map(island -> getIslandLevel(gm.getOverWorld(), island.getOwner())) - .map(level -> getLevelPresenter().getLevelString(level)) - .orElse("0")); + .map(island -> getIslandLevel(gm.getOverWorld(), island.getOwner())) + .map(level -> getLevelPresenter().getLevelString(level)) + .orElse("0")); // Top Ten for (int i = 1; i <= 10; i++) { @@ -236,7 +238,7 @@ public class Level extends Addon { * @param island - island * @param level - initial calculated island level */ - public void setInitialIslandLevel(Island island, long level) { + public void setInitialIslandLevel(@NonNull Island island, long level) { if (island.getWorld() == null || island.getOwner() == null) { this.logError("Level: request to store a null (initial) " + island.getWorld() + " " + island.getOwner()); return; @@ -250,15 +252,19 @@ public class Level extends Addon { * @param island - island * @return level or 0 by default */ - public long getInitialIslandLevel(Island island) { + public long getInitialIslandLevel(@NonNull Island island) { return levelsCache.containsKey(island.getOwner()) ? levelsCache.get(island.getOwner()).getInitialLevel(island.getWorld()) : 0L; } + /** + * @return database handler + */ + @Nullable public Database getHandler() { return handler; } - public void uncachePlayer(UUID uniqueId) { + public void uncachePlayer(@Nullable UUID uniqueId) { if (levelsCache.containsKey(uniqueId) && levelsCache.get(uniqueId) != null) { handler.saveObject(levelsCache.get(uniqueId)); } diff --git a/src/test/java/world/bentobox/level/LevelTest.java b/src/test/java/world/bentobox/level/LevelTest.java new file mode 100644 index 0000000..d2d9353 --- /dev/null +++ b/src/test/java/world/bentobox/level/LevelTest.java @@ -0,0 +1,371 @@ +package world.bentobox.level; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.mockito.ArgumentMatchers.any; +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.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Collections; +import java.util.Comparator; +import java.util.Optional; +import java.util.UUID; +import java.util.jar.JarEntry; +import java.util.jar.JarOutputStream; +import java.util.logging.Logger; + +import org.bukkit.Bukkit; +import org.bukkit.Server; +import org.bukkit.UnsafeValues; +import org.bukkit.World; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemFactory; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.plugin.PluginManager; +import org.bukkit.scheduler.BukkitScheduler; +import org.eclipse.jdt.annotation.NonNull; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.stubbing.Answer; +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.Settings; +import world.bentobox.bentobox.api.addons.AddonDescription; +import world.bentobox.bentobox.api.addons.GameModeAddon; +import world.bentobox.bentobox.api.commands.CompositeCommand; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.bentobox.database.DatabaseSetup.DatabaseType; +import world.bentobox.bentobox.database.objects.Island; +import world.bentobox.bentobox.managers.AddonsManager; +import world.bentobox.bentobox.managers.CommandsManager; +import world.bentobox.bentobox.managers.FlagsManager; +import world.bentobox.bentobox.managers.IslandWorldManager; +import world.bentobox.bentobox.managers.IslandsManager; +import world.bentobox.bentobox.managers.PlaceholdersManager; +import world.bentobox.level.listeners.IslandTeamListeners; +import world.bentobox.level.listeners.JoinLeaveListener; + +/** + * @author tastybento + * + */ +@SuppressWarnings("deprecation") +@RunWith(PowerMockRunner.class) +@PrepareForTest({Bukkit.class, BentoBox.class, User.class}) +public class LevelTest { + + private static File jFile; + @Mock + private User user; + @Mock + private IslandsManager im; + @Mock + private Island island; + @Mock + private BentoBox plugin; + @Mock + private FlagsManager fm; + @Mock + private GameModeAddon gameMode; + @Mock + private AddonsManager am; + @Mock + private BukkitScheduler scheduler; + @Mock + private Settings settings; + + private Level addon; + + @Mock + private Logger logger; + @Mock + private PlaceholdersManager phm; + @Mock + private CompositeCommand cmd; + @Mock + private CompositeCommand adminCmd; + @Mock + private World world; + private UUID uuid; + + @BeforeClass + public static void beforeClass() throws IOException { + jFile = new File("addon.jar"); + // Copy over config file from src folder + Path fromPath = Paths.get("src/main/resources/config.yml"); + Path path = Paths.get("config.yml"); + Files.copy(fromPath, path); + try (JarOutputStream tempJarOutputStream = new JarOutputStream(new FileOutputStream(jFile))) { + //Added the new files to the jar. + try (FileInputStream fis = new FileInputStream(path.toFile())) { + byte[] buffer = new byte[1024]; + int bytesRead = 0; + JarEntry entry = new JarEntry(path.toString()); + tempJarOutputStream.putNextEntry(entry); + while((bytesRead = fis.read(buffer)) != -1) { + tempJarOutputStream.write(buffer, 0, bytesRead); + } + } + } + } + + /** + * @throws java.lang.Exception + */ + @SuppressWarnings("deprecation") + @Before + public void setUp() throws Exception { + // Set up plugin + Whitebox.setInternalState(BentoBox.class, "instance", plugin); + when(plugin.getLogger()).thenReturn(Logger.getAnonymousLogger()); + //when(plugin.isEnabled()).thenReturn(true); + // Command manager + CommandsManager cm = mock(CommandsManager.class); + when(plugin.getCommandsManager()).thenReturn(cm); + + // Player + Player p = mock(Player.class); + // Sometimes use Mockito.withSettings().verboseLogging() + when(user.isOp()).thenReturn(false); + uuid = UUID.randomUUID(); + when(user.getUniqueId()).thenReturn(uuid); + when(user.getPlayer()).thenReturn(p); + when(user.getName()).thenReturn("tastybento"); + User.setPlugin(plugin); + + // Island World Manager + IslandWorldManager iwm = mock(IslandWorldManager.class); + when(plugin.getIWM()).thenReturn(iwm); + + + // Player has island to begin with + when(im.getIsland(Mockito.any(), Mockito.any(UUID.class))).thenReturn(island); + when(plugin.getIslands()).thenReturn(im); + + // Locales + // Return the reference (USE THIS IN THE FUTURE) + when(user.getTranslation(Mockito.anyString())).thenAnswer((Answer) invocation -> invocation.getArgument(0, String.class)); + + // Server + PowerMockito.mockStatic(Bukkit.class); + Server server = mock(Server.class); + when(Bukkit.getServer()).thenReturn(server); + when(Bukkit.getLogger()).thenReturn(Logger.getAnonymousLogger()); + when(Bukkit.getPluginManager()).thenReturn(mock(PluginManager.class)); + + // Addon + addon = new Level(); + File dataFolder = new File("addons/Level"); + addon.setDataFolder(dataFolder); + addon.setFile(jFile); + AddonDescription desc = new AddonDescription.Builder("bentobox", "Level", "1.3").description("test").authors("tastybento").build(); + addon.setDescription(desc); + // Addons manager + when(plugin.getAddonsManager()).thenReturn(am); + // One game mode + when(am.getGameModeAddons()).thenReturn(Collections.singletonList(gameMode)); + AddonDescription desc2 = new AddonDescription.Builder("bentobox", "BSkyBlock", "1.3").description("test").authors("tasty").build(); + when(gameMode.getDescription()).thenReturn(desc2); + + // Player command + @NonNull + Optional opCmd = Optional.of(cmd); + when(gameMode.getPlayerCommand()).thenReturn(opCmd); + // Admin command + Optional opAdminCmd = Optional.of(adminCmd); + when(gameMode.getAdminCommand()).thenReturn(opAdminCmd); + + // Flags manager + when(plugin.getFlagsManager()).thenReturn(fm); + when(fm.getFlags()).thenReturn(Collections.emptyList()); + + // The database type has to be created one line before the thenReturn() to work! + when(plugin.getSettings()).thenReturn(settings); + DatabaseType value = DatabaseType.JSON; + when(settings.getDatabaseType()).thenReturn(value); + + // Bukkit + PowerMockito.mockStatic(Bukkit.class); + when(Bukkit.getScheduler()).thenReturn(scheduler); + ItemMeta meta = mock(ItemMeta.class); + ItemFactory itemFactory = mock(ItemFactory.class); + when(itemFactory.getItemMeta(any())).thenReturn(meta); + when(Bukkit.getItemFactory()).thenReturn(itemFactory); + UnsafeValues unsafe = mock(UnsafeValues.class); + when(unsafe.getDataVersion()).thenReturn(777); + when(Bukkit.getUnsafe()).thenReturn(unsafe); + + // placeholders + when(plugin.getPlaceholdersManager()).thenReturn(phm); + + // World + when(world.getName()).thenReturn("bskyblock-world"); + // Island + when(island.getWorld()).thenReturn(world); + when(island.getOwner()).thenReturn(uuid); + } + + /** + * @throws java.lang.Exception + */ + @After + public void tearDown() throws Exception { + deleteAll(new File("database")); + } + + @AfterClass + public static void cleanUp() throws Exception { + new File("addon.jar").delete(); + new File("config.yml").delete(); + deleteAll(new File("addons")); + } + + private static void deleteAll(File file) throws IOException { + if (file.exists()) { + Files.walk(file.toPath()) + .sorted(Comparator.reverseOrder()) + .map(Path::toFile) + .forEach(File::delete); + } + } + + /** + * Test method for {@link world.bentobox.level.Level#onEnable()}. + */ + @Test + public void testOnEnable() { + addon.onEnable(); + verify(plugin).logWarning("[Level] Level Addon: No such world in config.yml : acidisland_world"); + verify(plugin).log("[Level] Level hooking into BSkyBlock"); + verify(cmd, times(3)).getAddon(); // Three commands + verify(adminCmd, times(2)).getAddon(); // Two commands + // Placeholders + verify(phm).registerPlaceholder(eq(addon), eq("bskyblock-island-level"), any()); + for (int i = 1; i < 11; i++) { + verify(phm).registerPlaceholder(eq(addon), eq("bskyblock-island-level-top-name-" + i), any()); + verify(phm).registerPlaceholder(eq(addon), eq("bskyblock-island-level-top-value-" + i), any()); + } + verify(phm).registerPlaceholder(eq(addon), eq("bskyblock_island_level"), any()); + verify(phm).registerPlaceholder(eq(addon), eq("bskyblock_visited_island_level"), any()); + for (int i = 1; i < 11; i++) { + verify(phm).registerPlaceholder(eq(addon), eq("bskyblock_top_name_" + i), any()); + verify(phm).registerPlaceholder(eq(addon), eq("bskyblock_top_value_" + i), any()); + } + // Commands + verify(am).registerListener(eq(addon), any(IslandTeamListeners.class)); + verify(am).registerListener(eq(addon), any(JoinLeaveListener.class)); + } + + /** + * Test method for {@link world.bentobox.level.Level#getIslandLevel(org.bukkit.World, java.util.UUID)}. + */ + @Test + public void testGetIslandLevelUnknown() { + addon.onEnable(); + assertEquals(0L, addon.getIslandLevel(world, UUID.randomUUID())); + } + + /** + * Test method for {@link world.bentobox.level.Level#getIslandLevel(org.bukkit.World, java.util.UUID)}. + */ + @Test + public void testGetIslandLevelNullTarget() { + addon.onEnable(); + assertEquals(0L, addon.getIslandLevel(world, UUID.randomUUID())); + + } + + /** + * Test method for {@link world.bentobox.level.Level#getLevelsData(java.util.UUID)}. + */ + @Test + public void testGetLevelsDataUnknown() { + addon.onEnable(); + assertNull(addon.getLevelsData(UUID.randomUUID())); + } + + /** + * Test method for {@link world.bentobox.level.Level#getSettings()}. + */ + @Test + public void testGetSettings() { + addon.onEnable(); + world.bentobox.level.config.Settings s = addon.getSettings(); + assertEquals(100, s.getDeathPenalty()); + } + + /** + * Test method for {@link world.bentobox.level.Level#getTopTen()}. + */ + @Test + public void testGetTopTen() { + addon.onEnable(); + assertNotNull(addon.getTopTen()); + } + + /** + * Test method for {@link world.bentobox.level.Level#getLevelPresenter()}. + */ + @Test + public void testGetLevelPresenter() { + addon.onEnable(); + assertNotNull(addon.getLevelPresenter()); + } + + /** + * Test method for {@link world.bentobox.level.Level#setIslandLevel(org.bukkit.World, java.util.UUID, long)}. + */ + @Test + public void testSetIslandLevel() { + addon.onEnable(); + addon.setIslandLevel(world, uuid, 345L); + assertEquals(345L, addon.getIslandLevel(world, uuid)); + verify(plugin, never()).logError(anyString()); + } + + /** + * Test method for {@link world.bentobox.level.Level#getInitialIslandLevel(world.bentobox.bentobox.database.objects.Island)}. + */ + @Test + public void testGetInitialIslandLevel() { + addon.onEnable(); + addon.setInitialIslandLevel(island, 40); + verify(plugin, never()).logError(anyString()); + assertEquals(40, addon.getInitialIslandLevel(island)); + + } + + /** + * Test method for {@link world.bentobox.level.Level#getHandler()}. + */ + @Test + public void testGetHandler() { + addon.onEnable(); + assertNotNull(addon.getHandler()); + } + + +}