diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index eddabd2..5097068 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index ad7845b..9b7cb4a 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-9.7.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.8.0-bin.zip networkTimeout=10000 retries=0 retryBackOffMs=500 diff --git a/gradlew.bat b/gradlew.bat index 8508ef6..03754c4 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -26,6 +26,33 @@ @rem Set local scope for the variables, and ensure extensions are enabled setlocal EnableExtensions +@rem Catch executions from older scripts and ensure they exit cleanly. +@rem This can be removed once we can be reasonably confident that few people +@rem will be migrating directly to this new wrapper. +goto afterSafetyNet +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +goto exitWithErrorLevel +:afterSafetyNet + set DIRNAME=%~dp0 if "%DIRNAME%"=="" set DIRNAME=. @rem This is normally unused @@ -45,13 +72,14 @@ set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if %ERRORLEVEL% equ 0 goto execute -echo. 1>&2 -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 -echo. 1>&2 -echo Please set the JAVA_HOME variable in your environment to match the 1>&2 -echo location of your Java installation. 1>&2 +1>&2 echo. +1>&2 echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +1>&2 echo. +1>&2 echo Please set the JAVA_HOME variable in your environment to match the +1>&2 echo location of your Java installation. "%COMSPEC%" /c exit 1 +goto exitWithErrorLevel :findJavaFromJavaHome set JAVA_HOME=%JAVA_HOME:"=% @@ -59,13 +87,14 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto execute -echo. 1>&2 -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 -echo. 1>&2 -echo Please set the JAVA_HOME variable in your environment to match the 1>&2 -echo location of your Java installation. 1>&2 +1>&2 echo. +1>&2 echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +1>&2 echo. +1>&2 echo Please set the JAVA_HOME variable in your environment to match the +1>&2 echo location of your Java installation. "%COMSPEC%" /c exit 1 +goto exitWithErrorLevel :execute @rem Setup the command line @@ -75,8 +104,9 @@ echo location of your Java installation. 1>&2 @rem Execute gradlew @rem endlocal doesn't take effect until after the line is parsed and variables are expanded @rem which allows us to clear the local environment before executing the java command -endlocal & "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* & call :exitWithErrorLevel +endlocal & "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* & call :exitWithErrorLevel & goto exitWithErrorLevel +@rem This label must not be changed. We rely on old scripts being able to jump to this point. :exitWithErrorLevel @rem Use "%COMSPEC%" /c exit to allow operators to work properly in scripts "%COMSPEC%" /c exit %ERRORLEVEL% diff --git a/src/main/java/io/github/misode/packtest/PackTest.java b/src/main/java/io/github/misode/packtest/PackTest.java index aec828a..2199dd9 100644 --- a/src/main/java/io/github/misode/packtest/PackTest.java +++ b/src/main/java/io/github/misode/packtest/PackTest.java @@ -7,6 +7,7 @@ import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import net.fabricmc.api.ModInitializer; import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback; +import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents; import net.minecraft.util.FileUtil; import net.minecraft.util.Util; import net.minecraft.commands.CommandBuildContext; @@ -56,8 +57,19 @@ public void onInitialize() { CommandRegistrationCallback.EVENT.register((dispatcher, buildContext, environment) -> registerCommands(dispatcher, buildContext) ); + ServerLifecycleEvents.SERVER_STARTED.register(server -> + server.execute(() -> loadTests(server))); + ServerLifecycleEvents.END_DATA_PACK_RELOAD.register((server, resourceManager, success) -> { + if (success) { + server.execute(() -> loadTests(server)); + } + }); } + private static void loadTests(net.minecraft.server.MinecraftServer server) { + new PackTestLibrary(server.registryAccess()).load(server); + } + public static void generateCommandsReport() { Path path = Paths.get("generated", "reports", "commands.json"); CommandDispatcher dispatcher = new CommandDispatcher<>(); diff --git a/src/main/java/io/github/misode/packtest/PackTestException.java b/src/main/java/io/github/misode/packtest/PackTestException.java index bef44cd..d5bcbb6 100644 --- a/src/main/java/io/github/misode/packtest/PackTestException.java +++ b/src/main/java/io/github/misode/packtest/PackTestException.java @@ -3,13 +3,18 @@ import net.minecraft.gametest.framework.GameTestAssertException; import net.minecraft.network.chat.Component; import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; public class PackTestException extends GameTestAssertException { final int line; + final String command; + final @Nullable String description; - public PackTestException(Component message, int tick, int line) { + public PackTestException(Component message, int tick, int line, String command, @Nullable String description) { super(message, tick); this.line = line; + this.command = command; + this.description = description; } public int getLine() { @@ -18,6 +23,9 @@ public int getLine() { @Override public @NonNull Component getDescription() { - return Component.literal("On line " + this.line + ": ").append(super.getDescription()); + String prefix = this.command.isEmpty() + ? "On line " + this.line + ": " + : "\"" + this.command + "\" on line " + this.line + ": "; + return Component.literal(prefix).append(super.getDescription()); } } diff --git a/src/main/java/io/github/misode/packtest/PackTestExecutor.java b/src/main/java/io/github/misode/packtest/PackTestExecutor.java index 3f2f3e5..5cde6f3 100644 --- a/src/main/java/io/github/misode/packtest/PackTestExecutor.java +++ b/src/main/java/io/github/misode/packtest/PackTestExecutor.java @@ -5,6 +5,7 @@ import io.github.misode.packtest.commands.assertions.AssertResult; import io.github.misode.packtest.dummy.Dummy; import net.minecraft.commands.CommandResultCallback; +import net.minecraft.commands.CommandSource; import net.minecraft.commands.CommandSourceStack; import net.minecraft.commands.Commands; import net.minecraft.commands.arguments.coordinates.Coordinates; @@ -15,6 +16,7 @@ import net.minecraft.world.phys.Vec2; import net.minecraft.world.phys.Vec3; import org.jetbrains.annotations.Nullable; +import org.jspecify.annotations.NonNull; import java.util.*; import java.util.concurrent.atomic.AtomicInteger; @@ -24,13 +26,22 @@ public class PackTestExecutor { private static final SimpleCommandExceptionType ERROR_NOT_IN_TEST = new SimpleCommandExceptionType( Component.literal("Command can only be used inside a test")); + private static final CommandSource SILENT_SOURCE = new CommandSource() { + @Override public void sendSystemMessage(@NonNull Component message) {} + @Override public boolean acceptsSuccess() { return true; } + @Override public boolean acceptsFailure() { return true; } + @Override public boolean shouldInformAdmins() { return false; } + @Override public boolean alwaysAccepts() { return true; } + }; private static @Nullable PackTestExecutor current; private final List> awaits = new ArrayList<>(); private final GameTestHelper helper; private final int timeout; private final long chatSequence = ChatRecorder.sequence(); - private int line = 0; + private PackTestFunction function; + private int sourceLine = 0; + private String commandName = ""; private boolean done = false; public PackTestExecutor(GameTestHelper helper, int timeout) { @@ -46,6 +57,7 @@ public static PackTestExecutor current() throws CommandSyntaxException { } public void run(PackTestFunction function) { + this.function = function; CommandSourceStack source = createCommandSourceStack(function); Queue steps = new ArrayDeque<>(function.steps()); @@ -58,7 +70,8 @@ public void run(PackTestFunction function) { } while (!steps.isEmpty() && !this.done && this.awaits.isEmpty()) { PackTestFunction.Step step = steps.poll(); - this.line = step.line(); + this.sourceLine = step.sourceLine(); + this.commandName = step.commandName(); Commands.executeCommandInContext(source, ctx -> ExecutionContext.queueInitialCommandExecution(ctx, step.command(), step.chain(), source, CommandResultCallback.EMPTY)); } @@ -78,7 +91,7 @@ private CommandSourceStack createCommandSourceStack(PackTestFunction function) { CommandSourceStack source = helper.getLevel().getServer().createCommandSourceStack() .withLevel(helper.getLevel()) .withPosition(helper.absoluteVec(Vec3.ZERO)) - .withSuppressedOutput(); + .withSource(SILENT_SOURCE); Optional coordinates = function.directives().dummy(); if (coordinates.isPresent()) { @@ -144,7 +157,8 @@ private void registerPoll(Supplier check, boolean negated) { } private PackTestException failure(Component message) { - return new PackTestException(message, (int)this.helper.getTick(), this.line); + String description = this.function != null ? this.function.description() : ""; + return new PackTestException(message, (int)this.helper.getTick(), this.sourceLine, this.commandName, description); } private boolean isLastTick() { diff --git a/src/main/java/io/github/misode/packtest/PackTestFunction.java b/src/main/java/io/github/misode/packtest/PackTestFunction.java index 973dd78..0e09a8a 100644 --- a/src/main/java/io/github/misode/packtest/PackTestFunction.java +++ b/src/main/java/io/github/misode/packtest/PackTestFunction.java @@ -12,7 +12,7 @@ import java.util.*; -public record PackTestFunction(List steps, PackTestDirectives directives) { +public record PackTestFunction(List steps, PackTestDirectives directives, String description) { public void run(GameTestHelper helper) { PackTestExecutor executor = new PackTestExecutor(helper, this.directives.maxTicks()); executor.run(this); @@ -24,10 +24,11 @@ public static PackTestFunction fromLines( List lines) throws IllegalArgumentException { PackTestDirectives directives = new PackTestDirectives(); List steps = new ArrayList<>(); + String description = ""; int i = 0; while (i < lines.size()) { - final int line = i + 1; + final int lineNumber = i + 1; StringBuilder builder = new StringBuilder(lines.get(i++).trim()); while (!builder.isEmpty() && builder.charAt(builder.length() - 1) == '\\') { @@ -40,26 +41,30 @@ public static PackTestFunction fromLines( CommandFunction.checkCommandLineLength(builder); } - String command = builder.toString(); - if (command.isEmpty()) continue; + String line = builder.toString(); + if (line.isEmpty()) continue; - CommandFunction.checkCommandLineLength(command); - StringReader reader = new StringReader(command); + CommandFunction.checkCommandLineLength(line); + StringReader reader = new StringReader(line); if (!reader.canRead()) continue; if (reader.peek() == '#') { - parseDirective(reader, directives); + if (line.length() > 1 && line.charAt(1) == '>') { + description = line.substring(2).trim(); + } else { + parseDirective(new StringReader(line), directives); + } continue; } try { - steps.add(new Step(command, parseCommand(dispatcher, context, command), line)); + steps.add(parseCommand(dispatcher, context, line, lineNumber)); } catch (CommandSyntaxException e) { - throw new IllegalArgumentException("Whilst parsing command on line " + line + ": " + e.getMessage()); + throw new IllegalArgumentException("Whilst parsing command on line " + lineNumber + ": " + e.getMessage()); } } - return new PackTestFunction(steps, directives); + return new PackTestFunction(steps, directives, description); } private static void parseDirective( @@ -77,17 +82,24 @@ private static void parseDirective( } } - private static ContextChain parseCommand( + private static Step parseCommand( CommandDispatcher dispatcher, CommandSourceStack context, - String command) throws CommandSyntaxException { + String command, + int sourceLine) throws CommandSyntaxException { ParseResults parseResults = dispatcher.parse(command, context); Commands.validateParseResults(parseResults); - return ContextChain.tryFlatten(parseResults.getContext().build(command)) + ContextChain chain = ContextChain.tryFlatten(parseResults.getContext().build(command)) .orElseThrow(() -> CommandSyntaxException.BUILT_IN_EXCEPTIONS .dispatcherUnknownCommand() .createWithContext(parseResults.getReader())); + String effectiveCommandName = chain.getTopContext().getLastChild().getNodes().stream() + .map(n -> n.getNode().getName()) + .filter(n -> !n.isEmpty()) + .findFirst() + .orElse(""); + return new Step(command, chain, sourceLine, effectiveCommandName); } - public record Step(String command, ContextChain chain, int line) {} + public record Step(String command, ContextChain chain, int sourceLine, String commandName) {} } diff --git a/src/main/java/io/github/misode/packtest/PackTestLibrary.java b/src/main/java/io/github/misode/packtest/PackTestLibrary.java index 6d52740..a48194a 100644 --- a/src/main/java/io/github/misode/packtest/PackTestLibrary.java +++ b/src/main/java/io/github/misode/packtest/PackTestLibrary.java @@ -1,91 +1,63 @@ package io.github.misode.packtest; -import com.google.common.collect.*; import com.mojang.brigadier.CommandDispatcher; import net.minecraft.commands.CommandSourceStack; -import net.minecraft.commands.Commands; import net.minecraft.core.*; import net.minecraft.resources.FileToIdConverter; import net.minecraft.resources.Identifier; -import net.minecraft.server.packs.resources.PreparableReloadListener; +import net.minecraft.server.MinecraftServer; import net.minecraft.server.packs.resources.Resource; import net.minecraft.server.packs.resources.ResourceManager; -import net.minecraft.server.permissions.PermissionSet; -import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.io.BufferedReader; import java.io.IOException; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; -import java.util.concurrent.Executor; +import java.util.concurrent.ForkJoinPool; -public class PackTestLibrary implements PreparableReloadListener { +public class PackTestLibrary { private static final FileToIdConverter LISTER = new FileToIdConverter("test", ".mcfunction"); + private static final Map LOADED = new HashMap<>(); + private final PackTestRegistries registries; - private final PermissionSet testCompilationPermissions; - private final CommandDispatcher dispatcher; - public PackTestLibrary( - HolderLookup.Provider registries, - PermissionSet testCompilationPermissions, - CommandDispatcher dispatcher) { + public PackTestLibrary(HolderLookup.Provider registries) { this.registries = new PackTestRegistries(registries); - this.testCompilationPermissions = testCompilationPermissions; - this.dispatcher = dispatcher; } - @Override - public @NotNull CompletableFuture reload( - @NotNull SharedState currentReload, - @NotNull Executor taskExecutor, - PreparationBarrier preparationBarrier, - @NotNull Executor reloadExecutor) { - ResourceManager manager = currentReload.resourceManager(); - CompletableFuture registryLoad = this.registries.load(manager, taskExecutor); - - CompletableFuture>> testFunctions = CompletableFuture - .supplyAsync(() -> LISTER.listMatchingResources(manager), taskExecutor) - .thenComposeAsync(map -> prepareTestFunctions(map, taskExecutor)); - - return CompletableFuture.allOf(registryLoad, testFunctions) - .thenCompose(preparationBarrier::wait) - .thenAcceptAsync((_) -> - this.registries.register(registryLoad.join(), collectTestFunctions(testFunctions.join())), - reloadExecutor); + public static @Nullable PackTestFunction getLoaded(Identifier id) { + return LOADED.get(id); } - private CompletableFuture>> prepareTestFunctions( - Map resources, - Executor taskExecutor) { - Map> result = new HashMap<>(); - CommandSourceStack compilationContext = Commands.createCompilationContext(this.testCompilationPermissions); + public void load(MinecraftServer server) { + ResourceManager manager = server.getResourceManager(); + RegistryAccess.Frozen registryLookup = this.registries + .load(manager, ForkJoinPool.commonPool()) + .join(); + CommandDispatcher dispatcher = server.getCommands().getDispatcher(); + CommandSourceStack source = server.createCommandSourceStack(); + Map resources = LISTER.listMatchingResources(manager); + Map loaded = new HashMap<>(); for (Map.Entry entry : resources.entrySet()) { Identifier id = LISTER.fileToId(entry.getKey()); - result.put(id, CompletableFuture.supplyAsync(() -> { + try { List lines = readLines(entry.getValue()); - return PackTestFunction.fromLines(this.dispatcher, compilationContext, lines); - }, taskExecutor)); - } - - return CompletableFuture.allOf(result.values().toArray(new CompletableFuture[0])).handle((_, _) -> result); - } - - private static Map collectTestFunctions(Map> futures) { - ImmutableMap.Builder result = ImmutableMap.builder(); - futures.forEach((id, future) -> future.handle((test, e) -> { - if (e == null) { - result.put(id, test); - } else { + PackTestFunction fn = PackTestFunction.fromLines(dispatcher, source, lines); + loaded.put(id, fn); + } catch (Exception e) { PackTest.LOGGER.error("Failed to load test {}", id, e); } - return null; - }).join()); - return result.build(); + } + + LOADED.clear(); + LOADED.putAll(loaded); + this.registries.register(registryLookup, loaded); + PackTest.LOGGER.info("Loaded {} test functions", loaded.size()); } private static List readLines(Resource resource) { diff --git a/src/main/java/io/github/misode/packtest/TestMessages.java b/src/main/java/io/github/misode/packtest/TestMessages.java new file mode 100644 index 0000000..7debced --- /dev/null +++ b/src/main/java/io/github/misode/packtest/TestMessages.java @@ -0,0 +1,41 @@ +package io.github.misode.packtest; + +import net.minecraft.gametest.framework.GameTestInfo; +import net.minecraft.util.Util; +import org.jspecify.annotations.NonNull; + +public final class TestMessages { + private TestMessages() {} + + public static String pass(GameTestInfo testInfo, PackTestFunction fn) { + return header(testInfo, fn) + " passed at " + testInfo.getTestBlockPos().toShortString() + " on tick " + testInfo.getTick() + "!"; + } + + public static String fail(GameTestInfo testInfo, PackTestFunction fn, Throwable error) { + return header(testInfo, fn) + " failed! " + Util.describeError(error); + } + + public static String failAnnotated(GameTestInfo testInfo, PackTestFunction fn, Throwable error, boolean annotations) { + String errorMessage = Util.describeError(error); + String lineSuffix = (error instanceof PackTestException pe ? " on line " + pe.getLine() : "") + "! "; + String text = header(testInfo, fn) + " failed" + lineSuffix + errorMessage; + if (testInfo.isRequired()) { + if (annotations) { + return "::error title=Test " + text + "::" + errorMessage; + } + return colorize(text, "\u001b[0;31m"); + } + return colorize(text, "\u001b[0;33m"); + } + + private static String header(GameTestInfo testInfo, PackTestFunction fn) { + String prefix = testInfo.isRequired() ? "" : "(optional) "; + String description = fn.description(); + String wrappedDescription = description.isEmpty() ? "" : " (" + description + ")"; + return prefix + testInfo.id() + wrappedDescription; + } + + private static String colorize(String text, @NonNull String code) { + return code + text + "\u001b[0m"; + } +} \ No newline at end of file diff --git a/src/main/java/io/github/misode/packtest/mixin/LogTestReporterMixin.java b/src/main/java/io/github/misode/packtest/mixin/LogTestReporterMixin.java index 9a9f80d..6a08b58 100644 --- a/src/main/java/io/github/misode/packtest/mixin/LogTestReporterMixin.java +++ b/src/main/java/io/github/misode/packtest/mixin/LogTestReporterMixin.java @@ -1,8 +1,9 @@ package io.github.misode.packtest.mixin; -import io.github.misode.packtest.PackTestException; import io.github.misode.packtest.PackTest; -import net.minecraft.util.Util; +import io.github.misode.packtest.PackTestFunction; +import io.github.misode.packtest.PackTestLibrary; +import io.github.misode.packtest.TestMessages; import net.minecraft.gametest.framework.GameTestInfo; import net.minecraft.gametest.framework.LogTestReporter; import org.slf4j.Logger; @@ -29,21 +30,25 @@ public class LogTestReporterMixin { @Inject(method = "onTestFailed", at = @At(value = "HEAD"), cancellable = true) private void onTestFailed(GameTestInfo testInfo, CallbackInfo ci) { if (PackTest.isAutoEnabled()) { - String testName = testInfo.id().toString(); - String lineNumber = testInfo.getError() instanceof PackTestException err - ? " on line " + err.getLine() - : ""; - String message = Util.describeError(testInfo.getError()); + String text = TestMessages.failAnnotated( + testInfo, + PackTestLibrary.getLoaded(testInfo.id()), + testInfo.getError(), + PackTest.isAnnotationsEnabled() + ); if (testInfo.isRequired()) { - if (PackTest.isAnnotationsEnabled()) { - LOGGER.error(PackTest.wrapError("{} failed{}!") + "\n::error title=Test {} failed{}!::{}", testName, lineNumber, testName, lineNumber, message); - } else { - LOGGER.error(PackTest.wrapError("{} failed{}! {}"), testName, lineNumber, message); - } + LOGGER.error("{}", text); } else { - LOGGER.warn(PackTest.wrapWarning("(optional) {} failed{}! {}"), testName, lineNumber, message); + LOGGER.warn("{}", text); } ci.cancel(); } } -} + + @Inject(method = "onTestSuccess", at = @At(value = "HEAD")) + private void onTestSuccess(GameTestInfo testInfo, CallbackInfo ci) { + PackTestFunction fn = PackTestLibrary.getLoaded(testInfo.id()); + if (fn == null) return; + LOGGER.info("{}", TestMessages.pass(testInfo, fn)); + } +} \ No newline at end of file diff --git a/src/main/java/io/github/misode/packtest/mixin/ReloadableServerResourcesMixin.java b/src/main/java/io/github/misode/packtest/mixin/ReloadableServerResourcesMixin.java deleted file mode 100644 index e9fa203..0000000 --- a/src/main/java/io/github/misode/packtest/mixin/ReloadableServerResourcesMixin.java +++ /dev/null @@ -1,56 +0,0 @@ -package io.github.misode.packtest.mixin; - -import com.llamalad7.mixinextras.injector.ModifyReturnValue; -import io.github.misode.packtest.PackTestLibrary; -import net.minecraft.commands.Commands; -import net.minecraft.core.Registry; -import net.minecraft.core.component.DataComponentInitializers; -import net.minecraft.server.ReloadableServerRegistries; -import net.minecraft.server.ReloadableServerResources; -import net.minecraft.server.packs.resources.PreparableReloadListener; -import net.minecraft.server.permissions.PermissionSet; -import net.minecraft.world.flag.FeatureFlagSet; -import org.spongepowered.asm.mixin.Final; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.Unique; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; - -import java.util.ArrayList; -import java.util.List; - -/** - * Adds {@link PackTestLibrary} to the /reload listeners - */ -@Mixin(ReloadableServerResources.class) -public class ReloadableServerResourcesMixin { - @Shadow - @Final - private Commands commands; - @Unique - private PackTestLibrary testLibrary; - - @Inject(method = "", at = @At("RETURN")) - private void init( - ReloadableServerRegistries.LoadResult loadingContext, - FeatureFlagSet enabledFeatures, - Commands.CommandSelection commandSelection, - List>> postponedTags, - PermissionSet functionCompilationPermissions, - List> newComponents, - CallbackInfo ci) { - this.testLibrary = new PackTestLibrary( - loadingContext.lookupWithUpdatedTags(), - functionCompilationPermissions, - this.commands.getDispatcher()); - } - - @ModifyReturnValue(method = "listeners", at = @At("RETURN")) - private List listeners(List list) { - List result = new ArrayList<>(list); - result.add(testLibrary); - return result; - } -} diff --git a/src/main/java/io/github/misode/packtest/mixin/ReportGameListenerMixin.java b/src/main/java/io/github/misode/packtest/mixin/ReportGameListenerMixin.java new file mode 100644 index 0000000..bd76206 --- /dev/null +++ b/src/main/java/io/github/misode/packtest/mixin/ReportGameListenerMixin.java @@ -0,0 +1,26 @@ +package io.github.misode.packtest.mixin; + +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import io.github.misode.packtest.PackTestFunction; +import io.github.misode.packtest.PackTestLibrary; +import io.github.misode.packtest.TestMessages; +import net.minecraft.ChatFormatting; +import net.minecraft.gametest.framework.GameTestInfo; +import net.minecraft.gametest.framework.ReportGameListener; +import net.minecraft.server.level.ServerLevel; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; + +@Mixin(ReportGameListener.class) +public class ReportGameListenerMixin { + @WrapOperation(method = "visualizePassedTest", at = @At(value = "INVOKE", target = "Lnet/minecraft/gametest/framework/ReportGameListener;say(Lnet/minecraft/server/level/ServerLevel;Lnet/minecraft/ChatFormatting;Ljava/lang/String;)V", ordinal = 0)) + private static void injectDescriptionPassed(ServerLevel level, ChatFormatting color, String text, Operation original, GameTestInfo testInfo) { + PackTestFunction fn = PackTestLibrary.getLoaded(testInfo.id()); + if (fn == null) { + original.call(level, color, text); + return; + } + original.call(level, ChatFormatting.GREEN, TestMessages.pass(testInfo, fn)); + } +} \ No newline at end of file diff --git a/src/main/resources/packtest.mixins.json b/src/main/resources/packtest.mixins.json index 2e593f1..34b4d09 100644 --- a/src/main/resources/packtest.mixins.json +++ b/src/main/resources/packtest.mixins.json @@ -11,7 +11,7 @@ "MappedRegistryMixin", "PackMixin", "PlayerListMixin", - "ReloadableServerResourcesMixin", + "ReportGameListenerMixin", "ServerFunctionLibraryMixin", "ServerPlayerMixin", "SimpleJsonResourceReloadListenerMixin",