From 0de243ed2760b544ffb316e7797283511a884e70 Mon Sep 17 00:00:00 2001 From: Ruben Taelman Date: Sat, 12 Sep 2026 09:09:14 +0000 Subject: [PATCH] Report the crafting state when the furnace crafting game tests time out testItemsCraftIngotsAndExtractFromStorage failed sporadically with one raw iron left in the input chest, and the message only said which item was left, which is not enough to tell a slow craft from a stuck one. Both furnace tests now also report the crafting jobs of their interface with their status, ingredient buffer and running operations, the interface's result buffer, and the contents of the furnace. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01CYLrSSsEJgQocz2Z4BSFfU --- .../GameTestHelpersIntegratedCrafting.java | 57 +++++++++++++++++++ .../gametest/GameTestsItemsCraft.java | 6 +- 2 files changed, 61 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/cyclops/integratedcrafting/gametest/GameTestHelpersIntegratedCrafting.java b/src/main/java/org/cyclops/integratedcrafting/gametest/GameTestHelpersIntegratedCrafting.java index 30963b134..ce77c8bb6 100644 --- a/src/main/java/org/cyclops/integratedcrafting/gametest/GameTestHelpersIntegratedCrafting.java +++ b/src/main/java/org/cyclops/integratedcrafting/gametest/GameTestHelpersIntegratedCrafting.java @@ -8,6 +8,7 @@ import net.minecraft.gametest.framework.GameTestAssertException; import net.minecraft.gametest.framework.GameTestHelper; import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.Container; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; import net.minecraft.world.item.crafting.*; @@ -15,6 +16,7 @@ import net.minecraft.world.level.block.AbstractFurnaceBlock; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.ChestBlockEntity; import net.minecraft.world.level.block.entity.FurnaceBlockEntity; import org.apache.commons.lang3.tuple.Pair; @@ -27,9 +29,12 @@ import org.cyclops.commoncapabilities.api.capability.recipehandler.PrototypedIngredientAlternativesList; import org.cyclops.commoncapabilities.api.capability.recipehandler.RecipeDefinition; import org.cyclops.commoncapabilities.api.ingredient.IngredientComponent; +import org.cyclops.commoncapabilities.api.ingredient.IngredientInstanceWrapper; import org.cyclops.commoncapabilities.api.ingredient.MixedIngredients; import org.cyclops.commoncapabilities.api.ingredient.PrototypedIngredient; import org.cyclops.cyclopscore.helper.IModHelpers; +import org.cyclops.integratedcrafting.api.crafting.CraftingJob; +import org.cyclops.integratedcrafting.core.CraftingJobHandler; import org.cyclops.integratedcrafting.core.part.PartTypeInterfaceCraftingBase; import org.cyclops.integratedcrafting.part.PartTypeInterfaceCrafting; import org.cyclops.integratedcrafting.part.PartTypes; @@ -286,6 +291,58 @@ public static , V extends IValue> void setCraftingInterf partStateHolder.getState().setUpdateInterval(updateInterval); } + /** + * Describe what the given crafting interface and the machine it targets are doing. + * + * A test that waits for crafting to finish can only report that it did not finish when it times out, + * which does not tell whether crafting was slow, or stuck on a job that can not make progress anymore. + * + * @param helper The game test helper. + * @param interfaceState The state of the crafting interface. + * @param machinePos The relative position of the machine that the crafting interface targets. + * @return A description of the item crafting state. + */ + public static String describeCraftingState(GameTestHelper helper, + PartTypeInterfaceCraftingBase.State interfaceState, + BlockPos machinePos) { + CraftingJobHandler craftingJobHandler = interfaceState.getCraftingJobHandler(); + StringBuilder description = new StringBuilder("jobs=["); + for (CraftingJob craftingJob : craftingJobHandler.getAllCraftingJobs().values()) { + List operations = craftingJobHandler.getProcessingCraftingJobsPendingIngredients().get(craftingJob.getId()); + description.append('#').append(craftingJob.getId()) + .append(operations == null ? " pending" : " processing") + .append(craftingJob.isInvalidInputs() ? " invalid-inputs" : "") + .append(craftingJob.getLastMissingIngredients().isEmpty() ? "" : " missing-ingredients") + .append(" amount=").append(craftingJob.getAmount()) + .append(" operations=").append(operations == null ? 0 : operations.size()) + .append(" inputs=").append(craftingJob.getIngredientsStorageBuffer().getInstances(IngredientComponent.ITEMSTACK)) + .append(' '); + } + return description + .append("] results=").append(interfaceState.getInventoryOutputBuffer() + .stream().map(IngredientInstanceWrapper::getInstance).toList()) + .append(" machine=").append(describeContainer(helper.getBlockEntity(machinePos))) + .toString(); + } + + /** + * @param blockEntity A block entity. + * @return A description of the items that the given block entity holds. + */ + public static String describeContainer(BlockEntity blockEntity) { + if (!(blockEntity instanceof Container container)) { + return "no container"; + } + StringBuilder description = new StringBuilder("["); + for (int slot = 0; slot < container.getContainerSize(); slot++) { + ItemStack itemStack = container.getItem(slot); + if (!itemStack.isEmpty()) { + description.append(slot).append('=').append(itemStack).append(' '); + } + } + return description.append(']').toString(); + } + public static void chestContains(GameTestHelper helper, ChestBlockEntity chest, ItemStack itemStack) { int remainingCount = itemStack.getCount(); for (int i = 0; i < chest.getContainerSize(); i++) { diff --git a/src/main/java/org/cyclops/integratedcrafting/gametest/GameTestsItemsCraft.java b/src/main/java/org/cyclops/integratedcrafting/gametest/GameTestsItemsCraft.java index e7261320e..d0125e1d7 100644 --- a/src/main/java/org/cyclops/integratedcrafting/gametest/GameTestsItemsCraft.java +++ b/src/main/java/org/cyclops/integratedcrafting/gametest/GameTestsItemsCraft.java @@ -565,7 +565,8 @@ public void testItemsCraftIngotsAndExtractFromStorage(GameTestHelper helper) { helper.assertTrue(positions.interfaceStates().get(0).isRecipeSlotValid(0), "Recipe in crafting interface is not valid"); // Check if items have been crafted - helper.assertTrue(chestIn.getItem(0).isEmpty(), "Slot 0 item is incorrect, was " + chestIn.getItem(0)); + helper.assertTrue(chestIn.getItem(0).isEmpty(), "Slot 0 item is incorrect, was " + chestIn.getItem(0) + + ", " + describeCraftingState(helper, positions.interfaceStates().get(0), POS.west())); }); } @@ -597,7 +598,8 @@ public void testItemsCraftIngotsAndExtractFromStorageSameNetwork(GameTestHelper helper.assertTrue(positions.interfaceStates().get(0).isRecipeSlotValid(0), "Recipe in crafting interface is not valid"); // Check if items have been crafted - helper.assertTrue(chestIn.getItem(0).isEmpty(), "Slot 0 item is incorrect"); + helper.assertTrue(chestIn.getItem(0).isEmpty(), "Slot 0 item is incorrect, was " + chestIn.getItem(0) + + ", " + describeCraftingState(helper, positions.interfaceStates().get(0), POS.west())); }); }