mirror of
https://github.com/FabricMC/fabric.git
synced 2025-07-29 15:39:55 -04:00
25w03a game tests (#4385)
* 25w03a game tests * Cleanup and improvements * Update fabric-gametest-api-v1/src/main/java/net/fabricmc/fabric/impl/gametest/TestAnnotationLocator.java Co-authored-by: Joseph Burton <burtonjae@hotmail.co.uk> * Use an 8x8 empty structure by default * Use a dedicated RegistryLoaderMixin instead of hacking around registry sync's api * Fix * Checkstyle --------- Co-authored-by: Joseph Burton <burtonjae@hotmail.co.uk>
This commit is contained in:
parent
bcdf965b28
commit
73a52b4b18
45 changed files with 849 additions and 802 deletions
build.gradle
fabric-api-base/src/testmod/java/net/fabricmc/fabric/test/base
fabric-command-api-v2/src/testmod/java/net/fabricmc/fabric/test/command
fabric-content-registries-v0/src/testmod/java/net/fabricmc/fabric/test/content/registry
fabric-data-attachment-api-v1/src/testmod/java/net/fabricmc/fabric/test/attachment/gametest
fabric-events-interaction-v0/src/testmod/java/net/fabricmc/fabric/test/event/interaction
fabric-gametest-api-v1
build.gradle
src
main
java/net/fabricmc/fabric
api/gametest/v1
impl/gametest
FabricGameTestHelper.javaFabricGameTestModInitializer.javaFabricGameTestRunner.javaGameTestSystemProperties.javaTestAnnotationLocator.java
mixin/gametest
resources
testmod
java/net/fabricmc/fabric/test/gametest
resources
fabric-item-api-v1/src/testmod/java/net/fabricmc/fabric/test/item/gametest
BrewingStandGameTest.javaCustomEnchantmentEffectsGameTest.javaDefaultItemComponentGameTest.javaFurnaceGameTest.javaRecipeGameTest.java
fabric-object-builder-api-v1/src/testmod/java/net/fabricmc/fabric/test/object/builder
fabric-recipe-api-v1/src/testmod
java/net/fabricmc/fabric/test/recipe/ingredient
resources
fabric-resource-conditions-api-v1/src/testmod/java/net/fabricmc/fabric/test/resource/conditions
fabric-resource-loader-v0/src/main/java/net/fabricmc/fabric/impl/resource/loader
fabric-transfer-api-v1/src/testmod/java/net/fabricmc/fabric/test/transfer/gametests
gradle.propertiessettings.gradle
12
build.gradle
12
build.gradle
|
@ -631,12 +631,12 @@ subprojects {
|
|||
testmodImplementation sourceSets.main.output
|
||||
|
||||
// Make all modules depend on the gametest api (and thus res loader) to try and promote its usage.
|
||||
// if (project.name != "fabric-gametest-api-v1") {
|
||||
// testmodImplementation project(path: ':fabric-gametest-api-v1', configuration: 'namedElements')
|
||||
// testmodClientImplementation project(":fabric-gametest-api-v1").sourceSets.client.output
|
||||
// testmodImplementation project(path: ':fabric-resource-loader-v0', configuration: 'namedElements')
|
||||
// testmodClientImplementation project(":fabric-resource-loader-v0").sourceSets.client.output
|
||||
// }
|
||||
if (project.name != "fabric-gametest-api-v1") {
|
||||
testmodImplementation project(path: ':fabric-gametest-api-v1', configuration: 'namedElements')
|
||||
testmodClientImplementation project(":fabric-gametest-api-v1").sourceSets.client.output
|
||||
testmodImplementation project(path: ':fabric-resource-loader-v0', configuration: 'namedElements')
|
||||
testmodClientImplementation project(":fabric-resource-loader-v0").sourceSets.client.output
|
||||
}
|
||||
|
||||
// Make all testmods run with registry-sync-v0 as it is required to register new objects.
|
||||
if (project.name != "fabric-registry-sync-v0") {
|
||||
|
|
|
@ -20,8 +20,10 @@ import org.spongepowered.asm.mixin.MixinEnvironment;
|
|||
|
||||
import net.minecraft.test.TestContext;
|
||||
|
||||
import net.fabricmc.fabric.api.gametest.v1.GameTest;
|
||||
|
||||
public class FabricApiBaseGameTest {
|
||||
//@GameTest(templateName = FabricGameTest.EMPTY_STRUCTURE) TODO 1.21.5 tests
|
||||
@GameTest
|
||||
public void auditMixins(TestContext context) {
|
||||
MixinEnvironment.getCurrentEnvironment().audit();
|
||||
|
||||
|
|
|
@ -24,6 +24,8 @@ import net.minecraft.server.MinecraftServer;
|
|||
import net.minecraft.test.TestContext;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
|
||||
import net.fabricmc.fabric.api.gametest.v1.GameTest;
|
||||
|
||||
public class EntitySelectorGameTest {
|
||||
private void spawn(TestContext context, float health) {
|
||||
MobEntity entity = context.spawnMob(EntityType.CREEPER, BlockPos.ORIGIN);
|
||||
|
@ -31,7 +33,7 @@ public class EntitySelectorGameTest {
|
|||
entity.setHealth(health);
|
||||
}
|
||||
|
||||
// @GameTest(templateName = FabricGameTest.EMPTY_STRUCTURE) TODO 1.21.5 tests
|
||||
@GameTest
|
||||
public void testEntitySelector(TestContext context) {
|
||||
BlockPos absolute = context.getAbsolutePos(BlockPos.ORIGIN);
|
||||
|
||||
|
|
|
@ -16,9 +16,33 @@
|
|||
|
||||
package net.fabricmc.fabric.test.content.registry;
|
||||
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.Blocks;
|
||||
import net.minecraft.block.ComposterBlock;
|
||||
import net.minecraft.block.HopperBlock;
|
||||
import net.minecraft.block.entity.AbstractFurnaceBlockEntity;
|
||||
import net.minecraft.block.entity.BrewingStandBlockEntity;
|
||||
import net.minecraft.block.entity.HopperBlockEntity;
|
||||
import net.minecraft.component.DataComponentTypes;
|
||||
import net.minecraft.component.type.PotionContentsComponent;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.item.Items;
|
||||
import net.minecraft.potion.Potions;
|
||||
import net.minecraft.test.TestContext;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.util.Hand;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import net.minecraft.world.GameMode;
|
||||
|
||||
import net.fabricmc.fabric.api.gametest.v1.GameTest;
|
||||
|
||||
public class ContentRegistryGameTest {
|
||||
/* TODO 1.21.5 tests
|
||||
@GameTest(templateName = FabricGameTest.EMPTY_STRUCTURE)
|
||||
@GameTest
|
||||
public void testCompostingChanceRegistry(TestContext context) {
|
||||
BlockPos pos = new BlockPos(0, 1, 0);
|
||||
context.setBlockState(pos, Blocks.COMPOSTER);
|
||||
|
@ -28,11 +52,11 @@ public class ContentRegistryGameTest {
|
|||
// If on level 0, composting always increases composter level
|
||||
context.useBlock(pos, player);
|
||||
context.expectBlockProperty(pos, ComposterBlock.LEVEL, 1);
|
||||
context.assertEquals(obsidian.getCount(), 63, "obsidian stack count");
|
||||
context.assertEquals(obsidian.getCount(), 63, Text.literal("obsidian stack count"));
|
||||
context.complete();
|
||||
}
|
||||
|
||||
@GameTest(templateName = FabricGameTest.EMPTY_STRUCTURE)
|
||||
@GameTest
|
||||
public void testFlattenableBlockRegistry(TestContext context) {
|
||||
BlockPos pos = new BlockPos(0, 1, 0);
|
||||
context.setBlockState(pos, Blocks.RED_WOOL);
|
||||
|
@ -41,7 +65,7 @@ public class ContentRegistryGameTest {
|
|||
player.setStackInHand(Hand.MAIN_HAND, shovel);
|
||||
context.useBlock(pos, player);
|
||||
context.expectBlock(Blocks.YELLOW_WOOL, pos);
|
||||
context.assertEquals(shovel.getDamage(), 1, "shovel damage");
|
||||
context.assertEquals(shovel.getDamage(), 1, Text.literal("shovel damage"));
|
||||
context.complete();
|
||||
}
|
||||
|
||||
|
@ -52,10 +76,7 @@ public class ContentRegistryGameTest {
|
|||
BlockState furnaceState = Blocks.BLAST_FURNACE.getDefaultState();
|
||||
|
||||
context.setBlockState(furnacePos, furnaceState);
|
||||
|
||||
if (!(context.getBlockEntity(furnacePos) instanceof AbstractFurnaceBlockEntity furnace)) {
|
||||
throw new AssertionError("Furnace was not placed");
|
||||
}
|
||||
AbstractFurnaceBlockEntity furnace = context.getBlockEntity(furnacePos, AbstractFurnaceBlockEntity.class);
|
||||
|
||||
// Create a hopper that attempts to insert fuel into the furnace
|
||||
BlockPos hopperPos = furnacePos.east();
|
||||
|
@ -63,10 +84,7 @@ public class ContentRegistryGameTest {
|
|||
.with(HopperBlock.FACING, context.getRotation().rotate(Direction.WEST));
|
||||
|
||||
context.setBlockState(hopperPos, hopperState);
|
||||
|
||||
if (!(context.getBlockEntity(hopperPos) instanceof HopperBlockEntity hopper)) {
|
||||
throw new AssertionError("Hopper was not placed");
|
||||
}
|
||||
HopperBlockEntity hopper = context.getBlockEntity(hopperPos, HopperBlockEntity.class);
|
||||
|
||||
// Insert the fuel into the hopper, which transfers it into the furnace
|
||||
hopper.setStack(0, fuelStack.copy());
|
||||
|
@ -80,11 +98,11 @@ public class ContentRegistryGameTest {
|
|||
|
||||
private void smeltCompleted(TestContext context, ItemStack fuelStack) {
|
||||
smelt(context, fuelStack, (furnace, hopper) -> {
|
||||
context.assertTrue(hopper.isEmpty(), "fuel hopper should have been emptied");
|
||||
context.assertTrue(hopper.isEmpty(), Text.literal("fuel hopper should have been emptied"));
|
||||
|
||||
context.assertTrue(furnace.getStack(0).isEmpty(), "furnace input slot should have been emptied");
|
||||
context.assertTrue(furnace.getStack(0).isEmpty(), "furnace fuel slot should have been emptied");
|
||||
context.assertTrue(ItemStack.areEqual(furnace.getStack(2), new ItemStack(Items.IRON_INGOT, 1)), "one iron ingot should have been smelted and placed into the furnace output slot");
|
||||
context.assertTrue(furnace.getStack(0).isEmpty(), Text.literal("furnace input slot should have been emptied"));
|
||||
context.assertTrue(furnace.getStack(0).isEmpty(), Text.literal("furnace fuel slot should have been emptied"));
|
||||
context.assertTrue(ItemStack.areEqual(furnace.getStack(2), new ItemStack(Items.IRON_INGOT, 1)), Text.literal("one iron ingot should have been smelted and placed into the furnace output slot"));
|
||||
|
||||
context.complete();
|
||||
});
|
||||
|
@ -92,41 +110,41 @@ public class ContentRegistryGameTest {
|
|||
|
||||
private void smeltFailed(TestContext context, ItemStack fuelStack) {
|
||||
smelt(context, fuelStack, (furnace, hopper) -> {
|
||||
context.assertTrue(ItemStack.areEqual(hopper.getStack(0), fuelStack), "fuel hopper should not have been emptied");
|
||||
context.assertTrue(ItemStack.areEqual(hopper.getStack(0), fuelStack), Text.literal("fuel hopper should not have been emptied"));
|
||||
|
||||
context.assertTrue(ItemStack.areEqual(furnace.getStack(0), new ItemStack(Items.RAW_IRON, 1)), "furnace input slot should not have been emptied");
|
||||
context.assertTrue(furnace.getStack(1).isEmpty(), "furnace fuel slot should not have been filled");
|
||||
context.assertTrue(furnace.getStack(2).isEmpty(), "furnace output slot should not have been filled");
|
||||
context.assertTrue(ItemStack.areEqual(furnace.getStack(0), new ItemStack(Items.RAW_IRON, 1)), Text.literal("furnace input slot should not have been emptied"));
|
||||
context.assertTrue(furnace.getStack(1).isEmpty(), Text.literal("furnace fuel slot should not have been filled"));
|
||||
context.assertTrue(furnace.getStack(2).isEmpty(), Text.literal("furnace output slot should not have been filled"));
|
||||
|
||||
context.complete();
|
||||
});
|
||||
}
|
||||
|
||||
@GameTest(templateName = FabricGameTest.EMPTY_STRUCTURE, tickLimit = 110)
|
||||
@GameTest(maxTicks = 110)
|
||||
public void testSmeltingFuelIncludedByItem(TestContext context) {
|
||||
// Item with 50 fuel time x4 = 200 fuel time
|
||||
smeltCompleted(context, new ItemStack(ContentRegistryTest.SMELTING_FUEL_INCLUDED_BY_ITEM, 4));
|
||||
}
|
||||
|
||||
@GameTest(templateName = FabricGameTest.EMPTY_STRUCTURE, tickLimit = 110)
|
||||
@GameTest(maxTicks = 110)
|
||||
public void testSmeltingFuelIncludedByTag(TestContext context) {
|
||||
// Item in tag with 100 fuel time x2 = 200 fuel time
|
||||
smeltCompleted(context, new ItemStack(ContentRegistryTest.SMELTING_FUEL_INCLUDED_BY_TAG, 2));
|
||||
}
|
||||
|
||||
@GameTest(templateName = FabricGameTest.EMPTY_STRUCTURE, tickLimit = 110)
|
||||
@GameTest(maxTicks = 110)
|
||||
public void testSmeltingFuelExcludedByTag(TestContext context) {
|
||||
// Item is in both the smelting fuels tag and the excluded smithing fuels tag
|
||||
smeltFailed(context, new ItemStack(ContentRegistryTest.SMELTING_FUEL_EXCLUDED_BY_TAG));
|
||||
}
|
||||
|
||||
@GameTest(templateName = FabricGameTest.EMPTY_STRUCTURE, tickLimit = 110)
|
||||
@GameTest(maxTicks = 110)
|
||||
public void testSmeltingFuelExcludedByVanillaTag(TestContext context) {
|
||||
// Item is in both the smelting fuel tag and vanilla's excluded non-flammable wood tag
|
||||
smeltFailed(context, new ItemStack(ContentRegistryTest.SMELTING_FUEL_EXCLUDED_BY_VANILLA_TAG));
|
||||
}
|
||||
|
||||
@GameTest(templateName = FabricGameTest.EMPTY_STRUCTURE)
|
||||
@GameTest
|
||||
public void testStrippableBlockRegistry(TestContext context) {
|
||||
BlockPos pos = new BlockPos(0, 1, 0);
|
||||
context.setBlockState(pos, Blocks.QUARTZ_PILLAR);
|
||||
|
@ -135,11 +153,11 @@ public class ContentRegistryGameTest {
|
|||
player.setStackInHand(Hand.MAIN_HAND, axe);
|
||||
context.useBlock(pos, player);
|
||||
context.expectBlock(Blocks.HAY_BLOCK, pos);
|
||||
context.assertEquals(axe.getDamage(), 1, "axe damage");
|
||||
context.assertEquals(axe.getDamage(), 1, Text.literal("axe damage"));
|
||||
context.complete();
|
||||
}
|
||||
|
||||
@GameTest(templateName = FabricGameTest.EMPTY_STRUCTURE)
|
||||
@GameTest
|
||||
public void testTillableBlockRegistry(TestContext context) {
|
||||
BlockPos pos = new BlockPos(0, 1, 0);
|
||||
context.setBlockState(pos, Blocks.GREEN_WOOL);
|
||||
|
@ -148,11 +166,11 @@ public class ContentRegistryGameTest {
|
|||
player.setStackInHand(Hand.MAIN_HAND, hoe);
|
||||
context.useBlock(pos, player);
|
||||
context.expectBlock(Blocks.LIME_WOOL, pos);
|
||||
context.assertEquals(hoe.getDamage(), 1, "hoe damage");
|
||||
context.assertEquals(hoe.getDamage(), 1, Text.literal("hoe damage"));
|
||||
context.complete();
|
||||
}
|
||||
|
||||
@GameTest(templateName = FabricGameTest.EMPTY_STRUCTURE)
|
||||
@GameTest
|
||||
public void testOxidizableBlocksRegistry(TestContext context) {
|
||||
// Test de-oxidation. (the registry does not make the blocks oxidize.)
|
||||
PlayerEntity player = context.createMockPlayer(GameMode.SURVIVAL);
|
||||
|
@ -162,7 +180,7 @@ public class ContentRegistryGameTest {
|
|||
player.setStackInHand(Hand.MAIN_HAND, axe);
|
||||
context.useBlock(pos, player);
|
||||
context.expectBlock(Blocks.GOLD_ORE, pos);
|
||||
context.assertEquals(axe.getDamage(), 1, "axe damage");
|
||||
context.assertEquals(axe.getDamage(), 1, Text.literal("axe damage"));
|
||||
context.useBlock(pos, player);
|
||||
context.expectBlock(Blocks.IRON_ORE, pos);
|
||||
context.useBlock(pos, player);
|
||||
|
@ -170,7 +188,7 @@ public class ContentRegistryGameTest {
|
|||
context.complete();
|
||||
}
|
||||
|
||||
@GameTest(templateName = FabricGameTest.EMPTY_STRUCTURE)
|
||||
@GameTest
|
||||
public void testWaxableBlocksRegistry(TestContext context) {
|
||||
PlayerEntity player = context.createMockPlayer(GameMode.SURVIVAL);
|
||||
BlockPos pos = new BlockPos(0, 1, 0);
|
||||
|
@ -179,22 +197,19 @@ public class ContentRegistryGameTest {
|
|||
player.setStackInHand(Hand.MAIN_HAND, honeycomb);
|
||||
context.useBlock(pos, player);
|
||||
context.expectBlock(Blocks.DEEPSLATE_DIAMOND_ORE, pos);
|
||||
context.assertEquals(honeycomb.getCount(), 63, "honeycomb count");
|
||||
context.assertEquals(honeycomb.getCount(), 63, Text.literal("honeycomb count"));
|
||||
ItemStack axe = new ItemStack(Items.NETHERITE_AXE);
|
||||
player.setStackInHand(Hand.MAIN_HAND, axe);
|
||||
context.useBlock(pos, player);
|
||||
context.expectBlock(Blocks.DIAMOND_ORE, pos);
|
||||
context.assertEquals(axe.getDamage(), 1, "axe damage");
|
||||
context.assertEquals(axe.getDamage(), 1, Text.literal("axe damage"));
|
||||
context.complete();
|
||||
}
|
||||
|
||||
private void brew(TestContext context, ItemStack input, ItemStack bottle, Consumer<BrewingStandBlockEntity> callback) {
|
||||
BlockPos pos = new BlockPos(0, 1, 0);
|
||||
context.setBlockState(pos, Blocks.BREWING_STAND);
|
||||
|
||||
if (!(context.getBlockEntity(pos) instanceof BrewingStandBlockEntity brewingStand)) {
|
||||
throw new AssertionError("Brewing stand was not placed");
|
||||
}
|
||||
BrewingStandBlockEntity brewingStand = context.getBlockEntity(pos, BrewingStandBlockEntity.class);
|
||||
|
||||
brewingStand.setStack(0, bottle);
|
||||
brewingStand.setStack(3, input);
|
||||
|
@ -202,24 +217,22 @@ public class ContentRegistryGameTest {
|
|||
context.waitAndRun(401, () -> callback.accept(brewingStand));
|
||||
}
|
||||
|
||||
@GameTest(templateName = FabricGameTest.EMPTY_STRUCTURE, tickLimit = 410)
|
||||
@GameTest(maxTicks = 410)
|
||||
public void testBrewingFlower(TestContext context) {
|
||||
brew(context, new ItemStack(Items.DANDELION), PotionContentsComponent.createStack(Items.POTION, Potions.AWKWARD), brewingStand -> {
|
||||
ItemStack bottle = brewingStand.getStack(0);
|
||||
PotionContentsComponent potion = bottle.getOrDefault(DataComponentTypes.POTION_CONTENTS, PotionContentsComponent.DEFAULT);
|
||||
context.assertEquals(potion.potion().orElseThrow(), Potions.HEALING, "brewed potion");
|
||||
context.assertEquals(potion.potion().orElseThrow(), Potions.HEALING, Text.literal("brewed potion"));
|
||||
context.complete();
|
||||
});
|
||||
}
|
||||
|
||||
@GameTest(templateName = FabricGameTest.EMPTY_STRUCTURE, tickLimit = 410)
|
||||
@GameTest(maxTicks = 410)
|
||||
public void testBrewingDirt(TestContext context) {
|
||||
brew(context, new ItemStack(Items.DIRT), PotionContentsComponent.createStack(Items.POTION, Potions.AWKWARD), brewingStand -> {
|
||||
ItemStack bottle = brewingStand.getStack(0);
|
||||
context.assertTrue(bottle.getItem() instanceof ContentRegistryTest.DirtyPotionItem, "potion became dirty");
|
||||
context.assertTrue(bottle.getItem() instanceof ContentRegistryTest.DirtyPotionItem, Text.literal("potion became dirty"));
|
||||
context.complete();
|
||||
});
|
||||
}
|
||||
|
||||
*/
|
||||
}
|
||||
|
|
|
@ -20,16 +20,17 @@ import net.minecraft.block.Blocks;
|
|||
import net.minecraft.test.TestContext;
|
||||
import net.minecraft.text.Text;
|
||||
|
||||
import net.fabricmc.fabric.api.gametest.v1.GameTest;
|
||||
import net.fabricmc.fabric.api.registry.FlammableBlockRegistry;
|
||||
|
||||
public class FlammableTest {
|
||||
/**
|
||||
* Regression test for <a href="https://github.com/FabricMC/fabric/issues/2108">FlammableBlockRegistry ignoring tags on first load</a>.
|
||||
*/
|
||||
// @GameTest(templateName = FabricGameTest.EMPTY_STRUCTURE) TODO 1.21.5 tests
|
||||
@GameTest
|
||||
public void testFlammableTag(TestContext context) {
|
||||
if (FlammableBlockRegistry.getDefaultInstance().get(Blocks.SAND).getBurnChance() != 4) {
|
||||
context.method_66943(Text.literal("Expected blocks in the sand tag to be flammable!"));
|
||||
throw context.createError(Text.literal("Expected blocks in the sand tag to be flammable!"));
|
||||
}
|
||||
|
||||
context.complete();
|
||||
|
|
|
@ -16,13 +16,28 @@
|
|||
|
||||
package net.fabricmc.fabric.test.attachment.gametest;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.function.IntSupplier;
|
||||
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityType;
|
||||
import net.minecraft.entity.SpawnReason;
|
||||
import net.minecraft.entity.mob.DrownedEntity;
|
||||
import net.minecraft.entity.mob.ZombieEntity;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.world.ServerWorld;
|
||||
import net.minecraft.test.TestContext;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.TeleportTarget;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import net.fabricmc.fabric.api.attachment.v1.AttachmentRegistry;
|
||||
import net.fabricmc.fabric.api.attachment.v1.AttachmentType;
|
||||
import net.fabricmc.fabric.api.gametest.v1.GameTest;
|
||||
import net.fabricmc.fabric.test.attachment.AttachmentTestMod;
|
||||
import net.fabricmc.fabric.test.attachment.mixin.ZombieEntityAccessor;
|
||||
|
||||
public class AttachmentCopyTests {
|
||||
// using a lambda type because serialization shouldn't play a role in this
|
||||
|
@ -34,8 +49,7 @@ public class AttachmentCopyTests {
|
|||
AttachmentRegistry.Builder::copyOnDeath
|
||||
);
|
||||
|
||||
/* TODO 1.21.5 tests
|
||||
@GameTest(templateName = FabricGameTest.EMPTY_STRUCTURE)
|
||||
@GameTest
|
||||
public void testCrossWorldTeleport(TestContext context) {
|
||||
MinecraftServer server = context.getWorld().getServer();
|
||||
ServerWorld overworld = server.getOverworld();
|
||||
|
@ -48,20 +62,20 @@ public class AttachmentCopyTests {
|
|||
entity.setAttached(COPY_ON_DEATH, () -> 10);
|
||||
|
||||
Entity moved = entity.teleportTo(new TeleportTarget(end, entity, TeleportTarget.NO_OP));
|
||||
if (moved == null) throw new GameTestException("Cross-world teleportation failed");
|
||||
if (moved == null) throw context.createError("Cross-world teleportation failed");
|
||||
|
||||
IntSupplier attached1 = moved.getAttached(DUMMY);
|
||||
IntSupplier attached2 = moved.getAttached(COPY_ON_DEATH);
|
||||
|
||||
if (attached1 == null || attached1.getAsInt() != 10 || attached2 == null || attached2.getAsInt() != 10) {
|
||||
throw new GameTestException("Attachment copying failed during cross-world teleportation");
|
||||
throw context.createError("Attachment copying failed during cross-world teleportation");
|
||||
}
|
||||
|
||||
moved.discard();
|
||||
context.complete();
|
||||
}
|
||||
|
||||
@GameTest(templateName = FabricGameTest.EMPTY_STRUCTURE)
|
||||
@GameTest
|
||||
public void testMobConversion(TestContext context) {
|
||||
ZombieEntity mob = context.spawnEntity(EntityType.ZOMBIE, BlockPos.ORIGIN);
|
||||
mob.setAttached(DUMMY, () -> 42);
|
||||
|
@ -72,25 +86,23 @@ public class AttachmentCopyTests {
|
|||
List<DrownedEntity> drowned = context.getEntities(EntityType.DROWNED);
|
||||
|
||||
if (drowned.size() != 1) {
|
||||
throw new GameTestException("Conversion failed");
|
||||
throw context.createError("Conversion failed");
|
||||
}
|
||||
|
||||
DrownedEntity converted = drowned.getFirst();
|
||||
if (converted == null) throw new GameTestException("Conversion failed");
|
||||
if (converted == null) throw context.createError("Conversion failed");
|
||||
|
||||
if (converted.hasAttached(DUMMY)) {
|
||||
throw new GameTestException("Attachment shouldn't have been copied on mob conversion");
|
||||
throw context.createError("Attachment shouldn't have been copied on mob conversion");
|
||||
}
|
||||
|
||||
IntSupplier attached = converted.getAttached(COPY_ON_DEATH);
|
||||
|
||||
if (attached == null || attached.getAsInt() != 42) {
|
||||
throw new GameTestException("Attachment copying failed during mob conversion");
|
||||
throw context.createError("Attachment copying failed during mob conversion");
|
||||
}
|
||||
|
||||
converted.discard();
|
||||
context.complete();
|
||||
}
|
||||
|
||||
*/
|
||||
}
|
||||
|
|
|
@ -19,11 +19,27 @@ package net.fabricmc.fabric.test.attachment.gametest;
|
|||
import com.mojang.logging.LogUtils;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.entity.BlockEntity;
|
||||
import net.minecraft.block.entity.BlockEntityType;
|
||||
import net.minecraft.nbt.NbtCompound;
|
||||
import net.minecraft.network.listener.ClientPlayPacketListener;
|
||||
import net.minecraft.network.packet.Packet;
|
||||
import net.minecraft.network.packet.s2c.play.BlockEntityUpdateS2CPacket;
|
||||
import net.minecraft.registry.Registries;
|
||||
import net.minecraft.registry.entry.RegistryEntry;
|
||||
import net.minecraft.test.TestContext;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
|
||||
import net.fabricmc.fabric.api.attachment.v1.AttachmentTarget;
|
||||
import net.fabricmc.fabric.api.gametest.v1.GameTest;
|
||||
import net.fabricmc.fabric.test.attachment.AttachmentTestMod;
|
||||
import net.fabricmc.fabric.test.attachment.mixin.BlockEntityTypeAccessor;
|
||||
|
||||
public class BlockEntityTests {
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
|
||||
/* TODO 1.21.5 tests
|
||||
@GameTest(templateName = FabricGameTest.EMPTY_STRUCTURE)
|
||||
@GameTest
|
||||
public void testBlockEntitySync(TestContext context) {
|
||||
BlockPos pos = BlockPos.ORIGIN.up();
|
||||
|
||||
|
@ -60,12 +76,10 @@ public class BlockEntityTests {
|
|||
|
||||
if (nbt != null && nbt.contains(AttachmentTarget.NBT_ATTACHMENT_KEY)) {
|
||||
// Note: this is a vanilla bug (it called createNbt, instead of the correct createComponentlessNbt)
|
||||
throw new GameTestException("Packet NBT for " + entry + " had persistent data: " + nbt.asString());
|
||||
throw context.createError("Packet NBT for " + entry + " had persistent data: " + nbt.asString());
|
||||
}
|
||||
}
|
||||
|
||||
context.complete();
|
||||
}
|
||||
|
||||
*/
|
||||
}
|
||||
|
|
|
@ -16,12 +16,29 @@
|
|||
|
||||
package net.fabricmc.fabric.test.event.interaction;
|
||||
|
||||
import net.minecraft.block.Blocks;
|
||||
import net.minecraft.entity.EntityType;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.item.ItemUsageContext;
|
||||
import net.minecraft.item.Items;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.test.TestContext;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.util.Hand;
|
||||
import net.minecraft.util.hit.BlockHitResult;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
|
||||
import net.fabricmc.fabric.api.entity.FakePlayer;
|
||||
import net.fabricmc.fabric.api.gametest.v1.GameTest;
|
||||
|
||||
public class FakePlayerTests {
|
||||
/**
|
||||
* Try placing a sign with a fake player.
|
||||
*/
|
||||
/* TODO 1.21.5 tests
|
||||
@GameTest(templateName = FabricGameTest.EMPTY_STRUCTURE)
|
||||
@GameTest
|
||||
public void testFakePlayerPlaceSign(TestContext context) {
|
||||
// This is for Fabric internal testing only, if you copy this to your mod you're on your own...
|
||||
|
||||
|
@ -41,18 +58,15 @@ public class FakePlayerTests {
|
|||
BlockHitResult hitResult = new BlockHitResult(hitPos, Direction.UP, context.getAbsolutePos(basePos), false);
|
||||
signStack.useOnBlock(new ItemUsageContext(fakePlayer, Hand.MAIN_HAND, hitResult));
|
||||
|
||||
context.checkBlockState(signPos, x -> x.isOf(Blocks.OAK_SIGN), () -> "Sign was not placed");
|
||||
context.assertTrue(signStack.isEmpty(), "Sign stack was not emptied");
|
||||
context.checkBlockState(signPos, x -> x.isOf(Blocks.OAK_SIGN), (b) -> Text.literal("Sign was not placed"));
|
||||
context.assertTrue(signStack.isEmpty(), Text.literal("Sign stack was not emptied"));
|
||||
context.complete();
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
/**
|
||||
* Try breaking a beehive with a fake player (see {@code BeehiveBlockMixin}).
|
||||
*/
|
||||
/* TODO 1.21.5 tests
|
||||
@GameTest(templateName = FabricGameTest.EMPTY_STRUCTURE)
|
||||
@GameTest
|
||||
public void testFakePlayerBreakBeehive(TestContext context) {
|
||||
BlockPos basePos = new BlockPos(0, 1, 0);
|
||||
context.setBlockState(basePos, Blocks.BEEHIVE);
|
||||
|
@ -63,10 +77,8 @@ public class FakePlayerTests {
|
|||
BlockPos fakePlayerPos = context.getAbsolutePos(basePos.add(2, 0, 2));
|
||||
fakePlayer.setPosition(fakePlayerPos.getX(), fakePlayerPos.getY(), fakePlayerPos.getZ());
|
||||
|
||||
context.assertTrue(fakePlayer.interactionManager.tryBreakBlock(context.getAbsolutePos(basePos)), "Block was not broken");
|
||||
context.assertTrue(fakePlayer.interactionManager.tryBreakBlock(context.getAbsolutePos(basePos)), Text.literal("Block was not broken"));
|
||||
context.expectBlock(Blocks.AIR, basePos);
|
||||
context.complete();
|
||||
}
|
||||
|
||||
*/
|
||||
}
|
||||
|
|
|
@ -17,5 +17,5 @@ loom {
|
|||
|
||||
moduleDependencies(project, [
|
||||
'fabric-api-base',
|
||||
'fabric-resource-loader-v0'
|
||||
'fabric-resource-loader-v0',
|
||||
])
|
||||
|
|
|
@ -1,46 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.api.gametest.v1;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import net.minecraft.test.TestContext;
|
||||
|
||||
import net.fabricmc.fabric.impl.gametest.FabricGameTestHelper;
|
||||
|
||||
/**
|
||||
* This interface can be optionally implemented on your test class.
|
||||
*/
|
||||
public interface FabricGameTest {
|
||||
/**
|
||||
* Use in {@link net.minecraft.test.GameTest} structureName to use an empty 8x8 structure for the test.
|
||||
*/
|
||||
String EMPTY_STRUCTURE = "fabric-gametest-api-v1:empty";
|
||||
|
||||
/**
|
||||
* Override this method to implement custom logic to invoke the test method.
|
||||
* This can be used to run code before or after each test.
|
||||
* You can also pass in custom objects into the test method if desired.
|
||||
* The structure will have been placed in the world before this method is invoked.
|
||||
*
|
||||
* @param context The vanilla test context
|
||||
* @param method The test method to invoke
|
||||
*/
|
||||
default void invokeTestMethod(TestContext context, Method method) {
|
||||
FabricGameTestHelper.invokeTestMethod(context, method, this);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.api.gametest.v1;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import net.minecraft.util.BlockRotation;
|
||||
|
||||
/**
|
||||
* {@link GameTest} is an annotation that can be used to mark a method as a game test.
|
||||
*
|
||||
* <p>{@link GameTest} methods must be {@code public} not {@code static}, return {@code void } and take exactly one argument of type {@link net.minecraft.test.TestContext}.
|
||||
*
|
||||
* <p>The values in this class directly correspond to the values in {@link net.minecraft.test.TestData}.
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
@Documented
|
||||
public @interface GameTest {
|
||||
/**
|
||||
* A namespaced ID of an entry within the {@link net.minecraft.registry.RegistryKeys#TEST_ENVIRONMENT} registry.
|
||||
*/
|
||||
String environment() default "minecraft:default";
|
||||
|
||||
/**
|
||||
* A namespaced ID pointing to a structure resource in the {@code modid/gametest/structure/} directory.
|
||||
*
|
||||
* <p>Defaults to an 8x8 structure with no blocks.
|
||||
*/
|
||||
String structure() default "fabric-gametest-api-v1:empty";
|
||||
|
||||
/**
|
||||
* The maximum number of ticks the test is allowed to run for.
|
||||
*/
|
||||
int maxTicks() default 20;
|
||||
|
||||
/**
|
||||
* The number of ticks to wait before starting the test after placing the structure.
|
||||
*/
|
||||
int setupTicks() default 0;
|
||||
|
||||
/**
|
||||
* Whether the test is required to pass for the test suite to pass.
|
||||
*/
|
||||
boolean required() default true;
|
||||
|
||||
/**
|
||||
* The rotation of the structure when placed.
|
||||
*/
|
||||
BlockRotation rotation() default BlockRotation.NONE;
|
||||
|
||||
/**
|
||||
* When set the test must be ran manually.
|
||||
*/
|
||||
boolean manualOnly() default false;
|
||||
|
||||
/**
|
||||
* The number of times the test should be re attempted if it fails.
|
||||
*/
|
||||
int maxAttempts() default 1;
|
||||
|
||||
/**
|
||||
* The number of times the test should be successfully ran before it is considered a success.
|
||||
*/
|
||||
int requiredSuccesses() default 1;
|
||||
|
||||
/**
|
||||
* Whether the test should have sky access. When {@code false} the test will be enclosed by barrier blocks.
|
||||
*/
|
||||
boolean skyAccess() default false;
|
||||
}
|
|
@ -33,8 +33,8 @@
|
|||
* <p>Each "test method" represents a set of code that sets up the testing site and checks the
|
||||
* behavior of the code - for example, it could check that using a flint and steel on a creeper
|
||||
* causes explosion, or that hoppers can insert items into barrels. A test method is always annotated
|
||||
* with {@link net.minecraft.test.GameTest}. For most cases you can set the {@link
|
||||
* net.minecraft.test.GameTest#templateName()} as {@link net.fabricmc.fabric.api.gametest.v1.FabricGameTest#EMPTY_STRUCTURE}.
|
||||
* with {@link net.fabricmc.fabric.api.gametest.v1.GameTest}. By default the test will run with
|
||||
* an empty structure, you can specify a structure using {@link net.fabricmc.fabric.api.gametest.v1.GameTest#structure()}
|
||||
* For complex tests, you can also save a structure as an SNBT file under {@code modid/gametest/structure/}
|
||||
* in the test mod's data pack and reference that structure. It will then be loaded before the test.
|
||||
*
|
||||
|
@ -46,7 +46,7 @@
|
|||
* <p>Example of a test method:
|
||||
* <pre>{@code
|
||||
* public class MyTest {
|
||||
* @GameTest(templateName=FabricGameTest.EMPTY_STRUCTURE)
|
||||
* @FabricGameTest
|
||||
* public void testSomething(TestContext context) {
|
||||
* context.assertTrue(MyMod.getSomeValue(context.getWorld()) > 0, "SomeValue should be positive.");
|
||||
* context.complete(); // do not forget!
|
||||
|
@ -78,7 +78,6 @@
|
|||
* }
|
||||
* }</pre>
|
||||
*
|
||||
* @see net.minecraft.test.GameTest
|
||||
* @see net.fabricmc.fabric.api.gametest.v1.FabricGameTest
|
||||
* @see net.fabricmc.fabric.api.gametest.v1.GameTest
|
||||
*/
|
||||
package net.fabricmc.fabric.api.gametest.v1;
|
||||
|
|
|
@ -1,132 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.impl.gametest;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collection;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import net.minecraft.resource.ResourceFinder;
|
||||
import net.minecraft.resource.ResourcePackManager;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.command.TestCommand;
|
||||
import net.minecraft.test.TestContext;
|
||||
import net.minecraft.test.TestFailureLogger;
|
||||
import net.minecraft.test.TestFunction;
|
||||
import net.minecraft.test.TestFunctions;
|
||||
import net.minecraft.test.TestServer;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.level.storage.LevelStorage;
|
||||
|
||||
import net.fabricmc.api.EnvType;
|
||||
import net.fabricmc.fabric.api.gametest.v1.FabricGameTest;
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
|
||||
public final class FabricGameTestHelper {
|
||||
public static final boolean ENABLED = System.getProperty("fabric-api.gametest") != null;
|
||||
|
||||
/**
|
||||
* When enabled the {@link TestCommand} and related arguments will be registered.
|
||||
*
|
||||
* <p>When {@link EnvType#CLIENT} the default value is true.
|
||||
*
|
||||
* <p>When {@link EnvType#SERVER} the default value is false.
|
||||
*/
|
||||
public static final boolean COMMAND_ENABLED = Boolean.parseBoolean(System.getProperty("fabric-api.gametest.command", FabricLoader.getInstance().getEnvironmentType() == EnvType.CLIENT ? "true" : "false"));
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(FabricGameTestHelper.class);
|
||||
|
||||
private static final String GAMETEST_STRUCTURE_PATH = "gametest/structure";
|
||||
|
||||
public static final ResourceFinder GAMETEST_STRUCTURE_FINDER = new ResourceFinder(GAMETEST_STRUCTURE_PATH, ".snbt");
|
||||
|
||||
private FabricGameTestHelper() {
|
||||
}
|
||||
|
||||
public static void runHeadlessServer(LevelStorage.Session session, ResourcePackManager resourcePackManager) {
|
||||
String reportPath = System.getProperty("fabric-api.gametest.report-file");
|
||||
|
||||
if (reportPath != null) {
|
||||
try {
|
||||
TestFailureLogger.setCompletionListener(new SavingXmlReportingTestCompletionListener(new File(reportPath)));
|
||||
} catch (ParserConfigurationException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
LOGGER.info("Starting test server");
|
||||
MinecraftServer server = TestServer.startServer(thread -> {
|
||||
return TestServer.create(thread, session, resourcePackManager, getTestFunctions(), BlockPos.ORIGIN);
|
||||
});
|
||||
}
|
||||
|
||||
public static Consumer<TestContext> getTestMethodInvoker(Method method) {
|
||||
return testContext -> {
|
||||
Class<?> testClass = method.getDeclaringClass();
|
||||
|
||||
Constructor<?> constructor;
|
||||
|
||||
try {
|
||||
constructor = testClass.getConstructor();
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new RuntimeException("Test class (%s) provided by (%s) must have a public default or no args constructor".formatted(testClass.getSimpleName(), FabricGameTestModInitializer.getModIdForTestClass(testClass)));
|
||||
}
|
||||
|
||||
Object testObject;
|
||||
|
||||
try {
|
||||
testObject = constructor.newInstance();
|
||||
} catch (InvocationTargetException | InstantiationException | IllegalAccessException e) {
|
||||
throw new RuntimeException("Failed to create instance of test class (%s)".formatted(testClass.getCanonicalName()), e);
|
||||
}
|
||||
|
||||
if (testObject instanceof FabricGameTest fabricGameTest) {
|
||||
fabricGameTest.invokeTestMethod(testContext, method);
|
||||
} else {
|
||||
invokeTestMethod(testContext, method, testObject);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static void invokeTestMethod(TestContext testContext, Method method, Object testObject) {
|
||||
try {
|
||||
method.invoke(testObject, testContext);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException("Failed to invoke test method (%s) in (%s) because %s".formatted(method.getName(), method.getDeclaringClass().getCanonicalName(), e.getMessage()), e);
|
||||
} catch (InvocationTargetException e) {
|
||||
LOGGER.error("Exception occurred when invoking test method {} in ({})", method.getName(), method.getDeclaringClass().getCanonicalName(), e);
|
||||
|
||||
if (e.getCause() instanceof RuntimeException runtimeException) {
|
||||
throw runtimeException;
|
||||
} else {
|
||||
throw new RuntimeException(e.getCause());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Collection<TestFunction> getTestFunctions() {
|
||||
return TestFunctions.getTestFunctions();
|
||||
}
|
||||
}
|
|
@ -16,49 +16,55 @@
|
|||
|
||||
package net.fabricmc.fabric.impl.gametest;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import net.minecraft.test.TestFunctions;
|
||||
import net.minecraft.registry.Registries;
|
||||
import net.minecraft.registry.Registry;
|
||||
import net.minecraft.registry.RegistryKey;
|
||||
import net.minecraft.registry.RegistryKeys;
|
||||
import net.minecraft.registry.RegistryLoader;
|
||||
import net.minecraft.test.TestEnvironmentDefinition;
|
||||
import net.minecraft.test.TestInstance;
|
||||
|
||||
import net.fabricmc.api.ModInitializer;
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
import net.fabricmc.loader.api.entrypoint.EntrypointContainer;
|
||||
|
||||
public final class FabricGameTestModInitializer implements ModInitializer {
|
||||
private static final String ENTRYPOINT_KEY = "fabric-gametest";
|
||||
private static final Map<Class<?>, String> GAME_TEST_IDS = new HashMap<>();
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(FabricGameTestModInitializer.class);
|
||||
private static TestAnnotationLocator locator = new TestAnnotationLocator(FabricLoader.getInstance());
|
||||
|
||||
@Override
|
||||
public void onInitialize() {
|
||||
List<EntrypointContainer<Object>> entrypointContainers = FabricLoader.getInstance()
|
||||
.getEntrypointContainers(ENTRYPOINT_KEY, Object.class);
|
||||
if (!(FabricGameTestRunner.ENABLED || FabricLoader.getInstance().isDevelopmentEnvironment())) {
|
||||
// Don't try to load the tests if the game test runner is disabled or we are not in a development environment
|
||||
return;
|
||||
}
|
||||
|
||||
for (EntrypointContainer<Object> container : entrypointContainers) {
|
||||
Class<?> testClass = container.getEntrypoint().getClass();
|
||||
String modid = container.getProvider().getMetadata().getId();
|
||||
|
||||
if (GAME_TEST_IDS.containsKey(testClass)) {
|
||||
throw new UnsupportedOperationException("Test class (%s) has already been registered with mod (%s)".formatted(testClass.getCanonicalName(), modid));
|
||||
}
|
||||
|
||||
GAME_TEST_IDS.put(testClass, modid);
|
||||
TestFunctions.register(testClass);
|
||||
|
||||
LOGGER.debug("Registered test class {} for mod {}", testClass.getCanonicalName(), modid);
|
||||
for (TestAnnotationLocator.TestMethod testMethod : locator.getTestMethods()) {
|
||||
LOGGER.debug("Registering test method: {}", testMethod.identifier());
|
||||
Registry.register(Registries.TEST_FUNCTION, testMethod.identifier(), testMethod.testFunction());
|
||||
}
|
||||
}
|
||||
|
||||
public static String getModIdForTestClass(Class<?> testClass) {
|
||||
if (!GAME_TEST_IDS.containsKey(testClass)) {
|
||||
throw new UnsupportedOperationException("The test class (%s) was not registered using the '%s' entrypoint".formatted(testClass.getCanonicalName(), ENTRYPOINT_KEY));
|
||||
public static void registerDynamicEntries(List<RegistryLoader.Loader<?>> registriesList) {
|
||||
Map<RegistryKey<? extends Registry<?>>, Registry<?>> registries = new IdentityHashMap<>(registriesList.size());
|
||||
|
||||
for (RegistryLoader.Loader<?> entry : registriesList) {
|
||||
registries.put(entry.registry().getKey(), entry.registry());
|
||||
}
|
||||
|
||||
return GAME_TEST_IDS.get(testClass);
|
||||
Registry<TestInstance> testInstances = (Registry<TestInstance>) registries.get(RegistryKeys.TEST_INSTANCE);
|
||||
Registry<TestEnvironmentDefinition> testEnvironmentDefinitionRegistry = (Registry<TestEnvironmentDefinition>) Objects.requireNonNull(registries.get(RegistryKeys.TEST_ENVIRONMENT));
|
||||
|
||||
for (TestAnnotationLocator.TestMethod testMethod : locator.getTestMethods()) {
|
||||
TestInstance testInstance = testMethod.testInstance(testEnvironmentDefinitionRegistry);
|
||||
Registry.register(testInstances, testMethod.identifier(), testInstance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.impl.gametest;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Optional;
|
||||
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import net.minecraft.resource.ResourceFinder;
|
||||
import net.minecraft.resource.ResourcePackManager;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.test.TestFailureLogger;
|
||||
import net.minecraft.test.TestServer;
|
||||
import net.minecraft.world.level.storage.LevelStorage;
|
||||
|
||||
public final class FabricGameTestRunner {
|
||||
public static final boolean ENABLED = System.getProperty(GameTestSystemProperties.ENABLED) != null;
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(FabricGameTestRunner.class);
|
||||
private static final String GAMETEST_STRUCTURE_PATH = "gametest/structure";
|
||||
|
||||
public static final ResourceFinder GAMETEST_STRUCTURE_FINDER = new ResourceFinder(GAMETEST_STRUCTURE_PATH, ".snbt");
|
||||
|
||||
private FabricGameTestRunner() {
|
||||
}
|
||||
|
||||
public static void runHeadlessServer(LevelStorage.Session session, ResourcePackManager resourcePackManager) {
|
||||
String reportPath = System.getProperty(GameTestSystemProperties.REPORT_FILE);
|
||||
|
||||
if (reportPath != null) {
|
||||
try {
|
||||
TestFailureLogger.setCompletionListener(new SavingXmlReportingTestCompletionListener(new File(reportPath)));
|
||||
} catch (ParserConfigurationException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
LOGGER.info("Starting test server");
|
||||
|
||||
Optional<String> filter = Optional.ofNullable(System.getProperty(GameTestSystemProperties.FILTER));
|
||||
boolean verify = Boolean.getBoolean(GameTestSystemProperties.VERIFY);
|
||||
MinecraftServer.startServer((thread) -> TestServer.create(thread, session, resourcePackManager, filter, verify));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.impl.gametest;
|
||||
|
||||
public final class GameTestSystemProperties {
|
||||
/**
|
||||
* Enable the game test system.
|
||||
*/
|
||||
public static final String ENABLED = "fabric-api.gametest";
|
||||
|
||||
/**
|
||||
* A JUnit XML report file to write the test results to.
|
||||
*/
|
||||
public static final String REPORT_FILE = "fabric-api.gametest.report-file";
|
||||
|
||||
/**
|
||||
* Filter the tests to run by the test name.
|
||||
*/
|
||||
public static final String FILTER = "fabric-api.gametest.filter";
|
||||
|
||||
/**
|
||||
* Run the enabled tests 100 times for each 90 degree rotation.
|
||||
*/
|
||||
public static final String VERIFY = "fabric-api.gametest.verify";
|
||||
|
||||
private GameTestSystemProperties() {
|
||||
}
|
||||
}
|
|
@ -0,0 +1,157 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.impl.gametest;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import net.minecraft.registry.Registries;
|
||||
import net.minecraft.registry.Registry;
|
||||
import net.minecraft.registry.RegistryKey;
|
||||
import net.minecraft.registry.RegistryKeys;
|
||||
import net.minecraft.registry.entry.RegistryEntry;
|
||||
import net.minecraft.test.FunctionTestInstance;
|
||||
import net.minecraft.test.TestContext;
|
||||
import net.minecraft.test.TestData;
|
||||
import net.minecraft.test.TestEnvironmentDefinition;
|
||||
import net.minecraft.test.TestInstance;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import net.fabricmc.fabric.api.gametest.v1.GameTest;
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
import net.fabricmc.loader.api.entrypoint.EntrypointContainer;
|
||||
|
||||
final class TestAnnotationLocator {
|
||||
private static final String ENTRYPOINT_KEY = "fabric-gametest";
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(TestAnnotationLocator.class);
|
||||
|
||||
private final FabricLoader fabricLoader;
|
||||
|
||||
private List<TestMethod> testMethods = null;
|
||||
|
||||
TestAnnotationLocator(FabricLoader fabricLoader) {
|
||||
this.fabricLoader = fabricLoader;
|
||||
}
|
||||
|
||||
public List<TestMethod> getTestMethods() {
|
||||
if (testMethods != null) {
|
||||
return testMethods;
|
||||
}
|
||||
|
||||
List<EntrypointContainer<Object>> entrypointContainers = fabricLoader
|
||||
.getEntrypointContainers(ENTRYPOINT_KEY, Object.class);
|
||||
|
||||
return testMethods = entrypointContainers.stream()
|
||||
.flatMap(entrypoint -> findMagicMethods(entrypoint).stream())
|
||||
.toList();
|
||||
}
|
||||
|
||||
private List<TestMethod> findMagicMethods(EntrypointContainer<Object> entrypoint) {
|
||||
Class<?> testClass = entrypoint.getEntrypoint().getClass();
|
||||
List<TestMethod> methods = new ArrayList<>();
|
||||
findMagicMethods(entrypoint, testClass, methods);
|
||||
|
||||
if (methods.isEmpty()) {
|
||||
LOGGER.warn("No methods with the FabricGameTest annotation were found in {}", testClass.getName());
|
||||
}
|
||||
|
||||
return methods;
|
||||
}
|
||||
|
||||
// Recursively find all methods with the GameTest annotation
|
||||
private void findMagicMethods(EntrypointContainer<Object> entrypoint, Class<?> testClass, List<TestMethod> methods) {
|
||||
for (Method method : testClass.getDeclaredMethods()) {
|
||||
if (method.isAnnotationPresent(GameTest.class)) {
|
||||
validateMethod(method);
|
||||
methods.add(new TestMethod(method, method.getAnnotation(GameTest.class), entrypoint));
|
||||
}
|
||||
}
|
||||
|
||||
if (testClass.getSuperclass() != null) {
|
||||
findMagicMethods(entrypoint, testClass.getSuperclass(), methods);
|
||||
}
|
||||
}
|
||||
|
||||
private void validateMethod(Method method) {
|
||||
if (method.getParameterCount() != 1 || method.getParameterTypes()[0] != TestContext.class) {
|
||||
throw new UnsupportedOperationException("Method %s must have a single parameter of type TestContext".formatted(method.getName()));
|
||||
}
|
||||
|
||||
if (!Modifier.isPublic(method.getModifiers())) {
|
||||
throw new UnsupportedOperationException("Method %s must be public".formatted(method.getName()));
|
||||
}
|
||||
|
||||
if (Modifier.isStatic(method.getModifiers())) {
|
||||
throw new UnsupportedOperationException("Method %s must not be static".formatted(method.getName()));
|
||||
}
|
||||
|
||||
if (method.getReturnType() != void.class) {
|
||||
throw new UnsupportedOperationException("Method %s must return void".formatted(method.getName()));
|
||||
}
|
||||
}
|
||||
|
||||
public record TestMethod(Method method, GameTest gameTest, EntrypointContainer<Object> entrypoint) {
|
||||
Identifier identifier() {
|
||||
String name = camelToSnake(entrypoint.getEntrypoint().getClass().getSimpleName() + "_" + method.getName());
|
||||
return Identifier.of(entrypoint.getProvider().getMetadata().getId(), name);
|
||||
}
|
||||
|
||||
Consumer<TestContext> testFunction() {
|
||||
return context -> {
|
||||
try {
|
||||
method.invoke(entrypoint.getEntrypoint(), context);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new RuntimeException("Failed to invoke test method", e);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
TestData<RegistryEntry<TestEnvironmentDefinition>> testData(Registry<TestEnvironmentDefinition> testEnvironmentDefinitionRegistry) {
|
||||
RegistryEntry<TestEnvironmentDefinition> testEnvironment = testEnvironmentDefinitionRegistry.getOrThrow(RegistryKey.of(RegistryKeys.TEST_ENVIRONMENT, Identifier.of(gameTest.environment())));
|
||||
|
||||
return new TestData<>(
|
||||
testEnvironment,
|
||||
Identifier.of(gameTest.structure()),
|
||||
gameTest.maxTicks(),
|
||||
gameTest.setupTicks(),
|
||||
gameTest.required(),
|
||||
gameTest.rotation(),
|
||||
gameTest.manualOnly(),
|
||||
gameTest.maxAttempts(),
|
||||
gameTest.requiredSuccesses(),
|
||||
gameTest.skyAccess()
|
||||
);
|
||||
}
|
||||
|
||||
TestInstance testInstance(Registry<TestEnvironmentDefinition> testEnvironmentDefinitionRegistry) {
|
||||
return new FunctionTestInstance(
|
||||
Registries.TEST_FUNCTION.getEntry(identifier()).get(),
|
||||
testData(testEnvironmentDefinitionRegistry)
|
||||
);
|
||||
}
|
||||
|
||||
private static String camelToSnake(String input) {
|
||||
return input.replaceAll("([a-z])([A-Z])", "$1_$2").toLowerCase();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,51 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.mixin.gametest;
|
||||
|
||||
import com.mojang.brigadier.arguments.ArgumentType;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import net.minecraft.SharedConstants;
|
||||
import net.minecraft.command.argument.ArgumentTypes;
|
||||
import net.minecraft.command.argument.TestClassArgumentType;
|
||||
import net.minecraft.command.argument.TestFunctionArgumentType;
|
||||
import net.minecraft.command.argument.serialize.ArgumentSerializer;
|
||||
import net.minecraft.command.argument.serialize.ConstantArgumentSerializer;
|
||||
import net.minecraft.registry.Registry;
|
||||
|
||||
import net.fabricmc.fabric.impl.gametest.FabricGameTestHelper;
|
||||
|
||||
@Mixin(ArgumentTypes.class)
|
||||
public abstract class ArgumentTypesMixin {
|
||||
@Shadow
|
||||
private static <A extends ArgumentType<?>, T extends ArgumentSerializer.ArgumentTypeProperties<A>> ArgumentSerializer<A, T> register(Registry<ArgumentSerializer<?, ?>> registry, String string, Class<? extends A> clazz, ArgumentSerializer<A, T> argumentSerializer) {
|
||||
throw new AssertionError("Nope.");
|
||||
}
|
||||
|
||||
@Inject(method = "register(Lnet/minecraft/registry/Registry;)Lnet/minecraft/command/argument/serialize/ArgumentSerializer;", at = @At("RETURN"))
|
||||
private static void register(Registry<ArgumentSerializer<?, ?>> registry, CallbackInfoReturnable<ArgumentSerializer<?, ?>> ci) {
|
||||
// Registered by vanilla when isDevelopment is enabled.
|
||||
if (FabricGameTestHelper.COMMAND_ENABLED && !SharedConstants.isDevelopment) {
|
||||
register(registry, "test_argument", TestFunctionArgumentType.class, ConstantArgumentSerializer.of(TestFunctionArgumentType::testFunction));
|
||||
register(registry, "test_class", TestClassArgumentType.class, ConstantArgumentSerializer.of(TestClassArgumentType::testClass));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.mixin.gametest;
|
||||
|
||||
import com.mojang.brigadier.CommandDispatcher;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import net.minecraft.SharedConstants;
|
||||
import net.minecraft.command.CommandRegistryAccess;
|
||||
import net.minecraft.server.command.CommandManager;
|
||||
import net.minecraft.server.command.ServerCommandSource;
|
||||
import net.minecraft.server.command.TestCommand;
|
||||
|
||||
import net.fabricmc.fabric.impl.gametest.FabricGameTestHelper;
|
||||
|
||||
@Mixin(CommandManager.class)
|
||||
public abstract class CommandManagerMixin {
|
||||
@Shadow
|
||||
@Final
|
||||
private CommandDispatcher<ServerCommandSource> dispatcher;
|
||||
|
||||
@Inject(method = "<init>", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/command/WorldBorderCommand;register(Lcom/mojang/brigadier/CommandDispatcher;)V", shift = At.Shift.AFTER))
|
||||
private void construct(CommandManager.RegistrationEnvironment environment, CommandRegistryAccess registryAccess, CallbackInfo info) {
|
||||
// Registered by vanilla when isDevelopment is enabled.
|
||||
if (FabricGameTestHelper.COMMAND_ENABLED && !SharedConstants.isDevelopment) {
|
||||
TestCommand.register(this.dispatcher);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.mixin.gametest;
|
||||
|
||||
import java.util.function.BooleanSupplier;
|
||||
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import net.minecraft.SharedConstants;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.ServerTickManager;
|
||||
import net.minecraft.test.TestManager;
|
||||
|
||||
@Mixin(MinecraftServer.class)
|
||||
public abstract class MinecraftServerMixin {
|
||||
@Shadow
|
||||
@Final
|
||||
private ServerTickManager tickManager;
|
||||
|
||||
@Inject(method = "tickWorlds", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/PlayerManager;updatePlayerLatency()V", shift = At.Shift.AFTER))
|
||||
private void tickWorlds(BooleanSupplier shouldKeepTicking, CallbackInfo callbackInfo) {
|
||||
// Called by vanilla when isDevelopment is enabled.
|
||||
if (!SharedConstants.isDevelopment && this.tickManager.shouldTick()) {
|
||||
TestManager.INSTANCE.tick();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.mixin.gametest;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Coerce;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import net.minecraft.registry.DynamicRegistryManager;
|
||||
import net.minecraft.registry.RegistryKeys;
|
||||
import net.minecraft.registry.RegistryLoader;
|
||||
import net.minecraft.registry.RegistryWrapper;
|
||||
import net.minecraft.resource.ResourceManager;
|
||||
|
||||
import net.fabricmc.fabric.impl.gametest.FabricGameTestModInitializer;
|
||||
|
||||
@Mixin(RegistryLoader.class)
|
||||
public class RegistryLoaderMixin {
|
||||
@Unique
|
||||
private static final AtomicBoolean LOADING_DYNAMIC_REGISTRIES = new AtomicBoolean(false);
|
||||
|
||||
@Inject(method = "loadFromResource(Lnet/minecraft/resource/ResourceManager;Ljava/util/List;Ljava/util/List;)Lnet/minecraft/registry/DynamicRegistryManager$Immutable;", at = @At("HEAD"))
|
||||
private static void loadFromResources(ResourceManager resourceManager, List<RegistryWrapper.Impl<?>> registries, List<RegistryLoader.Entry<?>> entries, CallbackInfoReturnable<DynamicRegistryManager.Immutable> cir) {
|
||||
LOADING_DYNAMIC_REGISTRIES.set(entries.stream().anyMatch(entry -> entry.key() == RegistryKeys.TEST_INSTANCE));
|
||||
}
|
||||
|
||||
@Inject(
|
||||
method = "load(Lnet/minecraft/registry/RegistryLoader$RegistryLoadable;Ljava/util/List;Ljava/util/List;)Lnet/minecraft/registry/DynamicRegistryManager$Immutable;",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Ljava/util/List;forEach(Ljava/util/function/Consumer;)V",
|
||||
ordinal = 1
|
||||
)
|
||||
)
|
||||
private static void beforeFreeze(@Coerce Object loadable, List<RegistryWrapper.Impl<?>> wrappers, List<RegistryLoader.Entry<?>> entries, CallbackInfoReturnable<DynamicRegistryManager.Immutable> cir, @Local(ordinal = 2) List<RegistryLoader.Loader<?>> registriesList) {
|
||||
if (LOADING_DYNAMIC_REGISTRIES.getAndSet(false)) {
|
||||
FabricGameTestModInitializer.registerDynamicEntries(registriesList);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -43,7 +43,7 @@ import net.minecraft.structure.StructureTemplateManager;
|
|||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.world.level.storage.LevelStorage;
|
||||
|
||||
import net.fabricmc.fabric.impl.gametest.FabricGameTestHelper;
|
||||
import net.fabricmc.fabric.impl.gametest.FabricGameTestRunner;
|
||||
|
||||
@Mixin(StructureTemplateManager.class)
|
||||
public abstract class StructureTemplateManagerMixin {
|
||||
|
@ -54,7 +54,7 @@ public abstract class StructureTemplateManagerMixin {
|
|||
public abstract StructureTemplate createTemplate(NbtCompound nbt);
|
||||
|
||||
private Optional<StructureTemplate> fabric_loadSnbtFromResource(Identifier id) {
|
||||
Identifier path = FabricGameTestHelper.GAMETEST_STRUCTURE_FINDER.toResourcePath(id);
|
||||
Identifier path = FabricGameTestRunner.GAMETEST_STRUCTURE_FINDER.toResourcePath(id);
|
||||
Optional<Resource> resource = this.resourceManager.getResource(path);
|
||||
|
||||
if (resource.isPresent()) {
|
||||
|
@ -71,7 +71,7 @@ public abstract class StructureTemplateManagerMixin {
|
|||
}
|
||||
|
||||
private Stream<Identifier> fabric_streamTemplatesFromResource() {
|
||||
ResourceFinder finder = FabricGameTestHelper.GAMETEST_STRUCTURE_FINDER;
|
||||
ResourceFinder finder = FabricGameTestRunner.GAMETEST_STRUCTURE_FINDER;
|
||||
return finder.findResources(this.resourceManager).keySet().stream().map(finder::toResourceId);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,45 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.mixin.gametest;
|
||||
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.ModifyArg;
|
||||
|
||||
import net.minecraft.server.command.TestCommand;
|
||||
|
||||
@Mixin(TestCommand.class)
|
||||
public class TestCommandMixin {
|
||||
@Unique
|
||||
private static final String OUTPUT_DIR = System.getProperty("fabric-api.gametest.structures.output-dir");
|
||||
|
||||
@ModifyArg(
|
||||
method = "executeExport(Lnet/minecraft/server/command/ServerCommandSource;Ljava/lang/String;)I",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Ljava/nio/file/Paths;get(Ljava/lang/String;[Ljava/lang/String;)Ljava/nio/file/Path;"
|
||||
)
|
||||
)
|
||||
private static String useCustomOutputDirectory(String first) {
|
||||
if (OUTPUT_DIR != null) {
|
||||
return OUTPUT_DIR;
|
||||
}
|
||||
|
||||
return first;
|
||||
}
|
||||
}
|
|
@ -1,66 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.mixin.gametest;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Locale;
|
||||
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import net.minecraft.test.GameTest;
|
||||
import net.minecraft.test.StructureTestUtil;
|
||||
import net.minecraft.test.TestFunction;
|
||||
import net.minecraft.test.TestFunctions;
|
||||
|
||||
import net.fabricmc.fabric.impl.gametest.FabricGameTestHelper;
|
||||
import net.fabricmc.fabric.impl.gametest.FabricGameTestModInitializer;
|
||||
|
||||
@Mixin(TestFunctions.class)
|
||||
public abstract class TestFunctionsMixin {
|
||||
@Inject(at = @At("HEAD"), method = "getTestFunction(Ljava/lang/reflect/Method;)Lnet/minecraft/test/TestFunction;", cancellable = true)
|
||||
private static void getTestFunction(Method method, CallbackInfoReturnable<TestFunction> cir) {
|
||||
GameTest gameTest = method.getAnnotation(GameTest.class);
|
||||
String testSuiteName = method.getDeclaringClass().getSimpleName().toLowerCase(Locale.ROOT);
|
||||
String testCaseName = testSuiteName + "." + method.getName().toLowerCase(Locale.ROOT);
|
||||
|
||||
String modId = FabricGameTestModInitializer.getModIdForTestClass(method.getDeclaringClass());
|
||||
String structureName = "%s:%s".formatted(modId, testCaseName);
|
||||
|
||||
if (!gameTest.templateName().isEmpty()) {
|
||||
structureName = gameTest.templateName();
|
||||
}
|
||||
|
||||
TestFunction testFunction = new TestFunction(gameTest.batchId(),
|
||||
testCaseName,
|
||||
structureName,
|
||||
StructureTestUtil.getRotation(gameTest.rotation()),
|
||||
gameTest.tickLimit(),
|
||||
gameTest.duration(),
|
||||
gameTest.required(),
|
||||
gameTest.manualOnly(),
|
||||
gameTest.maxAttempts(),
|
||||
gameTest.requiredSuccesses(),
|
||||
gameTest.skyAccess(),
|
||||
FabricGameTestHelper.getTestMethodInvoker(method)
|
||||
);
|
||||
|
||||
cir.setReturnValue(testFunction);
|
||||
}
|
||||
}
|
|
@ -27,20 +27,20 @@ import net.minecraft.resource.ResourcePackManager;
|
|||
import net.minecraft.server.Main;
|
||||
import net.minecraft.world.level.storage.LevelStorage;
|
||||
|
||||
import net.fabricmc.fabric.impl.gametest.FabricGameTestHelper;
|
||||
import net.fabricmc.fabric.impl.gametest.FabricGameTestRunner;
|
||||
|
||||
@Mixin(Main.class)
|
||||
public class MainMixin {
|
||||
@ModifyExpressionValue(method = "main", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/dedicated/EulaReader;isEulaAgreedTo()Z"))
|
||||
private static boolean isEulaAgreedTo(boolean isEulaAgreedTo) {
|
||||
return FabricGameTestHelper.ENABLED || isEulaAgreedTo;
|
||||
return FabricGameTestRunner.ENABLED || isEulaAgreedTo;
|
||||
}
|
||||
|
||||
// Inject after resourcePackManager is stored
|
||||
@Inject(method = "main", cancellable = true, at = @At(value = "INVOKE_ASSIGN", target = "Lnet/minecraft/resource/VanillaDataPackProvider;createManager(Lnet/minecraft/world/level/storage/LevelStorage$Session;)Lnet/minecraft/resource/ResourcePackManager;"))
|
||||
private static void main(String[] args, CallbackInfo info, @Local LevelStorage.Session session, @Local ResourcePackManager resourcePackManager) {
|
||||
if (FabricGameTestHelper.ENABLED) {
|
||||
FabricGameTestHelper.runHeadlessServer(session, resourcePackManager);
|
||||
if (FabricGameTestRunner.ENABLED) {
|
||||
FabricGameTestRunner.runHeadlessServer(session, resourcePackManager);
|
||||
info.cancel(); // Do not progress in starting the normal dedicated server
|
||||
}
|
||||
}
|
||||
|
@ -49,7 +49,7 @@ public class MainMixin {
|
|||
// Otherwise gradlew test will succeed without errors, although no tests have been run.
|
||||
@Inject(method = "main", at = @At(value = "INVOKE", target = "Lorg/slf4j/Logger;error(Lorg/slf4j/Marker;Ljava/lang/String;Ljava/lang/Throwable;)V", shift = At.Shift.AFTER, remap = false), remap = false)
|
||||
private static void exitOnError(CallbackInfo info) {
|
||||
if (FabricGameTestHelper.ENABLED) {
|
||||
if (FabricGameTestRunner.ENABLED) {
|
||||
System.exit(-1);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,3 +2,5 @@ accessWidener v2 named
|
|||
|
||||
accessible class net/minecraft/structure/StructureTemplateManager$Provider
|
||||
accessible method net/minecraft/structure/StructureTemplateManager$Provider <init> (Ljava/util/function/Function;Ljava/util/function/Supplier;)V
|
||||
|
||||
accessible class net/minecraft/registry/RegistryLoader$Loader
|
||||
|
|
|
@ -3,12 +3,8 @@
|
|||
"package": "net.fabricmc.fabric.mixin.gametest",
|
||||
"compatibilityLevel": "JAVA_21",
|
||||
"mixins": [
|
||||
"ArgumentTypesMixin",
|
||||
"CommandManagerMixin",
|
||||
"MinecraftServerMixin",
|
||||
"RegistryLoaderMixin",
|
||||
"StructureTemplateManagerMixin",
|
||||
"TestCommandMixin",
|
||||
"TestFunctionsMixin",
|
||||
"TestServerMixin"
|
||||
],
|
||||
"server": [
|
||||
|
|
|
@ -16,48 +16,27 @@
|
|||
|
||||
package net.fabricmc.fabric.test.gametest;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import net.minecraft.block.Blocks;
|
||||
import net.minecraft.test.GameTest;
|
||||
import net.minecraft.test.TestContext;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
|
||||
import net.fabricmc.fabric.api.gametest.v1.FabricGameTest;
|
||||
import net.fabricmc.fabric.api.gametest.v1.GameTest;
|
||||
|
||||
// optional to impl FabricGameTest
|
||||
public class ExampleFabricTestSuite implements FabricGameTest {
|
||||
/**
|
||||
* By overriding invokeTestMethod you can wrap the method call.
|
||||
* This can be used as shown to run code before and after each test.
|
||||
*/
|
||||
@Override
|
||||
public void invokeTestMethod(TestContext context, Method method) {
|
||||
beforeEach(context);
|
||||
|
||||
FabricGameTest.super.invokeTestMethod(context, method);
|
||||
|
||||
afterEach(context);
|
||||
}
|
||||
|
||||
private void beforeEach(TestContext context) {
|
||||
System.out.println("Hello beforeEach");
|
||||
context.setBlockState(0, 5, 0, Blocks.GOLD_BLOCK);
|
||||
}
|
||||
|
||||
private void afterEach(TestContext context) {
|
||||
public class ExampleFabricTestSuite {
|
||||
@GameTest(structure = "fabric-gametest-api-v1-testmod:exampletestsuite.diamond")
|
||||
public void diamond(TestContext context) {
|
||||
// Nothing to do as the structure placed the block.
|
||||
context.addInstantFinalTask(() ->
|
||||
context.checkBlock(new BlockPos(0, 1, 0), (block) -> block == Blocks.DIAMOND_BLOCK, "Expect block to be diamond")
|
||||
context.checkBlock(new BlockPos(0, 1, 0), (block) -> block == Blocks.DIAMOND_BLOCK, (b) -> Text.literal("Expect block to be diamond"))
|
||||
);
|
||||
}
|
||||
|
||||
@GameTest(templateName = "fabric-gametest-api-v1-testmod:exampletestsuite.diamond")
|
||||
public void diamond(TestContext context) {
|
||||
// Nothing to do as the structure placed the block.
|
||||
}
|
||||
|
||||
@GameTest(templateName = EMPTY_STRUCTURE)
|
||||
@GameTest
|
||||
public void noStructure(TestContext context) {
|
||||
context.setBlockState(0, 1, 0, Blocks.DIAMOND_BLOCK);
|
||||
context.addInstantFinalTask(() ->
|
||||
context.checkBlock(new BlockPos(0, 1, 0), (block) -> block == Blocks.DIAMOND_BLOCK, (b) -> Text.literal("Expect block to be diamond"))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,42 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.test.gametest;
|
||||
|
||||
import net.minecraft.block.Blocks;
|
||||
import net.minecraft.test.GameTest;
|
||||
import net.minecraft.test.TestContext;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
|
||||
import net.fabricmc.fabric.api.gametest.v1.FabricGameTest;
|
||||
|
||||
public class ExampleTestSuite {
|
||||
@GameTest
|
||||
public void diamond(TestContext context) {
|
||||
context.addInstantFinalTask(() ->
|
||||
context.checkBlock(new BlockPos(0, 1, 0), (block) -> block == Blocks.DIAMOND_BLOCK, "Expect block to be diamond")
|
||||
);
|
||||
}
|
||||
|
||||
@GameTest(templateName = FabricGameTest.EMPTY_STRUCTURE)
|
||||
public void noStructure(TestContext context) {
|
||||
context.setBlockState(0, 2, 0, Blocks.DIAMOND_BLOCK);
|
||||
|
||||
context.addInstantFinalTask(() ->
|
||||
context.checkBlock(new BlockPos(0, 2, 0), (block) -> block == Blocks.DIAMOND_BLOCK, "Expect block to be diamond")
|
||||
);
|
||||
}
|
||||
}
|
|
@ -7,8 +7,7 @@
|
|||
"license": "Apache-2.0",
|
||||
"entrypoints": {
|
||||
"fabric-gametest" : [
|
||||
"net.fabricmc.fabric.test.gametest.ExampleFabricTestSuite",
|
||||
"net.fabricmc.fabric.test.gametest.ExampleTestSuite"
|
||||
"net.fabricmc.fabric.test.gametest.ExampleFabricTestSuite"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,17 +16,29 @@
|
|||
|
||||
package net.fabricmc.fabric.test.item.gametest;
|
||||
|
||||
import net.minecraft.block.Blocks;
|
||||
import net.minecraft.block.entity.BrewingStandBlockEntity;
|
||||
import net.minecraft.component.DataComponentTypes;
|
||||
import net.minecraft.component.type.PotionContentsComponent;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.item.Items;
|
||||
import net.minecraft.potion.Potion;
|
||||
import net.minecraft.potion.Potions;
|
||||
import net.minecraft.registry.entry.RegistryEntry;
|
||||
import net.minecraft.test.TestContext;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
|
||||
import net.fabricmc.fabric.api.gametest.v1.GameTest;
|
||||
import net.fabricmc.fabric.test.item.CustomDamageTest;
|
||||
|
||||
public class BrewingStandGameTest {
|
||||
private static final int BREWING_TIME = 800;
|
||||
private static final BlockPos POS = new BlockPos(0, 1, 0);
|
||||
|
||||
/* TODO 1.21.5 tests
|
||||
@GameTest(templateName = EMPTY_STRUCTURE)
|
||||
@GameTest
|
||||
public void basicBrewing(TestContext context) {
|
||||
context.setBlockState(POS, Blocks.BREWING_STAND);
|
||||
BrewingStandBlockEntity blockEntity = context.getBlockEntity(POS);
|
||||
BrewingStandBlockEntity blockEntity = context.getBlockEntity(POS, BrewingStandBlockEntity.class);
|
||||
|
||||
loadFuel(blockEntity, context);
|
||||
|
||||
|
@ -44,10 +56,10 @@ public class BrewingStandGameTest {
|
|||
context.complete();
|
||||
}
|
||||
|
||||
@GameTest(templateName = EMPTY_STRUCTURE)
|
||||
@GameTest
|
||||
public void vanillaRemainderTest(TestContext context) {
|
||||
context.setBlockState(POS, Blocks.BREWING_STAND);
|
||||
BrewingStandBlockEntity blockEntity = context.getBlockEntity(POS);
|
||||
BrewingStandBlockEntity blockEntity = context.getBlockEntity(POS, BrewingStandBlockEntity.class);
|
||||
|
||||
loadFuel(blockEntity, context);
|
||||
|
||||
|
@ -69,7 +81,7 @@ public class BrewingStandGameTest {
|
|||
// Skip see: https://github.com/FabricMC/fabric/pull/2874
|
||||
public void fabricRemainderTest(TestContext context) {
|
||||
context.setBlockState(POS, Blocks.BREWING_STAND);
|
||||
BrewingStandBlockEntity blockEntity = context.getBlockEntity(POS);
|
||||
BrewingStandBlockEntity blockEntity = context.getBlockEntity(POS, BrewingStandBlockEntity.class);
|
||||
|
||||
loadFuel(blockEntity, context);
|
||||
|
||||
|
@ -140,6 +152,4 @@ public class BrewingStandGameTest {
|
|||
itemStack.set(DataComponentTypes.POTION_CONTENTS, new PotionContentsComponent(potion));
|
||||
return itemStack;
|
||||
}
|
||||
|
||||
*/
|
||||
}
|
||||
|
|
|
@ -16,9 +16,34 @@
|
|||
|
||||
package net.fabricmc.fabric.test.item.gametest;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import net.minecraft.component.EnchantmentEffectComponentTypes;
|
||||
import net.minecraft.enchantment.Enchantment;
|
||||
import net.minecraft.enchantment.effect.EnchantmentEffectEntry;
|
||||
import net.minecraft.enchantment.effect.EnchantmentValueEffect;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityType;
|
||||
import net.minecraft.entity.mob.CreeperEntity;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.item.Items;
|
||||
import net.minecraft.registry.DynamicRegistryManager;
|
||||
import net.minecraft.registry.Registry;
|
||||
import net.minecraft.registry.RegistryKeys;
|
||||
import net.minecraft.registry.entry.RegistryEntry;
|
||||
import net.minecraft.test.TestContext;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.util.Hand;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.GameMode;
|
||||
|
||||
import net.fabricmc.fabric.api.gametest.v1.GameTest;
|
||||
import net.fabricmc.fabric.test.item.CustomEnchantmentEffectsTest;
|
||||
|
||||
public class CustomEnchantmentEffectsGameTest {
|
||||
/* TODO 1.21.5 tests
|
||||
@GameTest(templateName = "fabric-item-api-v1-testmod:bedrock_platform")
|
||||
@GameTest
|
||||
public void weirdImpalingSetsFireToTargets(TestContext context) {
|
||||
BlockPos pos = new BlockPos(3, 3, 3);
|
||||
CreeperEntity creeper = context.spawnEntity(EntityType.CREEPER, pos);
|
||||
|
@ -28,7 +53,7 @@ public class CustomEnchantmentEffectsGameTest {
|
|||
Optional<RegistryEntry.Reference<Enchantment>> impaling = getEnchantmentRegistry(context)
|
||||
.getOptional(CustomEnchantmentEffectsTest.WEIRD_IMPALING);
|
||||
if (impaling.isEmpty()) {
|
||||
throw new GameTestException("Weird Impaling enchantment is not present");
|
||||
throw context.createError("Weird Impaling enchantment is not present");
|
||||
}
|
||||
|
||||
trident.addEnchantment(impaling.get(), 1);
|
||||
|
@ -40,12 +65,12 @@ public class CustomEnchantmentEffectsGameTest {
|
|||
context.expectEntityWithDataEnd(pos, EntityType.CREEPER, Entity::isOnFire, true);
|
||||
}
|
||||
|
||||
@GameTest(templateName = EMPTY_STRUCTURE)
|
||||
@GameTest
|
||||
public void weirdImpalingHasTwoDamageEffects(TestContext context) {
|
||||
Enchantment impaling = getEnchantmentRegistry(context).get(CustomEnchantmentEffectsTest.WEIRD_IMPALING);
|
||||
|
||||
if (impaling == null) {
|
||||
throw new GameTestException("Weird Impaling enchantment is not present");
|
||||
throw context.createError("Weird Impaling enchantment is not present");
|
||||
}
|
||||
|
||||
List<EnchantmentEffectEntry<EnchantmentValueEffect>> damageEffects = impaling
|
||||
|
@ -53,7 +78,7 @@ public class CustomEnchantmentEffectsGameTest {
|
|||
|
||||
context.assertTrue(
|
||||
damageEffects.size() == 2,
|
||||
String.format("Weird Impaling has %d damage effect(s), not the expected 2", damageEffects.size())
|
||||
Text.literal(String.format("Weird Impaling has %d damage effect(s), not the expected 2", damageEffects.size()))
|
||||
);
|
||||
context.complete();
|
||||
}
|
||||
|
@ -62,6 +87,4 @@ public class CustomEnchantmentEffectsGameTest {
|
|||
DynamicRegistryManager registryManager = context.getWorld().getRegistryManager();
|
||||
return registryManager.getOrThrow(RegistryKeys.ENCHANTMENT);
|
||||
}
|
||||
|
||||
*/
|
||||
}
|
||||
|
|
|
@ -16,17 +16,28 @@
|
|||
|
||||
package net.fabricmc.fabric.test.item.gametest;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import net.minecraft.component.DataComponentTypes;
|
||||
import net.minecraft.component.type.FireworksComponent;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.item.Items;
|
||||
import net.minecraft.test.TestContext;
|
||||
import net.minecraft.text.Text;
|
||||
|
||||
import net.fabricmc.fabric.api.gametest.v1.GameTest;
|
||||
|
||||
public class DefaultItemComponentGameTest {
|
||||
/* TODO 1.21.5 tests
|
||||
@GameTest(templateName = EMPTY_STRUCTURE)
|
||||
@GameTest
|
||||
public void modify(TestContext context) {
|
||||
Consumer<Text> checkText = text -> {
|
||||
if (text == null) {
|
||||
throw new GameTestException("Item name component not found on gold ingot");
|
||||
throw context.createError("Item name component not found on gold ingot");
|
||||
}
|
||||
|
||||
if (!"Fool's Gold".equals(text.getString())) {
|
||||
throw new GameTestException("Item name component on gold ingot is not set");
|
||||
throw context.createError("Item name component on gold ingot is not set");
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -39,30 +50,30 @@ public class DefaultItemComponentGameTest {
|
|||
boolean isBeefFood = Items.BEEF.getComponents().contains(DataComponentTypes.FOOD);
|
||||
|
||||
if (isBeefFood) {
|
||||
throw new GameTestException("Food component not removed from beef");
|
||||
throw context.createError("Food component not removed from beef");
|
||||
}
|
||||
|
||||
context.complete();
|
||||
}
|
||||
|
||||
@GameTest(templateName = EMPTY_STRUCTURE)
|
||||
@GameTest
|
||||
public void afterModify(TestContext context) {
|
||||
FireworksComponent fireworksComponent = Items.GOLD_NUGGET.getComponents().get(DataComponentTypes.FIREWORKS);
|
||||
|
||||
if (fireworksComponent == null) {
|
||||
throw new GameTestException("Fireworks component not found on gold nugget");
|
||||
throw context.createError("Fireworks component not found on gold nugget");
|
||||
}
|
||||
|
||||
Boolean enchantGlint = Items.GOLD_NUGGET.getComponents().get(DataComponentTypes.ENCHANTMENT_GLINT_OVERRIDE);
|
||||
|
||||
if (enchantGlint != Boolean.TRUE) {
|
||||
throw new GameTestException("Enchantment glint override not set on gold nugget");
|
||||
throw context.createError("Enchantment glint override not set on gold nugget");
|
||||
}
|
||||
|
||||
context.complete();
|
||||
}
|
||||
|
||||
@GameTest(templateName = EMPTY_STRUCTURE)
|
||||
@GameTest
|
||||
public void diamondPickaxeIsRenamed(TestContext context) {
|
||||
Item testItem = Items.DIAMOND_PICKAXE;
|
||||
ItemStack stack = testItem.getDefaultStack();
|
||||
|
@ -73,10 +84,8 @@ public class DefaultItemComponentGameTest {
|
|||
String errorMessage = "Expected '%s' to be contained in '%s', but it was not!";
|
||||
|
||||
// if they contain each other, then they are equal
|
||||
context.assertTrue(itemName.contains(expectedName), errorMessage.formatted(expectedName, itemName));
|
||||
context.assertTrue(expectedName.contains(itemName), errorMessage.formatted(itemName, expectedName));
|
||||
context.assertTrue(itemName.contains(expectedName), Text.literal(errorMessage.formatted(expectedName, itemName)));
|
||||
context.assertTrue(expectedName.contains(itemName), Text.literal(errorMessage.formatted(itemName, expectedName)));
|
||||
context.complete();
|
||||
}
|
||||
|
||||
*/
|
||||
}
|
||||
|
|
|
@ -16,21 +16,25 @@
|
|||
|
||||
package net.fabricmc.fabric.test.item.gametest;
|
||||
|
||||
import net.minecraft.block.Blocks;
|
||||
import net.minecraft.block.entity.AbstractFurnaceBlockEntity;
|
||||
import net.minecraft.block.entity.FurnaceBlockEntity;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.item.Items;
|
||||
import net.minecraft.test.TestContext;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
|
||||
import net.fabricmc.fabric.api.gametest.v1.GameTest;
|
||||
import net.fabricmc.fabric.test.item.CustomDamageTest;
|
||||
|
||||
public class FurnaceGameTest {
|
||||
private static final int COOK_TIME = 200;
|
||||
private static final BlockPos POS = new BlockPos(0, 1, 0);
|
||||
|
||||
/* TODO 1.21.5 tests
|
||||
@GameTest(templateName = EMPTY_STRUCTURE)
|
||||
@GameTest
|
||||
public void basicSmelt(TestContext context) {
|
||||
context.setBlockState(POS, Blocks.FURNACE);
|
||||
FurnaceBlockEntity blockEntity = context.getBlockEntity(POS);
|
||||
FurnaceBlockEntity blockEntity = context.getBlockEntity(POS, FurnaceBlockEntity.class);
|
||||
|
||||
setInputs(blockEntity, new ItemStack(Blocks.COBBLESTONE, 8), new ItemStack(Items.COAL, 2));
|
||||
|
||||
|
@ -49,10 +53,10 @@ public class FurnaceGameTest {
|
|||
context.complete();
|
||||
}
|
||||
|
||||
@GameTest(templateName = EMPTY_STRUCTURE)
|
||||
@GameTest
|
||||
public void vanillaRemainderTest(TestContext context) {
|
||||
context.setBlockState(POS, Blocks.FURNACE);
|
||||
FurnaceBlockEntity blockEntity = context.getBlockEntity(POS);
|
||||
FurnaceBlockEntity blockEntity = context.getBlockEntity(POS, FurnaceBlockEntity.class);
|
||||
|
||||
setInputs(blockEntity, new ItemStack(Blocks.COBBLESTONE, 64), new ItemStack(Items.LAVA_BUCKET));
|
||||
|
||||
|
@ -65,10 +69,10 @@ public class FurnaceGameTest {
|
|||
context.complete();
|
||||
}
|
||||
|
||||
@GameTest(templateName = EMPTY_STRUCTURE)
|
||||
@GameTest
|
||||
public void fabricRemainderTest(TestContext context) {
|
||||
context.setBlockState(POS, Blocks.FURNACE);
|
||||
FurnaceBlockEntity blockEntity = context.getBlockEntity(POS);
|
||||
FurnaceBlockEntity blockEntity = context.getBlockEntity(POS, FurnaceBlockEntity.class);
|
||||
|
||||
setInputs(blockEntity, new ItemStack(Blocks.COBBLESTONE, 32), new ItemStack(CustomDamageTest.WEIRD_PICK));
|
||||
|
||||
|
@ -93,8 +97,6 @@ public class FurnaceGameTest {
|
|||
context.complete();
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
private void setInputs(FurnaceBlockEntity blockEntity, ItemStack ingredient, ItemStack fuel) {
|
||||
blockEntity.setStack(0, ingredient);
|
||||
blockEntity.setStack(1, fuel);
|
||||
|
@ -105,8 +107,7 @@ public class FurnaceGameTest {
|
|||
ItemStack currentStack = blockEntity.getStack(i);
|
||||
ItemStack expectedStack = stacks[i];
|
||||
|
||||
throw new AssertionError("Not implemented yet");
|
||||
// RecipeGameTest.assertStacks(currentStack, expectedStack, extraErrorInfo);
|
||||
RecipeGameTest.assertStacks(currentStack, expectedStack, extraErrorInfo);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -16,9 +16,20 @@
|
|||
|
||||
package net.fabricmc.fabric.test.item.gametest;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.item.Items;
|
||||
import net.minecraft.recipe.CraftingRecipe;
|
||||
import net.minecraft.recipe.input.CraftingRecipeInput;
|
||||
import net.minecraft.test.TestContext;
|
||||
import net.minecraft.util.collection.DefaultedList;
|
||||
|
||||
import net.fabricmc.fabric.api.gametest.v1.GameTest;
|
||||
import net.fabricmc.fabric.test.item.CustomDamageTest;
|
||||
|
||||
public class RecipeGameTest {
|
||||
/* TODO 1.21.5 tests
|
||||
@GameTest(templateName = EMPTY_STRUCTURE)
|
||||
@GameTest
|
||||
public void vanillaRemainderTest(TestContext context) {
|
||||
CraftingRecipeInput inventory = CraftingRecipeInput.create(1, 2, List.of(
|
||||
new ItemStack(Items.WATER_BUCKET),
|
||||
|
@ -33,7 +44,7 @@ public class RecipeGameTest {
|
|||
context.complete();
|
||||
}
|
||||
|
||||
@GameTest(templateName = EMPTY_STRUCTURE)
|
||||
@GameTest
|
||||
public void fabricRemainderTest(TestContext context) {
|
||||
CraftingRecipeInput inventory = CraftingRecipeInput.create(1, 4, List.of(
|
||||
new ItemStack(CustomDamageTest.WEIRD_PICK),
|
||||
|
@ -67,15 +78,15 @@ public class RecipeGameTest {
|
|||
}
|
||||
|
||||
if (!currentStack.isOf(expectedStack.getItem())) {
|
||||
throw new GameTestException("Item stacks dont match. " + extraErrorInfo);
|
||||
throw new RuntimeException("Item stacks dont match. " + extraErrorInfo);
|
||||
}
|
||||
|
||||
if (currentStack.getCount() != expectedStack.getCount()) {
|
||||
throw new GameTestException("Size doesnt match. " + extraErrorInfo);
|
||||
throw new RuntimeException("Size doesnt match. " + extraErrorInfo);
|
||||
}
|
||||
|
||||
if (!ItemStack.areItemsAndComponentsEqual(currentStack, expectedStack)) {
|
||||
throw new GameTestException("Stack doesnt match. " + extraErrorInfo);
|
||||
throw new RuntimeException("Stack doesnt match. " + extraErrorInfo);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -83,6 +94,4 @@ public class RecipeGameTest {
|
|||
stack.setDamage(damage);
|
||||
return stack;
|
||||
}
|
||||
|
||||
*/
|
||||
}
|
||||
|
|
|
@ -22,8 +22,10 @@ import net.minecraft.block.Block;
|
|||
import net.minecraft.test.TestContext;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
|
||||
import net.fabricmc.fabric.api.gametest.v1.GameTest;
|
||||
|
||||
public class ObjectBuilderGameTest {
|
||||
// @GameTest(templateName = FabricGameTest.EMPTY_STRUCTURE) TODO 1.21.5 tests
|
||||
@GameTest
|
||||
public void testBlockUse(TestContext context) {
|
||||
List<Block> blocks = List.of(BlockEntityTypeBuilderTest.INITIAL_BETRAYAL_BLOCK, BlockEntityTypeBuilderTest.ADDED_BETRAYAL_BLOCK, BlockEntityTypeBuilderTest.FIRST_MULTI_BETRAYAL_BLOCK, BlockEntityTypeBuilderTest.SECOND_MULTI_BETRAYAL_BLOCK);
|
||||
BlockPos.Mutable pos = BlockPos.ORIGIN.up().mutableCopy();
|
||||
|
|
|
@ -32,11 +32,13 @@ import net.minecraft.test.TestContext;
|
|||
import net.minecraft.text.Text;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import net.fabricmc.fabric.api.gametest.v1.GameTest;
|
||||
|
||||
public class ShapelessRecipeMatchTests {
|
||||
/**
|
||||
* The recipe requires at least one undamaged pickaxe.
|
||||
*/
|
||||
// @GameTest(templateName = FabricGameTest.EMPTY_STRUCTURE) TODO 1.21.5 tests
|
||||
@GameTest
|
||||
public void testShapelessMatch(TestContext context) {
|
||||
RegistryKey<Recipe<?>> recipeKey = RegistryKey.of(RegistryKeys.RECIPE, Identifier.of("fabric-recipe-api-v1-testmod", "test_shapeless_match"));
|
||||
ShapelessRecipe recipe = (ShapelessRecipe) context.getWorld().getRecipeManager().get(recipeKey).get().value();
|
||||
|
|
|
@ -10,8 +10,6 @@
|
|||
},
|
||||
"entrypoints": {
|
||||
"fabric-gametest": [
|
||||
"net.fabricmc.fabric.test.recipe.ingredient.IngredientMatchTests",
|
||||
"net.fabricmc.fabric.test.recipe.ingredient.SerializationTests",
|
||||
"net.fabricmc.fabric.test.recipe.ingredient.ShapelessRecipeMatchTests"
|
||||
]
|
||||
}
|
||||
|
|
|
@ -16,8 +16,19 @@
|
|||
|
||||
package net.fabricmc.fabric.test.resource.conditions;
|
||||
|
||||
import net.minecraft.block.entity.BannerPattern;
|
||||
import net.minecraft.loot.LootTable;
|
||||
import net.minecraft.recipe.ServerRecipeManager;
|
||||
import net.minecraft.registry.Registry;
|
||||
import net.minecraft.registry.RegistryEntryLookup;
|
||||
import net.minecraft.registry.RegistryKey;
|
||||
import net.minecraft.registry.RegistryKeys;
|
||||
import net.minecraft.registry.ReloadableRegistries;
|
||||
import net.minecraft.test.TestContext;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import net.fabricmc.fabric.api.gametest.v1.GameTest;
|
||||
|
||||
public class ConditionalResourcesTest {
|
||||
private static final String MOD_ID = "fabric-resource-conditions-api-v1-testmod";
|
||||
|
||||
|
@ -25,8 +36,7 @@ public class ConditionalResourcesTest {
|
|||
return Identifier.of(MOD_ID, path);
|
||||
}
|
||||
|
||||
/* TODO 1.21.5 tests
|
||||
@GameTest(templateName = FabricGameTest.EMPTY_STRUCTURE)
|
||||
@GameTest
|
||||
public void conditionalRecipes(TestContext context) {
|
||||
ServerRecipeManager manager = context.getWorld().getRecipeManager();
|
||||
|
||||
|
@ -64,7 +74,7 @@ public class ConditionalResourcesTest {
|
|||
context.complete();
|
||||
}
|
||||
|
||||
@GameTest(templateName = FabricGameTest.EMPTY_STRUCTURE)
|
||||
@GameTest
|
||||
public void conditionalPredicates(TestContext context) {
|
||||
// Predicates are internally handled as a kind of loot data,
|
||||
// hence the yarn name "loot condition".
|
||||
|
@ -82,7 +92,7 @@ public class ConditionalResourcesTest {
|
|||
context.complete();
|
||||
}
|
||||
|
||||
@GameTest(templateName = FabricGameTest.EMPTY_STRUCTURE)
|
||||
@GameTest
|
||||
public void conditionalLootTables(TestContext context) {
|
||||
ReloadableRegistries.Lookup registries = context.getWorld().getServer().getReloadableRegistries();
|
||||
|
||||
|
@ -97,7 +107,7 @@ public class ConditionalResourcesTest {
|
|||
context.complete();
|
||||
}
|
||||
|
||||
@GameTest(templateName = FabricGameTest.EMPTY_STRUCTURE)
|
||||
@GameTest
|
||||
public void conditionalDynamicRegistry(TestContext context) {
|
||||
Registry<BannerPattern> registry = context.getWorld().getRegistryManager().getOrThrow(RegistryKeys.BANNER_PATTERN);
|
||||
|
||||
|
@ -112,7 +122,7 @@ public class ConditionalResourcesTest {
|
|||
context.complete();
|
||||
}
|
||||
|
||||
@GameTest(templateName = FabricGameTest.EMPTY_STRUCTURE)
|
||||
@GameTest
|
||||
public void conditionalOverlays(TestContext context) {
|
||||
RegistryEntryLookup.RegistryLookup registries = context.getWorld().getServer().getReloadableRegistries().createRegistryLookup();
|
||||
|
||||
|
@ -126,6 +136,4 @@ public class ConditionalResourcesTest {
|
|||
|
||||
context.complete();
|
||||
}
|
||||
|
||||
*/
|
||||
}
|
||||
|
|
|
@ -16,16 +16,29 @@
|
|||
|
||||
package net.fabricmc.fabric.test.resource.conditions;
|
||||
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.mojang.serialization.JsonOps;
|
||||
|
||||
import net.minecraft.registry.DynamicRegistryManager;
|
||||
import net.minecraft.registry.Registry;
|
||||
import net.minecraft.registry.RegistryKey;
|
||||
import net.minecraft.registry.RegistryKeys;
|
||||
import net.minecraft.registry.RegistryOps;
|
||||
import net.minecraft.registry.RegistryWrapper;
|
||||
import net.minecraft.registry.tag.BiomeTags;
|
||||
import net.minecraft.registry.tag.BlockTags;
|
||||
import net.minecraft.registry.tag.TagKey;
|
||||
import net.minecraft.resource.featuretoggle.FeatureFlag;
|
||||
import net.minecraft.resource.featuretoggle.FeatureFlags;
|
||||
import net.minecraft.test.TestContext;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.world.biome.BiomeKeys;
|
||||
|
||||
import net.fabricmc.fabric.api.gametest.v1.GameTest;
|
||||
import net.fabricmc.fabric.api.resource.conditions.v1.ResourceCondition;
|
||||
import net.fabricmc.fabric.api.resource.conditions.v1.ResourceConditions;
|
||||
import net.fabricmc.fabric.impl.resource.conditions.ResourceConditionsImpl;
|
||||
|
||||
public class DefaultResourceConditionsTest {
|
||||
private static final String TESTMOD_ID = "fabric-resource-conditions-api-v1-testmod";
|
||||
|
@ -46,8 +59,7 @@ public class DefaultResourceConditionsTest {
|
|||
ResourceCondition.CODEC.encodeStart(JsonOps.INSTANCE, condition).getOrThrow(message -> new AssertionError("Could not serialize \"%s\": %s".formatted(name, message)));
|
||||
}
|
||||
|
||||
/* TODO 1.21.5 tests
|
||||
@GameTest(templateName = FabricGameTest.EMPTY_STRUCTURE)
|
||||
@GameTest
|
||||
public void featuresEnabled(TestContext context) {
|
||||
ResourceCondition vanilla = ResourceConditions.featuresEnabled(FeatureFlags.VANILLA);
|
||||
// Reminder: GameTest enables all features by default
|
||||
|
@ -65,7 +77,7 @@ public class DefaultResourceConditionsTest {
|
|||
context.complete();
|
||||
}
|
||||
|
||||
@GameTest(templateName = FabricGameTest.EMPTY_STRUCTURE)
|
||||
@GameTest
|
||||
public void registryContains(TestContext context) {
|
||||
// Dynamic registry (in vitro; separate testmod needs to determine if this actually functions while loading)
|
||||
ResourceCondition plains = ResourceConditions.registryContains(BiomeKeys.PLAINS);
|
||||
|
@ -79,7 +91,7 @@ public class DefaultResourceConditionsTest {
|
|||
context.complete();
|
||||
}
|
||||
|
||||
@GameTest(templateName = FabricGameTest.EMPTY_STRUCTURE)
|
||||
@GameTest
|
||||
public void tagsPopulated(TestContext context) {
|
||||
// We need to set the tags ourselves as it is cleared outside the resource loading context.
|
||||
ResourceConditionsImpl.LOADED_TAGS.set(
|
||||
|
@ -113,6 +125,4 @@ public class DefaultResourceConditionsTest {
|
|||
|
||||
context.complete();
|
||||
}
|
||||
|
||||
*/
|
||||
}
|
||||
|
|
|
@ -51,7 +51,7 @@ import net.minecraft.resource.ResourceType;
|
|||
import net.minecraft.resource.metadata.ResourceMetadataSerializer;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.PathUtil;
|
||||
import net.minecraft.util.path.PathUtil;
|
||||
|
||||
import net.fabricmc.fabric.api.resource.ModResourcePack;
|
||||
import net.fabricmc.fabric.api.resource.ResourcePackActivationType;
|
||||
|
|
|
@ -16,17 +16,51 @@
|
|||
|
||||
package net.fabricmc.fabric.test.transfer.gametests;
|
||||
|
||||
import org.apache.commons.lang3.mutable.MutableInt;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.Blocks;
|
||||
import net.minecraft.block.ComparatorBlock;
|
||||
import net.minecraft.block.ComposterBlock;
|
||||
import net.minecraft.block.JukeboxBlock;
|
||||
import net.minecraft.block.entity.BlockEntity;
|
||||
import net.minecraft.block.entity.BrewingStandBlockEntity;
|
||||
import net.minecraft.block.entity.ChestBlockEntity;
|
||||
import net.minecraft.block.entity.ChiseledBookshelfBlockEntity;
|
||||
import net.minecraft.block.entity.FurnaceBlockEntity;
|
||||
import net.minecraft.block.entity.HopperBlockEntity;
|
||||
import net.minecraft.block.entity.ShulkerBoxBlockEntity;
|
||||
import net.minecraft.inventory.Inventory;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.item.Items;
|
||||
import net.minecraft.state.property.Properties;
|
||||
import net.minecraft.test.TestContext;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import net.fabricmc.fabric.api.gametest.v1.GameTest;
|
||||
import net.fabricmc.fabric.api.transfer.v1.item.InventoryStorage;
|
||||
import net.fabricmc.fabric.api.transfer.v1.item.ItemStorage;
|
||||
import net.fabricmc.fabric.api.transfer.v1.item.ItemVariant;
|
||||
import net.fabricmc.fabric.api.transfer.v1.storage.SlottedStorage;
|
||||
import net.fabricmc.fabric.api.transfer.v1.storage.Storage;
|
||||
import net.fabricmc.fabric.api.transfer.v1.storage.StorageUtil;
|
||||
import net.fabricmc.fabric.api.transfer.v1.transaction.Transaction;
|
||||
import net.fabricmc.fabric.test.transfer.mixin.AbstractFurnaceBlockEntityAccessor;
|
||||
|
||||
public class VanillaStorageTests {
|
||||
/**
|
||||
* Regression test for https://github.com/FabricMC/fabric/issues/1972.
|
||||
* Ensures that furnace cook time is only reset when extraction is actually committed.
|
||||
*/
|
||||
/*
|
||||
@GameTest(templateName = FabricGameTest.EMPTY_STRUCTURE)
|
||||
@GameTest
|
||||
public void testFurnaceCookTime(TestContext context) {
|
||||
BlockPos pos = new BlockPos(0, 1, 0);
|
||||
context.setBlockState(pos, Blocks.FURNACE.getDefaultState());
|
||||
FurnaceBlockEntity furnace = context.getBlockEntity(pos);
|
||||
FurnaceBlockEntity furnace = context.getBlockEntity(pos, FurnaceBlockEntity.class);
|
||||
AbstractFurnaceBlockEntityAccessor accessor = (AbstractFurnaceBlockEntityAccessor) furnace;
|
||||
|
||||
ItemVariant rawIron = ItemVariant.of(Items.RAW_IRON);
|
||||
|
@ -36,50 +70,47 @@ public class VanillaStorageTests {
|
|||
|
||||
context.runAtTick(5, () -> {
|
||||
if (accessor.getCookingTimeSpent() <= 0) {
|
||||
throw new GameTestException("Furnace should have started cooking.");
|
||||
throw context.createError("Furnace should have started cooking.");
|
||||
}
|
||||
|
||||
try (Transaction transaction = Transaction.openOuter()) {
|
||||
if (furnaceWrapper.extract(rawIron, 64, transaction) != 64) {
|
||||
throw new GameTestException("Failed to extract 64 raw iron.");
|
||||
throw context.createError("Failed to extract 64 raw iron.");
|
||||
}
|
||||
}
|
||||
|
||||
if (accessor.getCookingTimeSpent() <= 0) {
|
||||
throw new GameTestException("Furnace should still cook after simulation.");
|
||||
throw context.createError("Furnace should still cook after simulation.");
|
||||
}
|
||||
|
||||
try (Transaction transaction = Transaction.openOuter()) {
|
||||
if (furnaceWrapper.extract(rawIron, 64, transaction) != 64) {
|
||||
throw new GameTestException("Failed to extract 64 raw iron.");
|
||||
throw context.createError("Failed to extract 64 raw iron.");
|
||||
}
|
||||
|
||||
transaction.commit();
|
||||
}
|
||||
|
||||
if (accessor.getCookingTimeSpent() != 0) {
|
||||
throw new GameTestException("Furnace should have reset cook time after being emptied.");
|
||||
throw context.createError("Furnace should have reset cook time after being emptied.");
|
||||
}
|
||||
|
||||
context.complete();
|
||||
});
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
/**
|
||||
* Tests that the passed block doesn't update adjacent comparators until the very end of a committed transaction.
|
||||
*
|
||||
* @param block A block with an Inventory block entity.
|
||||
* @param variant The variant to try to insert (needs to be supported by the Inventory).
|
||||
*/
|
||||
/*
|
||||
private static void testComparatorOnInventory(TestContext context, Block block, ItemVariant variant) {
|
||||
private static <T extends BlockEntity & Inventory> void testComparatorOnInventory(TestContext context, Block block, ItemVariant variant, Class<T> inventoryClass) {
|
||||
World world = context.getWorld();
|
||||
|
||||
BlockPos pos = new BlockPos(0, 2, 0);
|
||||
context.setBlockState(pos, block.getDefaultState());
|
||||
Inventory inventory = context.getBlockEntity(pos);
|
||||
T inventory = context.getBlockEntity(pos, inventoryClass);
|
||||
InventoryStorage storage = InventoryStorage.of(inventory, null);
|
||||
|
||||
BlockPos comparatorPos = new BlockPos(1, 2, 0);
|
||||
|
@ -91,220 +122,199 @@ public class VanillaStorageTests {
|
|||
|
||||
try (Transaction transaction = Transaction.openOuter()) {
|
||||
if (world.getBlockTickScheduler().isQueued(context.getAbsolutePos(comparatorPos), Blocks.COMPARATOR)) {
|
||||
throw new GameTestException("Comparator should not have a tick scheduled.");
|
||||
throw context.createError("Comparator should not have a tick scheduled.");
|
||||
}
|
||||
|
||||
storage.insert(variant, 1000000, transaction);
|
||||
|
||||
// uncommitted insert should not schedule an update
|
||||
if (world.getBlockTickScheduler().isQueued(context.getAbsolutePos(comparatorPos), Blocks.COMPARATOR)) {
|
||||
throw new GameTestException("Comparator should not have a tick scheduled.");
|
||||
throw context.createError("Comparator should not have a tick scheduled.");
|
||||
}
|
||||
|
||||
transaction.commit();
|
||||
|
||||
// committed insert should schedule an update
|
||||
if (!world.getBlockTickScheduler().isQueued(context.getAbsolutePos(comparatorPos), Blocks.COMPARATOR)) {
|
||||
throw new GameTestException("Comparator should have a tick scheduled.");
|
||||
throw context.createError("Comparator should have a tick scheduled.");
|
||||
}
|
||||
}
|
||||
|
||||
context.complete();
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
/**
|
||||
* Tests that containers such as chests don't update adjacent comparators until the very end of a committed transaction.
|
||||
*/
|
||||
/*
|
||||
@GameTest(templateName = FabricGameTest.EMPTY_STRUCTURE)
|
||||
@GameTest
|
||||
public void testChestComparator(TestContext context) {
|
||||
testComparatorOnInventory(context, Blocks.CHEST, ItemVariant.of(Items.DIAMOND));
|
||||
testComparatorOnInventory(context, Blocks.CHEST, ItemVariant.of(Items.DIAMOND), ChestBlockEntity.class);
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
/**
|
||||
* Same as {@link #testChestComparator} but for chiseled bookshelves, because their implementation is very... strange.
|
||||
*/
|
||||
/*
|
||||
@GameTest(templateName = FabricGameTest.EMPTY_STRUCTURE)
|
||||
@GameTest
|
||||
public void testChiseledBookshelfComparator(TestContext context) {
|
||||
testComparatorOnInventory(context, Blocks.CHISELED_BOOKSHELF, ItemVariant.of(Items.BOOK));
|
||||
testComparatorOnInventory(context, Blocks.CHISELED_BOOKSHELF, ItemVariant.of(Items.BOOK), ChiseledBookshelfBlockEntity.class);
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
/**
|
||||
* Test for chiseled bookshelves, because their implementation is very... strange.
|
||||
*/
|
||||
/*
|
||||
@GameTest(templateName = FabricGameTest.EMPTY_STRUCTURE)
|
||||
@GameTest
|
||||
public void testChiseledBookshelf(TestContext context) {
|
||||
ItemVariant book = ItemVariant.of(Items.BOOK);
|
||||
|
||||
BlockPos pos = new BlockPos(0, 1, 0);
|
||||
context.setBlockState(pos, Blocks.CHISELED_BOOKSHELF.getDefaultState());
|
||||
ChiseledBookshelfBlockEntity bookshelf = context.getBlockEntity(pos);
|
||||
ChiseledBookshelfBlockEntity bookshelf = context.getBlockEntity(pos, ChiseledBookshelfBlockEntity.class);
|
||||
InventoryStorage storage = InventoryStorage.of(bookshelf, null);
|
||||
|
||||
// First, check that we can correctly undo insert operations, because vanilla's setStack doesn't permit it without our patches.
|
||||
try (Transaction transaction = Transaction.openOuter()) {
|
||||
if (storage.insert(book, 2, transaction) != 2) throw new GameTestException("Should have inserted 2 books");
|
||||
if (storage.insert(book, 2, transaction) != 2) throw context.createError("Should have inserted 2 books");
|
||||
|
||||
if (bookshelf.getStack(0).getCount() != 1) throw new GameTestException("Bookshelf stack 0 should have size 1");
|
||||
if (!book.matches(bookshelf.getStack(0))) throw new GameTestException("Bookshelf stack 0 should be a book");
|
||||
if (bookshelf.getStack(1).getCount() != 1) throw new GameTestException("Bookshelf stack 1 should have size 1");
|
||||
if (!book.matches(bookshelf.getStack(1))) throw new GameTestException("Bookshelf stack 1 should be a book");
|
||||
if (bookshelf.getStack(0).getCount() != 1) throw context.createError("Bookshelf stack 0 should have size 1");
|
||||
if (!book.matches(bookshelf.getStack(0))) throw context.createError("Bookshelf stack 0 should be a book");
|
||||
if (bookshelf.getStack(1).getCount() != 1) throw context.createError("Bookshelf stack 1 should have size 1");
|
||||
if (!book.matches(bookshelf.getStack(1))) throw context.createError("Bookshelf stack 1 should be a book");
|
||||
}
|
||||
|
||||
if (!bookshelf.getStack(0).isEmpty()) throw new GameTestException("Bookshelf stack 0 should be empty again after aborting transaction");
|
||||
if (!bookshelf.getStack(1).isEmpty()) throw new GameTestException("Bookshelf stack 1 should be empty again after aborting transaction");
|
||||
if (!bookshelf.getStack(0).isEmpty()) throw context.createError("Bookshelf stack 0 should be empty again after aborting transaction");
|
||||
if (!bookshelf.getStack(1).isEmpty()) throw context.createError("Bookshelf stack 1 should be empty again after aborting transaction");
|
||||
|
||||
// Second, check that we correctly update the last modified slot.
|
||||
try (Transaction tx = Transaction.openOuter()) {
|
||||
if (storage.getSlot(1).insert(book, 1, tx) != 1) throw new GameTestException("Should have inserted 1 book");
|
||||
if (bookshelf.getLastInteractedSlot() != 1) throw new GameTestException("Last modified slot should be 1");
|
||||
if (storage.getSlot(1).insert(book, 1, tx) != 1) throw context.createError("Should have inserted 1 book");
|
||||
if (bookshelf.getLastInteractedSlot() != 1) throw context.createError("Last modified slot should be 1");
|
||||
|
||||
if (storage.getSlot(2).insert(book, 1, tx) != 1) throw new GameTestException("Should have inserted 1 book");
|
||||
if (bookshelf.getLastInteractedSlot() != 2) throw new GameTestException("Last modified slot should be 2");
|
||||
if (storage.getSlot(2).insert(book, 1, tx) != 1) throw context.createError("Should have inserted 1 book");
|
||||
if (bookshelf.getLastInteractedSlot() != 2) throw context.createError("Last modified slot should be 2");
|
||||
|
||||
if (storage.getSlot(1).extract(book, 1, tx) != 1) throw new GameTestException("Should have extracted 1 book");
|
||||
if (bookshelf.getLastInteractedSlot() != 1) throw new GameTestException("Last modified slot should be 1");
|
||||
if (storage.getSlot(1).extract(book, 1, tx) != 1) throw context.createError("Should have extracted 1 book");
|
||||
if (bookshelf.getLastInteractedSlot() != 1) throw context.createError("Last modified slot should be 1");
|
||||
|
||||
// Now, create an aborted nested transaction.
|
||||
try (Transaction nested = tx.openNested()) {
|
||||
if (storage.insert(book, 100, nested) != 5) throw new GameTestException("Should have inserted 5 books");
|
||||
if (storage.insert(book, 100, nested) != 5) throw context.createError("Should have inserted 5 books");
|
||||
// Now, last modified slot should be 5.
|
||||
if (bookshelf.getLastInteractedSlot() != 5) throw new GameTestException("Last modified slot should be 5");
|
||||
if (bookshelf.getLastInteractedSlot() != 5) throw context.createError("Last modified slot should be 5");
|
||||
}
|
||||
|
||||
// And it's back to 1 in theory.
|
||||
if (bookshelf.getLastInteractedSlot() != 1) throw new GameTestException("Last modified slot should be 1");
|
||||
if (bookshelf.getLastInteractedSlot() != 1) throw context.createError("Last modified slot should be 1");
|
||||
tx.commit();
|
||||
}
|
||||
|
||||
if (bookshelf.getLastInteractedSlot() != 1) throw new GameTestException("Last modified slot should be 1 after committing transaction");
|
||||
if (bookshelf.getLastInteractedSlot() != 1) throw context.createError("Last modified slot should be 1 after committing transaction");
|
||||
|
||||
// Let's also check the state properties. Only slot 2 should be occupied.
|
||||
BlockState state = bookshelf.getCachedState();
|
||||
|
||||
if (state.get(Properties.SLOT_0_OCCUPIED)) throw new GameTestException("Slot 0 should not be occupied");
|
||||
if (state.get(Properties.SLOT_1_OCCUPIED)) throw new GameTestException("Slot 1 should not be occupied");
|
||||
if (!state.get(Properties.SLOT_2_OCCUPIED)) throw new GameTestException("Slot 2 should be occupied");
|
||||
if (state.get(Properties.SLOT_3_OCCUPIED)) throw new GameTestException("Slot 3 should not be occupied");
|
||||
if (state.get(Properties.SLOT_4_OCCUPIED)) throw new GameTestException("Slot 4 should not be occupied");
|
||||
if (state.get(Properties.SLOT_5_OCCUPIED)) throw new GameTestException("Slot 5 should not be occupied");
|
||||
if (state.get(Properties.SLOT_0_OCCUPIED)) throw context.createError("Slot 0 should not be occupied");
|
||||
if (state.get(Properties.SLOT_1_OCCUPIED)) throw context.createError("Slot 1 should not be occupied");
|
||||
if (!state.get(Properties.SLOT_2_OCCUPIED)) throw context.createError("Slot 2 should be occupied");
|
||||
if (state.get(Properties.SLOT_3_OCCUPIED)) throw context.createError("Slot 3 should not be occupied");
|
||||
if (state.get(Properties.SLOT_4_OCCUPIED)) throw context.createError("Slot 4 should not be occupied");
|
||||
if (state.get(Properties.SLOT_5_OCCUPIED)) throw context.createError("Slot 5 should not be occupied");
|
||||
|
||||
context.complete();
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
/**
|
||||
* Tests that shulker boxes cannot be inserted into other shulker boxes.
|
||||
*/
|
||||
/*
|
||||
@GameTest(templateName = FabricGameTest.EMPTY_STRUCTURE)
|
||||
@GameTest
|
||||
public void testShulkerNoInsert(TestContext context) {
|
||||
BlockPos pos = new BlockPos(0, 2, 0);
|
||||
context.setBlockState(pos, Blocks.SHULKER_BOX);
|
||||
ShulkerBoxBlockEntity shulker = context.getBlockEntity(pos);
|
||||
ShulkerBoxBlockEntity shulker = context.getBlockEntity(pos, ShulkerBoxBlockEntity.class);
|
||||
InventoryStorage storage = InventoryStorage.of(shulker, null);
|
||||
|
||||
if (StorageUtil.simulateInsert(storage, ItemVariant.of(Items.SHULKER_BOX), 1, null) > 0) {
|
||||
context.throwPositionedException("Expected shulker box to be rejected", pos);
|
||||
context.throwPositionedException(Text.literal("Expected shulker box to be rejected"), pos);
|
||||
}
|
||||
|
||||
context.complete();
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
/**
|
||||
* {@link Inventory#isValid(int, ItemStack)} is supposed to be independent of the stack size.
|
||||
* However, to limit some stackable inputs to a size of 1, brewing stands and furnaces don't follow this rule in all cases.
|
||||
* This test ensures that the Transfer API works around this issue for furnaces.
|
||||
*/
|
||||
/*
|
||||
@GameTest(templateName = FabricGameTest.EMPTY_STRUCTURE)
|
||||
@GameTest
|
||||
public void testBadFurnaceIsValid(TestContext context) {
|
||||
BlockPos pos = new BlockPos(0, 1, 0);
|
||||
context.setBlockState(pos, Blocks.FURNACE.getDefaultState());
|
||||
FurnaceBlockEntity furnace = context.getBlockEntity(pos);
|
||||
FurnaceBlockEntity furnace = context.getBlockEntity(pos, FurnaceBlockEntity.class);
|
||||
InventoryStorage furnaceWrapper = InventoryStorage.of(furnace, null);
|
||||
|
||||
try (Transaction tx = Transaction.openOuter()) {
|
||||
if (furnaceWrapper.getSlot(1).insert(ItemVariant.of(Items.BUCKET), 2, tx) != 1) {
|
||||
throw new GameTestException("Exactly 1 bucket should have been inserted");
|
||||
throw context.createError("Exactly 1 bucket should have been inserted");
|
||||
}
|
||||
}
|
||||
|
||||
context.complete();
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
/**
|
||||
* Same as {@link #testBadFurnaceIsValid(TestContext)}, but for brewing stands.
|
||||
*/
|
||||
/*
|
||||
@GameTest(templateName = FabricGameTest.EMPTY_STRUCTURE)
|
||||
@GameTest
|
||||
public void testBadBrewingStandIsValid(TestContext context) {
|
||||
BlockPos pos = new BlockPos(0, 1, 0);
|
||||
context.setBlockState(pos, Blocks.BREWING_STAND.getDefaultState());
|
||||
BrewingStandBlockEntity brewingStand = context.getBlockEntity(pos);
|
||||
BrewingStandBlockEntity brewingStand = context.getBlockEntity(pos, BrewingStandBlockEntity.class);
|
||||
InventoryStorage brewingStandWrapper = InventoryStorage.of(brewingStand, null);
|
||||
|
||||
try (Transaction tx = Transaction.openOuter()) {
|
||||
for (int bottleSlot = 0; bottleSlot < 3; ++bottleSlot) {
|
||||
if (brewingStandWrapper.getSlot(bottleSlot).insert(ItemVariant.of(Items.GLASS_BOTTLE), 2, tx) != 1) {
|
||||
throw new GameTestException("Exactly 1 glass bottle should have been inserted");
|
||||
throw context.createError("Exactly 1 glass bottle should have been inserted");
|
||||
}
|
||||
}
|
||||
|
||||
if (brewingStandWrapper.getSlot(3).insert(ItemVariant.of(Items.REDSTONE), 2, tx) != 2) {
|
||||
throw new GameTestException("Brewing ingredient insertion should not be limited");
|
||||
throw context.createError("Brewing ingredient insertion should not be limited");
|
||||
}
|
||||
}
|
||||
|
||||
context.complete();
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
/**
|
||||
* Regression test for <a href="https://github.com/FabricMC/fabric/issues/2810">double chest wrapper only updating modified halves</a>.
|
||||
*/
|
||||
/*
|
||||
@GameTest(templateName = "fabric-transfer-api-v1-testmod:double_chest_comparators", skyAccess = true)
|
||||
@GameTest(structure = "fabric-transfer-api-v1-testmod:double_chest_comparators", skyAccess = true)
|
||||
public void testDoubleChestComparator(TestContext context) {
|
||||
BlockPos chestPos = new BlockPos(2, 1, 2);
|
||||
Storage<ItemVariant> storage = ItemStorage.SIDED.find(context.getWorld(), context.getAbsolutePos(chestPos), Direction.UP);
|
||||
context.assertTrue(storage != null, "Storage must not be null");
|
||||
context.assertTrue(storage != null, Text.literal("Storage must not be null"));
|
||||
|
||||
// Insert one item
|
||||
try (Transaction tx = Transaction.openOuter()) {
|
||||
context.assertTrue(storage.insert(ItemVariant.of(Items.DIAMOND), 1, tx) == 1, "Diamond should have been inserted");
|
||||
context.assertTrue(storage.insert(ItemVariant.of(Items.DIAMOND), 1, tx) == 1, Text.literal("Diamond should have been inserted"));
|
||||
tx.commit();
|
||||
}
|
||||
|
||||
// Check that the inventory and slotted storages match
|
||||
Inventory inventory = HopperBlockEntity.getInventoryAt(context.getWorld(), context.getAbsolutePos(chestPos));
|
||||
context.assertTrue(inventory != null, "Inventory must not be null");
|
||||
context.assertTrue(inventory != null, Text.literal("Inventory must not be null"));
|
||||
|
||||
if (!(storage instanceof SlottedStorage<ItemVariant> slottedStorage)) {
|
||||
throw new GameTestException("Double chest storage must be a SlottedStorage");
|
||||
throw context.createError("Double chest storage must be a SlottedStorage");
|
||||
}
|
||||
|
||||
for (int i = 0; i < inventory.size(); ++i) {
|
||||
ItemStack stack = inventory.getStack(i);
|
||||
ItemVariant variant = ItemVariant.of(stack.getItem());
|
||||
context.assertTrue(variant.matches(stack), "Item variant in slot " + i + " must match stack");
|
||||
context.assertTrue(variant.matches(stack), Text.literal("Item variant in slot " + i + " must match stack"));
|
||||
long expectedCount = stack.getCount();
|
||||
long actualCount = slottedStorage.getSlot(i).getAmount();
|
||||
context.assertTrue(expectedCount == actualCount, "Slot " + i + " should have " + expectedCount + " items, but has " + actualCount);
|
||||
context.assertTrue(expectedCount == actualCount, Text.literal("Slot " + i + " should have " + expectedCount + " items, but has " + actualCount));
|
||||
}
|
||||
|
||||
// Check that an update is queued for every single comparator
|
||||
|
@ -318,22 +328,19 @@ public class VanillaStorageTests {
|
|||
comparatorCount.increment();
|
||||
|
||||
if (!context.getWorld().getBlockTickScheduler().isQueued(context.getAbsolutePos(relativePos), Blocks.COMPARATOR)) {
|
||||
throw new GameTestException("Comparator at " + relativePos + " should have an update scheduled");
|
||||
throw context.createError("Comparator at " + relativePos + " should have an update scheduled");
|
||||
}
|
||||
});
|
||||
|
||||
context.assertTrue(comparatorCount.intValue() == 6, "Expected exactly 6 comparators");
|
||||
context.assertTrue(comparatorCount.intValue() == 6, Text.literal("Expected exactly 6 comparators"));
|
||||
|
||||
context.complete();
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
/**
|
||||
* Regression test for <a href="https://github.com/FabricMC/fabric/issues/3017">composters not always incrementing their level on the first insert</a>.
|
||||
*/
|
||||
/*
|
||||
@GameTest(templateName = FabricGameTest.EMPTY_STRUCTURE)
|
||||
@GameTest
|
||||
public void testComposterFirstInsert(TestContext context) {
|
||||
BlockPos pos = new BlockPos(0, 1, 0);
|
||||
|
||||
|
@ -345,25 +352,22 @@ public class VanillaStorageTests {
|
|||
|
||||
try (Transaction tx = Transaction.openOuter()) {
|
||||
if (storage.insert(carrot, 1, tx) != 1) {
|
||||
context.throwPositionedException("Carrot should have been inserted", pos);
|
||||
context.throwPositionedException(Text.literal("Carrot should have been inserted"), pos);
|
||||
}
|
||||
|
||||
tx.commit();
|
||||
}
|
||||
|
||||
context.checkBlockState(pos, state -> state.get(ComposterBlock.LEVEL) == 1, () -> "Composter should have level 1");
|
||||
context.checkBlockState(pos, state -> state.get(ComposterBlock.LEVEL) == 1, (s) -> Text.literal("Composter should have level 1"));
|
||||
}
|
||||
|
||||
context.complete();
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
/**
|
||||
* Regression test for <a href="https://github.com/FabricMC/fabric/issues/3485">jukeboxes having their state changed mid-transaction</a>.
|
||||
*/
|
||||
/*
|
||||
@GameTest(templateName = FabricGameTest.EMPTY_STRUCTURE)
|
||||
@GameTest
|
||||
public void testJukeboxState(TestContext context) {
|
||||
BlockPos pos = new BlockPos(2, 2, 2);
|
||||
context.setBlockState(pos, Blocks.JUKEBOX.getDefaultState());
|
||||
|
@ -371,13 +375,11 @@ public class VanillaStorageTests {
|
|||
|
||||
try (Transaction tx = Transaction.openOuter()) {
|
||||
storage.insert(ItemVariant.of(Items.MUSIC_DISC_11), 1, tx);
|
||||
context.checkBlockState(pos, state -> !state.get(JukeboxBlock.HAS_RECORD), () -> "Jukebox should not have its state changed mid-transaction");
|
||||
context.checkBlockState(pos, state -> !state.get(JukeboxBlock.HAS_RECORD), (b) -> Text.literal("Jukebox should not have its state changed mid-transaction"));
|
||||
tx.commit();
|
||||
}
|
||||
|
||||
context.checkBlockState(pos, state -> state.get(JukeboxBlock.HAS_RECORD), () -> "Jukebox should have its state changed");
|
||||
context.checkBlockState(pos, state -> state.get(JukeboxBlock.HAS_RECORD), (b) -> Text.literal("Jukebox should have its state changed"));
|
||||
context.complete();
|
||||
}
|
||||
|
||||
*/
|
||||
}
|
||||
|
|
|
@ -22,12 +22,13 @@ import net.minecraft.fluid.Fluids;
|
|||
import net.minecraft.server.world.ServerWorld;
|
||||
import net.minecraft.test.TestContext;
|
||||
|
||||
import net.fabricmc.fabric.api.gametest.v1.GameTest;
|
||||
import net.fabricmc.fabric.api.transfer.v1.fluid.FluidConstants;
|
||||
import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant;
|
||||
import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariantAttributes;
|
||||
|
||||
public class WorldDependentAttributesTest {
|
||||
//@GameTest(templateName = FabricGameTest.EMPTY_STRUCTURE) TODO 1.21.5 tests
|
||||
@GameTest
|
||||
public void testViscosity(TestContext context) {
|
||||
ServerWorld overworld = context.getWorld();
|
||||
ServerWorld nether = overworld.getServer().getWorld(ServerWorld.NETHER);
|
||||
|
|
|
@ -3,7 +3,7 @@ org.gradle.parallel=true
|
|||
|
||||
version=0.114.4
|
||||
minecraft_version=25w03a
|
||||
yarn_version=+build.1
|
||||
yarn_version=+build.3
|
||||
loader_version=0.16.10
|
||||
installer_version=1.0.1
|
||||
|
||||
|
|
|
@ -42,7 +42,7 @@ include 'fabric-dimensions-v1'
|
|||
include 'fabric-entity-events-v1'
|
||||
include 'fabric-events-interaction-v0'
|
||||
include 'fabric-game-rule-api-v1'
|
||||
//include 'fabric-gametest-api-v1'
|
||||
include 'fabric-gametest-api-v1'
|
||||
include 'fabric-item-api-v1'
|
||||
include 'fabric-item-group-api-v1'
|
||||
include 'fabric-key-binding-api-v1'
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue