From 7fcad62b830e4df07ad4f4d5acd05791e773937b Mon Sep 17 00:00:00 2001 From: Gabriel Harris-Rouquette Date: Thu, 22 Oct 2026 00:29:54 -0700 Subject: [PATCH 1/2] feat: add gametest demonstration Most plugins won't want to set up gametests, but for those that wish to see the basic premise, we can enable game tests out the door when a new plugin repo is created via this template. Signed-off-by: Gabriel Harris-Rouquette --- README.md | 14 ++++++++ build.gradle.kts | 10 +++++- settings.gradle.kts | 10 ++++++ .../example/ExampleGameTests.java | 32 +++++++++++++++++++ 4 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 src/test/java/org/spongepowered/example/ExampleGameTests.java diff --git a/README.md b/README.md index 4d24463..e2f2eb0 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,20 @@ A simple example plugin for Sponge, demonstrating SpongeGradle. To build a plugin, simply run `./gradlew build`. While working on your plugin, run `./gradlew runServer` to get a development server with your plugin installed! +### Gametests + +`sponge { gameTests() }` in `build.gradle.kts` runs Minecraft gametests against your plugin on a headless +SpongeVanilla server: `./gradlew test`, or the run icon beside a test in your IDE. Tests live in `src/test` and +never ship in your plugin's jar; see `ExampleGameTests` for the shape of one. + +- A test class is annotated `@GameTestGroup`, and each test is an instance method annotated `@GameTest` that takes + only a `GameTestHelper`. Tests are named `..`. +- A test ends when it calls `succeed()` or its `succeedWhen(...)` check holds; otherwise it fails once `maxTicks` + run out. +- `GameTestHelper` positions are relative to the test's own space; convert them with `absolutePos` before + passing them to the Sponge API. +- Tests in a group run at the same time on one server, so keep shared state, like static fields, out of them. + Read more about Sponge plugin development at *(docs not yet written)* Read more about Gradle from Gradle's [documentation and tutorials](https://docs.gradle.org/current/userguide/getting_started.html) diff --git a/build.gradle.kts b/build.gradle.kts index 5628266..943ff00 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,11 +2,12 @@ import org.spongepowered.gradle.plugin.config.PluginLoaders import org.spongepowered.plugin.metadata.model.PluginDependency plugins { - val spongeGradleVersion = "2.2.0" + val spongeGradleVersion = "2.4.0" `java-library` id("org.spongepowered.gradle.plugin") version spongeGradleVersion id("org.spongepowered.gradle.ore") version spongeGradleVersion // for Ore publishing + id("org.spongepowered.gradle.vanilla") version "0.2.2" // Minecraft classes for gametests } group = "org.spongepowered" @@ -18,6 +19,7 @@ repositories { sponge { apiVersion("12.0.0") + minecraftVersion("1.21.1") license("CHANGEME") loader { name(PluginLoaders.JAVA_PLAIN) @@ -40,6 +42,12 @@ sponge { optional(false) } } + // Runs the gametests under src/test on a headless SpongeVanilla server through the test task + gameTests() +} + +minecraft { + version("1.21.1") } val javaTarget = 21 diff --git a/settings.gradle.kts b/settings.gradle.kts index 5d54170..15d6e29 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1 +1,11 @@ +pluginManagement { + repositories { + gradlePluginPortal() + // VanillaGradle's own dependencies are published here + maven("https://repo.spongepowered.org/repository/maven-public/") { + name = "sponge" + } + } +} + rootProject.name = "sponge-plugin-template" diff --git a/src/test/java/org/spongepowered/example/ExampleGameTests.java b/src/test/java/org/spongepowered/example/ExampleGameTests.java new file mode 100644 index 0000000..de7288c --- /dev/null +++ b/src/test/java/org/spongepowered/example/ExampleGameTests.java @@ -0,0 +1,32 @@ +package org.spongepowered.example; + +import net.minecraft.core.BlockPos; +import net.minecraft.gametest.framework.GameTestHelper; +import net.minecraft.world.level.block.Blocks; +import org.spongepowered.api.Sponge; +import org.spongepowered.common.test.gametest.GameTest; +import org.spongepowered.common.test.gametest.GameTestGroup; + +/** + * Gametests run on a headless SpongeVanilla server with this plugin loaded, through {@code ./gradlew test} or the run + * icon beside a test. Each is named {@code example..}, here {@code example.basics.*}. + */ +@GameTestGroup("basics") +public final class ExampleGameTests { + + // The plugin registers /greet during RegisterCommandEvent, so it is there once the server runs the test + @GameTest + void greetCommandIsRegistered(final GameTestHelper helper) { + helper.succeedWhen(() -> helper.assertTrue( + Sponge.server().commandManager().knownAliases().contains("greet"), "the greet command is registered")); + } + + // Positions are relative to the test's own 3x3x3 space. A test passes once it calls succeed() or its + // succeedWhen(...) check holds, and fails if neither happens within maxTicks (20 by default). + @GameTest + void placedBlockIsThere(final GameTestHelper helper) { + final BlockPos pos = new BlockPos(1, 1, 1); + helper.setBlock(pos, Blocks.STONE); + helper.succeedWhen(() -> helper.assertBlockPresent(Blocks.STONE, pos)); + } +} From e792401baf542f27ef065cd6f28ead516df4bc2f Mon Sep 17 00:00:00 2001 From: Gabriel Harris-Rouquette Date: Sat, 24 Oct 2026 10:09:42 -0700 Subject: [PATCH 2/2] fix(ci): bump gradle requirements for vanillagradle --- gradle.properties | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 gradle.properties diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..7f10f48 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,2 @@ +# VanillaGradle prepares the Minecraft jars for gametests, which needs more than the default 512 MiB heap +org.gradle.jvmargs=-Xmx2G