Skip to content

Commit

Permalink
Use the gametest framework and add a few tests (gnembon#320)
Browse files Browse the repository at this point in the history
Includes making `/test export` export to the right place for Carpet extra
  • Loading branch information
altrisi authored Jun 26, 2024
1 parent 2cdb697 commit 0a6c551
Show file tree
Hide file tree
Showing 8 changed files with 458 additions and 6 deletions.
48 changes: 42 additions & 6 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,10 @@ repositories {
base {
archivesName = project.archives_base_name
}

version = project.minecraft_version+'-'+project.mod_version
group = project.maven_group

loom {
}

dependencies {
//to change the versions see the gradle.properties file
Expand All @@ -25,11 +24,48 @@ dependencies {
modImplementation "net.fabricmc:fabric-loader:${project.loader_version}"
modImplementation "carpet:fabric-carpet:${project.minecraft_version}-${project.carpet_core_version}"

// Fabric API. This is technically optional, but you probably want it anyway.
// modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}"
// for gametests
modRuntimeOnly fabricApi.module("fabric-resource-loader-v0", project.fabric_api_version)
modRuntimeOnly fabricApi.module("fabric-api-base", project.fabric_api_version)
modRuntimeOnly fabricApi.module("fabric-gametest-api-v1", project.fabric_api_version)
}

sourceSets {
gametest {
java {
compileClasspath += main.output
compileClasspath += main.compileClasspath
runtimeClasspath += main.output
runtimeClasspath += main.runtimeClasspath
}
}
}

// PSA: Some older mods, compiled on Loom 0.2.1, might have outdated Maven POMs.
// You may need to force-disable transitiveness on them.
loom {
runs {
gametest {
inherit server
name "Game Test"
vmArg "-Dfabric-api.gametest"
//vmArg "-Dmixin.debug.countInjections=true" // gnembon/fabric-carpet#1938
vmArg "-Dfabric-api.gametest.report-file=${project.buildDir}/junit.xml"
source sourceSets.gametest
runDir "build/gametest"
ideConfigGenerated = false
}
}
runConfigs.configureEach {
// to be able to create and run gametests on regular debug
source sourceSets.gametest
}
mods {
"carpet-extra" {
sourceSet sourceSets.main
}
"carpet-extra-gametest" {
sourceSet sourceSets.gametest
}
}
}

processResources {
Expand Down
2 changes: 2 additions & 0 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ org.gradle.jvmargs=-Xmx1G
loader_version=0.15.11
# check available versions on maven (https://masa.dy.fi/maven/carpet/fabric-carpet/) for the given minecraft version you are using
carpet_core_version=1.4.147+v240613
# for gametests
fabric_api_version=0.100.3+1.21

# Mod Properties
mod_version = 1.4.147
Expand Down
11 changes: 11 additions & 0 deletions src/gametest/java/carpetextra/ExportPathChanger.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package carpetextra;

import net.fabricmc.api.ModInitializer;
import net.minecraft.test.StructureTestUtil;

public class ExportPathChanger implements ModInitializer {
@Override
public void onInitialize() {
StructureTestUtil.testStructuresDirectoryName = "../src/gametest/resources/data/carpet-extra/gametest/structure";
}
}
257 changes: 257 additions & 0 deletions src/gametest/java/carpetextra/test/DispenserWithBlock.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,257 @@
package carpetextra.test;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;

import carpetextra.CarpetExtraSettings;
import carpetextra.mixins.AxeItem_StrippedBlocksAccessorMixin;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.block.LeveledCauldronBlock;
import net.minecraft.block.entity.DispenserBlockEntity;
import net.minecraft.component.type.PotionContentsComponent;
import net.minecraft.entity.EntityType;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.potion.Potions;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.test.AfterBatch;
import net.minecraft.test.BeforeBatch;
import net.minecraft.test.CustomTestProvider;
import net.minecraft.test.GameTest;
import net.minecraft.test.TestContext;
import net.minecraft.test.TestFunction;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;

public class DispenserWithBlock {
static final String STRUCTURE = "carpet-extra:dispenserbase";
static final String BATCH = "dispenserwithblock";
static final int DISPENSER_DELAY = 4;
BlockPos lapis = new BlockPos(2, 1, 0);
BlockPos button = new BlockPos(0, 2, 0);
BlockPos dispenser = new BlockPos(1, 2, 0);

@BeforeBatch(batchId = BATCH)
public void before(ServerWorld world) {
CarpetExtraSettings.dispensersFillMinecarts = true;
CarpetExtraSettings.dispensersCarvePumpkins = true;
CarpetExtraSettings.dispensersStripBlocks = true;
CarpetExtraSettings.dispensersTillSoil = true;
CarpetExtraSettings.dispensersUseCauldrons = true;
}

@AfterBatch(batchId = BATCH)
public void after(ServerWorld world) {
CarpetExtraSettings.dispensersFillMinecarts = false;
CarpetExtraSettings.dispensersCarvePumpkins = false;
CarpetExtraSettings.dispensersStripBlocks = false;
CarpetExtraSettings.dispensersTillSoil = false;
CarpetExtraSettings.dispensersUseCauldrons = false;
}

@GameTest(templateName = STRUCTURE, batchId = BATCH)
public void shearPumpkin(TestContext ctx) {
blockConversionTest(ctx, Items.SHEARS, Blocks.PUMPKIN, Blocks.CARVED_PUMPKIN, 1, false, () -> ctx.expectItem(Items.PUMPKIN_SEEDS));
}

@GameTest(templateName = STRUCTURE, batchId = BATCH)
public void shearPumpkinBreaks(TestContext ctx) {
blockConversionTest(ctx, Items.SHEARS, Blocks.PUMPKIN, Blocks.CARVED_PUMPKIN, 1, true, () -> ctx.expectItem(Items.PUMPKIN_SEEDS));
}

@CustomTestProvider
public Collection<TestFunction> stripTests() {
List<TestFunction> fns = new ArrayList<>();
Map<Block, Block> conversions = AxeItem_StrippedBlocksAccessorMixin.getStrippedBlocks();

for (Map.Entry<Block, Block> entry : conversions.entrySet()) {
fns.add(makeDispenserTest("strip_" + entry.getKey().asItem(), (ctx) -> {
stripTest(ctx, Items.IRON_AXE, entry.getKey(), entry.getValue());
}));
}
for (Item tool : List.of(Items.WOODEN_AXE, Items.STONE_AXE, Items.GOLDEN_AXE, Items.IRON_AXE, Items.DIAMOND_AXE, Items.NETHERITE_AXE)) {
fns.add(makeDispenserTest("stripwith" + tool, (ctx) -> {
stripTest(ctx, tool, Blocks.OAK_LOG, Blocks.STRIPPED_OAK_LOG);
}));
}
// Copper. There's no way I'm adding all the coppers
fns.add(makeDispenserTest("stripDeoxidateCopper", (ctx) -> {
stripTest(ctx, Items.IRON_AXE, Blocks.WEATHERED_COPPER, Blocks.EXPOSED_COPPER);
}));
fns.add(makeDispenserTest("stripUnwaxCopper", (ctx) -> {
stripTest(ctx, Items.IRON_AXE, Blocks.WAXED_COPPER_BLOCK, Blocks.COPPER_BLOCK);
}));

return fns;
}

private void stripTest(TestContext ctx, Item tool, Block blockFrom, Block blockTo) {
blockConversionTest(ctx, tool, blockFrom, blockTo, 1, false);
}

@CustomTestProvider
public Collection<TestFunction> cauldronTests() {
List<TestFunction> fns = new ArrayList<>();
for (int i = 0; i < 2; i++) { // 3 is handled separately
for (int off = -1; off <= 1; off += 2) {
int finalOff = off;
int adjustedI = off > 0 ? i : i + 1;
fns.add(makeDispenserTest("cauldronBottleLvl" + adjustedI + (off > 0 ? "Fill" : "Empty"), ctx -> {
putInDispenser(ctx, finalOff > 0 ? waterBottle() : Items.GLASS_BOTTLE.getDefaultStack());
ctx.setBlockState(lapis.up(), waterCauldronFor(ctx, adjustedI));
BlockPos referencePos = dispenser.down();
ctx.setBlockState(referencePos, waterCauldronFor(ctx, adjustedI + finalOff));

ctx.pushButton(button);

ctx.addFinalTaskWithDuration(DISPENSER_DELAY, () -> {
ctx.expectSameStates(lapis.up(), referencePos);
checkFirstSlotHas(ctx, finalOff > 0 ? Items.GLASS_BOTTLE : Items.POTION, false);
});
}));
}
}
// TODO powdered snow
for (Map.Entry<Item, Block> entry : Map.of(Items.LAVA_BUCKET, Blocks.LAVA_CAULDRON
//Items.POWDER_SNOW_BUCKET, Blocks.POWDER_SNOW_CAULDRON
).entrySet()) {
fns.add(makeDispenserTest("cauldronFillWith" + entry.getKey(), (ctx) -> {
blockConversionTest(ctx, entry.getKey(), Blocks.CAULDRON, entry.getValue(), 1, false, () -> {
checkFirstSlotHas(ctx, Items.BUCKET, false);
});
}));
fns.add(makeDispenserTest("cauldronEmptyTo" + entry.getKey(), (ctx) -> {
blockConversionTest(ctx, Items.BUCKET, entry.getValue(), Blocks.CAULDRON, 1, false, () -> {
checkFirstSlotHas(ctx, entry.getKey(), false);
});
}));
}
return fns;
}

private static ItemStack waterBottle() {
return PotionContentsComponent.createStack(Items.POTION, Potions.WATER);
}

private BlockState waterCauldronFor(TestContext ctx, int level) {
if (level == 0) {
return Blocks.CAULDRON.getDefaultState();
}
return Blocks.WATER_CAULDRON.getDefaultState().with(LeveledCauldronBlock.LEVEL, level);
}

@CustomTestProvider
public Collection<TestFunction> tillTests() {
List<TestFunction> fns = new ArrayList<>();
fns.add(makeDispenserTest("tillDirtAbove", (ctx) -> {
blockConversionTest(ctx, Items.IRON_HOE, Blocks.DIRT, Blocks.FARMLAND, 1, false);
}));
fns.add(makeDispenserTest("tillGrass", (ctx) -> {
blockConversionTest(ctx, Items.IRON_HOE, Blocks.GRASS_BLOCK, Blocks.FARMLAND, 0, false);
}));
fns.add(makeDispenserTest("tillCheckDurability", (ctx) -> {
blockConversionTest(ctx, Items.IRON_HOE, Blocks.DIRT, Blocks.FARMLAND, 0, true);
}));
fns.add(makeDispenserTest("tillCoarseDirt", (ctx) -> {
blockConversionTest(ctx, Items.IRON_HOE, Blocks.COARSE_DIRT, Blocks.DIRT, 0, false);
}));
fns.add(makeDispenserTest("tillRootedDirt", (ctx) -> {
blockConversionTest(ctx, Items.IRON_HOE, Blocks.ROOTED_DIRT, Blocks.DIRT, 0, false, () -> ctx.expectItem(Items.HANGING_ROOTS));
}));

for (Item hoe : List.of(Items.WOODEN_HOE, Items.STONE_HOE, Items.GOLDEN_HOE, Items.IRON_HOE, Items.DIAMOND_HOE, Items.NETHERITE_HOE)) {
fns.add(makeDispenserTest("tillDirtWith" + hoe, (ctx) -> {
blockConversionTest(ctx, hoe, Blocks.DIRT, Blocks.FARMLAND, 0, false);
}));
}

return fns;
}

private void blockConversionTest(TestContext ctx, Item tool, Block from, Block to, int offset, boolean putDamaged, Runnable... extras) {
if (putDamaged) {
putAtOneDurability(ctx, tool);
} else {
putInDispenser(ctx, tool.getDefaultStack());
}
ctx.setBlockState(lapis.offset(Direction.UP, offset), from);
ctx.pushButton(button);

ctx.addFinalTaskWithDuration(DISPENSER_DELAY, () -> {
ctx.expectBlock(to, lapis.offset(Direction.UP, offset));
if (putDamaged) {
ctx.expectEmptyContainer(dispenser);
} else if (tool.getDefaultStack().isDamageable()) { // otherwise we expect extras to handle it
checkFirstSlotHas(ctx, tool, true);
}
runAll(extras);
});
}

@GameTest(templateName = STRUCTURE, batchId = BATCH)
public void fillMinecartChest(TestContext ctx) {
cartTest(ctx, Items.CHEST, EntityType.CHEST_MINECART);
}

@GameTest(templateName = STRUCTURE, batchId = BATCH)
public void fillMinecartHopper(TestContext ctx) {
cartTest(ctx, Items.HOPPER, EntityType.HOPPER_MINECART);
}

@GameTest(templateName = STRUCTURE, batchId = BATCH)
public void fillMinecartTnt(TestContext ctx) {
cartTest(ctx, Items.TNT, EntityType.TNT_MINECART, () -> ctx.dontExpectEntity(EntityType.TNT));
}

@GameTest(templateName = STRUCTURE, batchId = BATCH)
public void fillMinecartFurnace(TestContext ctx) {
cartTest(ctx, Items.FURNACE, EntityType.FURNACE_MINECART);
}

private void cartTest(TestContext ctx, Item item, EntityType<?> entity, Runnable... extras) {
putInDispenser(ctx, item.getDefaultStack());
ctx.setBlockState(lapis.up(), Blocks.RAIL);
ctx.spawnEntity(EntityType.MINECART, lapis.up());

ctx.pushButton(button);
ctx.addFinalTaskWithDuration(4, () -> {
ctx.expectEntityAt(entity, lapis.up());
ctx.dontExpectEntity(EntityType.MINECART);
ctx.dontExpectEntity(EntityType.ITEM);
runAll(extras);
});
}

// Util
private void putInDispenser(TestContext ctx, ItemStack item) {
ctx.<DispenserBlockEntity>getBlockEntity(dispenser).addToFirstFreeSlot(item);
}

private void putAtOneDurability(TestContext ctx, Item item) {
ItemStack stack = item.getDefaultStack();
stack.setDamage(stack.getMaxDamage() - 1);
putInDispenser(ctx, stack);
}

private void checkFirstSlotHas(TestContext ctx, Item item, boolean damaged) {
ctx.<DispenserBlockEntity>checkBlockEntity(dispenser,
disp -> disp.getStack(0).getItem() == item && (!damaged || disp.getStack(0).isDamaged()),
() -> "Must have " + (damaged ? "damaged " : "") + item + " in dispenser");
}

private void runAll(Runnable... actions) {
for (Runnable r : actions) r.run();
}

// Setup util
private TestFunction makeDispenserTest(String name, Consumer<TestContext> runner) {
name = name.replace("minecraft\\:", "");
return new TestFunction(BATCH, BATCH + '.' + name, STRUCTURE, 20, 0, true, runner);
}
}
Loading

0 comments on commit 0a6c551

Please sign in to comment.