diff --git a/.gitignore b/.gitignore index 1deb298..3d09ebd 100644 --- a/.gitignore +++ b/.gitignore @@ -130,4 +130,5 @@ local.properties .springBeans /target/ checkstyle.xml -classes/ \ No newline at end of file +classes/ +/.DS_Store diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..a7b17e0 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,21 @@ +language: java +sudo: false +addons: + sonarcloud: + organization: "bentobox-world" + token: + secure: $SONAR_TOKEN + branches: + - develop + - master +jdk: + - openjdk8 + +script: + #- sonar-scanner + - mvn clean org.jacoco:jacoco-maven-plugin:prepare-agent package -P sonar sonar:sonar -B + #- echo "${TRAVIS_PULL_REQUEST_BRANCH:-$TRAVIS_BRANCH}" +cache: + directories: + - '$HOME/.m2/repository' + - '$HOME/.sonar/cache' diff --git a/pom.xml b/pom.xml index 6ca353c..2cba69d 100644 --- a/pom.xml +++ b/pom.xml @@ -33,7 +33,7 @@ UTF-8 UTF-8 1.8 - 1.7.4 + 2.0.2 1.14.4-R0.1-SNAPSHOT 1.7.0 @@ -129,24 +129,25 @@ ${spigot.version} provided - - org.mockito - mockito-all - 1.10.19 - test - - - org.powermock - powermock-module-junit4 - ${powermock.version} - test - - - org.powermock - powermock-api-mockito - ${powermock.version} - test - + + + org.mockito + mockito-core + 3.0.0 + test + + + org.powermock + powermock-module-junit4 + ${powermock.version} + test + + + org.powermock + powermock-api-mockito2 + ${powermock.version} + test + world.bentobox bentobox diff --git a/src/main/java/world/bentobox/challenges/ChallengesAddon.java b/src/main/java/world/bentobox/challenges/ChallengesAddon.java index 8a5ef3a..452f265 100644 --- a/src/main/java/world/bentobox/challenges/ChallengesAddon.java +++ b/src/main/java/world/bentobox/challenges/ChallengesAddon.java @@ -5,6 +5,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Optional; +import org.bukkit.Bukkit; import org.bukkit.Material; import world.bentobox.bentobox.api.addons.Addon; @@ -88,7 +89,7 @@ public class ChallengesAddon extends Addon { * This flag allows to complete challenges in any part of the world. It will not limit * player to their island. Useful for skygrid without protection flags. */ - public static Flag CHALLENGES_WORLD_PROTECTION = + public static final Flag CHALLENGES_WORLD_PROTECTION = new Flag.Builder("CHALLENGES_WORLD_PROTECTION", Material.GRASS_BLOCK).type(Flag.Type.WORLD_SETTING).defaultSetting(true).build(); /** @@ -96,7 +97,7 @@ public class ChallengesAddon extends Addon { * that only Island owner can complete challenge. * By default it is set to Visitor. */ - public static Flag CHALLENGES_ISLAND_PROTECTION = + public static final Flag CHALLENGES_ISLAND_PROTECTION = new Flag.Builder("CHALLENGES_ISLAND_PROTECTION", Material.COMMAND_BLOCK).defaultRank(RanksManager.VISITOR_RANK).build(); @@ -155,7 +156,11 @@ public class ChallengesAddon extends Addon { List hookedGameModes = new ArrayList<>(); this.getPlugin().getAddonsManager().getGameModeAddons().forEach(gameModeAddon -> { - if (!this.settings.getDisabledGameModes().contains(gameModeAddon.getDescription().getName())) + if (!this.settings + .getDisabledGameModes() + .contains(gameModeAddon + .getDescription() + .getName())) { if (gameModeAddon.getPlayerCommand().isPresent()) { @@ -238,7 +243,7 @@ public class ChallengesAddon extends Addon { if (this.settings.getAutoSaveTimer() > 0) { - this.getPlugin().getServer().getScheduler().runTaskTimerAsynchronously( + Bukkit.getScheduler().runTaskTimerAsynchronously( this.getPlugin(), bukkitTask -> ChallengesAddon.this.challengesManager.save(), this.settings.getAutoSaveTimer() * 60 * 20, @@ -264,7 +269,7 @@ public class ChallengesAddon extends Addon { { this.loadSettings(); this.challengesManager.reload(); - this.getLogger().info("Challenges addon reloaded."); + this.log("Challenges addon reloaded."); } } diff --git a/src/main/java/world/bentobox/challenges/ChallengesImportManager.java b/src/main/java/world/bentobox/challenges/ChallengesImportManager.java index dfec22d..e0feb65 100644 --- a/src/main/java/world/bentobox/challenges/ChallengesImportManager.java +++ b/src/main/java/world/bentobox/challenges/ChallengesImportManager.java @@ -43,14 +43,14 @@ public class ChallengesImportManager * @param challengesAddon */ public ChallengesImportManager(ChallengesAddon challengesAddon) - { + { this.addon = challengesAddon; } -// --------------------------------------------------------------------- -// Section: Default Challenge Loader -// --------------------------------------------------------------------- + // --------------------------------------------------------------------- + // Section: Default Challenge Loader + // --------------------------------------------------------------------- /** @@ -79,45 +79,46 @@ public class ChallengesImportManager } // default configuration should be removed. - // user made configuration should not!. + // user made configuration should not!. boolean removeAtEnd = - !Files.exists(Paths.get(this.addon.getDataFolder().getPath() + "/default.json")); + !Files.exists(Paths.get(this.addon.getDataFolder().getPath() + "/default.json")); // Safe json configuration to Challenges folder. - this.addon.saveResource("default.json", false); + this.addon.saveResource("default.json", false); try { - // This prefix will be used to all challenges. That is a unique way how to separate challenged for - // each game mode. - String uniqueIDPrefix = Utils.getGameMode(world) + "_"; - DefaultDataHolder defaultChallenges = new DefaultJSONHandler(this.addon).loadObject(); + // This prefix will be used to all challenges. That is a unique way how to separate challenged for + // each game mode. + String uniqueIDPrefix = Utils.getGameMode(world) + "_"; + DefaultDataHolder defaultChallenges = new DefaultJSONHandler(this.addon).loadObject(); + if (defaultChallenges != null) { + // All new challenges should get correct ID. So we need to map it to loaded challenges. + defaultChallenges.getChallengeList().forEach(challenge -> { + // Set correct challenge ID + challenge.setUniqueId(uniqueIDPrefix + challenge.getUniqueId()); + // Set up correct level ID if it is necessary + if (!challenge.getLevel().isEmpty()) + { + challenge.setLevel(uniqueIDPrefix + challenge.getLevel()); + } + // Load challenge in memory + manager.loadChallenge(challenge, false, user, user == null); + }); - // All new challenges should get correct ID. So we need to map it to loaded challenges. - defaultChallenges.getChallengeList().forEach(challenge -> { - // Set correct challenge ID - challenge.setUniqueId(uniqueIDPrefix + challenge.getUniqueId()); - // Set up correct level ID if it is necessary - if (!challenge.getLevel().isEmpty()) - { - challenge.setLevel(uniqueIDPrefix + challenge.getLevel()); - } - // Load challenge in memory - manager.loadChallenge(challenge, false, user, user == null); - }); - - defaultChallenges.getLevelList().forEach(challengeLevel -> { - // Set correct level ID - challengeLevel.setUniqueId(uniqueIDPrefix + challengeLevel.getUniqueId()); - // Set correct world name - challengeLevel.setWorld(Util.getWorld(world).getName()); - // Reset names for all challenges. - challengeLevel.setChallenges(challengeLevel.getChallenges().stream(). - map(challenge -> uniqueIDPrefix + challenge). - collect(Collectors.toSet())); - // Load level in memory - manager.loadLevel(challengeLevel, false, user, user == null); - }); + defaultChallenges.getLevelList().forEach(challengeLevel -> { + // Set correct level ID + challengeLevel.setUniqueId(uniqueIDPrefix + challengeLevel.getUniqueId()); + // Set correct world name + challengeLevel.setWorld(Util.getWorld(world).getName()); + // Reset names for all challenges. + challengeLevel.setChallenges(challengeLevel.getChallenges().stream(). + map(challenge -> uniqueIDPrefix + challenge). + collect(Collectors.toSet())); + // Load level in memory + manager.loadLevel(challengeLevel, false, user, user == null); + }); + } } catch (Exception e) { @@ -128,132 +129,132 @@ public class ChallengesImportManager this.addon.getChallengesManager().save(); if (removeAtEnd) - { - // Remove default.yml file from resources to avoid interacting with it. - new File(this.addon.getDataFolder(), "default.json").delete(); - } + { + // Remove default.yml file from resources to avoid interacting with it. + return new File(this.addon.getDataFolder(), "default.json").delete(); + } return true; } - /** - * This method loads downloaded challenges into memory. - * @param user User who calls downloaded challenge loading - * @param world Target world. - * @param downloadString String that need to be loaded via DefaultDataHolder. - * @return true if everything was successful, otherwise false. - */ - public boolean loadDownloadedChallenges(User user, World world, String downloadString) - { - ChallengesManager manager = this.addon.getChallengesManager(); + /** + * This method loads downloaded challenges into memory. + * @param user User who calls downloaded challenge loading + * @param world Target world. + * @param downloadString String that need to be loaded via DefaultDataHolder. + * @return true if everything was successful, otherwise false. + */ + public boolean loadDownloadedChallenges(User user, World world, String downloadString) + { + ChallengesManager manager = this.addon.getChallengesManager(); - // If exist any challenge or level that is bound to current world, then do not load default challenges. - if (manager.hasAnyChallengeData(world.getName())) - { - if (user.isPlayer()) - { - user.sendMessage("challenges.errors.exist-challenges-or-levels"); - } - else - { - this.addon.logWarning("challenges.errors.exist-challenges-or-levels"); - } + // If exist any challenge or level that is bound to current world, then do not load default challenges. + if (manager.hasAnyChallengeData(world.getName())) + { + if (user.isPlayer()) + { + user.sendMessage("challenges.errors.exist-challenges-or-levels"); + } + else + { + this.addon.logWarning("challenges.errors.exist-challenges-or-levels"); + } - return false; - } + return false; + } - try - { - // This prefix will be used to all challenges. That is a unique way how to separate challenged for - // each game mode. - String uniqueIDPrefix = Utils.getGameMode(world) + "_"; - DefaultDataHolder downloadedChallenges = new DefaultJSONHandler(this.addon).loadWebObject(downloadString); + try + { + // This prefix will be used to all challenges. That is a unique way how to separate challenged for + // each game mode. + String uniqueIDPrefix = Utils.getGameMode(world) + "_"; + DefaultDataHolder downloadedChallenges = new DefaultJSONHandler(this.addon).loadWebObject(downloadString); - // All new challenges should get correct ID. So we need to map it to loaded challenges. - downloadedChallenges.getChallengeList().forEach(challenge -> { - // Set correct challenge ID - challenge.setUniqueId(uniqueIDPrefix + challenge.getUniqueId()); - // Set up correct level ID if it is necessary - if (!challenge.getLevel().isEmpty()) - { - challenge.setLevel(uniqueIDPrefix + challenge.getLevel()); - } - // Load challenge in memory - manager.loadChallenge(challenge, false, user, user == null); - }); + // All new challenges should get correct ID. So we need to map it to loaded challenges. + downloadedChallenges.getChallengeList().forEach(challenge -> { + // Set correct challenge ID + challenge.setUniqueId(uniqueIDPrefix + challenge.getUniqueId()); + // Set up correct level ID if it is necessary + if (!challenge.getLevel().isEmpty()) + { + challenge.setLevel(uniqueIDPrefix + challenge.getLevel()); + } + // Load challenge in memory + manager.loadChallenge(challenge, false, user, user == null); + }); - downloadedChallenges.getLevelList().forEach(challengeLevel -> { - // Set correct level ID - challengeLevel.setUniqueId(uniqueIDPrefix + challengeLevel.getUniqueId()); - // Set correct world name - challengeLevel.setWorld(Util.getWorld(world).getName()); - // Reset names for all challenges. - challengeLevel.setChallenges(challengeLevel.getChallenges().stream(). - map(challenge -> uniqueIDPrefix + challenge). - collect(Collectors.toSet())); - // Load level in memory - manager.loadLevel(challengeLevel, false, user, user == null); - }); - } - catch (Exception e) - { - e.printStackTrace(); - return false; - } + downloadedChallenges.getLevelList().forEach(challengeLevel -> { + // Set correct level ID + challengeLevel.setUniqueId(uniqueIDPrefix + challengeLevel.getUniqueId()); + // Set correct world name + challengeLevel.setWorld(Util.getWorld(world).getName()); + // Reset names for all challenges. + challengeLevel.setChallenges(challengeLevel.getChallenges().stream(). + map(challenge -> uniqueIDPrefix + challenge). + collect(Collectors.toSet())); + // Load level in memory + manager.loadLevel(challengeLevel, false, user, user == null); + }); + } + catch (Exception e) + { + addon.getPlugin().logStacktrace(e); + return false; + } - this.addon.getChallengesManager().save(); + this.addon.getChallengesManager().save(); - return true; - } + return true; + } -// --------------------------------------------------------------------- -// Section: Default generation -// --------------------------------------------------------------------- + // --------------------------------------------------------------------- + // Section: Default generation + // --------------------------------------------------------------------- /** * Create method that can generate default challenge file from existing challenges in given world. * This method will create default.json file in Challenges folder. - * @param user User who calls this method. + * @param user User who calls this method. * @param world from which challenges must be stored. - * @param overwrite indicates if existing default.json file can be overwritten. - * @return true if everything was successful, otherwise false + * @param overwrite indicates if existing default.json file can be overwritten. + * @return true if everything was successful, otherwise false */ public boolean generateDefaultChallengeFile(User user, World world, boolean overwrite) { File defaultFile = new File(this.addon.getDataFolder(), "default.json"); if (defaultFile.exists()) - { - if (overwrite) - { - if (user.isPlayer()) - { - user.sendMessage("challenges.messages.defaults-file-overwrite"); - } - else - { - this.addon.logWarning("challenges.messages.defaults-file-overwrite"); - } + { + if (overwrite) + { + if (user.isPlayer()) + { + user.sendMessage("challenges.messages.defaults-file-overwrite"); + } + else + { + this.addon.logWarning("challenges.messages.defaults-file-overwrite"); + } - defaultFile.delete(); - } - else - { - if (user.isPlayer()) - { - user.sendMessage("challenges.errors.defaults-file-exist"); - } - else - { - this.addon.logWarning("challenges.errors.defaults-file-exist"); - } + defaultFile.delete(); + } + else + { + if (user.isPlayer()) + { + user.sendMessage("challenges.errors.defaults-file-exist"); + } + else + { + this.addon.logWarning("challenges.errors.defaults-file-exist"); + } - return false; - } - } + return false; + } + } try { @@ -262,102 +263,102 @@ public class ChallengesImportManager String replacementString = Utils.getGameMode(world) + "_"; ChallengesManager manager = this.addon.getChallengesManager(); - List challengeList = manager.getAllChallenges(world). - stream(). - map(challenge -> { - // Use clone to avoid any changes in existing challenges. - Challenge clone = challenge.clone(); - // Remove world name from challenge id. - clone.setUniqueId(challenge.getUniqueId().replaceFirst(replacementString, "")); - // Remove world name from level id. - clone.setLevel(challenge.getLevel().replaceFirst(replacementString, "")); + List challengeList = manager.getAllChallenges(world). + stream(). + map(challenge -> { + // Use clone to avoid any changes in existing challenges. + Challenge clone = challenge.clone(); + // Remove world name from challenge id. + clone.setUniqueId(challenge.getUniqueId().replaceFirst(replacementString, "")); + // Remove world name from level id. + clone.setLevel(challenge.getLevel().replaceFirst(replacementString, "")); - return clone; - }). - collect(Collectors.toList()); + return clone; + }). + collect(Collectors.toList()); - List levelList = manager.getLevels(world). - stream(). - map(challengeLevel -> { - // Use clone to avoid any changes in existing levels. - ChallengeLevel clone = challengeLevel.clone(); - // Remove world name from level ID. - clone.setUniqueId(challengeLevel.getUniqueId().replaceFirst(replacementString, "")); - // Remove world name. - clone.setWorld(""); - // Challenges must be reassign, as they also contains world name. - clone.setChallenges(challengeLevel.getChallenges().stream(). - map(challenge -> challenge.replaceFirst(replacementString, "")). - collect(Collectors.toSet())); + List levelList = manager.getLevels(world). + stream(). + map(challengeLevel -> { + // Use clone to avoid any changes in existing levels. + ChallengeLevel clone = challengeLevel.clone(); + // Remove world name from level ID. + clone.setUniqueId(challengeLevel.getUniqueId().replaceFirst(replacementString, "")); + // Remove world name. + clone.setWorld(""); + // Challenges must be reassign, as they also contains world name. + clone.setChallenges(challengeLevel.getChallenges().stream(). + map(challenge -> challenge.replaceFirst(replacementString, "")). + collect(Collectors.toSet())); - return clone; - }). - collect(Collectors.toList()); + return clone; + }). + collect(Collectors.toList()); - DefaultDataHolder defaultChallenges = new DefaultDataHolder(); - defaultChallenges.setChallengeList(challengeList); - defaultChallenges.setLevelList(levelList); - defaultChallenges.setVersion(this.addon.getDescription().getVersion()); + DefaultDataHolder defaultChallenges = new DefaultDataHolder(); + defaultChallenges.setChallengeList(challengeList); + defaultChallenges.setLevelList(levelList); + defaultChallenges.setVersion(this.addon.getDescription().getVersion()); - BufferedWriter writer = new BufferedWriter( - new OutputStreamWriter(new FileOutputStream(defaultFile), StandardCharsets.UTF_8)); - writer.write(Objects.requireNonNull( - new DefaultJSONHandler(this.addon).toJsonString(defaultChallenges))); - writer.close(); + try (BufferedWriter writer = new BufferedWriter( + new OutputStreamWriter(new FileOutputStream(defaultFile), StandardCharsets.UTF_8))) { + writer.write(Objects.requireNonNull( + new DefaultJSONHandler(this.addon).toJsonString(defaultChallenges))); + } } } catch (IOException e) { - if (user.isPlayer()) - { - user.sendMessage("challenges.errors.defaults-file-error"); - } + if (user.isPlayer()) + { + user.sendMessage("challenges.errors.defaults-file-error"); + } - this.addon.logError("Could not save json file: " + e.getMessage()); - return false; - } + this.addon.logError("Could not save json file: " + e.getMessage()); + return false; + } finally - { - if (user.isPlayer()) - { - user.sendMessage("challenges.messages.defaults-file-completed", "[world]", world.getName()); - } - else - { - this.addon.logWarning("challenges.messages.defaults-file-completed"); - } - } + { + if (user.isPlayer()) + { + user.sendMessage("challenges.messages.defaults-file-completed", "[world]", world.getName()); + } + else + { + this.addon.logWarning("challenges.messages.defaults-file-completed"); + } + } - return true; - } + return true; + } -// --------------------------------------------------------------------- -// Section: Private classes for default challenges -// --------------------------------------------------------------------- + // --------------------------------------------------------------------- + // Section: Private classes for default challenges + // --------------------------------------------------------------------- /** * This Class allows to load default challenges and their levels as objects much easier. */ - private static final class DefaultJSONHandler + private static final class DefaultJSONHandler { - /** - * This constructor inits JSON builder that will be used to parse challenges. - * @param addon Challenges Adddon - */ - DefaultJSONHandler(ChallengesAddon addon) + /** + * This constructor inits JSON builder that will be used to parse challenges. + * @param addon Challenges Adddon + */ + DefaultJSONHandler(ChallengesAddon addon) { - GsonBuilder builder = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().enableComplexMapKeySerialization(); - // Register adapters - builder.registerTypeAdapterFactory(new BentoboxTypeAdapterFactory(addon.getPlugin())); - // Keep null in the database - builder.serializeNulls(); - // Allow characters like < or > without escaping them - builder.disableHtmlEscaping(); + GsonBuilder builder = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().enableComplexMapKeySerialization(); + // Register adapters + builder.registerTypeAdapterFactory(new BentoboxTypeAdapterFactory(addon.getPlugin())); + // Keep null in the database + builder.serializeNulls(); + // Allow characters like < or > without escaping them + builder.disableHtmlEscaping(); - this.addon = addon; - this.gson = builder.setPrettyPrinting().create(); + this.addon = addon; + this.gson = builder.setPrettyPrinting().create(); } @@ -366,7 +367,7 @@ public class ChallengesImportManager * @param instance Instance that must be parsed to json string. * @return String that contains JSON information from instance object. */ - String toJsonString(DefaultDataHolder instance) + String toJsonString(DefaultDataHolder instance) { // Null check if (instance == null) @@ -375,193 +376,193 @@ public class ChallengesImportManager return null; } - return this.gson.toJson(instance); + return this.gson.toJson(instance); } - /** - * This method creates and adds to list all objects from default.json file. - * @return List of all objects from default.json that is with T instance. - */ - DefaultDataHolder loadObject() - { - File defaultFile = new File(this.addon.getDataFolder(), "default.json"); + /** + * This method creates and adds to list all objects from default.json file. + * @return List of all objects from default.json that is with T instance. + */ + DefaultDataHolder loadObject() + { + File defaultFile = new File(this.addon.getDataFolder(), "default.json"); - try (InputStreamReader reader = new InputStreamReader(new FileInputStream(defaultFile), StandardCharsets.UTF_8)) - { - DefaultDataHolder object = this.gson.fromJson(reader, DefaultDataHolder.class); + try (InputStreamReader reader = new InputStreamReader(new FileInputStream(defaultFile), StandardCharsets.UTF_8)) + { + DefaultDataHolder object = this.gson.fromJson(reader, DefaultDataHolder.class); - reader.close(); // NOSONAR Required to keep OS file handlers low and not rely on GC + reader.close(); // NOSONAR Required to keep OS file handlers low and not rely on GC - return object; - } - catch (FileNotFoundException e) - { - this.addon.logError("Could not load file '" + defaultFile.getName() + "': File not found."); - } - catch (Exception e) - { - this.addon.logError("Could not load objects " + defaultFile.getName() + " " + e.getMessage()); - } + return object; + } + catch (FileNotFoundException e) + { + this.addon.logError("Could not load file '" + defaultFile.getName() + "': File not found."); + } + catch (Exception e) + { + this.addon.logError("Could not load objects " + defaultFile.getName() + " " + e.getMessage()); + } - return null; - } + return null; + } - /** - * This method creates and adds to list all objects from default.json file. - * @return List of all objects from default.json that is with T instance. - */ - DefaultDataHolder loadWebObject(String downloadedObject) - { - return this.gson.fromJson(downloadedObject, DefaultDataHolder.class); - } + /** + * This method creates and adds to list all objects from default.json file. + * @return List of all objects from default.json that is with T instance. + */ + DefaultDataHolder loadWebObject(String downloadedObject) + { + return this.gson.fromJson(downloadedObject, DefaultDataHolder.class); + } - // --------------------------------------------------------------------- - // Section: Variables - // --------------------------------------------------------------------- + // --------------------------------------------------------------------- + // Section: Variables + // --------------------------------------------------------------------- - /** - * Holds JSON builder object. - */ - private Gson gson; + /** + * Holds JSON builder object. + */ + private Gson gson; - /** - * Holds ChallengesAddon object. - */ - private ChallengesAddon addon; + /** + * Holds ChallengesAddon object. + */ + private ChallengesAddon addon; } - /** - * This is simple object that will allow to store all current challenges and levels - * in single file. - */ - private static final class DefaultDataHolder implements DataObject - { - /** - * Default constructor. Creates object with empty lists. - */ - DefaultDataHolder() - { - this.challengeList = Collections.emptyList(); - this.challengeLevelList = Collections.emptyList(); - this.version = ""; - } + /** + * This is simple object that will allow to store all current challenges and levels + * in single file. + */ + private static final class DefaultDataHolder implements DataObject + { + /** + * Default constructor. Creates object with empty lists. + */ + DefaultDataHolder() + { + this.challengeList = Collections.emptyList(); + this.challengeLevelList = Collections.emptyList(); + this.version = ""; + } - /** - * This method returns stored challenge list. - * @return list that contains default challenges. - */ - List getChallengeList() - { - return challengeList; - } + /** + * This method returns stored challenge list. + * @return list that contains default challenges. + */ + List getChallengeList() + { + return challengeList; + } - /** - * This method sets given list as default challenge list. - * @param challengeList new default challenge list. - */ - void setChallengeList(List challengeList) - { - this.challengeList = challengeList; - } + /** + * This method sets given list as default challenge list. + * @param challengeList new default challenge list. + */ + void setChallengeList(List challengeList) + { + this.challengeList = challengeList; + } - /** - * This method returns list of default challenge levels. - * @return List that contains default challenge levels. - */ - List getLevelList() - { - return challengeLevelList; - } + /** + * This method returns list of default challenge levels. + * @return List that contains default challenge levels. + */ + List getLevelList() + { + return challengeLevelList; + } - /** - * This method sets given list as default challenge level list. - * @param levelList new default challenge level list. - */ - void setLevelList(List levelList) - { - this.challengeLevelList = levelList; - } + /** + * This method sets given list as default challenge level list. + * @param levelList new default challenge level list. + */ + void setLevelList(List levelList) + { + this.challengeLevelList = levelList; + } - /** - * This method returns the version value. - * @return the value of version. - */ - public String getVersion() - { - return version; - } + /** + * This method returns the version value. + * @return the value of version. + */ + public String getVersion() + { + return version; + } - /** - * This method sets the version value. - * @param version the version new value. - * - */ - public void setVersion(String version) - { - this.version = version; - } + /** + * This method sets the version value. + * @param version the version new value. + * + */ + public void setVersion(String version) + { + this.version = version; + } - /** - * @return default.json - */ - @Override - public String getUniqueId() - { - return "default.json"; - } + /** + * @return default.json + */ + @Override + public String getUniqueId() + { + return "default.json"; + } - /** - * @param uniqueId - unique ID the uniqueId to set - */ - @Override - public void setUniqueId(String uniqueId) - { - // method not used. - } + /** + * @param uniqueId - unique ID the uniqueId to set + */ + @Override + public void setUniqueId(String uniqueId) + { + // method not used. + } - // --------------------------------------------------------------------- - // Section: Variables - // --------------------------------------------------------------------- + // --------------------------------------------------------------------- + // Section: Variables + // --------------------------------------------------------------------- - /** - * Holds a list with default challenges. - */ - @Expose - private List challengeList; + /** + * Holds a list with default challenges. + */ + @Expose + private List challengeList; - /** - * Holds a list with default levels. - */ - @Expose - private List challengeLevelList; + /** + * Holds a list with default levels. + */ + @Expose + private List challengeLevelList; - /** - * Holds a variable that stores in which addon version file was made. - */ - @Expose - private String version; - } + /** + * Holds a variable that stores in which addon version file was made. + */ + @Expose + private String version; + } -// --------------------------------------------------------------------- -// Section: Variables -// --------------------------------------------------------------------- + // --------------------------------------------------------------------- + // Section: Variables + // --------------------------------------------------------------------- - private ChallengesAddon addon; + private ChallengesAddon addon; } \ No newline at end of file diff --git a/src/main/java/world/bentobox/challenges/ChallengesManager.java b/src/main/java/world/bentobox/challenges/ChallengesManager.java index 601d0ff..bca29bf 100644 --- a/src/main/java/world/bentobox/challenges/ChallengesManager.java +++ b/src/main/java/world/bentobox/challenges/ChallengesManager.java @@ -19,6 +19,7 @@ import java.util.stream.Collectors; import org.bukkit.Bukkit; import org.bukkit.World; import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; import world.bentobox.bentobox.api.logs.LogEntry; import world.bentobox.bentobox.api.user.User; @@ -43,7 +44,7 @@ import world.bentobox.challenges.utils.Utils; /** - * This class manges challenges. It allows access to all data that is stored to database. + * This class manages challenges. It allows access to all data that is stored to database. * It also provides information about challenge level status for each user. */ public class ChallengesManager @@ -107,6 +108,11 @@ public class ChallengesManager * String for free Challenge Level. */ public static final String FREE = ""; + public static final String VALUE = "[value]"; + public static final String USER_ID = "user-id"; + public static final String CHALLENGE_ID = "challenge-id"; + public static final String ADMIN_ID = "admin-id"; + public static final String RESET = "RESET"; // --------------------------------------------------------------------- @@ -142,7 +148,7 @@ public class ChallengesManager { // Sort by challenges level order numbers return Integer.compare(this.getLevel(o1.getLevel()).getOrder(), - this.getLevel(o2.getLevel()).getOrder()); + this.getLevel(o2.getLevel()).getOrder()); } } }; @@ -189,6 +195,7 @@ public class ChallengesManager */ public void load() { + this.addon.log("Loading challenges..."); this.challengeCacheData.clear(); this.levelCacheData.clear(); @@ -199,17 +206,15 @@ public class ChallengesManager } this.playerCacheData.clear(); + loadAndValidate(); + } - this.addon.getLogger().info("Loading challenges..."); + private void loadAndValidate() { this.challengeDatabase.loadObjects().forEach(this::loadChallenge); this.levelDatabase.loadObjects().forEach(this::loadLevel); - // this validate challenge levels this.validateChallenges(); - - // It is not necessary to load all players in memory. -// this.playersDatabase.loadObjects().forEach(this::loadPlayerData); } @@ -218,24 +223,17 @@ public class ChallengesManager */ public void reload() { + this.addon.log("Reloading challenges..."); if (!this.playerCacheData.isEmpty()) { // store player data before cleaning. this.savePlayersData(); } + //this.challengeDatabase = new Database<>(addon, Challenge.class); + //this.levelDatabase = new Database<>(addon, ChallengeLevel.class); + //this.playersDatabase = new Database<>(addon, ChallengesPlayerData.class); - this.addon.getLogger().info("Reloading challenges..."); - - this.challengeDatabase = new Database<>(addon, Challenge.class); - this.levelDatabase = new Database<>(addon, ChallengeLevel.class); - this.playersDatabase = new Database<>(addon, ChallengesPlayerData.class); - - this.challengeDatabase.loadObjects().forEach(this::loadChallenge); - this.levelDatabase.loadObjects().forEach(this::loadLevel); - - this.validateChallenges(); - // It is not necessary to load all players in memory. -// this.playersDatabase.loadObjects().forEach(this::loadPlayerData); + loadAndValidate(); } @@ -261,17 +259,10 @@ public class ChallengesManager * @return - true if imported */ public boolean loadChallenge(@NonNull Challenge challenge, - boolean overwrite, - User user, - boolean silent) + boolean overwrite, + User user, + boolean silent) { - if (challenge == null) - { - this.addon.logError( - "Tried to load NULL element from Database. One challenge is broken and will not work."); - return false; - } - if (this.challengeCacheData.containsKey(challenge.getUniqueId())) { if (!overwrite) @@ -279,7 +270,7 @@ public class ChallengesManager if (!silent) { user.sendMessage("challenges.messages.load-skipping", - "[value]", challenge.getFriendlyName()); + VALUE, challenge.getFriendlyName()); } return false; @@ -289,7 +280,7 @@ public class ChallengesManager if (!silent) { user.sendMessage("challenges.messages.load-overwriting", - "[value]", challenge.getFriendlyName()); + VALUE, challenge.getFriendlyName()); } } } @@ -298,7 +289,7 @@ public class ChallengesManager if (!silent) { user.sendMessage("challenges.messages.load-add", - "[value]", challenge.getFriendlyName()); + VALUE, challenge.getFriendlyName()); } } @@ -329,28 +320,21 @@ public class ChallengesManager * @return boolean that indicate about load status. */ public boolean loadLevel(@NonNull ChallengeLevel level, - boolean overwrite, - User user, - boolean silent) + boolean overwrite, + User user, + boolean silent) { - if (level == null) - { - this.addon.logError( - "Tried to load NULL element from Database. One level is broken and will not work."); - return false; - } - if (!this.isValidLevel(level)) { if (user != null) { user.sendMessage("challenges.errors.load-error", - "[value]", level.getFriendlyName()); + VALUE, level.getFriendlyName()); } else { this.addon.logError( - "Challenge Level '" + level.getUniqueId() + "' is not valid and skipped"); + "Challenge Level '" + level.getUniqueId() + "' is not valid and skipped"); } return false; @@ -363,7 +347,7 @@ public class ChallengesManager if (!silent) { user.sendMessage("challenges.messages.load-skipping", - "[value]", level.getFriendlyName()); + VALUE, level.getFriendlyName()); } return false; @@ -373,7 +357,7 @@ public class ChallengesManager if (!silent) { user.sendMessage("challenges.messages.load-overwriting", - "[value]", level.getFriendlyName()); + VALUE, level.getFriendlyName()); } } } @@ -382,7 +366,7 @@ public class ChallengesManager if (!silent) { user.sendMessage("challenges.messages.load-add", - "[value]", level.getFriendlyName()); + VALUE, level.getFriendlyName()); } } @@ -416,14 +400,11 @@ public class ChallengesManager */ public void removeFromCache(UUID playerID) { - if (!this.settings.isStoreAsIslandData()) + if (!this.settings.isStoreAsIslandData() && this.playerCacheData.containsKey(playerID.toString())) { - if (this.playerCacheData.containsKey(playerID.toString())) - { - // save before remove - this.savePlayerData(playerID.toString()); - this.playerCacheData.remove(playerID.toString()); - } + // save before remove + this.savePlayerData(playerID.toString()); + this.playerCacheData.remove(playerID.toString()); } // TODO: It would be necessary to remove also data, if they stores islands. @@ -449,7 +430,7 @@ public class ChallengesManager // If challenge's level is not found, then set it as free challenge. challenge.setLevel(FREE); this.addon.logWarning("Challenge's " + challenge.getUniqueId() + " level was not found in the database. " + - "To avoid any errors with missing level, challenge was added to the FREE level!"); + "To avoid any errors with missing level, challenge was added to the FREE level!"); } }); } @@ -680,13 +661,13 @@ public class ChallengesManager this.levelCacheData.remove(level.getUniqueId()); level.setUniqueId( - addonName + level.getUniqueId().substring(world.getName().length())); + addonName + level.getUniqueId().substring(world.getName().length())); Set challengesID = new HashSet<>(level.getChallenges()); level.getChallenges().clear(); challengesID.forEach(challenge -> - level.getChallenges().add(addonName + challenge.substring(world.getName().length()))); + level.getChallenges().add(addonName + challenge.substring(world.getName().length()))); this.levelDatabase.saveObject(level); this.levelCacheData.put(level.getUniqueId(), level); @@ -740,36 +721,36 @@ public class ChallengesManager { switch (challenge.getChallengeType()) { - case INVENTORY: - InventoryRequirements inventoryRequirements = new InventoryRequirements(); - inventoryRequirements.setRequiredItems(challenge.getRequiredItems()); - inventoryRequirements.setTakeItems(challenge.isTakeItems()); + case INVENTORY: + InventoryRequirements inventoryRequirements = new InventoryRequirements(); + inventoryRequirements.setRequiredItems(challenge.getRequiredItems()); + inventoryRequirements.setTakeItems(challenge.isTakeItems()); - inventoryRequirements.setRequiredPermissions(challenge.getRequiredPermissions()); - challenge.setRequirements(inventoryRequirements); - break; - case ISLAND: - IslandRequirements islandRequirements = new IslandRequirements(); - islandRequirements.setRemoveBlocks(challenge.isRemoveBlocks()); - islandRequirements.setRemoveEntities(challenge.isRemoveEntities()); - islandRequirements.setRequiredBlocks(challenge.getRequiredBlocks()); - islandRequirements.setRequiredEntities(challenge.getRequiredEntities()); - islandRequirements.setSearchRadius(challenge.getSearchRadius()); + inventoryRequirements.setRequiredPermissions(challenge.getRequiredPermissions()); + challenge.setRequirements(inventoryRequirements); + break; + case ISLAND: + IslandRequirements islandRequirements = new IslandRequirements(); + islandRequirements.setRemoveBlocks(challenge.isRemoveBlocks()); + islandRequirements.setRemoveEntities(challenge.isRemoveEntities()); + islandRequirements.setRequiredBlocks(challenge.getRequiredBlocks()); + islandRequirements.setRequiredEntities(challenge.getRequiredEntities()); + islandRequirements.setSearchRadius(challenge.getSearchRadius()); - islandRequirements.setRequiredPermissions(challenge.getRequiredPermissions()); - challenge.setRequirements(islandRequirements); - break; - case OTHER: - OtherRequirements otherRequirements = new OtherRequirements(); - otherRequirements.setRequiredExperience(challenge.getRequiredExperience()); - otherRequirements.setRequiredIslandLevel(challenge.getRequiredIslandLevel()); - otherRequirements.setRequiredMoney(challenge.getRequiredMoney()); - otherRequirements.setTakeExperience(challenge.isTakeExperience()); - otherRequirements.setTakeMoney(challenge.isTakeMoney()); + islandRequirements.setRequiredPermissions(challenge.getRequiredPermissions()); + challenge.setRequirements(islandRequirements); + break; + case OTHER: + OtherRequirements otherRequirements = new OtherRequirements(); + otherRequirements.setRequiredExperience(challenge.getRequiredExperience()); + otherRequirements.setRequiredIslandLevel(challenge.getRequiredIslandLevel()); + otherRequirements.setRequiredMoney(challenge.getRequiredMoney()); + otherRequirements.setTakeExperience(challenge.isTakeExperience()); + otherRequirements.setTakeMoney(challenge.isTakeMoney()); - otherRequirements.setRequiredPermissions(challenge.getRequiredPermissions()); - challenge.setRequirements(otherRequirements); - break; + otherRequirements.setRequiredPermissions(challenge.getRequiredPermissions()); + challenge.setRequirements(otherRequirements); + break; } // This save should not involve any upgrades in other parts. @@ -1082,6 +1063,7 @@ public class ChallengesManager * @param gameMode - World Name where levels should be searched. * @return Level status - how many challenges still to do on which level */ + @NonNull private List getAllChallengeLevelStatus(String storageDataID, String gameMode) { this.addPlayerData(storageDataID); @@ -1109,15 +1091,14 @@ public class ChallengesManager doneChallengeCount = (int) level.getChallenges().stream().filter(playerData::isChallengeDone).count(); result.add(new LevelStatus( - level, - previousLevel, - challengesToDo, - level.getChallenges().size() == doneChallengeCount, - challengesToDo <= 0)); + level, + previousLevel, + challengesToDo, + level.getChallenges().size() == doneChallengeCount, + challengesToDo <= 0)); previousLevel = level; } - return result; } @@ -1129,6 +1110,7 @@ public class ChallengesManager * @param level Level which status must be calculated. * @return LevelStatus of given level. */ + @Nullable private LevelStatus getChallengeLevelStatus(@NonNull String storageDataID, World world, @NonNull ChallengeLevel level) { this.addPlayerData(storageDataID); @@ -1148,43 +1130,20 @@ public class ChallengesManager int challengesToDo = previousLevel == null ? 0 : (previousLevel.getChallenges().size() - level.getWaiverAmount()) - - (int) previousLevel.getChallenges().stream().filter(playerData::isChallengeDone).count(); + (int) previousLevel.getChallenges().stream().filter(playerData::isChallengeDone).count(); // As level already contains unique ids of challenges, just iterate through them. int doneChallengeCount = (int) level.getChallenges().stream().filter(playerData::isChallengeDone).count(); return new LevelStatus( - level, - previousLevel, - challengesToDo, - level.getChallenges().size() == doneChallengeCount, - challengesToDo <= 0); + level, + previousLevel, + challengesToDo, + level.getChallenges().size() == doneChallengeCount, + challengesToDo <= 0); } } - - /** - * Check is playerData can see given level. - * TODO: not an optimal way. Faster would be to check previous level challenges. - * @param storageDataID - playerData ID - * @param level - level - * @return true if level is unlocked - */ - private boolean isLevelUnlocked(@NonNull String storageDataID, - World world, - ChallengeLevel level) - { - this.addPlayerData(storageDataID); - - return this.islandWorldManager.getAddon(world).filter(gameMode -> - this.getAllChallengeLevelStatus(storageDataID, gameMode.getDescription().getName()). - stream(). - filter(LevelStatus::isUnlocked). - anyMatch(lv -> lv.getLevel().equals(level))). - isPresent(); - } - - /** * This method returns if given user has been already completed given level. * @param levelID Level that must be checked. @@ -1316,17 +1275,17 @@ public class ChallengesManager String storageID = this.getDataUniqueID(userID, Util.getWorld(world)); this.setChallengeComplete(storageID, challenge.getUniqueId(), completionCount); this.addLogEntry(storageID, new LogEntry.Builder("COMPLETE"). - data("user-id", userID.toString()). - data("challenge-id", challenge.getUniqueId()). - data("completion-count", Integer.toString(completionCount)). - build()); + data(USER_ID, userID.toString()). + data(CHALLENGE_ID, challenge.getUniqueId()). + data("completion-count", Integer.toString(completionCount)). + build()); // Fire event that user completes challenge - Bukkit.getServer().getPluginManager().callEvent( - new ChallengeCompletedEvent(challenge.getUniqueId(), - userID, - false, - completionCount)); + Bukkit.getPluginManager().callEvent( + new ChallengeCompletedEvent(challenge.getUniqueId(), + userID, + false, + completionCount)); } @@ -1343,17 +1302,17 @@ public class ChallengesManager this.setChallengeComplete(storageID, challenge.getUniqueId()); this.addLogEntry(storageID, new LogEntry.Builder("COMPLETE"). - data("user-id", userID.toString()). - data("challenge-id", challenge.getUniqueId()). - data("admin-id", adminID == null ? "OP" : adminID.toString()). - build()); + data(USER_ID, userID.toString()). + data(CHALLENGE_ID, challenge.getUniqueId()). + data(ADMIN_ID, adminID == null ? "OP" : adminID.toString()). + build()); // Fire event that admin completes user challenge - Bukkit.getServer().getPluginManager().callEvent( - new ChallengeCompletedEvent(challenge.getUniqueId(), - userID, - true, - 1)); + Bukkit.getPluginManager().callEvent( + new ChallengeCompletedEvent(challenge.getUniqueId(), + userID, + true, + 1)); } @@ -1368,18 +1327,18 @@ public class ChallengesManager String storageID = this.getDataUniqueID(userID, Util.getWorld(world)); this.resetChallenge(storageID, challenge.getUniqueId()); - this.addLogEntry(storageID, new LogEntry.Builder("RESET"). - data("user-id", userID.toString()). - data("challenge-id", challenge.getUniqueId()). - data("admin-id", adminID == null ? "RESET" : adminID.toString()). - build()); + this.addLogEntry(storageID, new LogEntry.Builder(RESET). + data(USER_ID, userID.toString()). + data(CHALLENGE_ID, challenge.getUniqueId()). + data(ADMIN_ID, adminID == null ? RESET : adminID.toString()). + build()); // Fire event that admin resets user challenge - Bukkit.getServer().getPluginManager().callEvent( - new ChallengeResetEvent(challenge.getUniqueId(), - userID, - true, - "RESET")); + Bukkit.getPluginManager().callEvent( + new ChallengeResetEvent(challenge.getUniqueId(), + userID, + true, + RESET)); } @@ -1407,16 +1366,16 @@ public class ChallengesManager this.islandWorldManager.getAddon(world).ifPresent(gameMode -> { this.resetAllChallenges(storageID, gameMode.getDescription().getName()); this.addLogEntry(storageID, new LogEntry.Builder("RESET_ALL"). - data("user-id", userID.toString()). - data("admin-id", adminID == null ? "ISLAND_RESET" : adminID.toString()). - build()); + data(USER_ID, userID.toString()). + data(ADMIN_ID, adminID == null ? "ISLAND_RESET" : adminID.toString()). + build()); // Fire event that admin resets user challenge - Bukkit.getServer().getPluginManager().callEvent( - new ChallengeResetAllEvent(gameMode.getDescription().getName(), - userID, - adminID != null, - adminID == null ? "ISLAND_RESET" : "RESET_ALL")); + Bukkit.getPluginManager().callEvent( + new ChallengeResetAllEvent(gameMode.getDescription().getName(), + userID, + adminID != null, + adminID == null ? "ISLAND_RESET" : "RESET_ALL")); }); } @@ -1468,11 +1427,19 @@ public class ChallengesManager * @param world World where level must be checked. * @param level Level that must be checked. * @param user User who need to be checked. - * @return true, if level is already completed. + * @return true, if level is unlocked. */ public boolean isLevelUnlocked(User user, World world, ChallengeLevel level) { - return this.isLevelUnlocked(this.getDataUniqueID(user, Util.getWorld(world)), world, level); + String storageDataID = this.getDataUniqueID(user, Util.getWorld(world)); + this.addPlayerData(storageDataID); + + return this.islandWorldManager.getAddon(world).filter(gameMode -> this.getAllChallengeLevelStatus(storageDataID, gameMode.getDescription().getName()). + stream(). + filter(LevelStatus::isUnlocked). + anyMatch(lv -> lv.getLevel().equals(level))). + isPresent(); + } @@ -1488,14 +1455,14 @@ public class ChallengesManager this.setLevelComplete(storageID, level.getUniqueId()); this.addLogEntry(storageID, new LogEntry.Builder("COMPLETE_LEVEL"). - data("user-id", user.getUniqueId().toString()). - data("level", level.getUniqueId()).build()); + data(USER_ID, user.getUniqueId().toString()). + data("level", level.getUniqueId()).build()); // Fire event that user completes level - Bukkit.getServer().getPluginManager().callEvent( - new LevelCompletedEvent(level.getUniqueId(), - user.getUniqueId(), - false)); + Bukkit.getPluginManager().callEvent( + new LevelCompletedEvent(level.getUniqueId(), + user.getUniqueId(), + false)); } @@ -1514,14 +1481,15 @@ public class ChallengesManager /** * This method returns LevelStatus object for given challenge level. + * @param uniqueId UUID of user who need to be validated. * @param world World where level must be validated. * @param level Level that must be validated. - * @param user User who need to be validated. * @return LevelStatus of given level. */ - public LevelStatus getChallengeLevelStatus(UUID user, World world, ChallengeLevel level) + @Nullable + public LevelStatus getChallengeLevelStatus(UUID uniqueId, World world, ChallengeLevel level) { - return this.getChallengeLevelStatus(this.getDataUniqueID(user, Util.getWorld(world)), world, level); + return this.getChallengeLevelStatus(this.getDataUniqueID(uniqueId, Util.getWorld(world)), world, level); } @@ -1532,13 +1500,14 @@ public class ChallengesManager * @param world - World where levels should be searched. * @return Level status - how many challenges still to do on which level */ + @NonNull public List getAllChallengeLevelStatus(User user, World world) { return this.islandWorldManager.getAddon(world).map(gameMode -> - this.getAllChallengeLevelStatus( + this.getAllChallengeLevelStatus( this.getDataUniqueID(user, Util.getWorld(world)), gameMode.getDescription().getName())). - orElse(Collections.emptyList()); + orElse(Collections.emptyList()); } @@ -1556,12 +1525,12 @@ public class ChallengesManager public List getAllChallengesNames(@NonNull World world) { return this.islandWorldManager.getAddon(world).map(gameMode -> - this.challengeCacheData.values().stream(). - filter(challenge -> challenge.matchGameMode(gameMode.getDescription().getName())). - sorted(this.challengeComparator). - map(Challenge::getUniqueId). - collect(Collectors.toList())). - orElse(Collections.emptyList()); + this.challengeCacheData.values().stream(). + filter(challenge -> challenge.matchGameMode(gameMode.getDescription().getName())). + sorted(this.challengeComparator). + map(Challenge::getUniqueId). + collect(Collectors.toList())). + orElse(Collections.emptyList()); } @@ -1574,11 +1543,11 @@ public class ChallengesManager public List getAllChallenges(@NonNull World world) { return this.islandWorldManager.getAddon(world).map(gameMode -> - this.challengeCacheData.values().stream(). - filter(challenge -> challenge.matchGameMode(gameMode.getDescription().getName())). - sorted(this.challengeComparator). - collect(Collectors.toList())). - orElse(Collections.emptyList()); + this.challengeCacheData.values().stream(). + filter(challenge -> challenge.matchGameMode(gameMode.getDescription().getName())). + sorted(this.challengeComparator). + collect(Collectors.toList())). + orElse(Collections.emptyList()); } @@ -1591,12 +1560,12 @@ public class ChallengesManager { // Free Challenges hides under FREE level. return this.islandWorldManager.getAddon(world).map(gameMode -> - this.challengeCacheData.values().stream(). - filter(challenge -> challenge.getLevel().equals(FREE) && - challenge.matchGameMode(gameMode.getDescription().getName())). - sorted(Comparator.comparing(Challenge::getOrder)). - collect(Collectors.toList())). - orElse(Collections.emptyList()); + this.challengeCacheData.values().stream(). + filter(challenge -> challenge.getLevel().equals(FREE) && + challenge.matchGameMode(gameMode.getDescription().getName())). + sorted(Comparator.comparing(Challenge::getOrder)). + collect(Collectors.toList())). + orElse(Collections.emptyList()); } @@ -1608,10 +1577,10 @@ public class ChallengesManager public List getLevelChallenges(ChallengeLevel level) { return level.getChallenges().stream(). - map(this::getChallenge). - filter(Objects::nonNull). - sorted(Comparator.comparing(Challenge::getOrder)). - collect(Collectors.toList()); + map(this::getChallenge). + filter(Objects::nonNull). + sorted(Comparator.comparing(Challenge::getOrder)). + collect(Collectors.toList()); } @@ -1621,6 +1590,7 @@ public class ChallengesManager * @param name - unique name of challenge * @return - challenge or null if it does not exist */ + @Nullable public Challenge getChallenge(String name) { if (this.challengeCacheData.containsKey(name)) @@ -1658,30 +1628,7 @@ public class ChallengesManager */ public boolean containsChallenge(String name) { - if (this.challengeCacheData.containsKey(name)) - { - return true; - } - else - { - // check database. - if (this.challengeDatabase.objectExists(name)) - { - Challenge challenge = this.challengeDatabase.loadObject(name); - - if (challenge != null) - { - this.challengeCacheData.put(name, challenge); - return true; - } - else - { - this.addon.logError("Tried to load NULL challenge object!"); - } - } - } - - return false; + return getChallenge(name) != null; } @@ -1691,6 +1638,7 @@ public class ChallengesManager * @param requirements - requirements object, as it is not changeable anymore. * @return Challenge that is currently created. */ + @Nullable public Challenge createChallenge(String uniqueID, Challenge.ChallengeType type, Requirements requirements) { if (!this.containsChallenge(uniqueID)) @@ -1725,7 +1673,7 @@ public class ChallengesManager this.challengeDatabase.deleteObject(challenge); this.addon.getPlugin().getPlaceholdersManager(). - unregisterPlaceholder("challenges_challenge_repetition_count_" + challenge.getUniqueId()); + unregisterPlaceholder("challenges_challenge_repetition_count_" + challenge.getUniqueId()); } } @@ -1743,8 +1691,8 @@ public class ChallengesManager public List getLevels(@NonNull World world) { return this.islandWorldManager.getAddon(world).map(gameMode -> - this.getLevels(gameMode.getDescription().getName())). - orElse(Collections.emptyList()); + this.getLevels(gameMode.getDescription().getName())). + orElse(Collections.emptyList()); } @@ -1757,9 +1705,9 @@ public class ChallengesManager { // TODO: Probably need to check also database. return this.levelCacheData.values().stream(). - sorted(ChallengeLevel::compareTo). - filter(level -> level.matchGameMode(gameMode)). - collect(Collectors.toList()); + sorted(ChallengeLevel::compareTo). + filter(level -> level.matchGameMode(gameMode)). + collect(Collectors.toList()); } @@ -1769,6 +1717,7 @@ public class ChallengesManager * @param challenge - challenge which level must be returned. * @return - challenge level or null if it does not exist */ + @Nullable public ChallengeLevel getLevel(Challenge challenge) { if (!challenge.getLevel().equals(FREE)) @@ -1786,6 +1735,7 @@ public class ChallengesManager * @param name - unique name of challenge level * @return - challenge level or null if it does not exist */ + @Nullable public ChallengeLevel getLevel(String name) { if (this.levelCacheData.containsKey(name)) @@ -1853,7 +1803,7 @@ public class ChallengesManager /** * This method adds given challenge to given challenge level. * @param newChallenge Challenge who must change owner. - * @param newLevel Level who must add new challenge + * @param newLevel Level to add to - must exist already */ public void addChallengeToLevel(Challenge newChallenge, ChallengeLevel newLevel) { @@ -1869,7 +1819,7 @@ public class ChallengesManager { ChallengeLevel oldLevel = this.getLevel(newChallenge.getLevel()); - if (!oldLevel.equals(newLevel)) + if (oldLevel == null || !oldLevel.equals(newLevel)) { this.removeChallengeFromLevel(newChallenge, newLevel); newLevel.getChallenges().add(newChallenge.getUniqueId()); @@ -1904,6 +1854,7 @@ public class ChallengesManager * @param uniqueID - new ID for challenge level. * @return ChallengeLevel that is currently created. */ + @Nullable public ChallengeLevel createLevel(String uniqueID, World world) { if (!this.containsLevel(uniqueID)) @@ -1951,7 +1902,7 @@ public class ChallengesManager this.levelDatabase.deleteObject(challengeLevel); this.addon.getPlugin().getPlaceholdersManager(). - unregisterPlaceholder("challenges_completed_challenge_count_per_level_" + challengeLevel.getUniqueId()); + unregisterPlaceholder("challenges_completed_challenge_count_per_level_" + challengeLevel.getUniqueId()); } } @@ -1964,7 +1915,7 @@ public class ChallengesManager public boolean hasAnyChallengeData(@NonNull World world) { return this.islandWorldManager.getAddon(world).filter(gameMode -> - this.hasAnyChallengeData(gameMode.getDescription().getName())).isPresent(); + this.hasAnyChallengeData(gameMode.getDescription().getName())).isPresent(); } @@ -1976,8 +1927,8 @@ public class ChallengesManager public boolean hasAnyChallengeData(@NonNull String gameMode) { return this.challengeDatabase.loadObjects().stream().anyMatch( - challenge -> challenge.matchGameMode(gameMode)) || - this.levelDatabase.loadObjects().stream().anyMatch( - level -> level.matchGameMode(gameMode)); + challenge -> challenge.matchGameMode(gameMode)) || + this.levelDatabase.loadObjects().stream().anyMatch( + level -> level.matchGameMode(gameMode)); } } \ No newline at end of file diff --git a/src/main/java/world/bentobox/challenges/commands/ChallengesCommand.java b/src/main/java/world/bentobox/challenges/commands/ChallengesCommand.java index b26a194..7012c9b 100644 --- a/src/main/java/world/bentobox/challenges/commands/ChallengesCommand.java +++ b/src/main/java/world/bentobox/challenges/commands/ChallengesCommand.java @@ -1,7 +1,6 @@ package world.bentobox.challenges.commands; import java.util.List; -import java.util.Optional; import world.bentobox.bentobox.api.addons.GameModeAddon; import world.bentobox.bentobox.api.commands.CompositeCommand; @@ -27,10 +26,7 @@ public class ChallengesCommand extends CompositeCommand @Override public boolean canExecute(User user, String label, List args) { - Optional optionalAddon = this.getAddon().getPlugin().getIWM().getAddon(this.getWorld()); - - if (!optionalAddon.isPresent()) - { + if (!getIWM().inWorld(getWorld())) { // Not a GameMode world. user.sendMessage("general.errors.wrong-world"); return false; @@ -39,13 +35,14 @@ public class ChallengesCommand extends CompositeCommand if (!((ChallengesAddon) this.getAddon()).getChallengesManager().hasAnyChallengeData(this.getWorld())) { // Do not open gui if there is no challenges. - - this.getAddon().getLogger().severe("There are no challenges set up in " + this.getWorld() + "!"); + this.getAddon().logError("There are no challenges set up in " + this.getWorld() + "!"); // Show admin better explanation. if (user.isOp() || user.hasPermission(this.getPermissionPrefix() + "admin.challenges")) { - String topLabel = optionalAddon.get().getAdminCommand().orElseGet(this::getParent).getTopLabel(); + String topLabel = getIWM().getAddon(this.getWorld()) + .map(GameModeAddon::getAdminCommand) + .map(optionalAdminCommand -> optionalAdminCommand.map(ac -> ac.getTopLabel()).orElse(this.getTopLabel())).orElse(this.getTopLabel()); user.sendMessage("challenges.errors.no-challenges-admin", "[command]", topLabel + " challenges"); } else @@ -56,7 +53,7 @@ public class ChallengesCommand extends CompositeCommand return false; } - if (this.getPlugin().getIslands().getIsland(this.getWorld(), user.getUniqueId()) == null) + if (this.getIslands().getIsland(this.getWorld(), user) == null) { // Do not open gui if there is no island for this player. user.sendMessage("general.errors.no-island"); diff --git a/src/main/java/world/bentobox/challenges/commands/CompleteChallengeCommand.java b/src/main/java/world/bentobox/challenges/commands/CompleteChallengeCommand.java index 2259f13..840759f 100644 --- a/src/main/java/world/bentobox/challenges/commands/CompleteChallengeCommand.java +++ b/src/main/java/world/bentobox/challenges/commands/CompleteChallengeCommand.java @@ -21,128 +21,128 @@ import world.bentobox.challenges.utils.Utils; */ public class CompleteChallengeCommand extends CompositeCommand { - /** - * Default constructor for Composite Command. - * @param addon Challenges addon. - * @param cmd Parent Command. - */ - public CompleteChallengeCommand(Addon addon, CompositeCommand cmd) - { - super(addon, cmd, "complete"); - this.addon = (ChallengesAddon) addon; - } + /** + * Default constructor for Composite Command. + * @param addon Challenges addon. + * @param cmd Parent Command. + */ + public CompleteChallengeCommand(Addon addon, CompositeCommand cmd) + { + super(addon, cmd, "complete"); + this.addon = (ChallengesAddon) addon; + } - /** - * {@inheritDoc} - */ - @Override - public void setup() - { - this.setOnlyPlayer(true); - this.setPermission("complete"); - this.setParametersHelp("challenges.commands.user.complete.parameters"); - this.setDescription("challenges.commands.user.complete.description"); - } + /** + * {@inheritDoc} + */ + @Override + public void setup() + { + this.setOnlyPlayer(true); + this.setPermission("complete"); + this.setParametersHelp("challenges.commands.user.complete.parameters"); + this.setDescription("challenges.commands.user.complete.description"); + } - /** - * {@inheritDoc} - */ - @Override - public boolean execute(User user, String label, List args) - { - if (args.isEmpty()) - { - user.sendMessage("challenges.errors.no-name"); - this.showHelp(this, user); - return false; - } - else if (!args.get(0).isEmpty()) - { - // Add world name back at the start - String challengeName = Utils.getGameMode(this.getWorld()) + "_" + args.get(0); - Challenge challenge = this.addon.getChallengesManager().getChallenge(challengeName); + /** + * {@inheritDoc} + */ + @Override + public boolean execute(User user, String label, List args) + { + if (args.isEmpty()) + { + user.sendMessage("challenges.errors.no-name"); + this.showHelp(this, user); + return false; + } + else + { + // Add world name back at the start + String challengeName = Utils.getGameMode(this.getWorld()) + "_" + args.get(0); + Challenge challenge = this.addon.getChallengesManager().getChallenge(challengeName); - if (challenge != null) - { - int count = args.size() == 2 ? Integer.valueOf(args.get(1)) : 1; + if (challenge != null) + { + int count = args.size() == 2 ? Integer.valueOf(args.get(1)) : 1; - boolean canMultipleTimes = - user.hasPermission(this.getPermission() + ".multiple"); + boolean canMultipleTimes = + user.hasPermission(this.getPermission() + ".multiple"); - if (!canMultipleTimes && count > 1) - { - user.sendMessage("challenges.error.no-multiple-permission"); - count = 1; - } + if (!canMultipleTimes && count > 1) + { + user.sendMessage("challenges.error.no-multiple-permission"); + count = 1; + } - return TryToComplete.complete(this.addon, - user, - challenge, - this.getWorld(), - this.getTopLabel(), - this.getPermissionPrefix(), - count); - } - else - { - user.sendMessage("challenges.errors.unknown-challenge"); - this.showHelp(this, user); - return false; - } - } - - this.showHelp(this, user); - return false; - } + return TryToComplete.complete(this.addon, + user, + challenge, + this.getWorld(), + this.getTopLabel(), + this.getPermissionPrefix(), + count); + } + else + { + user.sendMessage("challenges.errors.unknown-challenge"); + this.showHelp(this, user); + return false; + } + } + } - /** - * {@inheritDoc} - */ - @Override - public Optional> tabComplete(User user, String alias, List args) - { - String lastString = args.get(args.size() - 1); + /** + * {@inheritDoc} + */ + @Override + public Optional> tabComplete(User user, String alias, List args) + { + if (args.isEmpty()) return Optional.empty(); - final List returnList = new ArrayList<>(); - final int size = args.size(); + String lastString = args.get(args.size() - 1); - switch (size) - { - case 3: - // Create suggestions with all challenges that is available for users. - returnList.addAll(this.addon.getChallengesManager().getAllChallengesNames(this.getWorld()).stream(). - map(challenge -> challenge.substring(Utils.getGameMode(this.getWorld()).length() + 1)). - collect(Collectors.toList())); + final List returnList = new ArrayList<>(); + final int size = args.size(); - break; - case 4: - // Suggest a number of completions. - if (lastString.isEmpty() || lastString.matches("[0-9]*")) - { - returnList.add(""); - } + switch (size) + { + case 3: + + // Create suggestions with all challenges that is available for users. + returnList.addAll(this.addon.getChallengesManager().getAllChallengesNames(this.getWorld()).stream(). + filter(challenge -> challenge.startsWith(Utils.getGameMode(this.getWorld()) + "_")). + map(challenge -> challenge.substring(Utils.getGameMode(this.getWorld()).length() + 1)). + collect(Collectors.toList())); + break; + case 4: + // Suggest a number of completions. + if (lastString.isEmpty() || lastString.matches("[0-9]*")) + { + returnList.add(""); + } - break; - default: - { - returnList.add("help"); - break; - } - } + break; + default: + { + returnList.add("help"); + break; + } + } - return Optional.of(Util.tabLimit(returnList, lastString)); - } + return Optional.of(Util.tabLimit(returnList, lastString)); + } -// --------------------------------------------------------------------- -// Section: Variables -// --------------------------------------------------------------------- + // --------------------------------------------------------------------- + // Section: Variables + // --------------------------------------------------------------------- - /** - * Variable that holds challenge addon. Single casting. - */ - private ChallengesAddon addon; + /** + * Variable that holds challenge addon. Single casting. + */ + private ChallengesAddon addon; } diff --git a/src/main/java/world/bentobox/challenges/database/object/Challenge.java b/src/main/java/world/bentobox/challenges/database/object/Challenge.java index b532f52..cff291f 100644 --- a/src/main/java/world/bentobox/challenges/database/object/Challenge.java +++ b/src/main/java/world/bentobox/challenges/database/object/Challenge.java @@ -13,6 +13,7 @@ import org.bukkit.Material; import org.bukkit.World; import org.bukkit.entity.EntityType; import org.bukkit.inventory.ItemStack; +import org.eclipse.jdt.annotation.NonNull; import com.google.gson.annotations.Expose; import com.google.gson.annotations.JsonAdapter; @@ -717,7 +718,7 @@ public class Challenge implements DataObject * This method sets the level value. * @param level the level new value. */ - public void setLevel(String level) + public void setLevel(@NonNull String level) { this.level = level; } @@ -1050,7 +1051,7 @@ public class Challenge implements DataObject public boolean matchGameMode(String gameMode) { return gameMode != null && - this.uniqueId.regionMatches(true, 0, gameMode, 0, gameMode.length()); + this.uniqueId.regionMatches(true, 0, gameMode, 0, gameMode.length()); } @@ -1129,7 +1130,7 @@ public class Challenge implements DataObject clone.setRequirements(this.requirements.clone()); clone.setRewardText(this.rewardText); clone.setRewardItems(this.rewardItems.stream().map(ItemStack::clone). - collect(Collectors.toCollection(() -> new ArrayList<>(this.rewardItems.size())))); + collect(Collectors.toCollection(() -> new ArrayList<>(this.rewardItems.size())))); clone.setRewardExperience(this.rewardExperience); clone.setRewardMoney(this.rewardMoney); clone.setRewardCommands(new ArrayList<>(this.rewardCommands)); @@ -1138,7 +1139,7 @@ public class Challenge implements DataObject clone.setMaxTimes(this.maxTimes); clone.setRepeatExperienceReward(this.repeatExperienceReward); clone.setRepeatItemReward(this.repeatItemReward.stream().map(ItemStack::clone). - collect(Collectors.toCollection(() -> new ArrayList<>(this.repeatItemReward.size())))); + collect(Collectors.toCollection(() -> new ArrayList<>(this.repeatItemReward.size())))); clone.setRepeatMoneyReward(this.repeatMoneyReward); clone.setRepeatRewardCommands(new ArrayList<>(this.repeatRewardCommands)); } diff --git a/src/main/java/world/bentobox/challenges/database/object/ChallengesPlayerData.java b/src/main/java/world/bentobox/challenges/database/object/ChallengesPlayerData.java index 3f81b5b..db306a9 100644 --- a/src/main/java/world/bentobox/challenges/database/object/ChallengesPlayerData.java +++ b/src/main/java/world/bentobox/challenges/database/object/ChallengesPlayerData.java @@ -25,334 +25,334 @@ import world.bentobox.bentobox.database.objects.adapters.LogEntryListAdapter; */ public class ChallengesPlayerData implements DataObject { - /** - * Constructor ChallengesPlayerData creates a new ChallengesPlayerData instance. - */ - public ChallengesPlayerData() - { - } + /** + * Constructor ChallengesPlayerData creates a new ChallengesPlayerData instance. + */ + public ChallengesPlayerData() + { + } - /** - * Creates a player data entry - * - * @param uniqueId - the player's UUID in string format - */ - public ChallengesPlayerData(String uniqueId) - { - this.uniqueId = uniqueId; - } + /** + * Creates a player data entry + * + * @param uniqueId - the player's UUID in string format + */ + public ChallengesPlayerData(String uniqueId) + { + this.uniqueId = uniqueId; + } -// --------------------------------------------------------------------- -// Section: Variables -// --------------------------------------------------------------------- + // --------------------------------------------------------------------- + // Section: Variables + // --------------------------------------------------------------------- - /** - * This variable stores each player UUID as string. - */ - @Expose - private String uniqueId = ""; + /** + * This variable stores each player UUID as string. + */ + @Expose + private String uniqueId = ""; - /** - * Challenge map, where key = unique challenge name and Value = number of times - * completed - */ - @Expose - private Map challengeStatus = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); + /** + * Challenge map, where key = unique challenge name and Value = number of times + * completed + */ + @Expose + private Map challengeStatus = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); - /** - * Map of challenges completion time where key is challenges unique id and value is - * timestamp when challenge was completed last time. - */ - @Expose - private Map challengesTimestamp = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); + /** + * Map of challenges completion time where key is challenges unique id and value is + * timestamp when challenge was completed last time. + */ + @Expose + private Map challengesTimestamp = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); - /** - * Set of Strings that contains all challenge levels that are completed. - */ - @Expose - private Set levelsDone = new TreeSet<>(String.CASE_INSENSITIVE_ORDER); + /** + * Set of Strings that contains all challenge levels that are completed. + */ + @Expose + private Set levelsDone = new TreeSet<>(String.CASE_INSENSITIVE_ORDER); - /** - * Stores history about challenge completion. - */ - @Adapter(LogEntryListAdapter.class) - @Expose - private List history = new LinkedList<>(); + /** + * Stores history about challenge completion. + */ + @Adapter(LogEntryListAdapter.class) + @Expose + private List history = new LinkedList<>(); -// --------------------------------------------------------------------- -// Section: Getters -// --------------------------------------------------------------------- + // --------------------------------------------------------------------- + // Section: Getters + // --------------------------------------------------------------------- - /** - * @return uniqueID - * @see DataObject#getUniqueId() - */ - @Override - public String getUniqueId() - { - return uniqueId; - } + /** + * @return uniqueID + * @see DataObject#getUniqueId() + */ + @Override + public String getUniqueId() + { + return uniqueId; + } - /** - * This method returns the challengeStatus value. - * @return the value of challengeStatus. - */ - public Map getChallengeStatus() - { - return challengeStatus; - } + /** + * This method returns the challengeStatus value. + * @return the value of challengeStatus. + */ + public Map getChallengeStatus() + { + return challengeStatus; + } - /** - * This method returns the challengesTimestamp value. - * @return the value of challengesTimestamp. - */ - public Map getChallengesTimestamp() - { - return challengesTimestamp; - } + /** + * This method returns the challengesTimestamp value. + * @return the value of challengesTimestamp. + */ + public Map getChallengesTimestamp() + { + return challengesTimestamp; + } - /** - * This method returns the levelsDone value. - * @return the value of levelsDone. - */ - public Set getLevelsDone() - { - return levelsDone; - } + /** + * This method returns the levelsDone value. + * @return the value of levelsDone. + */ + public Set getLevelsDone() + { + return levelsDone; + } - /** - * This method returns the history object. - * @return the history object. - */ - public List getHistory() - { - return history; - } + /** + * This method returns the history object. + * @return the history object. + */ + public List getHistory() + { + return history; + } -// --------------------------------------------------------------------- -// Section: Setters -// --------------------------------------------------------------------- + // --------------------------------------------------------------------- + // Section: Setters + // --------------------------------------------------------------------- - /** - * @param uniqueId - unique ID the uniqueId to set - * @see DataObject#setUniqueId(String) - */ - @Override - public void setUniqueId(String uniqueId) - { - this.uniqueId = uniqueId; - } + /** + * @param uniqueId - unique ID the uniqueId to set + * @see DataObject#setUniqueId(String) + */ + @Override + public void setUniqueId(String uniqueId) + { + this.uniqueId = uniqueId; + } - /** - * This method sets the challengeStatus value. - * @param challengeStatus the challengeStatus new value. - * - */ - public void setChallengeStatus(Map challengeStatus) - { - this.challengeStatus = challengeStatus; - } + /** + * This method sets the challengeStatus value. + * @param challengeStatus the challengeStatus new value. + * + */ + public void setChallengeStatus(Map challengeStatus) + { + this.challengeStatus = challengeStatus; + } - /** - * This method sets the challengesTimestamp value. - * @param challengesTimestamp the challengesTimestamp new value. - * - */ - public void setChallengesTimestamp(Map challengesTimestamp) - { - this.challengesTimestamp = challengesTimestamp; - } + /** + * This method sets the challengesTimestamp value. + * @param challengesTimestamp the challengesTimestamp new value. + * + */ + public void setChallengesTimestamp(Map challengesTimestamp) + { + this.challengesTimestamp = challengesTimestamp; + } - /** - * This method sets the levelsDone value. - * @param levelsDone the levelsDone new value. - * - */ - public void setLevelsDone(Set levelsDone) - { - this.levelsDone = levelsDone; - } + /** + * This method sets the levelsDone value. + * @param levelsDone the levelsDone new value. + * + */ + public void setLevelsDone(Set levelsDone) + { + this.levelsDone = levelsDone; + } - /** - * This method sets the history object value. - * @param history the history object new value. - */ - public void setHistory(List history) - { - this.history = history; - } + /** + * This method sets the history object value. + * @param history the history object new value. + */ + public void setHistory(List history) + { + this.history = history; + } -// --------------------------------------------------------------------- -// Section: Other Methods -// --------------------------------------------------------------------- + // --------------------------------------------------------------------- + // Section: Other Methods + // --------------------------------------------------------------------- - /** - * Resets all challenges and levels in GameMode for this player - * - * @param gameMode GameMode which challenges must be reset. - */ - public void reset(@NonNull String gameMode) - { - challengeStatus.keySet().removeIf(n -> n.regionMatches(true, 0, gameMode, 0, gameMode.length())); - challengesTimestamp.keySet().removeIf(n -> n.regionMatches(true, 0, gameMode, 0, gameMode.length())); - levelsDone.removeIf(n -> n.regionMatches(true, 0, gameMode, 0, gameMode.length())); - } + /** + * Resets all challenges and levels in GameMode for this player + * + * @param gameMode GameMode which challenges must be reset. + */ + public void reset(@NonNull String gameMode) + { + challengeStatus.keySet().removeIf(n -> n.regionMatches(true, 0, gameMode, 0, gameMode.length())); + challengesTimestamp.keySet().removeIf(n -> n.regionMatches(true, 0, gameMode, 0, gameMode.length())); + levelsDone.removeIf(n -> n.regionMatches(true, 0, gameMode, 0, gameMode.length())); + } - /** - * Mark a challenge as having been completed. Will increment the number of times and - * timestamp - * - * @param challengeName - unique challenge name - */ - public void setChallengeDone(@NonNull String challengeName) - { - this.addChallengeDone(challengeName, 1); - } + /** + * Mark a challenge as having been completed. Will increment the number of times and + * timestamp + * + * @param challengeName - unique challenge name + */ + public void setChallengeDone(@NonNull String challengeName) + { + this.addChallengeDone(challengeName, 1); + } - /** - * Mark a challenge as having been completed. Will increment the number of times and - * timestamp - * - * @param challengeName - unique challenge name - * @param times - how many new times should be added - */ - public void addChallengeDone(@NonNull String challengeName, int times) - { - int newTimes = challengeStatus.getOrDefault(challengeName, 0) + times; - challengeStatus.put(challengeName, newTimes); - challengesTimestamp.put(challengeName, System.currentTimeMillis()); - } + /** + * Mark a challenge as having been completed. Will increment the number of times and + * timestamp + * + * @param challengeName - unique challenge name + * @param times - how many new times should be added + */ + public void addChallengeDone(@NonNull String challengeName, int times) + { + int newTimes = challengeStatus.getOrDefault(challengeName, 0) + times; + challengeStatus.put(challengeName, newTimes); + challengesTimestamp.put(challengeName, System.currentTimeMillis()); + } - /** - * Set the number of times a challenge has been done - * - * @param challengeName - unique challenge name - * @param times - the number of times to set - */ - public void setChallengeTimes(@NonNull String challengeName, @NonNull int times) - { - challengeStatus.put(challengeName, times); - challengesTimestamp.put(challengeName, System.currentTimeMillis()); - } + /** + * Set the number of times a challenge has been done + * + * @param challengeName - unique challenge name + * @param times - the number of times to set + */ + public void setChallengeTimes(@NonNull String challengeName, @NonNull int times) + { + challengeStatus.put(challengeName, times); + challengesTimestamp.put(challengeName, System.currentTimeMillis()); + } - /** - * Check if a challenge has been done - * - * @param challengeName - unique challenge name - * @return true if done at least once - */ - public boolean isChallengeDone(@NonNull String challengeName) - { - return this.getTimes(challengeName) > 0; - } + /** + * Check if a challenge has been done + * + * @param challengeName - unique challenge name + * @return true if done at least once + */ + public boolean isChallengeDone(@NonNull String challengeName) + { + return this.getTimes(challengeName) > 0; + } - /** - * Check how many times a challenge has been done - * - * @param challengeName - unique challenge name - * @return - number of times - */ - public int getTimes(@NonNull String challengeName) - { - return challengeStatus.getOrDefault(challengeName, 0); - } + /** + * Check how many times a challenge has been done + * + * @param challengeName - unique challenge name + * @return - number of times + */ + public int getTimes(@NonNull String challengeName) + { + return challengeStatus.getOrDefault(challengeName, 0); + } - /** - * This method adds given level id to completed level set. - * @param uniqueId from ChallengeLevel object. - */ - public void addCompletedLevel(@NonNull String uniqueId) - { - this.levelsDone.add(uniqueId); - } + /** + * This method adds given level id to completed level set. + * @param uniqueId from ChallengeLevel object. + */ + public void addCompletedLevel(@NonNull String uniqueId) + { + this.levelsDone.add(uniqueId); + } - /** - * This method returns if given level is done. - * @param uniqueId of ChallengeLevel object. - * @return true if level is completed, otherwise false - */ - public boolean isLevelDone(@NonNull String uniqueId) - { - return !this.levelsDone.isEmpty() && this.levelsDone.contains(uniqueId); - } + /** + * This method returns if given level is done. + * @param uniqueId of ChallengeLevel object. + * @return true if level is completed, otherwise false + */ + public boolean isLevelDone(@NonNull String uniqueId) + { + return !this.levelsDone.isEmpty() && this.levelsDone.contains(uniqueId); + } - /** - * This method adds given LogEntry to history. - * - * @param entry of type LogEntry - */ - public void addHistoryRecord(@NonNull LogEntry entry) - { - this.history.add(entry); - } + /** + * This method adds given LogEntry to history. + * + * @param entry of type LogEntry + */ + public void addHistoryRecord(@NonNull LogEntry entry) + { + this.history.add(entry); + } - /** - * @see Object#hashCode() - * @return object hashCode value. - */ - @Override - public int hashCode() - { - final int prime = 31; - int result = 1; - result = prime * result + ((uniqueId == null) ? 0 : uniqueId.hashCode()); - return result; - } + /** + * @see Object#hashCode() + * @return object hashCode value. + */ + @Override + public int hashCode() + { + final int prime = 31; + int result = 1; + result = prime * result + ((uniqueId == null) ? 0 : uniqueId.hashCode()); + return result; + } - /** - * @see java.lang.Object#equals(java.lang.Object) - * @param obj Other object. - * @return boolean that indicate if objects are equals. - */ - @Override - public boolean equals(Object obj) - { - if (this == obj) - { - return true; - } + /** + * @see java.lang.Object#equals(java.lang.Object) + * @param obj Other object. + * @return boolean that indicate if objects are equals. + */ + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } - if (!(obj instanceof ChallengesPlayerData)) - { - return false; - } + if (!(obj instanceof ChallengesPlayerData)) + { + return false; + } - ChallengesPlayerData other = (ChallengesPlayerData) obj; + ChallengesPlayerData other = (ChallengesPlayerData) obj; - if (uniqueId == null) - { - return other.uniqueId == null; - } - else - { - return uniqueId.equalsIgnoreCase(other.uniqueId); - } - } + if (uniqueId == null) + { + return other.uniqueId == null; + } + else + { + return uniqueId.equalsIgnoreCase(other.uniqueId); + } + } } \ No newline at end of file diff --git a/src/main/java/world/bentobox/challenges/panel/admin/EditLoreGUI.java b/src/main/java/world/bentobox/challenges/panel/admin/EditLoreGUI.java index ac229fd..860de1a 100644 --- a/src/main/java/world/bentobox/challenges/panel/admin/EditLoreGUI.java +++ b/src/main/java/world/bentobox/challenges/panel/admin/EditLoreGUI.java @@ -12,6 +12,7 @@ import org.bukkit.Material; import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.event.inventory.InventoryCloseEvent; import org.bukkit.inventory.ItemStack; +import org.eclipse.jdt.annotation.Nullable; import world.bentobox.bentobox.api.panels.PanelItem; import world.bentobox.bentobox.api.panels.PanelListener; @@ -29,605 +30,609 @@ import world.bentobox.challenges.utils.GuiUtils; */ public class EditLoreGUI extends CommonGUI { - public EditLoreGUI(CommonGUI parent, LoreType loreType) - { - super(parent); - - this.lore = loreType; - this.activeValues = new ArrayList<>(); - - switch (this.lore) - { - case CHALLENGES: - - for (ChallengeLore lore : this.addon.getChallengesSettings().getChallengeLoreMessage()) - { - this.activeValues.add(lore.name()); - } - - break; - case LEVELS: - - for (LevelLore lore : this.addon.getChallengesSettings().getLevelLoreMessage()) - { - this.activeValues.add(lore.name()); - } - - break; - } - } - - - /** - * This is static call method for easier GUI opening. - * @param parent Parent GUI. - * @param loreType loreType that will be edited. - */ - public static void open(CommonGUI parent, LoreType loreType) - { - new EditLoreGUI(parent, loreType).build(); - } - - -// --------------------------------------------------------------------- -// Section: Methods -// --------------------------------------------------------------------- - - - /** - * This method builds panel that allows to change given number value. - */ - @Override - public void build() - { - PanelBuilder panelBuilder = new PanelBuilder(). - name(this.user.getTranslation("challenges.gui.title.admin.lore-edit")). - user(this.user). - listener(new CustomPanelListener()); - - GuiUtils.fillBorder(panelBuilder, 5, Material.MAGENTA_STAINED_GLASS_PANE); - - // Define all active buttons - panelBuilder.item(1, this.getButton(Button.SAVE)); - - panelBuilder.item(3, this.getButton(Button.ADD)); - panelBuilder.item(4, this.getButton(Button.REMOVE)); - - // TODO: Need 2 View Buttons - // One for closes / One for opened. -// panelBuilder.item(6, this.getButton(Button.VIEW)); - - panelBuilder.item(44, this.returnButton); - - // necessary as I have a border around this GUI - int currentIndex = 10; - - // Only 21 elements will be displayed. On porpoise! - for (int i = 0; i < this.activeValues.size() || i > 21; i++) - { - panelBuilder.item(currentIndex++, this.getLoreButton(this.activeValues.get(i))); - - // Border element - if (currentIndex % 9 == 8) - { - currentIndex += 2; - } - - // Just in case. Should never occur. - if (currentIndex % 9 == 0) - { - currentIndex++; - } - } - - panelBuilder.build(); - } - - - /** - * This method create button that does some functionality in current gui. - * @param button Button functionality. - * @return PanelItem. - */ - private PanelItem getButton(Button button) - { - ItemStack icon; - String name; - List description; - PanelItem.ClickHandler clickHandler; - - switch (button) - { - case SAVE: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.save"); - description = Collections.emptyList(); - icon = new ItemStack(Material.COMMAND_BLOCK); - clickHandler = (panel, user, clickType, slot) -> { - - switch (this.lore) - { - case CHALLENGES: - { - List lore = this.activeValues.stream(). - map(ChallengeLore::valueOf). - collect(Collectors.toCollection(() -> new ArrayList<>(this.activeValues.size()))); - - this.addon.getChallengesSettings().setChallengeLoreMessage(lore); - - break; - } - case LEVELS: - { - List lore = this.activeValues.stream(). - map(LevelLore::valueOf). - collect(Collectors.toCollection(() -> new ArrayList<>(this.activeValues.size()))); - - this.addon.getChallengesSettings().setLevelLoreMessage(lore); - - break; - } - } - - // Save and return to parent gui. - this.parentGUI.build(); - - return true; - }; - break; - } - case ADD: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.add"); - description = Collections.emptyList(); - icon = new ItemStack(Material.GREEN_STAINED_GLASS_PANE); - clickHandler = (panel, user, clickType, slot) -> { - new AddLoreElementGUI(element -> { - this.activeValues.add(element); - this.build(); - }); - - return true; - }; - - break; - } - case REMOVE: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.remove-selected"); - description = Collections.emptyList(); - icon = new ItemStack(Material.RED_STAINED_GLASS_PANE); - clickHandler = (panel, user, clickType, slot) -> { - new RemoveLoreElementGUI((element, index) -> { - if (this.activeValues.get(index).equals(element)) - { - this.activeValues.remove(element); - } - - this.build(); - }); - - return true; - }; - - break; - } - case VIEW: - { - name = this.user.getTranslation("challenges.gui.buttons.admin.view"); - description = Collections.emptyList(); - icon = new ItemStack(Material.YELLOW_STAINED_GLASS_PANE); - clickHandler = (panel, user, clickType, slot) -> { - return true; - }; - - break; - } - default: - return null; - } - - return new PanelItemBuilder(). - icon(icon). - name(name). - description(GuiUtils.stringSplit(description, this.addon.getChallengesSettings().getLoreLineLength())). - glow(false). - clickHandler(clickHandler). - build(); - } - - - /** - * This method creates button for lore element. - * @param loreElement String that represents current lore element. - * @return PanelItem. - */ - private PanelItem getLoreButton(String loreElement) - { - switch (this.lore) - { - case CHALLENGES: - return this.getChallengeLoreButton(loreElement); - case LEVELS: - return this.getLevelLoreButton(loreElement); - default: - // this should never happen! - return null; - } - } - - - /** - * This method creates button for challenge lore element. - * @param loreElement String that represents current challenge lore element. - * @return PanelItem. - */ - private PanelItem getChallengeLoreButton(String loreElement) - { - Material icon; - String name = loreElement; - List description = new ArrayList<>(); - description.add(this.user.getTranslation(REFERENCE_DESCRIPTION + "lore." + loreElement.toLowerCase())); - - PanelItem.ClickHandler clickHandler = (panel, user1, clickType, slot) -> true; - - switch (ChallengeLore.valueOf(loreElement)) - { - case LEVEL: - { - icon = Material.DIRT; - break; - } - case STATUS: - { - icon = Material.LEVER; - break; - } - case COUNT: - { - icon = Material.REPEATER; - break; - } - case DESCRIPTION: - { - icon = Material.WRITTEN_BOOK; - break; - } - case WARNINGS: - { - icon = Material.LAVA_BUCKET; - break; - } - case ENVIRONMENT: - { - icon = Material.GLASS; - break; - } - case REQUIREMENTS: - { - icon = Material.HOPPER; - break; - } - case REWARD_TEXT: - { - icon = Material.PAPER; - break; - } - case REWARD_OTHER: - { - icon = Material.CHEST; - break; - } - case REWARD_ITEMS: - { - icon = Material.TRAPPED_CHEST; - break; - } - case REWARD_COMMANDS: - { - icon = Material.COMMAND_BLOCK; - break; - } - default: - { - icon = Material.BARRIER; - break; - } - } - - return new PanelItemBuilder(). - name(name). - icon(icon). - description(GuiUtils.stringSplit(description, this.addon.getChallengesSettings().getLoreLineLength())). - clickHandler(clickHandler). - glow(false). - build(); - } - - - /** - * This method creates button for challenge level lore element. - * @param loreElement String that represents current challenge level lore element. - * @return PanelItem. - */ - private PanelItem getLevelLoreButton(String loreElement) - { - Material icon; - String name = loreElement; - List description = new ArrayList<>(); - description.add(this.user.getTranslation(REFERENCE_DESCRIPTION + "lore." + loreElement.toLowerCase())); - - PanelItem.ClickHandler clickHandler = (panel, user1, clickType, slot) -> true; - - switch (LevelLore.valueOf(loreElement)) - { - case LEVEL_STATUS: - { - icon = Material.DIRT; - break; - } - case CHALLENGE_COUNT: - { - icon = Material.REPEATER; - break; - } - case UNLOCK_MESSAGE: - { - icon = Material.WRITTEN_BOOK; - break; - } - case WAIVER_AMOUNT: - { - icon = Material.COMPARATOR; - break; - } - case LEVEL_REWARD_TEXT: - { - icon = Material.PAPER; - break; - } - case LEVEL_REWARD_OTHER: - { - icon = Material.CHEST; - break; - } - case LEVEL_REWARD_ITEMS: - { - icon = Material.TRAPPED_CHEST; - break; - } - case LEVEL_REWARD_COMMANDS: - { - icon = Material.COMMAND_BLOCK; - break; - } - default: - { - icon = Material.BARRIER; - break; - } - } - - return new PanelItemBuilder(). - name(name). - icon(icon). - description(GuiUtils.stringSplit(description, this.addon.getChallengesSettings().getLoreLineLength())). - clickHandler(clickHandler). - glow(false). - build(); - } - - -// --------------------------------------------------------------------- -// Section: Select GUI -// --------------------------------------------------------------------- - - - /** - * This class opens new GUI that add an element from all available lore values. - */ - private class AddLoreElementGUI - { - private AddLoreElementGUI(Consumer selectedElement) - { - PanelBuilder panelBuilder = new PanelBuilder(). - name(EditLoreGUI.this.user.getTranslation("challenges.gui.title.admin.lore-add")). - user(EditLoreGUI.this.user); - - GuiUtils.fillBorder(panelBuilder, 5, Material.MAGENTA_STAINED_GLASS_PANE); - - int currentIndex = 10; - - List values = new ArrayList<>(); - - // Populate list with all elements. - switch (EditLoreGUI.this.lore) - { - case CHALLENGES: - for (ChallengeLore value : ChallengeLore.values()) - { - values.add(value.name()); - } - break; - case LEVELS: - for (LevelLore value : LevelLore.values()) - { - values.add(value.name()); - } - break; - } - - for (String value : values) - { - PanelItem item = EditLoreGUI.this.getLoreButton(value); - - item.setClickHandler((panel, user1, clickType, slot) -> { - selectedElement.accept(value); - return true; - }); - - panelBuilder.item(currentIndex++, item); - - // Border element - if (currentIndex % 9 == 8) - { - currentIndex += 2; - } - - // Just in case. Should never occur. - if (currentIndex % 9 == 0) - { - currentIndex++; - } - - // Just in case. Should never occur. - if (currentIndex > 35) - { - break; - } - } - - panelBuilder.build(); - } - } - - - /** - * This class opens new GUI that remove an element from all available lore values. - */ - private class RemoveLoreElementGUI - { - private RemoveLoreElementGUI(BiConsumer selectedElement) - { - PanelBuilder panelBuilder = new PanelBuilder(). - name(EditLoreGUI.this.user.getTranslation("challenges.gui.title.admin.lore-remove")). - user(EditLoreGUI.this.user); - - GuiUtils.fillBorder(panelBuilder, 5, Material.MAGENTA_STAINED_GLASS_PANE); - - int currentIndex = 10; - - List values = EditLoreGUI.this.activeValues; - - for (int i = 0; i < values.size(); i++) - { - final int counter = i; - - String value = values.get(counter); - PanelItem item = EditLoreGUI.this.getLoreButton(value); - - item.setClickHandler((panel, user1, clickType, slot) -> { - selectedElement.accept(value, counter); - return true; - }); - - panelBuilder.item(currentIndex++, item); - - // Border element - if (currentIndex % 9 == 8) - { - currentIndex += 2; - } - - // Just in case. Should never occur. - if (currentIndex % 9 == 0) - { - currentIndex++; - } - - // Just in case. Should never occur. - if (currentIndex > 35) - { - break; - } - } - - panelBuilder.build(); - } - } - - -// --------------------------------------------------------------------- -// Section: Private classes -// --------------------------------------------------------------------- - - - /** - * This CustomPanelListener allows to move items in current panel. - */ - private class CustomPanelListener implements PanelListener - { - @Override - public void setup() - { - } - - - @Override - public void onInventoryClose(InventoryCloseEvent inventoryCloseEvent) - { - } - - - @Override - public void onInventoryClick(User user, InventoryClickEvent event) - { - // First row of elements should be ignored, as it contains buttons and blocked slots. - event.setCancelled(event.getRawSlot() < 9 || - event.getRawSlot() < 35 || - event.getRawSlot() % 9 == 0 || - event.getRawSlot() % 9 == 8); - } - } + public EditLoreGUI(CommonGUI parent, LoreType loreType) + { + super(parent); + + this.lore = loreType; + this.activeValues = new ArrayList<>(); + + switch (this.lore) + { + case CHALLENGES: + + for (ChallengeLore lore : this.addon.getChallengesSettings().getChallengeLoreMessage()) + { + this.activeValues.add(lore.name()); + } + + break; + case LEVELS: + + for (LevelLore lore : this.addon.getChallengesSettings().getLevelLoreMessage()) + { + this.activeValues.add(lore.name()); + } + + break; + } + } + + + /** + * This is static call method for easier GUI opening. + * @param parent Parent GUI. + * @param loreType loreType that will be edited. + */ + public static void open(CommonGUI parent, LoreType loreType) + { + new EditLoreGUI(parent, loreType).build(); + } + + + // --------------------------------------------------------------------- + // Section: Methods + // --------------------------------------------------------------------- + + + /** + * This method builds panel that allows to change given number value. + */ + @Override + public void build() + { + PanelBuilder panelBuilder = new PanelBuilder(). + name(this.user.getTranslation("challenges.gui.title.admin.lore-edit")). + user(this.user). + listener(new CustomPanelListener()); + + GuiUtils.fillBorder(panelBuilder, 5, Material.MAGENTA_STAINED_GLASS_PANE); + + // Define all active buttons + panelBuilder.item(1, this.getButton(Button.SAVE)); + + panelBuilder.item(3, this.getButton(Button.ADD)); + panelBuilder.item(4, this.getButton(Button.REMOVE)); + + // TODO: Need 2 View Buttons + // One for closes / One for opened. + // panelBuilder.item(6, this.getButton(Button.VIEW)); + + panelBuilder.item(44, this.returnButton); + + // necessary as I have a border around this GUI + int currentIndex = 10; + + // Only 21 elements will be displayed. On porpoise! + for (int i = 0; i < this.activeValues.size() || i > 21; i++) + { + panelBuilder.item(currentIndex++, this.getLoreButton(this.activeValues.get(i))); + + // Border element + if (currentIndex % 9 == 8) + { + currentIndex += 2; + } + + // Just in case. Should never occur. + if (currentIndex % 9 == 0) + { + currentIndex++; + } + } + + panelBuilder.build(); + } + + + /** + * This method create button that does some functionality in current gui. + * @param button Button functionality. + * @return PanelItem. + */ + private PanelItem getButton(Button button) + { + ItemStack icon; + String name; + List description; + PanelItem.ClickHandler clickHandler; + + switch (button) + { + case SAVE: + { + name = this.user.getTranslation("challenges.gui.buttons.admin.save"); + description = Collections.emptyList(); + icon = new ItemStack(Material.COMMAND_BLOCK); + clickHandler = (panel, user, clickType, slot) -> { + + switch (this.lore) + { + case CHALLENGES: + { + List lore = this.activeValues.stream(). + map(ChallengeLore::valueOf). + collect(Collectors.toCollection(() -> new ArrayList<>(this.activeValues.size()))); + + this.addon.getChallengesSettings().setChallengeLoreMessage(lore); + + break; + } + case LEVELS: + { + List lore = this.activeValues.stream(). + map(LevelLore::valueOf). + collect(Collectors.toCollection(() -> new ArrayList<>(this.activeValues.size()))); + + this.addon.getChallengesSettings().setLevelLoreMessage(lore); + + break; + } + } + + // Save and return to parent gui. + this.parentGUI.build(); + + return true; + }; + break; + } + case ADD: + { + name = this.user.getTranslation("challenges.gui.buttons.admin.add"); + description = Collections.emptyList(); + icon = new ItemStack(Material.GREEN_STAINED_GLASS_PANE); + clickHandler = (panel, user, clickType, slot) -> { + new AddLoreElementGUI(element -> { + this.activeValues.add(element); + this.build(); + }); + + return true; + }; + + break; + } + case REMOVE: + { + name = this.user.getTranslation("challenges.gui.buttons.admin.remove-selected"); + description = Collections.emptyList(); + icon = new ItemStack(Material.RED_STAINED_GLASS_PANE); + clickHandler = (panel, user, clickType, slot) -> { + new RemoveLoreElementGUI((element, index) -> { + if (this.activeValues.get(index).equals(element)) + { + this.activeValues.remove(element); + } + + this.build(); + }); + + return true; + }; + + break; + } + case VIEW: + { + name = this.user.getTranslation("challenges.gui.buttons.admin.view"); + description = Collections.emptyList(); + icon = new ItemStack(Material.YELLOW_STAINED_GLASS_PANE); + clickHandler = (panel, user, clickType, slot) -> { + return true; + }; + + break; + } + default: + return null; + } + + return new PanelItemBuilder(). + icon(icon). + name(name). + description(GuiUtils.stringSplit(description, this.addon.getChallengesSettings().getLoreLineLength())). + glow(false). + clickHandler(clickHandler). + build(); + } + + + /** + * This method creates button for lore element. + * @param loreElement String that represents current lore element. + * @return PanelItem. + */ + @Nullable + private PanelItem getLoreButton(String loreElement) + { + switch (this.lore) + { + case CHALLENGES: + return this.getChallengeLoreButton(loreElement); + case LEVELS: + return this.getLevelLoreButton(loreElement); + default: + // this should never happen! + return null; + } + } + + + /** + * This method creates button for challenge lore element. + * @param loreElement String that represents current challenge lore element. + * @return PanelItem. + */ + private PanelItem getChallengeLoreButton(String loreElement) + { + Material icon; + String name = loreElement; + List description = new ArrayList<>(); + description.add(this.user.getTranslation(REFERENCE_DESCRIPTION + "lore." + loreElement.toLowerCase())); + + PanelItem.ClickHandler clickHandler = (panel, user1, clickType, slot) -> true; + + switch (ChallengeLore.valueOf(loreElement)) + { + case LEVEL: + { + icon = Material.DIRT; + break; + } + case STATUS: + { + icon = Material.LEVER; + break; + } + case COUNT: + { + icon = Material.REPEATER; + break; + } + case DESCRIPTION: + { + icon = Material.WRITTEN_BOOK; + break; + } + case WARNINGS: + { + icon = Material.LAVA_BUCKET; + break; + } + case ENVIRONMENT: + { + icon = Material.GLASS; + break; + } + case REQUIREMENTS: + { + icon = Material.HOPPER; + break; + } + case REWARD_TEXT: + { + icon = Material.PAPER; + break; + } + case REWARD_OTHER: + { + icon = Material.CHEST; + break; + } + case REWARD_ITEMS: + { + icon = Material.TRAPPED_CHEST; + break; + } + case REWARD_COMMANDS: + { + icon = Material.COMMAND_BLOCK; + break; + } + default: + { + icon = Material.BARRIER; + break; + } + } + + return new PanelItemBuilder(). + name(name). + icon(icon). + description(GuiUtils.stringSplit(description, this.addon.getChallengesSettings().getLoreLineLength())). + clickHandler(clickHandler). + glow(false). + build(); + } + + + /** + * This method creates button for challenge level lore element. + * @param loreElement String that represents current challenge level lore element. + * @return PanelItem. + */ + private PanelItem getLevelLoreButton(String loreElement) + { + Material icon; + String name = loreElement; + List description = new ArrayList<>(); + description.add(this.user.getTranslation(REFERENCE_DESCRIPTION + "lore." + loreElement.toLowerCase())); + + PanelItem.ClickHandler clickHandler = (panel, user1, clickType, slot) -> true; + + switch (LevelLore.valueOf(loreElement)) + { + case LEVEL_STATUS: + { + icon = Material.DIRT; + break; + } + case CHALLENGE_COUNT: + { + icon = Material.REPEATER; + break; + } + case UNLOCK_MESSAGE: + { + icon = Material.WRITTEN_BOOK; + break; + } + case WAIVER_AMOUNT: + { + icon = Material.COMPARATOR; + break; + } + case LEVEL_REWARD_TEXT: + { + icon = Material.PAPER; + break; + } + case LEVEL_REWARD_OTHER: + { + icon = Material.CHEST; + break; + } + case LEVEL_REWARD_ITEMS: + { + icon = Material.TRAPPED_CHEST; + break; + } + case LEVEL_REWARD_COMMANDS: + { + icon = Material.COMMAND_BLOCK; + break; + } + default: + { + icon = Material.BARRIER; + break; + } + } + + return new PanelItemBuilder(). + name(name). + icon(icon). + description(GuiUtils.stringSplit(description, this.addon.getChallengesSettings().getLoreLineLength())). + clickHandler(clickHandler). + glow(false). + build(); + } + + + // --------------------------------------------------------------------- + // Section: Select GUI + // --------------------------------------------------------------------- + + + /** + * This class opens new GUI that add an element from all available lore values. + */ + private class AddLoreElementGUI + { + private AddLoreElementGUI(Consumer selectedElement) + { + PanelBuilder panelBuilder = new PanelBuilder(). + name(EditLoreGUI.this.user.getTranslation("challenges.gui.title.admin.lore-add")). + user(EditLoreGUI.this.user); + + GuiUtils.fillBorder(panelBuilder, 5, Material.MAGENTA_STAINED_GLASS_PANE); + + int currentIndex = 10; + + List values = new ArrayList<>(); + + // Populate list with all elements. + switch (EditLoreGUI.this.lore) + { + case CHALLENGES: + for (ChallengeLore value : ChallengeLore.values()) + { + values.add(value.name()); + } + break; + case LEVELS: + for (LevelLore value : LevelLore.values()) + { + values.add(value.name()); + } + break; + } + + for (String value : values) + { + PanelItem item = EditLoreGUI.this.getLoreButton(value); + if (item != null) { + item.setClickHandler((panel, user1, clickType, slot) -> { + selectedElement.accept(value); + return true; + }); + + panelBuilder.item(currentIndex++, item); + + + // Border element + if (currentIndex % 9 == 8) + { + currentIndex += 2; + } + + // Just in case. Should never occur. + if (currentIndex % 9 == 0) + { + currentIndex++; + } + + // Just in case. Should never occur. + if (currentIndex > 35) + { + break; + } + } + } + + panelBuilder.build(); + } + } + + + /** + * This class opens new GUI that remove an element from all available lore values. + */ + private class RemoveLoreElementGUI + { + private RemoveLoreElementGUI(BiConsumer selectedElement) + { + PanelBuilder panelBuilder = new PanelBuilder(). + name(EditLoreGUI.this.user.getTranslation("challenges.gui.title.admin.lore-remove")). + user(EditLoreGUI.this.user); + + GuiUtils.fillBorder(panelBuilder, 5, Material.MAGENTA_STAINED_GLASS_PANE); + + int currentIndex = 10; + + List values = EditLoreGUI.this.activeValues; + + for (int i = 0; i < values.size(); i++) + { + final int counter = i; + + String value = values.get(counter); + PanelItem item = EditLoreGUI.this.getLoreButton(value); + if (item != null) { + item.setClickHandler((panel, user1, clickType, slot) -> { + selectedElement.accept(value, counter); + return true; + }); + + panelBuilder.item(currentIndex++, item); + + // Border element + if (currentIndex % 9 == 8) + { + currentIndex += 2; + } + + // Just in case. Should never occur. + if (currentIndex % 9 == 0) + { + currentIndex++; + } + + // Just in case. Should never occur. + if (currentIndex > 35) + { + break; + } + } + } + + panelBuilder.build(); + } + } + + + // --------------------------------------------------------------------- + // Section: Private classes + // --------------------------------------------------------------------- + + + /** + * This CustomPanelListener allows to move items in current panel. + */ + private class CustomPanelListener implements PanelListener + { + @Override + public void setup() + { + } + + + @Override + public void onInventoryClose(InventoryCloseEvent inventoryCloseEvent) + { + } + + + @Override + public void onInventoryClick(User user, InventoryClickEvent event) + { + // First row of elements should be ignored, as it contains buttons and blocked slots. + event.setCancelled(event.getRawSlot() < 9 || + event.getRawSlot() < 35 || + event.getRawSlot() % 9 == 0 || + event.getRawSlot() % 9 == 8); + } + } + + + // --------------------------------------------------------------------- + // Section: Enums + // --------------------------------------------------------------------- + + + /** + * This enum holds all button values in current gui. + */ + private enum Button + { + SAVE, + ADD, + REMOVE, + VIEW, + RETURN + } + + + /** + * This enum holds which Lore is edited with current GUI. + */ + public enum LoreType + { + CHALLENGES, + LEVELS, + } + + + // --------------------------------------------------------------------- + // Section: Variables + // --------------------------------------------------------------------- + + /** + * Lore that will be edited with current GUI. + */ + private final LoreType lore; + + /** + * List of lore elements that are currently enabled. + */ + private List activeValues; + + + // --------------------------------------------------------------------- + // Section: Constants + // --------------------------------------------------------------------- - -// --------------------------------------------------------------------- -// Section: Enums -// --------------------------------------------------------------------- - - - /** - * This enum holds all button values in current gui. - */ - private enum Button - { - SAVE, - ADD, - REMOVE, - VIEW, - RETURN - } - - - /** - * This enum holds which Lore is edited with current GUI. - */ - public enum LoreType - { - CHALLENGES, - LEVELS, - } - - -// --------------------------------------------------------------------- -// Section: Variables -// --------------------------------------------------------------------- - - /** - * Lore that will be edited with current GUI. - */ - private final LoreType lore; - - /** - * List of lore elements that are currently enabled. - */ - private List activeValues; - - -// --------------------------------------------------------------------- -// Section: Constants -// --------------------------------------------------------------------- - - - private final static String REFERENCE_DESCRIPTION = "challenges.gui.descriptions.admin."; + + private final static String REFERENCE_DESCRIPTION = "challenges.gui.descriptions.admin."; } diff --git a/src/main/java/world/bentobox/challenges/panel/user/ChallengesGUI.java b/src/main/java/world/bentobox/challenges/panel/user/ChallengesGUI.java index fd336ea..38005ad 100644 --- a/src/main/java/world/bentobox/challenges/panel/user/ChallengesGUI.java +++ b/src/main/java/world/bentobox/challenges/panel/user/ChallengesGUI.java @@ -78,7 +78,7 @@ public class ChallengesGUI extends CommonGUI // Do not open gui if there is no challenges. if (!this.challengesManager.hasAnyChallengeData(this.world)) { - this.addon.getLogger().severe("There are no challenges set up!"); + this.addon.logError("There are no challenges set up!"); this.user.sendMessage("challenges.errors.no-challenges"); return; } diff --git a/src/main/java/world/bentobox/challenges/tasks/TryToComplete.java b/src/main/java/world/bentobox/challenges/tasks/TryToComplete.java index 5e53e6e..6185dec 100644 --- a/src/main/java/world/bentobox/challenges/tasks/TryToComplete.java +++ b/src/main/java/world/bentobox/challenges/tasks/TryToComplete.java @@ -1,6 +1,3 @@ -/** - * - */ package world.bentobox.challenges.tasks; @@ -16,6 +13,7 @@ import java.util.PriorityQueue; import java.util.Queue; import java.util.stream.Collectors; +import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.GameMode; import org.bukkit.Material; @@ -178,7 +176,7 @@ public class TryToComplete this.permissionPrefix = permissionPrefix; this.user = user; this.manager = addon.getChallengesManager(); - // To avoid any modifications that may accure to challenges in current completion + // To avoid any modifications that may occur to challenges in current completion // just clone it. this.challenge = challenge.clone(); this.topLabel = topLabel; @@ -239,7 +237,7 @@ public class TryToComplete * This method checks if challenge can be done, and complete it, if it is possible. * @return ChallengeResult object, that contains completion status. */ - public ChallengeResult build(int maxTimes) + ChallengeResult build(int maxTimes) { // Check if can complete challenge ChallengeResult result = this.checkIfCanCompleteChallenge(maxTimes); @@ -304,7 +302,7 @@ public class TryToComplete if (this.addon.getChallengesSettings().isBroadcastMessages()) { - for (Player player : this.addon.getServer().getOnlinePlayers()) + for (Player player : Bukkit.getOnlinePlayers()) { // Only other players should see message. if (!player.getUniqueId().equals(this.user.getUniqueId())) @@ -349,7 +347,7 @@ public class TryToComplete if (this.addon.isEconomyProvided()) { this.addon.getEconomyProvider().deposit(this.user, - this.challenge.getRepeatMoneyReward() * rewardFactor); + (double)this.challenge.getRepeatMoneyReward() * rewardFactor); } // Experience Repeat Reward @@ -521,7 +519,6 @@ public class TryToComplete ChallengeResult result; ChallengeType type = this.challenge.getChallengeType(); - // Check the world if (!this.challenge.isDeployed()) { @@ -609,7 +606,6 @@ public class TryToComplete { result.setCompleted(this.manager.isChallengeComplete(this.user, this.world, this.challenge)); } - // Everything fails till this point. return result; } @@ -881,6 +877,11 @@ public class TryToComplete // Players should not be able to complete challenge if they stay near island with required blocks. Island island = this.addon.getIslands().getIsland(this.world, this.user); + + if (island == null) { + // Just in case. Should never hit because there is a check if the player is on this island further up + return EMPTY_RESULT; + } if (boundingBox.getMinX() < island.getMinX()) { @@ -1316,7 +1317,7 @@ public class TryToComplete // --------------------------------------------------------------------- -// Section: Private classes +// Section: Result classes // --------------------------------------------------------------------- @@ -1325,7 +1326,7 @@ public class TryToComplete * * @author tastybento */ - private class ChallengeResult + class ChallengeResult { /** * This method sets that challenge meets all requirements at least once. diff --git a/src/main/java/world/bentobox/challenges/utils/Utils.java b/src/main/java/world/bentobox/challenges/utils/Utils.java index 45508d8..b67593a 100644 --- a/src/main/java/world/bentobox/challenges/utils/Utils.java +++ b/src/main/java/world/bentobox/challenges/utils/Utils.java @@ -26,7 +26,7 @@ public class Utils { List returnItems = new ArrayList<>(requiredItems.size()); - // Group all equal items in singe stack, as otherwise it will be too complicated to check if all + // Group all equal items in single stack, as otherwise it will be too complicated to check if all // items are in players inventory. for (ItemStack item : requiredItems) { diff --git a/src/main/java/world/bentobox/challenges/web/WebManager.java b/src/main/java/world/bentobox/challenges/web/WebManager.java index d2545f6..702d7ef 100644 --- a/src/main/java/world/bentobox/challenges/web/WebManager.java +++ b/src/main/java/world/bentobox/challenges/web/WebManager.java @@ -126,7 +126,6 @@ public class WebManager * @param user User who inits request. * @param world Target world where challenges should be loaded. * @param entry Entry that contains information about requested object. - * @return {@code true} if request was successful, {@code false} otherwise. */ public void requestEntryGitHubData(User user, World world, LibraryEntry entry) { diff --git a/src/test/java/world/bentobox/challenges/ChallengesAddonTest.java b/src/test/java/world/bentobox/challenges/ChallengesAddonTest.java index b0d8992..3f633b3 100644 --- a/src/test/java/world/bentobox/challenges/ChallengesAddonTest.java +++ b/src/test/java/world/bentobox/challenges/ChallengesAddonTest.java @@ -1,117 +1,414 @@ -/** - * - */ package world.bentobox.challenges; -import static org.mockito.Matchers.any; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +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.ArrayList; -import java.util.HashMap; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.charset.Charset; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; import java.util.List; -import java.util.Map; +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.Material; -import org.bukkit.OfflinePlayer; import org.bukkit.Server; -import org.bukkit.World; -import org.bukkit.enchantments.Enchantment; +import org.bukkit.UnsafeValues; +import org.bukkit.entity.Player; import org.bukkit.inventory.ItemFactory; -import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.meta.PotionMeta; +import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.plugin.PluginManager; -import org.bukkit.potion.PotionData; -import org.bukkit.potion.PotionType; -import org.junit.BeforeClass; +import org.bukkit.scheduler.BukkitScheduler; +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.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 com.google.gson.Gson; -import com.google.gson.GsonBuilder; - -import world.bentobox.challenges.database.object.Challenge; -import world.bentobox.challenges.database.object.Challenge.ChallengeType; +import world.bentobox.bentobox.BentoBox; +import world.bentobox.bentobox.Settings; +import world.bentobox.bentobox.api.addons.Addon; +import world.bentobox.bentobox.api.addons.Addon.State; +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.configuration.Config; +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; /** * @author tastybento * */ @RunWith(PowerMockRunner.class) +@PrepareForTest({Bukkit.class, BentoBox.class, User.class, Config.class }) public class ChallengesAddonTest { + @Mock + private User user; + @Mock + private IslandsManager im; + @Mock + private Island island; + + private ChallengesAddon addon; + @Mock + private BentoBox plugin; + @Mock + private FlagsManager fm; + @Mock + private Settings settings; + @Mock + private GameModeAddon gameMode; + @Mock + private AddonsManager am; + @Mock + private BukkitScheduler scheduler; + /** * @throws java.lang.Exception */ - @BeforeClass - public static void setUpBeforeClass() throws Exception { - Server server = mock(Server.class); - World world = mock(World.class); - world = mock(World.class); - Mockito.when(server.getLogger()).thenReturn(Logger.getAnonymousLogger()); - Mockito.when(server.getWorld("world")).thenReturn(world); - Mockito.when(server.getVersion()).thenReturn("BSB_Mocking"); - - PluginManager pluginManager = mock(PluginManager.class); - when(server.getPluginManager()).thenReturn(pluginManager); - - ItemFactory itemFactory = mock(ItemFactory.class); - when(server.getItemFactory()).thenReturn(itemFactory); - - Bukkit.setServer(server); - - PotionMeta potionMeta = mock(PotionMeta.class); - when(itemFactory.getItemMeta(any())).thenReturn(potionMeta); - - OfflinePlayer offlinePlayer = mock(OfflinePlayer.class); - when(Bukkit.getOfflinePlayer(any(UUID.class))).thenReturn(offlinePlayer); - when(offlinePlayer.getName()).thenReturn("tastybento"); + @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); - when(Bukkit.getItemFactory()).thenReturn(itemFactory); + // Player + Player p = mock(Player.class); + // Sometimes use Mockito.withSettings().verboseLogging() + when(user.isOp()).thenReturn(false); + UUID 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 + island = mock(Island.class); + 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 ChallengesAddon(); + File jFile = new File("addon.jar"); + List lines = Arrays.asList("# ChallengesAddon Configuration", "uniqueId: config"); + Path path = Paths.get("config.yml"); + Files.write(path, lines, Charset.forName("UTF-8")); + 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); + } + } + } + File dataFolder = new File("addons/Challenges"); + addon.setDataFolder(dataFolder); + addon.setFile(jFile); + AddonDescription desc = new AddonDescription.Builder("bentobox", "challenges", "1.3").description("test").authors("BONNe").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 + CompositeCommand cmd = mock(CompositeCommand.class); + @NonNull + Optional opCmd = Optional.of(cmd); + when(gameMode.getPlayerCommand()).thenReturn(opCmd); + // Admin command + when(gameMode.getAdminCommand()).thenReturn(opCmd); + + // 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); + } + /** + * @throws java.lang.Exception + */ + @After + public void tearDown() throws Exception { + new File("addon.jar").delete(); + new File("config.yml").delete(); + deleteAll(new File("addons")); + deleteAll(new File("database")); + + } + + private 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.challenges.ChallengesAddon#onLoad()}. + */ @Test - public void test() { + public void testOnLoad() { + addon.onLoad(); + // Check that config.yml file has been saved + File check = new File("addons/Challenges","config.yml"); + assertTrue(check.exists()); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesAddon#onEnable()}. + */ + @Test + public void testOnEnableDisabledPlugin() { + when(plugin.isEnabled()).thenReturn(false); + addon.onEnable(); + verify(plugin).logError("[challenges] BentoBox is not available or disabled!"); + assertEquals(Addon.State.DISABLED, addon.getState()); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesAddon#onEnable()}. + */ + @Test + public void testOnEnableDisabledAddon() { + when(plugin.isEnabled()).thenReturn(true); + addon.setState(State.DISABLED); + addon.onEnable(); + verify(plugin).logError("[challenges] Challenges Addon is not available or disabled!"); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesAddon#onEnable()}. + */ + @Test + public void testOnEnableIncompatibleDatabase() { + // The database type has to be created one line before the thenReturn() to work! + DatabaseType value = DatabaseType.YAML; + when(settings.getDatabaseType()).thenReturn(value); + when(plugin.isEnabled()).thenReturn(true); + addon.setState(State.LOADED); + addon.onEnable(); + verify(plugin).logError("[challenges] BentoBox database is not compatible with Challenges Addon."); + verify(plugin).logError("[challenges] Please use JSON based database type."); + assertEquals(State.INCOMPATIBLE, addon.getState()); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesAddon#onEnable()}. + */ + @Test + public void testOnEnableHooked() { + addon.onLoad(); + when(plugin.isEnabled()).thenReturn(true); + addon.setState(State.LOADED); + addon.onEnable(); + verify(plugin).logWarning("[challenges] Level add-on not found so level challenges will not work!"); + verify(plugin).logWarning("[challenges] Economy plugin not found so money options will not work!"); + verify(plugin).log("[challenges] Loading challenges..."); + verify(plugin, never()).logError("Challenges could not hook into AcidIsland or BSkyBlock so will not do anything!"); - Gson gson = new GsonBuilder().setPrettyPrinting().create(); - Challenge challenges = new Challenge(); - challenges.setChallengeType(ChallengeType.ISLAND); - Map map = new HashMap<>(); - map.put(Material.DIRT, 5); - map.put(Material.ACACIA_FENCE_GATE, 3); - challenges.setRequiredBlocks(map); - challenges.setIcon(new ItemStack(Material.ACACIA_FENCE_GATE)); - List requiredItems = new ArrayList<>(); - ItemStack result = new ItemStack(Material.POTION, 55); - ItemStack result2 = new ItemStack(Material.SPLASH_POTION, 22); - ItemStack result3 = new ItemStack(Material.LINGERING_POTION, 11); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesAddon#onEnable()}. + */ + @Test + public void testOnEnableNotHooked() { + addon.onLoad(); + when(am.getGameModeAddons()).thenReturn(Collections.emptyList()); + when(plugin.isEnabled()).thenReturn(true); + addon.setState(State.LOADED); + addon.onEnable(); + verify(plugin).log("[challenges] Loading challenges..."); + verify(plugin).logError("[challenges] Challenges could not hook into AcidIsland or BSkyBlock so will not do anything!"); + + } - PotionMeta potionMeta = (PotionMeta) result.getItemMeta(); - PotionData potionData = new PotionData(PotionType.FIRE_RESISTANCE, true, false); - potionMeta.setBasePotionData(potionData); - result.setItemMeta(potionMeta); - - PotionMeta potionMeta2 = (PotionMeta) result2.getItemMeta(); - PotionData potionData2 = new PotionData(PotionType.SPEED, true, false); - potionMeta2.setBasePotionData(potionData2); - potionMeta2.addEnchant(Enchantment.BINDING_CURSE, 1, true); - result2.setItemMeta(potionMeta2); - - requiredItems.add(result); - requiredItems.add(result2); - requiredItems.add(result3); - challenges.setRequiredItems(requiredItems); - String json = gson.toJson(challenges); + /** + * Test method for {@link world.bentobox.challenges.ChallengesAddon#onReload()}. + */ + @Test + public void testOnReloadNotHooked() { + addon.onReload(); + verify(plugin, never()).log(anyString()); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesAddon#onDisable()}. + */ + @Test + public void testOnDisable() { + this.testOnEnableHooked(); + addon.onDisable(); - Logger.getAnonymousLogger().info(json); + // Verify database saved exists + File chDir = new File("database", "Challenge"); + assertTrue(chDir.exists()); + File lvDir = new File("database", "ChallengeLevel"); + assertTrue(lvDir.exists()); } + /** + * Test method for {@link world.bentobox.challenges.ChallengesAddon#getChallengesManager()}. + */ + @Test + public void testGetChallengesManager() { + assertNull(addon.getChallengesManager()); + this.testOnEnableHooked(); + assertNotNull(addon.getChallengesManager()); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesAddon#getPermissionPrefix()}. + */ + @Test + public void testGetPermissionPrefix() { + assertEquals("addon.", addon.getPermissionPrefix()); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesAddon#getImportManager()}. + */ + @Test + public void testGetImportManager() { + assertNull(addon.getImportManager()); + this.testOnEnableHooked(); + assertNotNull(addon.getImportManager()); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesAddon#getWebManager()}. + */ + @Test + public void testGetWebManager() { + assertNull(addon.getWebManager()); + this.testOnEnableHooked(); + assertNotNull(addon.getWebManager()); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesAddon#getChallengesSettings()}. + */ + @Test + public void testGetChallengesSettings() { + assertNull(addon.getChallengesSettings()); + addon.onLoad(); + assertNotNull(addon.getChallengesSettings()); + + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesAddon#isEconomyProvided()}. + */ + @Test + public void testIsEconomyProvided() { + assertFalse(addon.isEconomyProvided()); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesAddon#getEconomyProvider()}. + */ + @Test + public void testGetEconomyProvider() { + assertNull(addon.getEconomyProvider()); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesAddon#isLevelProvided()}. + */ + @Test + public void testIsLevelProvided() { + assertFalse(addon.isLevelProvided()); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesAddon#getLevelAddon()}. + */ + @Test + public void testGetLevelAddon() { + assertNull(addon.getLevelAddon()); + } + } diff --git a/src/test/java/world/bentobox/challenges/ChallengesManagerTest.java b/src/test/java/world/bentobox/challenges/ChallengesManagerTest.java new file mode 100644 index 0000000..63503f7 --- /dev/null +++ b/src/test/java/world/bentobox/challenges/ChallengesManagerTest.java @@ -0,0 +1,870 @@ +package world.bentobox.challenges; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +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.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +import org.bukkit.Bukkit; +import org.bukkit.Server; +import org.bukkit.UnsafeValues; +import org.bukkit.World; +import org.bukkit.inventory.ItemFactory; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.plugin.PluginManager; +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +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.api.addons.AddonDescription; +import world.bentobox.bentobox.api.addons.GameModeAddon; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.bentobox.database.DatabaseSetup.DatabaseType; +import world.bentobox.bentobox.managers.IslandWorldManager; +import world.bentobox.bentobox.managers.PlaceholdersManager; +import world.bentobox.bentobox.util.Util; +import world.bentobox.challenges.config.Settings; +import world.bentobox.challenges.database.object.Challenge; +import world.bentobox.challenges.database.object.Challenge.ChallengeType; +import world.bentobox.challenges.database.object.ChallengeLevel; +import world.bentobox.challenges.events.ChallengeCompletedEvent; +import world.bentobox.challenges.events.ChallengeResetAllEvent; +import world.bentobox.challenges.events.ChallengeResetEvent; +import world.bentobox.challenges.events.LevelCompletedEvent; +import world.bentobox.challenges.utils.LevelStatus; + +/** + * @author tastybento + * + */ +@SuppressWarnings("deprecation") +@RunWith(PowerMockRunner.class) +@PrepareForTest({Bukkit.class, BentoBox.class, Util.class}) +public class ChallengesManagerTest { + + // Constants + private static final String GAME_MODE_NAME = "BSkyBlock"; + + // Mocks + @Mock + private ChallengesAddon addon; + @Mock + private Settings settings; + @Mock + private IslandWorldManager iwm; + @Mock + private Server server; + @Mock + private PluginManager pim; + @Mock + private ItemFactory itemFactory; + @Mock + private User user; + @Mock + private World world; + @Mock + private GameModeAddon gameModeAddon; + @Mock + private PlaceholdersManager plhm; + + // Variable fields + private ChallengesManager cm; + private File database; + private String uuid; + private Challenge challenge; + private @NonNull ChallengeLevel level; + private UUID playerID = UUID.randomUUID(); + private String cName; + private String levelName; + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + // Set up plugin + BentoBox plugin = mock(BentoBox.class); + Whitebox.setInternalState(BentoBox.class, "instance", plugin); + when(addon.getPlugin()).thenReturn(plugin); + + // IWM + when(plugin.getIWM()).thenReturn(iwm); + when(iwm.inWorld(any(World.class))).thenReturn(true); + + // Placeholders + when(plugin.getPlaceholdersManager()).thenReturn(plhm); + + // Settings for Database + world.bentobox.bentobox.Settings s = mock(world.bentobox.bentobox.Settings.class); + when(plugin.getSettings()).thenReturn(s); + when(s.getDatabaseType()).thenReturn(DatabaseType.JSON); + + // Addon Settings + when(addon.getChallengesSettings()).thenReturn(settings); + when(settings.isStoreHistory()).thenReturn(true); + when(settings.getLifeSpan()).thenReturn(10); + + // Database + database = new File("database"); + tearDown(); + + // Bukkit + PowerMockito.mockStatic(Bukkit.class); + when(Bukkit.getServer()).thenReturn(server); + when(Bukkit.getPluginManager()).thenReturn(pim); + when(Bukkit.getWorld(anyString())).thenReturn(world); + + ItemMeta meta = mock(ItemMeta.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); + + // Challenge + challenge = new Challenge(); + uuid = UUID.randomUUID().toString(); + challenge.setUniqueId(GAME_MODE_NAME + "_" + uuid); + challenge.setFriendlyName("name"); + challenge.setLevel(GAME_MODE_NAME + "_novice"); + challenge.setDescription(Collections.singletonList("A description")); + + // Challenge Level + level = new ChallengeLevel(); + levelName = GAME_MODE_NAME + "_novice"; + level.setUniqueId(levelName); + level.setFriendlyName("Novice"); + + // User + when(user.getUniqueId()).thenReturn(playerID); + + // Util + PowerMockito.mockStatic(Util.class); + when(Util.getWorld(any())).thenReturn(world); + + // Addon + AddonDescription desc = new AddonDescription.Builder("main", GAME_MODE_NAME, "1.0").build(); + when(gameModeAddon.getDescription()).thenReturn(desc); + Optional opAddon = Optional.of(gameModeAddon); + when(iwm.getAddon(any())).thenReturn(opAddon); + + // Challenge name + cName = GAME_MODE_NAME + "_" + uuid; + + // Class under test + cm = new ChallengesManager(addon); + } + + /** + * @throws java.lang.Exception + */ + @After + public void tearDown() throws Exception { + // Clean up JSON database + // Clean up file system + if (database.exists()) { + Files.walk(database.toPath()) + .sorted(Comparator.reverseOrder()) + .map(Path::toFile) + .forEach(File::delete); + } + + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#load()}. + * @throws InterruptedException + */ + @Test + public void testLoad() throws InterruptedException { + verify(addon).log("Loading challenges..."); + verify(addon, never()).logError(anyString()); + this.testSaveLevel(); + this.testSaveChallenge(); + cm.load(); + verify(addon, times(2)).log("Loading challenges..."); + verify(addon, never()).logError(anyString()); + assertTrue(cm.containsChallenge(cName)); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#reload()}. + * @throws InterruptedException + */ + @Test + public void testReload() throws InterruptedException { + cm.reload(); + verify(addon).log("Reloading challenges..."); + this.testSaveLevel(); + this.testSaveChallenge(); + cm.reload(); + verify(addon, times(2)).log("Reloading challenges..."); + verify(addon, never()).logError(anyString()); + assertTrue(cm.containsChallenge(cName)); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#loadChallenge(world.bentobox.challenges.database.object.Challenge, boolean, world.bentobox.bentobox.api.user.User, boolean)}. + */ + @Test + public void testLoadChallengeNoOverwriteSilent() { + // load once + assertTrue(cm.loadChallenge(challenge, false, user, true)); + // load twice - no overwrite + assertFalse(cm.loadChallenge(challenge, false, user, true)); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#loadChallenge(world.bentobox.challenges.database.object.Challenge, boolean, world.bentobox.bentobox.api.user.User, boolean)}. + */ + @Test + public void testLoadChallengeNoOverwriteNotSilent() { + // load once + assertTrue(cm.loadChallenge(challenge, false, user, true)); + // load twice - no overwrite, not silent + assertFalse(cm.loadChallenge(challenge, false, user, false)); + verify(user).sendMessage("challenges.messages.load-skipping", "[value]", "name"); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#loadChallenge(world.bentobox.challenges.database.object.Challenge, boolean, world.bentobox.bentobox.api.user.User, boolean)}. + */ + @Test + public void testLoadChallengeOverwriteSilent() { + // load once + assertTrue(cm.loadChallenge(challenge, false, user, true)); + // overwrite + assertTrue(cm.loadChallenge(challenge, true, user, true)); + verify(user, never()).sendMessage(anyString(), anyString(), anyString()); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#loadChallenge(world.bentobox.challenges.database.object.Challenge, boolean, world.bentobox.bentobox.api.user.User, boolean)}. + */ + @Test + public void testLoadChallengeOverwriteNotSilent() { + // load once + assertTrue(cm.loadChallenge(challenge, false, user, true)); + // overwrite not silent + assertTrue(cm.loadChallenge(challenge, true, user, false)); + verify(user).sendMessage("challenges.messages.load-overwriting", "[value]", "name"); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#loadLevel(world.bentobox.challenges.database.object.ChallengeLevel, boolean, world.bentobox.bentobox.api.user.User, boolean)}. + */ + @Test + public void testLoadLevelNoOverwriteSilent() { + // load once + assertTrue(cm.loadLevel(level, false, user, true)); + // load twice - no overwrite + assertFalse(cm.loadLevel(level, false, user, true)); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#loadLevel(world.bentobox.challenges.database.object.ChallengeLevel, boolean, world.bentobox.bentobox.api.user.User, boolean)}. + */ + @Test + public void testLoadLevelNoOverwriteNotSilent() { + // load once + assertTrue(cm.loadLevel(level, false, user, true)); + // load twice - no overwrite, not silent + assertFalse(cm.loadLevel(level, false, user, false)); + verify(user).sendMessage("challenges.messages.load-skipping", "[value]", "Novice"); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#loadLevel(world.bentobox.challenges.database.object.ChallengeLevel, boolean, world.bentobox.bentobox.api.user.User, boolean)}. + */ + @Test + public void testLoadLevelOverwriteSilent() { + // load once + assertTrue(cm.loadLevel(level, false, user, true)); + // overwrite + assertTrue(cm.loadLevel(level, true, user, true)); + verify(user, never()).sendMessage(anyString(), anyString(), anyString()); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#loadLevel(world.bentobox.challenges.database.object.ChallengeLevel, boolean, world.bentobox.bentobox.api.user.User, boolean)}. + */ + @Test + public void testLoadLevelOverwriteNotSilent() { + // load once + assertTrue(cm.loadLevel(level, false, user, true)); + // overwrite not silent + assertTrue(cm.loadLevel(level, true, user, false)); + verify(user).sendMessage("challenges.messages.load-overwriting", "[value]", "Novice"); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#removeFromCache(java.util.UUID)}. + */ + @Test + public void testRemoveFromCache() { + cm.removeFromCache(playerID); + verify(settings).isStoreAsIslandData(); + // TODO there should be a test where isStoreAsIslandData returns true + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#wipeDatabase(boolean)}. + * @throws InterruptedException + */ + @Test + public void testWipeDatabase() throws InterruptedException { + // Create some database + this.testLoad(); + + // Verify file exists + File chDir = new File(database, "Challenge"); + File check = new File(chDir, cName + ".json"); + assertTrue(check.exists()); + + File lvDir = new File(database, "ChallengeLevel"); + File checkLv = new File(lvDir, levelName + ".json"); + assertTrue(checkLv.exists()); + + cm.setChallengeComplete(user, world, challenge, 20); + //cm.save(); + File plData = new File(database, "ChallengesPlayerData"); + File checkPd = new File(plData, playerID.toString() + ".json"); + assertTrue(checkPd.exists()); + + // Wipe it + cm.wipeDatabase(false); + + // Verify + assertFalse(check.exists()); + assertFalse(checkLv.exists()); + assertTrue(checkPd.exists()); + + cm.wipeDatabase(true); + assertFalse(checkPd.exists()); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#wipePlayers()}. + * @throws InterruptedException + */ + @Test + public void testWipePlayers() throws InterruptedException { + this.testLoad(); + cm.setChallengeComplete(user, world, challenge, 20); + cm.save(); + File plData = new File(database, "ChallengesPlayerData"); + File checkLv = new File(plData, playerID.toString() + ".json"); + assertTrue(checkLv.exists()); + cm.wipePlayers(); + assertFalse(checkLv.exists()); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#migrateDatabase(world.bentobox.bentobox.api.user.User, org.bukkit.World)}. + */ + @Test + public void testMigrateDatabase() { + cm.migrateDatabase(user, world); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#save()}. + */ + @Test + public void testSave() { + cm.save(); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#saveChallenge(world.bentobox.challenges.database.object.Challenge)}. + * @throws InterruptedException + */ + @Test + public void testSaveChallenge() throws InterruptedException { + // Async - may not happen quickly + cm.saveChallenge(challenge); + Thread.sleep(500); + File chDir = new File(database, "Challenge"); + assertTrue(chDir.exists()); + File check = new File(chDir, cName + ".json"); + assertTrue(check.exists()); + // Remove icon becauseit has mockito meta in it + removeLine(check); + } + + private boolean removeLine(File inputFile) { + File tempFile = new File("myTempFile.json"); + + try (BufferedReader reader = new BufferedReader(new FileReader(inputFile))) { + try (BufferedWriter writer = new BufferedWriter(new FileWriter(tempFile))) { + + String lineToRemove = "\"icon"; + String currentLine; + + while((currentLine = reader.readLine()) != null) { + // trim newline when comparing with lineToRemove + String trimmedLine = currentLine.trim(); + if(trimmedLine.startsWith(lineToRemove)) continue; + writer.write(currentLine + System.getProperty("line.separator")); + } + } + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + + return tempFile.renameTo(inputFile); + } + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#saveLevel(world.bentobox.challenges.database.object.ChallengeLevel)}. + * @throws InterruptedException + */ + @Test + public void testSaveLevel() throws InterruptedException { + cm.saveLevel(level); + Thread.sleep(500); + File chDir = new File(database, "ChallengeLevel"); + assertTrue(chDir.exists()); + File check = new File(chDir, GAME_MODE_NAME + "_novice.json"); + assertTrue(check.exists()); + // Remove icon becauseit has mockito meta in it + removeLine(check); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#isChallengeComplete(world.bentobox.bentobox.api.user.User, org.bukkit.World, world.bentobox.challenges.database.object.Challenge)}. + */ + @Test + public void testIsChallengeCompleteUserWorldChallenge() { + assertFalse(cm.isChallengeComplete(user, world, challenge)); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#isChallengeComplete(java.util.UUID, org.bukkit.World, world.bentobox.challenges.database.object.Challenge)}. + */ + @Test + public void testIsChallengeCompleteUUIDWorldChallenge() { + assertFalse(cm.isChallengeComplete(playerID, world, challenge)); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#isChallengeComplete(java.util.UUID, org.bukkit.World, java.lang.String)}. + */ + @Test + public void testIsChallengeCompleteUUIDWorldString() { + assertFalse(cm.isChallengeComplete(playerID, world, "Novice")); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#setChallengeComplete(world.bentobox.bentobox.api.user.User, org.bukkit.World, world.bentobox.challenges.database.object.Challenge, int)}. + */ + @Test + public void testSetChallengeCompleteUserWorldChallengeInt() { + cm.setChallengeComplete(user, world, challenge, 3); + assertTrue(cm.isChallengeComplete(user, world, challenge)); + verify(pim).callEvent(any(ChallengeCompletedEvent.class)); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#setChallengeComplete(java.util.UUID, org.bukkit.World, world.bentobox.challenges.database.object.Challenge, int)}. + */ + @Test + public void testSetChallengeCompleteUUIDWorldChallengeInt() { + cm.setChallengeComplete(playerID, world, challenge, 3); + assertTrue(cm.isChallengeComplete(playerID, world, challenge)); + verify(pim).callEvent(any(ChallengeCompletedEvent.class)); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#setChallengeComplete(java.util.UUID, org.bukkit.World, world.bentobox.challenges.database.object.Challenge, java.util.UUID)}. + */ + @Test + public void testSetChallengeCompleteUUIDWorldChallengeUUID() { + UUID adminID = UUID.randomUUID(); + cm.setChallengeComplete(playerID, world, challenge, adminID); + assertTrue(cm.isChallengeComplete(playerID, world, challenge)); + verify(pim).callEvent(any(ChallengeCompletedEvent.class)); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#resetChallenge(java.util.UUID, org.bukkit.World, world.bentobox.challenges.database.object.Challenge, java.util.UUID)}. + */ + @Test + public void testResetChallenge() { + cm.setChallengeComplete(user, world, challenge, 3); + assertTrue(cm.isChallengeComplete(user, world, challenge)); + cm.resetChallenge(playerID, world, challenge, playerID); + assertFalse(cm.isChallengeComplete(user, world, challenge)); + verify(pim).callEvent(any(ChallengeResetEvent.class)); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#resetAllChallenges(world.bentobox.bentobox.api.user.User, org.bukkit.World)}. + */ + @Test + public void testResetAllChallengesUserWorld() { + cm.setChallengeComplete(user, world, challenge, 3); + assertTrue(cm.isChallengeComplete(user, world, challenge)); + cm.resetAllChallenges(user, world); + assertFalse(cm.isChallengeComplete(user, world, challenge)); + verify(pim).callEvent(any(ChallengeResetAllEvent.class)); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#resetAllChallenges(java.util.UUID, org.bukkit.World, java.util.UUID)}. + */ + @Test + public void testResetAllChallengesUUIDWorldUUID() { + cm.setChallengeComplete(user, world, challenge, 3); + assertTrue(cm.isChallengeComplete(user, world, challenge)); + cm.resetAllChallenges(playerID, world, playerID); + assertFalse(cm.isChallengeComplete(user, world, challenge)); + verify(pim).callEvent(any(ChallengeResetAllEvent.class)); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#getChallengeTimes(world.bentobox.bentobox.api.user.User, org.bukkit.World, world.bentobox.challenges.database.object.Challenge)}. + */ + @Test + public void testGetChallengeTimesUserWorldChallenge() { + assertEquals(0L, cm.getChallengeTimes(user, world, challenge)); + cm.setChallengeComplete(user, world, challenge, 6); + assertEquals(6L, cm.getChallengeTimes(user, world, challenge)); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#getChallengeTimes(world.bentobox.bentobox.api.user.User, org.bukkit.World, java.lang.String)}. + */ + @Test + public void testGetChallengeTimesUserWorldString() { + assertEquals(0L, cm.getChallengeTimes(user, world, cName)); + cm.setChallengeComplete(user, world, challenge, 6); + assertEquals(6L, cm.getChallengeTimes(user, world, cName)); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#isLevelCompleted(world.bentobox.bentobox.api.user.User, org.bukkit.World, world.bentobox.challenges.database.object.ChallengeLevel)}. + */ + @Test + public void testIsLevelCompleted() { + assertFalse(cm.isLevelCompleted(user, world, level)); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#isLevelUnlocked(world.bentobox.bentobox.api.user.User, org.bukkit.World, world.bentobox.challenges.database.object.ChallengeLevel)}. + */ + @Test + public void testIsLevelUnlocked() { + assertFalse(cm.isLevelUnlocked(user, world, level)); + this.testLoadLevelNoOverwriteSilent(); + assertTrue(cm.isLevelUnlocked(user, world, level)); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#setLevelComplete(world.bentobox.bentobox.api.user.User, org.bukkit.World, world.bentobox.challenges.database.object.ChallengeLevel)}. + */ + @Test + public void testSetLevelComplete() { + assertFalse(cm.isLevelCompleted(user, world, level)); + cm.setLevelComplete(user, world, level); + assertTrue(cm.isLevelCompleted(user, world, level)); + verify(pim).callEvent(any(LevelCompletedEvent.class)); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#validateLevelCompletion(world.bentobox.bentobox.api.user.User, org.bukkit.World, world.bentobox.challenges.database.object.ChallengeLevel)}. + */ + @Test + public void testValidateLevelCompletion() { + assertTrue(cm.validateLevelCompletion(user, world, level)); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#getChallengeLevelStatus(java.util.UUID, org.bukkit.World, world.bentobox.challenges.database.object.ChallengeLevel)}. + */ + @Test + public void testGetChallengeLevelStatus() { + this.testLoadLevelNoOverwriteSilent(); + LevelStatus cls = cm.getChallengeLevelStatus(playerID, world, level); + assertTrue(cls.getNumberOfChallengesStillToDo() == 0); + assertEquals(level, cls.getLevel()); + assertTrue(cls.isComplete()); + assertTrue(cls.isUnlocked()); + assertEquals("BSkyBlock_novice", cls.getLevel().getUniqueId()); + + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#getAllChallengeLevelStatus(world.bentobox.bentobox.api.user.User, org.bukkit.World)}. + */ + @Test + public void testGetAllChallengeLevelStatus() { + this.testLoadLevelNoOverwriteSilent(); + List list = cm.getAllChallengeLevelStatus(user, world); + assertTrue(list.size() == 1); + LevelStatus cls = list.get(0); + assertTrue(cls.getNumberOfChallengesStillToDo() == 0); + assertEquals(level, cls.getLevel()); + assertTrue(cls.isComplete()); + assertTrue(cls.isUnlocked()); + assertEquals("BSkyBlock_novice", cls.getLevel().getUniqueId()); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#getAllChallengesNames(org.bukkit.World)}. + */ + @Test + public void testGetAllChallengesNames() { + assertTrue(cm.getAllChallengesNames(world).isEmpty()); + cm.saveChallenge(challenge); + cm.loadChallenge(challenge, false, user, true); + List list = cm.getAllChallengesNames(world); + assertFalse(list.isEmpty()); + assertEquals(cName, list.get(0)); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#getAllChallenges(org.bukkit.World)}. + */ + @Test + public void testGetAllChallenges() { + assertTrue(cm.getAllChallenges(world).isEmpty()); + cm.saveChallenge(challenge); + cm.loadChallenge(challenge, false, user, true); + List list = cm.getAllChallenges(world); + assertFalse(list.isEmpty()); + assertEquals(challenge, list.get(0)); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#getFreeChallenges(org.bukkit.World)}. + */ + @Test + public void testGetFreeChallenges() { + // Empty + assertTrue(cm.getFreeChallenges(world).isEmpty()); + // One normal + cm.saveChallenge(challenge); + cm.loadChallenge(challenge, false, user, true); + assertTrue(cm.getFreeChallenges(world).isEmpty()); + // One free + challenge.setLevel(""); + cm.saveChallenge(challenge); + cm.loadChallenge(challenge, false, user, true); + List list = cm.getFreeChallenges(world); + assertFalse(list.isEmpty()); + assertEquals(challenge, list.get(0)); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#getLevelChallenges(world.bentobox.challenges.database.object.ChallengeLevel)}. + * @throws InterruptedException + */ + @Test + public void testGetLevelChallenges() throws InterruptedException { + assertTrue(cm.getLevelChallenges(level).isEmpty()); + // make some challenges + this.testSaveLevel(); + this.testSaveChallenge(); + level.setChallenges(Collections.singleton(challenge.getUniqueId())); + // Test again + List list = cm.getLevelChallenges(level); + assertFalse(list.isEmpty()); + assertEquals(challenge, list.get(0)); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#getChallenge(java.lang.String)}. + * @throws InterruptedException + */ + @Test + public void testGetChallenge() throws InterruptedException { + assertNull(cm.getChallenge(cName)); + this.testSaveLevel(); + this.testSaveChallenge(); + Challenge ch = cm.getChallenge(cName); + assertNotNull(ch); + assertEquals(cName, ch.getUniqueId()); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#containsChallenge(java.lang.String)}. + */ + @Test + public void testContainsChallenge() { + assertFalse(cm.containsChallenge("no-such-challenge")); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#createChallenge(java.lang.String, world.bentobox.challenges.database.object.Challenge.ChallengeType, world.bentobox.challenges.database.object.requirements.Requirements)}. + */ + @Test + public void testCreateChallenge() { + @Nullable + Challenge ch = cm.createChallenge("newChal", ChallengeType.ISLAND, null); + assertEquals(ChallengeType.ISLAND, ch.getChallengeType()); + assertEquals("newChal", ch.getUniqueId()); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#deleteChallenge(world.bentobox.challenges.database.object.Challenge)}. + * @throws InterruptedException + */ + @Test + public void testDeleteChallenge() throws InterruptedException { + this.testSaveLevel(); + this.testSaveChallenge(); + Challenge ch = cm.getChallenge(cName); + assertNotNull(ch); + assertEquals(cName, ch.getUniqueId()); + cm.deleteChallenge(challenge); + ch = cm.getChallenge(cName); + assertNull(ch); + verify(plhm).unregisterPlaceholder(eq("challenges_challenge_repetition_count_" + cName)); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#getLevels(org.bukkit.World)}. + */ + @Test + public void testGetLevels() { + this.testGetLevelString(); + List lvs = cm.getLevels(world); + assertFalse(lvs.isEmpty()); + assertEquals(level, lvs.get(0)); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#getLevel(world.bentobox.challenges.database.object.Challenge)}. + */ + @Test + public void testGetLevelChallenge() { + this.testGetLevelString(); + assertEquals(level, cm.getLevel(challenge)); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#getLevel(java.lang.String)}. + */ + @Test + public void testGetLevelString() { + assertNull(cm.getLevel("dss")); + cm.saveLevel(level); + cm.loadLevel(level, false, user, true); + assertEquals(level, cm.getLevel(levelName)); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#containsLevel(java.lang.String)}. + */ + @Test + public void testContainsLevel() { + this.testGetLevelString(); + assertFalse(cm.containsLevel("sdsd")); + assertTrue(cm.containsLevel(levelName)); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#addChallengeToLevel(world.bentobox.challenges.database.object.Challenge, world.bentobox.challenges.database.object.ChallengeLevel)}. + * @throws InterruptedException + */ + @Test + public void testAddChallengeToLevel() throws InterruptedException { + this.testLoad(); + cm.deleteChallenge(challenge); + assertFalse(cm.containsChallenge(cName)); + cm.addChallengeToLevel(challenge, level); + assertEquals(level, cm.getLevel(challenge)); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#removeChallengeFromLevel(world.bentobox.challenges.database.object.Challenge, world.bentobox.challenges.database.object.ChallengeLevel)}. + * @throws InterruptedException + */ + @Test + public void testRemoveChallengeFromLevel() throws InterruptedException { + this.testAddChallengeToLevel(); + cm.removeChallengeFromLevel(challenge, level); + assertFalse(cm.containsChallenge(cName)); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#createLevel(java.lang.String, org.bukkit.World)}. + */ + @Test + public void testCreateLevel() { + @Nullable + ChallengeLevel cl = cm.createLevel("Expert", world); + assertEquals("Expert", cl.getUniqueId()); + assertEquals(world.getName(), cl.getWorld()); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#deleteChallengeLevel(world.bentobox.challenges.database.object.ChallengeLevel)}. + * @throws InterruptedException + */ + @Test + public void testDeleteChallengeLevel() throws InterruptedException { + this.testAddChallengeToLevel(); + assertTrue(cm.containsLevel(levelName)); + cm.deleteChallengeLevel(level); + assertFalse(cm.containsLevel(levelName)); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#hasAnyChallengeData(org.bukkit.World)}. + * @throws InterruptedException + */ + @Test + public void testHasAnyChallengeDataWorld() throws InterruptedException { + assertFalse(cm.hasAnyChallengeData(world)); + this.testLoad(); + assertTrue(cm.hasAnyChallengeData(world)); + } + + /** + * Test method for {@link world.bentobox.challenges.ChallengesManager#hasAnyChallengeData(java.lang.String)}. + * @throws InterruptedException + */ + @Test + public void testHasAnyChallengeDataString() throws InterruptedException { + assertFalse(cm.hasAnyChallengeData("BSkyBlock")); + this.testLoad(); + assertTrue(cm.hasAnyChallengeData("BSkyBlock")); + } + +} diff --git a/src/test/java/world/bentobox/challenges/commands/ChallengesCommandTest.java b/src/test/java/world/bentobox/challenges/commands/ChallengesCommandTest.java new file mode 100644 index 0000000..657a495 --- /dev/null +++ b/src/test/java/world/bentobox/challenges/commands/ChallengesCommandTest.java @@ -0,0 +1,268 @@ +package world.bentobox.challenges.commands; + +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.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; +import static org.mockito.Mockito.when; + +import java.util.Collections; +import java.util.Optional; +import java.util.UUID; + +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.World; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemFactory; +import org.bukkit.inventory.meta.ItemMeta; +import org.eclipse.jdt.annotation.NonNull; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +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.api.addons.GameModeAddon; +import world.bentobox.bentobox.api.commands.CompositeCommand; +import world.bentobox.bentobox.api.localization.TextVariables; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.bentobox.database.objects.Island; +import world.bentobox.bentobox.managers.CommandsManager; +import world.bentobox.bentobox.managers.IslandWorldManager; +import world.bentobox.bentobox.managers.IslandsManager; +import world.bentobox.challenges.ChallengesAddon; +import world.bentobox.challenges.ChallengesManager; +import world.bentobox.challenges.config.Settings; +import world.bentobox.challenges.config.SettingsUtils.VisibilityMode; + +/** + * @author tastybento + * + */ +@RunWith(PowerMockRunner.class) +@PrepareForTest({Bukkit.class, BentoBox.class, ChatColor.class}) +public class ChallengesCommandTest { + + @Mock + private CompositeCommand ic; + private UUID uuid; + @Mock + private User user; + @Mock + private IslandsManager im; + @Mock + private Island island; + @Mock + private ChallengesAddon addon; + private ChallengesCommand cc; + @Mock + private World world; + @Mock + private ChallengesManager chm; + @Mock + private IslandWorldManager iwm; + @Mock + private GameModeAddon gameModeAddon; + @Mock + private Settings settings; + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + // Set up plugin + BentoBox plugin = mock(BentoBox.class); + Whitebox.setInternalState(BentoBox.class, "instance", plugin); + User.setPlugin(plugin); + + // Command manager + CommandsManager cm = mock(CommandsManager.class); + when(plugin.getCommandsManager()).thenReturn(cm); + // Addon + when(ic.getAddon()).thenReturn(addon); + when(ic.getPermissionPrefix()).thenReturn("bskyblock."); + when(ic.getLabel()).thenReturn("island"); + when(ic.getTopLabel()).thenReturn("island"); + when(ic.getWorld()).thenReturn(world); + when(ic.getTopLabel()).thenReturn("bsb"); + + // IWM friendly name + when(iwm.getFriendlyName(any())).thenReturn("BSkyBlock"); + when(iwm.inWorld(any(World.class))).thenReturn(true); + Optional optionalAddon = Optional.of(gameModeAddon); + when(iwm.getAddon(any())).thenReturn(optionalAddon); + when(plugin.getIWM()).thenReturn(iwm); + + // Game Mode Addon + @NonNull + Optional optionalAdmin = Optional.of(ic); + when(gameModeAddon.getAdminCommand()).thenReturn(optionalAdmin); + + // World + when(world.toString()).thenReturn("world"); + + // 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"); + when(user.getPermissionValue(anyString(), anyInt())).thenReturn(-1); + when(user.isPlayer()).thenReturn(true); + + // Mock item factory (for itemstacks) + PowerMockito.mockStatic(Bukkit.class); + ItemFactory itemFactory = mock(ItemFactory.class); + when(Bukkit.getItemFactory()).thenReturn(itemFactory); + ItemMeta itemMeta = mock(ItemMeta.class); + when(itemFactory.getItemMeta(any())).thenReturn(itemMeta); + + // Addon + when(addon.getChallengesManager()).thenReturn(chm); + when(chm.getAllChallengeLevelStatus(any(), any())).thenReturn(Collections.emptyList()); + // Challenges exist + when(chm.hasAnyChallengeData(any(World.class))).thenReturn(true); + + // ChatColor + PowerMockito.mockStatic(ChatColor.class); + when(ChatColor.translateAlternateColorCodes(any(char.class), anyString())).thenAnswer((Answer) invocation -> invocation.getArgument(1, String.class)); + + // Settings + when(addon.getChallengesSettings()).thenReturn(settings); + when(settings.getVisibilityMode()).thenReturn(VisibilityMode.VISIBLE); + + // Island + when(plugin.getIslands()).thenReturn(im); + when(im.getIsland(any(), any(User.class))).thenReturn(island); + + // Command under test + cc = new ChallengesCommand(addon, ic); + } + + /** + * Test method for {@link world.bentobox.challenges.commands.ChallengesCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testCanExecuteWrongWorld() { + when(iwm.inWorld(any(World.class))).thenReturn(false); + assertFalse(cc.canExecute(user, "challenges", Collections.emptyList())); + verify(user).sendMessage("general.errors.wrong-world"); + } + + /** + * Test method for {@link world.bentobox.challenges.commands.ChallengesCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testCanExecuteNoChallenges() { + when(chm.hasAnyChallengeData(any(World.class))).thenReturn(false); + assertFalse(cc.canExecute(user, "challenges", Collections.emptyList())); + verify(addon).logError("There are no challenges set up in world!"); + verify(user).sendMessage("challenges.errors.no-challenges"); + } + + /** + * Test method for {@link world.bentobox.challenges.commands.ChallengesCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testCanExecuteNoChallengesOp() { + when(user.isOp()).thenReturn(true); + when(chm.hasAnyChallengeData(any(World.class))).thenReturn(false); + assertFalse(cc.canExecute(user, "challenges", Collections.emptyList())); + verify(addon).logError("There are no challenges set up in world!"); + verify(user).sendMessage("challenges.errors.no-challenges-admin", "[command]", "bsb challenges"); + verify(user, never()).sendMessage("challenges.errors.no-challenges"); + } + + /** + * Test method for {@link world.bentobox.challenges.commands.ChallengesCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testCanExecuteNoChallengesHasPerm() { + when(user.hasPermission(anyString())).thenReturn(true); + when(chm.hasAnyChallengeData(any(World.class))).thenReturn(false); + assertFalse(cc.canExecute(user, "challenges", Collections.emptyList())); + verify(addon).logError("There are no challenges set up in world!"); + verify(user).sendMessage("challenges.errors.no-challenges-admin", "[command]", "bsb challenges"); + verify(user, never()).sendMessage("challenges.errors.no-challenges"); + } + + /** + * Test method for {@link world.bentobox.challenges.commands.ChallengesCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testCanExecuteNoAdminCommand() { + when(gameModeAddon.getAdminCommand()).thenReturn(Optional.empty()); + when(user.isOp()).thenReturn(true); + when(chm.hasAnyChallengeData(any(World.class))).thenReturn(false); + assertFalse(cc.canExecute(user, "challenges", Collections.emptyList())); + verify(addon).logError("There are no challenges set up in world!"); + verify(user).sendMessage("challenges.errors.no-challenges-admin", "[command]", "bsb challenges"); + verify(user, never()).sendMessage("challenges.errors.no-challenges"); + } + + /** + * Test method for {@link world.bentobox.challenges.commands.ChallengesCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testCanExecuteNoIsland() { + when(im.getIsland(any(), any(User.class))).thenReturn(null); + assertFalse(cc.canExecute(user, "challenges", Collections.emptyList())); + verify(user).sendMessage("general.errors.no-island"); + } + + /** + * Test method for {@link world.bentobox.challenges.commands.ChallengesCommand#canExecute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testCanExecuteSuccess() { + assertTrue(cc.canExecute(user, "challenges", Collections.emptyList())); + verify(user, never()).sendMessage(anyString()); + } + + /** + * Test method for {@link world.bentobox.challenges.commands.ChallengesCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testExecuteUserStringListOfStringConsole() { + User console = mock(User.class); + assertFalse(cc.execute(console, "challenges", Collections.emptyList())); + verify(console).sendMessage(eq("commands.help.header"), eq(TextVariables.LABEL), eq("BSkyBlock")); + } + + /** + * Test method for {@link world.bentobox.challenges.commands.ChallengesCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testExecuteUserStringListOfStringUser() { + assertTrue(cc.execute(user, "challenges", Collections.emptyList())); + } + + /** + * Test method for {@link world.bentobox.challenges.commands.ChallengesCommand#setup()}. + */ + @Test + public void testSetup() { + assertEquals("bskyblock." + ChallengesCommand.CHALLENGE_COMMAND, cc.getPermission()); + assertEquals("challenges.commands.user.main.parameters", cc.getParameters()); + assertEquals("challenges.commands.user.main.description", cc.getDescription()); + assertTrue(cc.isOnlyPlayer()); + // CompleteChallengeCommand + assertEquals(1, cc.getSubCommands(true).size()); + } + +} diff --git a/src/test/java/world/bentobox/challenges/commands/CompleteChallengeCommandTest.java b/src/test/java/world/bentobox/challenges/commands/CompleteChallengeCommandTest.java new file mode 100644 index 0000000..d35a363 --- /dev/null +++ b/src/test/java/world/bentobox/challenges/commands/CompleteChallengeCommandTest.java @@ -0,0 +1,342 @@ +package world.bentobox.challenges.commands; + +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.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; +import static org.mockito.Mockito.when; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.World; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemFactory; +import org.bukkit.inventory.meta.ItemMeta; +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.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.api.addons.GameModeAddon; +import world.bentobox.bentobox.api.commands.CompositeCommand; +import world.bentobox.bentobox.api.localization.TextVariables; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.bentobox.database.objects.Island; +import world.bentobox.bentobox.managers.CommandsManager; +import world.bentobox.bentobox.managers.IslandWorldManager; +import world.bentobox.bentobox.managers.IslandsManager; +import world.bentobox.bentobox.util.Util; +import world.bentobox.challenges.ChallengesAddon; +import world.bentobox.challenges.ChallengesManager; +import world.bentobox.challenges.config.Settings; +import world.bentobox.challenges.config.SettingsUtils.VisibilityMode; +import world.bentobox.challenges.database.object.Challenge; +import world.bentobox.challenges.tasks.TryToComplete; +import world.bentobox.challenges.utils.Utils; + +/** + * @author tastybento + * + */ +@RunWith(PowerMockRunner.class) +@PrepareForTest({Bukkit.class, BentoBox.class, ChatColor.class, Utils.class, TryToComplete.class, Util.class}) +public class CompleteChallengeCommandTest { + + @Mock + private CompositeCommand ic; + private UUID uuid; + @Mock + private User user; + @Mock + private IslandsManager im; + @Mock + private Island island; + @Mock + private ChallengesAddon addon; + + private CompleteChallengeCommand cc; + @Mock + private World world; + @Mock + private ChallengesManager chm; + @Mock + private IslandWorldManager iwm; + @Mock + private GameModeAddon gameModeAddon; + @Mock + private Settings settings; + @Mock + private Challenge challenge; + + /** + * @throws java.lang.Exception + */ + @SuppressWarnings("unchecked") + @Before + public void setUp() throws Exception { + // Set up plugin + BentoBox plugin = mock(BentoBox.class); + Whitebox.setInternalState(BentoBox.class, "instance", plugin); + User.setPlugin(plugin); + + // Command manager + CommandsManager cm = mock(CommandsManager.class); + when(plugin.getCommandsManager()).thenReturn(cm); + // Addon + when(ic.getAddon()).thenReturn(addon); + when(ic.getPermissionPrefix()).thenReturn("bskyblock."); + when(ic.getLabel()).thenReturn("island"); + when(ic.getTopLabel()).thenReturn("island"); + when(ic.getWorld()).thenReturn(world); + when(ic.getTopLabel()).thenReturn("bsb"); + + // IWM friendly name + when(iwm.getFriendlyName(any())).thenReturn("BSkyBlock"); + when(iwm.inWorld(any(World.class))).thenReturn(true); + Optional optionalAddon = Optional.of(gameModeAddon); + when(iwm.getAddon(any())).thenReturn(optionalAddon); + when(plugin.getIWM()).thenReturn(iwm); + + // Game Mode Addon + @NonNull + Optional optionalAdmin = Optional.of(ic); + when(gameModeAddon.getAdminCommand()).thenReturn(optionalAdmin); + + // World + when(world.toString()).thenReturn("world"); + + // 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"); + when(user.getPermissionValue(anyString(), anyInt())).thenReturn(-1); + when(user.isPlayer()).thenReturn(true); + + // Mock item factory (for itemstacks) + PowerMockito.mockStatic(Bukkit.class); + ItemFactory itemFactory = mock(ItemFactory.class); + when(Bukkit.getItemFactory()).thenReturn(itemFactory); + ItemMeta itemMeta = mock(ItemMeta.class); + when(itemFactory.getItemMeta(any())).thenReturn(itemMeta); + + // Addon & Challenge Manager + when(addon.getChallengesManager()).thenReturn(chm); + when(chm.getAllChallengeLevelStatus(any(), any())).thenReturn(Collections.emptyList()); + // Challenges exist + when(chm.hasAnyChallengeData(any(World.class))).thenReturn(true); + // Challenges + when(chm.getChallenge(anyString())).thenReturn(challenge); + List nameList = Arrays.asList("world_maker", "world_placer", "bad_challenge_name", "world_breaker"); + when(chm.getAllChallengesNames(any())).thenReturn(nameList); + + + // ChatColor + PowerMockito.mockStatic(ChatColor.class); + when(ChatColor.translateAlternateColorCodes(any(char.class), anyString())).thenAnswer((Answer) invocation -> invocation.getArgument(1, String.class)); + + // Settings + when(addon.getChallengesSettings()).thenReturn(settings); + when(settings.getVisibilityMode()).thenReturn(VisibilityMode.VISIBLE); + + // Island + when(plugin.getIslands()).thenReturn(im); + when(im.getIsland(any(), any(User.class))).thenReturn(island); + + // Utils + PowerMockito.mockStatic(Utils.class); + when(Utils.getGameMode(any())).thenReturn("world"); + + // Try to complete + PowerMockito.mockStatic(TryToComplete.class); + // All challenges are successful! + when(TryToComplete.complete(any(), any(), any(), any(), anyString(), anyString(), anyInt())).thenReturn(true); + + // Util + PowerMockito.mockStatic(Util.class); + when(Util.tabLimit(any(), any())).thenAnswer((Answer>) invocation -> (List)invocation.getArgument(0, List.class)); + + // Command under test + cc = new CompleteChallengeCommand(addon, ic); + } + + /** + * @throws java.lang.Exception + */ + @After + public void tearDown() throws Exception { + } + + /** + * Test method for {@link world.bentobox.challenges.commands.CompleteChallengeCommand#CompleteChallengeCommand(world.bentobox.bentobox.api.addons.Addon, world.bentobox.bentobox.api.commands.CompositeCommand)}. + */ + @Test + public void testCompleteChallengeCommand() { + assertEquals("complete", cc.getLabel()); + } + + /** + * Test method for {@link world.bentobox.challenges.commands.CompleteChallengeCommand#setup()}. + */ + @Test + public void testSetup() { + assertEquals("bskyblock.complete", cc.getPermission()); + assertEquals("challenges.commands.user.complete.parameters", cc.getParameters()); + assertEquals("challenges.commands.user.complete.description", cc.getDescription()); + assertTrue(cc.isOnlyPlayer()); + // No sub commands + assertEquals(0, cc.getSubCommands(true).size()); + } + + /** + * Test method for {@link world.bentobox.challenges.commands.CompleteChallengeCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testExecuteUserStringListOfStringNoArgs() { + assertFalse(cc.execute(user, "complete", Collections.emptyList())); + verify(user).sendMessage(eq("challenges.errors.no-name")); + verify(user).sendMessage(eq("commands.help.header"), eq(TextVariables.LABEL), eq("BSkyBlock")); + } + + /** + * Test method for {@link world.bentobox.challenges.commands.CompleteChallengeCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testExecuteUserStringListOfStringUnknownChallenge() { + when(chm.getChallenge(anyString())).thenReturn(null); + assertFalse(cc.execute(user, "complete", Collections.singletonList("mychal"))); + verify(user).sendMessage(eq("challenges.errors.unknown-challenge")); + verify(user).sendMessage(eq("commands.help.header"), eq(TextVariables.LABEL), eq("BSkyBlock")); + } + + /** + * Test method for {@link world.bentobox.challenges.commands.CompleteChallengeCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testExecuteUserStringListOfStringKnownChallengeFail() { + when(TryToComplete.complete(any(), any(), any(), any(), anyString(), anyString(), anyInt())).thenReturn(false); + assertFalse(cc.execute(user, "complete", Collections.singletonList("mychal"))); + verify(user, never()).sendMessage(any()); + } + + /** + * Test method for {@link world.bentobox.challenges.commands.CompleteChallengeCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testExecuteUserStringListOfStringKnownChallengeSuccess() { + assertTrue(cc.execute(user, "complete", Collections.singletonList("mychal"))); + verify(user, never()).sendMessage(any()); + } + + /** + * Test method for {@link world.bentobox.challenges.commands.CompleteChallengeCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testExecuteUserStringListOfStringKnownChallengeSuccessMultipleTimesNoPerm() { + assertTrue(cc.execute(user, "complete", Arrays.asList("mychal", "5"))); + verify(user).sendMessage(eq("challenges.error.no-multiple-permission")); + } + + /** + * Test method for {@link world.bentobox.challenges.commands.CompleteChallengeCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testExecuteUserStringListOfStringKnownChallengeSuccessMultipleTimesHasPerm() { + when(user.hasPermission(anyString())).thenReturn(true); + assertTrue(cc.execute(user, "complete", Arrays.asList("mychal", "5"))); + verify(user, never()).sendMessage(any()); + } + + /** + * Test method for {@link world.bentobox.challenges.commands.CompleteChallengeCommand#tabComplete(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testTabCompleteUserStringListOfStringNoArgs() { + cc.tabComplete(user, "complete", Collections.emptyList()); + } + + /** + * Test method for {@link world.bentobox.challenges.commands.CompleteChallengeCommand#tabComplete(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testTabCompleteUserStringListOfStringOneArg() { + List list = cc.tabComplete(user, "complete", Collections.singletonList("arg")).get(); + assertFalse(list.isEmpty()); + assertEquals("help", list.get(0)); + } + + /** + * Test method for {@link world.bentobox.challenges.commands.CompleteChallengeCommand#tabComplete(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testTabCompleteUserStringListOfStringTwoArgs() { + List list = cc.tabComplete(user, "complete", Arrays.asList("arg1", "arg2")).get(); + assertFalse(list.isEmpty()); + assertEquals("help", list.get(0)); + } + + /** + * Test method for {@link world.bentobox.challenges.commands.CompleteChallengeCommand#tabComplete(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testTabCompleteUserStringListOfStringThreeArgs() { + List list = cc.tabComplete(user, "complete", Arrays.asList("arg1", "arg2", "arg3")).get(); + assertFalse(list.isEmpty()); + assertEquals("maker", list.get(0)); + assertEquals("placer", list.get(1)); + assertEquals("breaker", list.get(2)); + } + + /** + * Test method for {@link world.bentobox.challenges.commands.CompleteChallengeCommand#tabComplete(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testTabCompleteUserStringListOfStringFourArgs() { + List list = cc.tabComplete(user, "complete", Arrays.asList("arg1", "arg2", "arg3", "arg4")).get(); + assertTrue(list.isEmpty()); + } + + /** + * Test method for {@link world.bentobox.challenges.commands.CompleteChallengeCommand#tabComplete(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testTabCompleteUserStringListOfStringFourArgsNumber() { + List list = cc.tabComplete(user, "complete", Arrays.asList("arg1", "arg2", "arg3", "4")).get(); + assertFalse(list.isEmpty()); + assertEquals("", list.get(0)); + } + + /** + * Test method for {@link world.bentobox.challenges.commands.CompleteChallengeCommand#tabComplete(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)}. + */ + @Test + public void testTabCompleteUserStringListOfStringFiveArgs() { + List list = cc.tabComplete(user, "complete", Arrays.asList("arg1", "arg2", "arg23", "arg4", "arg5")).get(); + assertFalse(list.isEmpty()); + assertEquals("help", list.get(0)); + } + +} diff --git a/src/test/java/world/bentobox/challenges/panel/user/ChallengesGUITest.java b/src/test/java/world/bentobox/challenges/panel/user/ChallengesGUITest.java new file mode 100644 index 0000000..dcf0de5 --- /dev/null +++ b/src/test/java/world/bentobox/challenges/panel/user/ChallengesGUITest.java @@ -0,0 +1,392 @@ +package world.bentobox.challenges.panel.user; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +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; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.UUID; + +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.World.Environment; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemFactory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +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.api.commands.CompositeCommand; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.bentobox.managers.BlueprintsManager; +import world.bentobox.bentobox.managers.IslandWorldManager; +import world.bentobox.bentobox.managers.IslandsManager; +import world.bentobox.challenges.ChallengesAddon; +import world.bentobox.challenges.ChallengesManager; +import world.bentobox.challenges.config.Settings; +import world.bentobox.challenges.config.SettingsUtils.VisibilityMode; +import world.bentobox.challenges.database.object.Challenge; +import world.bentobox.challenges.database.object.Challenge.ChallengeType; +import world.bentobox.challenges.database.object.ChallengeLevel; +import world.bentobox.challenges.utils.LevelStatus; + +/** + * @author tastybento + * + */ +@RunWith(PowerMockRunner.class) +@PrepareForTest({Bukkit.class, BentoBox.class, ChatColor.class}) +public class ChallengesGUITest { + + @Mock + private User user; + @Mock + private IslandsManager im; + @Mock + private IslandWorldManager iwm; + @Mock + private BentoBox plugin; + @Mock + private Settings settings; + @Mock + private CompositeCommand ic; + @Mock + private BlueprintsManager bpm; + @Mock + private Inventory inv; + @Mock + private ItemMeta meta; + @Mock + private ChallengesAddon addon; + @Mock + private World world; + + private ChallengesGUI cg; + + @Mock + private ChallengesManager chm; + private UUID uuid; + @Mock + private Challenge challenge1; + @Mock + private Challenge challenge2; + @Mock + private Challenge challenge3; + @Mock + private Challenge challenge4; + @Mock + private LevelStatus levelStatus; + + private List freeChalls = new ArrayList<>(); + private List levelChalls = new ArrayList<>(); + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + // Set up plugin + Whitebox.setInternalState(BentoBox.class, "instance", plugin); + + PowerMockito.mockStatic(Bukkit.class); + // Item Factory (needed for ItemStack) + ItemFactory itemF = mock(ItemFactory.class); + when(itemF.getItemMeta(Mockito.any())).thenReturn(meta); + when(Bukkit.getItemFactory()).thenReturn(itemF); + // Inventory + when(Bukkit.createInventory(eq(null), anyInt(), anyString())).thenReturn(inv); + + // Addon + when(addon.getChallengesManager()).thenReturn(chm); + // Levels + when(levelStatus.isUnlocked()).thenReturn(true); + ChallengeLevel level = mock(ChallengeLevel.class); + when(level.getFriendlyName()).thenReturn("Novice"); + when(level.getUniqueId()).thenReturn("novice"); + when(level.getIcon()).thenReturn(new ItemStack(Material.BIRCH_BOAT)); + when(level.getLockedIcon()).thenReturn(new ItemStack(Material.DARK_OAK_BOAT)); + when(levelStatus.getLevel()).thenReturn(level); + List levels = Collections.singletonList(levelStatus); + when(chm.getAllChallengeLevelStatus(any(), any())).thenReturn(levels); + when(chm.getChallengeLevelStatus(any(), any(), any())).thenReturn(levelStatus); + // Challenges exist + when(chm.hasAnyChallengeData(any(World.class))).thenReturn(true); + + // Free challenges - have more than 18 so that the special processing kicks in + when(chm.getFreeChallenges(any())).thenReturn(freeChalls); + when(challenge1.isDeployed()).thenReturn(true); + when(challenge2.isDeployed()).thenReturn(true); + // 1 is repeatable, 2 is not + when(challenge1.isRepeatable()).thenReturn(true); + + // Level challenges + when(chm.getLevelChallenges(any())).thenReturn(levelChalls); + // ChatColor + PowerMockito.mockStatic(ChatColor.class); + when(ChatColor.translateAlternateColorCodes(any(char.class), anyString())).thenAnswer((Answer) invocation -> invocation.getArgument(1, String.class)); + // Settings + when(addon.getChallengesSettings()).thenReturn(settings); + when(settings.getVisibilityMode()).thenReturn(VisibilityMode.VISIBLE); + when(settings.isFreeChallengesFirst()).thenReturn(false); + when(settings.isRemoveCompleteOneTimeChallenges()).thenReturn(false); + + // Player + Player p = mock(Player.class); + when(user.isOp()).thenReturn(false); + uuid = UUID.randomUUID(); + when(user.getUniqueId()).thenReturn(uuid); + when(user.getPlayer()).thenReturn(p); + when(user.getName()).thenReturn("tastybento"); + when(user.getPermissionValue(anyString(), anyInt())).thenReturn(-1); + when(user.isPlayer()).thenReturn(true); + when(user.getTranslation(anyString())).thenAnswer((Answer) invocation -> invocation.getArgument(0, String.class)); + + cg = new ChallengesGUI(addon, world, user, "island", "bskyblock."); + } + + private void addLevelChallenges(int number) { + for (int i = 0; i < number; i++) { + Challenge c = mock(Challenge.class); + when(c.isRepeatable()).thenReturn(true); + when(c.getUniqueId()).thenReturn(String.valueOf(i) + "unique"); + when(c.getIcon()).thenReturn(new ItemStack(Material.EMERALD)); + when(c.getFriendlyName()).thenReturn(String.valueOf(i) + "name"); + when(c.getChallengeType()).thenReturn(ChallengeType.INVENTORY); + when(c.getDescription()).thenReturn(Collections.singletonList("Description")); + when(c.getEnvironment()).thenReturn(Collections.singleton(Environment.NORMAL)); + when(c.getLevel()).thenReturn("Novice"); + when(c.getRewardItems()).thenReturn(Collections.emptyList()); + when(c.isDeployed()).thenReturn(true); + levelChalls.add(c); + } + + } + + private void addFreeChallenges(int number) { + for (int i = 0; i < number; i++) { + Challenge c = mock(Challenge.class); + when(c.getUniqueId()).thenReturn(String.valueOf(i) + "unique"); + when(c.getIcon()).thenReturn(new ItemStack(Material.DIAMOND)); + when(c.getFriendlyName()).thenReturn(String.valueOf(i) + "name"); + when(c.getChallengeType()).thenReturn(ChallengeType.INVENTORY); + when(c.getDescription()).thenReturn(Collections.singletonList("Description")); + when(c.getEnvironment()).thenReturn(Collections.singleton(Environment.NORMAL)); + when(c.getLevel()).thenReturn("Novice"); + when(c.getRewardItems()).thenReturn(Collections.emptyList()); + when(c.isDeployed()).thenReturn(true); + when(c.isRepeatable()).thenReturn(true); + freeChalls.add(c); + } + + } + + /** + * @throws java.lang.Exception + */ + @After + public void tearDown() throws Exception { + } + + /** + * Test method for {@link world.bentobox.challenges.panel.user.ChallengesGUI#build()}. + */ + @Test + public void testBuildNoChallenges() { + when(chm.hasAnyChallengeData(any(World.class))).thenReturn(false); + cg.build(); + verify(addon).logError("There are no challenges set up!"); + verify(user).sendMessage("challenges.errors.no-challenges"); + } + + + /** + * Test method for {@link world.bentobox.challenges.panel.user.ChallengesGUI#build()}. + */ + @Test + public void testBuild0Free0LevelChallenge() { + when(settings.isFreeChallengesFirst()).thenReturn(true); + cg.build(); + verify(user).getTranslation("challenges.gui.title.challenges"); + ArgumentCaptor argument = ArgumentCaptor.forClass(Integer.class); + ArgumentCaptor argument2 = ArgumentCaptor.forClass(ItemStack.class); + verify(inv).setItem(argument.capture(), argument2.capture()); + // Level + assertTrue(argument.getAllValues().get(0) == 0); + assertEquals(Material.BIRCH_BOAT, argument2.getAllValues().get(0).getType()); + } + + /** + * Test method for {@link world.bentobox.challenges.panel.user.ChallengesGUI#build()}. + */ + @Test + public void testBuild10Free10LevelChallenge() { + addFreeChallenges(10); + addLevelChallenges(10); + when(settings.isFreeChallengesFirst()).thenReturn(true); + cg.build(); + verify(user).getTranslation("challenges.gui.title.challenges"); + ArgumentCaptor argument = ArgumentCaptor.forClass(Integer.class); + ArgumentCaptor argument2 = ArgumentCaptor.forClass(ItemStack.class); + verify(inv, times(21)).setItem(argument.capture(), argument2.capture()); + List values = argument2.getAllValues(); + // Free challenges + for (int i = 0; i < 10; i++) { + assertEquals("Failed on " + i, Material.DIAMOND, values.get(i).getType()); + } + // Level challenges + for (int i = 11; i < 20; i++) { + assertEquals("Failed on " + i, Material.EMERALD, values.get(i).getType()); + } + // Level icons + assertTrue(argument.getAllValues().get(20) == 36); + assertEquals(Material.BIRCH_BOAT, argument2.getAllValues().get(20).getType()); + + } + /** + * Test method for {@link world.bentobox.challenges.panel.user.ChallengesGUI#build()}. + */ + @Test + public void testBuild20Free20LevelChallenge() { + addFreeChallenges(20); + addLevelChallenges(20); + when(settings.isFreeChallengesFirst()).thenReturn(true); + cg.build(); + verify(user).getTranslation("challenges.gui.title.challenges"); + ArgumentCaptor argument = ArgumentCaptor.forClass(Integer.class); + ArgumentCaptor argument2 = ArgumentCaptor.forClass(ItemStack.class); + verify(inv, times(38)).setItem(argument.capture(), argument2.capture()); + List values = argument2.getAllValues(); + // Free challenges + for (int i = 0; i < 18; i++) { + assertEquals("Failed on " + i, Material.DIAMOND, values.get(i).getType()); + } + // Next page + assertTrue(argument.getAllValues().get(18) == 18); + assertEquals(Material.OAK_SIGN, argument2.getAllValues().get(18).getType()); + // Level challenges + for (int i = 19; i < 37; i++) { + assertEquals("Failed on " + i, Material.EMERALD, values.get(i).getType()); + } + // Next page + assertTrue(argument.getAllValues().get(37) == 45); + assertEquals(Material.OAK_SIGN, argument2.getAllValues().get(37).getType()); + } + /** + * Test method for {@link world.bentobox.challenges.panel.user.ChallengesGUI#build()}. + */ + @Test + public void testBuildFreeChallenges10Free20LevelChallenge() { + addFreeChallenges(10); + addLevelChallenges(20); + when(settings.isFreeChallengesFirst()).thenReturn(true); + cg.build(); + verify(user).getTranslation("challenges.gui.title.challenges"); + ArgumentCaptor argument = ArgumentCaptor.forClass(Integer.class); + ArgumentCaptor argument2 = ArgumentCaptor.forClass(ItemStack.class); + verify(inv, times(30)).setItem(argument.capture(), argument2.capture()); + List values = argument2.getAllValues(); + // Free challenges + for (int i = 0; i < 10; i++) { + assertEquals("Failed on " + i, Material.DIAMOND, values.get(i).getType()); + } + // Level challenges + for (int i = 10; i < 27; i++) { + assertEquals("Failed on " + i, Material.EMERALD, values.get(i).getType()); + } + // Next page + assertTrue(argument.getAllValues().get(28) == 36); + assertEquals(Material.OAK_SIGN, argument2.getAllValues().get(28).getType()); + // Level + assertTrue(argument.getAllValues().get(29) == 45); + assertEquals(Material.BIRCH_BOAT, argument2.getAllValues().get(29).getType()); + } + + /** + * Test method for {@link world.bentobox.challenges.panel.user.ChallengesGUI#build()}. + */ + @Test + public void testBuildFreeChallenges20Free10LevelChallenge() { + addFreeChallenges(20); + addLevelChallenges(10); + when(settings.isFreeChallengesFirst()).thenReturn(true); + cg.build(); + verify(user).getTranslation("challenges.gui.title.challenges"); + ArgumentCaptor argument = ArgumentCaptor.forClass(Integer.class); + ArgumentCaptor argument2 = ArgumentCaptor.forClass(ItemStack.class); + verify(inv, times(30)).setItem(argument.capture(), argument2.capture()); + + List values = argument2.getAllValues(); + // Free challenges + for (int i = 0; i < 18; i++) { + assertEquals("Failed on " + i, Material.DIAMOND, values.get(i).getType()); + } + // Next page + assertTrue(argument.getAllValues().get(18) == 18); + assertEquals(Material.OAK_SIGN, argument2.getAllValues().get(18).getType()); + // Level challenges + for (int i = 19; i < 29; i++) { + assertEquals("Failed on " + i, Material.EMERALD, values.get(i).getType()); + } + + // Level + assertTrue(argument.getAllValues().get(29) == 45); + assertEquals(Material.BIRCH_BOAT, argument2.getAllValues().get(29).getType()); + } + + /** + * Test method for {@link world.bentobox.challenges.panel.user.ChallengesGUI#build()}. + */ + @Test + public void testBuildFreeChallengesLast20Free10LevelChallenge() { + addFreeChallenges(20); + addLevelChallenges(10); + when(settings.isFreeChallengesFirst()).thenReturn(false); + cg.build(); + verify(user).getTranslation("challenges.gui.title.challenges"); + ArgumentCaptor argument = ArgumentCaptor.forClass(Integer.class); + ArgumentCaptor argument2 = ArgumentCaptor.forClass(ItemStack.class); + verify(inv, times(30)).setItem(argument.capture(), argument2.capture()); + List values = argument2.getAllValues(); + + // Level challenges + for (int i = 0; i < 10; i++) { + assertEquals("Failed on " + i, Material.EMERALD, values.get(i).getType()); + } + // Next page + assertTrue(argument.getAllValues().get(10) == 18); + assertEquals(Material.BIRCH_BOAT, argument2.getAllValues().get(10).getType()); + // Free challenges + for (int i = 11; i < 29; i++) { + assertEquals("Failed on " + i, Material.DIAMOND, values.get(i).getType()); + } + + // Level + assertTrue(argument.getAllValues().get(29) == 45); + assertEquals(Material.OAK_SIGN, argument2.getAllValues().get(29).getType()); + } + +} diff --git a/src/test/java/world/bentobox/challenges/tasks/TryToCompleteTest.java b/src/test/java/world/bentobox/challenges/tasks/TryToCompleteTest.java index b5bd660..0252e8d 100644 --- a/src/test/java/world/bentobox/challenges/tasks/TryToCompleteTest.java +++ b/src/test/java/world/bentobox/challenges/tasks/TryToCompleteTest.java @@ -1,233 +1,618 @@ package world.bentobox.challenges.tasks; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.any; +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.verify; import static org.mockito.Mockito.when; -import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.logging.Logger; +import java.util.Optional; +import java.util.Set; +import java.util.UUID; import org.bukkit.Bukkit; +import org.bukkit.GameMode; +import org.bukkit.Location; import org.bukkit.Material; -import org.bukkit.Server; +import org.bukkit.World; +import org.bukkit.World.Environment; +import org.bukkit.entity.Entity; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Player; import org.bukkit.inventory.ItemFactory; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.PlayerInventory; +import org.bukkit.util.BoundingBox; +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; +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.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.api.addons.AddonDescription; +import world.bentobox.bentobox.api.addons.GameModeAddon; +import world.bentobox.bentobox.api.configuration.WorldSettings; import world.bentobox.bentobox.api.user.User; +import world.bentobox.bentobox.database.objects.Island; +import world.bentobox.bentobox.managers.AddonsManager; +import world.bentobox.bentobox.managers.IslandWorldManager; +import world.bentobox.bentobox.managers.IslandsManager; +import world.bentobox.bentobox.managers.LocalesManager; +import world.bentobox.bentobox.managers.PlaceholdersManager; +import world.bentobox.bentobox.util.Util; import world.bentobox.challenges.ChallengesAddon; +import world.bentobox.challenges.ChallengesManager; +import world.bentobox.challenges.config.Settings; +import world.bentobox.challenges.database.object.Challenge; +import world.bentobox.challenges.database.object.Challenge.ChallengeType; +import world.bentobox.challenges.database.object.ChallengeLevel; +import world.bentobox.challenges.database.object.requirements.InventoryRequirements; +import world.bentobox.challenges.database.object.requirements.IslandRequirements; +import world.bentobox.challenges.tasks.TryToComplete.ChallengeResult; +import world.bentobox.challenges.utils.Utils; + /** * @author tastybento + * */ @RunWith(PowerMockRunner.class) -@PrepareForTest({ Bukkit.class}) +@PrepareForTest({Bukkit.class, BentoBox.class, Util.class, Utils.class}) public class TryToCompleteTest { - private User user; - ItemStack[] stacks = { new ItemStack(Material.PAPER, 32), - new ItemStack(Material.ACACIA_BOAT), - null, - null, - new ItemStack(Material.CACTUS, 32), - new ItemStack(Material.CACTUS, 32), - new ItemStack(Material.CACTUS, 32), - new ItemStack(Material.BRICK_STAIRS, 64), - new ItemStack(Material.BRICK_STAIRS, 64), - new ItemStack(Material.BRICK_STAIRS, 5), - new ItemStack(Material.GOLD_BLOCK, 32) - }; - List required; - private ChallengesAddon addon; - private PlayerInventory inv; + // Constants + private static final String GAME_MODE_NAME = "BSkyBlock"; + private static final String[] NAMES = {"adam", "ben", "cara", "dave", "ed", "frank", "freddy", "george", "harry", "ian", "joe"}; - /** - * @throws java.lang.Exception - */ - @Before - public void setUp() throws Exception { - Server server = mock(Server.class); - PowerMockito.mockStatic(Bukkit.class); - when(Bukkit.getServer()).thenReturn(server); - when(Bukkit.getBukkitVersion()).thenReturn("1.13.2"); + private TryToComplete ttc; + private Challenge challenge; + private @NonNull ChallengeLevel level; + @Mock + private ChallengesAddon addon; + @Mock + private User user; + @Mock + private World world; + private String topLabel = "island"; + private String permissionPrefix = "perm."; + private String cName; + private String levelName; + @Mock + private ChallengesManager cm; + @Mock + private BentoBox plugin; + @Mock + private GameModeAddon gameMode; + @Mock + private AddonsManager am; + @Mock + private IslandsManager im; + @Mock + private Island island; + @Mock + private Player player; + @Mock + private Settings settings; + @Mock + private WorldSettings mySettings; + private Map map; + @Mock + private @Nullable PlayerInventory inv; + private ItemStack[] contents = {}; + @Mock + private BoundingBox bb; - user = mock(User.class); - inv = mock(PlayerInventory.class); - when(inv.getContents()).thenReturn(stacks); - when(user.getInventory()).thenReturn(inv); - addon = mock(ChallengesAddon.class); - required = new ArrayList<>(); + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + // Set up plugin + Whitebox.setInternalState(BentoBox.class, "instance", plugin); + when(addon.getPlugin()).thenReturn(plugin); + // World + when(user.getWorld()).thenReturn(world); + when(world.getEnvironment()).thenReturn(Environment.NORMAL); - ItemFactory itemFactory = mock(ItemFactory.class); - when(server.getItemFactory()).thenReturn(itemFactory); + // Addons manager + when(plugin.getAddonsManager()).thenReturn(am); + // One game mode + when(am.getGameModeAddons()).thenReturn(Collections.singletonList(gameMode)); + AddonDescription desc2 = new AddonDescription.Builder("bentobox", GAME_MODE_NAME, "1.3").description("test").authors("tasty").build(); + when(gameMode.getDescription()).thenReturn(desc2); - // Test will not work with items that has meta data. - when(itemFactory.getItemMeta(any())).thenReturn(null); - when(itemFactory.equals(null, null)).thenReturn(true); + // Challenge Level + level = new ChallengeLevel(); + levelName = GAME_MODE_NAME + "_novice"; + level.setUniqueId(levelName); + level.setFriendlyName("Novice"); + // Set up challenge + String uuid = UUID.randomUUID().toString(); + challenge = new Challenge(); + challenge.setUniqueId(GAME_MODE_NAME + "_" + uuid); + challenge.setFriendlyName("name"); + challenge.setLevel(GAME_MODE_NAME + "_novice"); + challenge.setDescription(Collections.singletonList("A description")); + challenge.setChallengeType(ChallengeType.INVENTORY); + challenge.setDeployed(true); + challenge.setIcon(new ItemStack(Material.EMERALD)); + challenge.setEnvironment(Collections.singleton(World.Environment.NORMAL)); + challenge.setLevel(levelName); + challenge.setRepeatable(true); + challenge.setMaxTimes(10); + InventoryRequirements req = new InventoryRequirements(); + + challenge.setRequirements(req); + // Util + PowerMockito.mockStatic(Util.class); + when(Util.getWorld(any())).thenReturn(world); + when(Util.prettifyText(anyString())).thenCallRealMethod(); - when(Bukkit.getItemFactory()).thenReturn(itemFactory); - when(Bukkit.getLogger()).thenReturn(Logger.getAnonymousLogger()); - } + // Island World Manager + IslandWorldManager iwm = mock(IslandWorldManager.class); + when(plugin.getIWM()).thenReturn(iwm); + Optional optionalGameMode = Optional.of(gameMode); + when(iwm.getAddon(any())).thenReturn(optionalGameMode); + when(iwm.getIslandDistance(any())).thenReturn(400); - /** - * Test method for {@link TryToComplete#removeItems(java.util.List, int)}. - */ - @Test - public void testRemoveItemsSuccess() { - Material requiredMaterial = Material.PAPER; - int requiredQuantity = 21; + // Island Manager + when(addon.getIslands()).thenReturn(im); + Optional opIsland = Optional.of(island); + when(im.getIslandAt(any())).thenReturn(opIsland); + when(im.getIsland(any(), any(User.class))).thenReturn(island); + // Player is on island + when(im.locationIsOnIsland(any(), any())).thenReturn(true); + // Island flags - everything is allowed by default + when(island.isAllowed(any(), any())).thenReturn(true); + // Island + + @Nullable + Location loc = mock(Location.class); + when(loc.toString()).thenReturn("center"); + when(island.getCenter()).thenReturn(loc); - this.required.add(new ItemStack(requiredMaterial, requiredQuantity)); - TryToComplete x = new TryToComplete(this.addon); - x.user(this.user); - Map removed = x.removeItems(this.required, 1); + // Challenges Manager + when(addon.getChallengesManager()).thenReturn(cm); + // All levels unlocked by default + when(cm.isLevelUnlocked(any(), any(), any())).thenReturn(true); + // Player has done this challenge 3 times (default max is 10) + when(cm.getChallengeTimes(any(), any(), any(Challenge.class))).thenReturn(3L); - assertEquals((int) removed.getOrDefault(new ItemStack(requiredMaterial, 1), 0), requiredQuantity); - } + // User has all perms by default + when(user.hasPermission(anyString())).thenReturn(true); + when(user.getPlayer()).thenReturn(player); + when(user.getTranslation(Mockito.anyString())).thenAnswer((Answer) invocation -> invocation.getArgument(0, String.class)); + when(user.getName()).thenReturn("tastybento"); + @Nullable + Location userLoc = mock(Location.class); + when(userLoc.toString()).thenReturn("location"); + when(user.getLocation()).thenReturn(userLoc); + when(user.getInventory()).thenReturn(inv); + when(inv.getContents()).thenReturn(contents); + when(player.getBoundingBox()).thenReturn(bb); + when(bb.clone()).thenReturn(bb); + when(bb.toString()).thenReturn("BoundingBox"); + // Locales + User.setPlugin(plugin); + LocalesManager lm = mock(LocalesManager.class); + when(plugin.getLocalesManager()).thenReturn(lm); + when(lm.get(any(), any())).thenAnswer((Answer) invocation -> invocation.getArgument(1, String.class)); + PlaceholdersManager phm = mock(PlaceholdersManager.class); + when(plugin.getPlaceholdersManager()).thenReturn(phm); + when(phm.replacePlaceholders(any(), any())).thenAnswer((Answer) invocation -> invocation.getArgument(1, String.class)); + + // Survival by default + when(player.getGameMode()).thenReturn(GameMode.SURVIVAL); + + // Addon + when(addon.getChallengesSettings()).thenReturn(settings); + when(settings.isBroadcastMessages()).thenReturn(true); + + // Bukkit - online players + Map online = new HashMap<>(); - /** - * Test method for {@link TryToComplete#removeItems(java.util.List, int)}. - */ - @Test - public void testRemoveItemsMax() { - Material requiredMaterial = Material.PAPER; - int requiredQuantity = 50; + Set onlinePlayers = new HashSet<>(); + for (int j = 0; j < NAMES.length; j++) { + Player p1 = mock(Player.class); + UUID uuid2 = UUID.randomUUID(); + when(p1.getUniqueId()).thenReturn(uuid2); + when(p1.getName()).thenReturn(NAMES[j]); + online.put(uuid2, NAMES[j]); + onlinePlayers.add(p1); + } + PowerMockito.mockStatic(Bukkit.class); + when(Bukkit.getOnlinePlayers()).then((Answer>) invocation -> onlinePlayers); - this.required.add(new ItemStack(requiredMaterial, requiredQuantity)); - TryToComplete x = new TryToComplete(this.addon); - x.user(this.user); - Map removed = x.removeItems(this.required, 1); + // World settings + map = new HashMap<>(); + when(mySettings.getWorldFlags()).thenReturn(map); + when(iwm.getWorldSettings(any())).thenReturn(mySettings); + ChallengesAddon.CHALLENGES_WORLD_PROTECTION.setSetting(world, true); + + // ItemFactory + ItemFactory itemFactory = mock(ItemFactory.class); + when(Bukkit.getItemFactory()).thenReturn(itemFactory); + + } - assertNotEquals((int) removed.getOrDefault(new ItemStack(requiredMaterial, 1), 0), requiredQuantity); - } + /** + * @throws java.lang.Exception + */ + @After + public void tearDown() throws Exception { + } - /** - * Test method for {@link TryToComplete#removeItems(java.util.List, int)}. - */ - @Test - public void testRemoveItemsZero() { - Material requiredMaterial = Material.PAPER; - int requiredQuantity = 0; + /** + * Test method for {@link world.bentobox.challenges.tasks.TryToComplete#TryToComplete(world.bentobox.challenges.ChallengesAddon, world.bentobox.bentobox.api.user.User, world.bentobox.challenges.database.object.Challenge, org.bukkit.World, java.lang.String, java.lang.String)}. + */ + @Test + public void testTryToCompleteChallengesAddonUserChallengeWorldStringString() { + ttc = new TryToComplete(addon, + user, + challenge, + world, + topLabel, + permissionPrefix); + verify(addon).getChallengesManager(); - this.required.add(new ItemStack(requiredMaterial, requiredQuantity)); - TryToComplete x = new TryToComplete(this.addon); - x.user(this.user); - Map removed = x.removeItems(this.required, 1); + } - assertTrue(removed.isEmpty()); - } + /** + * Test method for {@link world.bentobox.challenges.tasks.TryToComplete#complete(world.bentobox.challenges.ChallengesAddon, world.bentobox.bentobox.api.user.User, world.bentobox.challenges.database.object.Challenge, org.bukkit.World, java.lang.String, java.lang.String)}. + */ + @Test + public void testCompleteChallengesAddonUserChallengeWorldStringStringNotDeployed() { + challenge.setDeployed(false); + assertFalse(TryToComplete.complete(addon, user, challenge, world, topLabel, permissionPrefix)); + verify(user).sendMessage("challenges.errors.not-deployed"); + } + + /** + * Test method for {@link world.bentobox.challenges.tasks.TryToComplete#complete(world.bentobox.challenges.ChallengesAddon, world.bentobox.bentobox.api.user.User, world.bentobox.challenges.database.object.Challenge, org.bukkit.World, java.lang.String, java.lang.String)}. + */ + @Test + public void testCompleteChallengesAddonUserChallengeWorldStringStringWrongWorld() { + challenge.setUniqueId("test"); + assertFalse(TryToComplete.complete(addon, user, challenge, world, topLabel, permissionPrefix)); + verify(user).sendMessage("general.errors.wrong-world"); + } + + /** + * Test method for {@link world.bentobox.challenges.tasks.TryToComplete#complete(world.bentobox.challenges.ChallengesAddon, world.bentobox.bentobox.api.user.User, world.bentobox.challenges.database.object.Challenge, org.bukkit.World, java.lang.String, java.lang.String)}. + */ + @Test + public void testCompleteChallengesAddonUserChallengeWorldStringStringNotOnIsland() { + ChallengesAddon.CHALLENGES_WORLD_PROTECTION.setSetting(world, true); + when(im.locationIsOnIsland(any(Player.class), any(Location.class))).thenReturn(false); + assertFalse(TryToComplete.complete(addon, user, challenge, world, topLabel, permissionPrefix)); + verify(user).sendMessage("challenges.errors.not-on-island"); + } + + /** + * Test method for {@link world.bentobox.challenges.tasks.TryToComplete#complete(world.bentobox.challenges.ChallengesAddon, world.bentobox.bentobox.api.user.User, world.bentobox.challenges.database.object.Challenge, org.bukkit.World, java.lang.String, java.lang.String)}. + */ + @Test + public void testCompleteChallengesAddonUserChallengeWorldStringStringNotOnIslandButOk() { + ChallengesAddon.CHALLENGES_WORLD_PROTECTION.setSetting(world, false); + when(im.locationIsOnIsland(any(Player.class), any(Location.class))).thenReturn(false); + assertTrue(TryToComplete.complete(addon, user, challenge, world, topLabel, permissionPrefix)); + verify(user).sendMessage("challenges.messages.you-completed-challenge", "[value]", "name"); + } + + /** + * Test method for {@link world.bentobox.challenges.tasks.TryToComplete#complete(world.bentobox.challenges.ChallengesAddon, world.bentobox.bentobox.api.user.User, world.bentobox.challenges.database.object.Challenge, org.bukkit.World, java.lang.String, java.lang.String)}. + */ + @Test + public void testCompleteChallengesAddonUserChallengeWorldStringStringLevelNotUnlocked() { + when(cm.isLevelUnlocked(any(), any(), any())).thenReturn(false); + assertFalse(TryToComplete.complete(addon, user, challenge, world, topLabel, permissionPrefix)); + verify(user).sendMessage("challenges.errors.challenge-level-not-available"); + } + + /** + * Test method for {@link world.bentobox.challenges.tasks.TryToComplete#complete(world.bentobox.challenges.ChallengesAddon, world.bentobox.bentobox.api.user.User, world.bentobox.challenges.database.object.Challenge, org.bukkit.World, java.lang.String, java.lang.String)}. + */ + @Test + public void testCompleteChallengesAddonUserChallengeWorldStringStringNotRepeatable() { + challenge.setRepeatable(false); + when(cm.isChallengeComplete(any(User.class), any(), any())).thenReturn(true); + assertFalse(TryToComplete.complete(addon, user, challenge, world, topLabel, permissionPrefix)); + verify(user).sendMessage("challenges.errors.not-repeatable"); + } + + /** + * Test method for {@link world.bentobox.challenges.tasks.TryToComplete#complete(world.bentobox.challenges.ChallengesAddon, world.bentobox.bentobox.api.user.User, world.bentobox.challenges.database.object.Challenge, org.bukkit.World, java.lang.String, java.lang.String)}. + */ + @Test + public void testCompleteChallengesAddonUserChallengeWorldStringStringNotRepeatableFirstTime() { + challenge.setRepeatable(false); + challenge.setMaxTimes(0); + when(cm.getChallengeTimes(any(), any(), any(Challenge.class))).thenReturn(0L); + assertTrue(TryToComplete.complete(addon, user, challenge, world, topLabel, permissionPrefix)); + verify(user).sendMessage("challenges.messages.you-completed-challenge", "[value]", "name"); + } + + /** + * Test method for {@link world.bentobox.challenges.tasks.TryToComplete#complete(world.bentobox.challenges.ChallengesAddon, world.bentobox.bentobox.api.user.User, world.bentobox.challenges.database.object.Challenge, org.bukkit.World, java.lang.String, java.lang.String)}. + */ + @Test + public void testCompleteChallengesAddonUserChallengeWorldStringStringNoRank() { + when(island.isAllowed(any(), any())).thenReturn(false); + assertFalse(TryToComplete.complete(addon, user, challenge, world, topLabel, permissionPrefix)); + verify(user).sendMessage("challenges.errors.no-rank"); + } - /** - * Test method for {@link TryToComplete#removeItems(java.util.List, int)}. - */ - @Test - public void testRemoveItemsSuccessMultiple() { - required.add(new ItemStack(Material.PAPER, 11)); - required.add(new ItemStack(Material.PAPER, 5)); - required.add(new ItemStack(Material.PAPER, 5)); - TryToComplete x = new TryToComplete(addon); - x.user(user); - Map removed = x.removeItems(required, 1); + /** + * Test method for {@link world.bentobox.challenges.tasks.TryToComplete#complete(world.bentobox.challenges.ChallengesAddon, world.bentobox.bentobox.api.user.User, world.bentobox.challenges.database.object.Challenge, org.bukkit.World, java.lang.String, java.lang.String, int)}. + */ + @Test + public void testCompleteChallengesAddonUserChallengeWorldStringStringIntZero() { + assertFalse(TryToComplete.complete(addon, user, challenge, world, topLabel, permissionPrefix, 0)); + verify(user).sendMessage("challenges.errors.not-valid-integer"); + } - assertEquals((int) removed.getOrDefault(new ItemStack(Material.PAPER, 1), 0), 21); - } + /** + * Test method for {@link world.bentobox.challenges.tasks.TryToComplete#complete(world.bentobox.challenges.ChallengesAddon, world.bentobox.bentobox.api.user.User, world.bentobox.challenges.database.object.Challenge, org.bukkit.World, java.lang.String, java.lang.String, int)}. + */ + @Test + public void testCompleteChallengesAddonUserChallengeWorldStringStringIntNegative() { + assertFalse(TryToComplete.complete(addon, user, challenge, world, topLabel, permissionPrefix, -10)); + verify(user).sendMessage("challenges.errors.not-valid-integer"); + } - /** - * Test method for {@link TryToComplete#removeItems(java.util.List, int)}. - */ - @Test - public void testRemoveItemsSuccessMultipleOther() { - required.add(new ItemStack(Material.CACTUS, 5)); - required.add(new ItemStack(Material.PAPER, 11)); - required.add(new ItemStack(Material.PAPER, 5)); - required.add(new ItemStack(Material.PAPER, 5)); - required.add(new ItemStack(Material.CACTUS, 5)); - TryToComplete x = new TryToComplete(addon); - x.user(user); - Map removed = x.removeItems(required, 1); + /** + * Test method for {@link world.bentobox.challenges.tasks.TryToComplete#complete(world.bentobox.challenges.ChallengesAddon, world.bentobox.bentobox.api.user.User, world.bentobox.challenges.database.object.Challenge, org.bukkit.World, java.lang.String, java.lang.String, int)}. + */ + @Test + public void testCompleteChallengesAddonUserChallengeWorldStringStringIntPositiveWrongEnvinonment() { + challenge.setEnvironment(Collections.singleton(Environment.NETHER)); + assertFalse(TryToComplete.complete(addon, user, challenge, world, topLabel, permissionPrefix, 100)); + verify(user).sendMessage("challenges.errors.wrong-environment"); + } + + /** + * Test method for {@link world.bentobox.challenges.tasks.TryToComplete#complete(world.bentobox.challenges.ChallengesAddon, world.bentobox.bentobox.api.user.User, world.bentobox.challenges.database.object.Challenge, org.bukkit.World, java.lang.String, java.lang.String, int)}. + */ + @Test + public void testCompleteChallengesAddonUserChallengeWorldStringStringIntPositiveNoPerm() { + InventoryRequirements req = new InventoryRequirements(); + req.setRequiredPermissions(Collections.singleton("perm-you-dont-have")); + when(user.hasPermission(anyString())).thenReturn(false); + challenge.setRequirements(req); + assertFalse(TryToComplete.complete(addon, user, challenge, world, topLabel, permissionPrefix, 100)); + verify(user).sendMessage("general.errors.no-permission"); + } - assertEquals((int) removed.getOrDefault(new ItemStack(Material.PAPER, 1), 0), 21); - assertEquals((int) removed.getOrDefault(new ItemStack(Material.CACTUS, 1), 0), 10); - } + /** + * Test method for {@link world.bentobox.challenges.tasks.TryToComplete#complete(world.bentobox.challenges.ChallengesAddon, world.bentobox.bentobox.api.user.User, world.bentobox.challenges.database.object.Challenge, org.bukkit.World, java.lang.String, java.lang.String)}. + */ + @Test + public void testCompleteChallengesAddonUserChallengeWorldStringStringSuccess() { + assertTrue(TryToComplete.complete(addon, user, challenge, world, topLabel, permissionPrefix)); + verify(user).sendMessage("challenges.messages.you-completed-challenge", "[value]", "name"); + } + + /** + * Test method for {@link world.bentobox.challenges.tasks.TryToComplete#complete(world.bentobox.challenges.ChallengesAddon, world.bentobox.bentobox.api.user.User, world.bentobox.challenges.database.object.Challenge, org.bukkit.World, java.lang.String, java.lang.String)}. + */ + @Test + public void testCompleteChallengesAddonUserChallengeWorldStringStringSuccessSingleReq() { + InventoryRequirements req = new InventoryRequirements(); + req.setRequiredItems(Collections.singletonList(new ItemStack(Material.EMERALD_BLOCK))); + challenge.setRequirements(req); + assertFalse(TryToComplete.complete(addon, user, challenge, world, topLabel, permissionPrefix)); + verify(user).sendMessage("challenges.errors.not-enough-items", "[items]", "Emerald Block"); + } + + /** + * Test method for {@link world.bentobox.challenges.tasks.TryToComplete#complete(world.bentobox.challenges.ChallengesAddon, world.bentobox.bentobox.api.user.User, world.bentobox.challenges.database.object.Challenge, org.bukkit.World, java.lang.String, java.lang.String)}. + */ + @Test + public void testCompleteChallengesAddonUserChallengeWorldStringStringSuccessMultipleReq() { + + InventoryRequirements req = new InventoryRequirements(); + ItemStack itemStackMock = mock(ItemStack.class); + when(itemStackMock.getAmount()).thenReturn(3); + when(itemStackMock.getType()).thenReturn(Material.EMERALD_BLOCK); + when(itemStackMock.clone()).thenReturn(itemStackMock); + + ItemStack itemStackMock2 = mock(ItemStack.class); + when(itemStackMock2.getType()).thenReturn(Material.ENCHANTED_BOOK); + when(itemStackMock2.getAmount()).thenReturn(10); + when(itemStackMock2.clone()).thenReturn(itemStackMock2); + + ItemStack itemStackMock3 = mock(ItemStack.class); + when(itemStackMock3.getType()).thenReturn(Material.EMERALD_BLOCK); + when(itemStackMock3.getAmount()).thenReturn(15); + when(itemStackMock3.clone()).thenReturn(itemStackMock3); + // itemStackMock and 3 are same type + when(itemStackMock3.isSimilar(eq(itemStackMock))).thenReturn(true); + when(itemStackMock.isSimilar(eq(itemStackMock3))).thenReturn(true); + + req.setRequiredItems(Arrays.asList(itemStackMock , itemStackMock2)); + challenge.setRequirements(req); + ItemStack[] newContents = {itemStackMock3}; + when(inv.getContents()).thenReturn(newContents); - /** - * Test method for {@link TryToComplete#removeItems(java.util.List, int)}. - */ - @Test - public void testRemoveItemsMultipleOtherFail() { - required.add(new ItemStack(Material.ACACIA_FENCE, 5)); - required.add(new ItemStack(Material.ARROW, 11)); - required.add(new ItemStack(Material.STONE, 5)); - required.add(new ItemStack(Material.BAKED_POTATO, 5)); - required.add(new ItemStack(Material.GHAST_SPAWN_EGG, 5)); - TryToComplete x = new TryToComplete(addon); - x.user(user); - Map removed = x.removeItems(required, 1); - assertTrue(removed.isEmpty()); - } + assertFalse(TryToComplete.complete(addon, user, challenge, world, topLabel, permissionPrefix)); + // Sufficient emerald blocks + verify(user, never()).sendMessage("challenges.errors.not-enough-items", "[items]", "Emerald Block"); + // Not enough books + verify(user).sendMessage("challenges.errors.not-enough-items", "[items]", "Enchanted Book"); + } + + /** + * Test method for {@link world.bentobox.challenges.tasks.TryToComplete#complete(world.bentobox.challenges.ChallengesAddon, world.bentobox.bentobox.api.user.User, world.bentobox.challenges.database.object.Challenge, org.bukkit.World, java.lang.String, java.lang.String)}. + */ + @Test + public void testCompleteChallengesAddonUserChallengeWorldStringStringSuccessCreative() { + when(player.getGameMode()).thenReturn(GameMode.CREATIVE); + assertTrue(TryToComplete.complete(addon, user, challenge, world, topLabel, permissionPrefix)); + verify(user).sendMessage("challenges.messages.you-completed-challenge", "[value]", "name"); + } + + /** + * Test method for {@link world.bentobox.challenges.tasks.TryToComplete#complete(world.bentobox.challenges.ChallengesAddon, world.bentobox.bentobox.api.user.User, world.bentobox.challenges.database.object.Challenge, org.bukkit.World, java.lang.String, java.lang.String)}. + */ + @Test + public void testCompleteChallengesAddonUserChallengeWorldStringStringIslandBBTooLarge() { + challenge.setChallengeType(ChallengeType.ISLAND); + IslandRequirements req = new IslandRequirements(); + req.setSearchRadius(1); + challenge.setRequirements(req); + // Trigger big bounding box error + when(bb.getWidthX()).thenReturn(50000D); + assertFalse(TryToComplete.complete(addon, user, challenge, world, topLabel, permissionPrefix)); + verify(addon).logError("BoundingBox is larger than SearchRadius. | BoundingBox: BoundingBox | Search Distance: 1 | Location: location | Center: center | Range: 0"); + verify(bb).expand(1); + + } + + /** + * Test method for {@link world.bentobox.challenges.tasks.TryToComplete#complete(world.bentobox.challenges.ChallengesAddon, world.bentobox.bentobox.api.user.User, world.bentobox.challenges.database.object.Challenge, org.bukkit.World, java.lang.String, java.lang.String)}. + */ + @Test + public void testCompleteChallengesAddonUserChallengeWorldStringStringIslandSuccessNoEntities() { + challenge.setChallengeType(ChallengeType.ISLAND); + IslandRequirements req = new IslandRequirements(); + req.setSearchRadius(1); + challenge.setRequirements(req); + assertTrue(TryToComplete.complete(addon, user, challenge, world, topLabel, permissionPrefix)); + verify(user).sendMessage("challenges.messages.you-completed-challenge", "[value]", "name"); + + } + + /** + * Test method for {@link world.bentobox.challenges.tasks.TryToComplete#complete(world.bentobox.challenges.ChallengesAddon, world.bentobox.bentobox.api.user.User, world.bentobox.challenges.database.object.Challenge, org.bukkit.World, java.lang.String, java.lang.String)}. + */ + @Test + public void testCompleteChallengesAddonUserChallengeWorldStringStringIslandFailEntities() { + challenge.setChallengeType(ChallengeType.ISLAND); + IslandRequirements req = new IslandRequirements(); + Map requiredEntities = Collections.singletonMap(EntityType.GHAST, 3); + req.setRequiredEntities(requiredEntities); + req.setSearchRadius(1); + challenge.setRequirements(req); + assertFalse(TryToComplete.complete(addon, user, challenge, world, topLabel, permissionPrefix)); + verify(user).sendMessage("challenges.errors.you-still-need", "[amount]", "3", "[item]", "Ghast"); + + } + + /** + * Test method for {@link world.bentobox.challenges.tasks.TryToComplete#complete(world.bentobox.challenges.ChallengesAddon, world.bentobox.bentobox.api.user.User, world.bentobox.challenges.database.object.Challenge, org.bukkit.World, java.lang.String, java.lang.String)}. + */ + @Test + public void testCompleteChallengesAddonUserChallengeWorldStringStringIslandFailMultipleEntities() { + challenge.setChallengeType(ChallengeType.ISLAND); + IslandRequirements req = new IslandRequirements(); + Map requiredEntities = new HashMap<>(); + requiredEntities.put(EntityType.GHAST, 3); + requiredEntities.put(EntityType.CHICKEN, 5); + requiredEntities.put(EntityType.PUFFERFISH, 1); + req.setRequiredEntities(requiredEntities); + req.setSearchRadius(1); + challenge.setRequirements(req); + assertFalse(TryToComplete.complete(addon, user, challenge, world, topLabel, permissionPrefix)); + verify(user).sendMessage("challenges.errors.you-still-need", "[amount]", "3", "[item]", "Ghast"); + verify(user).sendMessage("challenges.errors.you-still-need", "[amount]", "1", "[item]", "Pufferfish"); + verify(user).sendMessage("challenges.errors.you-still-need", "[amount]", "5", "[item]", "Chicken"); + + } + + /** + * Test method for {@link world.bentobox.challenges.tasks.TryToComplete#complete(world.bentobox.challenges.ChallengesAddon, world.bentobox.bentobox.api.user.User, world.bentobox.challenges.database.object.Challenge, org.bukkit.World, java.lang.String, java.lang.String)}. + */ + @Test + public void testCompleteChallengesAddonUserChallengeWorldStringStringIslandFailPartialMultipleEntities() { + challenge.setChallengeType(ChallengeType.ISLAND); + IslandRequirements req = new IslandRequirements(); + Map requiredEntities = new HashMap<>(); + requiredEntities.put(EntityType.GHAST, 3); + requiredEntities.put(EntityType.CHICKEN, 5); + requiredEntities.put(EntityType.PUFFERFISH, 1); + req.setRequiredEntities(requiredEntities); + req.setSearchRadius(1); + challenge.setRequirements(req); + Entity ent = mock(Entity.class); + when(ent.getType()).thenReturn(EntityType.PUFFERFISH); + Location loc = mock(Location.class); + when(ent.getLocation()).thenReturn(loc); + List list = Collections.singletonList(ent); + when(world.getNearbyEntities(any(BoundingBox.class))).thenReturn(list); + assertFalse(TryToComplete.complete(addon, user, challenge, world, topLabel, permissionPrefix)); + verify(user).sendMessage("challenges.errors.you-still-need", "[amount]", "3", "[item]", "Ghast"); + verify(user, never()).sendMessage("challenges.errors.you-still-need", "[amount]", "1", "[item]", "Pufferfish"); + verify(user).sendMessage("challenges.errors.you-still-need", "[amount]", "5", "[item]", "Chicken"); + + } + + /** + * Test method for {@link world.bentobox.challenges.tasks.TryToComplete#complete(world.bentobox.challenges.ChallengesAddon, world.bentobox.bentobox.api.user.User, world.bentobox.challenges.database.object.Challenge, org.bukkit.World, java.lang.String, java.lang.String)}. + */ + @Test + public void testCompleteChallengesAddonUserChallengeWorldStringStringIslandSuccess() { + challenge.setChallengeType(ChallengeType.ISLAND); + IslandRequirements req = new IslandRequirements(); + Map requiredEntities = new HashMap<>(); + requiredEntities.put(EntityType.PUFFERFISH, 1); + req.setRequiredEntities(requiredEntities); + req.setSearchRadius(1); + challenge.setRequirements(req); + Entity ent = mock(Entity.class); + when(ent.getType()).thenReturn(EntityType.PUFFERFISH); + Location loc = mock(Location.class); + when(ent.getLocation()).thenReturn(loc); + List list = Collections.singletonList(ent); + when(world.getNearbyEntities(any(BoundingBox.class))).thenReturn(list); + assertTrue(TryToComplete.complete(addon, user, challenge, world, topLabel, permissionPrefix)); + verify(user).sendMessage("challenges.messages.you-completed-challenge", "[value]", "name"); + } - /** - * Test method for {@link TryToComplete#removeItems(java.util.List, int)}. - */ - @Test - public void testRemoveItemsFail() { - ItemStack input = new ItemStack(Material.GOLD_BLOCK, 55); - required.add(input); - TryToComplete x = new TryToComplete(addon); - x.user(user); - Map removed = x.removeItems(required, 1); + /** + * Test method for {@link world.bentobox.challenges.tasks.TryToComplete#complete(world.bentobox.challenges.ChallengesAddon, world.bentobox.bentobox.api.user.User, world.bentobox.challenges.database.object.Challenge, org.bukkit.World, java.lang.String, java.lang.String, int)}. + */ + @Test + public void testCompleteChallengesAddonUserChallengeWorldStringStringIntMultipleTimesPositiveSuccess() { + // Try to complete 10 times. Already done 3 times, and max is 10, so it should be only done 7 times + assertTrue(TryToComplete.complete(addon, user, challenge, world, topLabel, permissionPrefix, 10)); + verify(user).sendMessage("challenges.messages.you-repeated-challenge-multiple", "[value]", "name", "[count]", "7"); + } - // It will remove 32, but not any more - assertEquals((int) removed.getOrDefault(new ItemStack(Material.GOLD_BLOCK, 1), 0), 32); + /** + * Test method for {@link world.bentobox.challenges.tasks.TryToComplete#build(int)}. + */ + @Test + public void testBuild() { + this.testTryToCompleteChallengesAddonUserChallengeWorldStringString(); + ChallengeResult result = this.ttc.build(10); + } - // An error will be thrown - Mockito.verify(addon, Mockito.times(1)).logError(Mockito.anyString()); - } + /** + * Test method for {@link world.bentobox.challenges.tasks.TryToComplete#removeItems(java.util.List, int)}. + */ + @Test + public void testRemoveItems() { + this.testTryToCompleteChallengesAddonUserChallengeWorldStringString(); + ttc.removeItems(Collections.emptyList(), 1); + } - - - /** - * Test method for {@link TryToComplete#removeItems(java.util.List, int)}. - */ - @Test - public void testRequireTwoStacks() { - required.add(new ItemStack(Material.BRICK_STAIRS, 64)); - required.add(new ItemStack(Material.BRICK_STAIRS, 64)); - - TryToComplete x = new TryToComplete(addon); - x.user(user); - Map removed = x.removeItems(required, 1); - - // It should remove both stacks - assertEquals((int) removed.getOrDefault(new ItemStack(Material.BRICK_STAIRS, 1), 0), 128); - } - - - /** - * Test method for {@link TryToComplete#removeItems(java.util.List, int)}. - */ - @Test - public void testFactorStacks() { - required.add(new ItemStack(Material.BRICK_STAIRS, 32)); - - TryToComplete x = new TryToComplete(addon); - x.user(user); - Map removed = x.removeItems(required, 4); - - // It should remove both stacks - assertEquals((int) removed.getOrDefault(new ItemStack(Material.BRICK_STAIRS, 1), 0), 128); - } } - diff --git a/src/test/java/world/bentobox/challenges/tasks/TryToCompleteTestOld.java b/src/test/java/world/bentobox/challenges/tasks/TryToCompleteTestOld.java new file mode 100644 index 0000000..91d85de --- /dev/null +++ b/src/test/java/world/bentobox/challenges/tasks/TryToCompleteTestOld.java @@ -0,0 +1,233 @@ +package world.bentobox.challenges.tasks; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.logging.Logger; + +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.Server; +import org.bukkit.inventory.ItemFactory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.PlayerInventory; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +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.api.user.User; +import world.bentobox.challenges.ChallengesAddon; + +/** + * @author tastybento + */ +@RunWith(PowerMockRunner.class) +@PrepareForTest({ Bukkit.class}) +public class TryToCompleteTestOld { + + private User user; + ItemStack[] stacks = { new ItemStack(Material.PAPER, 32), + new ItemStack(Material.ACACIA_BOAT), + null, + null, + new ItemStack(Material.CACTUS, 32), + new ItemStack(Material.CACTUS, 32), + new ItemStack(Material.CACTUS, 32), + new ItemStack(Material.BRICK_STAIRS, 64), + new ItemStack(Material.BRICK_STAIRS, 64), + new ItemStack(Material.BRICK_STAIRS, 5), + new ItemStack(Material.GOLD_BLOCK, 32) + }; + List required; + private ChallengesAddon addon; + private PlayerInventory inv; + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + Server server = mock(Server.class); + PowerMockito.mockStatic(Bukkit.class); + when(Bukkit.getServer()).thenReturn(server); + when(Bukkit.getBukkitVersion()).thenReturn("1.13.2"); + + user = mock(User.class); + inv = mock(PlayerInventory.class); + when(inv.getContents()).thenReturn(stacks); + when(user.getInventory()).thenReturn(inv); + addon = mock(ChallengesAddon.class); + required = new ArrayList<>(); + + ItemFactory itemFactory = mock(ItemFactory.class); + when(server.getItemFactory()).thenReturn(itemFactory); + + // Test will not work with items that has meta data. + when(itemFactory.getItemMeta(any())).thenReturn(null); + when(itemFactory.equals(null, null)).thenReturn(true); + + when(Bukkit.getItemFactory()).thenReturn(itemFactory); + when(Bukkit.getLogger()).thenReturn(Logger.getAnonymousLogger()); + } + + /** + * Test method for {@link TryToComplete#removeItems(java.util.List, int)}. + */ + @Test + public void testRemoveItemsSuccess() { + Material requiredMaterial = Material.PAPER; + int requiredQuantity = 21; + + this.required.add(new ItemStack(requiredMaterial, requiredQuantity)); + TryToComplete x = new TryToComplete(this.addon); + x.user(this.user); + Map removed = x.removeItems(this.required, 1); + + assertEquals((int) removed.getOrDefault(new ItemStack(requiredMaterial, 1), 0), requiredQuantity); + } + + /** + * Test method for {@link TryToComplete#removeItems(java.util.List, int)}. + */ + @Test + public void testRemoveItemsMax() { + Material requiredMaterial = Material.PAPER; + int requiredQuantity = 50; + + this.required.add(new ItemStack(requiredMaterial, requiredQuantity)); + TryToComplete x = new TryToComplete(this.addon); + x.user(this.user); + Map removed = x.removeItems(this.required, 1); + + assertNotEquals((int) removed.getOrDefault(new ItemStack(requiredMaterial, 1), 0), requiredQuantity); + } + + /** + * Test method for {@link TryToComplete#removeItems(java.util.List, int)}. + */ + @Test + public void testRemoveItemsZero() { + Material requiredMaterial = Material.PAPER; + int requiredQuantity = 0; + + this.required.add(new ItemStack(requiredMaterial, requiredQuantity)); + TryToComplete x = new TryToComplete(this.addon); + x.user(this.user); + Map removed = x.removeItems(this.required, 1); + + assertTrue(removed.isEmpty()); + } + + /** + * Test method for {@link TryToComplete#removeItems(java.util.List, int)}. + */ + @Test + public void testRemoveItemsSuccessMultiple() { + required.add(new ItemStack(Material.PAPER, 11)); + required.add(new ItemStack(Material.PAPER, 5)); + required.add(new ItemStack(Material.PAPER, 5)); + TryToComplete x = new TryToComplete(addon); + x.user(user); + Map removed = x.removeItems(required, 1); + + assertEquals((int) removed.getOrDefault(new ItemStack(Material.PAPER, 1), 0), 21); + } + + /** + * Test method for {@link TryToComplete#removeItems(java.util.List, int)}. + */ + @Test + public void testRemoveItemsSuccessMultipleOther() { + required.add(new ItemStack(Material.CACTUS, 5)); + required.add(new ItemStack(Material.PAPER, 11)); + required.add(new ItemStack(Material.PAPER, 5)); + required.add(new ItemStack(Material.PAPER, 5)); + required.add(new ItemStack(Material.CACTUS, 5)); + TryToComplete x = new TryToComplete(addon); + x.user(user); + Map removed = x.removeItems(required, 1); + + assertEquals((int) removed.getOrDefault(new ItemStack(Material.PAPER, 1), 0), 21); + assertEquals((int) removed.getOrDefault(new ItemStack(Material.CACTUS, 1), 0), 10); + } + + /** + * Test method for {@link TryToComplete#removeItems(java.util.List, int)}. + */ + @Test + public void testRemoveItemsMultipleOtherFail() { + required.add(new ItemStack(Material.ACACIA_FENCE, 5)); + required.add(new ItemStack(Material.ARROW, 11)); + required.add(new ItemStack(Material.STONE, 5)); + required.add(new ItemStack(Material.BAKED_POTATO, 5)); + required.add(new ItemStack(Material.GHAST_SPAWN_EGG, 5)); + TryToComplete x = new TryToComplete(addon); + x.user(user); + Map removed = x.removeItems(required, 1); + assertTrue(removed.isEmpty()); + } + + /** + * Test method for {@link TryToComplete#removeItems(java.util.List, int)}. + */ + @Test + public void testRemoveItemsFail() { + ItemStack input = new ItemStack(Material.GOLD_BLOCK, 55); + required.add(input); + TryToComplete x = new TryToComplete(addon); + x.user(user); + Map removed = x.removeItems(required, 1); + + // It will remove 32, but not any more + assertEquals((int) removed.getOrDefault(new ItemStack(Material.GOLD_BLOCK, 1), 0), 32); + + // An error will be thrown + Mockito.verify(addon, Mockito.times(1)).logError(Mockito.anyString()); + } + + + + /** + * Test method for {@link TryToComplete#removeItems(java.util.List, int)}. + */ + @Test + public void testRequireTwoStacks() { + required.add(new ItemStack(Material.BRICK_STAIRS, 64)); + required.add(new ItemStack(Material.BRICK_STAIRS, 64)); + + TryToComplete x = new TryToComplete(addon); + x.user(user); + Map removed = x.removeItems(required, 1); + + // It should remove both stacks + assertEquals((int) removed.getOrDefault(new ItemStack(Material.BRICK_STAIRS, 1), 0), 128); + } + + + /** + * Test method for {@link TryToComplete#removeItems(java.util.List, int)}. + */ + @Test + public void testFactorStacks() { + required.add(new ItemStack(Material.BRICK_STAIRS, 32)); + + TryToComplete x = new TryToComplete(addon); + x.user(user); + Map removed = x.removeItems(required, 4); + + // It should remove both stacks + assertEquals((int) removed.getOrDefault(new ItemStack(Material.BRICK_STAIRS, 1), 0), 128); + } +} + diff --git a/src/test/java/world/bentobox/challenges/utils/UtilsTest.java b/src/test/java/world/bentobox/challenges/utils/UtilsTest.java new file mode 100644 index 0000000..a89873f --- /dev/null +++ b/src/test/java/world/bentobox/challenges/utils/UtilsTest.java @@ -0,0 +1,199 @@ +package world.bentobox.challenges.utils; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +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.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.inventory.ItemFactory; +import org.bukkit.inventory.ItemStack; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +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.api.addons.AddonDescription; +import world.bentobox.bentobox.api.addons.GameModeAddon; +import world.bentobox.bentobox.managers.IslandWorldManager; +import world.bentobox.challenges.config.SettingsUtils.VisibilityMode; + +/** + * @author tastybento + * + */ +@RunWith(PowerMockRunner.class) +@PrepareForTest({Bukkit.class}) +public class UtilsTest { + + @Mock + private IslandWorldManager iwm; + @Mock + private GameModeAddon gameModeAddon; + + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + // Set up plugin + BentoBox plugin = mock(BentoBox.class); + Whitebox.setInternalState(BentoBox.class, "instance", plugin); + + // Mock item factory (for itemstacks) + PowerMockito.mockStatic(Bukkit.class); + ItemFactory itemFactory = mock(ItemFactory.class); + when(Bukkit.getItemFactory()).thenReturn(itemFactory); + + // IWM getAddon + AddonDescription desc = new AddonDescription.Builder("main", "name", "1.0").build(); + when(gameModeAddon.getDescription()).thenReturn(desc); + Optional optionalAddon = Optional.of(gameModeAddon); + when(iwm.getAddon(any())).thenReturn(optionalAddon); + when(plugin.getIWM()).thenReturn(iwm); + + } + + /** + * @throws java.lang.Exception + */ + @After + public void tearDown() throws Exception { + } + + /** + * Test method for {@link world.bentobox.challenges.utils.Utils#groupEqualItems(java.util.List)}. + */ + @Test + public void testGroupEqualItemsEmpty() { + assertTrue(Utils.groupEqualItems(Collections.emptyList()).isEmpty()); + } + + /** + * Test method for {@link world.bentobox.challenges.utils.Utils#groupEqualItems(java.util.List)}. + */ + @Test + public void testGroupEqualItems() { + List requiredItems = new ArrayList<>(); + // First item + ItemStack is = mock(ItemStack.class); + when(is.getAmount()).thenReturn(1); + when(is.getType()).thenReturn(Material.ACACIA_FENCE); + when(is.getMaxStackSize()).thenReturn(64); + when(is.isSimilar(any())).thenReturn(true); + when(is.clone()).thenReturn(is); + requiredItems.add(is); + for (int i = 0; i < 9; i++) { + ItemStack is2 = mock(ItemStack.class); + when(is2.getAmount()).thenReturn(1); + when(is2.getType()).thenReturn(Material.ACACIA_FENCE); + when(is2.getMaxStackSize()).thenReturn(64); + when(is2.isSimilar(any())).thenReturn(true); + when(is2.clone()).thenReturn(is); + requiredItems.add(is2); + } + List list = Utils.groupEqualItems(requiredItems); + // Result should be two stacks stack of 64 doors and 36 doors + assertEquals(1, list.size()); + verify(is, times(9)).setAmount(2); + } + + /** + * Test method for {@link world.bentobox.challenges.utils.Utils#groupEqualItems(java.util.List)}. + */ + @Test + public void testGroupEqualItemsUnique() { + List requiredItems = new ArrayList<>(); + // First item + ItemStack is = mock(ItemStack.class); + when(is.getAmount()).thenReturn(1); + when(is.getType()).thenReturn(Material.ACACIA_FENCE); + when(is.getMaxStackSize()).thenReturn(64); + when(is.isSimilar(any())).thenReturn(false); + when(is.clone()).thenReturn(is); + requiredItems.add(is); + for (int i = 0; i < 9; i++) { + ItemStack is2 = mock(ItemStack.class); + when(is2.getAmount()).thenReturn(1); + when(is2.getType()).thenReturn(Material.values()[i+20]); + when(is2.getMaxStackSize()).thenReturn(64); + when(is2.isSimilar(any())).thenReturn(false); + when(is2.clone()).thenReturn(is); + requiredItems.add(is2); + } + List list = Utils.groupEqualItems(requiredItems); + // Result should be two stacks stack of 64 doors and 36 doors + assertEquals(10, list.size()); + verify(is, never()).setAmount(2); + } + + /** + * Test method for {@link world.bentobox.challenges.utils.Utils#canIgnoreMeta(org.bukkit.Material)}. + */ + @Test + public void testCanIgnoreMeta() { + assertTrue(Utils.canIgnoreMeta(Material.FIREWORK_ROCKET)); + assertTrue(Utils.canIgnoreMeta(Material.ENCHANTED_BOOK)); + assertTrue(Utils.canIgnoreMeta(Material.WRITTEN_BOOK)); + assertTrue(Utils.canIgnoreMeta(Material.FILLED_MAP)); + assertFalse(Utils.canIgnoreMeta(Material.CHISELED_RED_SANDSTONE)); + } + + /** + * Test method for {@link world.bentobox.challenges.utils.Utils#getGameMode(org.bukkit.World)}. + */ + @Test + public void testGetGameModeNoGameMode() { + when(iwm.getAddon(any())).thenReturn(Optional.empty()); + assertNull(Utils.getGameMode(mock(World.class))); + } + + /** + * Test method for {@link world.bentobox.challenges.utils.Utils#getGameMode(org.bukkit.World)}. + */ + @Test + public void testGetGameMode() { + assertEquals("name", Utils.getGameMode(mock(World.class))); + } + + /** + * Test method for {@link world.bentobox.challenges.utils.Utils#getNextValue(T[], java.lang.Object)}. + */ + @Test + public void testGetNextValue() { + assertEquals(VisibilityMode.HIDDEN, Utils.getNextValue(VisibilityMode.values(), VisibilityMode.VISIBLE)); + assertEquals(VisibilityMode.TOGGLEABLE, Utils.getNextValue(VisibilityMode.values(), VisibilityMode.HIDDEN)); + assertEquals(VisibilityMode.VISIBLE, Utils.getNextValue(VisibilityMode.values(), VisibilityMode.TOGGLEABLE)); + } + + /** + * Test method for {@link world.bentobox.challenges.utils.Utils#getPreviousValue(T[], java.lang.Object)}. + */ + @Test + public void testGetPreviousValue() { + assertEquals(VisibilityMode.TOGGLEABLE, Utils.getPreviousValue(VisibilityMode.values(), VisibilityMode.VISIBLE)); + assertEquals(VisibilityMode.VISIBLE, Utils.getPreviousValue(VisibilityMode.values(), VisibilityMode.HIDDEN)); + assertEquals(VisibilityMode.HIDDEN, Utils.getPreviousValue(VisibilityMode.values(), VisibilityMode.TOGGLEABLE)); + } + +}