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 <jozsefaug@gmail.com>
This commit is contained in:
IThundxr 2024-11-03 20:57:47 -05:00 committed by GitHub
parent 4f0c1cc1ae
commit 461578ec0e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 197 additions and 37 deletions

View File

@ -9,6 +9,9 @@ charset = utf-8
trim_trailing_whitespace = true trim_trailing_whitespace = true
insert_final_newline = true insert_final_newline = true
[*.yml]
indent_size = 2
[*.json] [*.json]
indent_size = 2 indent_size = 2
max_line_length = 500 max_line_length = 500

View File

@ -1,43 +1,42 @@
name: build name: Build
on: [ pull_request, push ] on: [ workflow_dispatch, pull_request, push ]
env:
JAVA_VERSION: 17
jobs: jobs:
build: build:
strategy: runs-on: ubuntu-latest
matrix:
java: [
17 # Current Java LTS & minimum supported by Minecraft
]
os: [ ubuntu-latest ]
runs-on: ${{ matrix.os }}
steps: steps:
- name: Checkout - name: Checkout Repository
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Validate Gradle Wrapper
uses: gradle/actions/wrapper-validation@v3 - name: Setup Java
- name: Gradle Cache run: echo "JAVA_HOME=$JAVA_HOME_${{ env.JAVA_VERSION }}_X64" >> "$GITHUB_ENV"
- name: Loom Cache
uses: actions/cache@v4 uses: actions/cache@v4
with: with:
path: | path: "**/.gradle/loom-cache"
~/.gradle/caches key: "${{ runner.os }}-gradle-${{ hashFiles('**/libs.versions.*', '**/*.gradle*', '**/gradle-wrapper.properties') }}"
~/.gradle/wrapper restore-keys: "${{ runner.os }}-gradle-"
.gradle/loom-cache
build/ - name: Setup Gradle
key: ${{ runner.os }}-jdk${{ matrix.java }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle.properties', '**/gradle-wrapper.properties', '.github/workflows/build.yml') }} uses: gradle/actions/setup-gradle@v3
- name: Setup JDK ${{ matrix.java }}
uses: actions/setup-java@v4
with: with:
distribution: 'temurin' gradle-home-cache-cleanup: true
java-version: ${{ matrix.java }} cache-read-only: ${{ !endsWith(github.ref_name, '/dev') }}
- name: Make Gradle Wrapper Executable
if: ${{ runner.os != 'Windows' }} - name: Validate Gradle Wrapper Integrity
run: chmod +x ./gradlew uses: gradle/wrapper-validation-action@v2
- name: Build - name: Build
# doesn't actually publish, as no secrets are passed in, just makes sure that publishing works # Doesn't actually publish, as no secrets are passed in, just makes sure that publishing works
run: ./gradlew publish --no-daemon # Also generate the mod jars for the test job
run: ./gradlew remapTestModJar publish --no-daemon
- name: Capture Build Artifacts - name: Capture Build Artifacts
if: ${{ runner.os == 'Linux' && matrix.java == '17' }}
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: Artifacts name: Artifacts
@ -45,3 +44,42 @@ jobs:
common/build/libs/ common/build/libs/
fabric/build/libs/ fabric/build/libs/
forge/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' }}

View File

@ -2,17 +2,17 @@ package dev.engine_room.gradle.platform
import dev.engine_room.gradle.jarset.JarTaskSet import dev.engine_room.gradle.jarset.JarTaskSet
import net.fabricmc.loom.api.LoomGradleExtensionAPI import net.fabricmc.loom.api.LoomGradleExtensionAPI
import net.fabricmc.loom.task.RemapJarTask
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.tasks.SourceSet import org.gradle.api.tasks.SourceSet
import org.gradle.api.tasks.SourceSetContainer import org.gradle.api.tasks.SourceSetContainer
import org.gradle.api.tasks.compile.JavaCompile import org.gradle.api.tasks.compile.JavaCompile
import org.gradle.api.tasks.javadoc.Javadoc import org.gradle.api.tasks.javadoc.Javadoc
import org.gradle.jvm.tasks.Jar import org.gradle.jvm.tasks.Jar
import org.gradle.kotlin.dsl.named import org.gradle.kotlin.dsl.*
import org.gradle.kotlin.dsl.provideDelegate
import org.gradle.kotlin.dsl.the
import org.gradle.kotlin.dsl.withType
import org.gradle.language.jvm.tasks.ProcessResources import org.gradle.language.jvm.tasks.ProcessResources
import java.io.File
import kotlin.properties.ReadWriteProperty import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty import kotlin.reflect.KProperty
@ -102,6 +102,29 @@ open class PlatformExtension(val project: Project) {
} }
} }
fun setupTestMod(sourceSet: SourceSet) {
project.tasks.apply {
val testModJar = register<Jar>("testModJar") {
from(sourceSet.output)
val file = File(project.layout.buildDirectory.asFile.get(), "devlibs");
destinationDirectory.set(file)
archiveClassifier = "testmod"
}
val remapTestModJar = register<RemapJarTask>("remapTestModJar") {
dependsOn(testModJar)
inputFile.set(testModJar.get().archiveFile)
archiveClassifier = "testmod"
addNestedDependencies = false
classpath.from(sourceSet.compileClasspath)
}
named<Task>("build").configure {
dependsOn(remapTestModJar)
}
}
}
private class DependentProject(private val thisProject: Project) : ReadWriteProperty<Any?, Project> { private class DependentProject(private val thisProject: Project) : ReadWriteProperty<Any?, Project> {
private var value: Project? = null private var value: Project? = null

View File

@ -27,7 +27,10 @@ abstract class RenderSystemMixin {
FogUniforms.update(); 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) { private static void flywheel$onSetFogShape(CallbackInfo ci) {
FogUniforms.update(); FogUniforms.update();
} }

View File

@ -12,6 +12,7 @@ val lib = sourceSets.create("lib")
val backend = sourceSets.create("backend") val backend = sourceSets.create("backend")
val stubs = sourceSets.create("stubs") val stubs = sourceSets.create("stubs")
val main = sourceSets.getByName("main") val main = sourceSets.getByName("main")
val testMod = sourceSets.create("testMod")
transitiveSourceSets { transitiveSourceSets {
compileClasspath = main.compileClasspath compileClasspath = main.compileClasspath
@ -35,6 +36,9 @@ transitiveSourceSets {
compile(stubs) compile(stubs)
implementation(api, lib, backend) implementation(api, lib, backend)
} }
sourceSet(testMod) {
rootCompile()
}
createCompileConfigurations() createCompileConfigurations()
} }
@ -45,6 +49,7 @@ platform {
setupLoomMod(api, lib, backend, main) setupLoomMod(api, lib, backend, main)
setupLoomRuns() setupLoomRuns()
setupFatJar(api, lib, backend, main) setupFatJar(api, lib, backend, main)
setupTestMod(testMod)
} }
jarSets { jarSets {

View File

@ -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();
});
}
}

View File

@ -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"
]
}
}

View File

@ -12,6 +12,7 @@ val lib = sourceSets.create("lib")
val backend = sourceSets.create("backend") val backend = sourceSets.create("backend")
val stubs = sourceSets.create("stubs") val stubs = sourceSets.create("stubs")
val main = sourceSets.getByName("main") val main = sourceSets.getByName("main")
val testMod = sourceSets.create("testMod")
transitiveSourceSets { transitiveSourceSets {
compileClasspath = main.compileClasspath compileClasspath = main.compileClasspath
@ -33,6 +34,9 @@ transitiveSourceSets {
sourceSet(main) { sourceSet(main) {
compile(api, lib, backend, stubs) compile(api, lib, backend, stubs)
} }
sourceSet(testMod) {
rootCompile()
}
createCompileConfigurations() createCompileConfigurations()
} }
@ -43,6 +47,7 @@ platform {
setupLoomMod(api, lib, backend, main) setupLoomMod(api, lib, backend, main)
setupLoomRuns() setupLoomRuns()
setupFatJar(api, lib, backend, main) setupFatJar(api, lib, backend, main)
setupTestMod(testMod)
} }
jarSets { jarSets {

View File

@ -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();
}
});
}
}

View File

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