Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use the gametest framework and add a few tests #320

Merged
merged 5 commits into from
Jun 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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