diff --git a/build.gradle b/build.gradle index 72a6d840a0..91b20a15bd 100644 --- a/build.gradle +++ b/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") { diff --git a/fabric-api-base/src/testmod/java/net/fabricmc/fabric/test/base/FabricApiBaseGameTest.java b/fabric-api-base/src/testmod/java/net/fabricmc/fabric/test/base/FabricApiBaseGameTest.java index a5722277e0..4f9df096d4 100644 --- a/fabric-api-base/src/testmod/java/net/fabricmc/fabric/test/base/FabricApiBaseGameTest.java +++ b/fabric-api-base/src/testmod/java/net/fabricmc/fabric/test/base/FabricApiBaseGameTest.java @@ -20,8 +20,10 @@ 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(); diff --git a/fabric-command-api-v2/src/testmod/java/net/fabricmc/fabric/test/command/EntitySelectorGameTest.java b/fabric-command-api-v2/src/testmod/java/net/fabricmc/fabric/test/command/EntitySelectorGameTest.java index da8f917da8..dd474cd027 100644 --- a/fabric-command-api-v2/src/testmod/java/net/fabricmc/fabric/test/command/EntitySelectorGameTest.java +++ b/fabric-command-api-v2/src/testmod/java/net/fabricmc/fabric/test/command/EntitySelectorGameTest.java @@ -24,6 +24,8 @@ 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 @@ private void spawn(TestContext context, float health) { 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); diff --git a/fabric-content-registries-v0/src/testmod/java/net/fabricmc/fabric/test/content/registry/ContentRegistryGameTest.java b/fabric-content-registries-v0/src/testmod/java/net/fabricmc/fabric/test/content/registry/ContentRegistryGameTest.java index 21569b711d..643f842486 100644 --- a/fabric-content-registries-v0/src/testmod/java/net/fabricmc/fabric/test/content/registry/ContentRegistryGameTest.java +++ b/fabric-content-registries-v0/src/testmod/java/net/fabricmc/fabric/test/content/registry/ContentRegistryGameTest.java @@ -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 void testCompostingChanceRegistry(TestContext context) { // 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 void testFlattenableBlockRegistry(TestContext context) { 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 @@ private void smelt(TestContext context, ItemStack fuelStack, BiConsumer { - 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 @@ private void smeltCompleted(TestContext context, ItemStack fuelStack) { 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 void testStrippableBlockRegistry(TestContext context) { 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 void testTillableBlockRegistry(TestContext context) { 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 void testOxidizableBlocksRegistry(TestContext context) { 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 void testOxidizableBlocksRegistry(TestContext context) { 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 void testWaxableBlocksRegistry(TestContext context) { 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 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 @@ private void brew(TestContext context, ItemStack input, ItemStack bottle, Consum 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(); }); } - - */ } diff --git a/fabric-content-registries-v0/src/testmod/java/net/fabricmc/fabric/test/content/registry/FlammableTest.java b/fabric-content-registries-v0/src/testmod/java/net/fabricmc/fabric/test/content/registry/FlammableTest.java index 2c0f8d9af9..2aac502183 100644 --- a/fabric-content-registries-v0/src/testmod/java/net/fabricmc/fabric/test/content/registry/FlammableTest.java +++ b/fabric-content-registries-v0/src/testmod/java/net/fabricmc/fabric/test/content/registry/FlammableTest.java @@ -20,16 +20,17 @@ 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 FlammableBlockRegistry ignoring tags on first load. */ - // @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(); diff --git a/fabric-data-attachment-api-v1/src/testmod/java/net/fabricmc/fabric/test/attachment/gametest/AttachmentCopyTests.java b/fabric-data-attachment-api-v1/src/testmod/java/net/fabricmc/fabric/test/attachment/gametest/AttachmentCopyTests.java index 8184963e0a..4ee1d51bc1 100644 --- a/fabric-data-attachment-api-v1/src/testmod/java/net/fabricmc/fabric/test/attachment/gametest/AttachmentCopyTests.java +++ b/fabric-data-attachment-api-v1/src/testmod/java/net/fabricmc/fabric/test/attachment/gametest/AttachmentCopyTests.java @@ -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 void testCrossWorldTeleport(TestContext context) { 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 void testMobConversion(TestContext context) { List 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(); } - - */ } diff --git a/fabric-data-attachment-api-v1/src/testmod/java/net/fabricmc/fabric/test/attachment/gametest/BlockEntityTests.java b/fabric-data-attachment-api-v1/src/testmod/java/net/fabricmc/fabric/test/attachment/gametest/BlockEntityTests.java index 692c4ffc10..2bc0e4f628 100644 --- a/fabric-data-attachment-api-v1/src/testmod/java/net/fabricmc/fabric/test/attachment/gametest/BlockEntityTests.java +++ b/fabric-data-attachment-api-v1/src/testmod/java/net/fabricmc/fabric/test/attachment/gametest/BlockEntityTests.java @@ -19,11 +19,27 @@ 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 void testBlockEntitySync(TestContext context) { 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(); } - - */ } diff --git a/fabric-events-interaction-v0/src/testmod/java/net/fabricmc/fabric/test/event/interaction/FakePlayerTests.java b/fabric-events-interaction-v0/src/testmod/java/net/fabricmc/fabric/test/event/interaction/FakePlayerTests.java index 23e704e811..2fa2a22848 100644 --- a/fabric-events-interaction-v0/src/testmod/java/net/fabricmc/fabric/test/event/interaction/FakePlayerTests.java +++ b/fabric-events-interaction-v0/src/testmod/java/net/fabricmc/fabric/test/event/interaction/FakePlayerTests.java @@ -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 void testFakePlayerPlaceSign(TestContext context) { 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 void testFakePlayerBreakBeehive(TestContext context) { 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(); } - - */ } diff --git a/fabric-gametest-api-v1/build.gradle b/fabric-gametest-api-v1/build.gradle index 3d6b1ea003..2db9d70f33 100644 --- a/fabric-gametest-api-v1/build.gradle +++ b/fabric-gametest-api-v1/build.gradle @@ -17,5 +17,5 @@ loom { moduleDependencies(project, [ 'fabric-api-base', - 'fabric-resource-loader-v0' + 'fabric-resource-loader-v0', ]) diff --git a/fabric-gametest-api-v1/src/main/java/net/fabricmc/fabric/api/gametest/v1/FabricGameTest.java b/fabric-gametest-api-v1/src/main/java/net/fabricmc/fabric/api/gametest/v1/FabricGameTest.java deleted file mode 100644 index 8345dbcfd7..0000000000 --- a/fabric-gametest-api-v1/src/main/java/net/fabricmc/fabric/api/gametest/v1/FabricGameTest.java +++ /dev/null @@ -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); - } -} diff --git a/fabric-gametest-api-v1/src/main/java/net/fabricmc/fabric/api/gametest/v1/GameTest.java b/fabric-gametest-api-v1/src/main/java/net/fabricmc/fabric/api/gametest/v1/GameTest.java new file mode 100644 index 0000000000..98d11fb3b6 --- /dev/null +++ b/fabric-gametest-api-v1/src/main/java/net/fabricmc/fabric/api/gametest/v1/GameTest.java @@ -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. + * + *

{@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}. + * + *

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. + * + *

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; +} diff --git a/fabric-gametest-api-v1/src/main/java/net/fabricmc/fabric/api/gametest/v1/package-info.java b/fabric-gametest-api-v1/src/main/java/net/fabricmc/fabric/api/gametest/v1/package-info.java index 3a5b134c25..9b05cc0989 100644 --- a/fabric-gametest-api-v1/src/main/java/net/fabricmc/fabric/api/gametest/v1/package-info.java +++ b/fabric-gametest-api-v1/src/main/java/net/fabricmc/fabric/api/gametest/v1/package-info.java @@ -33,8 +33,8 @@ *

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 @@ *

Example of a test method: *

{@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 @@
  * }
  * }
* - * @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; diff --git a/fabric-gametest-api-v1/src/main/java/net/fabricmc/fabric/impl/gametest/FabricGameTestHelper.java b/fabric-gametest-api-v1/src/main/java/net/fabricmc/fabric/impl/gametest/FabricGameTestHelper.java deleted file mode 100644 index caf5ebaa07..0000000000 --- a/fabric-gametest-api-v1/src/main/java/net/fabricmc/fabric/impl/gametest/FabricGameTestHelper.java +++ /dev/null @@ -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. - * - *

When {@link EnvType#CLIENT} the default value is true. - * - *

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 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 getTestFunctions() { - return TestFunctions.getTestFunctions(); - } -} diff --git a/fabric-gametest-api-v1/src/main/java/net/fabricmc/fabric/impl/gametest/FabricGameTestModInitializer.java b/fabric-gametest-api-v1/src/main/java/net/fabricmc/fabric/impl/gametest/FabricGameTestModInitializer.java index ba1c6c6bef..4a031522e5 100644 --- a/fabric-gametest-api-v1/src/main/java/net/fabricmc/fabric/impl/gametest/FabricGameTestModInitializer.java +++ b/fabric-gametest-api-v1/src/main/java/net/fabricmc/fabric/impl/gametest/FabricGameTestModInitializer.java @@ -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, 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> entrypointContainers = FabricLoader.getInstance() - .getEntrypointContainers(ENTRYPOINT_KEY, Object.class); - - for (EntrypointContainer 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); + 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; + } - 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> registriesList) { + Map>, 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 testInstances = (Registry) registries.get(RegistryKeys.TEST_INSTANCE); + Registry testEnvironmentDefinitionRegistry = (Registry) Objects.requireNonNull(registries.get(RegistryKeys.TEST_ENVIRONMENT)); + + for (TestAnnotationLocator.TestMethod testMethod : locator.getTestMethods()) { + TestInstance testInstance = testMethod.testInstance(testEnvironmentDefinitionRegistry); + Registry.register(testInstances, testMethod.identifier(), testInstance); + } } } diff --git a/fabric-gametest-api-v1/src/main/java/net/fabricmc/fabric/impl/gametest/FabricGameTestRunner.java b/fabric-gametest-api-v1/src/main/java/net/fabricmc/fabric/impl/gametest/FabricGameTestRunner.java new file mode 100644 index 0000000000..2434f80bb3 --- /dev/null +++ b/fabric-gametest-api-v1/src/main/java/net/fabricmc/fabric/impl/gametest/FabricGameTestRunner.java @@ -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 filter = Optional.ofNullable(System.getProperty(GameTestSystemProperties.FILTER)); + boolean verify = Boolean.getBoolean(GameTestSystemProperties.VERIFY); + MinecraftServer.startServer((thread) -> TestServer.create(thread, session, resourcePackManager, filter, verify)); + } +} diff --git a/fabric-gametest-api-v1/src/main/java/net/fabricmc/fabric/impl/gametest/GameTestSystemProperties.java b/fabric-gametest-api-v1/src/main/java/net/fabricmc/fabric/impl/gametest/GameTestSystemProperties.java new file mode 100644 index 0000000000..96f05caa16 --- /dev/null +++ b/fabric-gametest-api-v1/src/main/java/net/fabricmc/fabric/impl/gametest/GameTestSystemProperties.java @@ -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() { + } +} diff --git a/fabric-gametest-api-v1/src/main/java/net/fabricmc/fabric/impl/gametest/TestAnnotationLocator.java b/fabric-gametest-api-v1/src/main/java/net/fabricmc/fabric/impl/gametest/TestAnnotationLocator.java new file mode 100644 index 0000000000..8dcbe86321 --- /dev/null +++ b/fabric-gametest-api-v1/src/main/java/net/fabricmc/fabric/impl/gametest/TestAnnotationLocator.java @@ -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 testMethods = null; + + TestAnnotationLocator(FabricLoader fabricLoader) { + this.fabricLoader = fabricLoader; + } + + public List getTestMethods() { + if (testMethods != null) { + return testMethods; + } + + List> entrypointContainers = fabricLoader + .getEntrypointContainers(ENTRYPOINT_KEY, Object.class); + + return testMethods = entrypointContainers.stream() + .flatMap(entrypoint -> findMagicMethods(entrypoint).stream()) + .toList(); + } + + private List findMagicMethods(EntrypointContainer entrypoint) { + Class testClass = entrypoint.getEntrypoint().getClass(); + List 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 entrypoint, Class testClass, List 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 entrypoint) { + Identifier identifier() { + String name = camelToSnake(entrypoint.getEntrypoint().getClass().getSimpleName() + "_" + method.getName()); + return Identifier.of(entrypoint.getProvider().getMetadata().getId(), name); + } + + Consumer testFunction() { + return context -> { + try { + method.invoke(entrypoint.getEntrypoint(), context); + } catch (ReflectiveOperationException e) { + throw new RuntimeException("Failed to invoke test method", e); + } + }; + } + + TestData> testData(Registry testEnvironmentDefinitionRegistry) { + RegistryEntry 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 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(); + } + } +} diff --git a/fabric-gametest-api-v1/src/main/java/net/fabricmc/fabric/mixin/gametest/ArgumentTypesMixin.java b/fabric-gametest-api-v1/src/main/java/net/fabricmc/fabric/mixin/gametest/ArgumentTypesMixin.java deleted file mode 100644 index d760aa5a63..0000000000 --- a/fabric-gametest-api-v1/src/main/java/net/fabricmc/fabric/mixin/gametest/ArgumentTypesMixin.java +++ /dev/null @@ -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 , T extends ArgumentSerializer.ArgumentTypeProperties> ArgumentSerializer register(Registry> registry, String string, Class clazz, ArgumentSerializer 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> registry, CallbackInfoReturnable> 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)); - } - } -} diff --git a/fabric-gametest-api-v1/src/main/java/net/fabricmc/fabric/mixin/gametest/CommandManagerMixin.java b/fabric-gametest-api-v1/src/main/java/net/fabricmc/fabric/mixin/gametest/CommandManagerMixin.java deleted file mode 100644 index d0a1263bbd..0000000000 --- a/fabric-gametest-api-v1/src/main/java/net/fabricmc/fabric/mixin/gametest/CommandManagerMixin.java +++ /dev/null @@ -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 dispatcher; - - @Inject(method = "", 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); - } - } -} diff --git a/fabric-gametest-api-v1/src/main/java/net/fabricmc/fabric/mixin/gametest/MinecraftServerMixin.java b/fabric-gametest-api-v1/src/main/java/net/fabricmc/fabric/mixin/gametest/MinecraftServerMixin.java deleted file mode 100644 index 4f469cbf3a..0000000000 --- a/fabric-gametest-api-v1/src/main/java/net/fabricmc/fabric/mixin/gametest/MinecraftServerMixin.java +++ /dev/null @@ -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(); - } - } -} diff --git a/fabric-gametest-api-v1/src/main/java/net/fabricmc/fabric/mixin/gametest/RegistryLoaderMixin.java b/fabric-gametest-api-v1/src/main/java/net/fabricmc/fabric/mixin/gametest/RegistryLoaderMixin.java new file mode 100644 index 0000000000..43c3da98b9 --- /dev/null +++ b/fabric-gametest-api-v1/src/main/java/net/fabricmc/fabric/mixin/gametest/RegistryLoaderMixin.java @@ -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> registries, List> entries, CallbackInfoReturnable 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> wrappers, List> entries, CallbackInfoReturnable cir, @Local(ordinal = 2) List> registriesList) { + if (LOADING_DYNAMIC_REGISTRIES.getAndSet(false)) { + FabricGameTestModInitializer.registerDynamicEntries(registriesList); + } + } +} diff --git a/fabric-gametest-api-v1/src/main/java/net/fabricmc/fabric/mixin/gametest/StructureTemplateManagerMixin.java b/fabric-gametest-api-v1/src/main/java/net/fabricmc/fabric/mixin/gametest/StructureTemplateManagerMixin.java index 73969e4e5a..0d6a0c54f5 100644 --- a/fabric-gametest-api-v1/src/main/java/net/fabricmc/fabric/mixin/gametest/StructureTemplateManagerMixin.java +++ b/fabric-gametest-api-v1/src/main/java/net/fabricmc/fabric/mixin/gametest/StructureTemplateManagerMixin.java @@ -43,7 +43,7 @@ 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 fabric_loadSnbtFromResource(Identifier id) { - Identifier path = FabricGameTestHelper.GAMETEST_STRUCTURE_FINDER.toResourcePath(id); + Identifier path = FabricGameTestRunner.GAMETEST_STRUCTURE_FINDER.toResourcePath(id); Optional resource = this.resourceManager.getResource(path); if (resource.isPresent()) { @@ -71,7 +71,7 @@ private Optional fabric_loadSnbtFromResource(Identifier id) { } private Stream fabric_streamTemplatesFromResource() { - ResourceFinder finder = FabricGameTestHelper.GAMETEST_STRUCTURE_FINDER; + ResourceFinder finder = FabricGameTestRunner.GAMETEST_STRUCTURE_FINDER; return finder.findResources(this.resourceManager).keySet().stream().map(finder::toResourceId); } diff --git a/fabric-gametest-api-v1/src/main/java/net/fabricmc/fabric/mixin/gametest/TestCommandMixin.java b/fabric-gametest-api-v1/src/main/java/net/fabricmc/fabric/mixin/gametest/TestCommandMixin.java deleted file mode 100644 index 44437616b5..0000000000 --- a/fabric-gametest-api-v1/src/main/java/net/fabricmc/fabric/mixin/gametest/TestCommandMixin.java +++ /dev/null @@ -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; - } -} diff --git a/fabric-gametest-api-v1/src/main/java/net/fabricmc/fabric/mixin/gametest/TestFunctionsMixin.java b/fabric-gametest-api-v1/src/main/java/net/fabricmc/fabric/mixin/gametest/TestFunctionsMixin.java deleted file mode 100644 index 2d18c6228e..0000000000 --- a/fabric-gametest-api-v1/src/main/java/net/fabricmc/fabric/mixin/gametest/TestFunctionsMixin.java +++ /dev/null @@ -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 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); - } -} diff --git a/fabric-gametest-api-v1/src/main/java/net/fabricmc/fabric/mixin/gametest/server/MainMixin.java b/fabric-gametest-api-v1/src/main/java/net/fabricmc/fabric/mixin/gametest/server/MainMixin.java index d94c78f657..1241ad3c87 100644 --- a/fabric-gametest-api-v1/src/main/java/net/fabricmc/fabric/mixin/gametest/server/MainMixin.java +++ b/fabric-gametest-api-v1/src/main/java/net/fabricmc/fabric/mixin/gametest/server/MainMixin.java @@ -27,20 +27,20 @@ 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 @@ private static void main(String[] args, CallbackInfo info, @Local LevelStorage.S // 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); } } diff --git a/fabric-gametest-api-v1/src/main/resources/fabric-gametest-api-v1.accesswidener b/fabric-gametest-api-v1/src/main/resources/fabric-gametest-api-v1.accesswidener index eb7f15b89d..753b71b7c0 100644 --- a/fabric-gametest-api-v1/src/main/resources/fabric-gametest-api-v1.accesswidener +++ b/fabric-gametest-api-v1/src/main/resources/fabric-gametest-api-v1.accesswidener @@ -2,3 +2,5 @@ accessWidener v2 named accessible class net/minecraft/structure/StructureTemplateManager$Provider accessible method net/minecraft/structure/StructureTemplateManager$Provider (Ljava/util/function/Function;Ljava/util/function/Supplier;)V + +accessible class net/minecraft/registry/RegistryLoader$Loader diff --git a/fabric-gametest-api-v1/src/main/resources/fabric-gametest-api-v1.mixins.json b/fabric-gametest-api-v1/src/main/resources/fabric-gametest-api-v1.mixins.json index 2f52ab6dd5..01aad9a759 100644 --- a/fabric-gametest-api-v1/src/main/resources/fabric-gametest-api-v1.mixins.json +++ b/fabric-gametest-api-v1/src/main/resources/fabric-gametest-api-v1.mixins.json @@ -3,12 +3,8 @@ "package": "net.fabricmc.fabric.mixin.gametest", "compatibilityLevel": "JAVA_21", "mixins": [ - "ArgumentTypesMixin", - "CommandManagerMixin", - "MinecraftServerMixin", + "RegistryLoaderMixin", "StructureTemplateManagerMixin", - "TestCommandMixin", - "TestFunctionsMixin", "TestServerMixin" ], "server": [ diff --git a/fabric-gametest-api-v1/src/testmod/java/net/fabricmc/fabric/test/gametest/ExampleFabricTestSuite.java b/fabric-gametest-api-v1/src/testmod/java/net/fabricmc/fabric/test/gametest/ExampleFabricTestSuite.java index e78bdd2348..a64b81a70b 100644 --- a/fabric-gametest-api-v1/src/testmod/java/net/fabricmc/fabric/test/gametest/ExampleFabricTestSuite.java +++ b/fabric-gametest-api-v1/src/testmod/java/net/fabricmc/fabric/test/gametest/ExampleFabricTestSuite.java @@ -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; - -// 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); - } +import net.fabricmc.fabric.api.gametest.v1.GameTest; - private void afterEach(TestContext context) { - context.addInstantFinalTask(() -> - context.checkBlock(new BlockPos(0, 1, 0), (block) -> block == Blocks.DIAMOND_BLOCK, "Expect block to be diamond") - ); - } - - @GameTest(templateName = "fabric-gametest-api-v1-testmod:exampletestsuite.diamond") +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, (b) -> Text.literal("Expect block to be diamond")) + ); } - @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")) + ); } } diff --git a/fabric-gametest-api-v1/src/testmod/java/net/fabricmc/fabric/test/gametest/ExampleTestSuite.java b/fabric-gametest-api-v1/src/testmod/java/net/fabricmc/fabric/test/gametest/ExampleTestSuite.java deleted file mode 100644 index b7b6184dbc..0000000000 --- a/fabric-gametest-api-v1/src/testmod/java/net/fabricmc/fabric/test/gametest/ExampleTestSuite.java +++ /dev/null @@ -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") - ); - } -} diff --git a/fabric-gametest-api-v1/src/testmod/resources/fabric.mod.json b/fabric-gametest-api-v1/src/testmod/resources/fabric.mod.json index 078fb5752c..5fedcedcf0 100644 --- a/fabric-gametest-api-v1/src/testmod/resources/fabric.mod.json +++ b/fabric-gametest-api-v1/src/testmod/resources/fabric.mod.json @@ -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" ] } } diff --git a/fabric-item-api-v1/src/testmod/java/net/fabricmc/fabric/test/item/gametest/BrewingStandGameTest.java b/fabric-item-api-v1/src/testmod/java/net/fabricmc/fabric/test/item/gametest/BrewingStandGameTest.java index 5dad24735b..75581077e5 100644 --- a/fabric-item-api-v1/src/testmod/java/net/fabricmc/fabric/test/item/gametest/BrewingStandGameTest.java +++ b/fabric-item-api-v1/src/testmod/java/net/fabricmc/fabric/test/item/gametest/BrewingStandGameTest.java @@ -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 void basicBrewing(TestContext context) { 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 void vanillaRemainderTest(TestContext context) { // 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 @@ private static ItemStack setPotion(ItemStack itemStack, RegistryEntry po itemStack.set(DataComponentTypes.POTION_CONTENTS, new PotionContentsComponent(potion)); return itemStack; } - - */ } diff --git a/fabric-item-api-v1/src/testmod/java/net/fabricmc/fabric/test/item/gametest/CustomEnchantmentEffectsGameTest.java b/fabric-item-api-v1/src/testmod/java/net/fabricmc/fabric/test/item/gametest/CustomEnchantmentEffectsGameTest.java index d2f08330b0..6b56a30332 100644 --- a/fabric-item-api-v1/src/testmod/java/net/fabricmc/fabric/test/item/gametest/CustomEnchantmentEffectsGameTest.java +++ b/fabric-item-api-v1/src/testmod/java/net/fabricmc/fabric/test/item/gametest/CustomEnchantmentEffectsGameTest.java @@ -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 void weirdImpalingSetsFireToTargets(TestContext context) { Optional> 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 void weirdImpalingSetsFireToTargets(TestContext context) { 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> damageEffects = impaling @@ -53,7 +78,7 @@ public void weirdImpalingHasTwoDamageEffects(TestContext context) { 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 @@ private static Registry getEnchantmentRegistry(TestContext context) DynamicRegistryManager registryManager = context.getWorld().getRegistryManager(); return registryManager.getOrThrow(RegistryKeys.ENCHANTMENT); } - - */ } diff --git a/fabric-item-api-v1/src/testmod/java/net/fabricmc/fabric/test/item/gametest/DefaultItemComponentGameTest.java b/fabric-item-api-v1/src/testmod/java/net/fabricmc/fabric/test/item/gametest/DefaultItemComponentGameTest.java index c2033721fa..4b42200da5 100644 --- a/fabric-item-api-v1/src/testmod/java/net/fabricmc/fabric/test/item/gametest/DefaultItemComponentGameTest.java +++ b/fabric-item-api-v1/src/testmod/java/net/fabricmc/fabric/test/item/gametest/DefaultItemComponentGameTest.java @@ -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 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 void modify(TestContext context) { 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 void diamondPickaxeIsRenamed(TestContext context) { 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(); } - - */ } diff --git a/fabric-item-api-v1/src/testmod/java/net/fabricmc/fabric/test/item/gametest/FurnaceGameTest.java b/fabric-item-api-v1/src/testmod/java/net/fabricmc/fabric/test/item/gametest/FurnaceGameTest.java index 5f445c8e09..ab6ec04fc2 100644 --- a/fabric-item-api-v1/src/testmod/java/net/fabricmc/fabric/test/item/gametest/FurnaceGameTest.java +++ b/fabric-item-api-v1/src/testmod/java/net/fabricmc/fabric/test/item/gametest/FurnaceGameTest.java @@ -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 void basicSmelt(TestContext context) { 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 void vanillaRemainderTest(TestContext context) { 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 void fabricRemainderTest(TestContext context) { context.complete(); } - */ - private void setInputs(FurnaceBlockEntity blockEntity, ItemStack ingredient, ItemStack fuel) { blockEntity.setStack(0, ingredient); blockEntity.setStack(1, fuel); @@ -105,8 +107,7 @@ private void assertInventory(FurnaceBlockEntity blockEntity, String extraErrorIn 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); } } diff --git a/fabric-item-api-v1/src/testmod/java/net/fabricmc/fabric/test/item/gametest/RecipeGameTest.java b/fabric-item-api-v1/src/testmod/java/net/fabricmc/fabric/test/item/gametest/RecipeGameTest.java index 22ec1f36f6..1e4f8cb588 100644 --- a/fabric-item-api-v1/src/testmod/java/net/fabricmc/fabric/test/item/gametest/RecipeGameTest.java +++ b/fabric-item-api-v1/src/testmod/java/net/fabricmc/fabric/test/item/gametest/RecipeGameTest.java @@ -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 void vanillaRemainderTest(TestContext context) { 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 @@ static void assertStacks(ItemStack currentStack, ItemStack expectedStack, String } 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 @@ static ItemStack withDamage(ItemStack stack, int damage) { stack.setDamage(damage); return stack; } - - */ } diff --git a/fabric-object-builder-api-v1/src/testmod/java/net/fabricmc/fabric/test/object/builder/ObjectBuilderGameTest.java b/fabric-object-builder-api-v1/src/testmod/java/net/fabricmc/fabric/test/object/builder/ObjectBuilderGameTest.java index 42cbf1e0ba..21e26221de 100644 --- a/fabric-object-builder-api-v1/src/testmod/java/net/fabricmc/fabric/test/object/builder/ObjectBuilderGameTest.java +++ b/fabric-object-builder-api-v1/src/testmod/java/net/fabricmc/fabric/test/object/builder/ObjectBuilderGameTest.java @@ -22,8 +22,10 @@ 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 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(); diff --git a/fabric-recipe-api-v1/src/testmod/java/net/fabricmc/fabric/test/recipe/ingredient/ShapelessRecipeMatchTests.java b/fabric-recipe-api-v1/src/testmod/java/net/fabricmc/fabric/test/recipe/ingredient/ShapelessRecipeMatchTests.java index e4233fcafa..3a7f100b02 100644 --- a/fabric-recipe-api-v1/src/testmod/java/net/fabricmc/fabric/test/recipe/ingredient/ShapelessRecipeMatchTests.java +++ b/fabric-recipe-api-v1/src/testmod/java/net/fabricmc/fabric/test/recipe/ingredient/ShapelessRecipeMatchTests.java @@ -32,11 +32,13 @@ 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> 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(); diff --git a/fabric-recipe-api-v1/src/testmod/resources/fabric.mod.json b/fabric-recipe-api-v1/src/testmod/resources/fabric.mod.json index 04fcaf6644..ff5470aa36 100644 --- a/fabric-recipe-api-v1/src/testmod/resources/fabric.mod.json +++ b/fabric-recipe-api-v1/src/testmod/resources/fabric.mod.json @@ -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" ] } diff --git a/fabric-resource-conditions-api-v1/src/testmod/java/net/fabricmc/fabric/test/resource/conditions/ConditionalResourcesTest.java b/fabric-resource-conditions-api-v1/src/testmod/java/net/fabricmc/fabric/test/resource/conditions/ConditionalResourcesTest.java index 5a349b5c6d..10c4369d0d 100644 --- a/fabric-resource-conditions-api-v1/src/testmod/java/net/fabricmc/fabric/test/resource/conditions/ConditionalResourcesTest.java +++ b/fabric-resource-conditions-api-v1/src/testmod/java/net/fabricmc/fabric/test/resource/conditions/ConditionalResourcesTest.java @@ -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 @@ private static Identifier id(String path) { 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 void conditionalRecipes(TestContext context) { 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 void conditionalPredicates(TestContext context) { 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 void conditionalLootTables(TestContext context) { context.complete(); } - @GameTest(templateName = FabricGameTest.EMPTY_STRUCTURE) + @GameTest public void conditionalDynamicRegistry(TestContext context) { Registry registry = context.getWorld().getRegistryManager().getOrThrow(RegistryKeys.BANNER_PATTERN); @@ -112,7 +122,7 @@ public void conditionalDynamicRegistry(TestContext context) { 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 void conditionalOverlays(TestContext context) { context.complete(); } - - */ } diff --git a/fabric-resource-conditions-api-v1/src/testmod/java/net/fabricmc/fabric/test/resource/conditions/DefaultResourceConditionsTest.java b/fabric-resource-conditions-api-v1/src/testmod/java/net/fabricmc/fabric/test/resource/conditions/DefaultResourceConditionsTest.java index 1baba4530b..31884f30f6 100644 --- a/fabric-resource-conditions-api-v1/src/testmod/java/net/fabricmc/fabric/test/resource/conditions/DefaultResourceConditionsTest.java +++ b/fabric-resource-conditions-api-v1/src/testmod/java/net/fabricmc/fabric/test/resource/conditions/DefaultResourceConditionsTest.java @@ -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 @@ private void expectCondition(TestContext context, String name, ResourceCondition 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 void featuresEnabled(TestContext context) { 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 void registryContains(TestContext context) { 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 void tagsPopulated(TestContext context) { context.complete(); } - - */ } diff --git a/fabric-resource-loader-v0/src/main/java/net/fabricmc/fabric/impl/resource/loader/ModNioResourcePack.java b/fabric-resource-loader-v0/src/main/java/net/fabricmc/fabric/impl/resource/loader/ModNioResourcePack.java index 286ea53c95..428a9faeb5 100644 --- a/fabric-resource-loader-v0/src/main/java/net/fabricmc/fabric/impl/resource/loader/ModNioResourcePack.java +++ b/fabric-resource-loader-v0/src/main/java/net/fabricmc/fabric/impl/resource/loader/ModNioResourcePack.java @@ -51,7 +51,7 @@ 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; diff --git a/fabric-transfer-api-v1/src/testmod/java/net/fabricmc/fabric/test/transfer/gametests/VanillaStorageTests.java b/fabric-transfer-api-v1/src/testmod/java/net/fabricmc/fabric/test/transfer/gametests/VanillaStorageTests.java index 05ead61949..43d0fd016c 100644 --- a/fabric-transfer-api-v1/src/testmod/java/net/fabricmc/fabric/test/transfer/gametests/VanillaStorageTests.java +++ b/fabric-transfer-api-v1/src/testmod/java/net/fabricmc/fabric/test/transfer/gametests/VanillaStorageTests.java @@ -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 void testFurnaceCookTime(TestContext context) { 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 void testComparatorOnInventory(TestContext context, Block block, ItemVariant variant, Class 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 @@ private static void testComparatorOnInventory(TestContext context, Block block, 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 double chest wrapper only updating modified halves. */ - /* - @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 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 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 void testDoubleChestComparator(TestContext context) { 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 composters not always incrementing their level on the first insert. */ - /* - @GameTest(templateName = FabricGameTest.EMPTY_STRUCTURE) + @GameTest public void testComposterFirstInsert(TestContext context) { BlockPos pos = new BlockPos(0, 1, 0); @@ -345,25 +352,22 @@ public void testComposterFirstInsert(TestContext context) { 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 jukeboxes having their state changed mid-transaction. */ - /* - @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 void testJukeboxState(TestContext context) { 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(); } - - */ } diff --git a/fabric-transfer-api-v1/src/testmod/java/net/fabricmc/fabric/test/transfer/gametests/WorldDependentAttributesTest.java b/fabric-transfer-api-v1/src/testmod/java/net/fabricmc/fabric/test/transfer/gametests/WorldDependentAttributesTest.java index 76efe44eb6..4dbc32a9d7 100644 --- a/fabric-transfer-api-v1/src/testmod/java/net/fabricmc/fabric/test/transfer/gametests/WorldDependentAttributesTest.java +++ b/fabric-transfer-api-v1/src/testmod/java/net/fabricmc/fabric/test/transfer/gametests/WorldDependentAttributesTest.java @@ -22,12 +22,13 @@ 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); diff --git a/gradle.properties b/gradle.properties index 15e63872b0..fbcd1f8d37 100644 --- a/gradle.properties +++ b/gradle.properties @@ -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 diff --git a/settings.gradle b/settings.gradle index 6b16a771d4..5dfaff9c31 100644 --- a/settings.gradle +++ b/settings.gradle @@ -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'