From 461578ec0ea44eb4736ed7a3ba20fb273eba3c78 Mon Sep 17 00:00:00 2001 From: IThundxr Date: Sun, 3 Nov 2024 20:57:47 -0500 Subject: [PATCH] Automated testing (#269) * Automated testing * Testing testing - Use 2 spaces for indents yaml - Move setupTestMod to PlatformExtension - Allow specifying the sourceSet for the testMod artifact - Rename things to camelCase - Use rootCompile from transitiveSourceSets for the testMod source sets - Use a blanket remapTestModJar task in the gh actions build * Fail slowly - We want to know the results of both tests regardless * Add workflow dispatch * Shoes should be steel toed, dangerous stuff * Update build.yml * fix modid * Update FlywheelTestModClient.java * add debug logging * fix syntax issues * fix issues * Update build.yml * Add debug logging * more logging * get testmod from correct dir * switch to env var * Why wait? - Immediately audit on client tick * DidObfuscate - Fix RenderSystemMixin on fabric - setShaderFogShape's arguments need to be remapped, but the name of the function should not be. Fortunately mixin allows matching by function name alone * Clever commit title - Change the Fabric mod ID to match Forge - Move "Flywheel Test Mod" to static - Cleanup start/stop messages - Use the client start event on Fabric --------- Co-authored-by: Jozufozu --- .editorconfig | 3 + .github/workflows/build.yml | 102 ++++++++++++------ .../gradle/platform/PlatformExtension.kt | 31 +++++- .../backend/mixin/RenderSystemMixin.java | 5 +- fabric/build.gradle.kts | 5 + .../flywheel/FlywheelTestModClient.java | 29 +++++ fabric/src/testMod/resources/fabric.mod.json | 13 +++ forge/build.gradle.kts | 5 + .../flywheel/FlywheelTestModClient.java | 33 ++++++ .../src/testMod/resources/META-INF/mods.toml | 8 ++ 10 files changed, 197 insertions(+), 37 deletions(-) create mode 100644 fabric/src/testMod/java/dev/engine_room/flywheel/FlywheelTestModClient.java create mode 100644 fabric/src/testMod/resources/fabric.mod.json create mode 100644 forge/src/testMod/java/dev/engine_room/flywheel/FlywheelTestModClient.java create mode 100644 forge/src/testMod/resources/META-INF/mods.toml diff --git a/.editorconfig b/.editorconfig index 038bc765e..294e122e9 100644 --- a/.editorconfig +++ b/.editorconfig @@ -9,6 +9,9 @@ charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true +[*.yml] +indent_size = 2 + [*.json] indent_size = 2 max_line_length = 500 diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index dd1169eae..cf5b1c501 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,47 +1,85 @@ -name: build +name: Build -on: [ pull_request, push ] +on: [ workflow_dispatch, pull_request, push ] + +env: + JAVA_VERSION: 17 jobs: build: - strategy: - matrix: - java: [ - 17 # Current Java LTS & minimum supported by Minecraft - ] - os: [ ubuntu-latest ] - runs-on: ${{ matrix.os }} + runs-on: ubuntu-latest steps: - - name: Checkout + - name: Checkout Repository uses: actions/checkout@v4 - - name: Validate Gradle Wrapper - uses: gradle/actions/wrapper-validation@v3 - - name: Gradle Cache + + - name: Setup Java + run: echo "JAVA_HOME=$JAVA_HOME_${{ env.JAVA_VERSION }}_X64" >> "$GITHUB_ENV" + + - name: Loom Cache uses: actions/cache@v4 with: - path: | - ~/.gradle/caches - ~/.gradle/wrapper - .gradle/loom-cache - build/ - key: ${{ runner.os }}-jdk${{ matrix.java }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle.properties', '**/gradle-wrapper.properties', '.github/workflows/build.yml') }} - - name: Setup JDK ${{ matrix.java }} - uses: actions/setup-java@v4 + path: "**/.gradle/loom-cache" + key: "${{ runner.os }}-gradle-${{ hashFiles('**/libs.versions.*', '**/*.gradle*', '**/gradle-wrapper.properties') }}" + restore-keys: "${{ runner.os }}-gradle-" + + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v3 with: - distribution: 'temurin' - java-version: ${{ matrix.java }} - - name: Make Gradle Wrapper Executable - if: ${{ runner.os != 'Windows' }} - run: chmod +x ./gradlew + gradle-home-cache-cleanup: true + cache-read-only: ${{ !endsWith(github.ref_name, '/dev') }} + + - name: Validate Gradle Wrapper Integrity + uses: gradle/wrapper-validation-action@v2 + - name: Build - # doesn't actually publish, as no secrets are passed in, just makes sure that publishing works - run: ./gradlew publish --no-daemon + # Doesn't actually publish, as no secrets are passed in, just makes sure that publishing works + # Also generate the mod jars for the test job + run: ./gradlew remapTestModJar publish --no-daemon + - name: Capture Build Artifacts - if: ${{ runner.os == 'Linux' && matrix.java == '17' }} uses: actions/upload-artifact@v4 with: name: Artifacts path: | - common/build/libs/ - fabric/build/libs/ - forge/build/libs/ + common/build/libs/ + fabric/build/libs/ + forge/build/libs/ + + test: + strategy: + fail-fast: false + matrix: + loader: [ forge, fabric ] + needs: build + runs-on: ubuntu-latest + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + + - name: Download build artifact + uses: actions/download-artifact@v4 + with: + name: Artifacts + + - name: Setup Environment Variables + run: | + echo "MOD_VERSION=$(grep '^mod_version =' gradle.properties | cut -d'=' -f2 | tr -d ' ')" >> "$GITHUB_ENV" + echo "MINECRAFT_VERSION=$(grep '^minecraft_version =' gradle.properties | cut -d'=' -f2 | tr -d ' ')" >> "$GITHUB_ENV" + echo "FABRIC_API_VERSION=$(grep '^fabric_api_version =' gradle.properties | cut -d'=' -f2 | tr -d ' ' | sed 's/+.*//')" >> "$GITHUB_ENV" + + - name: Move Test Mod and Flywheel into run/mods + run: | + mkdir -p run/mods + cp ${{ matrix.loader }}/build/libs/flywheel-${{ matrix.loader }}-${{ env.MINECRAFT_VERSION }}-${{ env.MOD_VERSION }}.jar run/mods + cp ${{ matrix.loader }}/build/libs/flywheel-${{ matrix.loader }}-${{ env.MINECRAFT_VERSION }}-${{ env.MOD_VERSION }}-testmod.jar run/mods + + # Lock to a specific commit, it would be bad if the tag is re-pushed with unwanted changes + - name: Run the MC client + uses: 3arthqu4ke/mc-runtime-test@e72f8fe1134aabf6fc749a2a8c09bb56dd7d283e + with: + mc: ${{ env.MINECRAFT_VERSION }} + modloader: ${{ matrix.loader }} + regex: .*${{ matrix.loader }}.* + mc-runtime-test: none + java: ${{ env.JAVA_VERSION }} + fabric-api: ${{ matrix.loader == 'fabric' && env.FABRIC_API_VERSION || 'none' }} diff --git a/buildSrc/src/main/kotlin/dev/engine_room/gradle/platform/PlatformExtension.kt b/buildSrc/src/main/kotlin/dev/engine_room/gradle/platform/PlatformExtension.kt index 66831cedf..2addef1dc 100644 --- a/buildSrc/src/main/kotlin/dev/engine_room/gradle/platform/PlatformExtension.kt +++ b/buildSrc/src/main/kotlin/dev/engine_room/gradle/platform/PlatformExtension.kt @@ -2,17 +2,17 @@ package dev.engine_room.gradle.platform import dev.engine_room.gradle.jarset.JarTaskSet import net.fabricmc.loom.api.LoomGradleExtensionAPI +import net.fabricmc.loom.task.RemapJarTask import org.gradle.api.Project +import org.gradle.api.Task import org.gradle.api.tasks.SourceSet import org.gradle.api.tasks.SourceSetContainer import org.gradle.api.tasks.compile.JavaCompile import org.gradle.api.tasks.javadoc.Javadoc import org.gradle.jvm.tasks.Jar -import org.gradle.kotlin.dsl.named -import org.gradle.kotlin.dsl.provideDelegate -import org.gradle.kotlin.dsl.the -import org.gradle.kotlin.dsl.withType +import org.gradle.kotlin.dsl.* import org.gradle.language.jvm.tasks.ProcessResources +import java.io.File import kotlin.properties.ReadWriteProperty import kotlin.reflect.KProperty @@ -102,6 +102,29 @@ open class PlatformExtension(val project: Project) { } } + fun setupTestMod(sourceSet: SourceSet) { + project.tasks.apply { + val testModJar = register("testModJar") { + from(sourceSet.output) + val file = File(project.layout.buildDirectory.asFile.get(), "devlibs"); + destinationDirectory.set(file) + archiveClassifier = "testmod" + } + + val remapTestModJar = register("remapTestModJar") { + dependsOn(testModJar) + inputFile.set(testModJar.get().archiveFile) + archiveClassifier = "testmod" + addNestedDependencies = false + classpath.from(sourceSet.compileClasspath) + } + + named("build").configure { + dependsOn(remapTestModJar) + } + } + } + private class DependentProject(private val thisProject: Project) : ReadWriteProperty { private var value: Project? = null diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/mixin/RenderSystemMixin.java b/common/src/backend/java/dev/engine_room/flywheel/backend/mixin/RenderSystemMixin.java index 9a51df11b..b08444dc7 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/mixin/RenderSystemMixin.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/mixin/RenderSystemMixin.java @@ -27,7 +27,10 @@ abstract class RenderSystemMixin { FogUniforms.update(); } - @Inject(method = "setShaderFogShape(Lcom/mojang/blaze3d/shaders/FogShape;)V", at = @At("RETURN")) + // Fabric fails to resolve the mixin in prod when the full signature is specified. + // I suspect it's because this method references a class name in its signature, + // and that needs to be remapped while the function names in RenderSystem are marked with @DontObfuscate. + @Inject(method = "setShaderFogShape", at = @At("RETURN")) private static void flywheel$onSetFogShape(CallbackInfo ci) { FogUniforms.update(); } diff --git a/fabric/build.gradle.kts b/fabric/build.gradle.kts index 865773be5..b1b04dad8 100644 --- a/fabric/build.gradle.kts +++ b/fabric/build.gradle.kts @@ -12,6 +12,7 @@ val lib = sourceSets.create("lib") val backend = sourceSets.create("backend") val stubs = sourceSets.create("stubs") val main = sourceSets.getByName("main") +val testMod = sourceSets.create("testMod") transitiveSourceSets { compileClasspath = main.compileClasspath @@ -35,6 +36,9 @@ transitiveSourceSets { compile(stubs) implementation(api, lib, backend) } + sourceSet(testMod) { + rootCompile() + } createCompileConfigurations() } @@ -45,6 +49,7 @@ platform { setupLoomMod(api, lib, backend, main) setupLoomRuns() setupFatJar(api, lib, backend, main) + setupTestMod(testMod) } jarSets { diff --git a/fabric/src/testMod/java/dev/engine_room/flywheel/FlywheelTestModClient.java b/fabric/src/testMod/java/dev/engine_room/flywheel/FlywheelTestModClient.java new file mode 100644 index 000000000..6b05b1e62 --- /dev/null +++ b/fabric/src/testMod/java/dev/engine_room/flywheel/FlywheelTestModClient.java @@ -0,0 +1,29 @@ +package dev.engine_room.flywheel; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.spongepowered.asm.mixin.MixinEnvironment; + +import net.fabricmc.api.ClientModInitializer; +import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents; +import net.fabricmc.loader.api.FabricLoader; + +public class FlywheelTestModClient implements ClientModInitializer { + public static final String NAME = "Flywheel Test Mod"; + private static final Logger LOGGER = LoggerFactory.getLogger(NAME); + + @Override + public void onInitializeClient() { + LOGGER.info("Starting {} on EnvType: {}", NAME, FabricLoader.getInstance() + .getEnvironmentType()); + + ClientLifecycleEvents.CLIENT_STARTED.register(client -> { + LOGGER.info("Running mixin audit"); + MixinEnvironment.getCurrentEnvironment() + .audit(); + + LOGGER.info("Stopping client"); + client.stop(); + }); + } +} diff --git a/fabric/src/testMod/resources/fabric.mod.json b/fabric/src/testMod/resources/fabric.mod.json new file mode 100644 index 000000000..11bd545a1 --- /dev/null +++ b/fabric/src/testMod/resources/fabric.mod.json @@ -0,0 +1,13 @@ +{ + "schemaVersion": 1, + "id" : "${mod_id}_testmod", + "name": "${mod_name} Test Mod", + "version": "1.0.0", + "environment": "*", + "license": "${mod_license}", + "entrypoints": { + "client": [ + "dev.engine_room.flywheel.FlywheelTestModClient" + ] + } +} diff --git a/forge/build.gradle.kts b/forge/build.gradle.kts index d1c6b3617..6900fd197 100644 --- a/forge/build.gradle.kts +++ b/forge/build.gradle.kts @@ -12,6 +12,7 @@ val lib = sourceSets.create("lib") val backend = sourceSets.create("backend") val stubs = sourceSets.create("stubs") val main = sourceSets.getByName("main") +val testMod = sourceSets.create("testMod") transitiveSourceSets { compileClasspath = main.compileClasspath @@ -33,6 +34,9 @@ transitiveSourceSets { sourceSet(main) { compile(api, lib, backend, stubs) } + sourceSet(testMod) { + rootCompile() + } createCompileConfigurations() } @@ -43,6 +47,7 @@ platform { setupLoomMod(api, lib, backend, main) setupLoomRuns() setupFatJar(api, lib, backend, main) + setupTestMod(testMod) } jarSets { diff --git a/forge/src/testMod/java/dev/engine_room/flywheel/FlywheelTestModClient.java b/forge/src/testMod/java/dev/engine_room/flywheel/FlywheelTestModClient.java new file mode 100644 index 000000000..f04eb604f --- /dev/null +++ b/forge/src/testMod/java/dev/engine_room/flywheel/FlywheelTestModClient.java @@ -0,0 +1,33 @@ +package dev.engine_room.flywheel; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.spongepowered.asm.mixin.MixinEnvironment; + +import net.minecraft.client.Minecraft; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.event.TickEvent; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.loading.FMLLoader; + +@Mod("flywheel_testmod") +public class FlywheelTestModClient { + public static final String NAME = "Flywheel Test Mod"; + private static final Logger LOGGER = LoggerFactory.getLogger(NAME); + + public FlywheelTestModClient() { + LOGGER.info("Starting {} on Dist: {}", NAME, FMLLoader.getDist()); + + MinecraftForge.EVENT_BUS.addListener((TickEvent.ClientTickEvent e) -> { + if (e.phase == TickEvent.Phase.END) { + LOGGER.info("Running mixin audit"); + MixinEnvironment.getCurrentEnvironment() + .audit(); + + LOGGER.info("Stopping client"); + Minecraft.getInstance() + .stop(); + } + }); + } +} diff --git a/forge/src/testMod/resources/META-INF/mods.toml b/forge/src/testMod/resources/META-INF/mods.toml new file mode 100644 index 000000000..4d76a503e --- /dev/null +++ b/forge/src/testMod/resources/META-INF/mods.toml @@ -0,0 +1,8 @@ +modLoader = "javafml" +loaderVersion = "[0,)" +license = "${mod_license}" + +[[mods]] +modId = "${mod_id}_testmod" +version = "1.0.0" +displayName = "${mod_name} Test Mod"