diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 345803a49..d8476f963 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -145,7 +145,7 @@ Additionally, tests in the `refinedstorage2-network` module use the `refinedstor To test the entire chain from Minecraft to the API modules, we use integration tests. These tests are located in the test source set of the `refinedstorage2-platform-forge` module. -We write these integration tests as Minecraft gametests using the NeoForge testing framework. +We write these integration tests as Minecraft gametests. ### Test coverage diff --git a/refinedstorage2-platform-common/src/main/java/com/refinedmods/refinedstorage2/platform/common/constructordestructor/ConstructorBlockEntity.java b/refinedstorage2-platform-common/src/main/java/com/refinedmods/refinedstorage2/platform/common/constructordestructor/ConstructorBlockEntity.java index 45cf2e459..4c870f75e 100644 --- a/refinedstorage2-platform-common/src/main/java/com/refinedmods/refinedstorage2/platform/common/constructordestructor/ConstructorBlockEntity.java +++ b/refinedstorage2-platform-common/src/main/java/com/refinedmods/refinedstorage2/platform/common/constructordestructor/ConstructorBlockEntity.java @@ -57,7 +57,7 @@ public ConstructorBlockEntity(final BlockPos pos, final BlockState state) { } @Override - public void setFilters(final List filters) { + protected void setFilters(final List filters) { this.tasks.clear(); this.tasks.addAll(filters.stream().map(TaskImpl::new).toList()); } diff --git a/refinedstorage2-platform-common/src/main/java/com/refinedmods/refinedstorage2/platform/common/exporter/ExporterBlockEntity.java b/refinedstorage2-platform-common/src/main/java/com/refinedmods/refinedstorage2/platform/common/exporter/ExporterBlockEntity.java index 2b911edb6..5ea3d2f31 100644 --- a/refinedstorage2-platform-common/src/main/java/com/refinedmods/refinedstorage2/platform/common/exporter/ExporterBlockEntity.java +++ b/refinedstorage2-platform-common/src/main/java/com/refinedmods/refinedstorage2/platform/common/exporter/ExporterBlockEntity.java @@ -95,7 +95,7 @@ protected void setTaskExecutor(final TaskExecutor filters) { + protected void setFilters(final List filters) { mainNode.setFilters(filters); } diff --git a/refinedstorage2-platform-common/src/main/java/com/refinedmods/refinedstorage2/platform/common/support/network/AbstractSchedulingNetworkNodeContainerBlockEntity.java b/refinedstorage2-platform-common/src/main/java/com/refinedmods/refinedstorage2/platform/common/support/network/AbstractSchedulingNetworkNodeContainerBlockEntity.java index dac1392be..df09b565f 100644 --- a/refinedstorage2-platform-common/src/main/java/com/refinedmods/refinedstorage2/platform/common/support/network/AbstractSchedulingNetworkNodeContainerBlockEntity.java +++ b/refinedstorage2-platform-common/src/main/java/com/refinedmods/refinedstorage2/platform/common/support/network/AbstractSchedulingNetworkNodeContainerBlockEntity.java @@ -82,5 +82,5 @@ public void writeScreenOpeningData(final ServerPlayer player, final FriendlyByte protected abstract void setTaskExecutor(TaskExecutor taskExecutor); - public abstract void setFilters(List filters); + protected abstract void setFilters(List filters); } diff --git a/refinedstorage2-platform-forge/src/test/java/com/refinedmods/refinedstorage2/platform/common/constructordestructor/ConstructorTest.java b/refinedstorage2-platform-forge/src/test/java/com/refinedmods/refinedstorage2/platform/common/constructordestructor/ConstructorTest.java new file mode 100644 index 000000000..994129a19 --- /dev/null +++ b/refinedstorage2-platform-forge/src/test/java/com/refinedmods/refinedstorage2/platform/common/constructordestructor/ConstructorTest.java @@ -0,0 +1,139 @@ +package com.refinedmods.refinedstorage2.platform.common.constructordestructor; + +import com.refinedmods.refinedstorage2.api.resource.ResourceAmount; +import com.refinedmods.refinedstorage2.platform.common.Platform; +import com.refinedmods.refinedstorage2.platform.common.util.IdentifierUtil; + +import java.util.List; + +import net.minecraft.core.Direction; +import net.minecraft.gametest.framework.GameTest; +import net.minecraft.gametest.framework.GameTestHelper; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.material.FluidState; +import net.neoforged.neoforge.gametest.GameTestHolder; +import net.neoforged.neoforge.gametest.PrefixGameTestTemplate; + +import static com.refinedmods.refinedstorage2.platform.common.constructordestructor.ConstructorTestPlots.preparePlot; +import static com.refinedmods.refinedstorage2.platform.forge.GameTestUtil.asResource; +import static com.refinedmods.refinedstorage2.platform.forge.GameTestUtil.assertFluidPresent; +import static com.refinedmods.refinedstorage2.platform.forge.GameTestUtil.insert; +import static com.refinedmods.refinedstorage2.platform.forge.GameTestUtil.networkIsAvailable; +import static com.refinedmods.refinedstorage2.platform.forge.GameTestUtil.storageContainsExactly; +import static net.minecraft.world.item.Items.DIRT; +import static net.minecraft.world.item.Items.FIREWORK_ROCKET; +import static net.minecraft.world.item.Items.STONE; +import static net.minecraft.world.level.material.Fluids.WATER; + +@GameTestHolder(IdentifierUtil.MOD_ID) +@PrefixGameTestTemplate(false) +public final class ConstructorTest { + private ConstructorTest() { + } + + @GameTest(template = "empty_15x15") + public static void shouldPlaceBlock(final GameTestHelper helper) { + preparePlot(helper, Direction.EAST, (constructor, pos, sequence) -> { + // Arrange + sequence.thenWaitUntil(networkIsAvailable(helper, pos, network -> { + insert(helper, network, DIRT, 10); + insert(helper, network, STONE, 15); + })); + + // Act + constructor.setFilters(List.of(asResource(DIRT))); + + // Assert + sequence + .thenWaitUntil(() -> helper.assertBlockPresent(Blocks.DIRT, pos.east())) + .thenWaitUntil(storageContainsExactly( + helper, + pos, + new ResourceAmount(asResource(DIRT), 9), + new ResourceAmount(asResource(STONE), 15) + )) + .thenSucceed(); + }); + } + + @GameTest(template = "empty_15x15") + public static void shouldPlaceWater(final GameTestHelper helper) { + preparePlot(helper, Direction.EAST, (constructor, pos, sequence) -> { + // Arrange + sequence.thenWaitUntil(networkIsAvailable(helper, pos, network -> { + insert(helper, network, DIRT, 10); + insert(helper, network, STONE, 15); + insert(helper, network, WATER, Platform.INSTANCE.getBucketAmount() * 2); + })); + + // Act + constructor.setFilters(List.of(asResource(WATER))); + + // Assert + sequence + .thenWaitUntil(() -> assertFluidPresent(helper, pos.east(), WATER, FluidState.AMOUNT_FULL)) + .thenWaitUntil(storageContainsExactly( + helper, + pos, + new ResourceAmount(asResource(DIRT), 10), + new ResourceAmount(asResource(STONE), 15), + new ResourceAmount(asResource(WATER), Platform.INSTANCE.getBucketAmount()) + )) + .thenSucceed(); + }); + } + + @GameTest(template = "empty_15x15") + public static void shouldDropItem(final GameTestHelper helper) { + preparePlot(helper, Direction.EAST, (constructor, pos, sequence) -> { + // Arrange + sequence.thenWaitUntil(networkIsAvailable(helper, pos, network -> { + insert(helper, network, DIRT, 10); + insert(helper, network, STONE, 15); + })); + + // Act + constructor.setDropItems(true); + constructor.setFilters(List.of(asResource(DIRT))); + + // Assert + sequence + .thenWaitUntil(() -> helper.assertBlockNotPresent(Blocks.DIRT, pos.east())) + .thenWaitUntil(() -> helper.assertItemEntityPresent(DIRT, pos.east(), 1)) + .thenWaitUntil(storageContainsExactly( + helper, + pos, + new ResourceAmount(asResource(DIRT), 9), + new ResourceAmount(asResource(STONE), 15) + )) + .thenSucceed(); + }); + } + + @GameTest(template = "empty_15x15") + public static void shouldPlaceFireworks(final GameTestHelper helper) { + preparePlot(helper, Direction.EAST, (constructor, pos, sequence) -> { + // Arrange + sequence.thenWaitUntil(networkIsAvailable(helper, pos, network -> { + insert(helper, network, DIRT, 10); + insert(helper, network, FIREWORK_ROCKET, 15); + })); + + // Act + constructor.setFilters(List.of(asResource(FIREWORK_ROCKET))); + + // Assert + sequence + .thenWaitUntil(() -> helper.assertBlockPresent(Blocks.AIR, pos.east())) + .thenWaitUntil(() -> helper.assertEntityPresent(EntityType.FIREWORK_ROCKET, pos.east())) + .thenWaitUntil(storageContainsExactly( + helper, + pos, + new ResourceAmount(asResource(DIRT), 10), + new ResourceAmount(asResource(FIREWORK_ROCKET), 14) + )) + .thenSucceed(); + }); + } +} diff --git a/refinedstorage2-platform-forge/src/test/java/com/refinedmods/refinedstorage2/platform/common/constructordestructor/ConstructorTestPlots.java b/refinedstorage2-platform-forge/src/test/java/com/refinedmods/refinedstorage2/platform/common/constructordestructor/ConstructorTestPlots.java new file mode 100644 index 000000000..35ed4ba4f --- /dev/null +++ b/refinedstorage2-platform-forge/src/test/java/com/refinedmods/refinedstorage2/platform/common/constructordestructor/ConstructorTestPlots.java @@ -0,0 +1,37 @@ +package com.refinedmods.refinedstorage2.platform.common.constructordestructor; + +import com.refinedmods.refinedstorage2.platform.common.storage.FluidStorageType; +import com.refinedmods.refinedstorage2.platform.common.storage.ItemStorageType; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.gametest.framework.GameTestHelper; +import net.minecraft.gametest.framework.GameTestSequence; +import org.apache.commons.lang3.function.TriConsumer; + +import static com.refinedmods.refinedstorage2.platform.forge.GameTestUtil.RSBLOCKS; +import static com.refinedmods.refinedstorage2.platform.forge.GameTestUtil.requireBlockEntity; +import static net.minecraft.core.BlockPos.ZERO; + +final class ConstructorTestPlots { + private ConstructorTestPlots() { + } + + static void preparePlot(final GameTestHelper helper, + final Direction direction, + final TriConsumer consumer) { + helper.setBlock(ZERO.above(), RSBLOCKS.getCreativeController().getDefault()); + helper.setBlock(ZERO.above().above(), RSBLOCKS.getItemStorageBlock(ItemStorageType.Variant.ONE_K)); + helper.setBlock( + ZERO.above().above().north(), + RSBLOCKS.getFluidStorageBlock(FluidStorageType.Variant.SIXTY_FOUR_B) + ); + final BlockPos constructorPos = ZERO.above().above().above(); + helper.setBlock(constructorPos, RSBLOCKS.getConstructor().getDefault().rotated(direction)); + consumer.accept( + requireBlockEntity(helper, constructorPos, ConstructorBlockEntity.class), + constructorPos, + helper.startSequence() + ); + } +} diff --git a/refinedstorage2-platform-forge/src/test/java/com/refinedmods/refinedstorage2/platform/forge/ConstructorTest.java b/refinedstorage2-platform-forge/src/test/java/com/refinedmods/refinedstorage2/platform/forge/ConstructorTest.java deleted file mode 100644 index 88523fe7d..000000000 --- a/refinedstorage2-platform-forge/src/test/java/com/refinedmods/refinedstorage2/platform/forge/ConstructorTest.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.refinedmods.refinedstorage2.platform.forge; - -import com.refinedmods.refinedstorage2.api.resource.ResourceAmount; -import com.refinedmods.refinedstorage2.platform.common.constructordestructor.ConstructorBlockEntity; -import com.refinedmods.refinedstorage2.platform.common.storage.ItemStorageType; -import com.refinedmods.refinedstorage2.platform.common.util.IdentifierUtil; - -import java.util.List; - -import net.minecraft.core.Direction; -import net.minecraft.gametest.framework.GameTest; -import net.minecraft.gametest.framework.GameTestHelper; -import net.minecraft.world.level.block.Blocks; -import net.neoforged.neoforge.gametest.GameTestHolder; -import net.neoforged.neoforge.gametest.PrefixGameTestTemplate; - -import static com.refinedmods.refinedstorage2.platform.forge.GameTestUtil.DIRT; -import static com.refinedmods.refinedstorage2.platform.forge.GameTestUtil.RSBLOCKS; -import static com.refinedmods.refinedstorage2.platform.forge.GameTestUtil.itemIsInserted; -import static com.refinedmods.refinedstorage2.platform.forge.GameTestUtil.requireBlockEntity; -import static com.refinedmods.refinedstorage2.platform.forge.GameTestUtil.storageMustContainExactly; -import static net.minecraft.core.BlockPos.ZERO; - -@GameTestHolder(IdentifierUtil.MOD_ID) -@PrefixGameTestTemplate(false) -public final class ConstructorTest { - private ConstructorTest() { - } - - @GameTest(template = "empty_15x15") - public static void shouldPlaceBlock(final GameTestHelper helper) { - // Arrange - helper.setBlock(ZERO.above(), RSBLOCKS.getCreativeController().getDefault()); - helper.setBlock(ZERO.above().above(), RSBLOCKS.getConstructor().getDefault().rotated(Direction.EAST)); - helper.setBlock( - ZERO.above().above().above(), - RSBLOCKS.getItemStorageBlock(ItemStorageType.Variant.ONE_K) - ); - - final var seq = helper.startSequence(); - seq.thenWaitUntil(itemIsInserted(helper, ZERO.above().above(), DIRT, 10)); - - // Act - requireBlockEntity(helper, ZERO.above().above(), ConstructorBlockEntity.class).setFilters(List.of(DIRT)); - - // Assert - seq.thenWaitUntil(() -> helper.assertBlockPresent(Blocks.DIRT, ZERO.above().above().east())) - .thenWaitUntil(storageMustContainExactly(helper, ZERO.above().above(), new ResourceAmount(DIRT, 9))) - .thenSucceed(); - } -} diff --git a/refinedstorage2-platform-forge/src/test/java/com/refinedmods/refinedstorage2/platform/forge/GameTestUtil.java b/refinedstorage2-platform-forge/src/test/java/com/refinedmods/refinedstorage2/platform/forge/GameTestUtil.java index 26ffd3042..5485bc5f1 100644 --- a/refinedstorage2-platform-forge/src/test/java/com/refinedmods/refinedstorage2/platform/forge/GameTestUtil.java +++ b/refinedstorage2-platform-forge/src/test/java/com/refinedmods/refinedstorage2/platform/forge/GameTestUtil.java @@ -5,30 +5,33 @@ import com.refinedmods.refinedstorage2.api.network.node.NetworkNode; import com.refinedmods.refinedstorage2.api.network.storage.StorageNetworkComponent; import com.refinedmods.refinedstorage2.api.resource.ResourceAmount; +import com.refinedmods.refinedstorage2.api.resource.ResourceKey; import com.refinedmods.refinedstorage2.api.storage.EmptyActor; import com.refinedmods.refinedstorage2.platform.api.support.network.AbstractNetworkNodeContainerBlockEntity; import com.refinedmods.refinedstorage2.platform.common.content.Blocks; +import com.refinedmods.refinedstorage2.platform.common.support.resource.FluidResource; import com.refinedmods.refinedstorage2.platform.common.support.resource.ItemResource; import java.util.Arrays; +import java.util.function.Consumer; import javax.annotation.Nullable; import net.minecraft.core.BlockPos; import net.minecraft.gametest.framework.GameTestAssertException; import net.minecraft.gametest.framework.GameTestHelper; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.Items; +import net.minecraft.world.item.Item; import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.material.Fluid; +import net.minecraft.world.level.material.FluidState; public final class GameTestUtil { - public static final ItemResource DIRT = ItemResource.ofItemStack(new ItemStack(Items.DIRT)); public static final Blocks RSBLOCKS = Blocks.INSTANCE; private GameTestUtil() { } @Nullable - public static Network getNetwork(final net.minecraft.gametest.framework.GameTestHelper helper, final BlockPos pos) { + private static Network getNetwork(final GameTestHelper helper, final BlockPos pos) { try { final var be = requireBlockEntity(helper, pos, AbstractNetworkNodeContainerBlockEntity.class); final var field = AbstractNetworkNodeContainerBlockEntity.class.getDeclaredField("mainNode"); @@ -40,19 +43,39 @@ public static Network getNetwork(final net.minecraft.gametest.framework.GameTest } } - public static Runnable itemIsInserted(final net.minecraft.gametest.framework.GameTestHelper helper, - final BlockPos networkPos, - final ItemResource resource, - final long amount) { + public static Runnable networkIsAvailable(final GameTestHelper helper, + final BlockPos networkPos, + final Consumer networkConsumer) { return () -> { final Network network = getNetwork(helper, networkPos); - helper.assertTrue(network != null && network.getComponent(StorageNetworkComponent.class) - .insert(resource, amount, Action.EXECUTE, EmptyActor.INSTANCE) == amount, - "Item couldn't be inserted" - ); + helper.assertTrue(network != null, "Network is not available"); + networkConsumer.accept(network); }; } + public static void insert(final GameTestHelper helper, + final Network network, + final Item resource, + final long amount) { + insert(helper, network, new ItemResource(resource, null), amount); + } + + public static void insert(final GameTestHelper helper, + final Network network, + final Fluid resource, + final long amount) { + insert(helper, network, new FluidResource(resource, null), amount); + } + + public static void insert(final GameTestHelper helper, + final Network network, + final ResourceKey resource, + final long amount) { + final StorageNetworkComponent storage = network.getComponent(StorageNetworkComponent.class); + final long inserted = storage.insert(resource, amount, Action.EXECUTE, EmptyActor.INSTANCE); + helper.assertTrue(inserted == amount, "Resource couldn't be inserted"); + } + @SuppressWarnings("unchecked") public static T requireBlockEntity( final GameTestHelper helper, @@ -71,34 +94,44 @@ public static T requireBlockEntity( return (T) blockEntity; } - public static Runnable storageMustContainExactly(final net.minecraft.gametest.framework.GameTestHelper helper, - final BlockPos networkPos, - final ResourceAmount... expected) { - return () -> { - final Network network = getNetwork(helper, networkPos); - helper.assertTrue(network != null, "Network is not found"); - if (network == null) { - return; - } + public static void assertFluidPresent(final GameTestHelper helper, + final BlockPos pos, + final Fluid fluid, + final int level) { + final FluidState fluidState = helper.getLevel().getFluidState(helper.absolutePos(pos)); + helper.assertTrue( + fluidState.getType() == fluid && fluidState.getAmount() == level, + "Unexpected " + fluidState.getType() + ", " + fluidState.getAmount() + ); + } + + public static Runnable storageContainsExactly(final GameTestHelper helper, + final BlockPos networkPos, + final ResourceAmount... expected) { + return networkIsAvailable(helper, networkPos, network -> { final StorageNetworkComponent storage = network.getComponent(StorageNetworkComponent.class); - for (final ResourceAmount expectedItem : expected) { + for (final ResourceAmount expectedResource : expected) { final boolean contains = storage.getAll() .stream() - .anyMatch(inStorage -> inStorage.getResource().equals(expectedItem.getResource()) - && inStorage.getAmount() == expectedItem.getAmount()); - if (!contains) { - throw new GameTestAssertException("Missing from storage: " + expectedItem); - } + .anyMatch(inStorage -> inStorage.getResource().equals(expectedResource.getResource()) + && inStorage.getAmount() == expectedResource.getAmount()); + helper.assertTrue(contains, "Expected resource is missing from storage: " + expectedResource); } for (final ResourceAmount inStorage : storage.getAll()) { final boolean wasExpected = Arrays.stream(expected).anyMatch( - expectedItem -> expectedItem.getResource().equals(inStorage.getResource()) - && expectedItem.getAmount() == inStorage.getAmount() + expectedResource -> expectedResource.getResource().equals(inStorage.getResource()) + && expectedResource.getAmount() == inStorage.getAmount() ); - if (!wasExpected) { - throw new GameTestAssertException("Unexpected in storage: " + inStorage); - } + helper.assertTrue(wasExpected, "Unexpected resource found in storage: " + inStorage); } - }; + }); + } + + public static ItemResource asResource(final Item item) { + return new ItemResource(item, null); + } + + public static FluidResource asResource(final Fluid fluid) { + return new FluidResource(fluid, null); } }