From 99a782999059e08799fd0c47ae8aa9162b6671f2 Mon Sep 17 00:00:00 2001 From: alexcrea <42614139+alexcrea@users.noreply.github.com> Date: Thu, 24 Oct 2024 10:29:29 +0200 Subject: [PATCH] Add fuse test. Also made some modification to allow the test to work --- build.gradle.kts | 2 +- .../gui/version/v1_21R1_ExternGuiTester.kt | 18 +++ .../bulk/BukkitEnchantBulkOperation.java | 4 +- .../cuanvil/dependency/DependencyManager.kt | 5 +- .../dependency/DisenchantmentDependency.kt | 7 +- .../cuanvil/listener/PrepareAnvilListener.kt | 23 +-- .../xyz/alexcrea/cuanvil/util/AnvilXpUtil.kt | 4 +- .../java/io/delilaheve/CustomAnvilTests.java | 39 +---- .../cuanvil/DefaultCustomAnvilTest.java | 53 +++++++ .../alexcrea/cuanvil/anvil/AnvilFuseTest.java | 81 ++++++++++ .../alexcrea/cuanvil/mock/AnvilViewMock.java | 76 ++++++++++ .../cuanvil/mock/EnchantedItemStackMock.java | 56 +++++++ .../cuanvil/util/AnvilFuseTestData.java | 39 +++++ .../cuanvil/util/AnvilFuseTestUtil.java | 138 ++++++++++++++++++ .../alexcrea/cuanvil/util/CommonItemUtil.java | 21 +++ 15 files changed, 514 insertions(+), 52 deletions(-) create mode 100644 src/test/java/xyz/alexcrea/cuanvil/DefaultCustomAnvilTest.java create mode 100644 src/test/java/xyz/alexcrea/cuanvil/anvil/AnvilFuseTest.java create mode 100644 src/test/java/xyz/alexcrea/cuanvil/mock/AnvilViewMock.java create mode 100644 src/test/java/xyz/alexcrea/cuanvil/mock/EnchantedItemStackMock.java create mode 100644 src/test/java/xyz/alexcrea/cuanvil/util/AnvilFuseTestData.java create mode 100644 src/test/java/xyz/alexcrea/cuanvil/util/AnvilFuseTestUtil.java create mode 100644 src/test/java/xyz/alexcrea/cuanvil/util/CommonItemUtil.java diff --git a/build.gradle.kts b/build.gradle.kts index 31ea957..c2a0da6 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -87,7 +87,7 @@ allprojects { compileOnly(kotlin("stdlib")) // Test dependency - testImplementation(platform("org.junit:junit-bom:5.9.1")) + testImplementation(platform("org.junit:junit-bom:5.11.3")) testImplementation("org.junit.jupiter:junit-jupiter") } diff --git a/nms/v1_21R1/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/gui/version/v1_21R1_ExternGuiTester.kt b/nms/v1_21R1/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/gui/version/v1_21R1_ExternGuiTester.kt index 7caa61b..eeee7ec 100644 --- a/nms/v1_21R1/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/gui/version/v1_21R1_ExternGuiTester.kt +++ b/nms/v1_21R1/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/gui/version/v1_21R1_ExternGuiTester.kt @@ -7,10 +7,28 @@ import xyz.alexcrea.cuanvil.dependency.gui.ExternGuiTester class v1_21R1_ExternGuiTester: ExternGuiTester { override val wesjdAnvilGuiName = "Wrapper1_21_R1" + var tested = false; + var possible = false; + override fun getContainerClass(view: InventoryView): Class? { + // In case we are in a test environment + if(!tested) testClassExist() + if(!possible) return null + if(view !is CraftInventoryView<*, *>) return null val container = view.handle return container.javaClass } + + fun testClassExist(){ + tested = true; + try { + Class.forName("org.bukkit.craftbukkit.inventory.CraftInventoryView") + possible = true + } catch (e: ClassNotFoundException){ + possible = false + } + } + } diff --git a/src/main/java/xyz/alexcrea/cuanvil/enchant/bulk/BukkitEnchantBulkOperation.java b/src/main/java/xyz/alexcrea/cuanvil/enchant/bulk/BukkitEnchantBulkOperation.java index d34bd49..cb56436 100644 --- a/src/main/java/xyz/alexcrea/cuanvil/enchant/bulk/BukkitEnchantBulkOperation.java +++ b/src/main/java/xyz/alexcrea/cuanvil/enchant/bulk/BukkitEnchantBulkOperation.java @@ -29,7 +29,8 @@ public class BukkitEnchantBulkOperation implements BulkGetEnchantOperation, Bulk @Override public void bulkClear(@NotNull ItemStack item) { if (item.getType() != Material.ENCHANTED_BOOK) { - item.getEnchantments().forEach((enchantment, leve) -> + + item.getEnchantments().forEach((enchantment, level) -> item.removeEnchantment(enchantment) ); } @@ -43,5 +44,6 @@ public class BukkitEnchantBulkOperation implements BulkGetEnchantOperation, Bulk bookMeta.removeStoredEnchant(enchantment) ); } + } } diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/DependencyManager.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/DependencyManager.kt index 4aa69db..9082b95 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/DependencyManager.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/DependencyManager.kt @@ -2,6 +2,7 @@ package xyz.alexcrea.cuanvil.dependency import io.delilaheve.CustomAnvil import org.bukkit.Bukkit +import org.bukkit.entity.HumanEntity import org.bukkit.event.inventory.InventoryClickEvent import org.bukkit.event.inventory.PrepareAnvilEvent import org.bukkit.inventory.AnvilInventory @@ -91,11 +92,11 @@ object DependencyManager { } - fun tryEventPreAnvilBypass(event: PrepareAnvilEvent): Boolean { + fun tryEventPreAnvilBypass(event: PrepareAnvilEvent, player: HumanEntity): Boolean { var bypass = false // Test if disenchantment used special prepare anvil - if(disenchantmentCompatibility?.testPrepareAnvil(event) == true) bypass = true + if(disenchantmentCompatibility?.testPrepareAnvil(event, player) == true) bypass = true // Test excellent enchantments used special prepare anvil if(!bypass && (excellentEnchantsCompatibility?.testPrepareAnvil(event) == true)) bypass = true diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/DisenchantmentDependency.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/DisenchantmentDependency.kt index 439dbb4..3ce2ca6 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/DisenchantmentDependency.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/dependency/DisenchantmentDependency.kt @@ -5,6 +5,7 @@ import cz.kominekjan.disenchantment.events.DisenchantEvent import cz.kominekjan.disenchantment.events.ShatterClickEvent import cz.kominekjan.disenchantment.events.ShatterEvent import io.delilaheve.CustomAnvil +import org.bukkit.entity.HumanEntity import org.bukkit.event.inventory.InventoryClickEvent import org.bukkit.event.inventory.PrepareAnvilEvent import org.bukkit.inventory.AnvilInventory @@ -70,7 +71,7 @@ class DisenchantmentDependency { } - fun testPrepareAnvil(event: PrepareAnvilEvent): Boolean { + fun testPrepareAnvil(event: PrepareAnvilEvent, player: HumanEntity): Boolean { val previousResult = event.result event.result = null @@ -79,14 +80,14 @@ class DisenchantmentDependency { if(event.result != null) { CustomAnvil.log("Detected pre anvil item extract bypass.") - AnvilXpUtil.setAnvilInvXp(event.inventory, event.view, event.inventory.repairCost) + AnvilXpUtil.setAnvilInvXp(event.inventory, event.view, player, event.inventory.repairCost) return true } splitEvent.onDisenchantmentEvent(event) if(event.result != null) { CustomAnvil.log("Detected pre anvil split enchant bypass.") - AnvilXpUtil.setAnvilInvXp(event.inventory, event.view, event.inventory.repairCost) + AnvilXpUtil.setAnvilInvXp(event.inventory, event.view, player, event.inventory.repairCost) return true } diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/listener/PrepareAnvilListener.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/listener/PrepareAnvilListener.kt index b1ec4b9..4ba12ca 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/listener/PrepareAnvilListener.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/listener/PrepareAnvilListener.kt @@ -11,11 +11,13 @@ import io.delilaheve.util.ItemUtil.setEnchantmentsUnsafe import io.delilaheve.util.ItemUtil.unitRepair import org.bukkit.ChatColor import org.bukkit.entity.HumanEntity +import org.bukkit.entity.Player import org.bukkit.event.EventHandler import org.bukkit.event.EventPriority import org.bukkit.event.Listener import org.bukkit.event.inventory.PrepareAnvilEvent import org.bukkit.inventory.AnvilInventory +import org.bukkit.inventory.InventoryView import org.bukkit.inventory.ItemStack import xyz.alexcrea.cuanvil.dependency.DependencyManager import xyz.alexcrea.cuanvil.util.AnvilColorUtil @@ -41,19 +43,20 @@ class PrepareAnvilListener : Listener { */ @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) fun anvilCombineCheck(event: PrepareAnvilEvent) { + // Should find player + val player: HumanEntity = event.viewers.first() + // Test if the event should bypass custom anvil. - if(DependencyManager.tryEventPreAnvilBypass(event)) return + if(DependencyManager.tryEventPreAnvilBypass(event, player)) return val inventory = event.inventory val first = inventory.getItem(ANVIL_INPUT_LEFT) ?: return val second = inventory.getItem(ANVIL_INPUT_RIGHT) - // Should find player - val player = event.view.player if (!player.hasPermission(CustomAnvil.affectedByPluginPermission)) return // Test custom recipe - if(testCustomRecipe(event, inventory, first, second)) return + if(testCustomRecipe(event, inventory, player, first, second)) return // Test rename lonely item if(second == null) { @@ -75,7 +78,9 @@ class PrepareAnvilListener : Listener { } - private fun testCustomRecipe(event: PrepareAnvilEvent, inventory: AnvilInventory, first: ItemStack, second: ItemStack?): Boolean { + private fun testCustomRecipe(event: PrepareAnvilEvent, inventory: AnvilInventory, + player: HumanEntity, + first: ItemStack, second: ItemStack?): Boolean { val recipe = CustomRecipeUtil.getCustomRecipe(first, second) CustomAnvil.verboseLog("custom recipe not null? ${recipe != null}") if(recipe == null) return false @@ -87,7 +92,7 @@ class PrepareAnvilListener : Listener { event.result = resultItem DependencyManager.treatAnvilResult(event, resultItem) - AnvilXpUtil.setAnvilInvXp(inventory, event.view, recipe.xpCostPerCraft * amount, true) + AnvilXpUtil.setAnvilInvXp(inventory, event.view, player, recipe.xpCostPerCraft * amount, true) return true } @@ -109,7 +114,7 @@ class PrepareAnvilListener : Listener { anvilCost += AnvilXpUtil.calculatePenalty(first, null, resultItem) - AnvilXpUtil.setAnvilInvXp(inventory, event.view, anvilCost) + AnvilXpUtil.setAnvilInvXp(inventory, event.view, player, anvilCost) } private fun handleRename(resultItem: ItemStack, inventory: AnvilInventory, player: HumanEntity): Int { @@ -177,7 +182,7 @@ class PrepareAnvilListener : Listener { event.result = resultItem DependencyManager.treatAnvilResult(event, resultItem) - AnvilXpUtil.setAnvilInvXp(inventory, event.view, anvilCost) + AnvilXpUtil.setAnvilInvXp(inventory, event.view, player, anvilCost) } private fun testUnitRepair(event: PrepareAnvilEvent, inventory: AnvilInventory, player: HumanEntity, @@ -203,7 +208,7 @@ class PrepareAnvilListener : Listener { event.result = resultItem DependencyManager.treatAnvilResult(event, resultItem) - AnvilXpUtil.setAnvilInvXp(inventory, event.view, anvilCost) + AnvilXpUtil.setAnvilInvXp(inventory, event.view, player, anvilCost) return true } diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/util/AnvilXpUtil.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/util/AnvilXpUtil.kt index bdbc8bd..74e604c 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/util/AnvilXpUtil.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/util/AnvilXpUtil.kt @@ -6,6 +6,7 @@ import io.delilaheve.util.EnchantmentUtil.enchantmentName import io.delilaheve.util.ItemUtil.findEnchantments import io.delilaheve.util.ItemUtil.isEnchantedBook import org.bukkit.GameMode +import org.bukkit.entity.HumanEntity import org.bukkit.entity.Player import org.bukkit.inventory.AnvilInventory import org.bukkit.inventory.InventoryView @@ -25,6 +26,7 @@ object AnvilXpUtil { fun setAnvilInvXp( inventory: AnvilInventory, view: InventoryView, + player: HumanEntity, anvilCost: Int, ignoreRules: Boolean = false ) { @@ -38,8 +40,6 @@ object AnvilXpUtil { anvilCost } - val player = view.player - /* Because Minecraft likes to have the final say in the repair cost displayed * we need to wait for the event to end before overriding it, this ensures that * we have the final say in the process. */ diff --git a/src/test/java/io/delilaheve/CustomAnvilTests.java b/src/test/java/io/delilaheve/CustomAnvilTests.java index 99785e7..0fb8cbe 100644 --- a/src/test/java/io/delilaheve/CustomAnvilTests.java +++ b/src/test/java/io/delilaheve/CustomAnvilTests.java @@ -1,44 +1,15 @@ package io.delilaheve; -import be.seeseemelk.mockbukkit.MockBukkit; -import be.seeseemelk.mockbukkit.ServerMock; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import xyz.alexcrea.cuanvil.DefaultCustomAnvilTest; -public class CustomAnvilTests { - - private ServerMock server; - - private CustomAnvil plugin; - - @BeforeEach - public void setUp() { - - // Start the mock server - server = MockBukkit.mock(); - - // Load your plugin - plugin = MockBukkit.load(CustomAnvil.class); - - - - } - - - @AfterEach - public void tearDown() { - // Stop the mock server - MockBukkit.unmock(); - - } - +public class CustomAnvilTests extends DefaultCustomAnvilTest { @Test public void simpleInitTest() { - // Continue initialization of the plugin - server.getScheduler().performOneTick(); - + Assertions.assertNotNull(server); + Assertions.assertNotNull(plugin); } } diff --git a/src/test/java/xyz/alexcrea/cuanvil/DefaultCustomAnvilTest.java b/src/test/java/xyz/alexcrea/cuanvil/DefaultCustomAnvilTest.java new file mode 100644 index 0000000..2cb6b62 --- /dev/null +++ b/src/test/java/xyz/alexcrea/cuanvil/DefaultCustomAnvilTest.java @@ -0,0 +1,53 @@ +package xyz.alexcrea.cuanvil; + +import be.seeseemelk.mockbukkit.MockBukkit; +import be.seeseemelk.mockbukkit.ServerMock; +import io.delilaheve.CustomAnvil; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import xyz.alexcrea.cuanvil.config.ConfigHolder; +import xyz.alexcrea.cuanvil.enchant.CAEnchantment; +import xyz.alexcrea.cuanvil.enchant.CAEnchantmentRegistry; + +import java.util.ArrayList; +import java.util.List; + +public class DefaultCustomAnvilTest { + + protected ServerMock server; + protected CustomAnvil plugin; + + @BeforeEach + public void setUp() { + // Start the mock server + server = MockBukkit.mock(); + // Load your plugin + plugin = MockBukkit.load(CustomAnvil.class); + // Continue initialization of the plugin + server.getScheduler().performOneTick(); + } + + @AfterEach + public void tearDown() { + // Set config to null + ConfigHolder.DEFAULT_CONFIG = null; + ConfigHolder.ITEM_GROUP_HOLDER = null; + ConfigHolder.CONFLICT_HOLDER = null; + ConfigHolder.UNIT_REPAIR_HOLDER = null; + ConfigHolder.CUSTOM_RECIPE_HOLDER = null; + + // Stop the mock server + MockBukkit.unmock(); + + // Unregister enchantments + List toUnregister = new ArrayList<>( + CAEnchantmentRegistry.getInstance().values() + ); + + for (CAEnchantment caEnchantment : toUnregister) { + CAEnchantmentRegistry.getInstance().unregister(caEnchantment); + } + + } + +} diff --git a/src/test/java/xyz/alexcrea/cuanvil/anvil/AnvilFuseTest.java b/src/test/java/xyz/alexcrea/cuanvil/anvil/AnvilFuseTest.java new file mode 100644 index 0000000..6b7bca7 --- /dev/null +++ b/src/test/java/xyz/alexcrea/cuanvil/anvil/AnvilFuseTest.java @@ -0,0 +1,81 @@ +package xyz.alexcrea.cuanvil.anvil; + +import be.seeseemelk.mockbukkit.entity.PlayerMock; +import org.bukkit.event.inventory.InventoryType; +import org.bukkit.inventory.AnvilInventory; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import xyz.alexcrea.cuanvil.DefaultCustomAnvilTest; +import xyz.alexcrea.cuanvil.config.ConfigHolder; +import xyz.alexcrea.cuanvil.util.AnvilFuseTestData; +import xyz.alexcrea.cuanvil.util.AnvilFuseTestUtil; +import xyz.alexcrea.cuanvil.util.CommonItemUtil; + +public class AnvilFuseTest extends DefaultCustomAnvilTest { + + private AnvilInventory anvil; + private PlayerMock player; + + @Override + @BeforeEach + public void setUp() { + super.setUp(); + // Mock used player & open anvil + player = server.addPlayer(); + + Inventory anvil = server.createInventory(player, InventoryType.ANVIL); + + this.anvil = (AnvilInventory) anvil; + player.openInventory(anvil); + + ConfigHolder.DEFAULT_CONFIG.getConfig().set("debug_log", true); + ConfigHolder.DEFAULT_CONFIG.getConfig().set("debug_log_verbose", true); + } + + @Test + public void mergeFuseTest(){ + // Literally just test a sharpness 4 + sharpness 4 + ItemStack sharpness4 = CommonItemUtil.sharpness(4); + ItemStack sharpness5 = CommonItemUtil.sharpness(5); + + AnvilFuseTestData data = new AnvilFuseTestData( + sharpness4, sharpness4, + sharpness5 + // TODO add expected price + ); + + AnvilFuseTestUtil.executeAnvilTest(anvil, player, data); + } + + @Test + public void overFuseTest(){ + // Test sharpness 4 + sharpness 5 + ItemStack sharpness4 = CommonItemUtil.sharpness(4); + ItemStack sharpness5 = CommonItemUtil.sharpness(5); + + AnvilFuseTestData data = new AnvilFuseTestData( + sharpness4, sharpness5, + sharpness5 + // TODO add expected price + ); + + AnvilFuseTestUtil.executeAnvilTest(anvil, player, data); + } + + @Test + public void underFuseTest(){ + // test sharpness 5 + 4. Custom Anvil should not allow it to be as it result as the same item as left item + ItemStack sharpness4 = CommonItemUtil.sharpness(4); + ItemStack sharpness5 = CommonItemUtil.sharpness(5); + + AnvilFuseTestData data = new AnvilFuseTestData( + sharpness5, sharpness4, + null + ); + + AnvilFuseTestUtil.executeAnvilTest(anvil, player, data); + } + +} diff --git a/src/test/java/xyz/alexcrea/cuanvil/mock/AnvilViewMock.java b/src/test/java/xyz/alexcrea/cuanvil/mock/AnvilViewMock.java new file mode 100644 index 0000000..87e4bde --- /dev/null +++ b/src/test/java/xyz/alexcrea/cuanvil/mock/AnvilViewMock.java @@ -0,0 +1,76 @@ +package xyz.alexcrea.cuanvil.mock; + +import be.seeseemelk.mockbukkit.inventory.PlayerInventoryViewMock; +import org.bukkit.entity.HumanEntity; +import org.bukkit.inventory.AnvilInventory; +import org.bukkit.inventory.view.AnvilView; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +@SuppressWarnings({"removal"}) +public class AnvilViewMock extends PlayerInventoryViewMock implements AnvilView { + + private @NotNull AnvilInventory top; + + /** + * Constructs a new {@link PlayerInventoryViewMock} for the provided player, with the specified top inventory. + * + * @param player The player to create the view for. + * @param top The top inventory. + */ + public AnvilViewMock(@NotNull HumanEntity player, @NotNull AnvilInventory top) { + super(player, top); + this.top = top; + } + + @Override + public @Nullable String getRenameText() { + return top.getRenameText(); + } + + @Override + public int getRepairItemCountCost() { + return top.getRepairCostAmount(); + } + + @Override + public int getRepairCost() { + return top.getRepairCost(); + } + + @Override + public int getMaximumRepairCost() { + return top.getMaximumRepairCost(); + } + + @Override + public void setRepairItemCountCost(int amount) { + top.setRepairCostAmount(amount); + } + + @Override + public void setRepairCost(int cost) { + top.setRepairCost(cost); + } + + @Override + public void setMaximumRepairCost(int levels) { + top.setMaximumRepairCost(levels); + } + + @Override + public boolean bypassesEnchantmentLevelRestriction() { + throw new UnsupportedOperationException("Custom anvil was not think with this existing"); + } + + @Override + public void bypassEnchantmentLevelRestriction(boolean bypassEnchantmentLevelRestriction) { + throw new UnsupportedOperationException("Custom anvil was not think with this existing"); + } + + @Override + public @NotNull AnvilInventory getTopInventory() { + return top; + } + +} diff --git a/src/test/java/xyz/alexcrea/cuanvil/mock/EnchantedItemStackMock.java b/src/test/java/xyz/alexcrea/cuanvil/mock/EnchantedItemStackMock.java new file mode 100644 index 0000000..928e2ce --- /dev/null +++ b/src/test/java/xyz/alexcrea/cuanvil/mock/EnchantedItemStackMock.java @@ -0,0 +1,56 @@ +package xyz.alexcrea.cuanvil.mock; + +import be.seeseemelk.mockbukkit.inventory.ItemStackMock; +import com.google.common.collect.ImmutableMap; +import org.bukkit.Material; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; + +import java.util.Map; + +public class EnchantedItemStackMock extends ItemStackMock { + + EnchantedItemStackMock(){} + + public EnchantedItemStackMock(@NotNull Material type, int amount) { + super(type, amount); + } + + public EnchantedItemStackMock(@NotNull Material type) { + this(type, 1); + } + + public EnchantedItemStackMock(@NotNull ItemStack stack) throws IllegalArgumentException { + super(stack); + } + + @Override + public int removeEnchantment(@NotNull Enchantment ench) { + int level = this.getEnchantmentLevel(ench); + this.getItemMeta().removeEnchant(ench); + + return level; + } + + @Override + public void removeEnchantments() { + this.getItemMeta().removeEnchantments(); + } + + // badly imitate paper (and I hope spigot) behavior and avoid concurrent modification exception + @Override + public @NotNull Map getEnchantments() { + return ImmutableMap.copyOf(super.getEnchantments()); + } + + @Override + public @NotNull ItemStack clone() { + ItemStackMock clone = new EnchantedItemStackMock(this.getType()); + + clone.setAmount(this.getAmount()); + clone.setDurability(this.getDurability()); + clone.setItemMeta(this.hasItemMeta() ? this.getItemMeta().clone() : null); + return clone; + } +} diff --git a/src/test/java/xyz/alexcrea/cuanvil/util/AnvilFuseTestData.java b/src/test/java/xyz/alexcrea/cuanvil/util/AnvilFuseTestData.java new file mode 100644 index 0000000..2268d44 --- /dev/null +++ b/src/test/java/xyz/alexcrea/cuanvil/util/AnvilFuseTestData.java @@ -0,0 +1,39 @@ +package xyz.alexcrea.cuanvil.util; + +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.Nullable; + +public record AnvilFuseTestData( + @Nullable ItemStack leftItem, + @Nullable ItemStack rightItem, + @Nullable ItemStack expectedResult, + + @Nullable ItemStack expectedAfterLeftPlaced, + @Nullable ItemStack expectedAfterRightPlaced, + + @Nullable Integer expectedPriceAfterLeftPlaced, + @Nullable Integer expectedPriceAfterRightPlaced, + @Nullable Integer expectedPriceAfterBothPlaced + ){ + + public AnvilFuseTestData( + @Nullable ItemStack leftItem, + @Nullable ItemStack rightItem, + @Nullable ItemStack expectedResult, + @Nullable Integer expectedPriceAfterBothPlaced + ){ + this(leftItem, rightItem, expectedResult, + null, null, null, null, + expectedPriceAfterBothPlaced + ); + } + + public AnvilFuseTestData( + @Nullable ItemStack leftItem, + @Nullable ItemStack rightItem, + @Nullable ItemStack expectedResult + ){ + this(leftItem, rightItem, expectedResult, null + ); + } +} diff --git a/src/test/java/xyz/alexcrea/cuanvil/util/AnvilFuseTestUtil.java b/src/test/java/xyz/alexcrea/cuanvil/util/AnvilFuseTestUtil.java new file mode 100644 index 0000000..eb48b13 --- /dev/null +++ b/src/test/java/xyz/alexcrea/cuanvil/util/AnvilFuseTestUtil.java @@ -0,0 +1,138 @@ +package xyz.alexcrea.cuanvil.util; + +import io.delilaheve.util.ItemUtil; +import org.bukkit.Material; +import org.bukkit.entity.HumanEntity; +import org.bukkit.event.inventory.PrepareAnvilEvent; +import org.bukkit.inventory.AnvilInventory; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.junit.jupiter.api.Assertions; +import xyz.alexcrea.cuanvil.enchant.CAEnchantment; +import xyz.alexcrea.cuanvil.listener.PrepareAnvilListener; +import xyz.alexcrea.cuanvil.mock.AnvilViewMock; +import xyz.alexcrea.cuanvil.mock.EnchantedItemStackMock; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +public class AnvilFuseTestUtil { + + public static ItemStack prepareItem(@NotNull Material material, + @NotNull List enchantments, + @NotNull List level){ + Assertions.assertEquals(enchantments.size(), level.size()); + + HashMap enchantmentMap = new HashMap<>(); + for (int i = 0; i < enchantments.size(); i++) { + enchantmentMap.put(enchantments.get(i), level.get(i)); + } + + ItemStack item = new EnchantedItemStackMock(material); + ItemUtil.INSTANCE.setEnchantmentsUnsafe(item, enchantmentMap); + + return item; + } + + + public static ItemStack prepareItem(@NotNull Material material, + @NotNull List enchantmentNames, + Integer... levels){ + List enchantments = new ArrayList<>(); + + for (String enchantmentName : enchantmentNames) { + List enchantmentList = CAEnchantment.getListByName(enchantmentName); + Assertions.assertNotEquals(0, enchantmentList.size(), + "Could not find enchantment \"" + enchantmentName + "\""); + + enchantments.addAll(enchantmentList); + } + + return prepareItem(material, enchantments, List.of(levels)); + } + + + /* + * Need to use that as it seems setting item in the inventory will not trigger the anvil click even + * + * Not the best for non-custom anvil plugins but work in the context of CA + */ + public static void imitateAnvilUpdate( + @NotNull HumanEntity player, + @NotNull AnvilInventory anvil) { + + AnvilViewMock view = new AnvilViewMock(player, anvil); + try { + PrepareAnvilEvent event = new PrepareAnvilEvent(view, anvil.getItem(2)); + + // Not ideal but possible and the easiest so why not + new PrepareAnvilListener().anvilCombineCheck(event); + anvil.setResult(event.getResult()); + } catch (Exception e){ + e.printStackTrace(); + Assertions.fail(); + } + } + + public static void executeAnvilTest( + @NotNull AnvilInventory anvil, + @NotNull HumanEntity player, + @NotNull AnvilFuseTestData data + ){ + Assertions.assertEquals(player.getOpenInventory().getTopInventory(), anvil, + "Openned inventory is not anvil"); + + // Test with only the left item + testPlacingItem(anvil, player, + 0, data.expectedPriceAfterLeftPlaced(), + data.leftItem(), data.expectedAfterLeftPlaced()); + + // Test with only the right item + anvil.setItem(0, null); // We only want the right item. so we remove the left one + testPlacingItem(anvil, player, + 1, data.expectedPriceAfterRightPlaced(), + data.rightItem(), data.expectedAfterRightPlaced()); + + // Test with both placed + testPlacingItem(anvil, player, + 0, data.expectedPriceAfterBothPlaced(), + data.leftItem(), data.expectedResult()); + + // Sadly, can't currently test player click + + } + + @SuppressWarnings({"removal"}) + private static void testPlacingItem( + @NotNull AnvilInventory anvil, + @NotNull HumanEntity player, + int slot, + Integer expectedPrice, + @Nullable ItemStack toPlace, + @Nullable ItemStack expectedResult){ + anvil.setItem(slot, toPlace); + AnvilFuseTestUtil.imitateAnvilUpdate(player, anvil); + + ItemStack result = anvil.getItem(2); + assertEqual(result, expectedResult); + assertPriceEqual(expectedPrice, anvil.getRepairCost()); + } + + public static void assertEqual(@Nullable ItemStack item1, @Nullable ItemStack item2) { + if(isAir(item1)) Assertions.assertTrue(isAir(item2)); + else Assertions.assertEquals(item1, item2); + + } + + public static boolean isAir(@Nullable ItemStack item){ + return item == null || item.isEmpty(); + } + + public static void assertPriceEqual(Integer expectedPrice, int price){ + if(expectedPrice == null) return; + Assertions.assertEquals(price, expectedPrice); + } + +} diff --git a/src/test/java/xyz/alexcrea/cuanvil/util/CommonItemUtil.java b/src/test/java/xyz/alexcrea/cuanvil/util/CommonItemUtil.java new file mode 100644 index 0000000..c1a3ef7 --- /dev/null +++ b/src/test/java/xyz/alexcrea/cuanvil/util/CommonItemUtil.java @@ -0,0 +1,21 @@ +package xyz.alexcrea.cuanvil.util; + +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; + +import java.util.List; + +public class CommonItemUtil { + + public static ItemStack sharpness(int level){ + return AnvilFuseTestUtil.prepareItem( + Material.DIAMOND_SWORD, + List.of("sharpness"), + level + ); + } + + + + +}